451ab91e |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus - main.c * |
3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
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 <string.h> |
32 | #include <stdlib.h> |
33 | |
34 | #include <SDL.h> |
35 | |
36 | #define M64P_CORE_PROTOTYPES 1 |
37 | #include "api/m64p_types.h" |
38 | #include "api/callbacks.h" |
39 | #include "api/config.h" |
40 | #include "api/m64p_config.h" |
41 | #include "api/debugger.h" |
42 | #include "api/vidext.h" |
43 | |
44 | #include "main.h" |
45 | #include "eventloop.h" |
46 | #include "rom.h" |
47 | #include "savestates.h" |
48 | #include "util.h" |
49 | |
50 | #include "memory/memory.h" |
51 | #include "osal/files.h" |
52 | #include "osal/preproc.h" |
53 | #include "osd/osd.h" |
54 | #include "osd/screenshot.h" |
55 | #include "plugin/plugin.h" |
56 | #include "r4300/r4300.h" |
57 | #include "r4300/interupt.h" |
58 | #include "r4300/reset.h" |
59 | |
60 | #ifdef DBG |
61 | #include "debugger/dbg_types.h" |
62 | #include "debugger/debugger.h" |
63 | #endif |
64 | |
65 | #ifdef WITH_LIRC |
66 | #include "lirc.h" |
67 | #endif //WITH_LIRC |
68 | |
69 | /* version number for Core config section */ |
70 | #define CONFIG_PARAM_VERSION 1.01 |
71 | |
72 | /** globals **/ |
73 | m64p_handle g_CoreConfig = NULL; |
74 | |
75 | m64p_frame_callback g_FrameCallback = NULL; |
76 | |
77 | int g_MemHasBeenBSwapped = 0; // store byte-swapped flag so we don't swap twice when re-playing game |
78 | int g_EmulatorRunning = 0; // need separate boolean to tell if emulator is running, since --nogui doesn't use a thread |
79 | |
80 | /** static (local) variables **/ |
81 | static int l_CurrentFrame = 0; // frame counter |
82 | static int l_TakeScreenshot = 0; // Tell OSD Rendering callback to take a screenshot just before drawing the OSD |
83 | static int l_SpeedFactor = 100; // percentage of nominal game speed at which emulator is running |
84 | static int l_FrameAdvance = 0; // variable to check if we pause on next frame |
85 | static int l_MainSpeedLimit = 1; // insert delay during vi_interrupt to keep speed at real-time |
86 | |
87 | static osd_message_t *l_msgVol = NULL; |
88 | static osd_message_t *l_msgFF = NULL; |
89 | static osd_message_t *l_msgPause = NULL; |
90 | |
91 | /********************************************************************************************************* |
92 | * static functions |
93 | */ |
94 | |
95 | static const char *get_savepathdefault(const char *configpath) |
96 | { |
97 | static char path[1024]; |
98 | |
99 | if (!configpath || (strlen(configpath) == 0)) { |
100 | snprintf(path, 1024, "%ssave%c", ConfigGetUserDataPath(), OSAL_DIR_SEPARATORS[0]); |
101 | path[1023] = 0; |
102 | } else { |
103 | snprintf(path, 1024, "%s%c", configpath, OSAL_DIR_SEPARATORS[0]); |
104 | path[1023] = 0; |
105 | } |
106 | |
107 | /* create directory if it doesn't exist */ |
108 | osal_mkdirp(path, 0700); |
109 | |
110 | return path; |
111 | } |
112 | |
113 | |
114 | /********************************************************************************************************* |
115 | * helper functions |
116 | */ |
117 | |
118 | |
119 | const char *get_savestatepath(void) |
120 | { |
121 | /* try to get the SaveStatePath string variable in the Core configuration section */ |
122 | return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveStatePath")); |
123 | } |
124 | |
125 | const char *get_savesrampath(void) |
126 | { |
127 | /* try to get the SaveSRAMPath string variable in the Core configuration section */ |
128 | return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveSRAMPath")); |
129 | } |
130 | |
131 | void main_message(m64p_msg_level level, unsigned int corner, const char *format, ...) |
132 | { |
133 | va_list ap; |
134 | char buffer[2049]; |
135 | va_start(ap, format); |
136 | vsnprintf(buffer, 2047, format, ap); |
137 | buffer[2048]='\0'; |
138 | va_end(ap); |
139 | |
140 | /* send message to on-screen-display if enabled */ |
141 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay")) |
142 | osd_new_message((enum osd_corner) corner, "%s", buffer); |
143 | /* send message to front-end */ |
144 | DebugMessage(level, "%s", buffer); |
145 | } |
146 | |
147 | |
148 | /********************************************************************************************************* |
149 | * global functions, for adjusting the core emulator behavior |
150 | */ |
151 | |
152 | int main_set_core_defaults(void) |
153 | { |
154 | float fConfigParamsVersion; |
155 | int bSaveConfig = 0, bUpgrade = 0; |
156 | |
157 | if (ConfigGetParameter(g_CoreConfig, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) |
158 | { |
159 | DebugMessage(M64MSG_WARNING, "No version number in 'Core' config section. Setting defaults."); |
160 | ConfigDeleteSection("Core"); |
161 | ConfigOpenSection("Core", &g_CoreConfig); |
162 | bSaveConfig = 1; |
163 | } |
164 | else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) |
165 | { |
166 | DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Core' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); |
167 | ConfigDeleteSection("Core"); |
168 | ConfigOpenSection("Core", &g_CoreConfig); |
169 | bSaveConfig = 1; |
170 | } |
171 | else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) |
172 | { |
173 | float fVersion = (float) CONFIG_PARAM_VERSION; |
174 | ConfigSetParameter(g_CoreConfig, "Version", M64TYPE_FLOAT, &fVersion); |
175 | DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Core' config section to %.2f", fVersion); |
176 | bUpgrade = 1; |
177 | bSaveConfig = 1; |
178 | } |
179 | |
180 | /* parameters controlling the operation of the core */ |
181 | ConfigSetDefaultFloat(g_CoreConfig, "Version", (float) CONFIG_PARAM_VERSION, "Mupen64Plus Core config parameter set version number. Please don't change this version number."); |
182 | ConfigSetDefaultBool(g_CoreConfig, "OnScreenDisplay", 1, "Draw on-screen display if True, otherwise don't draw OSD"); |
183 | #if defined(DYNAREC) |
184 | ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator", 2, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more"); |
185 | #else |
186 | ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator", 1, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more"); |
187 | #endif |
188 | ConfigSetDefaultBool(g_CoreConfig, "NoCompiledJump", 0, "Disable compiled jump commands in dynamic recompiler (should be set to False) "); |
189 | ConfigSetDefaultBool(g_CoreConfig, "DisableExtraMem", 0, "Disable 4MB expansion RAM pack. May be necessary for some games"); |
190 | ConfigSetDefaultBool(g_CoreConfig, "AutoStateSlotIncrement", 0, "Increment the save state slot after each save operation"); |
191 | ConfigSetDefaultBool(g_CoreConfig, "EnableDebugger", 0, "Activate the R4300 debugger when ROM execution begins, if core was built with Debugger support"); |
192 | ConfigSetDefaultInt(g_CoreConfig, "CurrentStateSlot", 0, "Save state slot (0-9) to use when saving/loading the emulator state"); |
193 | ConfigSetDefaultString(g_CoreConfig, "ScreenshotPath", "", "Path to directory where screenshots are saved. If this is blank, the default value of ${UserConfigPath}/screenshot will be used"); |
194 | ConfigSetDefaultString(g_CoreConfig, "SaveStatePath", "", "Path to directory where emulator save states (snapshots) are saved. If this is blank, the default value of ${UserConfigPath}/save will be used"); |
195 | ConfigSetDefaultString(g_CoreConfig, "SaveSRAMPath", "", "Path to directory where SRAM/EEPROM data (in-game saves) are stored. If this is blank, the default value of ${UserConfigPath}/save will be used"); |
196 | ConfigSetDefaultString(g_CoreConfig, "SharedDataPath", "", "Path to a directory to search when looking for shared data files"); |
197 | |
198 | /* handle upgrades */ |
199 | if (bUpgrade) |
200 | { |
201 | if (fConfigParamsVersion < 1.01f) |
202 | { // added separate SaveSRAMPath parameter in v1.01 |
203 | const char *pccSaveStatePath = ConfigGetParamString(g_CoreConfig, "SaveStatePath"); |
204 | if (pccSaveStatePath != NULL) |
205 | ConfigSetParameter(g_CoreConfig, "SaveSRAMPath", M64TYPE_STRING, pccSaveStatePath); |
206 | } |
207 | } |
208 | |
209 | if (bSaveConfig) |
210 | ConfigSaveSection("Core"); |
211 | |
212 | /* set config parameters for keyboard and joystick commands */ |
213 | return event_set_core_defaults(); |
214 | } |
215 | |
216 | void main_speeddown(int percent) |
217 | { |
218 | if (l_SpeedFactor - percent > 10) /* 10% minimum speed */ |
219 | { |
220 | l_SpeedFactor -= percent; |
221 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
222 | audio.setSpeedFactor(l_SpeedFactor); |
223 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
224 | } |
225 | } |
226 | |
227 | void main_speedup(int percent) |
228 | { |
229 | if (l_SpeedFactor + percent < 300) /* 300% maximum speed */ |
230 | { |
231 | l_SpeedFactor += percent; |
232 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
233 | audio.setSpeedFactor(l_SpeedFactor); |
234 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
235 | } |
236 | } |
237 | |
238 | static void main_speedset(int percent) |
239 | { |
240 | if (percent < 1 || percent > 1000) |
241 | { |
242 | DebugMessage(M64MSG_WARNING, "Invalid speed setting %i percent", percent); |
243 | return; |
244 | } |
245 | // disable fast-forward if it's enabled |
246 | main_set_fastforward(0); |
247 | // set speed |
248 | l_SpeedFactor = percent; |
249 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
250 | audio.setSpeedFactor(l_SpeedFactor); |
251 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
252 | } |
253 | |
254 | void main_set_fastforward(int enable) |
255 | { |
256 | static int ff_state = 0; |
257 | static int SavedSpeedFactor = 100; |
258 | |
259 | if (enable && !ff_state) |
260 | { |
261 | ff_state = 1; /* activate fast-forward */ |
262 | SavedSpeedFactor = l_SpeedFactor; |
263 | l_SpeedFactor = 250; |
264 | audio.setSpeedFactor(l_SpeedFactor); |
265 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
266 | // set fast-forward indicator |
267 | l_msgFF = osd_new_message(OSD_TOP_RIGHT, "Fast Forward"); |
268 | osd_message_set_static(l_msgFF); |
269 | osd_message_set_user_managed(l_msgFF); |
270 | } |
271 | else if (!enable && ff_state) |
272 | { |
273 | ff_state = 0; /* de-activate fast-forward */ |
274 | l_SpeedFactor = SavedSpeedFactor; |
275 | audio.setSpeedFactor(l_SpeedFactor); |
276 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
277 | // remove message |
278 | osd_delete_message(l_msgFF); |
279 | l_msgFF = NULL; |
280 | } |
281 | |
282 | } |
283 | |
284 | static void main_set_speedlimiter(int enable) |
285 | { |
286 | l_MainSpeedLimit = enable ? 1 : 0; |
287 | } |
288 | |
289 | static int main_is_paused(void) |
290 | { |
291 | return (g_EmulatorRunning && rompause); |
292 | } |
293 | |
294 | void main_toggle_pause(void) |
295 | { |
296 | if (!g_EmulatorRunning) |
297 | return; |
298 | |
299 | if (rompause) |
300 | { |
301 | DebugMessage(M64MSG_STATUS, "Emulation continued."); |
302 | if(l_msgPause) |
303 | { |
304 | osd_delete_message(l_msgPause); |
305 | l_msgPause = NULL; |
306 | } |
307 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
308 | } |
309 | else |
310 | { |
311 | if(l_msgPause) |
312 | osd_delete_message(l_msgPause); |
313 | |
314 | DebugMessage(M64MSG_STATUS, "Emulation paused."); |
315 | l_msgPause = osd_new_message(OSD_MIDDLE_CENTER, "Paused"); |
316 | osd_message_set_static(l_msgPause); |
317 | osd_message_set_user_managed(l_msgPause); |
318 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
319 | } |
320 | |
321 | rompause = !rompause; |
322 | l_FrameAdvance = 0; |
323 | } |
324 | |
325 | void main_advance_one(void) |
326 | { |
327 | l_FrameAdvance = 1; |
328 | rompause = 0; |
329 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
330 | } |
331 | |
332 | static void main_draw_volume_osd(void) |
333 | { |
334 | char msgString[64]; |
335 | const char *volString; |
336 | |
337 | // this calls into the audio plugin |
338 | volString = audio.volumeGetString(); |
339 | if (volString == NULL) |
340 | { |
341 | strcpy(msgString, "Volume Not Supported."); |
342 | } |
343 | else |
344 | { |
345 | sprintf(msgString, "%s: %s", "Volume", volString); |
346 | } |
347 | |
348 | // create a new message or update an existing one |
349 | if (l_msgVol != NULL) |
350 | osd_update_message(l_msgVol, "%s", msgString); |
351 | else { |
352 | l_msgVol = osd_new_message(OSD_MIDDLE_CENTER, "%s", msgString); |
353 | osd_message_set_user_managed(l_msgVol); |
354 | } |
355 | } |
356 | |
357 | /* this function could be called as a result of a keypress, joystick/button movement, |
358 | LIRC command, or 'testshots' command-line option timer */ |
359 | void main_take_next_screenshot(void) |
360 | { |
361 | l_TakeScreenshot = l_CurrentFrame + 1; |
362 | } |
363 | |
364 | void main_state_set_slot(int slot) |
365 | { |
366 | if (slot < 0 || slot > 9) |
367 | { |
368 | DebugMessage(M64MSG_WARNING, "Invalid savestate slot '%i' in main_state_set_slot(). Using 0", slot); |
369 | slot = 0; |
370 | } |
371 | |
372 | savestates_select_slot(slot); |
373 | StateChanged(M64CORE_SAVESTATE_SLOT, slot); |
374 | } |
375 | |
376 | void main_state_inc_slot(void) |
377 | { |
378 | savestates_inc_slot(); |
379 | } |
380 | |
381 | static unsigned char StopRumble[64] = {0x23, 0x01, 0x03, 0xc0, 0x1b, 0x00, 0x00, 0x00, |
382 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
383 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
384 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
385 | 0, 0, 0, 0, 0, 0, 0, 0}; |
386 | |
387 | void main_state_load(const char *filename) |
388 | { |
389 | input.controllerCommand(0, StopRumble); |
390 | input.controllerCommand(1, StopRumble); |
391 | input.controllerCommand(2, StopRumble); |
392 | input.controllerCommand(3, StopRumble); |
393 | |
394 | if (filename == NULL) // Save to slot |
395 | savestates_set_job(savestates_job_load, savestates_type_m64p, NULL); |
396 | else |
397 | savestates_set_job(savestates_job_load, savestates_type_unknown, filename); |
398 | } |
399 | |
400 | void main_state_save(int format, const char *filename) |
401 | { |
402 | if (filename == NULL) // Save to slot |
403 | savestates_set_job(savestates_job_save, savestates_type_m64p, NULL); |
404 | else // Save to file |
405 | savestates_set_job(savestates_job_save, (savestates_type)format, filename); |
406 | } |
407 | |
408 | m64p_error main_core_state_query(m64p_core_param param, int *rval) |
409 | { |
410 | switch (param) |
411 | { |
412 | case M64CORE_EMU_STATE: |
413 | if (!g_EmulatorRunning) |
414 | *rval = M64EMU_STOPPED; |
415 | else if (rompause) |
416 | *rval = M64EMU_PAUSED; |
417 | else |
418 | *rval = M64EMU_RUNNING; |
419 | break; |
420 | case M64CORE_VIDEO_MODE: |
421 | if (!VidExt_VideoRunning()) |
422 | *rval = M64VIDEO_NONE; |
423 | else if (VidExt_InFullscreenMode()) |
424 | *rval = M64VIDEO_FULLSCREEN; |
425 | else |
426 | *rval = M64VIDEO_WINDOWED; |
427 | break; |
428 | case M64CORE_SAVESTATE_SLOT: |
429 | *rval = savestates_get_slot(); |
430 | break; |
431 | case M64CORE_SPEED_FACTOR: |
432 | *rval = l_SpeedFactor; |
433 | break; |
434 | case M64CORE_SPEED_LIMITER: |
435 | *rval = l_MainSpeedLimit; |
436 | break; |
437 | case M64CORE_VIDEO_SIZE: |
438 | { |
439 | int width, height; |
440 | if (!g_EmulatorRunning) |
441 | return M64ERR_INVALID_STATE; |
442 | main_get_screen_size(&width, &height); |
443 | *rval = (width << 16) + height; |
444 | break; |
445 | } |
446 | case M64CORE_AUDIO_VOLUME: |
447 | { |
448 | if (!g_EmulatorRunning) |
449 | return M64ERR_INVALID_STATE; |
450 | return main_volume_get_level(rval); |
451 | } |
452 | case M64CORE_AUDIO_MUTE: |
453 | *rval = main_volume_get_muted(); |
454 | break; |
455 | case M64CORE_INPUT_GAMESHARK: |
456 | *rval = event_gameshark_active(); |
457 | break; |
458 | // these are only used for callbacks; they cannot be queried or set |
459 | case M64CORE_STATE_LOADCOMPLETE: |
460 | case M64CORE_STATE_SAVECOMPLETE: |
461 | return M64ERR_INPUT_INVALID; |
462 | default: |
463 | return M64ERR_INPUT_INVALID; |
464 | } |
465 | |
466 | return M64ERR_SUCCESS; |
467 | } |
468 | |
469 | m64p_error main_core_state_set(m64p_core_param param, int val) |
470 | { |
471 | switch (param) |
472 | { |
473 | case M64CORE_EMU_STATE: |
474 | if (!g_EmulatorRunning) |
475 | return M64ERR_INVALID_STATE; |
476 | if (val == M64EMU_STOPPED) |
477 | { |
478 | /* this stop function is asynchronous. The emulator may not terminate until later */ |
479 | main_stop(); |
480 | return M64ERR_SUCCESS; |
481 | } |
482 | else if (val == M64EMU_RUNNING) |
483 | { |
484 | if (main_is_paused()) |
485 | main_toggle_pause(); |
486 | return M64ERR_SUCCESS; |
487 | } |
488 | else if (val == M64EMU_PAUSED) |
489 | { |
490 | if (!main_is_paused()) |
491 | main_toggle_pause(); |
492 | return M64ERR_SUCCESS; |
493 | } |
494 | return M64ERR_INPUT_INVALID; |
495 | case M64CORE_VIDEO_MODE: |
496 | if (!g_EmulatorRunning) |
497 | return M64ERR_INVALID_STATE; |
498 | if (val == M64VIDEO_WINDOWED) |
499 | { |
500 | if (VidExt_InFullscreenMode()) |
501 | gfx.changeWindow(); |
502 | return M64ERR_SUCCESS; |
503 | } |
504 | else if (val == M64VIDEO_FULLSCREEN) |
505 | { |
506 | if (!VidExt_InFullscreenMode()) |
507 | gfx.changeWindow(); |
508 | return M64ERR_SUCCESS; |
509 | } |
510 | return M64ERR_INPUT_INVALID; |
511 | case M64CORE_SAVESTATE_SLOT: |
512 | if (val < 0 || val > 9) |
513 | return M64ERR_INPUT_INVALID; |
514 | savestates_select_slot(val); |
515 | return M64ERR_SUCCESS; |
516 | case M64CORE_SPEED_FACTOR: |
517 | if (!g_EmulatorRunning) |
518 | return M64ERR_INVALID_STATE; |
519 | main_speedset(val); |
520 | return M64ERR_SUCCESS; |
521 | case M64CORE_SPEED_LIMITER: |
522 | main_set_speedlimiter(val); |
523 | return M64ERR_SUCCESS; |
524 | case M64CORE_VIDEO_SIZE: |
525 | { |
526 | // the front-end app is telling us that the user has resized the video output frame, and so |
527 | // we should try to update the video plugin accordingly. First, check state |
528 | int width, height; |
529 | if (!g_EmulatorRunning) |
530 | return M64ERR_INVALID_STATE; |
531 | width = (val >> 16) & 0xffff; |
532 | height = val & 0xffff; |
533 | // then call the video plugin. if the video plugin supports resizing, it will resize its viewport and call |
534 | // VidExt_ResizeWindow to update the window manager handling our opengl output window |
535 | gfx.resizeVideoOutput(width, height); |
536 | return M64ERR_SUCCESS; |
537 | } |
538 | case M64CORE_AUDIO_VOLUME: |
539 | if (!g_EmulatorRunning) |
540 | return M64ERR_INVALID_STATE; |
541 | if (val < 0 || val > 100) |
542 | return M64ERR_INPUT_INVALID; |
543 | return main_volume_set_level(val); |
544 | case M64CORE_AUDIO_MUTE: |
545 | if ((main_volume_get_muted() && !val) || (!main_volume_get_muted() && val)) |
546 | return main_volume_mute(); |
547 | return M64ERR_SUCCESS; |
548 | case M64CORE_INPUT_GAMESHARK: |
549 | if (!g_EmulatorRunning) |
550 | return M64ERR_INVALID_STATE; |
551 | event_set_gameshark(val); |
552 | return M64ERR_SUCCESS; |
553 | // these are only used for callbacks; they cannot be queried or set |
554 | case M64CORE_STATE_LOADCOMPLETE: |
555 | case M64CORE_STATE_SAVECOMPLETE: |
556 | return M64ERR_INPUT_INVALID; |
557 | default: |
558 | return M64ERR_INPUT_INVALID; |
559 | } |
560 | } |
561 | |
562 | m64p_error main_get_screen_size(int *width, int *height) |
563 | { |
564 | gfx.readScreen(NULL, width, height, 0); |
565 | return M64ERR_SUCCESS; |
566 | } |
567 | |
568 | m64p_error main_read_screen(void *pixels, int bFront) |
569 | { |
570 | int width_trash, height_trash; |
571 | gfx.readScreen(pixels, &width_trash, &height_trash, bFront); |
572 | return M64ERR_SUCCESS; |
573 | } |
574 | |
575 | m64p_error main_volume_up(void) |
576 | { |
577 | int level = 0; |
578 | audio.volumeUp(); |
579 | main_draw_volume_osd(); |
580 | main_volume_get_level(&level); |
581 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
582 | return M64ERR_SUCCESS; |
583 | } |
584 | |
585 | m64p_error main_volume_down(void) |
586 | { |
587 | int level = 0; |
588 | audio.volumeDown(); |
589 | main_draw_volume_osd(); |
590 | main_volume_get_level(&level); |
591 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
592 | return M64ERR_SUCCESS; |
593 | } |
594 | |
595 | m64p_error main_volume_get_level(int *level) |
596 | { |
597 | *level = audio.volumeGetLevel(); |
598 | return M64ERR_SUCCESS; |
599 | } |
600 | |
601 | m64p_error main_volume_set_level(int level) |
602 | { |
603 | audio.volumeSetLevel(level); |
604 | main_draw_volume_osd(); |
605 | level = audio.volumeGetLevel(); |
606 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
607 | return M64ERR_SUCCESS; |
608 | } |
609 | |
610 | m64p_error main_volume_mute(void) |
611 | { |
612 | audio.volumeMute(); |
613 | main_draw_volume_osd(); |
614 | StateChanged(M64CORE_AUDIO_MUTE, main_volume_get_muted()); |
615 | return M64ERR_SUCCESS; |
616 | } |
617 | |
618 | int main_volume_get_muted(void) |
619 | { |
620 | return (audio.volumeGetLevel() == 0); |
621 | } |
622 | |
623 | m64p_error main_reset(int do_hard_reset) |
624 | { |
625 | if (do_hard_reset) |
626 | reset_hard_job |= 1; |
627 | else |
628 | reset_soft(); |
629 | return M64ERR_SUCCESS; |
630 | } |
631 | |
632 | /********************************************************************************************************* |
633 | * global functions, callbacks from the r4300 core or from other plugins |
634 | */ |
635 | |
636 | static void video_plugin_render_callback(int bScreenRedrawn) |
637 | { |
638 | int bOSD = ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay"); |
639 | |
640 | // if the flag is set to take a screenshot, then grab it now |
641 | if (l_TakeScreenshot != 0) |
642 | { |
643 | // if the OSD is enabled, and the screen has not been recently redrawn, then we cannot take a screenshot now because |
644 | // it contains the OSD text. Wait until the next redraw |
645 | if (!bOSD || bScreenRedrawn) |
646 | { |
647 | TakeScreenshot(l_TakeScreenshot - 1); // current frame number +1 is in l_TakeScreenshot |
648 | l_TakeScreenshot = 0; // reset flag |
649 | } |
650 | } |
651 | |
652 | // if the OSD is enabled, then draw it now |
653 | if (bOSD) |
654 | { |
655 | osd_render(); |
656 | } |
657 | } |
658 | |
659 | void new_frame(void) |
660 | { |
661 | if (g_FrameCallback != NULL) |
662 | (*g_FrameCallback)(l_CurrentFrame); |
663 | |
664 | /* advance the current frame */ |
665 | l_CurrentFrame++; |
666 | |
667 | if (l_FrameAdvance) { |
668 | rompause = 1; |
669 | l_FrameAdvance = 0; |
670 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
671 | } |
672 | } |
673 | |
674 | void new_vi(void) |
675 | { |
676 | int Dif; |
677 | unsigned int CurrentFPSTime; |
678 | static unsigned int LastFPSTime = 0; |
679 | static unsigned int CounterTime = 0; |
680 | static unsigned int CalculatedTime ; |
681 | static int VI_Counter = 0; |
682 | |
683 | double VILimitMilliseconds = 1000.0 / ROM_PARAMS.vilimit; |
684 | double AdjustedLimit = VILimitMilliseconds * 100.0 / l_SpeedFactor; // adjust for selected emulator speed |
685 | int time; |
686 | |
687 | start_section(IDLE_SECTION); |
688 | VI_Counter++; |
689 | |
690 | #ifdef DBG |
691 | if(g_DebuggerActive) DebuggerCallback(DEBUG_UI_VI, 0); |
692 | #endif |
693 | |
694 | if(LastFPSTime == 0) |
695 | { |
696 | LastFPSTime = CounterTime = SDL_GetTicks(); |
697 | return; |
698 | } |
699 | CurrentFPSTime = SDL_GetTicks(); |
700 | |
701 | Dif = CurrentFPSTime - LastFPSTime; |
702 | |
703 | if (Dif < AdjustedLimit) |
704 | { |
705 | CalculatedTime = (unsigned int) (CounterTime + AdjustedLimit * VI_Counter); |
706 | time = (int)(CalculatedTime - CurrentFPSTime); |
707 | if (time > 0 && l_MainSpeedLimit) |
708 | { |
709 | DebugMessage(M64MSG_VERBOSE, " new_vi(): Waiting %ims", time); |
710 | SDL_Delay(time); |
711 | } |
712 | CurrentFPSTime = CurrentFPSTime + time; |
713 | } |
714 | |
715 | if (CurrentFPSTime - CounterTime >= 1000.0 ) |
716 | { |
717 | CounterTime = SDL_GetTicks(); |
718 | VI_Counter = 0 ; |
719 | } |
720 | |
721 | LastFPSTime = CurrentFPSTime ; |
722 | end_section(IDLE_SECTION); |
723 | } |
724 | |
725 | /********************************************************************************************************* |
726 | * emulation thread - runs the core |
727 | */ |
728 | m64p_error main_run(void) |
729 | { |
730 | /* take the r4300 emulator mode from the config file at this point and cache it in a global variable */ |
731 | r4300emu = ConfigGetParamInt(g_CoreConfig, "R4300Emulator"); |
732 | |
733 | /* set some other core parameters based on the config file values */ |
734 | savestates_set_autoinc_slot(ConfigGetParamBool(g_CoreConfig, "AutoStateSlotIncrement")); |
735 | savestates_select_slot(ConfigGetParamInt(g_CoreConfig, "CurrentStateSlot")); |
736 | no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump"); |
737 | |
738 | // initialize memory, and do byte-swapping if it's not been done yet |
739 | if (g_MemHasBeenBSwapped == 0) |
740 | { |
741 | init_memory(1); |
742 | g_MemHasBeenBSwapped = 1; |
743 | } |
744 | else |
745 | { |
746 | init_memory(0); |
747 | } |
748 | // Attach rom to plugins |
749 | if (!gfx.romOpen()) |
750 | { |
751 | free_memory(); return M64ERR_PLUGIN_FAIL; |
752 | } |
753 | if (!audio.romOpen()) |
754 | { |
755 | gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL; |
756 | } |
757 | if (!input.romOpen()) |
758 | { |
759 | audio.romClosed(); gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL; |
760 | } |
761 | |
762 | |
763 | /* set up the SDL key repeat and event filter to catch keyboard/joystick commands for the core */ |
764 | event_initialize(); |
765 | |
766 | /* initialize the on-screen display */ |
767 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay")) |
768 | { |
769 | // init on-screen display |
770 | int width = 640, height = 480; |
771 | gfx.readScreen(NULL, &width, &height, 0); // read screen to get width and height |
772 | osd_init(width, height); |
773 | } |
774 | |
775 | // setup rendering callback from video plugin to the core, for screenshots and On-Screen-Display |
776 | gfx.setRenderingCallback(video_plugin_render_callback); |
777 | |
778 | #ifdef WITH_LIRC |
779 | lircStart(); |
780 | #endif // WITH_LIRC |
781 | |
782 | #ifdef DBG |
783 | if (ConfigGetParamBool(g_CoreConfig, "EnableDebugger")) |
784 | init_debugger(); |
785 | #endif |
786 | /* Startup message on the OSD */ |
787 | osd_new_message(OSD_MIDDLE_CENTER, "Mupen64Plus Started..."); |
788 | g_EmulatorRunning = 1; |
789 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
790 | /* call r4300 CPU core and run the game */ |
791 | r4300_reset_hard(); |
792 | r4300_reset_soft(); |
793 | r4300_execute(); |
794 | |
795 | /* now begin to shut down */ |
796 | #ifdef WITH_LIRC |
797 | lircStop(); |
798 | #endif // WITH_LIRC |
799 | |
800 | #ifdef DBG |
801 | if (g_DebuggerActive) |
802 | destroy_debugger(); |
803 | #endif |
804 | |
805 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay")) |
806 | { |
807 | osd_exit(); |
808 | } |
809 | |
810 | rsp.romClosed(); |
811 | input.romClosed(); |
812 | audio.romClosed(); |
813 | gfx.romClosed(); |
814 | free_memory(); |
815 | |
816 | // clean up |
817 | g_EmulatorRunning = 0; |
818 | StateChanged(M64CORE_EMU_STATE, M64EMU_STOPPED); |
819 | |
820 | return M64ERR_SUCCESS; |
821 | } |
822 | |
823 | void main_stop(void) |
824 | { |
825 | /* note: this operation is asynchronous. It may be called from a thread other than the |
826 | main emulator thread, and may return before the emulator is completely stopped */ |
827 | if (!g_EmulatorRunning) |
828 | return; |
829 | |
830 | DebugMessage(M64MSG_STATUS, "Stopping emulation."); |
831 | if(l_msgPause) |
832 | { |
833 | osd_delete_message(l_msgPause); |
834 | l_msgPause = NULL; |
835 | } |
836 | if(l_msgFF) |
837 | { |
838 | osd_delete_message(l_msgFF); |
839 | l_msgFF = NULL; |
840 | } |
841 | if(l_msgVol) |
842 | { |
843 | osd_delete_message(l_msgVol); |
844 | l_msgVol = NULL; |
845 | } |
846 | if (rompause) |
847 | { |
848 | rompause = 0; |
849 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
850 | } |
851 | stop = 1; |
852 | #ifdef DBG |
853 | if(g_DebuggerActive) |
854 | { |
855 | debugger_step(); |
856 | } |
857 | #endif |
858 | } |
859 | |
860 | /********************************************************************************************************* |
861 | * main function |
862 | */ |
863 | int main(int argc, char *argv[]) |
864 | { |
865 | return 1; |
866 | } |
867 | |