| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
| 2 | * Mupen64plus-core - api/frontend.c * |
| 3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
| 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 <stdlib.h> |
| 28 | #include <string.h> |
| 29 | #include <stdio.h> |
| 30 | #include <SDL.h> |
| 31 | |
| 32 | #define M64P_CORE_PROTOTYPES 1 |
| 33 | #include "m64p_types.h" |
| 34 | #include "callbacks.h" |
| 35 | #include "m64p_config.h" |
| 36 | #include "m64p_frontend.h" |
| 37 | #include "config.h" |
| 38 | #include "vidext.h" |
| 39 | |
| 40 | #include "main/cheat.h" |
| 41 | #include "main/eventloop.h" |
| 42 | #include "main/main.h" |
| 43 | #include "main/rom.h" |
| 44 | #include "main/savestates.h" |
| 45 | #include "main/version.h" |
| 46 | #include "main/util.h" |
| 47 | #include "main/workqueue.h" |
| 48 | #include "osd/screenshot.h" |
| 49 | #include "plugin/plugin.h" |
| 50 | |
| 51 | /* some local state variables */ |
| 52 | static int l_CoreInit = 0; |
| 53 | static int l_ROMOpen = 0; |
| 54 | |
| 55 | /* functions exported outside of libmupen64plus to front-end application */ |
| 56 | EXPORT 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 | /* The ROM database contains MD5 hashes, goodnames, and some game-specific parameters */ |
| 95 | romdatabase_open(); |
| 96 | |
| 97 | workqueue_init(); |
| 98 | |
| 99 | l_CoreInit = 1; |
| 100 | return M64ERR_SUCCESS; |
| 101 | } |
| 102 | |
| 103 | EXPORT m64p_error CALL CoreShutdown(void) |
| 104 | { |
| 105 | if (!l_CoreInit) |
| 106 | return M64ERR_NOT_INIT; |
| 107 | |
| 108 | /* close down some core sub-systems */ |
| 109 | romdatabase_close(); |
| 110 | ConfigShutdown(); |
| 111 | workqueue_shutdown(); |
| 112 | savestates_deinit(); |
| 113 | |
| 114 | /* tell SDL to shut down */ |
| 115 | SDL_Quit(); |
| 116 | |
| 117 | l_CoreInit = 0; |
| 118 | return M64ERR_SUCCESS; |
| 119 | } |
| 120 | |
| 121 | EXPORT m64p_error CALL CoreAttachPlugin(m64p_plugin_type PluginType, m64p_dynlib_handle PluginLibHandle) |
| 122 | { |
| 123 | m64p_error rval; |
| 124 | |
| 125 | if (!l_CoreInit) |
| 126 | return M64ERR_NOT_INIT; |
| 127 | if (g_EmulatorRunning || !l_ROMOpen) |
| 128 | return M64ERR_INVALID_STATE; |
| 129 | |
| 130 | rval = plugin_connect(PluginType, PluginLibHandle); |
| 131 | if (rval != M64ERR_SUCCESS) |
| 132 | return rval; |
| 133 | |
| 134 | rval = plugin_start(PluginType); |
| 135 | if (rval != M64ERR_SUCCESS) |
| 136 | return rval; |
| 137 | |
| 138 | return M64ERR_SUCCESS; |
| 139 | } |
| 140 | |
| 141 | EXPORT m64p_error CALL CoreDetachPlugin(m64p_plugin_type PluginType) |
| 142 | { |
| 143 | if (!l_CoreInit) |
| 144 | return M64ERR_NOT_INIT; |
| 145 | if (g_EmulatorRunning) |
| 146 | return M64ERR_INVALID_STATE; |
| 147 | |
| 148 | return plugin_connect(PluginType, NULL); |
| 149 | } |
| 150 | |
| 151 | EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *ParamPtr) |
| 152 | { |
| 153 | m64p_error rval; |
| 154 | int keysym, keymod; |
| 155 | |
| 156 | if (!l_CoreInit) |
| 157 | return M64ERR_NOT_INIT; |
| 158 | |
| 159 | switch(Command) |
| 160 | { |
| 161 | case M64CMD_NOP: |
| 162 | return M64ERR_SUCCESS; |
| 163 | case M64CMD_ROM_OPEN: |
| 164 | if (g_EmulatorRunning || l_ROMOpen) |
| 165 | return M64ERR_INVALID_STATE; |
| 166 | if (ParamPtr == NULL || ParamInt < 4096) |
| 167 | return M64ERR_INPUT_ASSERT; |
| 168 | rval = open_rom((const unsigned char *) ParamPtr, ParamInt); |
| 169 | if (rval == M64ERR_SUCCESS) |
| 170 | { |
| 171 | l_ROMOpen = 1; |
| 172 | ScreenshotRomOpen(); |
| 173 | cheat_init(); |
| 174 | } |
| 175 | return rval; |
| 176 | case M64CMD_ROM_CLOSE: |
| 177 | if (g_EmulatorRunning || !l_ROMOpen) |
| 178 | return M64ERR_INVALID_STATE; |
| 179 | l_ROMOpen = 0; |
| 180 | cheat_delete_all(); |
| 181 | cheat_uninit(); |
| 182 | return close_rom(); |
| 183 | case M64CMD_ROM_GET_HEADER: |
| 184 | if (!l_ROMOpen) |
| 185 | return M64ERR_INVALID_STATE; |
| 186 | if (ParamPtr == NULL) |
| 187 | return M64ERR_INPUT_ASSERT; |
| 188 | if (sizeof(m64p_rom_header) < ParamInt) |
| 189 | ParamInt = sizeof(m64p_rom_header); |
| 190 | memcpy(ParamPtr, &ROM_HEADER, ParamInt); |
| 191 | // Mupen64Plus used to keep a m64p_rom_header with a clean ROM name |
| 192 | // Keep returning a clean ROM name for backwards compatibility |
| 193 | if (ParamInt >= 0x20) |
| 194 | { |
| 195 | int size = (ParamInt >= 0x20 + 20) ? 20 : (ParamInt - 0x20); |
| 196 | memcpy((char *)ParamPtr + 0x20, ROM_PARAMS.headername, size); |
| 197 | } |
| 198 | return M64ERR_SUCCESS; |
| 199 | case M64CMD_ROM_GET_SETTINGS: |
| 200 | if (!l_ROMOpen) |
| 201 | return M64ERR_INVALID_STATE; |
| 202 | if (ParamPtr == NULL) |
| 203 | return M64ERR_INPUT_ASSERT; |
| 204 | if (sizeof(m64p_rom_settings) < ParamInt) |
| 205 | ParamInt = sizeof(m64p_rom_settings); |
| 206 | memcpy(ParamPtr, &ROM_SETTINGS, ParamInt); |
| 207 | return M64ERR_SUCCESS; |
| 208 | case M64CMD_EXECUTE: |
| 209 | if (g_EmulatorRunning || !l_ROMOpen) |
| 210 | return M64ERR_INVALID_STATE; |
| 211 | /* print out plugin-related warning messages */ |
| 212 | plugin_check(); |
| 213 | /* the main_run() function will not return until the player has quit the game */ |
| 214 | rval = main_run(); |
| 215 | return rval; |
| 216 | case M64CMD_STOP: |
| 217 | if (!g_EmulatorRunning) |
| 218 | return M64ERR_INVALID_STATE; |
| 219 | /* this stop function is asynchronous. The emulator may not terminate until later */ |
| 220 | return main_core_state_set(M64CORE_EMU_STATE, M64EMU_STOPPED); |
| 221 | case M64CMD_PAUSE: |
| 222 | if (!g_EmulatorRunning) |
| 223 | return M64ERR_INVALID_STATE; |
| 224 | return main_core_state_set(M64CORE_EMU_STATE, M64EMU_PAUSED); |
| 225 | case M64CMD_RESUME: |
| 226 | if (!g_EmulatorRunning) |
| 227 | return M64ERR_INVALID_STATE; |
| 228 | return main_core_state_set(M64CORE_EMU_STATE, M64EMU_RUNNING); |
| 229 | case M64CMD_CORE_STATE_QUERY: |
| 230 | if (ParamPtr == NULL) |
| 231 | return M64ERR_INPUT_ASSERT; |
| 232 | return main_core_state_query((m64p_core_param) ParamInt, (int *) ParamPtr); |
| 233 | case M64CMD_CORE_STATE_SET: |
| 234 | if (ParamPtr == NULL) |
| 235 | return M64ERR_INPUT_ASSERT; |
| 236 | return main_core_state_set((m64p_core_param) ParamInt, *((int *)ParamPtr)); |
| 237 | case M64CMD_STATE_LOAD: |
| 238 | if (!g_EmulatorRunning) |
| 239 | return M64ERR_INVALID_STATE; |
| 240 | main_state_load((char *) ParamPtr); |
| 241 | return M64ERR_SUCCESS; |
| 242 | case M64CMD_STATE_SAVE: |
| 243 | if (!g_EmulatorRunning) |
| 244 | return M64ERR_INVALID_STATE; |
| 245 | if (ParamPtr != NULL && (ParamInt < 1 || ParamInt > 3)) |
| 246 | return M64ERR_INPUT_INVALID; |
| 247 | main_state_save(ParamInt, (char *) ParamPtr); |
| 248 | return M64ERR_SUCCESS; |
| 249 | case M64CMD_STATE_SET_SLOT: |
| 250 | if (ParamInt < 0 || ParamInt > 9) |
| 251 | return M64ERR_INPUT_INVALID; |
| 252 | return main_core_state_set(M64CORE_SAVESTATE_SLOT, ParamInt); |
| 253 | case M64CMD_SEND_SDL_KEYDOWN: |
| 254 | if (!g_EmulatorRunning) |
| 255 | return M64ERR_INVALID_STATE; |
| 256 | keysym = ParamInt & 0xffff; |
| 257 | keymod = (ParamInt >> 16) & 0xffff; |
| 258 | event_sdl_keydown(keysym, keymod); |
| 259 | return M64ERR_SUCCESS; |
| 260 | case M64CMD_SEND_SDL_KEYUP: |
| 261 | if (!g_EmulatorRunning) |
| 262 | return M64ERR_INVALID_STATE; |
| 263 | keysym = ParamInt & 0xffff; |
| 264 | keymod = (ParamInt >> 16) & 0xffff; |
| 265 | event_sdl_keyup(keysym, keymod); |
| 266 | return M64ERR_SUCCESS; |
| 267 | case M64CMD_SET_FRAME_CALLBACK: |
| 268 | g_FrameCallback = (m64p_frame_callback) ParamPtr; |
| 269 | return M64ERR_SUCCESS; |
| 270 | case M64CMD_TAKE_NEXT_SCREENSHOT: |
| 271 | if (!g_EmulatorRunning) |
| 272 | return M64ERR_INVALID_STATE; |
| 273 | main_take_next_screenshot(); |
| 274 | return M64ERR_SUCCESS; |
| 275 | case M64CMD_READ_SCREEN: |
| 276 | if (!g_EmulatorRunning) |
| 277 | return M64ERR_INVALID_STATE; |
| 278 | if (ParamPtr == NULL) |
| 279 | return M64ERR_INPUT_ASSERT; |
| 280 | if (ParamInt < 0 || ParamInt > 1) |
| 281 | return M64ERR_INPUT_INVALID; |
| 282 | return main_read_screen(ParamPtr, ParamInt); |
| 283 | case M64CMD_RESET: |
| 284 | if (!g_EmulatorRunning) |
| 285 | return M64ERR_INVALID_STATE; |
| 286 | if (ParamInt < 0 || ParamInt > 1) |
| 287 | return M64ERR_INPUT_INVALID; |
| 288 | return main_reset(ParamInt); |
| 289 | case M64CMD_ADVANCE_FRAME: |
| 290 | if (!g_EmulatorRunning) |
| 291 | return M64ERR_INVALID_STATE; |
| 292 | main_advance_one(); |
| 293 | return M64ERR_SUCCESS; |
| 294 | default: |
| 295 | return M64ERR_INPUT_INVALID; |
| 296 | } |
| 297 | |
| 298 | return M64ERR_INTERNAL; |
| 299 | } |
| 300 | |
| 301 | EXPORT m64p_error CALL CoreOverrideVidExt(m64p_video_extension_functions *VideoFunctionStruct) |
| 302 | { |
| 303 | if (!l_CoreInit) |
| 304 | return M64ERR_NOT_INIT; |
| 305 | |
| 306 | return OverrideVideoFunctions(VideoFunctionStruct); /* in vidext.c */ |
| 307 | } |
| 308 | |
| 309 | EXPORT m64p_error CALL CoreAddCheat(const char *CheatName, m64p_cheat_code *CodeList, int NumCodes) |
| 310 | { |
| 311 | if (!l_CoreInit) |
| 312 | return M64ERR_NOT_INIT; |
| 313 | if (CheatName == NULL || CodeList == NULL) |
| 314 | return M64ERR_INPUT_ASSERT; |
| 315 | if (strlen(CheatName) < 1 || NumCodes < 1) |
| 316 | return M64ERR_INPUT_INVALID; |
| 317 | |
| 318 | if (cheat_add_new(CheatName, CodeList, NumCodes)) |
| 319 | return M64ERR_SUCCESS; |
| 320 | |
| 321 | return M64ERR_INPUT_INVALID; |
| 322 | } |
| 323 | |
| 324 | EXPORT m64p_error CALL CoreCheatEnabled(const char *CheatName, int Enabled) |
| 325 | { |
| 326 | if (!l_CoreInit) |
| 327 | return M64ERR_NOT_INIT; |
| 328 | if (CheatName == NULL) |
| 329 | return M64ERR_INPUT_ASSERT; |
| 330 | |
| 331 | if (cheat_set_enabled(CheatName, Enabled)) |
| 332 | return M64ERR_SUCCESS; |
| 333 | |
| 334 | return M64ERR_INPUT_INVALID; |
| 335 | } |
| 336 | |
| 337 | EXPORT m64p_error CALL CoreGetRomSettings(m64p_rom_settings *RomSettings, int RomSettingsLength, int Crc1, int Crc2) |
| 338 | { |
| 339 | romdatabase_entry* entry; |
| 340 | int i; |
| 341 | |
| 342 | if (!l_CoreInit) |
| 343 | return M64ERR_NOT_INIT; |
| 344 | if (RomSettings == NULL) |
| 345 | return M64ERR_INPUT_ASSERT; |
| 346 | if (RomSettingsLength < sizeof(m64p_rom_settings)) |
| 347 | return M64ERR_INPUT_INVALID; |
| 348 | |
| 349 | /* Look up this ROM in the .ini file and fill in goodname, etc */ |
| 350 | entry = ini_search_by_crc(Crc1, Crc2); |
| 351 | if (entry == NULL) |
| 352 | return M64ERR_INPUT_NOT_FOUND; |
| 353 | |
| 354 | strncpy(RomSettings->goodname, entry->goodname, 255); |
| 355 | RomSettings->goodname[255] = '\0'; |
| 356 | for (i = 0; i < 16; i++) |
| 357 | sprintf(RomSettings->MD5 + i*2, "%02X", entry->md5[i]); |
| 358 | RomSettings->MD5[32] = '\0'; |
| 359 | RomSettings->savetype = entry->savetype; |
| 360 | RomSettings->status = entry->status; |
| 361 | RomSettings->players = entry->players; |
| 362 | RomSettings->rumble = entry->rumble; |
| 363 | |
| 364 | return M64ERR_SUCCESS; |
| 365 | } |
| 366 | |
| 367 | |