Added missing launcher
[mupen64plus-pandora.git] / source / front-end / src / main.c
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