1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus-core - api/frontend.c *
3 * Mupen64Plus homepage: https://mupen64plus.org/ *
4 * Copyright (C) 2012 CasualJames *
5 * Copyright (C) 2009 Richard Goedeken *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22
23/* This file contains the Core front-end functions which will be exported
24 * outside of the core library.
25 */
26
27#include <SDL.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <md5.h>
32
33#define M64P_CORE_PROTOTYPES 1
34#include "callbacks.h"
35#include "config.h"
36#include "m64p_config.h"
37#include "m64p_frontend.h"
38#include "m64p_types.h"
39#include "main/cheat.h"
40#include "main/eventloop.h"
41#include "main/main.h"
42#include "main/rom.h"
43#include "main/savestates.h"
44#include "main/util.h"
45#include "main/version.h"
46#include "main/workqueue.h"
47#include "main/screenshot.h"
48#include "plugin/plugin.h"
49#include "vidext.h"
50
51/* some local state variables */
52static int l_CoreInit = 0;
53static int l_ROMOpen = 0;
54
55/* functions exported outside of libmupen64plus to front-end application */
56EXPORT m64p_error CALL CoreStartup(int APIVersion, const char *ConfigPath, const char *DataPath, void *Context,
57 void (*DebugCallback)(void *, int, const char *), void *Context2,
58 void (*StateCallback)(void *, m64p_core_param, int))
59{
60 if (l_CoreInit)
61 return M64ERR_ALREADY_INIT;
62
63 /* very first thing is to set the callback functions for debug info and state changing*/
64 SetDebugCallback(DebugCallback, Context);
65 SetStateCallback(StateCallback, Context2);
66
67 /* check front-end's API version */
68 if ((APIVersion & 0xffff0000) != (FRONTEND_API_VERSION & 0xffff0000))
69 {
70 DebugMessage(M64MSG_ERROR, "CoreStartup(): Front-end (API version %i.%i.%i) is incompatible with this core (API %i.%i.%i)",
71 VERSION_PRINTF_SPLIT(APIVersion), VERSION_PRINTF_SPLIT(FRONTEND_API_VERSION));
72 return M64ERR_INCOMPATIBLE;
73 }
74
75 /* set up the default (dummy) plugins */
76 plugin_connect(M64PLUGIN_GFX, NULL);
77 plugin_connect(M64PLUGIN_AUDIO, NULL);
78 plugin_connect(M64PLUGIN_INPUT, NULL);
79 plugin_connect(M64PLUGIN_CORE, NULL);
80
81 savestates_init();
82
83 /* next, start up the configuration handling code by loading and parsing the config file */
84 if (ConfigInit(ConfigPath, DataPath) != M64ERR_SUCCESS)
85 return M64ERR_INTERNAL;
86
87 /* set default configuration parameter values for Core */
88 if (ConfigOpenSection("Core", &g_CoreConfig) != M64ERR_SUCCESS || g_CoreConfig == NULL)
89 return M64ERR_INTERNAL;
90
91 if (!main_set_core_defaults())
92 return M64ERR_INTERNAL;
93
94 /* allocate base memory */
95 g_mem_base = init_mem_base();
96 if (g_mem_base == NULL) {
97 return M64ERR_NO_MEMORY;
98 }
99
100 /* The ROM database contains MD5 hashes, goodnames, and some game-specific parameters */
101 romdatabase_open();
102
103 workqueue_init();
104
105 l_CoreInit = 1;
106 return M64ERR_SUCCESS;
107}
108
109EXPORT m64p_error CALL CoreShutdown(void)
110{
111 if (!l_CoreInit)
112 return M64ERR_NOT_INIT;
113
114 /* close down some core sub-systems */
115 romdatabase_close();
116 ConfigShutdown();
117 workqueue_shutdown();
118 savestates_deinit();
119
120 /* tell SDL to shut down */
121 SDL_Quit();
122
123 /* deallocate base memory */
124 release_mem_base(g_mem_base);
125 g_mem_base = NULL;
126
127 l_CoreInit = 0;
128 return M64ERR_SUCCESS;
129}
130
131EXPORT m64p_error CALL CoreAttachPlugin(m64p_plugin_type PluginType, m64p_dynlib_handle PluginLibHandle)
132{
133 m64p_error rval;
134
135 if (!l_CoreInit)
136 return M64ERR_NOT_INIT;
137 if (g_EmulatorRunning || !l_ROMOpen)
138 return M64ERR_INVALID_STATE;
139
140 rval = plugin_connect(PluginType, PluginLibHandle);
141 if (rval != M64ERR_SUCCESS)
142 return rval;
143
144 rval = plugin_start(PluginType);
145 if (rval != M64ERR_SUCCESS)
146 return rval;
147
148 return M64ERR_SUCCESS;
149}
150
151EXPORT m64p_error CALL CoreDetachPlugin(m64p_plugin_type PluginType)
152{
153 if (!l_CoreInit)
154 return M64ERR_NOT_INIT;
155 if (g_EmulatorRunning)
156 return M64ERR_INVALID_STATE;
157
158 return plugin_connect(PluginType, NULL);
159}
160
161EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *ParamPtr)
162{
163 m64p_error rval;
164 int keysym, keymod;
165
166 if (!l_CoreInit)
167 return M64ERR_NOT_INIT;
168
169 switch(Command)
170 {
171 case M64CMD_NOP:
172 return M64ERR_SUCCESS;
173 case M64CMD_ROM_OPEN:
174 if (g_EmulatorRunning || l_ROMOpen)
175 return M64ERR_INVALID_STATE;
176 if (ParamPtr == NULL || ParamInt < 4096)
177 return M64ERR_INPUT_ASSERT;
178 rval = open_rom((const unsigned char *) ParamPtr, ParamInt);
179 if (rval == M64ERR_SUCCESS)
180 {
181 l_ROMOpen = 1;
182 ScreenshotRomOpen();
183 cheat_init(&g_cheat_ctx);
184 }
185 return rval;
186 case M64CMD_ROM_CLOSE:
187 if (g_EmulatorRunning || !l_ROMOpen)
188 return M64ERR_INVALID_STATE;
189 l_ROMOpen = 0;
190 cheat_delete_all(&g_cheat_ctx);
191 cheat_uninit(&g_cheat_ctx);
192 return close_rom();
193 case M64CMD_ROM_GET_HEADER:
194 if (!l_ROMOpen)
195 return M64ERR_INVALID_STATE;
196 if (ParamPtr == NULL)
197 return M64ERR_INPUT_ASSERT;
198 if ((int)sizeof(m64p_rom_header) < ParamInt)
199 ParamInt = sizeof(m64p_rom_header);
200 memcpy(ParamPtr, &ROM_HEADER, ParamInt);
201 // Mupen64Plus used to keep a m64p_rom_header with a clean ROM name
202 // Keep returning a clean ROM name for backwards compatibility
203 if (ParamInt >= 0x20)
204 {
205 int size = (ParamInt >= 0x20 + 20) ? 20 : (ParamInt - 0x20);
206 memcpy((char *)ParamPtr + 0x20, ROM_PARAMS.headername, size);
207 }
208 return M64ERR_SUCCESS;
209 case M64CMD_ROM_GET_SETTINGS:
210 if (!l_ROMOpen)
211 return M64ERR_INVALID_STATE;
212 if (ParamPtr == NULL)
213 return M64ERR_INPUT_ASSERT;
214 if ((int)sizeof(m64p_rom_settings) < ParamInt)
215 ParamInt = sizeof(m64p_rom_settings);
216 memcpy(ParamPtr, &ROM_SETTINGS, ParamInt);
217 return M64ERR_SUCCESS;
218 case M64CMD_EXECUTE:
219 if (g_EmulatorRunning || !l_ROMOpen)
220 return M64ERR_INVALID_STATE;
221 /* print out plugin-related warning messages */
222 plugin_check();
223 /* the main_run() function will not return until the player has quit the game */
224 rval = main_run();
225 return rval;
226 case M64CMD_STOP:
227 if (!g_EmulatorRunning)
228 return M64ERR_INVALID_STATE;
229 /* this stop function is asynchronous. The emulator may not terminate until later */
230 return main_core_state_set(M64CORE_EMU_STATE, M64EMU_STOPPED);
231 case M64CMD_PAUSE:
232 if (!g_EmulatorRunning)
233 return M64ERR_INVALID_STATE;
234 return main_core_state_set(M64CORE_EMU_STATE, M64EMU_PAUSED);
235 case M64CMD_RESUME:
236 if (!g_EmulatorRunning)
237 return M64ERR_INVALID_STATE;
238 return main_core_state_set(M64CORE_EMU_STATE, M64EMU_RUNNING);
239 case M64CMD_CORE_STATE_QUERY:
240 if (ParamPtr == NULL)
241 return M64ERR_INPUT_ASSERT;
242 return main_core_state_query((m64p_core_param) ParamInt, (int *) ParamPtr);
243 case M64CMD_CORE_STATE_SET:
244 if (ParamPtr == NULL)
245 return M64ERR_INPUT_ASSERT;
246 return main_core_state_set((m64p_core_param) ParamInt, *((int *)ParamPtr));
247 case M64CMD_STATE_LOAD:
248 main_state_load((char *) ParamPtr);
249 return M64ERR_SUCCESS;
250 case M64CMD_STATE_SAVE:
251 if (!g_EmulatorRunning)
252 return M64ERR_INVALID_STATE;
253 if (ParamPtr != NULL && (ParamInt < 1 || ParamInt > 3))
254 return M64ERR_INPUT_INVALID;
255 main_state_save(ParamInt, (char *) ParamPtr);
256 return M64ERR_SUCCESS;
257 case M64CMD_STATE_SET_SLOT:
258 if (ParamInt < 0 || ParamInt > 9)
259 return M64ERR_INPUT_INVALID;
260 return main_core_state_set(M64CORE_SAVESTATE_SLOT, ParamInt);
261 case M64CMD_SEND_SDL_KEYDOWN:
262 if (!g_EmulatorRunning)
263 return M64ERR_INVALID_STATE;
264 keysym = ParamInt & 0xffff;
265 keymod = (ParamInt >> 16) & 0xffff;
266 event_sdl_keydown(keysym, keymod);
267 return M64ERR_SUCCESS;
268 case M64CMD_SEND_SDL_KEYUP:
269 if (!g_EmulatorRunning)
270 return M64ERR_INVALID_STATE;
271 keysym = ParamInt & 0xffff;
272 keymod = (ParamInt >> 16) & 0xffff;
273 event_sdl_keyup(keysym, keymod);
274 return M64ERR_SUCCESS;
275 case M64CMD_SET_FRAME_CALLBACK:
276 *(void**)&g_FrameCallback = ParamPtr;
277 return M64ERR_SUCCESS;
278 case M64CMD_TAKE_NEXT_SCREENSHOT:
279 if (!g_EmulatorRunning)
280 return M64ERR_INVALID_STATE;
281 main_take_next_screenshot();
282 return M64ERR_SUCCESS;
283 case M64CMD_READ_SCREEN:
284 if (!g_EmulatorRunning)
285 return M64ERR_INVALID_STATE;
286 if (ParamPtr == NULL)
287 return M64ERR_INPUT_ASSERT;
288 if (ParamInt < 0 || ParamInt > 1)
289 return M64ERR_INPUT_INVALID;
290 return main_read_screen(ParamPtr, ParamInt);
291 case M64CMD_RESET:
292 if (!g_EmulatorRunning)
293 return M64ERR_INVALID_STATE;
294 if (ParamInt < 0 || ParamInt > 1)
295 return M64ERR_INPUT_INVALID;
296 return main_reset(ParamInt);
297 case M64CMD_ADVANCE_FRAME:
298 if (!g_EmulatorRunning)
299 return M64ERR_INVALID_STATE;
300 main_advance_one();
301 return M64ERR_SUCCESS;
302 case M64CMD_SET_MEDIA_LOADER:
303 if (ParamInt != sizeof(m64p_media_loader) || ParamPtr == NULL)
304 return M64ERR_INPUT_INVALID;
305 g_media_loader = *(m64p_media_loader*)ParamPtr;
306 return M64ERR_SUCCESS;
307 default:
308 return M64ERR_INPUT_INVALID;
309 }
310
311 return M64ERR_INTERNAL;
312}
313
314EXPORT m64p_error CALL CoreOverrideVidExt(m64p_video_extension_functions *VideoFunctionStruct)
315{
316 if (!l_CoreInit)
317 return M64ERR_NOT_INIT;
318
319 return OverrideVideoFunctions(VideoFunctionStruct); /* in vidext.c */
320}
321
322EXPORT m64p_error CALL CoreAddCheat(const char *CheatName, m64p_cheat_code *CodeList, int NumCodes)
323{
324 if (!l_CoreInit)
325 return M64ERR_NOT_INIT;
326 if (CheatName == NULL || CodeList == NULL)
327 return M64ERR_INPUT_ASSERT;
328 if (strlen(CheatName) < 1 || NumCodes < 1)
329 return M64ERR_INPUT_INVALID;
330
331 if (cheat_add_new(&g_cheat_ctx, CheatName, CodeList, NumCodes))
332 return M64ERR_SUCCESS;
333
334 return M64ERR_INPUT_INVALID;
335}
336
337EXPORT m64p_error CALL CoreCheatEnabled(const char *CheatName, int Enabled)
338{
339 if (!l_CoreInit)
340 return M64ERR_NOT_INIT;
341 if (CheatName == NULL)
342 return M64ERR_INPUT_ASSERT;
343
344 if (cheat_set_enabled(&g_cheat_ctx, CheatName, Enabled))
345 return M64ERR_SUCCESS;
346
347 return M64ERR_INPUT_INVALID;
348}
349
350EXPORT m64p_error CALL CoreGetRomSettings(m64p_rom_settings *RomSettings, int RomSettingsLength, int Crc1, int Crc2)
351{
352 romdatabase_entry* entry;
353 int i;
354
355 if (!l_CoreInit)
356 return M64ERR_NOT_INIT;
357 if (RomSettings == NULL)
358 return M64ERR_INPUT_ASSERT;
359 if (RomSettingsLength < (int)sizeof(m64p_rom_settings))
360 return M64ERR_INPUT_INVALID;
361
362 /* Look up this ROM in the .ini file and fill in goodname, etc */
363 entry = ini_search_by_crc(Crc1, Crc2);
364 if (entry == NULL)
365 return M64ERR_INPUT_NOT_FOUND;
366
367 strncpy(RomSettings->goodname, entry->goodname, 255);
368 RomSettings->goodname[255] = '\0';
369 for (i = 0; i < 16; i++)
370 sprintf(RomSettings->MD5 + i*2, "%02X", entry->md5[i]);
371 RomSettings->MD5[32] = '\0';
372 RomSettings->savetype = entry->savetype;
373 RomSettings->status = entry->status;
374 RomSettings->players = entry->players;
375 RomSettings->rumble = entry->rumble;
376 RomSettings->transferpak = entry->transferpak;
377 RomSettings->mempak = entry->mempak;
378
379 return M64ERR_SUCCESS;
380}
381
382
383