/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-ui-console - main.c * * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * * Copyright (C) 2007-2009 Richard42 * * Copyright (C) 2008 Ebenblues Nmn Okaygo Tillin9 * * Copyright (C) 2002 Hacktarux * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* This is the main application entry point for the console-only front-end * for Mupen64Plus v2.0. */ #include #include #include // The mac version of SDL requires inclusion of SDL_main in the executable #ifdef __APPLE__ #include #endif #include "cheat.h" #include "main.h" #include "plugin.h" #include "version.h" #include "core_interface.h" #include "compare_core.h" #include "osal_preproc.h" /** global variables **/ int g_Verbose = 0; /** static (local) variables **/ static m64p_handle l_ConfigCore = NULL; static m64p_handle l_ConfigVideo = NULL; static m64p_handle l_ConfigUI = NULL; static const char *l_CoreLibPath = NULL; static const char *l_ConfigDirPath = NULL; static const char *l_ROMFilepath = NULL; // filepath of ROM to load & run at startup #if defined(SHAREDIR) static const char *l_DataDirPath = SHAREDIR; #else static const char *l_DataDirPath = NULL; #endif static int *l_TestShotList = NULL; // list of screenshots to take for regression test support static int l_TestShotIdx = 0; // index of next screenshot frame in list static int l_SaveOptions = 0; // save command-line options in configuration file static int l_CoreCompareMode = 0; // 0 = disable, 1 = send, 2 = receive static eCheatMode l_CheatMode = CHEAT_DISABLE; static int *l_CheatNumList = NULL; static int l_CheatListLength = 0; /********************************************************************************************************* * Callback functions from the core */ void DebugCallback(void *Context, int level, const char *message) { if (level <= 1) printf("%s Error: %s\n", (const char *) Context, message); else if (level == 2) printf("%s Warning: %s\n", (const char *) Context, message); else if (level == 3 || (level == 5 && g_Verbose)) printf("%s: %s\n", (const char *) Context, message); else if (level == 4) printf("%s Status: %s\n", (const char *) Context, message); /* ignore the verbose info for now */ } static void FrameCallback(unsigned int FrameIndex) { // take a screenshot if we need to if (l_TestShotList != NULL) { int nextshot = l_TestShotList[l_TestShotIdx]; if (nextshot == FrameIndex) { (*CoreDoCommand)(M64CMD_TAKE_NEXT_SCREENSHOT, 0, NULL); /* tell the core take a screenshot */ // advance list index to next screenshot frame number. If it's 0, then quit l_TestShotIdx++; } else if (nextshot == 0) { (*CoreDoCommand)(M64CMD_STOP, 0, NULL); /* tell the core to shut down ASAP */ free(l_TestShotList); l_TestShotList = NULL; } } } /********************************************************************************************************* * Configuration handling */ static m64p_error OpenConfigurationHandles(void) { m64p_error rval; /* Open Configuration sections for core library and console User Interface */ rval = (*ConfigOpenSection)("Core", &l_ConfigCore); if (rval != M64ERR_SUCCESS) { fprintf(stderr, "Error: failed to open 'Core' configuration section\n"); return rval; } rval = (*ConfigOpenSection)("Video-General", &l_ConfigVideo); if (rval != M64ERR_SUCCESS) { fprintf(stderr, "Error: failed to open 'Video-General' configuration section\n"); return rval; } rval = (*ConfigOpenSection)("UI-Console", &l_ConfigUI); if (rval != M64ERR_SUCCESS) { fprintf(stderr, "Error: failed to open 'UI-Console' configuration section\n"); return rval; } /* Set default values for my Config parameters */ (*ConfigSetDefaultString)(l_ConfigUI, "PluginDir", OSAL_CURRENT_DIR, "Directory in which to search for plugins"); (*ConfigSetDefaultString)(l_ConfigUI, "VideoPlugin", "m64p_video_rice" OSAL_DLL_EXTENSION, "Filename of video plugin"); (*ConfigSetDefaultString)(l_ConfigUI, "AudioPlugin", "m64p_audio_jttl" OSAL_DLL_EXTENSION, "Filename of audio plugin"); (*ConfigSetDefaultString)(l_ConfigUI, "InputPlugin", "m64p_input_blight" OSAL_DLL_EXTENSION, "Filename of input plugin"); (*ConfigSetDefaultString)(l_ConfigUI, "RspPlugin", "m64p_rsp_hle" OSAL_DLL_EXTENSION, "Filename of RSP plugin"); return M64ERR_SUCCESS; } static m64p_error SaveConfigurationOptions(void) { /* if shared data directory was given on the command line, write it into the config file */ if (l_DataDirPath != NULL) (*ConfigSetParameter)(l_ConfigCore, "SharedDataPath", M64TYPE_STRING, l_DataDirPath); /* if any plugin filepaths were given on the command line, write them into the config file */ if (g_PluginDir != NULL) (*ConfigSetParameter)(l_ConfigUI, "PluginDir", M64TYPE_STRING, g_PluginDir); if (g_GfxPlugin != NULL) (*ConfigSetParameter)(l_ConfigUI, "VideoPlugin", M64TYPE_STRING, g_GfxPlugin); if (g_AudioPlugin != NULL) (*ConfigSetParameter)(l_ConfigUI, "AudioPlugin", M64TYPE_STRING, g_AudioPlugin); if (g_InputPlugin != NULL) (*ConfigSetParameter)(l_ConfigUI, "InputPlugin", M64TYPE_STRING, g_InputPlugin); if (g_RspPlugin != NULL) (*ConfigSetParameter)(l_ConfigUI, "RspPlugin", M64TYPE_STRING, g_RspPlugin); return (*ConfigSaveFile)(); } /********************************************************************************************************* * Command-line parsing */ static void printUsage(const char *progname) { printf("Usage: %s [parameters] [romfile]\n" "\n" "Parameters:\n" " --noosd : disable onscreen display\n" " --osd : enable onscreen display\n" " --fullscreen : use fullscreen display mode\n" " --windowed : use windowed display mode\n" " --resolution (res) : display resolution (640x480, 800x600, 1024x768, etc)\n" " --cheats (cheat-spec) : enable or list cheat codes for the given rom file\n" " --corelib (filepath) : use core library (filepath) (can be only filename or full path)\n" " --configdir (dir) : force configation directory to (dir); should contain mupen64plus.conf\n" " --datadir (dir) : search for shared data files (.ini files, languages, etc) in (dir)\n" " --plugindir (dir) : search for plugins in (dir)\n" " --sshotdir (dir) : set screenshot directory to (dir)\n" " --gfx (plugin-spec) : use gfx plugin given by (plugin-spec)\n" " --audio (plugin-spec) : use audio plugin given by (plugin-spec)\n" " --input (plugin-spec) : use input plugin given by (plugin-spec)\n" " --rsp (plugin-spec) : use rsp plugin given by (plugin-spec)\n" " --emumode (mode) : set emu mode to: 0=Pure Interpreter 1=Interpreter 2=DynaRec\n" " --testshots (list) : take screenshots at frames given in comma-separated (list), then quit\n" " --core-compare-send : use the Core Comparison debugging feature, in data sending mode\n" " --core-compare-recv : use the Core Comparison debugging feature, in data receiving mode\n" " --saveoptions : save the given command-line options in configuration file for future\n" " --verbose : print lots of information\n" " --help : see this help message\n\n" "(plugin-spec):\n" " (pluginname) : filename (without path) of plugin to find in plugin directory\n" " (pluginpath) : full path and filename of plugin\n" " 'dummy' : use dummy plugin\n\n" "(cheat-spec):\n" " 'list' : show all of the available cheat codes\n" " 'all' : enable all of the available cheat codes\n" " (codelist) : a comma-separated list of cheat code numbers to enable\n" "\n", progname); return; } static int *ParseNumberList(const char *InputString, int *ValuesFound) { const char *str; int *OutputList; /* count the number of integers in the list */ int values = 1; str = InputString; while ((str = strchr(str, ',')) != NULL) { str++; values++; } /* create a list and populate it with the frame counter values at which to take screenshots */ if ((OutputList = (int *) malloc(sizeof(int) * (values + 1))) != NULL) { int idx = 0; str = InputString; while (str != NULL) { OutputList[idx++] = atoi(str); str = strchr(str, ','); if (str != NULL) str++; } OutputList[idx] = 0; } if (ValuesFound != NULL) *ValuesFound = values; return OutputList; } static int ParseCommandLineInitial(int argc, const char **argv) { int i; /* look through commandline options */ for (i = 1; i < argc; i++) { int ArgsLeft = argc - i - 1; if (strcmp(argv[i], "--corelib") == 0 && ArgsLeft >= 1) { l_CoreLibPath = argv[i+1]; i++; } else if (strcmp(argv[i], "--configdir") == 0 && ArgsLeft >= 1) { l_ConfigDirPath = argv[i+1]; i++; } else if (strcmp(argv[i], "--datadir") == 0 && ArgsLeft >= 1) { l_DataDirPath = argv[i+1]; i++; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { printUsage(argv[0]); return 1; } } return 0; } static m64p_error ParseCommandLineFinal(int argc, const char **argv) { int i; /* parse commandline options */ for (i = 1; i < argc; i++) { int ArgsLeft = argc - i - 1; if (strcmp(argv[i], "--noosd") == 0) { int Osd = 0; (*ConfigSetParameter)(l_ConfigCore, "OnScreenDisplay", M64TYPE_BOOL, &Osd); } else if (strcmp(argv[i], "--osd") == 0) { int Osd = 1; (*ConfigSetParameter)(l_ConfigCore, "OnScreenDisplay", M64TYPE_BOOL, &Osd); } else if (strcmp(argv[i], "--fullscreen") == 0) { int Fullscreen = 1; (*ConfigSetParameter)(l_ConfigVideo, "Fullscreen", M64TYPE_BOOL, &Fullscreen); } else if (strcmp(argv[i], "--windowed") == 0) { int Fullscreen = 0; (*ConfigSetParameter)(l_ConfigVideo, "Fullscreen", M64TYPE_BOOL, &Fullscreen); } else if ((strcmp(argv[i], "--corelib") == 0 || strcmp(argv[i], "--configdir") == 0 || strcmp(argv[i], "--datadir") == 0) && ArgsLeft >= 1) { /* these are handled in ParseCommandLineInitial */ i++; } else if (strcmp(argv[i], "--resolution") == 0 && ArgsLeft >= 1) { const char *res = argv[i+1]; int xres, yres; i++; if (sscanf(res, "%ix%i", &xres, &yres) != 2) fprintf(stderr, "Warning: couldn't parse resolution '%s'\n", res); else { (*ConfigSetParameter)(l_ConfigVideo, "ScreenWidth", M64TYPE_INT, &xres); (*ConfigSetParameter)(l_ConfigVideo, "ScreenHeight", M64TYPE_INT, &yres); } } else if (strcmp(argv[i], "--cheats") == 0 && ArgsLeft >= 1) { if (strcmp(argv[i+1], "all") == 0) l_CheatMode = CHEAT_ALL; else if (strcmp(argv[i+1], "list") == 0) l_CheatMode = CHEAT_SHOW_LIST; else { l_CheatMode = CHEAT_LIST; l_CheatNumList = ParseNumberList(argv[i+1], &l_CheatListLength); } i++; } else if (strcmp(argv[i], "--plugindir") == 0 && ArgsLeft >= 1) { g_PluginDir = argv[i+1]; i++; } else if (strcmp(argv[i], "--sshotdir") == 0 && ArgsLeft >= 1) { (*ConfigSetParameter)(l_ConfigCore, "ScreenshotPath", M64TYPE_STRING, argv[i+1]); i++; } else if (strcmp(argv[i], "--gfx") == 0 && ArgsLeft >= 1) { g_GfxPlugin = argv[i+1]; i++; } else if (strcmp(argv[i], "--audio") == 0 && ArgsLeft >= 1) { g_AudioPlugin = argv[i+1]; i++; } else if (strcmp(argv[i], "--input") == 0 && ArgsLeft >= 1) { g_InputPlugin = argv[i+1]; i++; } else if (strcmp(argv[i], "--rsp") == 0 && ArgsLeft >= 1) { g_RspPlugin = argv[i+1]; i++; } else if (strcmp(argv[i], "--emumode") == 0 && ArgsLeft >= 1) { int emumode = atoi(argv[i+1]); (*ConfigSetParameter)(l_ConfigCore, "R4300Emulator", M64TYPE_INT, &emumode); i++; } else if (strcmp(argv[i], "--testshots") == 0 && ArgsLeft >= 1) { l_TestShotList = ParseNumberList(argv[i+1], NULL); i++; } else if (strcmp(argv[i], "--core-compare-send") == 0) { l_CoreCompareMode = 1; } else if (strcmp(argv[i], "--core-compare-recv") == 0) { l_CoreCompareMode = 2; } else if (strcmp(argv[i], "--saveoptions") == 0) { l_SaveOptions = 1; } else if (ArgsLeft == 0) { /* this is the last arg, it should be a ROM filename */ l_ROMFilepath = argv[i]; return M64ERR_SUCCESS; } else if (strcmp(argv[i], "--verbose") == 0) { g_Verbose = 1; } else { fprintf(stderr, "Warning: unrecognized command-line parameter '%s'\n", argv[i]); } /* continue argv loop */ } /* missing ROM filepath */ fprintf(stderr, "Error: no ROM filepath given\n"); exit(2); return M64ERR_INTERNAL; } /********************************************************************************************************* * main function */ int main(int argc, char *argv[]) { int i; printf(" __ __ __ _ _ ____ _ \n"); printf("| \\/ |_ _ _ __ ___ _ __ / /_ | || | | _ \\| |_ _ ___ \n"); printf("| |\\/| | | | | '_ \\ / _ \\ '_ \\| '_ \\| || |_| |_) | | | | / __| \n"); printf("| | | | |_| | |_) | __/ | | | (_) |__ _| __/| | |_| \\__ \\ \n"); printf("|_| |_|\\__,_| .__/ \\___|_| |_|\\___/ |_| |_| |_|\\__,_|___/ \n"); printf(" |_| http://code.google.com/p/mupen64plus/ \n"); printf("%s Version %i.%i.%i\n\n", CONSOLE_UI_NAME, VERSION_PRINTF_SPLIT(CONSOLE_UI_VERSION)); /* bootstrap some special parameters from the command line */ if (ParseCommandLineInitial(argc, (const char **) argv) != 0) return 1; /* load the Mupen64Plus core library */ if (AttachCoreLib(l_CoreLibPath) != M64ERR_SUCCESS) return 2; /* start the Mupen64Plus core library, load the configuration file */ m64p_error rval = (*CoreStartup)(CONSOLE_API_VERSION, l_ConfigDirPath, l_DataDirPath, "Core", DebugCallback, NULL, NULL); if (rval != M64ERR_SUCCESS) { printf("UI-console: error starting Mupen64Plus core library.\n"); DetachCoreLib(); return 3; } /* Open configuration sections */ rval = OpenConfigurationHandles(); if (rval != M64ERR_SUCCESS) { (*CoreShutdown)(); DetachCoreLib(); return 4; } /* parse command-line options */ rval = ParseCommandLineFinal(argc, (const char **) argv); if (rval != M64ERR_SUCCESS) { (*CoreShutdown)(); DetachCoreLib(); return 5; } /* Handle the core comparison feature */ if (l_CoreCompareMode != 0 && !(g_CoreCapabilities & M64CAPS_CORE_COMPARE)) { printf("UI-console: can't use --core-compare feature with this Mupen64Plus core library.\n"); DetachCoreLib(); return 3; } compare_core_init(l_CoreCompareMode); /* save the given command-line options in configuration file if requested */ if (l_SaveOptions) SaveConfigurationOptions(); /* load ROM image */ FILE *fPtr = fopen(l_ROMFilepath, "rb"); if (fPtr == NULL) { fprintf(stderr, "Error: couldn't open ROM file '%s' for reading.\n", l_ROMFilepath); (*CoreShutdown)(); DetachCoreLib(); return 6; } /* get the length of the ROM, allocate memory buffer, load it from disk */ long romlength = 0; fseek(fPtr, 0L, SEEK_END); romlength = ftell(fPtr); fseek(fPtr, 0L, SEEK_SET); unsigned char *ROM_buffer = (unsigned char *) malloc(romlength); if (ROM_buffer == NULL) { fprintf(stderr, "Error: couldn't allocate %li-byte buffer for ROM image file '%s'.\n", romlength, l_ROMFilepath); fclose(fPtr); (*CoreShutdown)(); DetachCoreLib(); return 7; } else if (fread(ROM_buffer, 1, romlength, fPtr) != romlength) { fprintf(stderr, "Error: couldn't read %li bytes from ROM image file '%s'.\n", romlength, l_ROMFilepath); free(ROM_buffer); fclose(fPtr); (*CoreShutdown)(); DetachCoreLib(); return 8; } fclose(fPtr); /* Try to load the ROM image into the core */ if ((*CoreDoCommand)(M64CMD_ROM_OPEN, (int) romlength, ROM_buffer) != M64ERR_SUCCESS) { fprintf(stderr, "Error: core failed to open ROM image file '%s'.\n", l_ROMFilepath); free(ROM_buffer); (*CoreShutdown)(); DetachCoreLib(); return 9; } free(ROM_buffer); /* the core copies the ROM image, so we can release this buffer immediately */ /* handle the cheat codes */ CheatStart(l_CheatMode, l_CheatNumList, l_CheatListLength); if (l_CheatMode == CHEAT_SHOW_LIST) { (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); (*CoreShutdown)(); DetachCoreLib(); return 10; } /* search for and load plugins */ rval = PluginSearchLoad(l_ConfigUI); if (rval != M64ERR_SUCCESS) { (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); (*CoreShutdown)(); DetachCoreLib(); return 11; } /* attach plugins to core */ for (i = 0; i < 4; i++) { if ((*CoreAttachPlugin)(g_PluginMap[i].type, g_PluginMap[i].handle) != M64ERR_SUCCESS) { fprintf(stderr, "UI-Console: error from core while attaching %s plugin.\n", g_PluginMap[i].name); (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); (*CoreShutdown)(); DetachCoreLib(); return 12; } } /* set up Frame Callback if --testshots is enabled */ if (l_TestShotList != NULL) { if ((*CoreDoCommand)(M64CMD_SET_FRAME_CALLBACK, 0, FrameCallback) != M64ERR_SUCCESS) { fprintf(stderr, "UI-Console: warning: couldn't set frame callback, so --testshots won't work.\n"); } } /* run the game */ (*CoreDoCommand)(M64CMD_EXECUTE, 0, NULL); /* detach plugins from core and unload them */ for (i = 0; i < 4; i++) (*CoreDetachPlugin)(g_PluginMap[i].type); PluginUnload(); /* close the ROM image */ (*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL); /* save the configuration file again if --saveoptions was specified, to keep any updated parameters from the core/plugins */ if (l_SaveOptions) SaveConfigurationOptions(); /* Shut down and release the Core library */ (*CoreShutdown)(); DetachCoreLib(); /* free allocated memory */ if (l_TestShotList != NULL) free(l_TestShotList); if (l_CheatNumList != NULL) free(l_CheatNumList); return 0; }