PageRenderTime 52ms CodeModel.GetById 12ms app.highlight 28ms RepoModel.GetById 7ms app.codeStats 0ms

/src/main.c

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