New settings framework
[lsnes.git] / src / core / mainloop.cpp
blobc49684c64cb532ac93d4e8381d7b6c34fc46fad9
1 #include "lsnes.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 "core/inthread.hpp"
9 #include "lua/lua.hpp"
10 #include "library/string.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/movie.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/moviefile.hpp"
15 #include "core/memorymanip.hpp"
16 #include "core/memorywatch.hpp"
17 #include "core/rom.hpp"
18 #include "core/rrdata.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
21 #include "interface/callbacks.hpp"
22 #include "interface/romtype.hpp"
23 #include "library/framebuffer.hpp"
24 #include "library/pixfmt-lrgb.hpp"
26 #include <iomanip>
27 #include <cassert>
28 #include <sstream>
29 #include <iostream>
30 #include <limits>
31 #include <set>
32 #include <sys/time.h>
34 #define SPECIAL_FRAME_START 0
35 #define SPECIAL_FRAME_VIDEO 1
36 #define SPECIAL_SAVEPOINT 2
37 #define SPECIAL_NONE 3
39 void update_movie_state();
40 time_t random_seed_value = 0;
43 namespace
45 setting_var<setting_var_model_int<0,999999>> advance_timeout_first(lsnes_vset, "advance-timeout",
46 "Delays‣First frame advance", 500);
47 setting_var<setting_var_model_bool> pause_on_end(lsnes_vset, "pause-on-end", "Movie‣Pause on end", false);
48 setting_var<setting_var_model_int<0,999>> jukebox_size(lsnes_vset, "jukebox-size",
49 "Movie‣Number of save slots", 12);
51 enum advance_mode
53 ADVANCE_QUIT, //Quit the emulator.
54 ADVANCE_AUTO, //Normal (possibly slowed down play).
55 ADVANCE_LOAD, //Loading a state.
56 ADVANCE_FRAME, //Frame advance.
57 ADVANCE_SUBFRAME, //Subframe advance.
58 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
59 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
60 ADVANCE_PAUSE, //Unconditional pause.
63 //Our thread.
64 threadid_class emulation_thread;
65 //Flags related to repeating advance.
66 bool advanced_once;
67 bool cancel_advance;
68 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
69 enum advance_mode amode;
70 //Mode and filename of pending load, one of LOAD_* constants.
71 int loadmode;
72 std::string pending_load;
73 //Queued saves (all savestates).
74 std::set<std::string> queued_saves;
75 //Save jukebox.
76 size_t save_jukebox_pointer;
77 //Special subframe location. One of SPECIAL_* constants.
78 int location_special;
79 //Few settings.
80 //Last frame params.
81 bool last_hires = false;
82 bool last_interlace = false;
83 //Unsafe rewind.
84 bool do_unsafe_rewind = false;
85 void* unsafe_rewind_obj = NULL;
86 //Stop at frame.
87 bool stop_at_frame_active = false;
88 uint64_t stop_at_frame = 0;
90 enum advance_mode old_mode;
92 std::string save_jukebox_name(size_t i)
94 return (stringfmt() << "${project}" << (i + 1) << ".lsmv").str();
97 class _lsnes_pflag_handler : public movie::poll_flag
99 public:
100 ~_lsnes_pflag_handler()
103 int get_pflag()
105 return our_rom->rtype->get_pflag();
107 void set_pflag(int flag)
109 our_rom->rtype->set_pflag(flag);
111 } lsnes_pflag_handler;
114 void mainloop_signal_need_rewind(void* ptr)
116 if(ptr) {
117 old_mode = amode;
118 amode = ADVANCE_LOAD;
120 do_unsafe_rewind = true;
121 unsafe_rewind_obj = ptr;
124 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
126 if(lua_requests_subframe_paint)
127 redraw_framebuffer();
129 if(subframe) {
130 if(amode == ADVANCE_SUBFRAME) {
131 if(!cancel_advance && !advanced_once) {
132 platform::wait(advance_timeout_first * 1000);
133 advanced_once = true;
135 if(cancel_advance) {
136 stop_at_frame_active = false;
137 amode = ADVANCE_PAUSE;
138 cancel_advance = false;
140 platform::set_paused(amode == ADVANCE_PAUSE);
141 } else if(amode == ADVANCE_FRAME) {
143 } else {
144 if(amode == ADVANCE_SKIPLAG) {
145 stop_at_frame_active = false;
146 amode = ADVANCE_PAUSE;
148 platform::set_paused(amode == ADVANCE_PAUSE);
149 cancel_advance = false;
151 location_special = SPECIAL_NONE;
152 update_movie_state();
153 } else {
154 if(amode == ADVANCE_SKIPLAG_PENDING)
155 amode = ADVANCE_SKIPLAG;
156 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
157 if(!cancel_advance) {
158 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
159 (advance_timeout_first * 1000));
160 advanced_once = true;
162 if(cancel_advance) {
163 stop_at_frame_active = false;
164 amode = ADVANCE_PAUSE;
165 cancel_advance = false;
167 platform::set_paused(amode == ADVANCE_PAUSE);
168 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end &&
169 !stop_at_frame_active) {
170 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count()) {
171 stop_at_frame_active = false;
172 amode = ADVANCE_PAUSE;
173 platform::set_paused(true);
175 } else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
176 if(movb.get_movie().get_current_frame() >= stop_at_frame) {
177 stop_at_frame_active = false;
178 amode = ADVANCE_PAUSE;
179 platform::set_paused(true);
181 } else {
182 platform::set_paused((amode == ADVANCE_PAUSE));
183 cancel_advance = false;
185 location_special = SPECIAL_FRAME_START;
186 update_movie_state();
189 information_dispatch::do_status_update();
190 platform::flush_command_queue();
191 controller_frame tmp = controls.get(movb.get_movie().get_current_frame());
192 our_rom->rtype->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
193 lua_callback_do_input(tmp, subframe);
194 controls.commit(tmp);
195 return tmp;
198 namespace
201 //Do pending load (automatically unpauses).
202 void mark_pending_load(const std::string& filename, int lmode)
204 loadmode = lmode;
205 pending_load = filename;
206 old_mode = amode;
207 amode = ADVANCE_LOAD;
208 platform::cancel_wait();
209 platform::set_paused(false);
212 void mark_pending_save(const std::string& filename, int smode)
214 if(smode == SAVE_MOVIE) {
215 //Just do this immediately.
216 do_save_movie(filename);
217 return;
219 if(location_special == SPECIAL_SAVEPOINT) {
220 //We can save immediately here.
221 do_save_state(filename);
222 return;
224 queued_saves.insert(filename);
225 messages << "Pending save on '" << filename << "'" << std::endl;
228 bool reload_rom(const std::string& filename)
230 std::string filenam = filename;
231 if(filenam == "")
232 filenam = our_rom->load_filename;
233 if(filenam == "") {
234 messages << "No ROM loaded" << std::endl;
235 return false;
237 try {
238 messages << "Loading ROM " << filenam << std::endl;
239 loaded_rom newrom(filenam);
240 *our_rom = newrom;
241 for(size_t i = 0; i < sizeof(our_rom->romimg)/sizeof(our_rom->romimg[0]); i++) {
242 our_movie.romimg_sha256[i] = our_rom->romimg[i].sha_256;
243 our_movie.romxml_sha256[i] = our_rom->romxml[i].sha_256;
245 } catch(std::exception& e) {
246 messages << "Can't reload ROM: " << e.what() << std::endl;
247 return false;
249 messages << "Using core: " << our_rom->rtype->get_core_identifier() << std::endl;
250 information_dispatch::do_core_change();
251 return true;
254 struct jukebox_size_listener : public setting_var_listener
256 jukebox_size_listener() { lsnes_vset.add_listener(*this); }
257 ~jukebox_size_listener() throw() {lsnes_vset.remove_listener(*this); };
258 void on_setting_change(setting_var_group& grp, const setting_var_base& val)
260 if(val.get_iname() == "jukebox-size") {
261 if(save_jukebox_pointer >= jukebox_size)
262 save_jukebox_pointer = 0;
265 if(emulation_thread == this_thread_id())
266 update_movie_state();
267 else
268 runemufn([]() { update_movie_state(); });
269 information_dispatch::do_status_update();
274 void update_movie_state()
276 static unsigned last_controllers = 0;
278 uint64_t magic[4];
279 our_rom->region->fill_framerate_magic(magic);
280 voice_frame_number(movb.get_movie().get_current_frame(), 1.0 * magic[1] / magic[0]);
282 auto& _status = platform::get_emustatus();
283 if(!system_corrupt) {
284 _status.set("!frame", (stringfmt() << movb.get_movie().get_current_frame()).str());
285 _status.set("!length", (stringfmt() << movb.get_movie().get_frame_count()).str());
286 _status.set("!lag", (stringfmt() << movb.get_movie().get_lag_frames()).str());
287 if(location_special == SPECIAL_FRAME_START)
288 _status.set("!subframe", "0");
289 else if(location_special == SPECIAL_SAVEPOINT)
290 _status.set("!subframe", "S");
291 else if(location_special == SPECIAL_FRAME_VIDEO)
292 _status.set("!subframe", "V");
293 else
294 _status.set("!subframe", (stringfmt() << movb.get_movie().next_poll_number()).str());
295 } else {
296 _status.set("!frame", "N/A");
297 _status.set("!length", "N/A");
298 _status.set("!lag", "N/A");
299 _status.set("!subframe", "N/A");
302 _status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
303 auto& mo = movb.get_movie();
304 if(system_corrupt)
305 _status.set("!mode", "C");
306 else if(!mo.readonly_mode())
307 _status.set("!mode", "R");
308 else if(mo.get_frame_count() >= mo.get_current_frame())
309 _status.set("!mode", "P");
310 else
311 _status.set("!mode", "F");
313 if(jukebox_size > 0)
314 _status.set("!saveslot", (stringfmt() << (save_jukebox_pointer + 1)).str());
315 else
316 _status.erase("!saveslot");
317 _status.set("!speed", (stringfmt() << (unsigned)(100 * get_realized_multiplier() + 0.5)).str());
319 if(!system_corrupt) {
320 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
321 struct tm* time_decompose = gmtime(&timevalue);
322 char datebuffer[512];
323 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
324 _status.set("RTC", datebuffer);
325 } else {
326 _status.set("RTC", "N/A");
328 do_watch_memory();
330 controller_frame c;
331 if(movb.get_movie().readonly_mode())
332 c = movb.get_movie().get_controls();
333 else
334 c = controls.get_committed();
335 for(unsigned i = 0;; i++) {
336 auto pindex = controls.lcid_to_pcid(i);
337 if(pindex.first < 0 || !controls.is_present(pindex.first, pindex.second)) {
338 for(unsigned j = i; j < last_controllers; j++)
339 _status.erase((stringfmt() << "P" << (j + 1)).str());
340 last_controllers = i;
341 break;
343 char buffer[MAX_DISPLAY_LENGTH];
344 c.display(pindex.first, pindex.second, buffer);
345 _status.set((stringfmt() << "P" << (i + 1)).str(), buffer);
349 uint64_t audio_irq_time;
350 uint64_t controller_irq_time;
351 uint64_t frame_irq_time;
353 struct lsnes_callbacks : public emucore_callbacks
355 public:
356 ~lsnes_callbacks() throw()
360 int16_t get_input(unsigned port, unsigned index, unsigned control)
362 int16_t x;
363 x = movb.input_poll(port, index, control);
364 lua_callback_snoop_input(port, index, control, x);
365 return x;
368 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
370 if(!movb.get_movie().readonly_mode()) {
371 controller_frame f = movb.get_movie().get_controls();
372 f.axis3(port, index, control, value);
373 movb.get_movie().set_controls(f);
375 return movb.get_movie().next_input(port, index, control);
378 void timer_tick(uint32_t increment, uint32_t per_second)
380 our_movie.rtc_subsecond += increment;
381 while(our_movie.rtc_subsecond >= per_second) {
382 our_movie.rtc_second++;
383 our_movie.rtc_subsecond -= per_second;
387 std::string get_firmware_path()
389 return lsnes_vset["firmwarepath"].str();
392 std::string get_base_path()
394 return our_rom->msu1_base;
397 time_t get_time()
399 return our_movie.rtc_second;
402 time_t get_randomseed()
404 return random_seed_value;
407 void output_frame(framebuffer_raw& screen, uint32_t fps_n, uint32_t fps_d)
409 lua_callback_do_frame_emulated();
410 location_special = SPECIAL_FRAME_VIDEO;
411 update_movie_state();
412 redraw_framebuffer(screen, false, true);
413 uint32_t g = gcd(fps_n, fps_d);
414 fps_n /= g;
415 fps_d /= g;
416 information_dispatch::do_frame(screen, fps_n, fps_d);
420 namespace
422 function_ptr_command<> count_rerecords(lsnes_cmd, "count-rerecords", "Count rerecords",
423 "Syntax: count-rerecords\nCounts rerecords.\n",
424 []() throw(std::bad_alloc, std::runtime_error) {
425 std::vector<char> tmp;
426 uint64_t x = rrdata::write(tmp);
427 messages << x << " rerecord(s)" << std::endl;
430 function_ptr_command<const std::string&> quit_emulator(lsnes_cmd, "quit-emulator", "Quit the emulator",
431 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
432 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
433 amode = ADVANCE_QUIT;
434 platform::set_paused(false);
435 platform::cancel_wait();
438 function_ptr_command<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
439 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
440 []() throw(std::bad_alloc, std::runtime_error) {
441 amode = ADVANCE_AUTO;
442 platform::set_paused(false);
443 platform::cancel_wait();
444 messages << "Unpaused" << std::endl;
447 function_ptr_command<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
448 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
449 []() throw(std::bad_alloc, std::runtime_error) {
450 if(amode != ADVANCE_AUTO) {
451 amode = ADVANCE_AUTO;
452 platform::set_paused(false);
453 platform::cancel_wait();
454 messages << "Unpaused" << std::endl;
455 } else {
456 platform::cancel_wait();
457 cancel_advance = false;
458 stop_at_frame_active = false;
459 amode = ADVANCE_PAUSE;
460 messages << "Paused" << std::endl;
464 function_ptr_command<> save_jukebox_prev(lsnes_cmd, "cycle-jukebox-backward", "Cycle save jukebox backwards",
465 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
466 []() throw(std::bad_alloc, std::runtime_error) {
467 if(jukebox_size == 0)
468 return;
469 if(save_jukebox_pointer == 0)
470 save_jukebox_pointer = jukebox_size - 1;
471 else
472 save_jukebox_pointer--;
473 if(save_jukebox_pointer >= jukebox_size)
474 save_jukebox_pointer = 0;
475 update_movie_state();
476 information_dispatch::do_status_update();
479 function_ptr_command<> save_jukebox_next(lsnes_cmd, "cycle-jukebox-forward", "Cycle save jukebox forwards",
480 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
481 []() throw(std::bad_alloc, std::runtime_error) {
482 if(jukebox_size == 0)
483 return;
484 if(save_jukebox_pointer >= jukebox_size - 1)
485 save_jukebox_pointer = 0;
486 else
487 save_jukebox_pointer++;
488 if(save_jukebox_pointer >= jukebox_size)
489 save_jukebox_pointer = 0;
490 update_movie_state();
491 information_dispatch::do_status_update();
494 function_ptr_command<> load_jukebox(lsnes_cmd, "load-jukebox", "Load save from jukebox",
495 "Syntax: load-jukebox\nLoad save from jukebox\n",
496 []() throw(std::bad_alloc, std::runtime_error) {
497 if(jukebox_size == 0)
498 throw std::runtime_error("No slot selected");
499 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
502 function_ptr_command<> load_jukebox_readwrite(lsnes_cmd, "load-jukebox-readwrite", "Load save from jukebox in"
503 " read-write mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
504 []() throw(std::bad_alloc, std::runtime_error) {
505 if(jukebox_size == 0)
506 throw std::runtime_error("No slot selected");
507 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
510 function_ptr_command<> load_jukebox_readonly(lsnes_cmd, "load-jukebox-readonly", "Load save from jukebox in "
511 "read-only mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
512 []() throw(std::bad_alloc, std::runtime_error) {
513 if(jukebox_size == 0)
514 throw std::runtime_error("No slot selected");
515 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
518 function_ptr_command<> load_jukebox_preserve(lsnes_cmd, "load-jukebox-preserve", "Load save from jukebox, "
519 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
520 []() throw(std::bad_alloc, std::runtime_error) {
521 if(jukebox_size == 0)
522 throw std::runtime_error("No slot selected");
523 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
526 function_ptr_command<> load_jukebox_movie(lsnes_cmd, "load-jukebox-movie", "Load save from jukebox as movie",
527 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
528 []() throw(std::bad_alloc, std::runtime_error) {
529 if(jukebox_size == 0)
530 throw std::runtime_error("No slot selected");
531 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
534 function_ptr_command<> save_jukebox_c(lsnes_cmd, "save-jukebox", "Save save to jukebox",
535 "Syntax: save-jukebox\nSave save to jukebox\n",
536 []() throw(std::bad_alloc, std::runtime_error) {
537 if(jukebox_size == 0)
538 throw std::runtime_error("No slot selected");
539 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE);
542 function_ptr_command<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
543 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
544 []() throw(std::bad_alloc, std::runtime_error) {
545 amode = ADVANCE_FRAME;
546 cancel_advance = false;
547 advanced_once = false;
548 platform::cancel_wait();
549 platform::set_paused(false);
552 function_ptr_command<> nadvance_frame(lsnes_cmd, "-advance-frame", "Advance one frame",
553 "No help available\n",
554 []() throw(std::bad_alloc, std::runtime_error) {
555 cancel_advance = true;
556 platform::cancel_wait();
557 platform::set_paused(false);
560 function_ptr_command<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
561 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
562 []() throw(std::bad_alloc, std::runtime_error) {
563 amode = ADVANCE_SUBFRAME;
564 cancel_advance = false;
565 advanced_once = false;
566 platform::cancel_wait();
567 platform::set_paused(false);
570 function_ptr_command<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
571 "No help available\n",
572 []() throw(std::bad_alloc, std::runtime_error) {
573 cancel_advance = true;
574 platform::cancel_wait();
575 platform::set_paused(false);
578 function_ptr_command<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
579 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
580 []() throw(std::bad_alloc, std::runtime_error) {
581 amode = ADVANCE_SKIPLAG_PENDING;
582 platform::cancel_wait();
583 platform::set_paused(false);
586 function_ptr_command<const std::string&> reset_c(lsnes_cmd, "reset", "Reset the system",
587 "Syntax: reset\nReset <delay>\nResets the system in beginning of the next frame.\n",
588 [](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
589 if(!our_rom->rtype->get_reset_support()) {
590 messages << "Emulator core does not support resets" << std::endl;
591 return;
593 if((our_rom->rtype->get_reset_support() & 3) < 2 && x != "") {
594 messages << "Emulator core does not support delayed resets" << std::endl;
595 return;
597 if(x == "")
598 our_rom->rtype->request_reset(0, false);
599 else
600 our_rom->rtype->request_reset(parse_value<uint32_t>(x), false);
603 function_ptr_command<const std::string&> hreset_c(lsnes_cmd, "reset-hard", "Reset the system",
604 "Syntax: reset-hard\nReset-hard <delay>\nHard resets the system in beginning of the next frame.\n",
605 [](const std::string& x) throw(std::bad_alloc, std::runtime_error) {
606 if((our_rom->rtype->get_reset_support() & 4) == 0) {
607 messages << "Emulator core does not support hard resets" << std::endl;
608 return;
610 if((our_rom->rtype->get_reset_support() & 3) < 2 && x != "") {
611 messages << "Emulator core does not support delayed hard resets" << std::endl;
612 return;
614 if(x == "")
615 our_rom->rtype->request_reset(0, true);
616 else
617 our_rom->rtype->request_reset(parse_value<uint32_t>(x), true);
620 function_ptr_command<arg_filename> load_c(lsnes_cmd, "load", "Load savestate (current mode)",
621 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
622 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
623 mark_pending_load(args, LOAD_STATE_CURRENT);
626 function_ptr_command<arg_filename> load_smart_c(lsnes_cmd, "load-smart", "Load savestate (heuristic mode)",
627 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
628 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
629 mark_pending_load(args, LOAD_STATE_DEFAULT);
632 function_ptr_command<arg_filename> load_state_c(lsnes_cmd, "load-state", "Load savestate (R/W)",
633 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
634 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
635 mark_pending_load(args, LOAD_STATE_RW);
638 function_ptr_command<arg_filename> load_readonly(lsnes_cmd, "load-readonly", "Load savestate (RO)",
639 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
640 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
641 mark_pending_load(args, LOAD_STATE_RO);
644 function_ptr_command<arg_filename> load_preserve(lsnes_cmd, "load-preserve", "Load savestate (preserve "
645 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
646 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
647 mark_pending_load(args, LOAD_STATE_PRESERVE);
650 function_ptr_command<arg_filename> load_movie_c(lsnes_cmd, "load-movie", "Load movie",
651 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
652 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
653 mark_pending_load(args, LOAD_STATE_MOVIE);
657 function_ptr_command<arg_filename> save_state(lsnes_cmd, "save-state", "Save state",
658 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
659 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
660 mark_pending_save(args, SAVE_STATE);
663 function_ptr_command<arg_filename> save_movie(lsnes_cmd, "save-movie", "Save movie",
664 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
665 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
666 mark_pending_save(args, SAVE_MOVIE);
669 function_ptr_command<> set_rwmode(lsnes_cmd, "set-rwmode", "Switch to read/write mode",
670 "Syntax: set-rwmode\nSwitches to read/write mode\n",
671 []() throw(std::bad_alloc, std::runtime_error) {
672 movb.get_movie().readonly_mode(false);
673 information_dispatch::do_mode_change(false);
674 lua_callback_do_readwrite();
675 update_movie_state();
676 information_dispatch::do_status_update();
679 function_ptr_command<> set_romode(lsnes_cmd, "set-romode", "Switch to read-only mode",
680 "Syntax: set-romode\nSwitches to read-only mode\n",
681 []() throw(std::bad_alloc, std::runtime_error) {
682 movb.get_movie().readonly_mode(true);
683 information_dispatch::do_mode_change(true);
684 update_movie_state();
685 information_dispatch::do_status_update();
688 function_ptr_command<> toggle_rwmode(lsnes_cmd, "toggle-rwmode", "Toggle read/write mode",
689 "Syntax: toggle-rwmode\nToggles read/write mode\n",
690 []() throw(std::bad_alloc, std::runtime_error) {
691 bool c = movb.get_movie().readonly_mode();
692 movb.get_movie().readonly_mode(!c);
693 information_dispatch::do_mode_change(!c);
694 if(c)
695 lua_callback_do_readwrite();
696 update_movie_state();
697 information_dispatch::do_status_update();
700 function_ptr_command<> repaint(lsnes_cmd, "repaint", "Redraw the screen",
701 "Syntax: repaint\nRedraws the screen\n",
702 []() throw(std::bad_alloc, std::runtime_error) {
703 redraw_framebuffer();
706 function_ptr_command<> tpon(lsnes_cmd, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
707 []() throw(std::bad_alloc, std::runtime_error) {
708 bool tmp = pause_on_end;
709 pause_on_end.set(!tmp);
710 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
713 function_ptr_command<> spon(lsnes_cmd, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
714 []() throw(std::bad_alloc, std::runtime_error) {
715 pause_on_end.set(true);
716 messages << "Pause-on-end is now ON" << std::endl;
719 function_ptr_command<> cpon(lsnes_cmd, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
720 []() throw(std::bad_alloc, std::runtime_error) {
721 pause_on_end.set(false);
722 messages << "Pause-on-end is now OFF" << std::endl;
725 function_ptr_command<> rewind_movie(lsnes_cmd, "rewind-movie", "Rewind movie to the beginning",
726 "Syntax: rewind-movie\nRewind movie to the beginning\n",
727 []() throw(std::bad_alloc, std::runtime_error) {
728 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
731 function_ptr_command<const std::string&> reload_rom2(lsnes_cmd, "reload-rom", "Reload the ROM image",
732 "Syntax: reload-rom [<file>]\nReload the ROM image from <file>\n",
733 [](const std::string& filename) throw(std::bad_alloc, std::runtime_error) {
734 if(reload_rom(filename))
735 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
738 function_ptr_command<> cancel_save(lsnes_cmd, "cancel-saves", "Cancel all pending saves", "Syntax: "
739 "cancel-save\nCancel pending saves\n",
740 []() throw(std::bad_alloc, std::runtime_error) {
741 queued_saves.clear();
742 messages << "Pending saves canceled." << std::endl;
745 inverse_bind ipause_emulator(lsnes_mapper, "pause-emulator", "Speed‣(Un)pause");
746 inverse_bind ijback(lsnes_mapper, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
747 inverse_bind ijforward(lsnes_mapper, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
748 inverse_bind iloadj(lsnes_mapper, "load-jukebox", "Load‣Selected slot");
749 inverse_bind iloadjrw(lsnes_mapper, "load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
750 inverse_bind iloadjro(lsnes_mapper, "load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
751 inverse_bind iloadjp(lsnes_mapper, "load-jukebox-preserve", "Load‣Selected slot (preserve input)");
752 inverse_bind iloadjm(lsnes_mapper, "load-jukebox-movie", "Load‣Selected slot (as movie)");
753 inverse_bind isavej(lsnes_mapper, "save-jukebox", "Save‣Selected slot");
754 inverse_bind iadvframe(lsnes_mapper, "+advance-frame", "Speed‣Advance frame");
755 inverse_bind iadvsubframe(lsnes_mapper, "+advance-poll", "Speed‣Advance subframe");
756 inverse_bind iskiplag(lsnes_mapper, "advance-skiplag", "Speed‣Advance poll");
757 inverse_bind ireset(lsnes_mapper, "reset", "System‣Reset");
758 inverse_bind iset_rwmode(lsnes_mapper, "set-rwmode", "Movie‣Switch to read/write");
759 inverse_bind itoggle_romode(lsnes_mapper, "set-romode", "Movie‣Switch to read-only");
760 inverse_bind itoggle_rwmode(lsnes_mapper, "toggle-rwmode", "Movie‣Toggle read-only");
761 inverse_bind irepaint(lsnes_mapper, "repaint", "System‣Repaint screen");
762 inverse_bind itogglepause(lsnes_mapper, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
763 inverse_bind irewind_movie(lsnes_mapper, "rewind-movie", "Movie‣Rewind movie");
764 inverse_bind icancel_saves(lsnes_mapper, "cancel-saves", "Save‣Cancel pending saves");
765 inverse_bind iload1(lsnes_mapper, "load ${project}1.lsmv", "Load‣Slot 1");
766 inverse_bind iload2(lsnes_mapper, "load ${project}2.lsmv", "Load‣Slot 2");
767 inverse_bind iload3(lsnes_mapper, "load ${project}3.lsmv", "Load‣Slot 3");
768 inverse_bind iload4(lsnes_mapper, "load ${project}4.lsmv", "Load‣Slot 4");
769 inverse_bind iload5(lsnes_mapper, "load ${project}5.lsmv", "Load‣Slot 5");
770 inverse_bind iload6(lsnes_mapper, "load ${project}6.lsmv", "Load‣Slot 6");
771 inverse_bind iload7(lsnes_mapper, "load ${project}7.lsmv", "Load‣Slot 7");
772 inverse_bind iload8(lsnes_mapper, "load ${project}8.lsmv", "Load‣Slot 8");
773 inverse_bind iload9(lsnes_mapper, "load ${project}9.lsmv", "Load‣Slot 9");
774 inverse_bind iload10(lsnes_mapper, "load ${project}10.lsmv", "Load‣Slot 10");
775 inverse_bind iload11(lsnes_mapper, "load ${project}11.lsmv", "Load‣Slot 11");
776 inverse_bind iload12(lsnes_mapper, "load ${project}12.lsmv", "Load‣Slot 12");
777 inverse_bind iload13(lsnes_mapper, "load ${project}13.lsmv", "Load‣Slot 13");
778 inverse_bind iload14(lsnes_mapper, "load ${project}14.lsmv", "Load‣Slot 14");
779 inverse_bind iload15(lsnes_mapper, "load ${project}15.lsmv", "Load‣Slot 15");
780 inverse_bind iload16(lsnes_mapper, "load ${project}16.lsmv", "Load‣Slot 16");
781 inverse_bind iload17(lsnes_mapper, "load ${project}17.lsmv", "Load‣Slot 17");
782 inverse_bind iload18(lsnes_mapper, "load ${project}18.lsmv", "Load‣Slot 18");
783 inverse_bind iload19(lsnes_mapper, "load ${project}19.lsmv", "Load‣Slot 19");
784 inverse_bind iload20(lsnes_mapper, "load ${project}20.lsmv", "Load‣Slot 20");
785 inverse_bind iload21(lsnes_mapper, "load ${project}21.lsmv", "Load‣Slot 21");
786 inverse_bind iload22(lsnes_mapper, "load ${project}22.lsmv", "Load‣Slot 22");
787 inverse_bind iload23(lsnes_mapper, "load ${project}23.lsmv", "Load‣Slot 23");
788 inverse_bind iload24(lsnes_mapper, "load ${project}24.lsmv", "Load‣Slot 24");
789 inverse_bind iload25(lsnes_mapper, "load ${project}25.lsmv", "Load‣Slot 25");
790 inverse_bind iload26(lsnes_mapper, "load ${project}26.lsmv", "Load‣Slot 26");
791 inverse_bind iload27(lsnes_mapper, "load ${project}27.lsmv", "Load‣Slot 27");
792 inverse_bind iload28(lsnes_mapper, "load ${project}28.lsmv", "Load‣Slot 28");
793 inverse_bind iload29(lsnes_mapper, "load ${project}29.lsmv", "Load‣Slot 29");
794 inverse_bind iload30(lsnes_mapper, "load ${project}30.lsmv", "Load‣Slot 30");
795 inverse_bind iload31(lsnes_mapper, "load ${project}31.lsmv", "Load‣Slot 31");
796 inverse_bind iload32(lsnes_mapper, "load ${project}32.lsmv", "Load‣Slot 32");
797 inverse_bind isave1(lsnes_mapper, "save-state ${project}1.lsmv", "Save‣Slot 1");
798 inverse_bind isave2(lsnes_mapper, "save-state ${project}2.lsmv", "Save‣Slot 2");
799 inverse_bind isave3(lsnes_mapper, "save-state ${project}3.lsmv", "Save‣Slot 3");
800 inverse_bind isave4(lsnes_mapper, "save-state ${project}4.lsmv", "Save‣Slot 4");
801 inverse_bind isave5(lsnes_mapper, "save-state ${project}5.lsmv", "Save‣Slot 5");
802 inverse_bind isave6(lsnes_mapper, "save-state ${project}6.lsmv", "Save‣Slot 6");
803 inverse_bind isave7(lsnes_mapper, "save-state ${project}7.lsmv", "Save‣Slot 7");
804 inverse_bind isave8(lsnes_mapper, "save-state ${project}8.lsmv", "Save‣Slot 8");
805 inverse_bind isave9(lsnes_mapper, "save-state ${project}9.lsmv", "Save‣Slot 9");
806 inverse_bind isave10(lsnes_mapper, "save-state ${project}10.lsmv", "Save‣Slot 10");
807 inverse_bind isave11(lsnes_mapper, "save-state ${project}11.lsmv", "Save‣Slot 11");
808 inverse_bind isave12(lsnes_mapper, "save-state ${project}12.lsmv", "Save‣Slot 12");
809 inverse_bind isave13(lsnes_mapper, "save-state ${project}13.lsmv", "Save‣Slot 13");
810 inverse_bind isave14(lsnes_mapper, "save-state ${project}14.lsmv", "Save‣Slot 14");
811 inverse_bind isave15(lsnes_mapper, "save-state ${project}15.lsmv", "Save‣Slot 15");
812 inverse_bind isave16(lsnes_mapper, "save-state ${project}16.lsmv", "Save‣Slot 16");
813 inverse_bind isave17(lsnes_mapper, "save-state ${project}17.lsmv", "Save‣Slot 17");
814 inverse_bind isave18(lsnes_mapper, "save-state ${project}18.lsmv", "Save‣Slot 18");
815 inverse_bind isave19(lsnes_mapper, "save-state ${project}19.lsmv", "Save‣Slot 19");
816 inverse_bind isave20(lsnes_mapper, "save-state ${project}20.lsmv", "Save‣Slot 20");
817 inverse_bind isave21(lsnes_mapper, "save-state ${project}21.lsmv", "Save‣Slot 21");
818 inverse_bind isave22(lsnes_mapper, "save-state ${project}22.lsmv", "Save‣Slot 22");
819 inverse_bind isave23(lsnes_mapper, "save-state ${project}23.lsmv", "Save‣Slot 23");
820 inverse_bind isave24(lsnes_mapper, "save-state ${project}24.lsmv", "Save‣Slot 24");
821 inverse_bind isave25(lsnes_mapper, "save-state ${project}25.lsmv", "Save‣Slot 25");
822 inverse_bind isave26(lsnes_mapper, "save-state ${project}26.lsmv", "Save‣Slot 26");
823 inverse_bind isave27(lsnes_mapper, "save-state ${project}27.lsmv", "Save‣Slot 27");
824 inverse_bind isave28(lsnes_mapper, "save-state ${project}28.lsmv", "Save‣Slot 28");
825 inverse_bind isave29(lsnes_mapper, "save-state ${project}29.lsmv", "Save‣Slot 29");
826 inverse_bind isave30(lsnes_mapper, "save-state ${project}30.lsmv", "Save‣Slot 30");
827 inverse_bind isave31(lsnes_mapper, "save-state ${project}31.lsmv", "Save‣Slot 31");
828 inverse_bind isave32(lsnes_mapper, "save-state ${project}32.lsmv", "Save‣Slot 32");
830 bool on_quit_prompt = false;
831 class mywindowcallbacks : public information_dispatch
833 public:
834 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
835 void on_new_dumper(const std::string& n)
837 update_movie_state();
839 void on_destroy_dumper(const std::string& n)
841 update_movie_state();
843 void on_close() throw()
845 if(on_quit_prompt) {
846 amode = ADVANCE_QUIT;
847 platform::set_paused(false);
848 platform::cancel_wait();
849 return;
851 on_quit_prompt = true;
852 try {
853 amode = ADVANCE_QUIT;
854 platform::set_paused(false);
855 platform::cancel_wait();
856 } catch(...) {
858 on_quit_prompt = false;
860 } mywcb;
862 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
863 //failing.
864 int handle_load()
866 if(do_unsafe_rewind && unsafe_rewind_obj) {
867 uint64_t t = get_utime();
868 std::vector<char> s;
869 lua_callback_do_unsafe_rewind(s, 0, 0, movb.get_movie(), unsafe_rewind_obj);
870 information_dispatch::do_mode_change(false);
871 do_unsafe_rewind = false;
872 our_movie.is_savestate = true;
873 location_special = SPECIAL_SAVEPOINT;
874 update_movie_state();
875 messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
876 return 1;
878 if(pending_load != "") {
879 system_corrupt = false;
880 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
881 !do_load_state(pending_load, loadmode)) {
882 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
883 pending_load = "";
884 return -1;
886 try {
887 if(loadmode == LOAD_STATE_BEGINNING)
888 do_load_beginning(false);
889 if(loadmode == LOAD_STATE_ROMRELOAD)
890 do_load_beginning(true);
891 } catch(std::exception& e) {
892 messages << "Load failed: " << e.what() << std::endl;
894 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
895 pending_load = "";
896 amode = ADVANCE_AUTO;
897 platform::cancel_wait();
898 platform::set_paused(false);
899 if(!system_corrupt) {
900 location_special = SPECIAL_SAVEPOINT;
901 update_movie_state();
902 information_dispatch::do_status_update();
903 platform::flush_command_queue();
905 return 1;
907 return 0;
910 //If there are pending saves, perform them.
911 void handle_saves()
913 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
914 our_rom->rtype->runtosave();
915 for(auto i : queued_saves)
916 do_save_state(i);
917 if(do_unsafe_rewind && !unsafe_rewind_obj) {
918 uint64_t t = get_utime();
919 std::vector<char> s = our_rom->save_core_state(true);
920 uint64_t secs = our_movie.rtc_second;
921 uint64_t ssecs = our_movie.rtc_subsecond;
922 lua_callback_do_unsafe_rewind(s, secs, ssecs, movb.get_movie(), NULL);
923 do_unsafe_rewind = false;
924 messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
927 queued_saves.clear();
930 bool handle_corrupt()
932 if(!system_corrupt)
933 return false;
934 while(system_corrupt) {
935 platform::cancel_wait();
936 platform::set_paused(true);
937 platform::flush_command_queue();
938 handle_load();
939 if(amode == ADVANCE_QUIT)
940 return true;
942 return true;
946 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
947 std::runtime_error)
949 //Basic initialization.
950 emulation_thread = this_thread_id();
951 jukebox_size_listener jlistener;
952 voicethread_task();
953 init_special_screens();
954 our_rom = &rom;
955 lsnes_callbacks lsnes_callbacks_obj;
956 ecore_callbacks = &lsnes_callbacks_obj;
957 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
958 core_core::install_all_handlers();
960 //Load our given movie.
961 bool first_round = false;
962 bool just_did_loadstate = false;
963 try {
964 do_load_state(initial, LOAD_STATE_INITIAL);
965 location_special = SPECIAL_SAVEPOINT;
966 update_movie_state();
967 first_round = our_movie.is_savestate;
968 just_did_loadstate = first_round;
969 } catch(std::bad_alloc& e) {
970 OOM_panic();
971 } catch(std::exception& e) {
972 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
973 if(load_has_to_succeed) {
974 messages << "FATAL: Can't load movie" << std::endl;
975 platform::fatal_error();
977 system_corrupt = true;
978 update_movie_state();
979 redraw_framebuffer(screen_corrupt);
982 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
983 lua_callback_startup();
985 platform::set_paused(initial.start_paused);
986 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
987 stop_at_frame_active = false;
988 uint64_t time_x = get_utime();
989 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
990 if(handle_corrupt()) {
991 first_round = our_movie.is_savestate;
992 just_did_loadstate = first_round;
993 continue;
995 ack_frame_tick(get_utime());
996 if(amode == ADVANCE_SKIPLAG_PENDING)
997 amode = ADVANCE_SKIPLAG;
999 if(!first_round) {
1000 controls.reset_framehold();
1001 movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
1002 if(amode == ADVANCE_QUIT && queued_saves.empty())
1003 break;
1004 handle_saves();
1005 int r = 0;
1006 if(queued_saves.empty())
1007 r = handle_load();
1008 if(r > 0 || system_corrupt) {
1009 first_round = our_movie.is_savestate;
1010 if(system_corrupt)
1011 amode = ADVANCE_PAUSE;
1012 else
1013 amode = old_mode;
1014 stop_at_frame_active = false;
1015 just_did_loadstate = first_round;
1016 controls.reset_framehold();
1017 continue;
1018 } else if(r < 0) {
1019 //Not exactly desriable, but this at least won't desync.
1020 stop_at_frame_active = false;
1021 amode = ADVANCE_PAUSE;
1024 if(just_did_loadstate) {
1025 //If we just loadstated, we are up to date.
1026 if(amode == ADVANCE_QUIT)
1027 break;
1028 platform::cancel_wait();
1029 platform::set_paused(amode == ADVANCE_PAUSE);
1030 platform::flush_command_queue();
1031 //We already have done the reset this frame if we are going to do one at all.
1032 movb.get_movie().set_controls(movb.update_controls(true));
1033 movb.get_movie().set_all_DRDY();
1034 just_did_loadstate = false;
1036 frame_irq_time = get_utime() - time_x;
1037 our_rom->rtype->emulate();
1038 time_x = get_utime();
1039 if(amode == ADVANCE_AUTO)
1040 platform::wait(to_wait_frame(get_utime()));
1041 first_round = false;
1042 lua_callback_do_frame();
1044 information_dispatch::do_dump_end();
1045 core_core::uninstall_all_handlers();
1046 voicethread_kill();
1049 void set_stop_at_frame(uint64_t frame)
1051 stop_at_frame = frame;
1052 stop_at_frame_active = (frame != 0);
1053 amode = ADVANCE_AUTO;
1054 platform::set_paused(false);