Add start paused option
[lsnes.git] / src / core / mainloop.cpp
blob4248b49a5430e7efa95c755a77632c81f44250ea
1 #include "core/bsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framebuffer.hpp"
7 #include "core/framerate.hpp"
8 #include "lua/lua.hpp"
9 #include "library/string.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/movie.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/moviefile.hpp"
14 #include "core/memorymanip.hpp"
15 #include "core/memorywatch.hpp"
16 #include "core/render.hpp"
17 #include "core/rom.hpp"
18 #include "core/rrdata.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
22 #include <iomanip>
23 #include <cassert>
24 #include <sstream>
25 #include <iostream>
26 #include <set>
27 #include <sys/time.h>
29 #define SPECIAL_FRAME_START 0
30 #define SPECIAL_FRAME_VIDEO 1
31 #define SPECIAL_SAVEPOINT 2
32 #define SPECIAL_NONE 3
34 void update_movie_state();
35 time_t random_seed_value = 0;
37 namespace
39 enum advance_mode
41 ADVANCE_QUIT, //Quit the emulator.
42 ADVANCE_AUTO, //Normal (possibly slowed down play).
43 ADVANCE_LOAD, //Loading a state.
44 ADVANCE_FRAME, //Frame advance.
45 ADVANCE_SUBFRAME, //Subframe advance.
46 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
47 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
48 ADVANCE_PAUSE, //Unconditional pause.
51 //Previous mouse mask.
52 int prev_mouse_mask = 0;
53 //Flags related to repeating advance.
54 bool advanced_once;
55 bool cancel_advance;
56 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
57 enum advance_mode amode;
58 //Mode and filename of pending load, one of LOAD_* constants.
59 int loadmode;
60 std::string pending_load;
61 //Queued saves (all savestates).
62 std::set<std::string> queued_saves;
63 bool stepping_into_save;
64 //Save jukebox.
65 numeric_setting jukebox_size("jukebox-size", 0, 999, 12);
66 size_t save_jukebox_pointer;
67 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
68 long pending_reset_cycles = -1;
69 //Set by every video refresh.
70 bool video_refresh_done;
71 //Special subframe location. One of SPECIAL_* constants.
72 int location_special;
73 //Few settings.
74 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
75 boolean_setting pause_on_end("pause-on-end", false);
76 //Last frame params.
77 bool last_hires = false;
78 bool last_interlace = false;
80 std::string save_jukebox_name(size_t i)
82 return (stringfmt() << "${project}" << (i + 1) << ".lsmv").str();
86 path_setting firmwarepath_setting("firmwarepath");
88 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
90 if(lua_requests_subframe_paint)
91 redraw_framebuffer();
93 if(subframe) {
94 if(amode == ADVANCE_SUBFRAME) {
95 if(!cancel_advance && !advanced_once) {
96 platform::wait(advance_timeout_first * 1000);
97 advanced_once = true;
99 if(cancel_advance) {
100 amode = ADVANCE_PAUSE;
101 cancel_advance = false;
103 platform::set_paused(amode == ADVANCE_PAUSE);
104 } else if(amode == ADVANCE_FRAME) {
106 } else {
107 if(amode == ADVANCE_SKIPLAG)
108 amode = ADVANCE_PAUSE;
109 platform::set_paused(amode == ADVANCE_PAUSE);
110 cancel_advance = false;
112 if(amode == ADVANCE_SKIPLAG)
113 amode = ADVANCE_AUTO;
114 location_special = SPECIAL_NONE;
115 update_movie_state();
116 } else {
117 if(amode == ADVANCE_SKIPLAG_PENDING)
118 amode = ADVANCE_SKIPLAG;
119 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
120 if(!cancel_advance) {
121 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
122 (advance_timeout_first * 1000));
123 advanced_once = true;
125 if(cancel_advance) {
126 amode = ADVANCE_PAUSE;
127 cancel_advance = false;
129 platform::set_paused(amode == ADVANCE_PAUSE);
130 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end) {
131 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count() + 1) {
132 amode = ADVANCE_PAUSE;
133 platform::set_paused(true);
135 } else {
136 platform::set_paused((amode == ADVANCE_PAUSE));
137 cancel_advance = false;
139 location_special = SPECIAL_FRAME_START;
140 update_movie_state();
143 information_dispatch::do_status_update();
144 platform::flush_command_queue();
145 if(!subframe && pending_reset_cycles >= 0)
146 controls.reset(pending_reset_cycles);
147 else if(!subframe)
148 controls.reset(-1);
149 controller_frame tmp = controls.commit(movb.get_movie().get_current_frame());
150 lua_callback_do_input(tmp, subframe);
151 return tmp;
154 namespace
156 enum advance_mode old_mode;
158 //Do pending load (automatically unpauses).
159 void mark_pending_load(const std::string& filename, int lmode)
161 loadmode = lmode;
162 pending_load = filename;
163 old_mode = amode;
164 amode = ADVANCE_LOAD;
165 platform::cancel_wait();
166 platform::set_paused(false);
169 void mark_pending_save(const std::string& filename, int smode)
171 if(smode == SAVE_MOVIE) {
172 //Just do this immediately.
173 do_save_movie(filename);
174 return;
176 queued_saves.insert(filename);
177 messages << "Pending save on '" << filename << "'" << std::endl;
180 uint32_t lpalette[0x80000];
181 void init_palette()
183 static bool palette_init = false;
184 if(palette_init)
185 return;
186 palette_init = true;
187 for(unsigned i = 0; i < 0x80000; i++) {
188 unsigned l = (i >> 15) & 0xF;
189 unsigned r = (i >> 0) & 0x1F;
190 unsigned g = (i >> 5) & 0x1F;
191 unsigned b = (i >> 10) & 0x1F;
192 double _l = static_cast<double>(l);
193 double m = 17.0 / 31.0;
194 r = floor(m * r * _l + 0.5);
195 g = floor(m * g * _l + 0.5);
196 b = floor(m * b * _l + 0.5);
197 lpalette[i] = r * 65536 + g * 256 + b;
202 void update_movie_state()
204 auto& _status = platform::get_emustatus();
205 if(!system_corrupt) {
206 std::ostringstream x;
207 x << movb.get_movie().get_current_frame() << "(";
208 if(location_special == SPECIAL_FRAME_START)
209 x << "0";
210 else if(location_special == SPECIAL_SAVEPOINT)
211 x << "S";
212 else if(location_special == SPECIAL_FRAME_VIDEO)
213 x << "V";
214 else
215 x << movb.get_movie().next_poll_number();
216 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
217 _status.set("Frame", x.str());
218 } else
219 _status.set("Frame", "N/A");
220 if(!system_corrupt) {
221 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
222 struct tm* time_decompose = gmtime(&timevalue);
223 char datebuffer[512];
224 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
225 _status.set("RTC", datebuffer);
226 } else {
227 _status.set("RTC", "N/A");
230 std::ostringstream x;
231 auto& mo = movb.get_movie();
232 x << (information_dispatch::get_dumper_count() ? "D" : "-");
233 x << (last_hires ? "H" : "-");
234 x << (last_interlace ? "I" : "-");
235 if(system_corrupt)
236 x << "C";
237 else if(!mo.readonly_mode())
238 x << "R";
239 else if(mo.get_frame_count() >= mo.get_current_frame())
240 x << "P";
241 else
242 x << "F";
243 _status.set("Flags", x.str());
245 if(jukebox_size > 0)
246 _status.set("Saveslot", translate_name_mprefix(save_jukebox_name(save_jukebox_pointer)));
247 else
248 _status.erase("Saveslot");
250 std::ostringstream x;
251 x << get_framerate();
252 _status.set("SPD%", x.str());
254 do_watch_memory();
256 controller_frame c;
257 if(movb.get_movie().readonly_mode())
258 c = movb.get_movie().get_controls();
259 else
260 c = controls.get_committed();
261 for(unsigned i = 0; i < 8; i++) {
262 unsigned pindex = controls.lcid_to_pcid(i);
263 devicetype_t dtype = controls.pcid_to_type(pindex);
264 if(dtype == DT_NONE)
265 continue;
266 char buffer[MAX_DISPLAY_LENGTH];
267 c.display(pindex, buffer);
268 char y[3] = {'P', 0, 0};
269 y[1] = 49 + i;
270 _status.set(y, buffer);
274 uint64_t audio_irq_time;
275 uint64_t controller_irq_time;
276 uint64_t frame_irq_time;
279 class my_interface : public SNES::Interface
281 string path(SNES::Cartridge::Slot slot, const string &hint)
283 const char* _hint = hint;
284 std::string _hint2 = _hint;
285 std::string fwp = firmwarepath_setting;
286 std::string finalpath = fwp + "/" + _hint2;
287 return finalpath.c_str();
290 time_t currentTime()
292 return our_movie.rtc_second;
295 time_t randomSeed()
297 return random_seed_value;
300 void videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan)
302 // uint64_t time_x = get_utime();
303 last_hires = hires;
304 last_interlace = interlace;
305 init_palette();
306 if(stepping_into_save)
307 messages << "Got video refresh in runtosave, expect desyncs!" << std::endl;
308 video_refresh_done = true;
309 lua_callback_do_frame_emulated();
310 bool region = (SNES::system.region() == SNES::System::Region::PAL);
311 information_dispatch::do_raw_frame(data, hires, interlace, overscan, region ? VIDEO_REGION_PAL :
312 VIDEO_REGION_NTSC);
313 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
314 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
315 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
316 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
317 lcscreen ls(data, hires, interlace, overscan, region);
318 location_special = SPECIAL_FRAME_VIDEO;
319 update_movie_state();
320 redraw_framebuffer(ls, false, true);
321 uint32_t fps_n, fps_d;
322 uint32_t fclocks;
323 if(region)
324 fclocks = interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
325 else
326 fclocks = interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
327 fps_n = SNES::system.cpu_frequency();
328 fps_d = fclocks;
329 uint32_t g = gcd(fps_n, fps_d);
330 fps_n /= g;
331 fps_d /= g;
332 information_dispatch::do_frame(ls, fps_n, fps_d);
333 // time_x = get_utime() - time_x;
334 // std::cerr << "IRQ TIMINGS (microseconds): "
335 // << "V: " << time_x << " "
336 // << "A: " << audio_irq_time << " "
337 // << "C: " << controller_irq_time << " "
338 // << "F: " << frame_irq_time << " "
339 // << "Total: " << (time_x + audio_irq_time + controller_irq_time + frame_irq_time) << std::endl;
340 audio_irq_time = controller_irq_time = 0;
343 void audioSample(int16_t l_sample, int16_t r_sample)
345 // uint64_t time_x = get_utime();
346 uint16_t _l = l_sample;
347 uint16_t _r = r_sample;
348 platform::audio_sample(_l + 32768, _r + 32768);
349 information_dispatch::do_sample(l_sample, r_sample);
350 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
351 our_movie.rtc_subsecond += 768;
352 while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
353 our_movie.rtc_second++;
354 our_movie.rtc_subsecond -= SNES::system.apu_frequency();
356 // audio_irq_time += get_utime() - time_x;
359 int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
361 // uint64_t time_x = get_utime();
362 int16_t x;
363 x = movb.input_poll(port, index, id);
364 lua_callback_snoop_input(port ? 1 : 0, index, id, x);
365 // controller_irq_time += get_utime() - time_x;
366 return x;
370 namespace
372 class jukebox_size_listener : public information_dispatch
374 public:
375 jukebox_size_listener() : information_dispatch("jukebox-size-listener") {};
376 void on_setting_change(const std::string& setting, const std::string& value)
378 if(setting == "jukebox-size") {
379 if(save_jukebox_pointer >= jukebox_size)
380 save_jukebox_pointer = 0;
381 update_movie_state();
384 } _jukebox_size_listener;
386 function_ptr_command<> count_rerecords("count-rerecords", "Count rerecords",
387 "Syntax: count-rerecords\nCounts rerecords.\n",
388 []() throw(std::bad_alloc, std::runtime_error) {
389 std::vector<char> tmp;
390 uint64_t x = rrdata::write(tmp);
391 messages << x << " rerecord(s)" << std::endl;
394 function_ptr_command<const std::string&> quit_emulator("quit-emulator", "Quit the emulator",
395 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
396 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
397 amode = ADVANCE_QUIT;
398 platform::set_paused(false);
399 platform::cancel_wait();
402 function_ptr_command<> pause_emulator("pause-emulator", "(Un)pause the emulator",
403 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
404 []() throw(std::bad_alloc, std::runtime_error) {
405 if(amode != ADVANCE_AUTO) {
406 amode = ADVANCE_AUTO;
407 platform::set_paused(false);
408 platform::cancel_wait();
409 messages << "Unpaused" << std::endl;
410 } else {
411 platform::cancel_wait();
412 cancel_advance = false;
413 amode = ADVANCE_PAUSE;
414 messages << "Paused" << std::endl;
418 function_ptr_command<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
419 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
420 []() throw(std::bad_alloc, std::runtime_error) {
421 if(jukebox_size == 0)
422 return;
423 if(save_jukebox_pointer == 0)
424 save_jukebox_pointer = jukebox_size - 1;
425 else
426 save_jukebox_pointer--;
427 if(save_jukebox_pointer >= jukebox_size)
428 save_jukebox_pointer = 0;
429 update_movie_state();
430 information_dispatch::do_status_update();
433 function_ptr_command<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
434 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
435 []() throw(std::bad_alloc, std::runtime_error) {
436 if(jukebox_size == 0)
437 return;
438 if(save_jukebox_pointer == jukebox_size - 1)
439 save_jukebox_pointer = 0;
440 else
441 save_jukebox_pointer++;
442 if(save_jukebox_pointer >= jukebox_size)
443 save_jukebox_pointer = 0;
444 update_movie_state();
445 information_dispatch::do_status_update();
448 function_ptr_command<> load_jukebox("load-jukebox", "Load save from jukebox",
449 "Syntax: load-jukebox\nLoad save from jukebox\n",
450 []() throw(std::bad_alloc, std::runtime_error) {
451 if(jukebox_size == 0)
452 throw std::runtime_error("No slot selected");
453 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
456 function_ptr_command<> save_jukebox_c("save-jukebox", "Save save to jukebox",
457 "Syntax: save-jukebox\nSave save to jukebox\n",
458 []() throw(std::bad_alloc, std::runtime_error) {
459 if(jukebox_size == 0)
460 throw std::runtime_error("No slot selected");
461 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE);
464 function_ptr_command<> padvance_frame("+advance-frame", "Advance one frame",
465 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
466 []() throw(std::bad_alloc, std::runtime_error) {
467 amode = ADVANCE_FRAME;
468 cancel_advance = false;
469 advanced_once = false;
470 platform::cancel_wait();
471 platform::set_paused(false);
474 function_ptr_command<> nadvance_frame("-advance-frame", "Advance one frame",
475 "No help available\n",
476 []() throw(std::bad_alloc, std::runtime_error) {
477 cancel_advance = true;
478 platform::cancel_wait();
479 platform::set_paused(false);
482 function_ptr_command<> padvance_poll("+advance-poll", "Advance one subframe",
483 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
484 []() throw(std::bad_alloc, std::runtime_error) {
485 amode = ADVANCE_SUBFRAME;
486 cancel_advance = false;
487 advanced_once = false;
488 platform::cancel_wait();
489 platform::set_paused(false);
492 function_ptr_command<> nadvance_poll("-advance-poll", "Advance one subframe",
493 "No help available\n",
494 []() throw(std::bad_alloc, std::runtime_error) {
495 cancel_advance = true;
496 platform::cancel_wait();
497 platform::set_paused(false);
500 function_ptr_command<> advance_skiplag("advance-skiplag", "Skip to next poll",
501 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
502 []() throw(std::bad_alloc, std::runtime_error) {
503 amode = ADVANCE_SKIPLAG;
504 platform::cancel_wait();
505 platform::set_paused(false);
508 function_ptr_command<> reset_c("reset", "Reset the SNES",
509 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
510 []() throw(std::bad_alloc, std::runtime_error) {
511 pending_reset_cycles = 0;
514 function_ptr_command<arg_filename> load_c("load", "Load savestate (current mode)",
515 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
516 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
517 mark_pending_load(args, LOAD_STATE_CURRENT);
520 function_ptr_command<arg_filename> load_state_c("load-state", "Load savestate (R/W)",
521 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
522 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
523 mark_pending_load(args, LOAD_STATE_RW);
526 function_ptr_command<arg_filename> load_readonly("load-readonly", "Load savestate (RO)",
527 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
528 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
529 mark_pending_load(args, LOAD_STATE_RO);
532 function_ptr_command<arg_filename> load_preserve("load-preserve", "Load savestate (preserve input)",
533 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
534 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
535 mark_pending_load(args, LOAD_STATE_PRESERVE);
538 function_ptr_command<arg_filename> load_movie_c("load-movie", "Load movie",
539 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
540 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
541 mark_pending_load(args, LOAD_STATE_MOVIE);
545 function_ptr_command<arg_filename> save_state("save-state", "Save state",
546 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
547 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
548 mark_pending_save(args, SAVE_STATE);
551 function_ptr_command<arg_filename> save_movie("save-movie", "Save movie",
552 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
553 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
554 mark_pending_save(args, SAVE_MOVIE);
557 function_ptr_command<> set_rwmode("set-rwmode", "Switch to read/write mode",
558 "Syntax: set-rwmode\nSwitches to read/write mode\n",
559 []() throw(std::bad_alloc, std::runtime_error) {
560 movb.get_movie().readonly_mode(false);
561 information_dispatch::do_mode_change(false);
562 lua_callback_do_readwrite();
563 update_movie_state();
564 information_dispatch::do_status_update();
567 function_ptr_command<> set_romode("set-romode", "Switch to read-only mode",
568 "Syntax: set-romode\nSwitches to read-only mode\n",
569 []() throw(std::bad_alloc, std::runtime_error) {
570 movb.get_movie().readonly_mode(true);
571 information_dispatch::do_mode_change(true);
572 update_movie_state();
573 information_dispatch::do_status_update();
576 function_ptr_command<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
577 "Syntax: toggle-rwmode\nToggles read/write mode\n",
578 []() throw(std::bad_alloc, std::runtime_error) {
579 bool c = movb.get_movie().readonly_mode();
580 movb.get_movie().readonly_mode(!c);
581 information_dispatch::do_mode_change(!c);
582 if(c)
583 lua_callback_do_readwrite();
584 update_movie_state();
585 information_dispatch::do_status_update();
588 function_ptr_command<> repaint("repaint", "Redraw the screen",
589 "Syntax: repaint\nRedraws the screen\n",
590 []() throw(std::bad_alloc, std::runtime_error) {
591 redraw_framebuffer();
594 function_ptr_command<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
595 []() throw(std::bad_alloc, std::runtime_error) {
596 bool newstate = !static_cast<bool>(pause_on_end);
597 pause_on_end.set(newstate ? "1" : "0");
598 messages << "Pause-on-end is now " << (newstate ? "ON" : "OFF") << std::endl;
601 function_ptr_command<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
602 "Syntax: rewind-movie\nRewind movie to the beginning\n",
603 []() throw(std::bad_alloc, std::runtime_error) {
604 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
607 function_ptr_command<> cancel_save("cancel-saves", "Cancel all pending saves", "Syntax: cancel-save\n"
608 "Cancel pending saves\n",
609 []() throw(std::bad_alloc, std::runtime_error) {
610 queued_saves.clear();
611 messages << "Pending saves canceled." << std::endl;
614 function_ptr_command<> test1("test-1", "no description available", "No help available\n",
615 []() throw(std::bad_alloc, std::runtime_error) {
616 redraw_framebuffer(screen_nosignal);
619 function_ptr_command<> test2("test-2", "no description available", "No help available\n",
620 []() throw(std::bad_alloc, std::runtime_error) {
621 redraw_framebuffer(screen_corrupt);
624 function_ptr_command<> test3("test-3", "no description available", "No help available\n",
625 []() throw(std::bad_alloc, std::runtime_error) {
626 while(1);
629 inverse_key ipause_emulator("pause-emulator", "Speed‣(Un)pause");
630 inverse_key ijback("cycle-jukebox-backward", "Slot select‣Cycle backwards");
631 inverse_key ijforward("cycle-jukebox-forward", "Slot select‣Cycle forwards");
632 inverse_key iloadj("load-jukebox", "Load‣Selected slot");
633 inverse_key isavej("save-jukebox", "Save‣Selected slot");
634 inverse_key iadvframe("+advance-frame", "Speed‣Advance frame");
635 inverse_key iadvsubframe("+advance-poll", "Speed‣Advance subframe");
636 inverse_key iskiplag("advance-skiplag", "Speed‣Advance poll");
637 inverse_key ireset("reset", "System‣Reset");
638 inverse_key iset_rwmode("set-rwmode", "Movie‣Switch to read/write");
639 inverse_key itoggle_romode("set-romode", "Movie‣Switch to read-only");
640 inverse_key itoggle_rwmode("toggle-rwmode", "Movie‣Toggle read-only");
641 inverse_key irepaint("repaint", "System‣Repaint screen");
642 inverse_key itogglepause("toggle-pause-on-end", "Movie‣Toggle pause-on-end");
643 inverse_key irewind_movie("rewind-movie", "Movie‣Rewind movie");
644 inverse_key icancel_saves("cancel-saves", "Save‣Cancel pending saves");
645 inverse_key iload1("load ${project}1.lsmv", "Load‣Slot 1");
646 inverse_key iload2("load ${project}2.lsmv", "Load‣Slot 2");
647 inverse_key iload3("load ${project}3.lsmv", "Load‣Slot 3");
648 inverse_key iload4("load ${project}4.lsmv", "Load‣Slot 4");
649 inverse_key iload5("load ${project}5.lsmv", "Load‣Slot 5");
650 inverse_key iload6("load ${project}6.lsmv", "Load‣Slot 6");
651 inverse_key iload7("load ${project}7.lsmv", "Load‣Slot 7");
652 inverse_key iload8("load ${project}8.lsmv", "Load‣Slot 8");
653 inverse_key iload9("load ${project}9.lsmv", "Load‣Slot 9");
654 inverse_key iload10("load ${project}10.lsmv", "Load‣Slot 10");
655 inverse_key iload11("load ${project}11.lsmv", "Load‣Slot 11");
656 inverse_key iload12("load ${project}12.lsmv", "Load‣Slot 12");
657 inverse_key iload13("load ${project}13.lsmv", "Load‣Slot 13");
658 inverse_key iload14("load ${project}14.lsmv", "Load‣Slot 14");
659 inverse_key iload15("load ${project}15.lsmv", "Load‣Slot 15");
660 inverse_key iload16("load ${project}16.lsmv", "Load‣Slot 16");
661 inverse_key iload17("load ${project}17.lsmv", "Load‣Slot 17");
662 inverse_key iload18("load ${project}18.lsmv", "Load‣Slot 18");
663 inverse_key iload19("load ${project}19.lsmv", "Load‣Slot 19");
664 inverse_key iload20("load ${project}20.lsmv", "Load‣Slot 20");
665 inverse_key iload21("load ${project}21.lsmv", "Load‣Slot 21");
666 inverse_key iload22("load ${project}22.lsmv", "Load‣Slot 22");
667 inverse_key iload23("load ${project}23.lsmv", "Load‣Slot 23");
668 inverse_key iload24("load ${project}24.lsmv", "Load‣Slot 24");
669 inverse_key iload25("load ${project}25.lsmv", "Load‣Slot 25");
670 inverse_key iload26("load ${project}26.lsmv", "Load‣Slot 26");
671 inverse_key iload27("load ${project}27.lsmv", "Load‣Slot 27");
672 inverse_key iload28("load ${project}28.lsmv", "Load‣Slot 28");
673 inverse_key iload29("load ${project}29.lsmv", "Load‣Slot 29");
674 inverse_key iload30("load ${project}30.lsmv", "Load‣Slot 30");
675 inverse_key iload31("load ${project}31.lsmv", "Load‣Slot 31");
676 inverse_key iload32("load ${project}32.lsmv", "Load‣Slot 32");
677 inverse_key isave1("save-state ${project}1.lsmv", "Save‣Slot 1");
678 inverse_key isave2("save-state ${project}2.lsmv", "Save‣Slot 2");
679 inverse_key isave3("save-state ${project}3.lsmv", "Save‣Slot 3");
680 inverse_key isave4("save-state ${project}4.lsmv", "Save‣Slot 4");
681 inverse_key isave5("save-state ${project}5.lsmv", "Save‣Slot 5");
682 inverse_key isave6("save-state ${project}6.lsmv", "Save‣Slot 6");
683 inverse_key isave7("save-state ${project}7.lsmv", "Save‣Slot 7");
684 inverse_key isave8("save-state ${project}8.lsmv", "Save‣Slot 8");
685 inverse_key isave9("save-state ${project}9.lsmv", "Save‣Slot 9");
686 inverse_key isave10("save-state ${project}10.lsmv", "Save‣Slot 10");
687 inverse_key isave11("save-state ${project}11.lsmv", "Save‣Slot 11");
688 inverse_key isave12("save-state ${project}12.lsmv", "Save‣Slot 12");
689 inverse_key isave13("save-state ${project}13.lsmv", "Save‣Slot 13");
690 inverse_key isave14("save-state ${project}14.lsmv", "Save‣Slot 14");
691 inverse_key isave15("save-state ${project}15.lsmv", "Save‣Slot 15");
692 inverse_key isave16("save-state ${project}16.lsmv", "Save‣Slot 16");
693 inverse_key isave17("save-state ${project}17.lsmv", "Save‣Slot 17");
694 inverse_key isave18("save-state ${project}18.lsmv", "Save‣Slot 18");
695 inverse_key isave19("save-state ${project}19.lsmv", "Save‣Slot 19");
696 inverse_key isave20("save-state ${project}20.lsmv", "Save‣Slot 20");
697 inverse_key isave21("save-state ${project}21.lsmv", "Save‣Slot 21");
698 inverse_key isave22("save-state ${project}22.lsmv", "Save‣Slot 22");
699 inverse_key isave23("save-state ${project}23.lsmv", "Save‣Slot 23");
700 inverse_key isave24("save-state ${project}24.lsmv", "Save‣Slot 24");
701 inverse_key isave25("save-state ${project}25.lsmv", "Save‣Slot 25");
702 inverse_key isave26("save-state ${project}26.lsmv", "Save‣Slot 26");
703 inverse_key isave27("save-state ${project}27.lsmv", "Save‣Slot 27");
704 inverse_key isave28("save-state ${project}28.lsmv", "Save‣Slot 28");
705 inverse_key isave29("save-state ${project}29.lsmv", "Save‣Slot 29");
706 inverse_key isave30("save-state ${project}30.lsmv", "Save‣Slot 30");
707 inverse_key isave31("save-state ${project}31.lsmv", "Save‣Slot 31");
708 inverse_key isave32("save-state ${project}32.lsmv", "Save‣Slot 32");
710 bool on_quit_prompt = false;
711 class mywindowcallbacks : public information_dispatch
713 public:
714 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
715 void on_new_dumper(const std::string& n)
717 update_movie_state();
719 void on_destroy_dumper(const std::string& n)
721 update_movie_state();
723 void on_close() throw()
725 if(on_quit_prompt) {
726 amode = ADVANCE_QUIT;
727 platform::set_paused(false);
728 platform::cancel_wait();
729 return;
731 on_quit_prompt = true;
732 try {
733 amode = ADVANCE_QUIT;
734 platform::set_paused(false);
735 platform::cancel_wait();
736 } catch(...) {
738 on_quit_prompt = false;
740 } mywcb;
742 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
743 //failing.
744 int handle_load()
746 if(pending_load != "") {
747 system_corrupt = false;
748 if(loadmode != LOAD_STATE_BEGINNING && !do_load_state(pending_load, loadmode)) {
749 pending_load = "";
750 return -1;
752 if(loadmode == LOAD_STATE_BEGINNING)
753 do_load_beginning();
754 pending_load = "";
755 pending_reset_cycles = -1;
756 amode = ADVANCE_AUTO;
757 platform::cancel_wait();
758 platform::set_paused(false);
759 if(!system_corrupt) {
760 location_special = SPECIAL_SAVEPOINT;
761 update_movie_state();
762 information_dispatch::do_status_update();
763 platform::flush_command_queue();
765 return 1;
767 return 0;
770 //If there are pending saves, perform them.
771 void handle_saves()
773 if(!queued_saves.empty()) {
774 stepping_into_save = true;
775 SNES::system.runtosave();
776 stepping_into_save = false;
777 for(auto i : queued_saves)
778 do_save_state(i);
780 queued_saves.clear();
783 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
784 bool handle_reset(long cycles)
786 if(cycles < 0)
787 return true;
788 video_refresh_done = false;
789 if(cycles == 0)
790 messages << "SNES reset" << std::endl;
791 else if(cycles > 0) {
792 messages << "SNES delayed reset not implemented (doing immediate reset)" << std::endl;
793 /* ... This code is just too buggy.
794 long cycles_executed = 0;
795 messages << "Executing delayed reset... This can take some time!" << std::endl;
796 while(cycles_executed < cycles && !video_refresh_done) {
797 //Poll inputs once in a while to prevent activating watchdog.
798 if(cycles_executed % 100 == 0)
799 platform::flush_command_queue();
800 SNES::cpu.op_step();
801 cycles_executed++;
803 if(!video_refresh_done)
804 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
805 else
806 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
809 SNES::system.reset();
810 lua_callback_do_reset();
811 redraw_framebuffer(screen_nosignal);
812 if(video_refresh_done) {
813 to_wait_frame(get_utime());
814 return false;
816 return true;
819 bool handle_corrupt()
821 if(!system_corrupt)
822 return false;
823 while(system_corrupt) {
824 platform::cancel_wait();
825 platform::set_paused(true);
826 platform::flush_command_queue();
827 handle_load();
828 if(amode == ADVANCE_QUIT)
829 return true;
831 return true;
835 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
836 std::runtime_error)
838 //Basic initialization.
839 init_special_screens();
840 our_rom = &rom;
841 my_interface intrf;
842 auto old_inteface = SNES::interface;
843 SNES::interface = &intrf;
844 SNES::system.init();
846 //Load our given movie.
847 bool first_round = false;
848 bool just_did_loadstate = false;
849 try {
850 do_load_state(initial, LOAD_STATE_DEFAULT);
851 location_special = SPECIAL_SAVEPOINT;
852 update_movie_state();
853 first_round = our_movie.is_savestate;
854 just_did_loadstate = first_round;
855 } catch(std::bad_alloc& e) {
856 OOM_panic();
857 } catch(std::exception& e) {
858 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
859 if(load_has_to_succeed) {
860 messages << "FATAL: Can't load movie" << std::endl;
861 platform::fatal_error();
863 system_corrupt = true;
864 update_movie_state();
865 redraw_framebuffer(screen_corrupt);
868 lua_callback_startup();
870 platform::set_paused(initial.start_paused);
871 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
872 uint64_t time_x = get_utime();
873 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
874 if(handle_corrupt()) {
875 first_round = our_movie.is_savestate;
876 just_did_loadstate = first_round;
877 continue;
879 long resetcycles = -1;
880 ack_frame_tick(get_utime());
881 if(amode == ADVANCE_SKIPLAG_PENDING)
882 amode = ADVANCE_SKIPLAG;
884 if(!first_round) {
885 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
886 if(amode == ADVANCE_QUIT && queued_saves.empty())
887 break;
888 bool delayed_reset = (resetcycles > 0);
889 pending_reset_cycles = -1;
890 if(!handle_reset(resetcycles)) {
891 continue;
893 if(!delayed_reset) {
894 handle_saves();
896 int r = 0;
897 if(queued_saves.empty())
898 r = handle_load();
899 if(r > 0 || system_corrupt) {
900 first_round = our_movie.is_savestate;
901 if(system_corrupt)
902 amode = ADVANCE_PAUSE;
903 else
904 amode = old_mode;
905 just_did_loadstate = first_round;
906 continue;
907 } else if(r < 0) {
908 //Not exactly desriable, but this at least won't desync.
909 amode = ADVANCE_PAUSE;
912 if(just_did_loadstate) {
913 //If we just loadstated, we are up to date.
914 if(amode == ADVANCE_QUIT)
915 break;
916 platform::cancel_wait();
917 platform::set_paused(amode == ADVANCE_PAUSE);
918 platform::flush_command_queue();
919 //We already have done the reset this frame if we are going to do one at all.
920 movb.get_movie().set_controls(movb.update_controls(true));
921 movb.get_movie().set_all_DRDY();
922 just_did_loadstate = false;
924 frame_irq_time = get_utime() - time_x;
925 SNES::system.run();
926 time_x = get_utime();
927 if(amode == ADVANCE_AUTO)
928 platform::wait(to_wait_frame(get_utime()));
929 first_round = false;
930 lua_callback_do_frame();
932 information_dispatch::do_dump_end();
933 SNES::interface = old_inteface;