PageRenderTime 162ms CodeModel.GetById 57ms app.highlight 68ms RepoModel.GetById 21ms app.codeStats 0ms

/src/main.c

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