5288f542 |
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 | |