PageRenderTime 74ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main.c

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