PageRenderTime 6ms CodeModel.GetById 6ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 1ms

/src/main.c

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