PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

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