1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus - main.c * |
3 | * Mupen64Plus homepage: https://mupen64plus.org/ * |
4 | * Copyright (C) 2012 CasualJames * |
5 | * Copyright (C) 2008-2009 Richard Goedeken * |
6 | * Copyright (C) 2008 Ebenblues Nmn Okaygo Tillin9 * |
7 | * Copyright (C) 2002 Hacktarux * |
8 | * * |
9 | * This program is free software; you can redistribute it and/or modify * |
10 | * it under the terms of the GNU General Public License as published by * |
11 | * the Free Software Foundation; either version 2 of the License, or * |
12 | * (at your option) any later version. * |
13 | * * |
14 | * This program is distributed in the hope that it will be useful, * |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
17 | * GNU General Public License for more details. * |
18 | * * |
19 | * You should have received a copy of the GNU General Public License * |
20 | * along with this program; if not, write to the * |
21 | * Free Software Foundation, Inc., * |
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
23 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
24 | |
25 | /* This is MUPEN64's main entry point. It contains code that is common |
26 | * to both the gui and non-gui versions of mupen64. See |
27 | * gui subdirectories for the gui-specific code. |
28 | * if you want to implement an interface, you should look here |
29 | */ |
30 | |
31 | #include <SDL.h> |
32 | #include <assert.h> |
33 | #include <stdarg.h> |
34 | #include <stddef.h> |
35 | #include <stdio.h> |
36 | #include <stdlib.h> |
37 | #include <string.h> |
38 | |
39 | #define M64P_CORE_PROTOTYPES 1 |
40 | #include "api/callbacks.h" |
41 | #include "api/config.h" |
42 | #include "api/debugger.h" |
43 | #include "api/m64p_config.h" |
44 | #include "api/m64p_types.h" |
45 | #include "api/m64p_vidext.h" |
46 | #include "api/vidext.h" |
47 | #include "backends/api/audio_out_backend.h" |
48 | #include "backends/api/clock_backend.h" |
49 | #include "backends/api/controller_input_backend.h" |
50 | #include "backends/api/joybus.h" |
51 | #include "backends/api/rumble_backend.h" |
52 | #include "backends/api/storage_backend.h" |
53 | #include "backends/api/video_capture_backend.h" |
54 | #include "backends/plugins_compat/plugins_compat.h" |
55 | #include "backends/clock_ctime_plus_delta.h" |
56 | #include "backends/file_storage.h" |
57 | #include "cheat.h" |
58 | #include "device/device.h" |
59 | #include "device/controllers/paks/biopak.h" |
60 | #include "device/controllers/paks/mempak.h" |
61 | #include "device/controllers/paks/rumblepak.h" |
62 | #include "device/controllers/paks/transferpak.h" |
63 | #include "device/gb/gb_cart.h" |
64 | #include "device/pif/bootrom_hle.h" |
65 | #include "eventloop.h" |
66 | #include "main.h" |
67 | #include "cheat.h" |
68 | #include "osal/files.h" |
69 | #include "osal/preproc.h" |
70 | #include "osd/osd.h" |
71 | #include "plugin/plugin.h" |
72 | #if defined(PROFILE) |
73 | #include "profile.h" |
74 | #endif |
75 | #include "rom.h" |
76 | #include "savestates.h" |
77 | #include "screenshot.h" |
78 | #include "util.h" |
79 | |
80 | #ifdef DBG |
81 | #include "debugger/dbg_debugger.h" |
82 | #endif |
83 | |
84 | #ifdef WITH_LIRC |
85 | #include "lirc.h" |
86 | #endif //WITH_LIRC |
87 | |
88 | /* version number for Core config section */ |
89 | #define CONFIG_PARAM_VERSION 1.01 |
90 | |
91 | /** globals **/ |
92 | m64p_handle g_CoreConfig = NULL; |
93 | |
94 | m64p_frame_callback g_FrameCallback = NULL; |
95 | |
96 | int g_RomWordsLittleEndian = 0; // after loading, ROM words are in native N64 byte order (big endian). We will swap them on x86 |
97 | int g_EmulatorRunning = 0; // need separate boolean to tell if emulator is running, since --nogui doesn't use a thread |
98 | |
99 | |
100 | int g_rom_pause; |
101 | |
102 | struct cheat_ctx g_cheat_ctx; |
103 | |
104 | /* g_mem_base is global to allow plugins early access (before device is initialized). |
105 | * Do not use this variable directly in emulation code. |
106 | * Initialization and DeInitialization of this variable is done at CoreStartup and CoreShutdown. |
107 | */ |
108 | void* g_mem_base = NULL; |
109 | |
110 | struct device g_dev; |
111 | |
112 | m64p_media_loader g_media_loader; |
113 | |
114 | int g_gs_vi_counter = 0; |
115 | |
116 | /** static (local) variables **/ |
117 | static int l_CurrentFrame = 0; // frame counter |
118 | static int l_TakeScreenshot = 0; // Tell OSD Rendering callback to take a screenshot just before drawing the OSD |
119 | static int l_SpeedFactor = 100; // percentage of nominal game speed at which emulator is running |
120 | static int l_FrameAdvance = 0; // variable to check if we pause on next frame |
121 | static int l_MainSpeedLimit = 1; // insert delay during vi_interrupt to keep speed at real-time |
122 | |
123 | static osd_message_t *l_msgVol = NULL; |
124 | static osd_message_t *l_msgFF = NULL; |
125 | static osd_message_t *l_msgPause = NULL; |
126 | |
127 | /* compatible paks */ |
128 | enum { PAK_MAX_SIZE = 5 }; |
129 | static size_t l_paks_idx[GAME_CONTROLLERS_COUNT]; |
130 | static void* l_paks[GAME_CONTROLLERS_COUNT][PAK_MAX_SIZE]; |
131 | static const struct pak_interface* l_ipaks[PAK_MAX_SIZE]; |
132 | static size_t l_pak_type_idx[6]; |
133 | |
134 | /********************************************************************************************************* |
135 | * static functions |
136 | */ |
137 | |
138 | static const char *get_savepathdefault(const char *configpath) |
139 | { |
140 | static char path[1024]; |
141 | |
142 | if (!configpath || (strlen(configpath) == 0)) { |
143 | snprintf(path, 1024, "%ssave%c" , ConfigGetUserDataPath(), OSAL_DIR_SEPARATORS[0]); |
144 | path[1023] = 0; |
145 | } else { |
146 | snprintf(path, 1024, "%s%c" , configpath, OSAL_DIR_SEPARATORS[0]); |
147 | path[1023] = 0; |
148 | } |
149 | |
150 | /* create directory if it doesn't exist */ |
151 | osal_mkdirp(path, 0700); |
152 | |
153 | return path; |
154 | } |
155 | |
156 | static char *get_mempaks_path(void) |
157 | { |
158 | return formatstr("%s%s.mpk" , get_savesrampath(), ROM_SETTINGS.goodname); |
159 | } |
160 | |
161 | static char *get_eeprom_path(void) |
162 | { |
163 | return formatstr("%s%s.eep" , get_savesrampath(), ROM_SETTINGS.goodname); |
164 | } |
165 | |
166 | static char *get_sram_path(void) |
167 | { |
168 | return formatstr("%s%s.sra" , get_savesrampath(), ROM_SETTINGS.goodname); |
169 | } |
170 | |
171 | static char *get_flashram_path(void) |
172 | { |
173 | return formatstr("%s%s.fla" , get_savesrampath(), ROM_SETTINGS.goodname); |
174 | } |
175 | |
176 | static char *get_gb_ram_path(const char* gbrom, unsigned int control_id) |
177 | { |
178 | return formatstr("%s%s.%u.sav" , get_savesrampath(), gbrom, control_id); |
179 | } |
180 | |
181 | static m64p_error init_video_capture_backend(const struct video_capture_backend_interface** ivcap, void** vcap, m64p_handle config, const char* key) |
182 | { |
183 | m64p_error err; |
184 | |
185 | const char* name = ConfigGetParamString(config, key); |
186 | if (name == NULL) { |
187 | DebugMessage(M64MSG_WARNING, "Couldn't get %s value. Using NULL value instead." , key); |
188 | } |
189 | |
190 | /* try to find desired backend (by name) */ |
191 | *ivcap = get_video_capture_backend(name); |
192 | |
193 | /* handle not found case */ |
194 | if (*ivcap == NULL) { |
195 | /* default to dummy backend */ |
196 | *ivcap = get_video_capture_backend(NULL); |
197 | |
198 | DebugMessage(M64MSG_WARNING, "Could not find %s video_capture_backend_interface. Using %s instead." , |
199 | name, (*ivcap)->name); |
200 | } |
201 | |
202 | /* build section name */ |
203 | char* section = formatstr("%s:%s" , key, (*ivcap)->name); |
204 | |
205 | /* init backend */ |
206 | err = (*ivcap)->init(vcap, section); |
207 | |
208 | if (err == M64ERR_SUCCESS) { |
209 | DebugMessage(M64MSG_INFO, "Using video capture backend: %s" , (*ivcap)->name); |
210 | } |
211 | else { |
212 | DebugMessage(M64MSG_ERROR, "Failed to initialize video capture backend %s: %s" , (*ivcap)->name, CoreErrorMessage(err)); |
213 | *ivcap = NULL; |
214 | } |
215 | |
216 | free(section); |
217 | |
218 | return err; |
219 | } |
220 | |
221 | /********************************************************************************************************* |
222 | * helper functions |
223 | */ |
224 | |
225 | |
226 | const char *get_savestatepath(void) |
227 | { |
228 | /* try to get the SaveStatePath string variable in the Core configuration section */ |
229 | return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveStatePath" )); |
230 | } |
231 | |
232 | const char *get_savesrampath(void) |
233 | { |
234 | /* try to get the SaveSRAMPath string variable in the Core configuration section */ |
235 | return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveSRAMPath" )); |
236 | } |
237 | |
238 | void main_message(m64p_msg_level level, unsigned int corner, const char *format, ...) |
239 | { |
240 | va_list ap; |
241 | char buffer[2049]; |
242 | va_start(ap, format); |
243 | vsnprintf(buffer, 2047, format, ap); |
244 | buffer[2048]='\0'; |
245 | va_end(ap); |
246 | |
247 | /* send message to on-screen-display if enabled */ |
248 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay" )) |
249 | osd_new_message((enum osd_corner) corner, "%s" , buffer); |
250 | /* send message to front-end */ |
251 | DebugMessage(level, "%s" , buffer); |
252 | } |
253 | |
254 | static void main_check_inputs(void) |
255 | { |
256 | #ifdef WITH_LIRC |
257 | lircCheckInput(); |
258 | #endif |
259 | } |
260 | |
261 | /********************************************************************************************************* |
262 | * global functions, for adjusting the core emulator behavior |
263 | */ |
264 | |
265 | int main_set_core_defaults(void) |
266 | { |
267 | float fConfigParamsVersion; |
268 | int bUpgrade = 0; |
269 | |
270 | if (ConfigGetParameter(g_CoreConfig, "Version" , M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) |
271 | { |
272 | DebugMessage(M64MSG_WARNING, "No version number in 'Core' config section. Setting defaults." ); |
273 | ConfigDeleteSection("Core" ); |
274 | ConfigOpenSection("Core" , &g_CoreConfig); |
275 | } |
276 | else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) |
277 | { |
278 | DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Core' config section: current is %.2f. Setting defaults." , fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); |
279 | ConfigDeleteSection("Core" ); |
280 | ConfigOpenSection("Core" , &g_CoreConfig); |
281 | } |
282 | else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) |
283 | { |
284 | float fVersion = (float) CONFIG_PARAM_VERSION; |
285 | ConfigSetParameter(g_CoreConfig, "Version" , M64TYPE_FLOAT, &fVersion); |
286 | DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Core' config section to %.2f" , fVersion); |
287 | bUpgrade = 1; |
288 | } |
289 | |
290 | /* parameters controlling the operation of the core */ |
291 | ConfigSetDefaultFloat(g_CoreConfig, "Version" , (float) CONFIG_PARAM_VERSION, "Mupen64Plus Core config parameter set version number. Please don't change this version number." ); |
292 | ConfigSetDefaultBool(g_CoreConfig, "OnScreenDisplay" , 1, "Draw on-screen display if True, otherwise don't draw OSD" ); |
293 | #if defined(DYNAREC) |
294 | ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator" , 2, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more" ); |
295 | #else |
296 | ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator" , 1, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more" ); |
297 | #endif |
298 | ConfigSetDefaultBool(g_CoreConfig, "NoCompiledJump" , 0, "Disable compiled jump commands in dynamic recompiler (should be set to False) " ); |
299 | ConfigSetDefaultBool(g_CoreConfig, "DisableExtraMem" , 0, "Disable 4MB expansion RAM pack. May be necessary for some games" ); |
300 | ConfigSetDefaultBool(g_CoreConfig, "AutoStateSlotIncrement" , 0, "Increment the save state slot after each save operation" ); |
301 | ConfigSetDefaultBool(g_CoreConfig, "EnableDebugger" , 0, "Activate the R4300 debugger when ROM execution begins, if core was built with Debugger support" ); |
302 | ConfigSetDefaultInt(g_CoreConfig, "CurrentStateSlot" , 0, "Save state slot (0-9) to use when saving/loading the emulator state" ); |
303 | ConfigSetDefaultString(g_CoreConfig, "ScreenshotPath" , "" , "Path to directory where screenshots are saved. If this is blank, the default value of ${UserDataPath}/screenshot will be used" ); |
304 | ConfigSetDefaultString(g_CoreConfig, "SaveStatePath" , "" , "Path to directory where emulator save states (snapshots) are saved. If this is blank, the default value of ${UserDataPath}/save will be used" ); |
305 | ConfigSetDefaultString(g_CoreConfig, "SaveSRAMPath" , "" , "Path to directory where SRAM/EEPROM data (in-game saves) are stored. If this is blank, the default value of ${UserDataPath}/save will be used" ); |
306 | ConfigSetDefaultString(g_CoreConfig, "SharedDataPath" , "" , "Path to a directory to search when looking for shared data files" ); |
307 | ConfigSetDefaultInt(g_CoreConfig, "CountPerOp" , 0, "Force number of cycles per emulated instruction" ); |
308 | ConfigSetDefaultBool(g_CoreConfig, "RandomizeInterrupt" , 1, "Randomize PI/SI Interrupt Timing" ); |
309 | ConfigSetDefaultInt(g_CoreConfig, "SiDmaDuration" , -1, "Duration of SI DMA (-1: use per game settings)" ); |
310 | ConfigSetDefaultString(g_CoreConfig, "GbCameraVideoCaptureBackend1" , DEFAULT_VIDEO_CAPTURE_BACKEND, "Gameboy Camera Video Capture backend" ); |
311 | |
312 | /* handle upgrades */ |
313 | if (bUpgrade) |
314 | { |
315 | if (fConfigParamsVersion < 1.01f) |
316 | { // added separate SaveSRAMPath parameter in v1.01 |
317 | const char *pccSaveStatePath = ConfigGetParamString(g_CoreConfig, "SaveStatePath" ); |
318 | if (pccSaveStatePath != NULL) |
319 | ConfigSetParameter(g_CoreConfig, "SaveSRAMPath" , M64TYPE_STRING, pccSaveStatePath); |
320 | } |
321 | } |
322 | |
323 | /* set config parameters for keyboard and joystick commands */ |
324 | return event_set_core_defaults(); |
325 | } |
326 | |
327 | void main_speeddown(int percent) |
328 | { |
329 | if (l_SpeedFactor - percent > 10) /* 10% minimum speed */ |
330 | { |
331 | l_SpeedFactor -= percent; |
332 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%" , "Playback speed:" , l_SpeedFactor); |
333 | audio.setSpeedFactor(l_SpeedFactor); |
334 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
335 | } |
336 | } |
337 | |
338 | void main_speedup(int percent) |
339 | { |
340 | if (l_SpeedFactor + percent < 300) /* 300% maximum speed */ |
341 | { |
342 | l_SpeedFactor += percent; |
343 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%" , "Playback speed:" , l_SpeedFactor); |
344 | audio.setSpeedFactor(l_SpeedFactor); |
345 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
346 | } |
347 | } |
348 | |
349 | static void main_speedset(int percent) |
350 | { |
351 | if (percent < 1 || percent > 1000) |
352 | { |
353 | DebugMessage(M64MSG_WARNING, "Invalid speed setting %i percent" , percent); |
354 | return; |
355 | } |
356 | // disable fast-forward if it's enabled |
357 | main_set_fastforward(0); |
358 | // set speed |
359 | l_SpeedFactor = percent; |
360 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%" , "Playback speed:" , l_SpeedFactor); |
361 | audio.setSpeedFactor(l_SpeedFactor); |
362 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
363 | } |
364 | |
365 | void main_set_fastforward(int enable) |
366 | { |
367 | static int ff_state = 0; |
368 | static int SavedSpeedFactor = 100; |
369 | |
370 | if (enable && !ff_state) |
371 | { |
372 | ff_state = 1; /* activate fast-forward */ |
373 | SavedSpeedFactor = l_SpeedFactor; |
374 | l_SpeedFactor = 250; |
375 | audio.setSpeedFactor(l_SpeedFactor); |
376 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
377 | // set fast-forward indicator |
378 | l_msgFF = osd_new_message(OSD_TOP_RIGHT, "Fast Forward" ); |
379 | osd_message_set_static(l_msgFF); |
380 | osd_message_set_user_managed(l_msgFF); |
381 | } |
382 | else if (!enable && ff_state) |
383 | { |
384 | ff_state = 0; /* de-activate fast-forward */ |
385 | l_SpeedFactor = SavedSpeedFactor; |
386 | audio.setSpeedFactor(l_SpeedFactor); |
387 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
388 | // remove message |
389 | osd_delete_message(l_msgFF); |
390 | l_msgFF = NULL; |
391 | } |
392 | |
393 | } |
394 | |
395 | static void main_set_speedlimiter(int enable) |
396 | { |
397 | l_MainSpeedLimit = enable ? 1 : 0; |
398 | } |
399 | |
400 | static int main_is_paused(void) |
401 | { |
402 | return (g_EmulatorRunning && g_rom_pause); |
403 | } |
404 | |
405 | void main_toggle_pause(void) |
406 | { |
407 | if (!g_EmulatorRunning) |
408 | return; |
409 | |
410 | if (g_rom_pause) |
411 | { |
412 | DebugMessage(M64MSG_STATUS, "Emulation continued." ); |
413 | if(l_msgPause) |
414 | { |
415 | osd_delete_message(l_msgPause); |
416 | l_msgPause = NULL; |
417 | } |
418 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
419 | } |
420 | else |
421 | { |
422 | if(l_msgPause) |
423 | osd_delete_message(l_msgPause); |
424 | |
425 | DebugMessage(M64MSG_STATUS, "Emulation paused." ); |
426 | l_msgPause = osd_new_message(OSD_MIDDLE_CENTER, "Paused" ); |
427 | osd_message_set_static(l_msgPause); |
428 | osd_message_set_user_managed(l_msgPause); |
429 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
430 | } |
431 | |
432 | g_rom_pause = !g_rom_pause; |
433 | l_FrameAdvance = 0; |
434 | } |
435 | |
436 | void main_advance_one(void) |
437 | { |
438 | l_FrameAdvance = 1; |
439 | g_rom_pause = 0; |
440 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
441 | } |
442 | |
443 | static void main_draw_volume_osd(void) |
444 | { |
445 | char msgString[64]; |
446 | const char *volString; |
447 | |
448 | // this calls into the audio plugin |
449 | volString = audio.volumeGetString(); |
450 | if (volString == NULL) |
451 | { |
452 | strcpy(msgString, "Volume Not Supported." ); |
453 | } |
454 | else |
455 | { |
456 | sprintf(msgString, "%s: %s" , "Volume" , volString); |
457 | } |
458 | |
459 | // create a new message or update an existing one |
460 | if (l_msgVol != NULL) |
461 | osd_update_message(l_msgVol, "%s" , msgString); |
462 | else { |
463 | l_msgVol = osd_new_message(OSD_MIDDLE_CENTER, "%s" , msgString); |
464 | osd_message_set_user_managed(l_msgVol); |
465 | } |
466 | } |
467 | |
468 | /* this function could be called as a result of a keypress, joystick/button movement, |
469 | LIRC command, or 'testshots' command-line option timer */ |
470 | void main_take_next_screenshot(void) |
471 | { |
472 | l_TakeScreenshot = l_CurrentFrame + 1; |
473 | } |
474 | |
475 | void main_state_set_slot(int slot) |
476 | { |
477 | if (slot < 0 || slot > 9) |
478 | { |
479 | DebugMessage(M64MSG_WARNING, "Invalid savestate slot '%i' in main_state_set_slot(). Using 0" , slot); |
480 | slot = 0; |
481 | } |
482 | |
483 | savestates_select_slot(slot); |
484 | } |
485 | |
486 | void main_state_inc_slot(void) |
487 | { |
488 | savestates_inc_slot(); |
489 | } |
490 | |
491 | void main_state_load(const char *filename) |
492 | { |
493 | if (filename == NULL) // Save to slot |
494 | savestates_set_job(savestates_job_load, savestates_type_m64p, NULL); |
495 | else |
496 | savestates_set_job(savestates_job_load, savestates_type_unknown, filename); |
497 | } |
498 | |
499 | void main_state_save(int format, const char *filename) |
500 | { |
501 | if (filename == NULL) // Save to slot |
502 | savestates_set_job(savestates_job_save, savestates_type_m64p, NULL); |
503 | else // Save to file |
504 | savestates_set_job(savestates_job_save, (savestates_type)format, filename); |
505 | } |
506 | |
507 | m64p_error main_core_state_query(m64p_core_param param, int *rval) |
508 | { |
509 | switch (param) |
510 | { |
511 | case M64CORE_EMU_STATE: |
512 | if (!g_EmulatorRunning) |
513 | *rval = M64EMU_STOPPED; |
514 | else if (g_rom_pause) |
515 | *rval = M64EMU_PAUSED; |
516 | else |
517 | *rval = M64EMU_RUNNING; |
518 | break; |
519 | case M64CORE_VIDEO_MODE: |
520 | if (!VidExt_VideoRunning()) |
521 | *rval = M64VIDEO_NONE; |
522 | else if (VidExt_InFullscreenMode()) |
523 | *rval = M64VIDEO_FULLSCREEN; |
524 | else |
525 | *rval = M64VIDEO_WINDOWED; |
526 | break; |
527 | case M64CORE_SAVESTATE_SLOT: |
528 | *rval = savestates_get_slot(); |
529 | break; |
530 | case M64CORE_SPEED_FACTOR: |
531 | *rval = l_SpeedFactor; |
532 | break; |
533 | case M64CORE_SPEED_LIMITER: |
534 | *rval = l_MainSpeedLimit; |
535 | break; |
536 | case M64CORE_VIDEO_SIZE: |
537 | { |
538 | int width, height; |
539 | if (!g_EmulatorRunning) |
540 | return M64ERR_INVALID_STATE; |
541 | main_get_screen_size(&width, &height); |
542 | *rval = (width << 16) + height; |
543 | break; |
544 | } |
545 | case M64CORE_AUDIO_VOLUME: |
546 | { |
547 | if (!g_EmulatorRunning) |
548 | return M64ERR_INVALID_STATE; |
549 | return main_volume_get_level(rval); |
550 | } |
551 | case M64CORE_AUDIO_MUTE: |
552 | *rval = main_volume_get_muted(); |
553 | break; |
554 | case M64CORE_INPUT_GAMESHARK: |
555 | *rval = event_gameshark_active(); |
556 | break; |
557 | // these are only used for callbacks; they cannot be queried or set |
558 | case M64CORE_STATE_LOADCOMPLETE: |
559 | case M64CORE_STATE_SAVECOMPLETE: |
560 | return M64ERR_INPUT_INVALID; |
561 | default: |
562 | return M64ERR_INPUT_INVALID; |
563 | } |
564 | |
565 | return M64ERR_SUCCESS; |
566 | } |
567 | |
568 | m64p_error main_core_state_set(m64p_core_param param, int val) |
569 | { |
570 | switch (param) |
571 | { |
572 | case M64CORE_EMU_STATE: |
573 | if (!g_EmulatorRunning) |
574 | return M64ERR_INVALID_STATE; |
575 | if (val == M64EMU_STOPPED) |
576 | { |
577 | /* this stop function is asynchronous. The emulator may not terminate until later */ |
578 | main_stop(); |
579 | return M64ERR_SUCCESS; |
580 | } |
581 | else if (val == M64EMU_RUNNING) |
582 | { |
583 | if (main_is_paused()) |
584 | main_toggle_pause(); |
585 | return M64ERR_SUCCESS; |
586 | } |
587 | else if (val == M64EMU_PAUSED) |
588 | { |
589 | if (!main_is_paused()) |
590 | main_toggle_pause(); |
591 | return M64ERR_SUCCESS; |
592 | } |
593 | return M64ERR_INPUT_INVALID; |
594 | case M64CORE_VIDEO_MODE: |
595 | if (!g_EmulatorRunning) |
596 | return M64ERR_INVALID_STATE; |
597 | if (val == M64VIDEO_WINDOWED) |
598 | { |
599 | if (VidExt_InFullscreenMode()) |
600 | gfx.changeWindow(); |
601 | return M64ERR_SUCCESS; |
602 | } |
603 | else if (val == M64VIDEO_FULLSCREEN) |
604 | { |
605 | if (!VidExt_InFullscreenMode()) |
606 | gfx.changeWindow(); |
607 | return M64ERR_SUCCESS; |
608 | } |
609 | return M64ERR_INPUT_INVALID; |
610 | case M64CORE_SAVESTATE_SLOT: |
611 | if (val < 0 || val > 9) |
612 | return M64ERR_INPUT_INVALID; |
613 | savestates_select_slot(val); |
614 | return M64ERR_SUCCESS; |
615 | case M64CORE_SPEED_FACTOR: |
616 | if (!g_EmulatorRunning) |
617 | return M64ERR_INVALID_STATE; |
618 | main_speedset(val); |
619 | return M64ERR_SUCCESS; |
620 | case M64CORE_SPEED_LIMITER: |
621 | main_set_speedlimiter(val); |
622 | return M64ERR_SUCCESS; |
623 | case M64CORE_VIDEO_SIZE: |
624 | { |
625 | // the front-end app is telling us that the user has resized the video output frame, and so |
626 | // we should try to update the video plugin accordingly. First, check state |
627 | int width, height; |
628 | if (!g_EmulatorRunning) |
629 | return M64ERR_INVALID_STATE; |
630 | width = (val >> 16) & 0xffff; |
631 | height = val & 0xffff; |
632 | // then call the video plugin. if the video plugin supports resizing, it will resize its viewport and call |
633 | // VidExt_ResizeWindow to update the window manager handling our opengl output window |
634 | gfx.resizeVideoOutput(width, height); |
635 | return M64ERR_SUCCESS; |
636 | } |
637 | case M64CORE_AUDIO_VOLUME: |
638 | if (!g_EmulatorRunning) |
639 | return M64ERR_INVALID_STATE; |
640 | if (val < 0 || val > 100) |
641 | return M64ERR_INPUT_INVALID; |
642 | return main_volume_set_level(val); |
643 | case M64CORE_AUDIO_MUTE: |
644 | if ((main_volume_get_muted() && !val) || (!main_volume_get_muted() && val)) |
645 | return main_volume_mute(); |
646 | return M64ERR_SUCCESS; |
647 | case M64CORE_INPUT_GAMESHARK: |
648 | if (!g_EmulatorRunning) |
649 | return M64ERR_INVALID_STATE; |
650 | event_set_gameshark(val); |
651 | return M64ERR_SUCCESS; |
652 | // these are only used for callbacks; they cannot be queried or set |
653 | case M64CORE_STATE_LOADCOMPLETE: |
654 | case M64CORE_STATE_SAVECOMPLETE: |
655 | return M64ERR_INPUT_INVALID; |
656 | default: |
657 | return M64ERR_INPUT_INVALID; |
658 | } |
659 | } |
660 | |
661 | m64p_error main_get_screen_size(int *width, int *height) |
662 | { |
663 | gfx.readScreen(NULL, width, height, 0); |
664 | return M64ERR_SUCCESS; |
665 | } |
666 | |
667 | m64p_error main_read_screen(void *pixels, int bFront) |
668 | { |
669 | int width_trash, height_trash; |
670 | gfx.readScreen(pixels, &width_trash, &height_trash, bFront); |
671 | return M64ERR_SUCCESS; |
672 | } |
673 | |
674 | m64p_error main_volume_up(void) |
675 | { |
676 | int level = 0; |
677 | audio.volumeUp(); |
678 | main_draw_volume_osd(); |
679 | main_volume_get_level(&level); |
680 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
681 | return M64ERR_SUCCESS; |
682 | } |
683 | |
684 | m64p_error main_volume_down(void) |
685 | { |
686 | int level = 0; |
687 | audio.volumeDown(); |
688 | main_draw_volume_osd(); |
689 | main_volume_get_level(&level); |
690 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
691 | return M64ERR_SUCCESS; |
692 | } |
693 | |
694 | m64p_error main_volume_get_level(int *level) |
695 | { |
696 | *level = audio.volumeGetLevel(); |
697 | return M64ERR_SUCCESS; |
698 | } |
699 | |
700 | m64p_error main_volume_set_level(int level) |
701 | { |
702 | audio.volumeSetLevel(level); |
703 | main_draw_volume_osd(); |
704 | level = audio.volumeGetLevel(); |
705 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
706 | return M64ERR_SUCCESS; |
707 | } |
708 | |
709 | m64p_error main_volume_mute(void) |
710 | { |
711 | audio.volumeMute(); |
712 | main_draw_volume_osd(); |
713 | StateChanged(M64CORE_AUDIO_MUTE, main_volume_get_muted()); |
714 | return M64ERR_SUCCESS; |
715 | } |
716 | |
717 | int main_volume_get_muted(void) |
718 | { |
719 | return (audio.volumeGetLevel() == 0); |
720 | } |
721 | |
722 | m64p_error main_reset(int do_hard_reset) |
723 | { |
724 | if (do_hard_reset) { |
725 | hard_reset_device(&g_dev); |
726 | } |
727 | else { |
728 | soft_reset_device(&g_dev); |
729 | } |
730 | |
731 | return M64ERR_SUCCESS; |
732 | } |
733 | |
734 | /********************************************************************************************************* |
735 | * global functions, callbacks from the r4300 core or from other plugins |
736 | */ |
737 | |
738 | static void video_plugin_render_callback(int bScreenRedrawn) |
739 | { |
740 | int bOSD = ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay" ); |
741 | |
742 | // if the flag is set to take a screenshot, then grab it now |
743 | if (l_TakeScreenshot != 0) |
744 | { |
745 | // if the OSD is enabled, and the screen has not been recently redrawn, then we cannot take a screenshot now because |
746 | // it contains the OSD text. Wait until the next redraw |
747 | if (!bOSD || bScreenRedrawn) |
748 | { |
749 | TakeScreenshot(l_TakeScreenshot - 1); // current frame number +1 is in l_TakeScreenshot |
750 | l_TakeScreenshot = 0; // reset flag |
751 | } |
752 | } |
753 | |
754 | // if the OSD is enabled, then draw it now |
755 | if (bOSD) |
756 | { |
757 | osd_render(); |
758 | } |
759 | |
760 | // if the input plugin specified a render callback, call it now |
761 | if(input.renderCallback) |
762 | { |
763 | input.renderCallback(); |
764 | } |
765 | } |
766 | |
767 | void new_frame(void) |
768 | { |
769 | if (g_FrameCallback != NULL) |
770 | (*g_FrameCallback)(l_CurrentFrame); |
771 | |
772 | /* advance the current frame */ |
773 | l_CurrentFrame++; |
774 | |
775 | if (l_FrameAdvance) { |
776 | g_rom_pause = 1; |
777 | l_FrameAdvance = 0; |
778 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
779 | } |
780 | } |
781 | |
782 | #define SAMPLE_COUNT 3 |
783 | static void apply_speed_limiter(void) |
784 | { |
785 | static unsigned long totalVIs = 0; |
786 | static int resetOnce = 0; |
787 | static int lastSpeedFactor = 100; |
788 | static unsigned int StartFPSTime = 0; |
789 | static const double defaultSpeedFactor = 100.0; |
790 | unsigned int CurrentFPSTime = SDL_GetTicks(); |
791 | static double sleepTimes[SAMPLE_COUNT]; |
792 | static unsigned int sleepTimesIndex = 0; |
793 | |
794 | // calculate frame duration based upon ROM setting (50/60hz) and mupen64plus speed adjustment |
795 | const double VILimitMilliseconds = 1000.0 / g_dev.vi.expected_refresh_rate; |
796 | const double SpeedFactorMultiple = defaultSpeedFactor/l_SpeedFactor; |
797 | const double AdjustedLimit = VILimitMilliseconds * SpeedFactorMultiple; |
798 | |
799 | //if this is the first time or we are resuming from pause |
800 | if(StartFPSTime == 0 || !resetOnce || lastSpeedFactor != l_SpeedFactor) |
801 | { |
802 | StartFPSTime = CurrentFPSTime; |
803 | totalVIs = 0; |
804 | resetOnce = 1; |
805 | } |
806 | else |
807 | { |
808 | ++totalVIs; |
809 | } |
810 | |
811 | lastSpeedFactor = l_SpeedFactor; |
812 | |
813 | #if defined(PROFILE) |
814 | timed_section_start(TIMED_SECTION_IDLE); |
815 | #endif |
816 | |
817 | #ifdef DBG |
818 | if(g_DebuggerActive) DebuggerCallback(DEBUG_UI_VI, 0); |
819 | #endif |
820 | |
821 | double totalElapsedGameTime = AdjustedLimit*totalVIs; |
822 | double elapsedRealTime = CurrentFPSTime - StartFPSTime; |
823 | double sleepTime = totalElapsedGameTime - elapsedRealTime; |
824 | |
825 | //Reset if the sleep needed is an unreasonable value |
826 | static const double minSleepNeeded = -50; |
827 | static const double maxSleepNeeded = 50; |
828 | if(sleepTime < minSleepNeeded || sleepTime > (maxSleepNeeded*SpeedFactorMultiple)) |
829 | { |
830 | resetOnce = 0; |
831 | } |
832 | |
833 | sleepTimes[sleepTimesIndex%SAMPLE_COUNT] = sleepTime; |
834 | sleepTimesIndex++; |
835 | |
836 | unsigned int elementsForAverage = sleepTimesIndex > SAMPLE_COUNT ? SAMPLE_COUNT : sleepTimesIndex; |
837 | |
838 | // compute the average sleepTime |
839 | double sum = 0; |
840 | unsigned int index; |
841 | for(index = 0; index < elementsForAverage; index++) |
842 | { |
843 | sum += sleepTimes[index]; |
844 | } |
845 | |
846 | double averageSleep = sum/elementsForAverage; |
847 | |
848 | int sleepMs = (int)averageSleep; |
849 | |
850 | if(sleepMs > 0 && sleepMs < maxSleepNeeded*SpeedFactorMultiple && l_MainSpeedLimit) |
851 | { |
852 | DebugMessage(M64MSG_VERBOSE, " apply_speed_limiter(): Waiting %ims" , sleepMs); |
853 | |
854 | SDL_Delay(sleepMs); |
855 | } |
856 | |
857 | |
858 | #if defined(PROFILE) |
859 | timed_section_end(TIMED_SECTION_IDLE); |
860 | #endif |
861 | } |
862 | |
863 | /* TODO: make a GameShark module and move that there */ |
864 | static void gs_apply_cheats(struct cheat_ctx* ctx) |
865 | { |
866 | struct r4300_core* r4300 = &g_dev.r4300; |
867 | |
868 | if (g_gs_vi_counter < 60) |
869 | { |
870 | if (g_gs_vi_counter == 0) |
871 | cheat_apply_cheats(ctx, r4300, ENTRY_BOOT); |
872 | g_gs_vi_counter++; |
873 | } |
874 | else |
875 | { |
876 | cheat_apply_cheats(ctx, r4300, ENTRY_VI); |
877 | } |
878 | } |
879 | |
880 | static void pause_loop(void) |
881 | { |
882 | if(g_rom_pause) |
883 | { |
884 | osd_render(); // draw Paused message in case gfx.updateScreen didn't do it |
885 | VidExt_GL_SwapBuffers(); |
886 | while(g_rom_pause) |
887 | { |
888 | SDL_Delay(10); |
889 | main_check_inputs(); |
890 | } |
891 | } |
892 | } |
893 | |
894 | /* called on vertical interrupt. |
895 | * Allow the core to perform various things */ |
896 | void new_vi(void) |
897 | { |
898 | #if defined(PROFILE) |
899 | timed_sections_refresh(); |
900 | #endif |
901 | |
902 | gs_apply_cheats(&g_cheat_ctx); |
903 | |
904 | apply_speed_limiter(); |
905 | main_check_inputs(); |
906 | |
907 | pause_loop(); |
908 | } |
909 | |
910 | static void main_switch_pak(int control_id) |
911 | { |
912 | struct game_controller* cont = &g_dev.controllers[control_id]; |
913 | |
914 | change_pak(cont, l_paks[control_id][l_paks_idx[control_id]], l_ipaks[l_paks_idx[control_id]]); |
915 | |
916 | if (cont->ipak != NULL) { |
917 | DebugMessage(M64MSG_INFO, "Controller %u pak changed to %s" , control_id, cont->ipak->name); |
918 | } |
919 | else { |
920 | DebugMessage(M64MSG_INFO, "Removing pak from controller %u" , control_id); |
921 | } |
922 | } |
923 | |
924 | void main_switch_next_pak(int control_id) |
925 | { |
926 | if (l_ipaks[l_paks_idx[control_id]] == NULL || |
927 | ++l_paks_idx[control_id] >= PAK_MAX_SIZE) { |
928 | l_paks_idx[control_id] = 0; |
929 | } |
930 | |
931 | main_switch_pak(control_id); |
932 | } |
933 | |
934 | void main_switch_plugin_pak(int control_id) |
935 | { |
936 | //Don't switch to the selected pak if it's not available for the game |
937 | if (l_ipaks[l_pak_type_idx[Controls[control_id].Plugin]] == NULL) { |
938 | Controls[control_id].Plugin = PLUGIN_NONE; |
939 | } |
940 | |
941 | l_paks_idx[control_id] = l_pak_type_idx[Controls[control_id].Plugin]; |
942 | |
943 | main_switch_pak(control_id); |
944 | } |
945 | |
946 | static void open_mpk_file(struct file_storage* fstorage) |
947 | { |
948 | unsigned int i; |
949 | int ret = open_file_storage(fstorage, GAME_CONTROLLERS_COUNT*MEMPAK_SIZE, get_mempaks_path()); |
950 | |
951 | if (ret == (int)file_open_error) { |
952 | /* if file doesn't exists provide default content */ |
953 | for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { |
954 | format_mempak(fstorage->data + i * MEMPAK_SIZE); |
955 | } |
956 | } |
957 | } |
958 | |
959 | static void open_fla_file(struct file_storage* fstorage) |
960 | { |
961 | int ret = open_file_storage(fstorage, FLASHRAM_SIZE, get_flashram_path()); |
962 | |
963 | if (ret == (int)file_open_error) { |
964 | /* if file doesn't exists provide default content */ |
965 | format_flashram(fstorage->data); |
966 | } |
967 | } |
968 | |
969 | static void open_sra_file(struct file_storage* fstorage) |
970 | { |
971 | int ret = open_file_storage(fstorage, SRAM_SIZE, get_sram_path()); |
972 | |
973 | if (ret == (int)file_open_error) { |
974 | /* if file doesn't exists provide default content */ |
975 | format_sram(fstorage->data); |
976 | } |
977 | } |
978 | |
979 | static void open_eep_file(struct file_storage* fstorage) |
980 | { |
981 | /* Note: EEP files are all EEPROM_MAX_SIZE bytes long, |
982 | * whatever the real EEPROM size is. |
983 | */ |
984 | enum { EEPROM_MAX_SIZE = 0x800 }; |
985 | |
986 | int ret = open_file_storage(fstorage, EEPROM_MAX_SIZE, get_eeprom_path()); |
987 | |
988 | if (ret == (int)file_open_error) { |
989 | /* if file doesn't exists provide default content */ |
990 | format_eeprom(fstorage->data, EEPROM_MAX_SIZE); |
991 | } |
992 | |
993 | /* Truncate to 4k bit if necessary */ |
994 | if (ROM_SETTINGS.savetype != EEPROM_16KB) { |
995 | fstorage->size = 0x200; |
996 | } |
997 | } |
998 | |
999 | static void load_dd_rom(uint8_t* rom, size_t* rom_size) |
1000 | { |
1001 | /* ask the core loader for DD disk filename */ |
1002 | char* dd_ipl_rom_filename = (g_media_loader.get_dd_rom == NULL) |
1003 | ? NULL |
1004 | : g_media_loader.get_dd_rom(g_media_loader.cb_data); |
1005 | |
1006 | if ((dd_ipl_rom_filename == NULL) || (strlen(dd_ipl_rom_filename) == 0)) { |
1007 | goto no_dd; |
1008 | } |
1009 | |
1010 | struct file_storage dd_rom; |
1011 | memset(&dd_rom, 0, sizeof(dd_rom)); |
1012 | |
1013 | if (open_rom_file_storage(&dd_rom, dd_ipl_rom_filename) != file_ok) { |
1014 | DebugMessage(M64MSG_ERROR, "Failed to load DD IPL ROM: %s. Disabling 64DD" , dd_ipl_rom_filename); |
1015 | goto no_dd; |
1016 | } |
1017 | |
1018 | DebugMessage(M64MSG_INFO, "DD IPL ROM: %s" , dd_ipl_rom_filename); |
1019 | |
1020 | /* load and swap DD IPL ROM */ |
1021 | *rom_size = g_ifile_storage_ro.size(&dd_rom); |
1022 | memcpy(rom, g_ifile_storage_ro.data(&dd_rom), *rom_size); |
1023 | close_file_storage(&dd_rom); |
1024 | |
1025 | /* fetch 1st word to identify IPL ROM format */ |
1026 | /* FIXME: use more robust ROM detection heuristic - do the same for regular ROMs */ |
1027 | uint32_t pi_bsd_dom1_config = 0 |
1028 | | ((uint32_t)rom[0] << 24) |
1029 | | ((uint32_t)rom[1] << 16) |
1030 | | ((uint32_t)rom[2] << 8) |
1031 | | ((uint32_t)rom[3] << 0); |
1032 | |
1033 | switch (pi_bsd_dom1_config) |
1034 | { |
1035 | case 0x80270740: /* Z64 - big endian */ |
1036 | to_big_endian_buffer(rom, 4, *rom_size/4); |
1037 | break; |
1038 | |
1039 | case 0x40072780: /* N64 - little endian */ |
1040 | to_little_endian_buffer(rom, 4, *rom_size/4); |
1041 | break; |
1042 | |
1043 | case 0x27804007: /* V64 - bi-endian */ |
1044 | swap_buffer(rom, 2, *rom_size/2); |
1045 | break; |
1046 | |
1047 | default: /* unknown */ |
1048 | DebugMessage(M64MSG_ERROR, "Invalid DD IPL ROM: Disabling 64DD." ); |
1049 | *rom_size = 0; |
1050 | return; |
1051 | } |
1052 | |
1053 | return; |
1054 | |
1055 | no_dd: |
1056 | free(dd_ipl_rom_filename); |
1057 | *rom_size = 0; |
1058 | } |
1059 | |
1060 | static void load_dd_disk(struct file_storage* dd_disk, const struct storage_backend_interface** dd_idisk) |
1061 | { |
1062 | const char* format_desc; |
1063 | /* ask the core loader for DD disk filename */ |
1064 | char* dd_disk_filename = (g_media_loader.get_dd_disk == NULL) |
1065 | ? NULL |
1066 | : g_media_loader.get_dd_disk(g_media_loader.cb_data); |
1067 | |
1068 | /* handle the no disk case */ |
1069 | if (dd_disk_filename == NULL || strlen(dd_disk_filename) == 0) { |
1070 | goto no_disk; |
1071 | } |
1072 | |
1073 | /* open file */ |
1074 | if (open_rom_file_storage(dd_disk, dd_disk_filename) != file_ok) { |
1075 | DebugMessage(M64MSG_ERROR, "Failed to load DD Disk: %s." , dd_disk_filename); |
1076 | goto no_disk; |
1077 | } |
1078 | |
1079 | /* FIXME: handle byte swapping */ |
1080 | |
1081 | |
1082 | switch (dd_disk->size) |
1083 | { |
1084 | case MAME_FORMAT_DUMP_SIZE: |
1085 | /* already in a compatible format */ |
1086 | *dd_idisk = &g_ifile_storage; |
1087 | format_desc = "MAME" ; |
1088 | break; |
1089 | |
1090 | case SDK_FORMAT_DUMP_SIZE: { |
1091 | /* convert to mame format */ |
1092 | uint8_t* buffer = malloc(MAME_FORMAT_DUMP_SIZE); |
1093 | if (buffer == NULL) { |
1094 | DebugMessage(M64MSG_ERROR, "Failed to allocate memory for MAME disk dump" ); |
1095 | close_file_storage(dd_disk); |
1096 | goto no_disk; |
1097 | } |
1098 | |
1099 | dd_convert_to_mame(buffer, dd_disk->data); |
1100 | free(dd_disk->data); |
1101 | dd_disk->data = buffer; |
1102 | dd_disk->size = MAME_FORMAT_DUMP_SIZE; |
1103 | *dd_idisk = &g_ifile_storage_dd_sdk_dump; |
1104 | format_desc = "SDK" ; |
1105 | } break; |
1106 | |
1107 | default: |
1108 | DebugMessage(M64MSG_ERROR, "Invalid DD Disk size %u." , (uint32_t) dd_disk->size); |
1109 | close_file_storage(dd_disk); |
1110 | goto no_disk; |
1111 | } |
1112 | |
1113 | DebugMessage(M64MSG_INFO, "DD Disk: %s - %zu - %s" , |
1114 | dd_disk->filename, |
1115 | dd_disk->size, |
1116 | format_desc); |
1117 | |
1118 | uint32_t w = *(uint32_t*)dd_disk->data; |
1119 | if (w == DD_REGION_JP || w == DD_REGION_US) { |
1120 | DebugMessage(M64MSG_WARNING, "Loading a saved disk " ); |
1121 | } |
1122 | |
1123 | return; |
1124 | |
1125 | no_disk: |
1126 | free(dd_disk_filename); |
1127 | *dd_idisk = NULL; |
1128 | } |
1129 | |
1130 | |
1131 | struct gb_cart_data |
1132 | { |
1133 | int control_id; |
1134 | struct file_storage rom_fstorage; |
1135 | struct file_storage ram_fstorage; |
1136 | void* gbcam_backend; |
1137 | const struct video_capture_backend_interface* igbcam_backend; |
1138 | }; |
1139 | |
1140 | static struct gb_cart_data l_gb_carts_data[GAME_CONTROLLERS_COUNT]; |
1141 | |
1142 | static void init_gb_rom(void* opaque, void** storage, const struct storage_backend_interface** istorage) |
1143 | { |
1144 | struct gb_cart_data* data = (struct gb_cart_data*)opaque; |
1145 | |
1146 | /* Ask the core loader for rom filename */ |
1147 | char* rom_filename = (g_media_loader.get_gb_cart_rom == NULL) |
1148 | ? NULL |
1149 | : g_media_loader.get_gb_cart_rom(g_media_loader.cb_data, data->control_id); |
1150 | |
1151 | /* Handle the no cart case */ |
1152 | if (rom_filename == NULL || strlen(rom_filename) == 0) { |
1153 | goto no_cart; |
1154 | } |
1155 | |
1156 | /* Open ROM file */ |
1157 | if (open_rom_file_storage(&data->rom_fstorage, rom_filename) != file_ok) { |
1158 | DebugMessage(M64MSG_ERROR, "Failed to load ROM file: %s" , rom_filename); |
1159 | goto no_cart; |
1160 | } |
1161 | |
1162 | DebugMessage(M64MSG_INFO, "GB Loader ROM: %s - %zu" , |
1163 | data->rom_fstorage.filename, |
1164 | data->rom_fstorage.size); |
1165 | |
1166 | /* init GB ROM storage */ |
1167 | *storage = &data->rom_fstorage; |
1168 | *istorage = &g_ifile_storage_ro; |
1169 | return; |
1170 | |
1171 | no_cart: |
1172 | free(rom_filename); |
1173 | *storage = NULL; |
1174 | *istorage = NULL; |
1175 | } |
1176 | |
1177 | static void release_gb_rom(void* opaque) |
1178 | { |
1179 | struct gb_cart_data* data = (struct gb_cart_data*)opaque; |
1180 | |
1181 | close_file_storage(&data->rom_fstorage); |
1182 | |
1183 | memset(&data->rom_fstorage, 0, sizeof(data->rom_fstorage)); |
1184 | } |
1185 | |
1186 | static void init_gb_ram(void* opaque, size_t ram_size, void** storage, const struct storage_backend_interface** istorage) |
1187 | { |
1188 | struct gb_cart_data* data = (struct gb_cart_data*)opaque; |
1189 | |
1190 | /* Ask the core loader for ram filename */ |
1191 | char* ram_filename = (g_media_loader.get_gb_cart_ram == NULL) |
1192 | ? NULL |
1193 | : g_media_loader.get_gb_cart_ram(g_media_loader.cb_data, data->control_id); |
1194 | |
1195 | /* Handle the no RAM case |
1196 | * if NULL or empty string generate a filename |
1197 | */ |
1198 | if (ram_filename == NULL || strlen(ram_filename) == 0) { |
1199 | free(ram_filename); |
1200 | ram_filename = get_gb_ram_path(namefrompath(data->rom_fstorage.filename), data->control_id+1); |
1201 | } |
1202 | |
1203 | /* Open RAM file |
1204 | * if file doesn't exists provide default content */ |
1205 | int err = open_file_storage(&data->ram_fstorage, ram_size, ram_filename); |
1206 | if (err == file_open_error) { |
1207 | memset(data->ram_fstorage.data, 0, data->ram_fstorage.size); |
1208 | DebugMessage(M64MSG_INFO, "Providing default RAM content" ); |
1209 | } |
1210 | else if (err == file_read_error) { |
1211 | DebugMessage(M64MSG_WARNING, "Size mismatch between expected RAM size and effective file size" ); |
1212 | } |
1213 | |
1214 | DebugMessage(M64MSG_INFO, "GB Loader RAM: %s - %zu" , |
1215 | data->ram_fstorage.filename, |
1216 | data->ram_fstorage.size); |
1217 | |
1218 | /* init GB RAM storage */ |
1219 | *storage = &data->ram_fstorage; |
1220 | *istorage = &g_ifile_storage; |
1221 | } |
1222 | |
1223 | static void release_gb_ram(void* opaque) |
1224 | { |
1225 | struct gb_cart_data* data = (struct gb_cart_data*)opaque; |
1226 | |
1227 | close_file_storage(&data->ram_fstorage); |
1228 | |
1229 | memset(&data->ram_fstorage, 0, sizeof(data->ram_fstorage)); |
1230 | } |
1231 | |
1232 | void main_change_gb_cart(int control_id) |
1233 | { |
1234 | struct transferpak* tpk = &g_dev.transferpaks[control_id]; |
1235 | struct gb_cart* gb_cart = &g_dev.gb_carts[control_id]; |
1236 | struct gb_cart_data* data = &l_gb_carts_data[control_id]; |
1237 | |
1238 | /* reset gb_cart_data */ |
1239 | memset(data, 0, sizeof(*data)); |
1240 | data->control_id = control_id; |
1241 | |
1242 | init_gb_cart(gb_cart, |
1243 | data, init_gb_rom, release_gb_rom, |
1244 | data, init_gb_ram, release_gb_ram, |
1245 | NULL, &g_iclock_ctime_plus_delta, |
1246 | &data->control_id, &g_irumble_backend_plugin_compat, |
1247 | data->gbcam_backend, data->igbcam_backend); |
1248 | |
1249 | if (gb_cart->read_gb_cart == NULL) { |
1250 | gb_cart = NULL; |
1251 | } |
1252 | |
1253 | change_gb_cart(tpk, gb_cart); |
1254 | |
1255 | if (tpk->gb_cart != NULL) { |
1256 | const uint8_t* rom_data = gb_cart->irom_storage->data(gb_cart->rom_storage); |
1257 | DebugMessage(M64MSG_INFO, "Inserting GB cart %s into transferpak %u" , rom_data + 0x134, control_id); |
1258 | } |
1259 | else { |
1260 | DebugMessage(M64MSG_INFO, "Removing GB cart from transferpak %u" , control_id); |
1261 | } |
1262 | } |
1263 | |
1264 | |
1265 | /********************************************************************************************************* |
1266 | * emulation thread - runs the core |
1267 | */ |
1268 | |
1269 | |
1270 | m64p_error main_run(void) |
1271 | { |
1272 | size_t i, k; |
1273 | size_t rdram_size; |
1274 | unsigned int count_per_op; |
1275 | unsigned int emumode; |
1276 | unsigned int ; |
1277 | int si_dma_duration; |
1278 | int no_compiled_jump; |
1279 | int randomize_interrupt; |
1280 | struct file_storage eep; |
1281 | struct file_storage fla; |
1282 | struct file_storage sra; |
1283 | size_t dd_rom_size; |
1284 | struct file_storage dd_disk; |
1285 | |
1286 | int control_ids[GAME_CONTROLLERS_COUNT]; |
1287 | struct controller_input_compat cin_compats[GAME_CONTROLLERS_COUNT]; |
1288 | |
1289 | struct file_storage mpk_storages[GAME_CONTROLLERS_COUNT]; |
1290 | struct file_storage mpk; |
1291 | |
1292 | void* gbcam_backend; |
1293 | const struct video_capture_backend_interface* igbcam_backend; |
1294 | |
1295 | /* XXX: select type of flashram from db */ |
1296 | uint32_t flashram_type = MX29L1100_ID; |
1297 | |
1298 | |
1299 | /* take the r4300 emulator mode from the config file at this point and cache it in a global variable */ |
1300 | emumode = ConfigGetParamInt(g_CoreConfig, "R4300Emulator" ); |
1301 | |
1302 | /* set some other core parameters based on the config file values */ |
1303 | savestates_set_autoinc_slot(ConfigGetParamBool(g_CoreConfig, "AutoStateSlotIncrement" )); |
1304 | savestates_select_slot(ConfigGetParamInt(g_CoreConfig, "CurrentStateSlot" )); |
1305 | no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump" ); |
1306 | randomize_interrupt = ConfigGetParamBool(g_CoreConfig, "RandomizeInterrupt" ); |
1307 | count_per_op = ConfigGetParamInt(g_CoreConfig, "CountPerOp" ); |
1308 | |
1309 | if (ROM_PARAMS.disableextramem) |
1310 | disable_extra_mem = ROM_PARAMS.disableextramem; |
1311 | else |
1312 | disable_extra_mem = ConfigGetParamInt(g_CoreConfig, "DisableExtraMem" ); |
1313 | |
1314 | |
1315 | rdram_size = (disable_extra_mem == 0) ? 0x800000 : 0x400000; |
1316 | |
1317 | if (count_per_op <= 0) |
1318 | count_per_op = ROM_PARAMS.countperop; |
1319 | |
1320 | si_dma_duration = ConfigGetParamInt(g_CoreConfig, "SiDmaDuration" ); |
1321 | if (si_dma_duration < 0) |
1322 | si_dma_duration = ROM_PARAMS.sidmaduration; |
1323 | |
1324 | cheat_add_hacks(&g_cheat_ctx, ROM_PARAMS.cheats); |
1325 | |
1326 | /* do byte-swapping if it hasn't been done yet */ |
1327 | #if !defined(M64P_BIG_ENDIAN) |
1328 | if (g_RomWordsLittleEndian == 0) |
1329 | { |
1330 | swap_buffer((uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM), 4, g_rom_size/4); |
1331 | g_RomWordsLittleEndian = 1; |
1332 | } |
1333 | #endif |
1334 | |
1335 | /* Fill-in l_pak_type_idx and l_ipaks according to game compatibility */ |
1336 | k = 0; |
1337 | if (ROM_SETTINGS.biopak) { |
1338 | l_ipaks[k++] = &g_ibiopak; |
1339 | } |
1340 | if (ROM_SETTINGS.mempak) { |
1341 | l_pak_type_idx[PLUGIN_MEMPAK] = k; |
1342 | l_ipaks[k] = &g_imempak; |
1343 | ++k; |
1344 | } |
1345 | if (ROM_SETTINGS.rumble) { |
1346 | l_pak_type_idx[PLUGIN_RUMBLE_PAK] = k; |
1347 | l_pak_type_idx[PLUGIN_RAW] = k; |
1348 | l_ipaks[k] = &g_irumblepak; |
1349 | ++k; |
1350 | } |
1351 | if (ROM_SETTINGS.transferpak) { |
1352 | l_pak_type_idx[PLUGIN_TRANSFER_PAK] = k; |
1353 | l_ipaks[k] = &g_itransferpak; |
1354 | ++k; |
1355 | } |
1356 | l_pak_type_idx[PLUGIN_NONE] = k; |
1357 | l_ipaks[k] = NULL; |
1358 | |
1359 | if (!ROM_SETTINGS.mempak) { |
1360 | l_pak_type_idx[PLUGIN_MEMPAK] = k; |
1361 | } |
1362 | if (!ROM_SETTINGS.rumble) { |
1363 | l_pak_type_idx[PLUGIN_RUMBLE_PAK] = k; |
1364 | l_pak_type_idx[PLUGIN_RAW] = k; |
1365 | } |
1366 | if (!ROM_SETTINGS.transferpak) { |
1367 | l_pak_type_idx[PLUGIN_TRANSFER_PAK] = k; |
1368 | } |
1369 | |
1370 | /* init GbCamera backend specified in the configuration file */ |
1371 | init_video_capture_backend(&igbcam_backend, &gbcam_backend, |
1372 | g_CoreConfig, "GbCameraVideoCaptureBackend1" ); |
1373 | |
1374 | /* open GB cam video device */ |
1375 | igbcam_backend->open(gbcam_backend, M64282FP_SENSOR_W, M64282FP_SENSOR_H); |
1376 | |
1377 | /* open storage files, provide default content if not present */ |
1378 | open_mpk_file(&mpk); |
1379 | open_eep_file(&eep); |
1380 | open_fla_file(&fla); |
1381 | open_sra_file(&sra); |
1382 | |
1383 | /* Load 64DD IPL ROM and Disk */ |
1384 | const struct clock_backend_interface* dd_rtc_iclock = NULL; |
1385 | const struct storage_backend_interface* dd_idisk = NULL; |
1386 | memset(&dd_disk, 0, sizeof(dd_disk)); |
1387 | |
1388 | load_dd_rom((uint8_t*)mem_base_u32(g_mem_base, MM_DD_ROM), &dd_rom_size); |
1389 | if (dd_rom_size > 0) { |
1390 | dd_rtc_iclock = &g_iclock_ctime_plus_delta; |
1391 | load_dd_disk(&dd_disk, &dd_idisk); |
1392 | } |
1393 | |
1394 | /* setup pif channel devices */ |
1395 | void* joybus_devices[PIF_CHANNELS_COUNT]; |
1396 | const struct joybus_device_interface* ijoybus_devices[PIF_CHANNELS_COUNT]; |
1397 | |
1398 | memset(&g_dev.gb_carts, 0, GAME_CONTROLLERS_COUNT*sizeof(*g_dev.gb_carts)); |
1399 | memset(&l_gb_carts_data, 0, GAME_CONTROLLERS_COUNT*sizeof(*l_gb_carts_data)); |
1400 | memset(cin_compats, 0, GAME_CONTROLLERS_COUNT*sizeof(*cin_compats)); |
1401 | |
1402 | for (i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { |
1403 | |
1404 | control_ids[i] = (int)i; |
1405 | |
1406 | /* if input plugin requests RawData let the input plugin do the channel device processing */ |
1407 | if (Controls[i].RawData) { |
1408 | joybus_devices[i] = &control_ids[i]; |
1409 | ijoybus_devices[i] = &g_ijoybus_device_plugin_compat; |
1410 | } |
1411 | /* otherwise let the core do the processing */ |
1412 | else { |
1413 | /* select appropriate controller |
1414 | * FIXME: assume for now that only standard controller is compatible |
1415 | * Use the rom db to know if other peripherals are compatibles (VRU, mouse, train, ...) |
1416 | */ |
1417 | const struct game_controller_flavor* cont_flavor = |
1418 | &g_standard_controller_flavor; |
1419 | |
1420 | joybus_devices[i] = &g_dev.controllers[i]; |
1421 | ijoybus_devices[i] = &g_ijoybus_device_controller; |
1422 | |
1423 | cin_compats[i].control_id = (int)i; |
1424 | cin_compats[i].cont = &g_dev.controllers[i]; |
1425 | cin_compats[i].tpk = &g_dev.transferpaks[i]; |
1426 | cin_compats[i].last_pak_type = Controls[i].Plugin; |
1427 | |
1428 | l_gb_carts_data[i].control_id = (int)i; |
1429 | |
1430 | l_gb_carts_data[i].gbcam_backend = gbcam_backend; |
1431 | l_gb_carts_data[i].igbcam_backend = igbcam_backend; |
1432 | |
1433 | l_paks_idx[i] = 0; |
1434 | |
1435 | //Don't use the selected pak if it's not available for the game, instead use NONE |
1436 | if (l_ipaks[l_pak_type_idx[Controls[i].Plugin]] == NULL) { |
1437 | Controls[i].Plugin = PLUGIN_NONE; |
1438 | } |
1439 | |
1440 | /* init all compatibles paks */ |
1441 | for(k = 0; k < PAK_MAX_SIZE; ++k) { |
1442 | /* Bio Pak */ |
1443 | if (l_ipaks[k] == &g_ibiopak) { |
1444 | init_biopak(&g_dev.biopaks[i], 64); |
1445 | l_paks[i][k] = &g_dev.biopaks[i]; |
1446 | |
1447 | if (Controls[i].Plugin == PLUGIN_BIO_PAK) { |
1448 | l_paks_idx[i] = k; |
1449 | } |
1450 | } |
1451 | /* Memory Pak */ |
1452 | else if (l_ipaks[k] == &g_imempak) { |
1453 | mpk_storages[i].data = mpk.data + i * MEMPAK_SIZE; |
1454 | mpk_storages[i].size = MEMPAK_SIZE; |
1455 | mpk_storages[i].filename = (void*)&mpk; /* OK for isubfile_storage */ |
1456 | |
1457 | init_mempak(&g_dev.mempaks[i], &mpk_storages[i], &g_isubfile_storage); |
1458 | l_paks[i][k] = &g_dev.mempaks[i]; |
1459 | |
1460 | if (Controls[i].Plugin == PLUGIN_MEMPAK) { |
1461 | l_paks_idx[i] = k; |
1462 | } |
1463 | } |
1464 | /* Rumble Pak */ |
1465 | else if (l_ipaks[k] == &g_irumblepak) { |
1466 | init_rumblepak(&g_dev.rumblepaks[i], &control_ids[i], &g_irumble_backend_plugin_compat); |
1467 | l_paks[i][k] = &g_dev.rumblepaks[i]; |
1468 | |
1469 | if (Controls[i].Plugin == PLUGIN_RUMBLE_PAK |
1470 | || Controls[i].Plugin == PLUGIN_RAW) { |
1471 | l_paks_idx[i] = k; |
1472 | } |
1473 | } |
1474 | /* Transfer Pak */ |
1475 | else if (l_ipaks[k] == &g_itransferpak) { |
1476 | |
1477 | /* init GB cart */ |
1478 | init_gb_cart(&g_dev.gb_carts[i], |
1479 | &l_gb_carts_data[i], init_gb_rom, release_gb_rom, |
1480 | &l_gb_carts_data[i], init_gb_ram, release_gb_ram, |
1481 | NULL, &g_iclock_ctime_plus_delta, |
1482 | &l_gb_carts_data[i].control_id, &g_irumble_backend_plugin_compat, |
1483 | l_gb_carts_data[i].gbcam_backend, l_gb_carts_data[i].igbcam_backend); |
1484 | |
1485 | init_transferpak(&g_dev.transferpaks[i], (g_dev.gb_carts[i].read_gb_cart == NULL) ? NULL : &g_dev.gb_carts[i]); |
1486 | l_paks[i][k] = &g_dev.transferpaks[i]; |
1487 | |
1488 | if (Controls[i].Plugin == PLUGIN_TRANSFER_PAK) { |
1489 | l_paks_idx[i] = k; |
1490 | } |
1491 | |
1492 | /* enable GB cart switch */ |
1493 | cin_compats[i].gb_cart_switch_enabled = 1; |
1494 | } |
1495 | /* No Pak */ |
1496 | else { |
1497 | l_ipaks[k] = NULL; |
1498 | l_paks[i][k] = NULL; |
1499 | |
1500 | if (Controls[i].Plugin == PLUGIN_NONE) { |
1501 | l_paks_idx[i] = k; |
1502 | } |
1503 | |
1504 | break; |
1505 | } |
1506 | } |
1507 | |
1508 | /* init game_controller */ |
1509 | init_game_controller(&g_dev.controllers[i], |
1510 | cont_flavor, |
1511 | &cin_compats[i], &g_icontroller_input_backend_plugin_compat, |
1512 | l_paks[i][l_paks_idx[i]], l_ipaks[l_paks_idx[i]]); |
1513 | |
1514 | if (l_ipaks[l_paks_idx[i]] != NULL) { |
1515 | DebugMessage(M64MSG_INFO, "Game controller %u (%s) has a %s plugged in" , |
1516 | (uint32_t) i, cont_flavor->name, l_ipaks[l_paks_idx[i]]->name); |
1517 | } else { |
1518 | DebugMessage(M64MSG_INFO, "Game controller %u (%s) has nothing plugged in" , |
1519 | (uint32_t) i, cont_flavor->name); |
1520 | } |
1521 | } |
1522 | } |
1523 | for (i = GAME_CONTROLLERS_COUNT; i < PIF_CHANNELS_COUNT; ++i) { |
1524 | joybus_devices[i] = &g_dev.cart; |
1525 | ijoybus_devices[i] = &g_ijoybus_device_cart; |
1526 | } |
1527 | |
1528 | |
1529 | init_device(&g_dev, |
1530 | g_mem_base, |
1531 | emumode, |
1532 | count_per_op, |
1533 | no_compiled_jump, |
1534 | randomize_interrupt, |
1535 | &g_dev.ai, &g_iaudio_out_backend_plugin_compat, |
1536 | si_dma_duration, |
1537 | rdram_size, |
1538 | joybus_devices, ijoybus_devices, |
1539 | vi_clock_from_tv_standard(ROM_PARAMS.systemtype), vi_expected_refresh_rate_from_tv_standard(ROM_PARAMS.systemtype), |
1540 | NULL, &g_iclock_ctime_plus_delta, |
1541 | g_rom_size, |
1542 | (ROM_SETTINGS.savetype != EEPROM_16KB) ? JDT_EEPROM_4K : JDT_EEPROM_16K, |
1543 | &eep, &g_ifile_storage, |
1544 | flashram_type, |
1545 | &fla, &g_ifile_storage, |
1546 | &sra, &g_ifile_storage, |
1547 | NULL, dd_rtc_iclock, |
1548 | dd_rom_size, |
1549 | &dd_disk, dd_idisk); |
1550 | |
1551 | // Attach rom to plugins |
1552 | if (!gfx.romOpen()) |
1553 | { |
1554 | goto on_gfx_open_failure; |
1555 | } |
1556 | if (!audio.romOpen()) |
1557 | { |
1558 | goto on_audio_open_failure; |
1559 | } |
1560 | if (!input.romOpen()) |
1561 | { |
1562 | goto on_input_open_failure; |
1563 | } |
1564 | |
1565 | /* set up the SDL key repeat and event filter to catch keyboard/joystick commands for the core */ |
1566 | event_initialize(); |
1567 | |
1568 | /* initialize the on-screen display */ |
1569 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay" )) |
1570 | { |
1571 | // init on-screen display |
1572 | int width = 640, height = 480; |
1573 | gfx.readScreen(NULL, &width, &height, 0); // read screen to get width and height |
1574 | osd_init(width, height); |
1575 | } |
1576 | |
1577 | // setup rendering callback from video plugin to the core, for screenshots and On-Screen-Display |
1578 | gfx.setRenderingCallback(video_plugin_render_callback); |
1579 | |
1580 | #ifdef WITH_LIRC |
1581 | lircStart(); |
1582 | #endif // WITH_LIRC |
1583 | |
1584 | #ifdef DBG |
1585 | if (ConfigGetParamBool(g_CoreConfig, "EnableDebugger" )) |
1586 | init_debugger(); |
1587 | #endif |
1588 | |
1589 | /* Startup message on the OSD */ |
1590 | osd_new_message(OSD_MIDDLE_CENTER, "Mupen64Plus Started..." ); |
1591 | |
1592 | g_EmulatorRunning = 1; |
1593 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
1594 | |
1595 | poweron_device(&g_dev); |
1596 | pif_bootrom_hle_execute(&g_dev.r4300); |
1597 | run_device(&g_dev); |
1598 | |
1599 | /* now begin to shut down */ |
1600 | #ifdef WITH_LIRC |
1601 | lircStop(); |
1602 | #endif // WITH_LIRC |
1603 | |
1604 | #ifdef DBG |
1605 | if (g_DebuggerActive) |
1606 | destroy_debugger(); |
1607 | #endif |
1608 | /* release gb_carts */ |
1609 | for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { |
1610 | if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) { |
1611 | release_gb_rom(&l_gb_carts_data[i]); |
1612 | release_gb_ram(&l_gb_carts_data[i]); |
1613 | } |
1614 | } |
1615 | |
1616 | igbcam_backend->close(gbcam_backend); |
1617 | igbcam_backend->release(gbcam_backend); |
1618 | |
1619 | close_file_storage(&sra); |
1620 | close_file_storage(&fla); |
1621 | close_file_storage(&eep); |
1622 | close_file_storage(&mpk); |
1623 | close_file_storage(&dd_disk); |
1624 | |
1625 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay" )) |
1626 | { |
1627 | osd_exit(); |
1628 | } |
1629 | |
1630 | rsp.romClosed(); |
1631 | input.romClosed(); |
1632 | audio.romClosed(); |
1633 | gfx.romClosed(); |
1634 | |
1635 | // clean up |
1636 | g_EmulatorRunning = 0; |
1637 | StateChanged(M64CORE_EMU_STATE, M64EMU_STOPPED); |
1638 | |
1639 | return M64ERR_SUCCESS; |
1640 | |
1641 | on_input_open_failure: |
1642 | audio.romClosed(); |
1643 | on_audio_open_failure: |
1644 | gfx.romClosed(); |
1645 | on_gfx_open_failure: |
1646 | /* release gb_carts */ |
1647 | for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) { |
1648 | if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) { |
1649 | release_gb_rom(&l_gb_carts_data[i]); |
1650 | release_gb_ram(&l_gb_carts_data[i]); |
1651 | } |
1652 | } |
1653 | |
1654 | igbcam_backend->close(gbcam_backend); |
1655 | igbcam_backend->release(gbcam_backend); |
1656 | |
1657 | /* release storage files */ |
1658 | close_file_storage(&sra); |
1659 | close_file_storage(&fla); |
1660 | close_file_storage(&eep); |
1661 | close_file_storage(&mpk); |
1662 | close_file_storage(&dd_disk); |
1663 | |
1664 | return M64ERR_PLUGIN_FAIL; |
1665 | } |
1666 | |
1667 | void main_stop(void) |
1668 | { |
1669 | /* note: this operation is asynchronous. It may be called from a thread other than the |
1670 | main emulator thread, and may return before the emulator is completely stopped */ |
1671 | if (!g_EmulatorRunning) |
1672 | return; |
1673 | |
1674 | DebugMessage(M64MSG_STATUS, "Stopping emulation." ); |
1675 | if(l_msgPause) |
1676 | { |
1677 | osd_delete_message(l_msgPause); |
1678 | l_msgPause = NULL; |
1679 | } |
1680 | if(l_msgFF) |
1681 | { |
1682 | osd_delete_message(l_msgFF); |
1683 | l_msgFF = NULL; |
1684 | } |
1685 | if(l_msgVol) |
1686 | { |
1687 | osd_delete_message(l_msgVol); |
1688 | l_msgVol = NULL; |
1689 | } |
1690 | if (g_rom_pause) |
1691 | { |
1692 | g_rom_pause = 0; |
1693 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
1694 | } |
1695 | |
1696 | stop_device(&g_dev); |
1697 | |
1698 | #ifdef DBG |
1699 | if(g_DebuggerActive) |
1700 | { |
1701 | debugger_step(); |
1702 | } |
1703 | #endif |
1704 | } |
1705 | |