Allow immediate saving at point of save
[lsnes.git] / src / core / mainloop.cpp
blobca4256e654e7954c39f09b35955bff9f95849d24
1 #include "lsnes.hpp"
2 #include "core/emucore.hpp"
4 #include "core/command.hpp"
5 #include "core/controller.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/framerate.hpp"
9 #include "core/inthread.hpp"
10 #include "lua/lua.hpp"
11 #include "library/string.hpp"
12 #include "core/mainloop.hpp"
13 #include "core/movie.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/moviefile.hpp"
16 #include "core/memorymanip.hpp"
17 #include "core/memorywatch.hpp"
18 #include "core/rom.hpp"
19 #include "core/rrdata.hpp"
20 #include "core/settings.hpp"
21 #include "core/window.hpp"
22 #include "library/framebuffer.hpp"
23 #include "library/pixfmt-lrgb.hpp"
25 #include <iomanip>
26 #include <cassert>
27 #include <sstream>
28 #include <iostream>
29 #include <limits>
30 #include <set>
31 #include <sys/time.h>
33 #define SPECIAL_FRAME_START 0
34 #define SPECIAL_FRAME_VIDEO 1
35 #define SPECIAL_SAVEPOINT 2
36 #define SPECIAL_NONE 3
38 void update_movie_state();
39 time_t random_seed_value = 0;
41 namespace
43 enum advance_mode
45 ADVANCE_QUIT, //Quit the emulator.
46 ADVANCE_AUTO, //Normal (possibly slowed down play).
47 ADVANCE_LOAD, //Loading a state.
48 ADVANCE_FRAME, //Frame advance.
49 ADVANCE_SUBFRAME, //Subframe advance.
50 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
51 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
52 ADVANCE_PAUSE, //Unconditional pause.
55 //Flags related to repeating advance.
56 bool advanced_once;
57 bool cancel_advance;
58 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
59 enum advance_mode amode;
60 //Mode and filename of pending load, one of LOAD_* constants.
61 int loadmode;
62 std::string pending_load;
63 //Queued saves (all savestates).
64 std::set<std::string> queued_saves;
65 //Save jukebox.
66 numeric_setting jukebox_size("jukebox-size", 0, 999, 12);
67 size_t save_jukebox_pointer;
68 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
69 long pending_reset_cycles = -1;
70 //Special subframe location. One of SPECIAL_* constants.
71 int location_special;
72 //Few settings.
73 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
74 boolean_setting pause_on_end("pause-on-end", false);
75 //Last frame params.
76 bool last_hires = false;
77 bool last_interlace = false;
78 //Unsafe rewind.
79 bool do_unsafe_rewind = false;
80 void* unsafe_rewind_obj = NULL;
81 //Stop at frame.
82 bool stop_at_frame_active = false;
83 uint64_t stop_at_frame = 0;
85 enum advance_mode old_mode;
87 std::string save_jukebox_name(size_t i)
89 return (stringfmt() << "${project}" << (i + 1) << ".lsmv").str();
93 path_setting firmwarepath_setting("firmwarepath");
95 void mainloop_signal_need_rewind(void* ptr)
97 if(ptr) {
98 old_mode = amode;
99 amode = ADVANCE_LOAD;
101 do_unsafe_rewind = true;
102 unsafe_rewind_obj = ptr;
105 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
107 if(lua_requests_subframe_paint)
108 redraw_framebuffer();
110 if(subframe) {
111 if(amode == ADVANCE_SUBFRAME) {
112 if(!cancel_advance && !advanced_once) {
113 platform::wait(advance_timeout_first * 1000);
114 advanced_once = true;
116 if(cancel_advance) {
117 stop_at_frame_active = false;
118 amode = ADVANCE_PAUSE;
119 cancel_advance = false;
121 platform::set_paused(amode == ADVANCE_PAUSE);
122 } else if(amode == ADVANCE_FRAME) {
124 } else {
125 if(amode == ADVANCE_SKIPLAG) {
126 stop_at_frame_active = false;
127 amode = ADVANCE_PAUSE;
129 platform::set_paused(amode == ADVANCE_PAUSE);
130 cancel_advance = false;
132 location_special = SPECIAL_NONE;
133 update_movie_state();
134 } else {
135 if(amode == ADVANCE_SKIPLAG_PENDING)
136 amode = ADVANCE_SKIPLAG;
137 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
138 if(!cancel_advance) {
139 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
140 (advance_timeout_first * 1000));
141 advanced_once = true;
143 if(cancel_advance) {
144 stop_at_frame_active = false;
145 amode = ADVANCE_PAUSE;
146 cancel_advance = false;
148 platform::set_paused(amode == ADVANCE_PAUSE);
149 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end &&
150 !stop_at_frame_active) {
151 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count()) {
152 stop_at_frame_active = false;
153 amode = ADVANCE_PAUSE;
154 platform::set_paused(true);
156 } else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
157 if(movb.get_movie().get_current_frame() >= stop_at_frame) {
158 stop_at_frame_active = false;
159 amode = ADVANCE_PAUSE;
160 platform::set_paused(true);
162 } else {
163 platform::set_paused((amode == ADVANCE_PAUSE));
164 cancel_advance = false;
166 location_special = SPECIAL_FRAME_START;
167 update_movie_state();
170 information_dispatch::do_status_update();
171 platform::flush_command_queue();
172 if(!subframe && pending_reset_cycles >= 0)
173 controls.reset(pending_reset_cycles);
174 else if(!subframe)
175 controls.reset(-1);
176 controller_frame tmp = controls.commit(movb.get_movie().get_current_frame());
177 lua_callback_do_input(tmp, subframe);
178 return tmp;
181 namespace
184 //Do pending load (automatically unpauses).
185 void mark_pending_load(const std::string& filename, int lmode)
187 loadmode = lmode;
188 pending_load = filename;
189 old_mode = amode;
190 amode = ADVANCE_LOAD;
191 platform::cancel_wait();
192 platform::set_paused(false);
195 void mark_pending_save(const std::string& filename, int smode)
197 if(smode == SAVE_MOVIE) {
198 //Just do this immediately.
199 do_save_movie(filename);
200 return;
202 if(location_special == SPECIAL_SAVEPOINT) {
203 //We can save immediately here.
204 do_save_state(filename);
205 return;
207 queued_saves.insert(filename);
208 messages << "Pending save on '" << filename << "'" << std::endl;
211 bool reload_rom(const std::string& filename)
213 std::string filenam = filename;
214 if(filenam == "")
215 filenam = our_rom->load_filename;
216 if(filenam == "") {
217 messages << "No ROM loaded" << std::endl;
218 return false;
220 try {
221 messages << "Loading ROM " << filenam << std::endl;
222 loaded_rom newrom(filenam);
223 *our_rom = newrom;
224 for(size_t i = 0; i < sizeof(our_rom->romimg)/sizeof(our_rom->romimg[0]); i++) {
225 our_movie.romimg_sha256[i] = our_rom->romimg[i].sha256;
226 our_movie.romxml_sha256[i] = our_rom->romxml[i].sha256;
228 } catch(std::exception& e) {
229 messages << "Can't reload ROM: " << e.what() << std::endl;
230 return false;
232 return true;
236 void update_movie_state()
239 uint64_t magic[4];
240 core_get_region().fill_framerate_magic(magic);
241 voice_frame_number(movb.get_movie().get_current_frame(), 1.0 * magic[1] / magic[0]);
243 auto& _status = platform::get_emustatus();
244 if(!system_corrupt) {
245 std::ostringstream x;
246 x << movb.get_movie().get_current_frame() << "(";
247 if(location_special == SPECIAL_FRAME_START)
248 x << "0";
249 else if(location_special == SPECIAL_SAVEPOINT)
250 x << "S";
251 else if(location_special == SPECIAL_FRAME_VIDEO)
252 x << "V";
253 else
254 x << movb.get_movie().next_poll_number();
255 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
256 _status.set("Frame", x.str());
257 } else
258 _status.set("Frame", "N/A");
259 if(!system_corrupt) {
260 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
261 struct tm* time_decompose = gmtime(&timevalue);
262 char datebuffer[512];
263 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
264 _status.set("RTC", datebuffer);
265 } else {
266 _status.set("RTC", "N/A");
269 std::ostringstream x;
270 auto& mo = movb.get_movie();
271 x << (information_dispatch::get_dumper_count() ? "D" : "-");
272 x << (last_hires ? "H" : "-");
273 x << (last_interlace ? "I" : "-");
274 if(system_corrupt)
275 x << "C";
276 else if(!mo.readonly_mode())
277 x << "R";
278 else if(mo.get_frame_count() >= mo.get_current_frame())
279 x << "P";
280 else
281 x << "F";
282 _status.set("Flags", x.str());
284 if(jukebox_size > 0)
285 _status.set("Saveslot", translate_name_mprefix(save_jukebox_name(save_jukebox_pointer)));
286 else
287 _status.erase("Saveslot");
289 std::ostringstream x;
290 x << get_framerate();
291 _status.set("SPD%", x.str());
293 do_watch_memory();
295 controller_frame c;
296 if(movb.get_movie().readonly_mode())
297 c = movb.get_movie().get_controls();
298 else
299 c = controls.get_committed();
300 auto lim = get_core_logical_controller_limits();
301 for(unsigned i = 0; i < lim.first; i++) {
302 unsigned pindex = controls.lcid_to_pcid(i);
303 std::string name = (stringfmt() << "P" << (i + 1)).str();
304 if(pindex == std::numeric_limits<unsigned>::max() || !controls.is_present(pindex)) {
305 _status.erase(name);
306 continue;
308 char buffer[MAX_DISPLAY_LENGTH];
309 c.display(pindex, buffer);
310 _status.set(name, buffer);
314 uint64_t audio_irq_time;
315 uint64_t controller_irq_time;
316 uint64_t frame_irq_time;
318 struct lsnes_callbacks : public emucore_callbacks
320 public:
321 ~lsnes_callbacks() throw()
325 int16_t get_input(unsigned port, unsigned index, unsigned control)
327 int16_t x;
328 x = movb.input_poll(port, index, control);
329 lua_callback_snoop_input(port, index, control, x);
330 return x;
333 void timer_tick(uint32_t increment, uint32_t per_second)
335 our_movie.rtc_subsecond += increment;
336 while(our_movie.rtc_subsecond >= per_second) {
337 our_movie.rtc_second++;
338 our_movie.rtc_subsecond -= per_second;
342 std::string get_firmware_path()
344 return firmwarepath_setting;
347 std::string get_base_path()
349 return our_rom->msu1_base;
352 time_t get_time()
354 return our_movie.rtc_second;
357 time_t get_randomseed()
359 return random_seed_value;
362 void output_frame(framebuffer_raw& screen, uint32_t fps_n, uint32_t fps_d)
364 lua_callback_do_frame_emulated();
365 location_special = SPECIAL_FRAME_VIDEO;
366 update_movie_state();
367 redraw_framebuffer(screen, false, true);
368 uint32_t g = gcd(fps_n, fps_d);
369 fps_n /= g;
370 fps_d /= g;
371 information_dispatch::do_frame(screen, fps_n, fps_d);
375 namespace
377 class jukebox_size_listener : public information_dispatch
379 public:
380 jukebox_size_listener() : information_dispatch("jukebox-size-listener") {};
381 void on_setting_change(const std::string& setting, const std::string& value)
383 if(setting == "jukebox-size") {
384 if(save_jukebox_pointer >= jukebox_size)
385 save_jukebox_pointer = 0;
386 update_movie_state();
389 } _jukebox_size_listener;
391 function_ptr_command<> count_rerecords("count-rerecords", "Count rerecords",
392 "Syntax: count-rerecords\nCounts rerecords.\n",
393 []() throw(std::bad_alloc, std::runtime_error) {
394 std::vector<char> tmp;
395 uint64_t x = rrdata::write(tmp);
396 messages << x << " rerecord(s)" << std::endl;
399 function_ptr_command<const std::string&> quit_emulator("quit-emulator", "Quit the emulator",
400 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
401 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
402 amode = ADVANCE_QUIT;
403 platform::set_paused(false);
404 platform::cancel_wait();
407 function_ptr_command<> unpause_emulator("unpause-emulator", "Unpause the emulator",
408 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
409 []() throw(std::bad_alloc, std::runtime_error) {
410 amode = ADVANCE_AUTO;
411 platform::set_paused(false);
412 platform::cancel_wait();
413 messages << "Unpaused" << std::endl;
416 function_ptr_command<> pause_emulator("pause-emulator", "(Un)pause the emulator",
417 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
418 []() throw(std::bad_alloc, std::runtime_error) {
419 if(amode != ADVANCE_AUTO) {
420 amode = ADVANCE_AUTO;
421 platform::set_paused(false);
422 platform::cancel_wait();
423 messages << "Unpaused" << std::endl;
424 } else {
425 platform::cancel_wait();
426 cancel_advance = false;
427 stop_at_frame_active = false;
428 amode = ADVANCE_PAUSE;
429 messages << "Paused" << std::endl;
433 function_ptr_command<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
434 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
435 []() throw(std::bad_alloc, std::runtime_error) {
436 if(jukebox_size == 0)
437 return;
438 if(save_jukebox_pointer == 0)
439 save_jukebox_pointer = jukebox_size - 1;
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<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
449 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
450 []() throw(std::bad_alloc, std::runtime_error) {
451 if(jukebox_size == 0)
452 return;
453 if(save_jukebox_pointer == jukebox_size - 1)
454 save_jukebox_pointer = 0;
455 else
456 save_jukebox_pointer++;
457 if(save_jukebox_pointer >= jukebox_size)
458 save_jukebox_pointer = 0;
459 update_movie_state();
460 information_dispatch::do_status_update();
463 function_ptr_command<> load_jukebox("load-jukebox", "Load save from jukebox",
464 "Syntax: load-jukebox\nLoad save from jukebox\n",
465 []() throw(std::bad_alloc, std::runtime_error) {
466 if(jukebox_size == 0)
467 throw std::runtime_error("No slot selected");
468 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
471 function_ptr_command<> load_jukebox_readwrite("load-jukebox-readwrite", "Load save from jukebox in read-write"
472 " mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
473 []() throw(std::bad_alloc, std::runtime_error) {
474 if(jukebox_size == 0)
475 throw std::runtime_error("No slot selected");
476 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
479 function_ptr_command<> load_jukebox_readonly("load-jukebox-readonly", "Load save from jukebox in read-only"
480 " mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
481 []() throw(std::bad_alloc, std::runtime_error) {
482 if(jukebox_size == 0)
483 throw std::runtime_error("No slot selected");
484 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
487 function_ptr_command<> load_jukebox_preserve("load-jukebox-preserve", "Load save from jukebox, preserving "
488 "input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
489 []() throw(std::bad_alloc, std::runtime_error) {
490 if(jukebox_size == 0)
491 throw std::runtime_error("No slot selected");
492 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
495 function_ptr_command<> load_jukebox_movie("load-jukebox-movie", "Load save from jukebox as movie",
496 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
497 []() throw(std::bad_alloc, std::runtime_error) {
498 if(jukebox_size == 0)
499 throw std::runtime_error("No slot selected");
500 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
503 function_ptr_command<> save_jukebox_c("save-jukebox", "Save save to jukebox",
504 "Syntax: save-jukebox\nSave save to jukebox\n",
505 []() throw(std::bad_alloc, std::runtime_error) {
506 if(jukebox_size == 0)
507 throw std::runtime_error("No slot selected");
508 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE);
511 function_ptr_command<> padvance_frame("+advance-frame", "Advance one frame",
512 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
513 []() throw(std::bad_alloc, std::runtime_error) {
514 amode = ADVANCE_FRAME;
515 cancel_advance = false;
516 advanced_once = false;
517 platform::cancel_wait();
518 platform::set_paused(false);
521 function_ptr_command<> nadvance_frame("-advance-frame", "Advance one frame",
522 "No help available\n",
523 []() throw(std::bad_alloc, std::runtime_error) {
524 cancel_advance = true;
525 platform::cancel_wait();
526 platform::set_paused(false);
529 function_ptr_command<> padvance_poll("+advance-poll", "Advance one subframe",
530 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
531 []() throw(std::bad_alloc, std::runtime_error) {
532 amode = ADVANCE_SUBFRAME;
533 cancel_advance = false;
534 advanced_once = false;
535 platform::cancel_wait();
536 platform::set_paused(false);
539 function_ptr_command<> nadvance_poll("-advance-poll", "Advance one subframe",
540 "No help available\n",
541 []() throw(std::bad_alloc, std::runtime_error) {
542 cancel_advance = true;
543 platform::cancel_wait();
544 platform::set_paused(false);
547 function_ptr_command<> advance_skiplag("advance-skiplag", "Skip to next poll",
548 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
549 []() throw(std::bad_alloc, std::runtime_error) {
550 amode = ADVANCE_SKIPLAG_PENDING;
551 platform::cancel_wait();
552 platform::set_paused(false);
555 function_ptr_command<const std::string&> reset_c("reset", "Reset the SNES",
556 "Syntax: reset\nReset <delay>\nResets the SNES in beginning of the next frame.\n",
557 [](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
558 if(x == "")
559 pending_reset_cycles = 0;
560 else
561 pending_reset_cycles = parse_value<uint32_t>(x);
564 function_ptr_command<arg_filename> load_c("load", "Load savestate (current mode)",
565 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
566 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
567 mark_pending_load(args, LOAD_STATE_CURRENT);
570 function_ptr_command<arg_filename> load_smart_c("load-smart", "Load savestate (heuristic mode)",
571 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
572 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
573 mark_pending_load(args, LOAD_STATE_DEFAULT);
576 function_ptr_command<arg_filename> load_state_c("load-state", "Load savestate (R/W)",
577 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
578 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
579 mark_pending_load(args, LOAD_STATE_RW);
582 function_ptr_command<arg_filename> load_readonly("load-readonly", "Load savestate (RO)",
583 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
584 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
585 mark_pending_load(args, LOAD_STATE_RO);
588 function_ptr_command<arg_filename> load_preserve("load-preserve", "Load savestate (preserve input)",
589 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
590 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
591 mark_pending_load(args, LOAD_STATE_PRESERVE);
594 function_ptr_command<arg_filename> load_movie_c("load-movie", "Load movie",
595 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
596 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
597 mark_pending_load(args, LOAD_STATE_MOVIE);
601 function_ptr_command<arg_filename> save_state("save-state", "Save state",
602 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
603 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
604 mark_pending_save(args, SAVE_STATE);
607 function_ptr_command<arg_filename> save_movie("save-movie", "Save movie",
608 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
609 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
610 mark_pending_save(args, SAVE_MOVIE);
613 function_ptr_command<> set_rwmode("set-rwmode", "Switch to read/write mode",
614 "Syntax: set-rwmode\nSwitches to read/write mode\n",
615 []() throw(std::bad_alloc, std::runtime_error) {
616 movb.get_movie().readonly_mode(false);
617 information_dispatch::do_mode_change(false);
618 lua_callback_do_readwrite();
619 update_movie_state();
620 information_dispatch::do_status_update();
623 function_ptr_command<> set_romode("set-romode", "Switch to read-only mode",
624 "Syntax: set-romode\nSwitches to read-only mode\n",
625 []() throw(std::bad_alloc, std::runtime_error) {
626 movb.get_movie().readonly_mode(true);
627 information_dispatch::do_mode_change(true);
628 update_movie_state();
629 information_dispatch::do_status_update();
632 function_ptr_command<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
633 "Syntax: toggle-rwmode\nToggles read/write mode\n",
634 []() throw(std::bad_alloc, std::runtime_error) {
635 bool c = movb.get_movie().readonly_mode();
636 movb.get_movie().readonly_mode(!c);
637 information_dispatch::do_mode_change(!c);
638 if(c)
639 lua_callback_do_readwrite();
640 update_movie_state();
641 information_dispatch::do_status_update();
644 function_ptr_command<> repaint("repaint", "Redraw the screen",
645 "Syntax: repaint\nRedraws the screen\n",
646 []() throw(std::bad_alloc, std::runtime_error) {
647 redraw_framebuffer();
650 function_ptr_command<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
651 []() throw(std::bad_alloc, std::runtime_error) {
652 bool newstate = !static_cast<bool>(pause_on_end);
653 pause_on_end.set(newstate ? "1" : "0");
654 messages << "Pause-on-end is now " << (newstate ? "ON" : "OFF") << std::endl;
657 function_ptr_command<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
658 "Syntax: rewind-movie\nRewind movie to the beginning\n",
659 []() throw(std::bad_alloc, std::runtime_error) {
660 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
663 function_ptr_command<const std::string&> reload_rom2("reload-rom", "Reload the ROM image",
664 "Syntax: reload-rom [<file>]\nReload the ROM image from <file>\n",
665 [](const std::string& filename) throw(std::bad_alloc, std::runtime_error) {
666 if(reload_rom(filename))
667 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
670 function_ptr_command<> cancel_save("cancel-saves", "Cancel all pending saves", "Syntax: cancel-save\n"
671 "Cancel pending saves\n",
672 []() throw(std::bad_alloc, std::runtime_error) {
673 queued_saves.clear();
674 messages << "Pending saves canceled." << std::endl;
677 function_ptr_command<> test1("test-1", "no description available", "No help available\n",
678 []() throw(std::bad_alloc, std::runtime_error) {
679 redraw_framebuffer(screen_nosignal);
682 function_ptr_command<> test2("test-2", "no description available", "No help available\n",
683 []() throw(std::bad_alloc, std::runtime_error) {
684 redraw_framebuffer(screen_corrupt);
687 function_ptr_command<> test3("test-3", "no description available", "No help available\n",
688 []() throw(std::bad_alloc, std::runtime_error) {
689 while(1);
692 inverse_key ipause_emulator("pause-emulator", "Speed‣(Un)pause");
693 inverse_key ijback("cycle-jukebox-backward", "Slot select‣Cycle backwards");
694 inverse_key ijforward("cycle-jukebox-forward", "Slot select‣Cycle forwards");
695 inverse_key iloadj("load-jukebox", "Load‣Selected slot");
696 inverse_key iloadjrw("load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
697 inverse_key iloadjro("load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
698 inverse_key iloadjp("load-jukebox-preserve", "Load‣Selected slot (preserve input)");
699 inverse_key iloadjm("load-jukebox-movie", "Load‣Selected slot (as movie)");
700 inverse_key isavej("save-jukebox", "Save‣Selected slot");
701 inverse_key iadvframe("+advance-frame", "Speed‣Advance frame");
702 inverse_key iadvsubframe("+advance-poll", "Speed‣Advance subframe");
703 inverse_key iskiplag("advance-skiplag", "Speed‣Advance poll");
704 inverse_key ireset("reset", "System‣Reset");
705 inverse_key iset_rwmode("set-rwmode", "Movie‣Switch to read/write");
706 inverse_key itoggle_romode("set-romode", "Movie‣Switch to read-only");
707 inverse_key itoggle_rwmode("toggle-rwmode", "Movie‣Toggle read-only");
708 inverse_key irepaint("repaint", "System‣Repaint screen");
709 inverse_key itogglepause("toggle-pause-on-end", "Movie‣Toggle pause-on-end");
710 inverse_key irewind_movie("rewind-movie", "Movie‣Rewind movie");
711 inverse_key icancel_saves("cancel-saves", "Save‣Cancel pending saves");
712 inverse_key iload1("load ${project}1.lsmv", "Load‣Slot 1");
713 inverse_key iload2("load ${project}2.lsmv", "Load‣Slot 2");
714 inverse_key iload3("load ${project}3.lsmv", "Load‣Slot 3");
715 inverse_key iload4("load ${project}4.lsmv", "Load‣Slot 4");
716 inverse_key iload5("load ${project}5.lsmv", "Load‣Slot 5");
717 inverse_key iload6("load ${project}6.lsmv", "Load‣Slot 6");
718 inverse_key iload7("load ${project}7.lsmv", "Load‣Slot 7");
719 inverse_key iload8("load ${project}8.lsmv", "Load‣Slot 8");
720 inverse_key iload9("load ${project}9.lsmv", "Load‣Slot 9");
721 inverse_key iload10("load ${project}10.lsmv", "Load‣Slot 10");
722 inverse_key iload11("load ${project}11.lsmv", "Load‣Slot 11");
723 inverse_key iload12("load ${project}12.lsmv", "Load‣Slot 12");
724 inverse_key iload13("load ${project}13.lsmv", "Load‣Slot 13");
725 inverse_key iload14("load ${project}14.lsmv", "Load‣Slot 14");
726 inverse_key iload15("load ${project}15.lsmv", "Load‣Slot 15");
727 inverse_key iload16("load ${project}16.lsmv", "Load‣Slot 16");
728 inverse_key iload17("load ${project}17.lsmv", "Load‣Slot 17");
729 inverse_key iload18("load ${project}18.lsmv", "Load‣Slot 18");
730 inverse_key iload19("load ${project}19.lsmv", "Load‣Slot 19");
731 inverse_key iload20("load ${project}20.lsmv", "Load‣Slot 20");
732 inverse_key iload21("load ${project}21.lsmv", "Load‣Slot 21");
733 inverse_key iload22("load ${project}22.lsmv", "Load‣Slot 22");
734 inverse_key iload23("load ${project}23.lsmv", "Load‣Slot 23");
735 inverse_key iload24("load ${project}24.lsmv", "Load‣Slot 24");
736 inverse_key iload25("load ${project}25.lsmv", "Load‣Slot 25");
737 inverse_key iload26("load ${project}26.lsmv", "Load‣Slot 26");
738 inverse_key iload27("load ${project}27.lsmv", "Load‣Slot 27");
739 inverse_key iload28("load ${project}28.lsmv", "Load‣Slot 28");
740 inverse_key iload29("load ${project}29.lsmv", "Load‣Slot 29");
741 inverse_key iload30("load ${project}30.lsmv", "Load‣Slot 30");
742 inverse_key iload31("load ${project}31.lsmv", "Load‣Slot 31");
743 inverse_key iload32("load ${project}32.lsmv", "Load‣Slot 32");
744 inverse_key isave1("save-state ${project}1.lsmv", "Save‣Slot 1");
745 inverse_key isave2("save-state ${project}2.lsmv", "Save‣Slot 2");
746 inverse_key isave3("save-state ${project}3.lsmv", "Save‣Slot 3");
747 inverse_key isave4("save-state ${project}4.lsmv", "Save‣Slot 4");
748 inverse_key isave5("save-state ${project}5.lsmv", "Save‣Slot 5");
749 inverse_key isave6("save-state ${project}6.lsmv", "Save‣Slot 6");
750 inverse_key isave7("save-state ${project}7.lsmv", "Save‣Slot 7");
751 inverse_key isave8("save-state ${project}8.lsmv", "Save‣Slot 8");
752 inverse_key isave9("save-state ${project}9.lsmv", "Save‣Slot 9");
753 inverse_key isave10("save-state ${project}10.lsmv", "Save‣Slot 10");
754 inverse_key isave11("save-state ${project}11.lsmv", "Save‣Slot 11");
755 inverse_key isave12("save-state ${project}12.lsmv", "Save‣Slot 12");
756 inverse_key isave13("save-state ${project}13.lsmv", "Save‣Slot 13");
757 inverse_key isave14("save-state ${project}14.lsmv", "Save‣Slot 14");
758 inverse_key isave15("save-state ${project}15.lsmv", "Save‣Slot 15");
759 inverse_key isave16("save-state ${project}16.lsmv", "Save‣Slot 16");
760 inverse_key isave17("save-state ${project}17.lsmv", "Save‣Slot 17");
761 inverse_key isave18("save-state ${project}18.lsmv", "Save‣Slot 18");
762 inverse_key isave19("save-state ${project}19.lsmv", "Save‣Slot 19");
763 inverse_key isave20("save-state ${project}20.lsmv", "Save‣Slot 20");
764 inverse_key isave21("save-state ${project}21.lsmv", "Save‣Slot 21");
765 inverse_key isave22("save-state ${project}22.lsmv", "Save‣Slot 22");
766 inverse_key isave23("save-state ${project}23.lsmv", "Save‣Slot 23");
767 inverse_key isave24("save-state ${project}24.lsmv", "Save‣Slot 24");
768 inverse_key isave25("save-state ${project}25.lsmv", "Save‣Slot 25");
769 inverse_key isave26("save-state ${project}26.lsmv", "Save‣Slot 26");
770 inverse_key isave27("save-state ${project}27.lsmv", "Save‣Slot 27");
771 inverse_key isave28("save-state ${project}28.lsmv", "Save‣Slot 28");
772 inverse_key isave29("save-state ${project}29.lsmv", "Save‣Slot 29");
773 inverse_key isave30("save-state ${project}30.lsmv", "Save‣Slot 30");
774 inverse_key isave31("save-state ${project}31.lsmv", "Save‣Slot 31");
775 inverse_key isave32("save-state ${project}32.lsmv", "Save‣Slot 32");
777 bool on_quit_prompt = false;
778 class mywindowcallbacks : public information_dispatch
780 public:
781 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
782 void on_new_dumper(const std::string& n)
784 update_movie_state();
786 void on_destroy_dumper(const std::string& n)
788 update_movie_state();
790 void on_close() throw()
792 if(on_quit_prompt) {
793 amode = ADVANCE_QUIT;
794 platform::set_paused(false);
795 platform::cancel_wait();
796 return;
798 on_quit_prompt = true;
799 try {
800 amode = ADVANCE_QUIT;
801 platform::set_paused(false);
802 platform::cancel_wait();
803 } catch(...) {
805 on_quit_prompt = false;
807 } mywcb;
809 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
810 //failing.
811 int handle_load()
813 if(do_unsafe_rewind && unsafe_rewind_obj) {
814 uint64_t t = get_utime();
815 std::vector<char> s;
816 lua_callback_do_unsafe_rewind(s, 0, 0, movb.get_movie(), unsafe_rewind_obj);
817 information_dispatch::do_mode_change(false);
818 do_unsafe_rewind = false;
819 our_movie.is_savestate = true;
820 location_special = SPECIAL_SAVEPOINT;
821 update_movie_state();
822 messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
823 return 1;
825 if(pending_load != "") {
826 system_corrupt = false;
827 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
828 !do_load_state(pending_load, loadmode)) {
829 pending_load = "";
830 return -1;
832 try {
833 if(loadmode == LOAD_STATE_BEGINNING)
834 do_load_beginning(false);
835 if(loadmode == LOAD_STATE_ROMRELOAD)
836 do_load_beginning(true);
837 } catch(std::exception& e) {
838 messages << "Load failed: " << e.what() << std::endl;
840 pending_load = "";
841 pending_reset_cycles = -1;
842 amode = ADVANCE_AUTO;
843 platform::cancel_wait();
844 platform::set_paused(false);
845 if(!system_corrupt) {
846 location_special = SPECIAL_SAVEPOINT;
847 update_movie_state();
848 information_dispatch::do_status_update();
849 platform::flush_command_queue();
851 return 1;
853 return 0;
856 //If there are pending saves, perform them.
857 void handle_saves()
859 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
860 core_runtosave();
861 for(auto i : queued_saves)
862 do_save_state(i);
863 if(do_unsafe_rewind && !unsafe_rewind_obj) {
864 uint64_t t = get_utime();
865 std::vector<char> s = save_core_state(true);
866 uint64_t secs = our_movie.rtc_second;
867 uint64_t ssecs = our_movie.rtc_subsecond;
868 lua_callback_do_unsafe_rewind(s, secs, ssecs, movb.get_movie(), NULL);
869 do_unsafe_rewind = false;
870 messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
873 queued_saves.clear();
876 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
877 bool handle_reset(long cycles)
879 if(cycles < 0)
880 return true;
881 bool video_refresh_done = false;
882 if(cycles == 0)
883 messages << "SNES reset" << std::endl;
884 else if(cycles > 0) {
885 auto x = core_emulate_cycles(cycles);
886 if(x.first)
887 messages << "SNES reset (delayed " << x.second << ")" << std::endl;
888 else
889 messages << "SNES reset (forced at " << x.second << ")" << std::endl;
890 video_refresh_done = !x.first;
892 core_reset();
893 lua_callback_do_reset();
894 redraw_framebuffer(screen_nosignal);
895 if(video_refresh_done) {
896 to_wait_frame(get_utime());
897 return false;
899 return true;
902 bool handle_corrupt()
904 if(!system_corrupt)
905 return false;
906 while(system_corrupt) {
907 platform::cancel_wait();
908 platform::set_paused(true);
909 platform::flush_command_queue();
910 handle_load();
911 if(amode == ADVANCE_QUIT)
912 return true;
914 return true;
918 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
919 std::runtime_error)
921 //Basic initialization.
922 voicethread_task();
923 init_special_screens();
924 our_rom = &rom;
925 lsnes_callbacks lsnes_callbacks_obj;
926 ecore_callbacks = &lsnes_callbacks_obj;
927 core_install_handler();
929 //Load our given movie.
930 bool first_round = false;
931 bool just_did_loadstate = false;
932 try {
933 do_load_state(initial, LOAD_STATE_INITIAL);
934 location_special = SPECIAL_SAVEPOINT;
935 update_movie_state();
936 first_round = our_movie.is_savestate;
937 just_did_loadstate = first_round;
938 } catch(std::bad_alloc& e) {
939 OOM_panic();
940 } catch(std::exception& e) {
941 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
942 if(load_has_to_succeed) {
943 messages << "FATAL: Can't load movie" << std::endl;
944 platform::fatal_error();
946 system_corrupt = true;
947 update_movie_state();
948 redraw_framebuffer(screen_corrupt);
951 lua_callback_startup();
953 platform::set_paused(initial.start_paused);
954 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
955 stop_at_frame_active = false;
956 uint64_t time_x = get_utime();
957 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
958 if(handle_corrupt()) {
959 first_round = our_movie.is_savestate;
960 just_did_loadstate = first_round;
961 continue;
963 long resetcycles = -1;
964 ack_frame_tick(get_utime());
965 if(amode == ADVANCE_SKIPLAG_PENDING)
966 amode = ADVANCE_SKIPLAG;
968 if(!first_round) {
969 controls.reset_framehold();
970 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
971 if(amode == ADVANCE_QUIT && queued_saves.empty())
972 break;
973 bool delayed_reset = (resetcycles > 0);
974 pending_reset_cycles = -1;
975 if(!handle_reset(resetcycles)) {
976 continue;
978 if(!delayed_reset) {
979 handle_saves();
981 int r = 0;
982 if(queued_saves.empty())
983 r = handle_load();
984 if(r > 0 || system_corrupt) {
985 first_round = our_movie.is_savestate;
986 if(system_corrupt)
987 amode = ADVANCE_PAUSE;
988 else
989 amode = old_mode;
990 stop_at_frame_active = false;
991 just_did_loadstate = first_round;
992 controls.reset_framehold();
993 continue;
994 } else if(r < 0) {
995 //Not exactly desriable, but this at least won't desync.
996 stop_at_frame_active = false;
997 amode = ADVANCE_PAUSE;
1000 if(just_did_loadstate) {
1001 //If we just loadstated, we are up to date.
1002 if(amode == ADVANCE_QUIT)
1003 break;
1004 platform::cancel_wait();
1005 platform::set_paused(amode == ADVANCE_PAUSE);
1006 platform::flush_command_queue();
1007 //We already have done the reset this frame if we are going to do one at all.
1008 movb.get_movie().set_controls(movb.update_controls(true));
1009 movb.get_movie().set_all_DRDY();
1010 just_did_loadstate = false;
1012 frame_irq_time = get_utime() - time_x;
1013 core_emulate_frame();
1014 time_x = get_utime();
1015 if(amode == ADVANCE_AUTO)
1016 platform::wait(to_wait_frame(get_utime()));
1017 first_round = false;
1018 lua_callback_do_frame();
1020 information_dispatch::do_dump_end();
1021 core_uninstall_handler();
1022 voicethread_kill();
1025 void set_stop_at_frame(uint64_t frame)
1027 stop_at_frame = frame;
1028 stop_at_frame_active = (frame != 0);
1029 amode = ADVANCE_AUTO;
1030 platform::set_paused(false);