| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
| 2 | * Mupen64plus-ui-console - main.c * |
| 3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
| 4 | * Copyright (C) 2007-2010 Richard42 * |
| 5 | * Copyright (C) 2008 Ebenblues Nmn Okaygo Tillin9 * |
| 6 | * Copyright (C) 2002 Hacktarux * |
| 7 | * * |
| 8 | * This program is free software; you can redistribute it and/or modify * |
| 9 | * it under the terms of the GNU General Public License as published by * |
| 10 | * the Free Software Foundation; either version 2 of the License, or * |
| 11 | * (at your option) any later version. * |
| 12 | * * |
| 13 | * This program is distributed in the hope that it will be useful, * |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 16 | * GNU General Public License for more details. * |
| 17 | * * |
| 18 | * You should have received a copy of the GNU General Public License * |
| 19 | * along with this program; if not, write to the * |
| 20 | * Free Software Foundation, Inc., * |
| 21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
| 22 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 23 | |
| 24 | /* This is the main application entry point for the console-only front-end |
| 25 | * for Mupen64Plus v2.0. |
| 26 | */ |
| 27 | |
| 28 | #include <stdio.h> |
| 29 | #include <string.h> |
| 30 | #include <stdlib.h> |
| 31 | #include <stdarg.h> |
| 32 | |
| 33 | #include <SDL_main.h> |
| 34 | |
| 35 | #include "cheat.h" |
| 36 | #include "main.h" |
| 37 | #include "plugin.h" |
| 38 | #include "version.h" |
| 39 | #include "core_interface.h" |
| 40 | #include "compare_core.h" |
| 41 | #include "osal_preproc.h" |
| 42 | |
| 43 | #ifdef PAULSCODE |
| 44 | //#include "ae_bridge.h" |
| 45 | #endif |
| 46 | |
| 47 | /* Version number for UI-Console config section parameters */ |
| 48 | #define CONFIG_PARAM_VERSION 1.00 |
| 49 | |
| 50 | /** global variables **/ |
| 51 | int g_Verbose = 0; |
| 52 | |
| 53 | /** static (local) variables **/ |
| 54 | static m64p_handle l_ConfigCore = NULL; |
| 55 | static m64p_handle l_ConfigVideo = NULL; |
| 56 | static m64p_handle l_ConfigUI = NULL; |
| 57 | |
| 58 | static const char *l_CoreLibPath = NULL; |
| 59 | static const char *l_ConfigDirPath = NULL; |
| 60 | static const char *l_ROMFilepath = NULL; // filepath of ROM to load & run at startup |
| 61 | |
| 62 | #if defined(SHAREDIR) |
| 63 | static const char *l_DataDirPath = SHAREDIR; |
| 64 | #else |
| 65 | static const char *l_DataDirPath = NULL; |
| 66 | #endif |
| 67 | |
| 68 | static int *l_TestShotList = NULL; // list of screenshots to take for regression test support |
| 69 | static int l_TestShotIdx = 0; // index of next screenshot frame in list |
| 70 | static int l_SaveOptions = 1; // save command-line options in configuration file (enabled by default) |
| 71 | static int l_CoreCompareMode = 0; // 0 = disable, 1 = send, 2 = receive |
| 72 | |
| 73 | static eCheatMode l_CheatMode = CHEAT_DISABLE; |
| 74 | static char *l_CheatNumList = NULL; |
| 75 | |
| 76 | /********************************************************************************************************* |
| 77 | * Callback functions from the core |
| 78 | */ |
| 79 | |
| 80 | void DebugMessage(int level, const char *message, ...) |
| 81 | { |
| 82 | char msgbuf[1024]; |
| 83 | va_list args; |
| 84 | |
| 85 | va_start(args, message); |
| 86 | vsnprintf(msgbuf, 1024, message, args); |
| 87 | |
| 88 | DebugCallback("UI-Console", level, msgbuf); |
| 89 | |
| 90 | va_end(args); |
| 91 | } |
| 92 | |
| 93 | void DebugCallback(void *Context, int level, const char *message) |
| 94 | { |
| 95 | #ifdef ANDROID0 |
| 96 | if (level == M64MSG_ERROR) |
| 97 | __android_log_print(ANDROID_LOG_ERROR, (const char *) Context, "%s", message); |
| 98 | else if (level == M64MSG_WARNING) |
| 99 | __android_log_print(ANDROID_LOG_WARN, (const char *) Context, "%s", message); |
| 100 | else if (level == M64MSG_INFO) |
| 101 | __android_log_print(ANDROID_LOG_INFO, (const char *) Context, "%s", message); |
| 102 | else if (level == M64MSG_STATUS) |
| 103 | __android_log_print(ANDROID_LOG_DEBUG, (const char *) Context, "%s", message); |
| 104 | else if (level == M64MSG_VERBOSE) |
| 105 | { |
| 106 | if (g_Verbose) |
| 107 | __android_log_print(ANDROID_LOG_VERBOSE, (const char *) Context, "%s", message); |
| 108 | } |
| 109 | else |
| 110 | __android_log_print(ANDROID_LOG_ERROR, (const char *) Context, "Unknown: %s", message); |
| 111 | #else |
| 112 | if (level == M64MSG_ERROR) |
| 113 | printf("%s Error: %s\n", (const char *) Context, message); |
| 114 | else if (level == M64MSG_WARNING) |
| 115 | printf("%s Warning: %s\n", (const char *) Context, message); |
| 116 | else if (level == M64MSG_INFO) |
| 117 | printf("%s: %s\n", (const char *) Context, message); |
| 118 | else if (level == M64MSG_STATUS) |
| 119 | printf("%s Status: %s\n", (const char *) Context, message); |
| 120 | else if (level == M64MSG_VERBOSE) |
| 121 | { |
| 122 | if (g_Verbose) |
| 123 | printf("%s: %s\n", (const char *) Context, message); |
| 124 | } |
| 125 | else |
| 126 | printf("%s Unknown: %s\n", (const char *) Context, message); |
| 127 | #endif |
| 128 | } |
| 129 | |
| 130 | static void FrameCallback(unsigned int FrameIndex) |
| 131 | { |
| 132 | // take a screenshot if we need to |
| 133 | if (l_TestShotList != NULL) |
| 134 | { |
| 135 | int nextshot = l_TestShotList[l_TestShotIdx]; |
| 136 | if (nextshot == FrameIndex) |
| 137 | { |
| 138 | (*CoreDoCommand)(M64CMD_TAKE_NEXT_SCREENSHOT, 0, NULL); /* tell the core take a screenshot */ |
| 139 | // advance list index to next screenshot frame number. If it's 0, then quit |
| 140 | l_TestShotIdx++; |
| 141 | } |
| 142 | else if (nextshot == 0) |
| 143 | { |
| 144 | (*CoreDoCommand)(M64CMD_STOP, 0, NULL); /* tell the core to shut down ASAP */ |
| 145 | free(l_TestShotList); |
| 146 | l_TestShotList = NULL; |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | #ifdef PAULSCODE |
| 152 | void StateCallback( void *Context, m64p_core_param ParamChanged, int NewValue ) |
| 153 | { |
| 154 | /*----ParamChanged----------------- |
| 155 | * --------NewValue-------- |
| 156 | * M64CORE_EMU_STATE 1 |
| 157 | * M64EMU_STOPPED 1 |
| 158 | * M64EMU_RUNNING 2 |
| 159 | * M64EMU_PAUSED 3 |
| 160 | * M64CORE_VIDEO_MODE 2 |
| 161 | * M64CORE_SAVESTATE_SLOT 3 |
| 162 | * M64CORE_SPEED_FACTOR 4 |
| 163 | * M64CORE_SPEED_LIMITER 5 |
| 164 | * M64CORE_VIDEO_SIZE 6 |
| 165 | * M64CORE_AUDIO_VOLUME 7 |
| 166 | * M64CORE_AUDIO_MUTE 8 |
| 167 | * M64CORE_INPUT_GAMESHARK 9 |
| 168 | * M64CORE_STATE_LOADCOMPLETE 10 |
| 169 | * (successful) 1 |
| 170 | * (unsuccessful) 0 |
| 171 | * M64CORE_STATE_SAVECOMPLETE 11 |
| 172 | * (successful) 1 |
| 173 | * (unsuccessful) 0 |
| 174 | */ |
| 175 | |
| 176 | if( ParamChanged == M64CORE_EMU_STATE || ParamChanged == M64CORE_STATE_SAVECOMPLETE || ParamChanged == M64CORE_STATE_LOADCOMPLETE ) |
| 177 | Android_JNI_State_Callback( ParamChanged, NewValue ); |
| 178 | } |
| 179 | #endif |
| 180 | |
| 181 | /********************************************************************************************************* |
| 182 | * Configuration handling |
| 183 | */ |
| 184 | |
| 185 | static m64p_error OpenConfigurationHandles(void) |
| 186 | { |
| 187 | float fConfigParamsVersion; |
| 188 | int bSaveConfig = 0; |
| 189 | m64p_error rval; |
| 190 | |
| 191 | /* Open Configuration sections for core library and console User Interface */ |
| 192 | rval = (*ConfigOpenSection)("Core", &l_ConfigCore); |
| 193 | if (rval != M64ERR_SUCCESS) |
| 194 | { |
| 195 | DebugMessage(M64MSG_ERROR, "failed to open 'Core' configuration section"); |
| 196 | return rval; |
| 197 | } |
| 198 | |
| 199 | rval = (*ConfigOpenSection)("Video-General", &l_ConfigVideo); |
| 200 | if (rval != M64ERR_SUCCESS) |
| 201 | { |
| 202 | DebugMessage(M64MSG_ERROR, "failed to open 'Video-General' configuration section"); |
| 203 | return rval; |
| 204 | } |
| 205 | |
| 206 | rval = (*ConfigOpenSection)("UI-Console", &l_ConfigUI); |
| 207 | if (rval != M64ERR_SUCCESS) |
| 208 | { |
| 209 | DebugMessage(M64MSG_ERROR, "failed to open 'UI-Console' configuration section"); |
| 210 | return rval; |
| 211 | } |
| 212 | |
| 213 | if ((*ConfigGetParameter)(l_ConfigUI, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) |
| 214 | { |
| 215 | DebugMessage(M64MSG_WARNING, "No version number in 'UI-Console' config section. Setting defaults."); |
| 216 | (*ConfigDeleteSection)("UI-Console"); |
| 217 | (*ConfigOpenSection)("UI-Console", &l_ConfigUI); |
| 218 | bSaveConfig = 1; |
| 219 | } |
| 220 | else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) |
| 221 | { |
| 222 | DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'UI-Console' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); |
| 223 | (*ConfigDeleteSection)("UI-Console"); |
| 224 | (*ConfigOpenSection)("UI-Console", &l_ConfigUI); |
| 225 | bSaveConfig = 1; |
| 226 | } |
| 227 | else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) |
| 228 | { |
| 229 | /* handle upgrades */ |
| 230 | float fVersion = CONFIG_PARAM_VERSION; |
| 231 | ConfigSetParameter(l_ConfigUI, "Version", M64TYPE_FLOAT, &fVersion); |
| 232 | DebugMessage(M64MSG_INFO, "Updating parameter set version in 'UI-Console' config section to %.2f", fVersion); |
| 233 | bSaveConfig = 1; |
| 234 | } |
| 235 | |
| 236 | /* Set default values for my Config parameters */ |
| 237 | (*ConfigSetDefaultFloat)(l_ConfigUI, "Version", CONFIG_PARAM_VERSION, "Mupen64Plus UI-Console config parameter set version number. Please don't change this version number."); |
| 238 | (*ConfigSetDefaultString)(l_ConfigUI, "PluginDir", OSAL_CURRENT_DIR, "Directory in which to search for plugins"); |
| 239 | (*ConfigSetDefaultString)(l_ConfigUI, "VideoPlugin", "mupen64plus-video-rice" OSAL_DLL_EXTENSION, "Filename of video plugin"); |
| 240 | (*ConfigSetDefaultString)(l_ConfigUI, "AudioPlugin", "mupen64plus-audio-sdl" OSAL_DLL_EXTENSION, "Filename of audio plugin"); |
| 241 | (*ConfigSetDefaultString)(l_ConfigUI, "InputPlugin", "mupen64plus-input-sdl" OSAL_DLL_EXTENSION, "Filename of input plugin"); |
| 242 | (*ConfigSetDefaultString)(l_ConfigUI, "RspPlugin", "mupen64plus-rsp-hle" OSAL_DLL_EXTENSION, "Filename of RSP plugin"); |
| 243 | |
| 244 | if (bSaveConfig && ConfigSaveSection != NULL) /* ConfigSaveSection was added in Config API v2.1.0 */ |
| 245 | (*ConfigSaveSection)("UI-Console"); |
| 246 | |
| 247 | return M64ERR_SUCCESS; |
| 248 | } |
| 249 | |
| 250 | static m64p_error SaveConfigurationOptions(void) |
| 251 | { |
| 252 | /* if shared data directory was given on the command line, write it into the config file */ |
| 253 | if (l_DataDirPath != NULL) |
| 254 | (*ConfigSetParameter)(l_ConfigCore, "SharedDataPath", M64TYPE_STRING, l_DataDirPath); |
| 255 | |
| 256 | /* if any plugin filepaths were given on the command line, write them into the config file */ |
| 257 | if (g_PluginDir != NULL) |
| 258 | (*ConfigSetParameter)(l_ConfigUI, "PluginDir", M64TYPE_STRING, g_PluginDir); |
| 259 | if (g_GfxPlugin != NULL) |
| 260 | (*ConfigSetParameter)(l_ConfigUI, "VideoPlugin", M64TYPE_STRING, g_GfxPlugin); |
| 261 | if (g_AudioPlugin != NULL) |
| 262 | (*ConfigSetParameter)(l_ConfigUI, "AudioPlugin", M64TYPE_STRING, g_AudioPlugin); |
| 263 | if (g_InputPlugin != NULL) |
| 264 | (*ConfigSetParameter)(l_ConfigUI, "InputPlugin", M64TYPE_STRING, g_InputPlugin); |
| 265 | if (g_RspPlugin != NULL) |
| 266 | (*ConfigSetParameter)(l_ConfigUI, "RspPlugin", M64TYPE_STRING, g_RspPlugin); |
| 267 | |
| 268 | return (*ConfigSaveFile)(); |
| 269 | } |
| 270 | |
| 271 | /********************************************************************************************************* |
| 272 | * Command-line parsing |
| 273 | */ |
| 274 | |
| 275 | static void printUsage(const char *progname) |
| 276 | { |
| 277 | printf("Usage: %s [parameters] [romfile]\n" |
| 278 | "\n" |
| 279 | "Parameters:\n" |
| 280 | " --noosd : disable onscreen display\n" |
| 281 | " --osd : enable onscreen display\n" |
| 282 | " --fullscreen : use fullscreen display mode\n" |
| 283 | " --windowed : use windowed display mode\n" |
| 284 | " --resolution (res) : display resolution (640x480, 800x600, 1024x768, etc)\n" |
| 285 | " --nospeedlimit : disable core speed limiter (should be used with dummy audio plugin)\n" |
| 286 | " --cheats (cheat-spec) : enable or list cheat codes for the given rom file\n" |
| 287 | " --corelib (filepath) : use core library (filepath) (can be only filename or full path)\n" |
| 288 | " --configdir (dir) : force configation directory to (dir); should contain mupen64plus.cfg\n" |
| 289 | " --datadir (dir) : search for shared data files (.ini files, languages, etc) in (dir)\n" |
| 290 | " --plugindir (dir) : search for plugins in (dir)\n" |
| 291 | " --sshotdir (dir) : set screenshot directory to (dir)\n" |
| 292 | " --gfx (plugin-spec) : use gfx plugin given by (plugin-spec)\n" |
| 293 | " --audio (plugin-spec) : use audio plugin given by (plugin-spec)\n" |
| 294 | " --input (plugin-spec) : use input plugin given by (plugin-spec)\n" |
| 295 | " --rsp (plugin-spec) : use rsp plugin given by (plugin-spec)\n" |
| 296 | " --emumode (mode) : set emu mode to: 0=Pure Interpreter 1=Interpreter 2=DynaRec\n" |
| 297 | " --testshots (list) : take screenshots at frames given in comma-separated (list), then quit\n" |
| 298 | " --set (param-spec) : set a configuration variable, format: ParamSection[ParamName]=Value\n" |
| 299 | " --core-compare-send : use the Core Comparison debugging feature, in data sending mode\n" |
| 300 | " --core-compare-recv : use the Core Comparison debugging feature, in data receiving mode\n" |
| 301 | " --nosaveoptions : do not save the given command-line options in configuration file\n" |
| 302 | " --verbose : print lots of information\n" |
| 303 | " --help : see this help message\n\n" |
| 304 | "(plugin-spec):\n" |
| 305 | " (pluginname) : filename (without path) of plugin to find in plugin directory\n" |
| 306 | " (pluginpath) : full path and filename of plugin\n" |
| 307 | " 'dummy' : use dummy plugin\n\n" |
| 308 | "(cheat-spec):\n" |
| 309 | " 'list' : show all of the available cheat codes\n" |
| 310 | " 'all' : enable all of the available cheat codes\n" |
| 311 | " (codelist) : a comma-separated list of cheat code numbers to enable,\n" |
| 312 | " with dashes to use code variables (ex 1-2 to use cheat 1 option 2)\n" |
| 313 | "\n", progname); |
| 314 | |
| 315 | return; |
| 316 | } |
| 317 | |
| 318 | static int SetConfigParameter(const char *ParamSpec) |
| 319 | { |
| 320 | char *ParsedString, *VarName, *VarValue=NULL; |
| 321 | m64p_handle ConfigSection; |
| 322 | m64p_type VarType; |
| 323 | m64p_error rval; |
| 324 | |
| 325 | if (ParamSpec == NULL) |
| 326 | { |
| 327 | DebugMessage(M64MSG_ERROR, "ParamSpec is NULL in SetConfigParameter()"); |
| 328 | return 1; |
| 329 | } |
| 330 | |
| 331 | /* make a copy of the input string */ |
| 332 | ParsedString = (char *) malloc(strlen(ParamSpec) + 1); |
| 333 | if (ParsedString == NULL) |
| 334 | { |
| 335 | DebugMessage(M64MSG_ERROR, "SetConfigParameter() couldn't allocate memory for temporary string."); |
| 336 | return 2; |
| 337 | } |
| 338 | strcpy(ParsedString, ParamSpec); |
| 339 | |
| 340 | /* parse it for the simple section[name]=value format */ |
| 341 | VarName = strchr(ParsedString, '['); |
| 342 | if (VarName != NULL) |
| 343 | { |
| 344 | *VarName++ = 0; |
| 345 | VarValue = strchr(VarName, ']'); |
| 346 | if (VarValue != NULL) |
| 347 | { |
| 348 | *VarValue++ = 0; |
| 349 | } |
| 350 | } |
| 351 | if (VarName == NULL || VarValue == NULL || *VarValue != '=') |
| 352 | { |
| 353 | DebugMessage(M64MSG_ERROR, "invalid (param-spec) '%s'", ParamSpec); |
| 354 | free(ParsedString); |
| 355 | return 3; |
| 356 | } |
| 357 | VarValue++; |
| 358 | |
| 359 | /* then set the value */ |
| 360 | rval = (*ConfigOpenSection)(ParsedString, &ConfigSection); |
| 361 | if (rval != M64ERR_SUCCESS) |
| 362 | { |
| 363 | DebugMessage(M64MSG_ERROR, "SetConfigParameter failed to open config section '%s'", ParsedString); |
| 364 | free(ParsedString); |
| 365 | return 4; |
| 366 | } |
| 367 | if ((*ConfigGetParameterType)(ConfigSection, VarName, &VarType) == M64ERR_SUCCESS) |
| 368 | { |
| 369 | switch(VarType) |
| 370 | { |
| 371 | int ValueInt; |
| 372 | float ValueFloat; |
| 373 | case M64TYPE_INT: |
| 374 | ValueInt = atoi(VarValue); |
| 375 | ConfigSetParameter(ConfigSection, VarName, M64TYPE_INT, &ValueInt); |
| 376 | break; |
| 377 | case M64TYPE_FLOAT: |
| 378 | ValueFloat = (float) atof(VarValue); |
| 379 | ConfigSetParameter(ConfigSection, VarName, M64TYPE_FLOAT, &ValueFloat); |
| 380 | break; |
| 381 | case M64TYPE_BOOL: |
| 382 | ValueInt = (int) (osal_insensitive_strcmp(VarValue, "true") == 0); |
| 383 | ConfigSetParameter(ConfigSection, VarName, M64TYPE_BOOL, &ValueInt); |
| 384 | break; |
| 385 | case M64TYPE_STRING: |
| 386 | ConfigSetParameter(ConfigSection, VarName, M64TYPE_STRING, VarValue); |
| 387 | break; |
| 388 | default: |
| 389 | DebugMessage(M64MSG_ERROR, "invalid VarType in SetConfigParameter()"); |
| 390 | return 5; |
| 391 | } |
| 392 | } |
| 393 | else |
| 394 | { |
| 395 | ConfigSetParameter(ConfigSection, VarName, M64TYPE_STRING, VarValue); |
| 396 | } |
| 397 | |
| 398 | free(ParsedString); |
| 399 | return 0; |
| 400 | } |
| 401 | |
| 402 | static int *ParseNumberList(const char *InputString, int *ValuesFound) |
| 403 | { |
| 404 | const char *str; |
| 405 | int *OutputList; |
| 406 | |
| 407 | /* count the number of integers in the list */ |
| 408 | int values = 1; |
| 409 | str = InputString; |
| 410 | while ((str = strchr(str, ',')) != NULL) |
| 411 | { |
| 412 | str++; |
| 413 | values++; |
| 414 | } |
| 415 | |
| 416 | /* create a list and populate it with the frame counter values at which to take screenshots */ |
| 417 | if ((OutputList = (int *) malloc(sizeof(int) * (values + 1))) != NULL) |
| 418 | { |
| 419 | int idx = 0; |
| 420 | str = InputString; |
| 421 | while (str != NULL) |
| 422 | { |
| 423 | OutputList[idx++] = atoi(str); |
| 424 | str = strchr(str, ','); |
| 425 | if (str != NULL) str++; |
| 426 | } |
| 427 | OutputList[idx] = 0; |
| 428 | } |
| 429 | |
| 430 | if (ValuesFound != NULL) |
| 431 | *ValuesFound = values; |
| 432 | return OutputList; |
| 433 | } |
| 434 | |
| 435 | static int ParseCommandLineInitial(int argc, const char **argv) |
| 436 | { |
| 437 | int i; |
| 438 | |
| 439 | /* look through commandline options */ |
| 440 | for (i = 1; i < argc; i++) |
| 441 | { |
| 442 | int ArgsLeft = argc - i - 1; |
| 443 | |
| 444 | if (strcmp(argv[i], "--corelib") == 0 && ArgsLeft >= 1) |
| 445 | { |
| 446 | l_CoreLibPath = argv[i+1]; |
| 447 | i++; |
| 448 | } |
| 449 | else if (strcmp(argv[i], "--configdir") == 0 && ArgsLeft >= 1) |
| 450 | { |
| 451 | l_ConfigDirPath = argv[i+1]; |
| 452 | i++; |
| 453 | } |
| 454 | else if (strcmp(argv[i], "--datadir") == 0 && ArgsLeft >= 1) |
| 455 | { |
| 456 | l_DataDirPath = argv[i+1]; |
| 457 | i++; |
| 458 | } |
| 459 | else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) |
| 460 | { |
| 461 | printUsage(argv[0]); |
| 462 | return 1; |
| 463 | } |
| 464 | } |
| 465 | |
| 466 | return 0; |
| 467 | } |
| 468 | |
| 469 | static m64p_error ParseCommandLineFinal(int argc, const char **argv) |
| 470 | { |
| 471 | int i; |
| 472 | |
| 473 | /* parse commandline options */ |
| 474 | for (i = 1; i < argc; i++) |
| 475 | { |
| 476 | int ArgsLeft = argc - i - 1; |
| 477 | if (strcmp(argv[i], "--noosd") == 0) |
| 478 | { |
| 479 | int Osd = 0; |
| 480 | (*ConfigSetParameter)(l_ConfigCore, "OnScreenDisplay", M64TYPE_BOOL, &Osd); |
| 481 | } |
| 482 | else if (strcmp(argv[i], "--osd") == 0) |
| 483 | { |
| 484 | int Osd = 1; |
| 485 | (*ConfigSetParameter)(l_ConfigCore, "OnScreenDisplay", M64TYPE_BOOL, &Osd); |
| 486 | } |
| 487 | else if (strcmp(argv[i], "--fullscreen") == 0) |
| 488 | { |
| 489 | int Fullscreen = 1; |
| 490 | (*ConfigSetParameter)(l_ConfigVideo, "Fullscreen", M64TYPE_BOOL, &Fullscreen); |
| 491 | } |
| 492 | else if (strcmp(argv[i], "--windowed") == 0) |
| 493 | { |
| 494 | int Fullscreen = 0; |
| 495 | (*ConfigSetParameter)(l_ConfigVideo, "Fullscreen", M64TYPE_BOOL, &Fullscreen); |
| 496 | } |
| 497 | else if (strcmp(argv[i], "--nospeedlimit") == 0) |
| 498 | { |
| 499 | int EnableSpeedLimit = 0; |
| 500 | if (g_CoreAPIVersion < 0x020001) |
| 501 | DebugMessage(M64MSG_WARNING, "core library doesn't support --nospeedlimit"); |
| 502 | else |
| 503 | { |
| 504 | if ((*CoreDoCommand)(M64CMD_CORE_STATE_SET, M64CORE_SPEED_LIMITER, &EnableSpeedLimit) != M64ERR_SUCCESS) |
| 505 | DebugMessage(M64MSG_ERROR, "core gave error while setting --nospeedlimit option"); |
| 506 | } |
| 507 | } |
| 508 | else if ((strcmp(argv[i], "--corelib") == 0 || strcmp(argv[i], "--configdir") == 0 || |
| 509 | strcmp(argv[i], "--datadir") == 0) && ArgsLeft >= 1) |
| 510 | { /* these are handled in ParseCommandLineInitial */ |
| 511 | i++; |
| 512 | } |
| 513 | else if (strcmp(argv[i], "--resolution") == 0 && ArgsLeft >= 1) |
| 514 | { |
| 515 | const char *res = argv[i+1]; |
| 516 | int xres, yres; |
| 517 | i++; |
| 518 | if (sscanf(res, "%ix%i", &xres, &yres) != 2) |
| 519 | DebugMessage(M64MSG_WARNING, "couldn't parse resolution '%s'", res); |
| 520 | else |
| 521 | { |
| 522 | (*ConfigSetParameter)(l_ConfigVideo, "ScreenWidth", M64TYPE_INT, &xres); |
| 523 | (*ConfigSetParameter)(l_ConfigVideo, "ScreenHeight", M64TYPE_INT, &yres); |
| 524 | } |
| 525 | } |
| 526 | else if (strcmp(argv[i], "--cheats") == 0 && ArgsLeft >= 1) |
| 527 | { |
| 528 | if (strcmp(argv[i+1], "all") == 0) |
| 529 | l_CheatMode = CHEAT_ALL; |
| 530 | else if (strcmp(argv[i+1], "list") == 0) |
| 531 | l_CheatMode = CHEAT_SHOW_LIST; |
| 532 | else |
| 533 | { |
| 534 | l_CheatMode = CHEAT_LIST; |
| 535 | l_CheatNumList = (char*) argv[i+1]; |
| 536 | } |
| 537 | i++; |
| 538 | } |
| 539 | else if (strcmp(argv[i], "--plugindir") == 0 && ArgsLeft >= 1) |
| 540 | { |
| 541 | g_PluginDir = argv[i+1]; |
| 542 | i++; |
| 543 | } |
| 544 | else if (strcmp(argv[i], "--sshotdir") == 0 && ArgsLeft >= 1) |
| 545 | { |
| 546 | (*ConfigSetParameter)(l_ConfigCore, "ScreenshotPath", M64TYPE_STRING, argv[i+1]); |
| 547 | i++; |
| 548 | } |
| 549 | else if (strcmp(argv[i], "--gfx") == 0 && ArgsLeft >= 1) |
| 550 | { |
| 551 | g_GfxPlugin = argv[i+1]; |
| 552 | i++; |
| 553 | } |
| 554 | else if (strcmp(argv[i], "--audio") == 0 && ArgsLeft >= 1) |
| 555 | { |
| 556 | g_AudioPlugin = argv[i+1]; |
| 557 | i++; |
| 558 | } |
| 559 | else if (strcmp(argv[i], "--input") == 0 && ArgsLeft >= 1) |
| 560 | { |
| 561 | g_InputPlugin = argv[i+1]; |
| 562 | i++; |
| 563 | } |
| 564 | else if (strcmp(argv[i], "--rsp") == 0 && ArgsLeft >= 1) |
| 565 | { |
| 566 | g_RspPlugin = argv[i+1]; |
| 567 | i++; |
| 568 | } |
| 569 | else if (strcmp(argv[i], "--emumode") == 0 && ArgsLeft >= 1) |
| 570 | { |
| 571 | int emumode = atoi(argv[i+1]); |
| 572 | i++; |
| 573 | if (emumode < 0 || emumode > 2) |
| 574 | { |
| 575 | DebugMessage(M64MSG_WARNING, "invalid --emumode value '%i'", emumode); |
| 576 | continue; |
| 577 | } |
| 578 | if (emumode == 2 && !(g_CoreCapabilities & M64CAPS_DYNAREC)) |
| 579 | { |
| 580 | DebugMessage(M64MSG_WARNING, "Emulator core doesn't support Dynamic Recompiler."); |
| 581 | emumode = 1; |
| 582 | } |
| 583 | (*ConfigSetParameter)(l_ConfigCore, "R4300Emulator", M64TYPE_INT, &emumode); |
| 584 | } |
| 585 | else if (strcmp(argv[i], "--testshots") == 0 && ArgsLeft >= 1) |
| 586 | { |
| 587 | l_TestShotList = ParseNumberList(argv[i+1], NULL); |
| 588 | i++; |
| 589 | } |
| 590 | else if (strcmp(argv[i], "--set") == 0 && ArgsLeft >= 1) |
| 591 | { |
| 592 | if (SetConfigParameter(argv[i+1]) != 0) |
| 593 | return M64ERR_INPUT_INVALID; |
| 594 | i++; |
| 595 | } |
| 596 | else if (strcmp(argv[i], "--core-compare-send") == 0) |
| 597 | { |
| 598 | l_CoreCompareMode = 1; |
| 599 | } |
| 600 | else if (strcmp(argv[i], "--core-compare-recv") == 0) |
| 601 | { |
| 602 | l_CoreCompareMode = 2; |
| 603 | } |
| 604 | else if (strcmp(argv[i], "--nosaveoptions") == 0) |
| 605 | { |
| 606 | l_SaveOptions = 0; |
| 607 | } |
| 608 | else if (ArgsLeft == 0) |
| 609 | { |
| 610 | /* this is the last arg, it should be a ROM filename */ |
| 611 | l_ROMFilepath = argv[i]; |
| 612 | return M64ERR_SUCCESS; |
| 613 | } |
| 614 | else if (strcmp(argv[i], "--verbose") == 0) |
| 615 | { |
| 616 | g_Verbose = 1; |
| 617 | } |
| 618 | else |
| 619 | { |
| 620 | DebugMessage(M64MSG_WARNING, "unrecognized command-line parameter '%s'", argv[i]); |
| 621 | } |
| 622 | /* continue argv loop */ |
| 623 | } |
| 624 | |
| 625 | /* missing ROM filepath */ |
| 626 | DebugMessage(M64MSG_ERROR, "no ROM filepath given"); |
| 627 | return M64ERR_INPUT_INVALID; |
| 628 | } |
| 629 | |
| 630 | /********************************************************************************************************* |
| 631 | * main function |
| 632 | */ |
| 633 | int main(int argc, char *argv[]) |
| 634 | { |
| 635 | int i; |
| 636 | |
| 637 | printf(" __ __ __ _ _ ____ _ \n"); |
| 638 | printf("| \\/ |_ _ _ __ ___ _ __ / /_ | || | | _ \\| |_ _ ___ \n"); |
| 639 | printf("| |\\/| | | | | '_ \\ / _ \\ '_ \\| '_ \\| || |_| |_) | | | | / __| \n"); |
| 640 | printf("| | | | |_| | |_) | __/ | | | (_) |__ _| __/| | |_| \\__ \\ \n"); |
| 641 | printf("|_| |_|\\__,_| .__/ \\___|_| |_|\\___/ |_| |_| |_|\\__,_|___/ \n"); |
| 642 | printf(" |_| http://code.google.com/p/mupen64plus/ \n"); |
| 643 | printf("%s Version %i.%i.%i\n\n", CONSOLE_UI_NAME, VERSION_PRINTF_SPLIT(CONSOLE_UI_VERSION)); |
| 644 | |
| 645 | /* bootstrap some special parameters from the command line */ |
| 646 | if (ParseCommandLineInitial(argc, (const char **) argv) != 0) |
| 647 | return 1; |
| 648 | |
| 649 | /* load the Mupen64Plus core library */ |
| 650 | if (AttachCoreLib(l_CoreLibPath) != M64ERR_SUCCESS) |
| 651 | return 2; |
| 652 | |
| 653 | /* start the Mupen64Plus core library, load the configuration file */ |
| 654 | #ifdef PAULSCODE |
| 655 | m64p_error rval = (*CoreStartup)( CORE_API_VERSION, l_ConfigDirPath, l_DataDirPath, "Core", DebugCallback, NULL, StateCallback ); |
| 656 | #else |
| 657 | m64p_error rval = (*CoreStartup)(CORE_API_VERSION, l_ConfigDirPath, l_DataDirPath, "Core", DebugCallback, NULL, NULL); |
| 658 | #endif |
| 659 | if (rval != M64ERR_SUCCESS) |
| 660 | { |
| 661 | DebugMessage(M64MSG_ERROR, "couldn't start Mupen64Plus core library."); |
| 662 | DetachCoreLib(); |
| 663 | return 3; |
| 664 | } |
| 665 | |
| 666 | /* Open configuration sections */ |
| 667 | rval = OpenConfigurationHandles(); |
| 668 | if (rval != M64ERR_SUCCESS) |
| 669 | { |
| 670 | (*CoreShutdown)(); |
| 671 | DetachCoreLib(); |
| 672 | return 4; |
| 673 | } |
| 674 | |
| 675 | /* parse command-line options */ |
| 676 | rval = ParseCommandLineFinal(argc, (const char **) argv); |
| 677 | if (rval != M64ERR_SUCCESS) |
| 678 | { |
| 679 | (*CoreShutdown)(); |
| 680 | DetachCoreLib(); |
| 681 | return 5; |
| 682 | } |
| 683 | |
| 684 | /* Handle the core comparison feature */ |
| 685 | if (l_CoreCompareMode != 0 && !(g_CoreCapabilities & M64CAPS_CORE_COMPARE)) |
| 686 | { |
| 687 | DebugMessage(M64MSG_ERROR, "can't use --core-compare feature with this Mupen64Plus core library."); |
| 688 | DetachCoreLib(); |
| 689 | return 6; |
| 690 | } |
| 691 | compare_core_init(l_CoreCompareMode); |
| 692 | |
| 693 | /* save the given command-line options in configuration file if requested */ |
| 694 | if (l_SaveOptions) |
| 695 | SaveConfigurationOptions(); |
| 696 | |
| 697 | /* load ROM image */ |
| 698 | FILE *fPtr = fopen(l_ROMFilepath, "rb"); |
| 699 | if (fPtr == NULL) |
| 700 | { |
| 701 | DebugMessage(M64MSG_ERROR, "couldn't open ROM file '%s' for reading.", l_ROMFilepath); |
| 702 | (*CoreShutdown)(); |
| 703 | DetachCoreLib(); |
| 704 | return 7; |
| 705 | } |
| 706 | |
| 707 | /* get the length of the ROM, allocate memory buffer, load it from disk */ |
| 708 | long romlength = 0; |
| 709 | fseek(fPtr, 0L, SEEK_END); |
| 710 | romlength = ftell(fPtr); |
| 711 | fseek(fPtr, 0L, SEEK_SET); |
| 712 | unsigned char *ROM_buffer = (unsigned char *) malloc(romlength); |
| 713 | if (ROM_buffer == NULL) |
| 714 | { |
| 715 | DebugMessage(M64MSG_ERROR, "couldn't allocate %li-byte buffer for ROM image file '%s'.", romlength, l_ROMFilepath); |
| 716 | fclose(fPtr); |
| 717 | (*CoreShutdown)(); |
| 718 | DetachCoreLib(); |
| 719 | return 8; |
| 720 | } |
| 721 | else if (fread(ROM_buffer, 1, romlength, fPtr) != romlength) |
| 722 | { |
| 723 | DebugMessage(M64MSG_ERROR, "couldn't read %li bytes from ROM image file '%s'.", romlength, l_ROMFilepath); |
| 724 | free(ROM_buffer); |
| 725 | fclose(fPtr); |
| 726 | (*CoreShutdown)(); |
| 727 | DetachCoreLib(); |
| 728 | return 9; |
| 729 | } |
| 730 | fclose(fPtr); |
| 731 | |
| 732 | /* Try to load the ROM image into the core */ |
| 733 | if ((*CoreDoCommand)(M64CMD_ROM_OPEN, (int) romlength, ROM_buffer) != M64ERR_SUCCESS) |
| 734 | { |
| 735 | DebugMessage(M64MSG_ERROR, "core failed to open ROM image file '%s'.", l_ROMFilepath); |
| 736 | free(ROM_buffer); |
| 737 | (*CoreShutdown)(); |
| 738 | DetachCoreLib(); |
| 739 | return 10; |
| 740 | } |
| 741 | free(ROM_buffer); /* the core copies the ROM image, so we can release this buffer immediately */ |
| 742 | |
| 743 | /* handle the cheat codes */ |
| 744 | CheatStart(l_CheatMode, l_CheatNumList); |
| 745 | if (l_CheatMode == CHEAT_SHOW_LIST) |
| 746 | { |
| 747 | (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); |
| 748 | (*CoreShutdown)(); |
| 749 | DetachCoreLib(); |
| 750 | return 11; |
| 751 | } |
| 752 | |
| 753 | /* search for and load plugins */ |
| 754 | rval = PluginSearchLoad(l_ConfigUI); |
| 755 | if (rval != M64ERR_SUCCESS) |
| 756 | { |
| 757 | (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); |
| 758 | (*CoreShutdown)(); |
| 759 | DetachCoreLib(); |
| 760 | return 12; |
| 761 | } |
| 762 | |
| 763 | /* attach plugins to core */ |
| 764 | for (i = 0; i < 4; i++) |
| 765 | { |
| 766 | if ((*CoreAttachPlugin)(g_PluginMap[i].type, g_PluginMap[i].handle) != M64ERR_SUCCESS) |
| 767 | { |
| 768 | DebugMessage(M64MSG_ERROR, "core error while attaching %s plugin.", g_PluginMap[i].name); |
| 769 | (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); |
| 770 | (*CoreShutdown)(); |
| 771 | DetachCoreLib(); |
| 772 | return 13; |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | /* set up Frame Callback if --testshots is enabled */ |
| 777 | if (l_TestShotList != NULL) |
| 778 | { |
| 779 | if ((*CoreDoCommand)(M64CMD_SET_FRAME_CALLBACK, 0, FrameCallback) != M64ERR_SUCCESS) |
| 780 | { |
| 781 | DebugMessage(M64MSG_WARNING, "couldn't set frame callback, so --testshots won't work."); |
| 782 | } |
| 783 | } |
| 784 | |
| 785 | /* run the game */ |
| 786 | #ifdef PAULSCODE |
| 787 | // paulscode: workaround for broken M64CMD_RESET. Set do_Start = 1 before M64CMD_STOP to reset the emulator. |
| 788 | while( do_Start ) |
| 789 | { |
| 790 | do_Start = 0; |
| 791 | (*CoreDoCommand)(M64CMD_EXECUTE, 0, NULL); |
| 792 | } |
| 793 | #else |
| 794 | (*CoreDoCommand)(M64CMD_EXECUTE, 0, NULL); |
| 795 | #endif |
| 796 | |
| 797 | /* detach plugins from core and unload them */ |
| 798 | for (i = 0; i < 4; i++) |
| 799 | (*CoreDetachPlugin)(g_PluginMap[i].type); |
| 800 | PluginUnload(); |
| 801 | |
| 802 | /* close the ROM image */ |
| 803 | (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); |
| 804 | |
| 805 | /* save the configuration file again if --nosaveoptions was not specified, to keep any updated parameters from the core/plugins */ |
| 806 | if (l_SaveOptions) |
| 807 | SaveConfigurationOptions(); |
| 808 | |
| 809 | /* Shut down and release the Core library */ |
| 810 | (*CoreShutdown)(); |
| 811 | DetachCoreLib(); |
| 812 | |
| 813 | /* free allocated memory */ |
| 814 | if (l_TestShotList != NULL) |
| 815 | free(l_TestShotList); |
| 816 | |
| 817 | return 0; |
| 818 | } |
| 819 | |