Make redraw_framebuffer call update_movie_state();
[lsnes.git] / src / core / mainloop.cpp
blobd1699d30b407bb3c54773e210c47e35fb2199bc9
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/command.hpp"
6 #include "core/debug.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/framebuffer.hpp"
9 #include "core/framerate.hpp"
10 #include "core/inthread.hpp"
11 #include "core/keymapper.hpp"
12 #include "core/multitrack.hpp"
13 #include "lua/lua.hpp"
14 #include "library/string.hpp"
15 #include "core/mainloop.hpp"
16 #include "core/movie.hpp"
17 #include "core/moviedata.hpp"
18 #include "core/moviefile.hpp"
19 #include "core/memorymanip.hpp"
20 #include "core/memorywatch.hpp"
21 #include "core/project.hpp"
22 #include "core/rom.hpp"
23 #include "core/romloader.hpp"
24 #include "core/settings.hpp"
25 #include "core/window.hpp"
26 #include "interface/c-interface.hpp"
27 #include "interface/callbacks.hpp"
28 #include "interface/romtype.hpp"
29 #include "library/framebuffer.hpp"
30 #include "library/zip.hpp"
32 #include <iomanip>
33 #include <cassert>
34 #include <sstream>
35 #include <iostream>
36 #include <limits>
37 #include <set>
38 #include <sys/time.h>
40 #define SPECIAL_FRAME_START 0
41 #define SPECIAL_FRAME_VIDEO 1
42 #define SPECIAL_SAVEPOINT 2
43 #define SPECIAL_NONE 3
45 void update_movie_state();
46 time_t random_seed_value = 0;
48 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary(lsnes_vset,
49 "jukebox-default-binary", "Movie‣Saving‣Saveslots binary", true);
50 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary(lsnes_vset, "movie-default-binary",
51 "Movie‣Saving‣Movies binary", false);
52 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> save_dflt_binary(lsnes_vset,
53 "savestate-default-binary", "Movie‣Saving‣Savestates binary", false);
55 namespace
57 settingvar::variable<settingvar::model_int<0,999999>> advance_timeout_first(lsnes_vset, "advance-timeout",
58 "Delays‣First frame advance", 500);
59 settingvar::variable<settingvar::model_int<0,999999>> advance_timeout_subframe(lsnes_vset,
60 "advance-subframe-timeout", "Delays‣Subframe advance", 100);
61 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> pause_on_end(lsnes_vset, "pause-on-end",
62 "Movie‣Pause on end", false);
63 settingvar::variable<settingvar::model_int<0,999999999>> jukebox_size(lsnes_vset, "jukebox-size",
64 "Movie‣Number of save slots", 12);
66 enum advance_mode
68 ADVANCE_QUIT, //Quit the emulator.
69 ADVANCE_AUTO, //Normal (possibly slowed down play).
70 ADVANCE_LOAD, //Loading a state.
71 ADVANCE_FRAME, //Frame advance.
72 ADVANCE_SUBFRAME, //Subframe advance.
73 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
74 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
75 ADVANCE_PAUSE, //Unconditional pause.
78 //Our thread.
79 threads::id emulation_thread;
80 //Flags related to repeating advance.
81 bool advanced_once;
82 bool cancel_advance;
83 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
84 enum advance_mode amode;
85 enum advance_mode old_mode;
86 //Mode and filename of pending load, one of LOAD_* constants.
87 bool load_paused;
88 int loadmode;
89 std::string pending_load;
90 std::string pending_new_project;
91 //Queued saves (all savestates).
92 std::set<std::pair<std::string, int>> queued_saves;
93 //Save jukebox.
94 size_t save_jukebox_pointer;
95 //Special subframe location. One of SPECIAL_* constants.
96 int location_special;
97 //Last frame params.
98 bool last_hires = false;
99 bool last_interlace = false;
100 //Unsafe rewind.
101 bool do_unsafe_rewind = false;
102 void* unsafe_rewind_obj = NULL;
103 //Stop at frame.
104 bool stop_at_frame_active = false;
105 uint64_t stop_at_frame = 0;
106 //Macro hold.
107 bool macro_hold_1;
108 bool macro_hold_2;
109 //In break pause.
110 bool break_pause = false;
113 std::string save_jukebox_name(size_t i)
115 return (stringfmt() << "$SLOT:" << (i + 1)).str();
118 std::map<std::string, std::string> slotinfo_cache;
120 std::string vector_to_string(const std::vector<char>& x)
122 std::string y(x.begin(), x.end());
123 while(y.length() > 0 && y[y.length() - 1] < 32)
124 y = y.substr(0, y.length() - 1);
125 return y;
128 std::string get_slotinfo(const std::string& _filename)
130 std::string filename = resolve_relative_path(_filename);
131 if(!slotinfo_cache.count(filename)) {
132 std::ostringstream out;
133 try {
134 moviefile::brief_info info(filename);
135 if(!movb)
136 out << "No movie";
137 else if(movb.get_mfile().projectid == info.projectid)
138 out << info.rerecords << "R/" << info.current_frame << "F";
139 else
140 out << "Wrong movie";
141 } catch(...) {
142 out << "Nonexistent";
144 slotinfo_cache[filename] = out.str();
146 return slotinfo_cache[filename];
149 void flush_slotinfo(const std::string& filename)
151 slotinfo_cache.erase(resolve_relative_path(filename));
154 void flush_slotinfo()
156 slotinfo_cache.clear();
160 void mainloop_signal_need_rewind(void* ptr)
162 if(ptr) {
163 old_mode = amode;
164 amode = ADVANCE_LOAD;
166 do_unsafe_rewind = true;
167 unsafe_rewind_obj = ptr;
170 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
172 if(lua_requests_subframe_paint)
173 redraw_framebuffer();
175 if(subframe) {
176 if(amode == ADVANCE_SUBFRAME) {
177 if(!cancel_advance) {
178 if(!advanced_once)
179 platform::wait(advance_timeout_first * 1000);
180 else
181 platform::wait(advance_timeout_subframe * 1000);
182 advanced_once = true;
184 if(cancel_advance) {
185 stop_at_frame_active = false;
186 amode = ADVANCE_PAUSE;
187 cancel_advance = false;
189 platform::set_paused(amode == ADVANCE_PAUSE);
190 } else if(amode == ADVANCE_FRAME) {
192 } else {
193 if(amode == ADVANCE_SKIPLAG) {
194 stop_at_frame_active = false;
195 amode = ADVANCE_PAUSE;
197 platform::set_paused(amode == ADVANCE_PAUSE);
198 cancel_advance = false;
200 location_special = SPECIAL_NONE;
201 update_movie_state();
202 } else {
203 if(amode == ADVANCE_SKIPLAG_PENDING)
204 amode = ADVANCE_SKIPLAG;
205 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
206 if(!cancel_advance) {
207 uint64_t wait = 0;
208 if(!advanced_once)
209 wait = advance_timeout_first * 1000;
210 else if(amode == ADVANCE_SUBFRAME)
211 wait = advance_timeout_subframe * 1000;
212 else
213 wait = to_wait_frame(get_utime());
214 platform::wait(wait);
215 advanced_once = true;
217 if(cancel_advance) {
218 stop_at_frame_active = false;
219 amode = ADVANCE_PAUSE;
220 cancel_advance = false;
222 platform::set_paused(amode == ADVANCE_PAUSE);
223 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end &&
224 !stop_at_frame_active) {
225 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count()) {
226 stop_at_frame_active = false;
227 amode = ADVANCE_PAUSE;
228 platform::set_paused(true);
230 } else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
231 if(movb.get_movie().get_current_frame() >= stop_at_frame) {
232 stop_at_frame_active = false;
233 amode = ADVANCE_PAUSE;
234 platform::set_paused(true);
236 } else {
237 platform::set_paused((amode == ADVANCE_PAUSE));
238 cancel_advance = false;
240 location_special = SPECIAL_FRAME_START;
241 update_movie_state();
243 platform::flush_command_queue();
244 controller_frame tmp = controls.get(movb.get_movie().get_current_frame());
245 our_rom.rtype->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
246 lua_callback_do_input(tmp, subframe);
247 multitrack_editor.process_frame(tmp);
248 controls.commit(tmp);
249 return tmp;
252 namespace
255 //Do pending load (automatically unpauses).
256 void mark_pending_load(std::string filename, int lmode)
258 if(break_pause)
259 break_pause = false;
260 loadmode = lmode;
261 pending_load = filename;
262 old_mode = amode;
263 amode = ADVANCE_LOAD;
264 platform::cancel_wait();
265 platform::set_paused(false);
268 void mark_pending_save(std::string filename, int smode, int binary)
270 int tmp = -1;
271 if(smode == SAVE_MOVIE) {
272 //Just do this immediately.
273 do_save_movie(filename, binary);
274 flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
275 return;
277 if(location_special == SPECIAL_SAVEPOINT) {
278 //We can save immediately here.
279 do_save_state(filename, binary);
280 flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
281 return;
283 queued_saves.insert(std::make_pair(filename, binary));
284 messages << "Pending save on '" << filename << "'" << std::endl;
287 struct jukebox_size_listener : public settingvar::listener
289 jukebox_size_listener() { lsnes_vset.add_listener(*this); }
290 ~jukebox_size_listener() throw() {lsnes_vset.remove_listener(*this); };
291 void on_setting_change(settingvar::group& grp, const settingvar::base& val)
293 if(val.get_iname() == "jukebox-size") {
294 if(save_jukebox_pointer >= jukebox_size)
295 save_jukebox_pointer = 0;
297 update_movie_state();
302 void update_movie_state()
304 auto p = project_get();
305 bool readonly = false;
306 static unsigned last_controllers = 0;
308 uint64_t magic[4];
309 our_rom.region->fill_framerate_magic(magic);
310 if(movb)
311 voice_frame_number(movb.get_movie().get_current_frame(), 1.0 * magic[1] / magic[0]);
312 else
313 voice_frame_number(0, 60.0); //Default.
315 auto& _status = platform::get_emustatus();
316 if(movb && !system_corrupt) {
317 _status.set("!frame", (stringfmt() << movb.get_movie().get_current_frame()).str());
318 _status.set("!length", (stringfmt() << movb.get_movie().get_frame_count()).str());
319 _status.set("!lag", (stringfmt() << movb.get_movie().get_lag_frames()).str());
320 if(location_special == SPECIAL_FRAME_START)
321 _status.set("!subframe", "0");
322 else if(location_special == SPECIAL_SAVEPOINT)
323 _status.set("!subframe", "S");
324 else if(location_special == SPECIAL_FRAME_VIDEO)
325 _status.set("!subframe", "V");
326 else
327 _status.set("!subframe", (stringfmt() << movb.get_movie().next_poll_number()).str());
328 } else {
329 _status.set("!frame", "N/A");
330 _status.set("!length", "N/A");
331 _status.set("!lag", "N/A");
332 _status.set("!subframe", "N/A");
334 _status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
335 if(break_pause)
336 _status.set("!pause", "B");
337 else if(amode == ADVANCE_PAUSE)
338 _status.set("!pause", "P");
339 else
340 _status.set("!pause", "");
341 if(movb) {
342 auto& mo = movb.get_movie();
343 readonly = mo.readonly_mode();
344 if(system_corrupt)
345 _status.set("!mode", "C");
346 else if(!readonly)
347 _status.set("!mode", "R");
348 else if(mo.get_frame_count() >= mo.get_current_frame())
349 _status.set("!mode", "P");
350 else
351 _status.set("!mode", "F");
352 } else {
353 _status.erase("!subframe");
355 if(jukebox_size > 0) {
356 int tmp = -1;
357 std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, -1);
358 _status.set("!saveslot", (stringfmt() << (save_jukebox_pointer + 1)).str());
359 _status.set("!saveslotinfo", get_slotinfo(sfilen));
360 } else {
361 _status.erase("!saveslot");
362 _status.erase("!saveslotinfo");
364 if(p) {
365 _status.set("!branch", p->get_branch_string());
366 } else {
367 _status.erase("!branch");
370 std::string cur_branch = movb ? movb.get_mfile().current_branch() : "";
371 if(cur_branch != "")
372 _status.set("!mbranch", cur_branch);
373 else
374 _status.erase("!mbranch");
376 _status.set("!speed", (stringfmt() << (unsigned)(100 * get_realized_multiplier() + 0.5)).str());
378 if(movb && !system_corrupt) {
379 time_t timevalue = static_cast<time_t>(movb.get_mfile().rtc_second);
380 struct tm* time_decompose = gmtime(&timevalue);
381 char datebuffer[512];
382 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
383 _status.set("RTC", datebuffer);
384 } else {
385 _status.erase("RTC");
388 auto mset = controls.active_macro_set();
389 bool mfirst = true;
390 std::ostringstream mss;
391 for(auto i: mset) {
392 if(!mfirst) mss << ",";
393 mss << i;
394 mfirst = false;
396 _status.set("!macros", mss.str());
398 controller_frame c;
399 if(!multitrack_editor.any_records())
400 c = movb.get_movie().get_controls();
401 else
402 c = controls.get_committed();
403 for(unsigned i = 0;; i++) {
404 auto pindex = controls.lcid_to_pcid(i);
405 if(pindex.first < 0 || !controls.is_present(pindex.first, pindex.second)) {
406 for(unsigned j = i; j < last_controllers; j++)
407 _status.erase((stringfmt() << "P" << (j + 1)).str());
408 last_controllers = i;
409 break;
411 char32_t buffer[MAX_DISPLAY_LENGTH];
412 c.display(pindex.first, pindex.second, buffer);
413 std::u32string _buffer = buffer;
414 if(readonly && multitrack_editor.is_enabled()) {
415 multitrack_edit::state st = multitrack_editor.get(pindex.first, pindex.second);
416 if(st == multitrack_edit::MT_PRESERVE)
417 _buffer += U" (keep)";
418 else if(st == multitrack_edit::MT_OVERWRITE)
419 _buffer += U" (rewrite)";
420 else if(st == multitrack_edit::MT_OR)
421 _buffer += U" (OR)";
422 else if(st == multitrack_edit::MT_XOR)
423 _buffer += U" (XOR)";
424 else
425 _buffer += U" (\?\?\?)";
427 _status.set((stringfmt() << "P" << (i + 1)).str(), _buffer);
429 notify_status_update();
432 uint64_t audio_irq_time;
433 uint64_t controller_irq_time;
434 uint64_t frame_irq_time;
437 struct lsnes_callbacks : public emucore_callbacks
439 public:
440 ~lsnes_callbacks() throw()
444 int16_t get_input(unsigned port, unsigned index, unsigned control)
446 int16_t x;
447 x = movb.input_poll(port, index, control);
448 lua_callback_snoop_input(port, index, control, x);
449 return x;
452 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
454 if(!movb.get_movie().readonly_mode()) {
455 controller_frame f = movb.get_movie().get_controls();
456 f.axis3(port, index, control, value);
457 movb.get_movie().set_controls(f);
459 return movb.get_movie().next_input(port, index, control);
462 void notify_latch(std::list<std::string>& args)
464 lua_callback_do_latch(args);
467 void timer_tick(uint32_t increment, uint32_t per_second)
469 if(!movb)
470 return;
471 auto& m = movb.get_mfile();
472 m.rtc_subsecond += increment;
473 while(m.rtc_subsecond >= per_second) {
474 m.rtc_second++;
475 m.rtc_subsecond -= per_second;
479 std::string get_firmware_path()
481 return lsnes_vset["firmwarepath"].str();
484 std::string get_base_path()
486 return our_rom.msu1_base;
489 time_t get_time()
491 return movb ? movb.get_mfile().rtc_second : 0;
494 time_t get_randomseed()
496 return random_seed_value;
499 void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
501 lua_callback_do_frame_emulated();
502 location_special = SPECIAL_FRAME_VIDEO;
503 redraw_framebuffer(screen, false, true);
504 uint32_t g = gcd(fps_n, fps_d);
505 fps_n /= g;
506 fps_d /= g;
507 information_dispatch::do_frame(screen, fps_n, fps_d);
510 void action_state_updated()
512 graphics_driver_action_updated();
515 void memory_read(uint64_t addr, uint64_t value)
517 debug_fire_callback_read(addr, value);
520 void memory_write(uint64_t addr, uint64_t value)
522 debug_fire_callback_write(addr, value);
525 void memory_execute(uint64_t addr, uint64_t proc)
527 debug_fire_callback_exec(addr, proc);
530 void memory_trace(uint64_t proc, const char* str, bool insn)
532 debug_fire_callback_trace(proc, str, insn);
536 namespace
538 lsnes_callbacks lsnes_callbacks_obj;
540 command::fnptr<const std::string&> test4(lsnes_cmd, "test4", "test", "test",
541 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
542 std::list<std::string> _args;
543 std::string args2 = args;
544 for(auto& sym : token_iterator_foreach(args, {" ", "\t"}))
545 _args.push_back(sym);
546 lua_callback_do_latch(_args);
548 command::fnptr<> count_rerecords(lsnes_cmd, "count-rerecords", "Count rerecords",
549 "Syntax: count-rerecords\nCounts rerecords.\n",
550 []() throw(std::bad_alloc, std::runtime_error) {
551 std::vector<char> tmp;
552 uint64_t x = movb.get_rrdata().write(tmp);
553 messages << x << " rerecord(s)" << std::endl;
556 command::fnptr<const std::string&> quit_emulator(lsnes_cmd, "quit-emulator", "Quit the emulator",
557 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
558 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
559 amode = ADVANCE_QUIT;
560 platform::set_paused(false);
561 platform::cancel_wait();
564 command::fnptr<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
565 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
566 []() throw(std::bad_alloc, std::runtime_error) {
567 break_pause = false;
568 amode = ADVANCE_AUTO;
569 platform::set_paused(false);
570 platform::cancel_wait();
573 command::fnptr<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
574 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
575 []() throw(std::bad_alloc, std::runtime_error) {
576 if(amode != ADVANCE_AUTO || break_pause) {
577 break_pause = false;
578 amode = ADVANCE_AUTO;
579 platform::set_paused(false);
580 platform::cancel_wait();
581 } else {
582 platform::cancel_wait();
583 cancel_advance = false;
584 stop_at_frame_active = false;
585 amode = ADVANCE_PAUSE;
589 command::fnptr<> save_jukebox_prev(lsnes_cmd, "cycle-jukebox-backward", "Cycle save jukebox backwards",
590 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
591 []() throw(std::bad_alloc, std::runtime_error) {
592 if(jukebox_size == 0)
593 return;
594 if(save_jukebox_pointer == 0)
595 save_jukebox_pointer = jukebox_size - 1;
596 else
597 save_jukebox_pointer--;
598 if(save_jukebox_pointer >= jukebox_size)
599 save_jukebox_pointer = 0;
600 update_movie_state();
603 command::fnptr<> save_jukebox_next(lsnes_cmd, "cycle-jukebox-forward", "Cycle save jukebox forwards",
604 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
605 []() throw(std::bad_alloc, std::runtime_error) {
606 if(jukebox_size == 0)
607 return;
608 if(save_jukebox_pointer >= jukebox_size - 1)
609 save_jukebox_pointer = 0;
610 else
611 save_jukebox_pointer++;
612 if(save_jukebox_pointer >= jukebox_size)
613 save_jukebox_pointer = 0;
614 update_movie_state();
617 command::fnptr<const std::string&> save_jukebox_set(lsnes_cmd, "set-jukebox-slot", "Set jukebox slot",
618 "Syntax: set-jukebox-slot\nSet jukebox slot\n", [](const std::string& args)
619 throw(std::bad_alloc, std::runtime_error) {
620 if(!regex_match("[1-9][0-9]{0,8}", args))
621 throw std::runtime_error("Bad slot number");
622 uint32_t slot = parse_value<uint32_t>(args);
623 if(slot >= jukebox_size)
624 throw std::runtime_error("Bad slot number");
625 save_jukebox_pointer = slot - 1;
626 update_movie_state();
629 command::fnptr<> load_jukebox(lsnes_cmd, "load-jukebox", "Load save from jukebox",
630 "Syntax: load-jukebox\nLoad save from jukebox\n",
631 []() throw(std::bad_alloc, std::runtime_error) {
632 if(jukebox_size == 0)
633 throw std::runtime_error("No slot selected");
634 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
637 command::fnptr<> load_jukebox_readwrite(lsnes_cmd, "load-jukebox-readwrite", "Load save from jukebox in"
638 " read-write mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
639 []() throw(std::bad_alloc, std::runtime_error) {
640 if(jukebox_size == 0)
641 throw std::runtime_error("No slot selected");
642 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
645 command::fnptr<> load_jukebox_readonly(lsnes_cmd, "load-jukebox-readonly", "Load save from jukebox in "
646 "read-only mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
647 []() throw(std::bad_alloc, std::runtime_error) {
648 if(jukebox_size == 0)
649 throw std::runtime_error("No slot selected");
650 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
653 command::fnptr<> load_jukebox_preserve(lsnes_cmd, "load-jukebox-preserve", "Load save from jukebox, "
654 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
655 []() throw(std::bad_alloc, std::runtime_error) {
656 if(jukebox_size == 0)
657 throw std::runtime_error("No slot selected");
658 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
661 command::fnptr<> load_jukebox_movie(lsnes_cmd, "load-jukebox-movie", "Load save from jukebox as movie",
662 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
663 []() throw(std::bad_alloc, std::runtime_error) {
664 if(jukebox_size == 0)
665 throw std::runtime_error("No slot selected");
666 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
669 command::fnptr<> save_jukebox_c(lsnes_cmd, "save-jukebox", "Save save to jukebox",
670 "Syntax: save-jukebox\nSave save to jukebox\n",
671 []() throw(std::bad_alloc, std::runtime_error) {
672 if(jukebox_size == 0)
673 throw std::runtime_error("No slot selected");
674 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE, -1);
677 command::fnptr<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
678 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
679 []() throw(std::bad_alloc, std::runtime_error) {
680 if(break_pause)
681 break_pause = false;
682 amode = ADVANCE_FRAME;
683 cancel_advance = false;
684 advanced_once = false;
685 platform::cancel_wait();
686 platform::set_paused(false);
689 command::fnptr<> nadvance_frame(lsnes_cmd, "-advance-frame", "Advance one frame",
690 "No help available\n",
691 []() throw(std::bad_alloc, std::runtime_error) {
692 cancel_advance = true;
693 platform::cancel_wait();
694 platform::set_paused(false);
697 command::fnptr<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
698 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
699 []() throw(std::bad_alloc, std::runtime_error) {
700 if(break_pause)
701 break_pause = false;
702 amode = ADVANCE_SUBFRAME;
703 cancel_advance = false;
704 advanced_once = false;
705 platform::cancel_wait();
706 platform::set_paused(false);
709 command::fnptr<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
710 "No help available\n",
711 []() throw(std::bad_alloc, std::runtime_error) {
712 if(break_pause)
713 break_pause = false;
714 cancel_advance = true;
715 platform::cancel_wait();
716 platform::set_paused(false);
719 command::fnptr<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
720 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
721 []() throw(std::bad_alloc, std::runtime_error) {
722 if(break_pause)
723 break_pause = false;
724 amode = ADVANCE_SKIPLAG_PENDING;
725 platform::cancel_wait();
726 platform::set_paused(false);
729 command::fnptr<> reset_c(lsnes_cmd, "reset", "Reset the system",
730 "Syntax: reset\nReset\nResets the system in beginning of the next frame.\n",
731 []() throw(std::bad_alloc, std::runtime_error) {
732 int sreset_action = our_rom.rtype->reset_action(false);
733 if(sreset_action < 0) {
734 platform::error_message("Core does not support resets");
735 messages << "Emulator core does not support resets" << std::endl;
736 return;
738 our_rom.rtype->execute_action(sreset_action, std::vector<interface_action_paramval>());
741 command::fnptr<> hreset_c(lsnes_cmd, "reset-hard", "Reset the system",
742 "Syntax: reset-hard\nReset-hard\nHard resets the system in beginning of the next frame.\n",
743 []() throw(std::bad_alloc, std::runtime_error) {
744 int hreset_action = our_rom.rtype->reset_action(true);
745 if(hreset_action < 0) {
746 platform::error_message("Core does not support hard resets");
747 messages << "Emulator core does not support hard resets" << std::endl;
748 return;
750 our_rom.rtype->execute_action(hreset_action, std::vector<interface_action_paramval>());
753 command::fnptr<command::arg_filename> load_c(lsnes_cmd, "load", "Load savestate (current mode)",
754 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
755 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
756 mark_pending_load(args, LOAD_STATE_CURRENT);
759 command::fnptr<command::arg_filename> load_smart_c(lsnes_cmd, "load-smart", "Load savestate (heuristic mode)",
760 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
761 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
762 mark_pending_load(args, LOAD_STATE_DEFAULT);
765 command::fnptr<command::arg_filename> load_state_c(lsnes_cmd, "load-state", "Load savestate (R/W)",
766 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
767 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
768 mark_pending_load(args, LOAD_STATE_RW);
771 command::fnptr<command::arg_filename> load_readonly(lsnes_cmd, "load-readonly", "Load savestate (RO)",
772 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
773 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
774 mark_pending_load(args, LOAD_STATE_RO);
777 command::fnptr<command::arg_filename> load_preserve(lsnes_cmd, "load-preserve", "Load savestate (preserve "
778 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
779 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
780 mark_pending_load(args, LOAD_STATE_PRESERVE);
783 command::fnptr<command::arg_filename> load_movie_c(lsnes_cmd, "load-movie", "Load movie",
784 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
785 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
786 mark_pending_load(args, LOAD_STATE_MOVIE);
789 command::fnptr<command::arg_filename> load_allbr_c(lsnes_cmd, "load-allbranches", "Load savestate "
790 "(all branches)", "Syntax: load-allbranches <file>\nLoads SNES state from <file> with all "
791 "branches\n",
792 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
793 mark_pending_load(args, LOAD_STATE_ALLBRANCH);
796 command::fnptr<command::arg_filename> save_state(lsnes_cmd, "save-state", "Save state",
797 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
798 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
799 mark_pending_save(args, SAVE_STATE, -1);
802 command::fnptr<command::arg_filename> save_state2(lsnes_cmd, "save-state-binary", "Save state (binary)",
803 "Syntax: save-state-binary <file>\nSaves binary state to <file>\n",
804 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
805 mark_pending_save(args, SAVE_STATE, 1);
808 command::fnptr<command::arg_filename> save_state3(lsnes_cmd, "save-state-zip", "Save state (zip)",
809 "Syntax: save-state-zip <file>\nSaves zip state to <file>\n",
810 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
811 mark_pending_save(args, SAVE_STATE, 0);
814 command::fnptr<command::arg_filename> save_movie(lsnes_cmd, "save-movie", "Save movie",
815 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
816 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
817 mark_pending_save(args, SAVE_MOVIE, -1);
820 command::fnptr<command::arg_filename> save_movie2(lsnes_cmd, "save-movie-binary", "Save movie (binary)",
821 "Syntax: save-movie-binary <file>\nSaves binary movie to <file>\n",
822 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
823 mark_pending_save(args, SAVE_MOVIE, 1);
826 command::fnptr<command::arg_filename> save_movie3(lsnes_cmd, "save-movie-zip", "Save movie (zip)",
827 "Syntax: save-movie-zip <file>\nSaves zip movie to <file>\n",
828 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
829 mark_pending_save(args, SAVE_MOVIE, 0);
832 command::fnptr<> set_rwmode(lsnes_cmd, "set-rwmode", "Switch to read/write mode",
833 "Syntax: set-rwmode\nSwitches to read/write mode\n",
834 []() throw(std::bad_alloc, std::runtime_error) {
835 lua_callback_movie_lost("readwrite");
836 movb.get_movie().readonly_mode(false);
837 notify_mode_change(false);
838 lua_callback_do_readwrite();
839 update_movie_state();
842 command::fnptr<> set_romode(lsnes_cmd, "set-romode", "Switch to read-only mode",
843 "Syntax: set-romode\nSwitches to read-only mode\n",
844 []() throw(std::bad_alloc, std::runtime_error) {
845 movb.get_movie().readonly_mode(true);
846 notify_mode_change(true);
847 update_movie_state();
850 command::fnptr<> toggle_rwmode(lsnes_cmd, "toggle-rwmode", "Toggle read/write mode",
851 "Syntax: toggle-rwmode\nToggles read/write mode\n",
852 []() throw(std::bad_alloc, std::runtime_error) {
853 bool c = movb.get_movie().readonly_mode();
854 if(c)
855 lua_callback_movie_lost("readwrite");
856 movb.get_movie().readonly_mode(!c);
857 notify_mode_change(!c);
858 if(c)
859 lua_callback_do_readwrite();
860 update_movie_state();
863 command::fnptr<> repaint(lsnes_cmd, "repaint", "Redraw the screen",
864 "Syntax: repaint\nRedraws the screen\n",
865 []() throw(std::bad_alloc, std::runtime_error) {
866 redraw_framebuffer();
869 command::fnptr<> tpon(lsnes_cmd, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
870 []() throw(std::bad_alloc, std::runtime_error) {
871 bool tmp = pause_on_end;
872 pause_on_end.set(!tmp);
873 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
876 command::fnptr<> spon(lsnes_cmd, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
877 []() throw(std::bad_alloc, std::runtime_error) {
878 pause_on_end.set(true);
879 messages << "Pause-on-end is now ON" << std::endl;
882 command::fnptr<> cpon(lsnes_cmd, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
883 []() throw(std::bad_alloc, std::runtime_error) {
884 pause_on_end.set(false);
885 messages << "Pause-on-end is now OFF" << std::endl;
888 command::fnptr<> rewind_movie(lsnes_cmd, "rewind-movie", "Rewind movie to the beginning",
889 "Syntax: rewind-movie\nRewind movie to the beginning\n",
890 []() throw(std::bad_alloc, std::runtime_error) {
891 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
894 command::fnptr<> cancel_save(lsnes_cmd, "cancel-saves", "Cancel all pending saves", "Syntax: "
895 "cancel-save\nCancel pending saves\n",
896 []() throw(std::bad_alloc, std::runtime_error) {
897 queued_saves.clear();
898 messages << "Pending saves canceled." << std::endl;
901 command::fnptr<> flushslots(lsnes_cmd, "flush-slotinfo", "Flush slotinfo cache",
902 "Flush slotinfo cache\n",
903 []() throw(std::bad_alloc, std::runtime_error) {
904 flush_slotinfo();
907 command::fnptr<> mhold1(lsnes_cmd, "+hold-macro", "Hold macro (hold)",
908 "Hold macros enable\n", []() throw(std::bad_alloc, std::runtime_error) {
909 macro_hold_1 = true;
912 command::fnptr<> mhold2(lsnes_cmd, "-hold-macro", "Hold macro (hold)",
913 "Hold macros disable\n", []() throw(std::bad_alloc, std::runtime_error) {
914 macro_hold_1 = false;
917 command::fnptr<> mhold3(lsnes_cmd, "hold-macro", "Hold macro (toggle)",
918 "Hold macros toggle\n", []() throw(std::bad_alloc, std::runtime_error) {
919 macro_hold_2 = !macro_hold_2;
920 if(macro_hold_2)
921 messages << "Macros are held for next frame." << std::endl;
922 else
923 messages << "Macros are not held for next frame." << std::endl;
926 keyboard::invbind imhold1(lsnes_mapper, "+hold-macro", "Macro‣Hold all macros");
927 keyboard::invbind imhold2(lsnes_mapper, "hold-macro", "Macro‣Hold all macros (typed)");
928 keyboard::invbind ipause_emulator(lsnes_mapper, "pause-emulator", "Speed‣(Un)pause");
929 keyboard::invbind ijback(lsnes_mapper, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
930 keyboard::invbind ijforward(lsnes_mapper, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
931 keyboard::invbind iloadj(lsnes_mapper, "load-jukebox", "Load‣Selected slot");
932 keyboard::invbind iloadjrw(lsnes_mapper, "load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
933 keyboard::invbind iloadjro(lsnes_mapper, "load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
934 keyboard::invbind iloadjp(lsnes_mapper, "load-jukebox-preserve", "Load‣Selected slot (preserve input)");
935 keyboard::invbind iloadjm(lsnes_mapper, "load-jukebox-movie", "Load‣Selected slot (as movie)");
936 keyboard::invbind isavej(lsnes_mapper, "save-jukebox", "Save‣Selected slot");
937 keyboard::invbind iadvframe(lsnes_mapper, "+advance-frame", "Speed‣Advance frame");
938 keyboard::invbind iadvsubframe(lsnes_mapper, "+advance-poll", "Speed‣Advance subframe");
939 keyboard::invbind iskiplag(lsnes_mapper, "advance-skiplag", "Speed‣Advance poll");
940 keyboard::invbind ireset(lsnes_mapper, "reset", "System‣Reset");
941 keyboard::invbind iset_rwmode(lsnes_mapper, "set-rwmode", "Movie‣Switch to read/write");
942 keyboard::invbind itoggle_romode(lsnes_mapper, "set-romode", "Movie‣Switch to read-only");
943 keyboard::invbind itoggle_rwmode(lsnes_mapper, "toggle-rwmode", "Movie‣Toggle read-only");
944 keyboard::invbind irepaint(lsnes_mapper, "repaint", "System‣Repaint screen");
945 keyboard::invbind itogglepause(lsnes_mapper, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
946 keyboard::invbind irewind_movie(lsnes_mapper, "rewind-movie", "Movie‣Rewind movie");
947 keyboard::invbind icancel_saves(lsnes_mapper, "cancel-saves", "Save‣Cancel pending saves");
948 keyboard::invbind iload1(lsnes_mapper, "load $SLOT:1", "Load‣Slot 1");
949 keyboard::invbind iload2(lsnes_mapper, "load $SLOT:2", "Load‣Slot 2");
950 keyboard::invbind iload3(lsnes_mapper, "load $SLOT:3", "Load‣Slot 3");
951 keyboard::invbind iload4(lsnes_mapper, "load $SLOT:4", "Load‣Slot 4");
952 keyboard::invbind iload5(lsnes_mapper, "load $SLOT:5", "Load‣Slot 5");
953 keyboard::invbind iload6(lsnes_mapper, "load $SLOT:6", "Load‣Slot 6");
954 keyboard::invbind iload7(lsnes_mapper, "load $SLOT:7", "Load‣Slot 7");
955 keyboard::invbind iload8(lsnes_mapper, "load $SLOT:8", "Load‣Slot 8");
956 keyboard::invbind iload9(lsnes_mapper, "load $SLOT:9", "Load‣Slot 9");
957 keyboard::invbind iload10(lsnes_mapper, "load $SLOT:10", "Load‣Slot 10");
958 keyboard::invbind iload11(lsnes_mapper, "load $SLOT:11", "Load‣Slot 11");
959 keyboard::invbind iload12(lsnes_mapper, "load $SLOT:12", "Load‣Slot 12");
960 keyboard::invbind iload13(lsnes_mapper, "load $SLOT:13", "Load‣Slot 13");
961 keyboard::invbind iload14(lsnes_mapper, "load $SLOT:14", "Load‣Slot 14");
962 keyboard::invbind iload15(lsnes_mapper, "load $SLOT:15", "Load‣Slot 15");
963 keyboard::invbind iload16(lsnes_mapper, "load $SLOT:16", "Load‣Slot 16");
964 keyboard::invbind iload17(lsnes_mapper, "load $SLOT:17", "Load‣Slot 17");
965 keyboard::invbind iload18(lsnes_mapper, "load $SLOT:18", "Load‣Slot 18");
966 keyboard::invbind iload19(lsnes_mapper, "load $SLOT:19", "Load‣Slot 19");
967 keyboard::invbind iload20(lsnes_mapper, "load $SLOT:20", "Load‣Slot 20");
968 keyboard::invbind iload21(lsnes_mapper, "load $SLOT:21", "Load‣Slot 21");
969 keyboard::invbind iload22(lsnes_mapper, "load $SLOT:22", "Load‣Slot 22");
970 keyboard::invbind iload23(lsnes_mapper, "load $SLOT:23", "Load‣Slot 23");
971 keyboard::invbind iload24(lsnes_mapper, "load $SLOT:24", "Load‣Slot 24");
972 keyboard::invbind iload25(lsnes_mapper, "load $SLOT:25", "Load‣Slot 25");
973 keyboard::invbind iload26(lsnes_mapper, "load $SLOT:26", "Load‣Slot 26");
974 keyboard::invbind iload27(lsnes_mapper, "load $SLOT:27", "Load‣Slot 27");
975 keyboard::invbind iload28(lsnes_mapper, "load $SLOT:28", "Load‣Slot 28");
976 keyboard::invbind iload29(lsnes_mapper, "load $SLOT:29", "Load‣Slot 29");
977 keyboard::invbind iload30(lsnes_mapper, "load $SLOT:30", "Load‣Slot 30");
978 keyboard::invbind iload31(lsnes_mapper, "load $SLOT:31", "Load‣Slot 31");
979 keyboard::invbind iload32(lsnes_mapper, "load $SLOT:32", "Load‣Slot 32");
980 keyboard::invbind isave1(lsnes_mapper, "save-state $SLOT:1", "Save‣Slot 1");
981 keyboard::invbind isave2(lsnes_mapper, "save-state $SLOT:2", "Save‣Slot 2");
982 keyboard::invbind isave3(lsnes_mapper, "save-state $SLOT:3", "Save‣Slot 3");
983 keyboard::invbind isave4(lsnes_mapper, "save-state $SLOT:4", "Save‣Slot 4");
984 keyboard::invbind isave5(lsnes_mapper, "save-state $SLOT:5", "Save‣Slot 5");
985 keyboard::invbind isave6(lsnes_mapper, "save-state $SLOT:6", "Save‣Slot 6");
986 keyboard::invbind isave7(lsnes_mapper, "save-state $SLOT:7", "Save‣Slot 7");
987 keyboard::invbind isave8(lsnes_mapper, "save-state $SLOT:8", "Save‣Slot 8");
988 keyboard::invbind isave9(lsnes_mapper, "save-state $SLOT:9", "Save‣Slot 9");
989 keyboard::invbind isave10(lsnes_mapper, "save-state $SLOT:10", "Save‣Slot 10");
990 keyboard::invbind isave11(lsnes_mapper, "save-state $SLOT:11", "Save‣Slot 11");
991 keyboard::invbind isave12(lsnes_mapper, "save-state $SLOT:12", "Save‣Slot 12");
992 keyboard::invbind isave13(lsnes_mapper, "save-state $SLOT:13", "Save‣Slot 13");
993 keyboard::invbind isave14(lsnes_mapper, "save-state $SLOT:14", "Save‣Slot 14");
994 keyboard::invbind isave15(lsnes_mapper, "save-state $SLOT:15", "Save‣Slot 15");
995 keyboard::invbind isave16(lsnes_mapper, "save-state $SLOT:16", "Save‣Slot 16");
996 keyboard::invbind isave17(lsnes_mapper, "save-state $SLOT:17", "Save‣Slot 17");
997 keyboard::invbind isave18(lsnes_mapper, "save-state $SLOT:18", "Save‣Slot 18");
998 keyboard::invbind isave19(lsnes_mapper, "save-state $SLOT:19", "Save‣Slot 19");
999 keyboard::invbind isave20(lsnes_mapper, "save-state $SLOT:20", "Save‣Slot 20");
1000 keyboard::invbind isave21(lsnes_mapper, "save-state $SLOT:21", "Save‣Slot 21");
1001 keyboard::invbind isave22(lsnes_mapper, "save-state $SLOT:22", "Save‣Slot 22");
1002 keyboard::invbind isave23(lsnes_mapper, "save-state $SLOT:23", "Save‣Slot 23");
1003 keyboard::invbind isave24(lsnes_mapper, "save-state $SLOT:24", "Save‣Slot 24");
1004 keyboard::invbind isave25(lsnes_mapper, "save-state $SLOT:25", "Save‣Slot 25");
1005 keyboard::invbind isave26(lsnes_mapper, "save-state $SLOT:26", "Save‣Slot 26");
1006 keyboard::invbind isave27(lsnes_mapper, "save-state $SLOT:27", "Save‣Slot 27");
1007 keyboard::invbind isave28(lsnes_mapper, "save-state $SLOT:28", "Save‣Slot 28");
1008 keyboard::invbind isave29(lsnes_mapper, "save-state $SLOT:29", "Save‣Slot 29");
1009 keyboard::invbind isave30(lsnes_mapper, "save-state $SLOT:30", "Save‣Slot 30");
1010 keyboard::invbind isave31(lsnes_mapper, "save-state $SLOT:31", "Save‣Slot 31");
1011 keyboard::invbind isave32(lsnes_mapper, "save-state $SLOT:32", "Save‣Slot 32");
1013 bool on_quit_prompt = false;
1014 class mywindowcallbacks : public information_dispatch
1016 public:
1017 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks")
1019 closenotify.set(notify_close, [this]() {
1020 if(on_quit_prompt) {
1021 amode = ADVANCE_QUIT;
1022 platform::set_paused(false);
1023 platform::cancel_wait();
1024 return;
1026 on_quit_prompt = true;
1027 try {
1028 amode = ADVANCE_QUIT;
1029 platform::set_paused(false);
1030 platform::cancel_wait();
1031 } catch(...) {
1033 on_quit_prompt = false;
1036 ~mywindowcallbacks() throw() {}
1037 void on_new_dumper(const std::string& n)
1039 update_movie_state();
1041 void on_destroy_dumper(const std::string& n)
1043 update_movie_state();
1045 private:
1046 struct dispatch::target<> closenotify;
1047 } mywcb;
1049 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
1050 //failing.
1051 int handle_load()
1053 std::string old_project = movb ? movb.get_mfile().projectid : "";
1054 jumpback:
1055 if(do_unsafe_rewind && unsafe_rewind_obj) {
1056 if(!movb)
1057 return 0;
1058 uint64_t t = get_utime();
1059 std::vector<char> s;
1060 lua_callback_do_unsafe_rewind(s, 0, 0, movb.get_movie(), unsafe_rewind_obj);
1061 notify_mode_change(false);
1062 do_unsafe_rewind = false;
1063 movb.get_mfile().is_savestate = true;
1064 location_special = SPECIAL_SAVEPOINT;
1065 update_movie_state();
1066 messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
1067 return 1;
1069 if(pending_new_project != "") {
1070 std::string id = pending_new_project;
1071 pending_new_project = "";
1072 project_info* old = project_get();
1073 if(old && old->id == id)
1074 goto nothing_to_do;
1075 try {
1076 auto& p = project_load(id);
1077 project_set(&p);
1078 if(project_get() != old)
1079 delete old;
1080 flush_slotinfo(); //Wrong movie may be stale.
1081 return 1;
1082 } catch(std::exception& e) {
1083 platform::error_message(std::string("Can't switch projects: ") + e.what());
1084 messages << "Can't switch projects: " << e.what() << std::endl;
1085 goto nothing_to_do;
1087 nothing_to_do:
1088 amode = old_mode;
1089 platform::set_paused(amode == ADVANCE_PAUSE);
1090 platform::flush_command_queue();
1091 if(amode == ADVANCE_LOAD)
1092 goto jumpback;
1093 return 0;
1095 if(pending_load != "") {
1096 bool system_was_corrupt = system_corrupt;
1097 system_corrupt = false;
1098 try {
1099 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
1100 !do_load_state(pending_load, loadmode)) {
1101 if(system_was_corrupt)
1102 system_corrupt = system_was_corrupt;
1103 pending_load = "";
1104 return -1;
1106 if(loadmode == LOAD_STATE_BEGINNING)
1107 do_load_rewind();
1108 if(loadmode == LOAD_STATE_ROMRELOAD)
1109 do_load_rom();
1110 } catch(std::exception& e) {
1111 if(!system_corrupt && system_was_corrupt)
1112 system_corrupt = true;
1113 platform::error_message(std::string("Load failed: ") + e.what());
1114 messages << "Load failed: " << e.what() << std::endl;
1116 pending_load = "";
1117 amode = load_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1118 platform::set_paused(load_paused);
1119 load_paused = false;
1120 if(!system_corrupt) {
1121 location_special = SPECIAL_SAVEPOINT;
1122 update_movie_state();
1123 platform::flush_command_queue();
1124 if(amode == ADVANCE_QUIT)
1125 return -1;
1126 if(amode == ADVANCE_LOAD)
1127 goto jumpback;
1129 if(old_project != (movb ? movb.get_mfile().projectid : ""))
1130 flush_slotinfo(); //Wrong movie may be stale.
1131 return 1;
1133 return 0;
1136 //If there are pending saves, perform them.
1137 void handle_saves()
1139 if(!movb)
1140 return;
1141 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
1142 our_rom.rtype->runtosave();
1143 for(auto i : queued_saves) {
1144 do_save_state(i.first, i.second);
1145 int tmp = -1;
1146 flush_slotinfo(translate_name_mprefix(i.first, tmp, -1));
1148 if(do_unsafe_rewind && !unsafe_rewind_obj) {
1149 uint64_t t = get_utime();
1150 std::vector<char> s = our_rom.save_core_state(true);
1151 uint64_t secs = movb.get_mfile().rtc_second;
1152 uint64_t ssecs = movb.get_mfile().rtc_subsecond;
1153 lua_callback_do_unsafe_rewind(s, secs, ssecs, movb.get_movie(), NULL);
1154 do_unsafe_rewind = false;
1155 messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
1158 queued_saves.clear();
1161 bool handle_corrupt()
1163 if(!system_corrupt)
1164 return false;
1165 while(system_corrupt) {
1166 platform::set_paused(true);
1167 platform::flush_command_queue();
1168 handle_load();
1169 if(amode == ADVANCE_QUIT)
1170 return true;
1172 return true;
1176 void init_main_callbacks()
1178 ecore_callbacks = &lsnes_callbacks_obj;
1181 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
1182 std::runtime_error)
1184 platform::system_thread_available(true);
1185 //Basic initialization.
1186 dispatch_set_error_streams(&messages.getstream());
1187 emulation_thread = threads::this_id();
1188 jukebox_size_listener jlistener;
1189 voicethread_task();
1190 init_special_screens();
1191 our_rom = rom;
1192 init_main_callbacks();
1193 initialize_all_builtin_c_cores();
1194 core_core::install_all_handlers();
1196 //Load our given movie.
1197 bool first_round = false;
1198 bool just_did_loadstate = false;
1199 bool used = false;
1200 try {
1201 do_load_state(initial, LOAD_STATE_INITIAL, used);
1202 location_special = SPECIAL_SAVEPOINT;
1203 update_movie_state();
1204 first_round = movb.get_mfile().is_savestate;
1205 just_did_loadstate = first_round;
1206 } catch(std::bad_alloc& e) {
1207 OOM_panic();
1208 } catch(std::exception& e) {
1209 if(!used)
1210 delete &initial;
1211 platform::error_message(std::string("Can't load initial state: ") + e.what());
1212 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
1213 if(load_has_to_succeed) {
1214 messages << "FATAL: Can't load movie" << std::endl;
1215 throw;
1217 system_corrupt = true;
1218 redraw_framebuffer(screen_corrupt);
1221 platform::set_paused(initial.start_paused);
1222 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1223 stop_at_frame_active = false;
1225 lua_run_startup_scripts();
1227 uint64_t time_x = get_utime();
1228 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
1229 if(handle_corrupt()) {
1230 first_round = movb && movb.get_mfile().is_savestate;
1231 just_did_loadstate = first_round;
1232 continue;
1234 ack_frame_tick(get_utime());
1235 if(amode == ADVANCE_SKIPLAG_PENDING)
1236 amode = ADVANCE_SKIPLAG;
1238 if(!first_round) {
1239 controls.reset_framehold();
1240 movb.get_movie().get_pollcounters().set_framepflag(false);
1241 movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
1242 movb.get_movie().get_pollcounters().set_framepflag(true);
1243 if(!macro_hold_1 && !macro_hold_2) {
1244 controls.advance_macros();
1246 macro_hold_2 = false;
1247 if(amode == ADVANCE_QUIT && queued_saves.empty())
1248 break;
1249 handle_saves();
1250 int r = 0;
1251 if(queued_saves.empty())
1252 r = handle_load();
1253 if(r > 0 || system_corrupt) {
1254 movb.get_movie().get_pollcounters().set_framepflag(movb.get_mfile().is_savestate);
1255 first_round = movb.get_mfile().is_savestate;
1256 if(system_corrupt)
1257 amode = ADVANCE_PAUSE;
1258 else
1259 amode = old_mode;
1260 stop_at_frame_active = false;
1261 just_did_loadstate = first_round;
1262 controls.reset_framehold();
1263 continue;
1264 } else if(r < 0) {
1265 //Not exactly desriable, but this at least won't desync.
1266 stop_at_frame_active = false;
1267 if(amode == ADVANCE_QUIT)
1268 return;
1269 amode = ADVANCE_PAUSE;
1272 if(just_did_loadstate) {
1273 //If we just loadstated, we are up to date.
1274 if(amode == ADVANCE_QUIT)
1275 break;
1276 platform::set_paused(amode == ADVANCE_PAUSE);
1277 platform::flush_command_queue();
1278 //We already have done the reset this frame if we are going to do one at all.
1279 movb.get_movie().set_controls(movb.update_controls(true));
1280 movb.get_movie().set_all_DRDY();
1281 just_did_loadstate = false;
1283 frame_irq_time = get_utime() - time_x;
1284 our_rom.rtype->emulate();
1285 random_mix_timing_entropy();
1286 time_x = get_utime();
1287 if(amode == ADVANCE_AUTO)
1288 platform::wait(to_wait_frame(get_utime()));
1289 first_round = false;
1290 lua_callback_do_frame();
1292 information_dispatch::do_dump_end();
1293 core_core::uninstall_all_handlers();
1294 voicethread_kill();
1295 platform::system_thread_available(false);
1296 //Kill some things to avoid crashes.
1297 debug_core_change();
1298 project_set(NULL, true);
1299 lsnes_memorywatch.clear_multi(lsnes_memorywatch.enumerate());
1302 void set_stop_at_frame(uint64_t frame)
1304 stop_at_frame = frame;
1305 stop_at_frame_active = (frame != 0);
1306 amode = ADVANCE_AUTO;
1307 platform::set_paused(false);
1310 void do_flush_slotinfo()
1312 flush_slotinfo();
1315 void switch_projects(const std::string& newproj)
1317 pending_new_project = newproj;
1318 amode = ADVANCE_LOAD;
1319 old_mode = ADVANCE_PAUSE;
1320 platform::cancel_wait();
1321 platform::set_paused(false);
1324 void load_new_rom(const romload_request& req)
1326 if(_load_new_rom(req)) {
1327 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1331 void reload_current_rom()
1333 if(reload_active_rom()) {
1334 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1338 void close_rom()
1340 if(load_null_rom()) {
1341 load_paused = true;
1342 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1346 void do_break_pause()
1348 break_pause = true;
1349 update_movie_state();
1350 while(break_pause) {
1351 platform::set_paused(true);
1352 platform::flush_command_queue();
1356 void convert_break_to_pause()
1358 if(break_pause) {
1359 amode = ADVANCE_PAUSE;
1360 break_pause = false;
1361 update_movie_state();