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"); |
2d262872 |
197 | ConfigSetDefaultBool(g_CoreConfig, "DelaySI", 0, "Delay interrupt after DMA SI read/write"); |
198 | ConfigSetDefaultInt(g_CoreConfig, "CountPerOp", 2, "Force number of cycles per emulated instruction"); |
451ab91e |
199 | |
200 | /* handle upgrades */ |
201 | if (bUpgrade) |
202 | { |
203 | if (fConfigParamsVersion < 1.01f) |
204 | { // added separate SaveSRAMPath parameter in v1.01 |
205 | const char *pccSaveStatePath = ConfigGetParamString(g_CoreConfig, "SaveStatePath"); |
206 | if (pccSaveStatePath != NULL) |
207 | ConfigSetParameter(g_CoreConfig, "SaveSRAMPath", M64TYPE_STRING, pccSaveStatePath); |
208 | } |
209 | } |
210 | |
211 | if (bSaveConfig) |
212 | ConfigSaveSection("Core"); |
213 | |
214 | /* set config parameters for keyboard and joystick commands */ |
215 | return event_set_core_defaults(); |
216 | } |
217 | |
218 | void main_speeddown(int percent) |
219 | { |
220 | if (l_SpeedFactor - percent > 10) /* 10% minimum speed */ |
221 | { |
222 | l_SpeedFactor -= percent; |
223 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
224 | audio.setSpeedFactor(l_SpeedFactor); |
225 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
226 | } |
227 | } |
228 | |
229 | void main_speedup(int percent) |
230 | { |
231 | if (l_SpeedFactor + percent < 300) /* 300% maximum speed */ |
232 | { |
233 | l_SpeedFactor += percent; |
234 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
235 | audio.setSpeedFactor(l_SpeedFactor); |
236 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
237 | } |
238 | } |
239 | |
240 | static void main_speedset(int percent) |
241 | { |
242 | if (percent < 1 || percent > 1000) |
243 | { |
244 | DebugMessage(M64MSG_WARNING, "Invalid speed setting %i percent", percent); |
245 | return; |
246 | } |
247 | // disable fast-forward if it's enabled |
248 | main_set_fastforward(0); |
249 | // set speed |
250 | l_SpeedFactor = percent; |
251 | main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor); |
252 | audio.setSpeedFactor(l_SpeedFactor); |
253 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
254 | } |
255 | |
256 | void main_set_fastforward(int enable) |
257 | { |
258 | static int ff_state = 0; |
259 | static int SavedSpeedFactor = 100; |
260 | |
261 | if (enable && !ff_state) |
262 | { |
263 | ff_state = 1; /* activate fast-forward */ |
264 | SavedSpeedFactor = l_SpeedFactor; |
265 | l_SpeedFactor = 250; |
266 | audio.setSpeedFactor(l_SpeedFactor); |
267 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
268 | // set fast-forward indicator |
269 | l_msgFF = osd_new_message(OSD_TOP_RIGHT, "Fast Forward"); |
270 | osd_message_set_static(l_msgFF); |
271 | osd_message_set_user_managed(l_msgFF); |
272 | } |
273 | else if (!enable && ff_state) |
274 | { |
275 | ff_state = 0; /* de-activate fast-forward */ |
276 | l_SpeedFactor = SavedSpeedFactor; |
277 | audio.setSpeedFactor(l_SpeedFactor); |
278 | StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor); |
279 | // remove message |
280 | osd_delete_message(l_msgFF); |
281 | l_msgFF = NULL; |
282 | } |
283 | |
284 | } |
285 | |
286 | static void main_set_speedlimiter(int enable) |
287 | { |
288 | l_MainSpeedLimit = enable ? 1 : 0; |
289 | } |
290 | |
291 | static int main_is_paused(void) |
292 | { |
293 | return (g_EmulatorRunning && rompause); |
294 | } |
295 | |
296 | void main_toggle_pause(void) |
297 | { |
298 | if (!g_EmulatorRunning) |
299 | return; |
300 | |
301 | if (rompause) |
302 | { |
303 | DebugMessage(M64MSG_STATUS, "Emulation continued."); |
304 | if(l_msgPause) |
305 | { |
306 | osd_delete_message(l_msgPause); |
307 | l_msgPause = NULL; |
308 | } |
309 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
310 | } |
311 | else |
312 | { |
313 | if(l_msgPause) |
314 | osd_delete_message(l_msgPause); |
315 | |
316 | DebugMessage(M64MSG_STATUS, "Emulation paused."); |
317 | l_msgPause = osd_new_message(OSD_MIDDLE_CENTER, "Paused"); |
318 | osd_message_set_static(l_msgPause); |
319 | osd_message_set_user_managed(l_msgPause); |
320 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
321 | } |
322 | |
323 | rompause = !rompause; |
324 | l_FrameAdvance = 0; |
325 | } |
326 | |
327 | void main_advance_one(void) |
328 | { |
329 | l_FrameAdvance = 1; |
330 | rompause = 0; |
331 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
332 | } |
333 | |
334 | static void main_draw_volume_osd(void) |
335 | { |
336 | char msgString[64]; |
337 | const char *volString; |
338 | |
339 | // this calls into the audio plugin |
340 | volString = audio.volumeGetString(); |
341 | if (volString == NULL) |
342 | { |
343 | strcpy(msgString, "Volume Not Supported."); |
344 | } |
345 | else |
346 | { |
347 | sprintf(msgString, "%s: %s", "Volume", volString); |
348 | } |
349 | |
350 | // create a new message or update an existing one |
351 | if (l_msgVol != NULL) |
352 | osd_update_message(l_msgVol, "%s", msgString); |
353 | else { |
354 | l_msgVol = osd_new_message(OSD_MIDDLE_CENTER, "%s", msgString); |
355 | osd_message_set_user_managed(l_msgVol); |
356 | } |
357 | } |
358 | |
359 | /* this function could be called as a result of a keypress, joystick/button movement, |
360 | LIRC command, or 'testshots' command-line option timer */ |
361 | void main_take_next_screenshot(void) |
362 | { |
363 | l_TakeScreenshot = l_CurrentFrame + 1; |
364 | } |
365 | |
366 | void main_state_set_slot(int slot) |
367 | { |
368 | if (slot < 0 || slot > 9) |
369 | { |
370 | DebugMessage(M64MSG_WARNING, "Invalid savestate slot '%i' in main_state_set_slot(). Using 0", slot); |
371 | slot = 0; |
372 | } |
373 | |
374 | savestates_select_slot(slot); |
375 | StateChanged(M64CORE_SAVESTATE_SLOT, slot); |
376 | } |
377 | |
378 | void main_state_inc_slot(void) |
379 | { |
380 | savestates_inc_slot(); |
381 | } |
382 | |
383 | static unsigned char StopRumble[64] = {0x23, 0x01, 0x03, 0xc0, 0x1b, 0x00, 0x00, 0x00, |
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, 0, 0, 0, 0, 0, 0, 0, 0, |
386 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
387 | 0, 0, 0, 0, 0, 0, 0, 0}; |
388 | |
389 | void main_state_load(const char *filename) |
390 | { |
391 | input.controllerCommand(0, StopRumble); |
392 | input.controllerCommand(1, StopRumble); |
393 | input.controllerCommand(2, StopRumble); |
394 | input.controllerCommand(3, StopRumble); |
395 | |
396 | if (filename == NULL) // Save to slot |
397 | savestates_set_job(savestates_job_load, savestates_type_m64p, NULL); |
398 | else |
399 | savestates_set_job(savestates_job_load, savestates_type_unknown, filename); |
400 | } |
401 | |
402 | void main_state_save(int format, const char *filename) |
403 | { |
404 | if (filename == NULL) // Save to slot |
405 | savestates_set_job(savestates_job_save, savestates_type_m64p, NULL); |
406 | else // Save to file |
407 | savestates_set_job(savestates_job_save, (savestates_type)format, filename); |
408 | } |
409 | |
410 | m64p_error main_core_state_query(m64p_core_param param, int *rval) |
411 | { |
412 | switch (param) |
413 | { |
414 | case M64CORE_EMU_STATE: |
415 | if (!g_EmulatorRunning) |
416 | *rval = M64EMU_STOPPED; |
417 | else if (rompause) |
418 | *rval = M64EMU_PAUSED; |
419 | else |
420 | *rval = M64EMU_RUNNING; |
421 | break; |
422 | case M64CORE_VIDEO_MODE: |
423 | if (!VidExt_VideoRunning()) |
424 | *rval = M64VIDEO_NONE; |
425 | else if (VidExt_InFullscreenMode()) |
426 | *rval = M64VIDEO_FULLSCREEN; |
427 | else |
428 | *rval = M64VIDEO_WINDOWED; |
429 | break; |
430 | case M64CORE_SAVESTATE_SLOT: |
431 | *rval = savestates_get_slot(); |
432 | break; |
433 | case M64CORE_SPEED_FACTOR: |
434 | *rval = l_SpeedFactor; |
435 | break; |
436 | case M64CORE_SPEED_LIMITER: |
437 | *rval = l_MainSpeedLimit; |
438 | break; |
439 | case M64CORE_VIDEO_SIZE: |
440 | { |
441 | int width, height; |
442 | if (!g_EmulatorRunning) |
443 | return M64ERR_INVALID_STATE; |
444 | main_get_screen_size(&width, &height); |
445 | *rval = (width << 16) + height; |
446 | break; |
447 | } |
448 | case M64CORE_AUDIO_VOLUME: |
449 | { |
450 | if (!g_EmulatorRunning) |
451 | return M64ERR_INVALID_STATE; |
452 | return main_volume_get_level(rval); |
453 | } |
454 | case M64CORE_AUDIO_MUTE: |
455 | *rval = main_volume_get_muted(); |
456 | break; |
457 | case M64CORE_INPUT_GAMESHARK: |
458 | *rval = event_gameshark_active(); |
459 | break; |
460 | // these are only used for callbacks; they cannot be queried or set |
461 | case M64CORE_STATE_LOADCOMPLETE: |
462 | case M64CORE_STATE_SAVECOMPLETE: |
463 | return M64ERR_INPUT_INVALID; |
464 | default: |
465 | return M64ERR_INPUT_INVALID; |
466 | } |
467 | |
468 | return M64ERR_SUCCESS; |
469 | } |
470 | |
471 | m64p_error main_core_state_set(m64p_core_param param, int val) |
472 | { |
473 | switch (param) |
474 | { |
475 | case M64CORE_EMU_STATE: |
476 | if (!g_EmulatorRunning) |
477 | return M64ERR_INVALID_STATE; |
478 | if (val == M64EMU_STOPPED) |
479 | { |
480 | /* this stop function is asynchronous. The emulator may not terminate until later */ |
481 | main_stop(); |
482 | return M64ERR_SUCCESS; |
483 | } |
484 | else if (val == M64EMU_RUNNING) |
485 | { |
486 | if (main_is_paused()) |
487 | main_toggle_pause(); |
488 | return M64ERR_SUCCESS; |
489 | } |
490 | else if (val == M64EMU_PAUSED) |
491 | { |
492 | if (!main_is_paused()) |
493 | main_toggle_pause(); |
494 | return M64ERR_SUCCESS; |
495 | } |
496 | return M64ERR_INPUT_INVALID; |
497 | case M64CORE_VIDEO_MODE: |
498 | if (!g_EmulatorRunning) |
499 | return M64ERR_INVALID_STATE; |
500 | if (val == M64VIDEO_WINDOWED) |
501 | { |
502 | if (VidExt_InFullscreenMode()) |
503 | gfx.changeWindow(); |
504 | return M64ERR_SUCCESS; |
505 | } |
506 | else if (val == M64VIDEO_FULLSCREEN) |
507 | { |
508 | if (!VidExt_InFullscreenMode()) |
509 | gfx.changeWindow(); |
510 | return M64ERR_SUCCESS; |
511 | } |
512 | return M64ERR_INPUT_INVALID; |
513 | case M64CORE_SAVESTATE_SLOT: |
514 | if (val < 0 || val > 9) |
515 | return M64ERR_INPUT_INVALID; |
516 | savestates_select_slot(val); |
517 | return M64ERR_SUCCESS; |
518 | case M64CORE_SPEED_FACTOR: |
519 | if (!g_EmulatorRunning) |
520 | return M64ERR_INVALID_STATE; |
521 | main_speedset(val); |
522 | return M64ERR_SUCCESS; |
523 | case M64CORE_SPEED_LIMITER: |
524 | main_set_speedlimiter(val); |
525 | return M64ERR_SUCCESS; |
526 | case M64CORE_VIDEO_SIZE: |
527 | { |
528 | // the front-end app is telling us that the user has resized the video output frame, and so |
529 | // we should try to update the video plugin accordingly. First, check state |
530 | int width, height; |
531 | if (!g_EmulatorRunning) |
532 | return M64ERR_INVALID_STATE; |
533 | width = (val >> 16) & 0xffff; |
534 | height = val & 0xffff; |
535 | // then call the video plugin. if the video plugin supports resizing, it will resize its viewport and call |
536 | // VidExt_ResizeWindow to update the window manager handling our opengl output window |
537 | gfx.resizeVideoOutput(width, height); |
538 | return M64ERR_SUCCESS; |
539 | } |
540 | case M64CORE_AUDIO_VOLUME: |
541 | if (!g_EmulatorRunning) |
542 | return M64ERR_INVALID_STATE; |
543 | if (val < 0 || val > 100) |
544 | return M64ERR_INPUT_INVALID; |
545 | return main_volume_set_level(val); |
546 | case M64CORE_AUDIO_MUTE: |
547 | if ((main_volume_get_muted() && !val) || (!main_volume_get_muted() && val)) |
548 | return main_volume_mute(); |
549 | return M64ERR_SUCCESS; |
550 | case M64CORE_INPUT_GAMESHARK: |
551 | if (!g_EmulatorRunning) |
552 | return M64ERR_INVALID_STATE; |
553 | event_set_gameshark(val); |
554 | return M64ERR_SUCCESS; |
555 | // these are only used for callbacks; they cannot be queried or set |
556 | case M64CORE_STATE_LOADCOMPLETE: |
557 | case M64CORE_STATE_SAVECOMPLETE: |
558 | return M64ERR_INPUT_INVALID; |
559 | default: |
560 | return M64ERR_INPUT_INVALID; |
561 | } |
562 | } |
563 | |
564 | m64p_error main_get_screen_size(int *width, int *height) |
565 | { |
566 | gfx.readScreen(NULL, width, height, 0); |
567 | return M64ERR_SUCCESS; |
568 | } |
569 | |
570 | m64p_error main_read_screen(void *pixels, int bFront) |
571 | { |
572 | int width_trash, height_trash; |
573 | gfx.readScreen(pixels, &width_trash, &height_trash, bFront); |
574 | return M64ERR_SUCCESS; |
575 | } |
576 | |
577 | m64p_error main_volume_up(void) |
578 | { |
579 | int level = 0; |
580 | audio.volumeUp(); |
581 | main_draw_volume_osd(); |
582 | main_volume_get_level(&level); |
583 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
584 | return M64ERR_SUCCESS; |
585 | } |
586 | |
587 | m64p_error main_volume_down(void) |
588 | { |
589 | int level = 0; |
590 | audio.volumeDown(); |
591 | main_draw_volume_osd(); |
592 | main_volume_get_level(&level); |
593 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
594 | return M64ERR_SUCCESS; |
595 | } |
596 | |
597 | m64p_error main_volume_get_level(int *level) |
598 | { |
599 | *level = audio.volumeGetLevel(); |
600 | return M64ERR_SUCCESS; |
601 | } |
602 | |
603 | m64p_error main_volume_set_level(int level) |
604 | { |
605 | audio.volumeSetLevel(level); |
606 | main_draw_volume_osd(); |
607 | level = audio.volumeGetLevel(); |
608 | StateChanged(M64CORE_AUDIO_VOLUME, level); |
609 | return M64ERR_SUCCESS; |
610 | } |
611 | |
612 | m64p_error main_volume_mute(void) |
613 | { |
614 | audio.volumeMute(); |
615 | main_draw_volume_osd(); |
616 | StateChanged(M64CORE_AUDIO_MUTE, main_volume_get_muted()); |
617 | return M64ERR_SUCCESS; |
618 | } |
619 | |
620 | int main_volume_get_muted(void) |
621 | { |
622 | return (audio.volumeGetLevel() == 0); |
623 | } |
624 | |
625 | m64p_error main_reset(int do_hard_reset) |
626 | { |
627 | if (do_hard_reset) |
628 | reset_hard_job |= 1; |
629 | else |
630 | reset_soft(); |
631 | return M64ERR_SUCCESS; |
632 | } |
633 | |
634 | /********************************************************************************************************* |
635 | * global functions, callbacks from the r4300 core or from other plugins |
636 | */ |
637 | |
638 | static void video_plugin_render_callback(int bScreenRedrawn) |
639 | { |
640 | int bOSD = ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay"); |
641 | |
642 | // if the flag is set to take a screenshot, then grab it now |
643 | if (l_TakeScreenshot != 0) |
644 | { |
645 | // if the OSD is enabled, and the screen has not been recently redrawn, then we cannot take a screenshot now because |
646 | // it contains the OSD text. Wait until the next redraw |
647 | if (!bOSD || bScreenRedrawn) |
648 | { |
649 | TakeScreenshot(l_TakeScreenshot - 1); // current frame number +1 is in l_TakeScreenshot |
650 | l_TakeScreenshot = 0; // reset flag |
651 | } |
652 | } |
653 | |
654 | // if the OSD is enabled, then draw it now |
655 | if (bOSD) |
656 | { |
657 | osd_render(); |
658 | } |
659 | } |
660 | |
661 | void new_frame(void) |
662 | { |
663 | if (g_FrameCallback != NULL) |
664 | (*g_FrameCallback)(l_CurrentFrame); |
665 | |
666 | /* advance the current frame */ |
667 | l_CurrentFrame++; |
668 | |
669 | if (l_FrameAdvance) { |
670 | rompause = 1; |
671 | l_FrameAdvance = 0; |
672 | StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED); |
673 | } |
674 | } |
675 | |
676 | void new_vi(void) |
677 | { |
678 | int Dif; |
679 | unsigned int CurrentFPSTime; |
680 | static unsigned int LastFPSTime = 0; |
681 | static unsigned int CounterTime = 0; |
682 | static unsigned int CalculatedTime ; |
683 | static int VI_Counter = 0; |
684 | |
685 | double VILimitMilliseconds = 1000.0 / ROM_PARAMS.vilimit; |
686 | double AdjustedLimit = VILimitMilliseconds * 100.0 / l_SpeedFactor; // adjust for selected emulator speed |
687 | int time; |
688 | |
689 | start_section(IDLE_SECTION); |
690 | VI_Counter++; |
691 | |
692 | #ifdef DBG |
693 | if(g_DebuggerActive) DebuggerCallback(DEBUG_UI_VI, 0); |
694 | #endif |
695 | |
696 | if(LastFPSTime == 0) |
697 | { |
698 | LastFPSTime = CounterTime = SDL_GetTicks(); |
699 | return; |
700 | } |
701 | CurrentFPSTime = SDL_GetTicks(); |
702 | |
703 | Dif = CurrentFPSTime - LastFPSTime; |
704 | |
705 | if (Dif < AdjustedLimit) |
706 | { |
707 | CalculatedTime = (unsigned int) (CounterTime + AdjustedLimit * VI_Counter); |
708 | time = (int)(CalculatedTime - CurrentFPSTime); |
709 | if (time > 0 && l_MainSpeedLimit) |
710 | { |
711 | DebugMessage(M64MSG_VERBOSE, " new_vi(): Waiting %ims", time); |
712 | SDL_Delay(time); |
713 | } |
714 | CurrentFPSTime = CurrentFPSTime + time; |
715 | } |
716 | |
717 | if (CurrentFPSTime - CounterTime >= 1000.0 ) |
718 | { |
719 | CounterTime = SDL_GetTicks(); |
720 | VI_Counter = 0 ; |
721 | } |
722 | |
723 | LastFPSTime = CurrentFPSTime ; |
724 | end_section(IDLE_SECTION); |
725 | } |
726 | |
727 | /********************************************************************************************************* |
728 | * emulation thread - runs the core |
729 | */ |
730 | m64p_error main_run(void) |
731 | { |
732 | /* take the r4300 emulator mode from the config file at this point and cache it in a global variable */ |
733 | r4300emu = ConfigGetParamInt(g_CoreConfig, "R4300Emulator"); |
734 | |
735 | /* set some other core parameters based on the config file values */ |
736 | savestates_set_autoinc_slot(ConfigGetParamBool(g_CoreConfig, "AutoStateSlotIncrement")); |
737 | savestates_select_slot(ConfigGetParamInt(g_CoreConfig, "CurrentStateSlot")); |
738 | no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump"); |
2d262872 |
739 | if (delay_si==-1) delay_si = ConfigGetParamBool(g_CoreConfig, "DelaySI"); |
740 | if (count_per_op==-1) count_per_op = ConfigGetParamInt(g_CoreConfig, "CountPerOp"); |
741 | if (count_per_op <= 0) |
742 | count_per_op = 2; |
451ab91e |
743 | |
2d262872 |
744 | // initialize memory, and do byte-swapping if it's not been done yet |
451ab91e |
745 | if (g_MemHasBeenBSwapped == 0) |
746 | { |
747 | init_memory(1); |
748 | g_MemHasBeenBSwapped = 1; |
749 | } |
750 | else |
751 | { |
752 | init_memory(0); |
753 | } |
754 | // Attach rom to plugins |
755 | if (!gfx.romOpen()) |
756 | { |
757 | free_memory(); return M64ERR_PLUGIN_FAIL; |
758 | } |
759 | if (!audio.romOpen()) |
760 | { |
761 | gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL; |
762 | } |
763 | if (!input.romOpen()) |
764 | { |
765 | audio.romClosed(); gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL; |
766 | } |
767 | |
768 | |
769 | /* set up the SDL key repeat and event filter to catch keyboard/joystick commands for the core */ |
770 | event_initialize(); |
771 | |
772 | /* initialize the on-screen display */ |
773 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay")) |
774 | { |
775 | // init on-screen display |
776 | int width = 640, height = 480; |
777 | gfx.readScreen(NULL, &width, &height, 0); // read screen to get width and height |
778 | osd_init(width, height); |
779 | } |
780 | |
781 | // setup rendering callback from video plugin to the core, for screenshots and On-Screen-Display |
782 | gfx.setRenderingCallback(video_plugin_render_callback); |
783 | |
784 | #ifdef WITH_LIRC |
785 | lircStart(); |
786 | #endif // WITH_LIRC |
787 | |
788 | #ifdef DBG |
789 | if (ConfigGetParamBool(g_CoreConfig, "EnableDebugger")) |
790 | init_debugger(); |
791 | #endif |
792 | /* Startup message on the OSD */ |
793 | osd_new_message(OSD_MIDDLE_CENTER, "Mupen64Plus Started..."); |
794 | g_EmulatorRunning = 1; |
795 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
796 | /* call r4300 CPU core and run the game */ |
797 | r4300_reset_hard(); |
798 | r4300_reset_soft(); |
799 | r4300_execute(); |
800 | |
801 | /* now begin to shut down */ |
802 | #ifdef WITH_LIRC |
803 | lircStop(); |
804 | #endif // WITH_LIRC |
805 | |
806 | #ifdef DBG |
807 | if (g_DebuggerActive) |
808 | destroy_debugger(); |
809 | #endif |
810 | |
811 | if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay")) |
812 | { |
813 | osd_exit(); |
814 | } |
815 | |
816 | rsp.romClosed(); |
817 | input.romClosed(); |
818 | audio.romClosed(); |
819 | gfx.romClosed(); |
820 | free_memory(); |
821 | |
822 | // clean up |
823 | g_EmulatorRunning = 0; |
824 | StateChanged(M64CORE_EMU_STATE, M64EMU_STOPPED); |
825 | |
826 | return M64ERR_SUCCESS; |
827 | } |
828 | |
829 | void main_stop(void) |
830 | { |
831 | /* note: this operation is asynchronous. It may be called from a thread other than the |
832 | main emulator thread, and may return before the emulator is completely stopped */ |
833 | if (!g_EmulatorRunning) |
834 | return; |
835 | |
836 | DebugMessage(M64MSG_STATUS, "Stopping emulation."); |
837 | if(l_msgPause) |
838 | { |
839 | osd_delete_message(l_msgPause); |
840 | l_msgPause = NULL; |
841 | } |
842 | if(l_msgFF) |
843 | { |
844 | osd_delete_message(l_msgFF); |
845 | l_msgFF = NULL; |
846 | } |
847 | if(l_msgVol) |
848 | { |
849 | osd_delete_message(l_msgVol); |
850 | l_msgVol = NULL; |
851 | } |
852 | if (rompause) |
853 | { |
854 | rompause = 0; |
855 | StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING); |
856 | } |
857 | stop = 1; |
858 | #ifdef DBG |
859 | if(g_DebuggerActive) |
860 | { |
861 | debugger_step(); |
862 | } |
863 | #endif |
864 | } |