Make various instance stuff to take references to other instance objs
[lsnes.git] / src / core / mainloop.cpp
blob20fcdd95f779faac1cd8b0b3c59ee283a29f8c73
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/emustatus.hpp"
9 #include "core/framebuffer.hpp"
10 #include "core/framerate.hpp"
11 #include "core/inthread.hpp"
12 #include "core/keymapper.hpp"
13 #include "core/multitrack.hpp"
14 #include "lua/lua.hpp"
15 #include "library/string.hpp"
16 #include "core/mainloop.hpp"
17 #include "core/instance.hpp"
18 #include "core/moviedata.hpp"
19 #include "core/moviefile.hpp"
20 #include "core/memorymanip.hpp"
21 #include "core/memorywatch.hpp"
22 #include "core/project.hpp"
23 #include "core/random.hpp"
24 #include "core/rom.hpp"
25 #include "core/romloader.hpp"
26 #include "core/settings.hpp"
27 #include "core/window.hpp"
28 #include "interface/c-interface.hpp"
29 #include "interface/callbacks.hpp"
30 #include "interface/romtype.hpp"
31 #include "library/framebuffer.hpp"
32 #include "library/zip.hpp"
34 #include <iomanip>
35 #include <cassert>
36 #include <sstream>
37 #include <iostream>
38 #include <limits>
39 #include <set>
40 #include <sys/time.h>
42 #define QUIT_MAGIC 0x5a8c4bef
44 #define SPECIAL_FRAME_START 0
45 #define SPECIAL_FRAME_VIDEO 1
46 #define SPECIAL_SAVEPOINT 2
47 #define SPECIAL_NONE 3
49 void update_movie_state();
50 time_t random_seed_value = 0;
52 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary(lsnes_setgrp,
53 "jukebox-default-binary", "Movie‣Saving‣Saveslots binary", true);
54 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary(lsnes_setgrp,
55 "movie-default-binary", "Movie‣Saving‣Movies binary", false);
56 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> save_dflt_binary(lsnes_setgrp,
57 "savestate-default-binary", "Movie‣Saving‣Savestates binary", false);
59 namespace
61 settingvar::supervariable<settingvar::model_int<0,999999>> advance_timeout_first(lsnes_setgrp,
62 "advance-timeout", "Delays‣First frame advance", 500);
63 settingvar::supervariable<settingvar::model_int<0,999999>> advance_timeout_subframe(lsnes_setgrp,
64 "advance-subframe-timeout", "Delays‣Subframe advance", 100);
65 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> pause_on_end(lsnes_setgrp,
66 "pause-on-end", "Movie‣Pause on end", false);
67 settingvar::supervariable<settingvar::model_int<0,999999999>> jukebox_size(lsnes_setgrp, "jukebox-size",
68 "Movie‣Number of save slots", 12);
70 enum advance_mode
72 ADVANCE_INVALID, //In case someone trashes this.
73 ADVANCE_QUIT, //Quit the emulator.
74 ADVANCE_AUTO, //Normal (possibly slowed down play).
75 ADVANCE_LOAD, //Loading a state.
76 ADVANCE_FRAME, //Frame advance.
77 ADVANCE_SUBFRAME, //Subframe advance.
78 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
79 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
80 ADVANCE_PAUSE, //Unconditional pause.
81 ADVANCE_BREAK_PAUSE, //Break pause.
84 //Our thread.
85 threads::id emulation_thread;
86 //Flags related to repeating advance.
87 bool advanced_once;
88 bool cancel_advance;
89 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
90 enum advance_mode amode;
91 enum advance_mode old_mode;
92 //Mode and filename of pending load, one of LOAD_* constants.
93 bool load_paused;
94 int loadmode;
95 std::string pending_load;
96 std::string pending_new_project;
97 //Queued saves (all savestates).
98 std::set<std::pair<std::string, int>> queued_saves;
99 //Save jukebox.
100 size_t save_jukebox_pointer;
101 //Special subframe location. One of SPECIAL_* constants.
102 int location_special;
103 //Unsafe rewind.
104 bool do_unsafe_rewind = false;
105 void* unsafe_rewind_obj = NULL;
106 //Stop at frame.
107 bool stop_at_frame_active = false;
108 uint64_t stop_at_frame = 0;
109 //Macro hold.
110 bool macro_hold_1;
111 bool macro_hold_2;
112 //Quit magic.
113 unsigned quit_magic;
115 bool is_quitting()
117 if(amode == ADVANCE_QUIT && quit_magic == QUIT_MAGIC)
118 return true;
119 if(amode == ADVANCE_INVALID || (amode == ADVANCE_QUIT && quit_magic != QUIT_MAGIC) ||
120 amode > ADVANCE_BREAK_PAUSE) {
121 //Ouch.
122 if(lsnes_instance.mlogic)
123 emerg_save_movie(lsnes_instance.mlogic.get_mfile(),
124 lsnes_instance.mlogic.get_rrdata());
125 messages << "WARNING: Emulator runmode undefined, invoked movie dump." << std::endl;
126 amode = ADVANCE_PAUSE;
128 return false;
131 std::string save_jukebox_name(size_t i)
133 return (stringfmt() << "$SLOT:" << (i + 1)).str();
136 std::map<std::string, std::string> slotinfo_cache;
138 std::string vector_to_string(const std::vector<char>& x)
140 std::string y(x.begin(), x.end());
141 while(y.length() > 0 && y[y.length() - 1] < 32)
142 y = y.substr(0, y.length() - 1);
143 return y;
146 std::string get_slotinfo(const std::string& _filename)
148 std::string filename = resolve_relative_path(_filename);
149 if(!slotinfo_cache.count(filename)) {
150 std::ostringstream out;
151 try {
152 moviefile::brief_info info(filename);
153 if(!CORE().mlogic)
154 out << "No movie";
155 else if(CORE().mlogic.get_mfile().projectid == info.projectid)
156 out << info.rerecords << "R/" << info.current_frame << "F";
157 else
158 out << "Wrong movie";
159 } catch(...) {
160 out << "Nonexistent";
162 slotinfo_cache[filename] = out.str();
164 return slotinfo_cache[filename];
167 void flush_slotinfo(const std::string& filename)
169 slotinfo_cache.erase(resolve_relative_path(filename));
172 void flush_slotinfo()
174 slotinfo_cache.clear();
178 void mainloop_signal_need_rewind(void* ptr)
180 if(ptr) {
181 old_mode = amode;
182 amode = ADVANCE_LOAD;
184 do_unsafe_rewind = true;
185 unsafe_rewind_obj = ptr;
188 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
190 if(lua_requests_subframe_paint)
191 CORE().fbuf.redraw_framebuffer();
193 if(subframe) {
194 if(amode == ADVANCE_SUBFRAME) {
195 if(!cancel_advance) {
196 if(!advanced_once)
197 platform::wait(advance_timeout_first(CORE().settings) * 1000);
198 else
199 platform::wait(advance_timeout_subframe(CORE().settings) * 1000);
200 advanced_once = true;
202 if(cancel_advance) {
203 stop_at_frame_active = false;
204 amode = ADVANCE_PAUSE;
205 cancel_advance = false;
207 platform::set_paused(amode == ADVANCE_PAUSE);
208 } else if(amode == ADVANCE_FRAME) {
210 } else {
211 if(amode == ADVANCE_SKIPLAG) {
212 stop_at_frame_active = false;
213 amode = ADVANCE_PAUSE;
215 platform::set_paused(amode == ADVANCE_PAUSE);
216 cancel_advance = false;
218 location_special = SPECIAL_NONE;
219 update_movie_state();
220 } else {
221 if(amode == ADVANCE_SKIPLAG_PENDING)
222 amode = ADVANCE_SKIPLAG;
223 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
224 if(!cancel_advance) {
225 uint64_t wait = 0;
226 if(!advanced_once)
227 wait = advance_timeout_first(CORE().settings) * 1000;
228 else if(amode == ADVANCE_SUBFRAME)
229 wait = advance_timeout_subframe(CORE().settings) * 1000;
230 else
231 wait = CORE().framerate.to_wait_frame(framerate_regulator::get_utime());
232 platform::wait(wait);
233 advanced_once = true;
235 if(cancel_advance) {
236 stop_at_frame_active = false;
237 amode = ADVANCE_PAUSE;
238 cancel_advance = false;
240 platform::set_paused(amode == ADVANCE_PAUSE);
241 } else if(amode == ADVANCE_AUTO && CORE().mlogic.get_movie().readonly_mode() &&
242 pause_on_end(CORE().settings) && !stop_at_frame_active) {
243 if(CORE().mlogic.get_movie().get_current_frame() ==
244 CORE().mlogic.get_movie().get_frame_count()) {
245 stop_at_frame_active = false;
246 amode = ADVANCE_PAUSE;
247 platform::set_paused(true);
249 } else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
250 if(CORE().mlogic.get_movie().get_current_frame() >= stop_at_frame) {
251 stop_at_frame_active = false;
252 amode = ADVANCE_PAUSE;
253 platform::set_paused(true);
255 } else {
256 platform::set_paused((amode == ADVANCE_PAUSE));
257 cancel_advance = false;
259 location_special = SPECIAL_FRAME_START;
260 update_movie_state();
262 platform::flush_command_queue();
263 controller_frame tmp = CORE().controls.get(CORE().mlogic.get_movie().get_current_frame());
264 our_rom.rtype->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
265 lua_callback_do_input(tmp, subframe);
266 CORE().mteditor.process_frame(tmp);
267 CORE().controls.commit(tmp);
268 return tmp;
271 namespace
274 //Do pending load (automatically unpauses).
275 void mark_pending_load(std::string filename, int lmode)
277 //Convert break pause to ordinary pause.
278 if(amode == ADVANCE_BREAK_PAUSE)
279 amode = ADVANCE_PAUSE;
280 loadmode = lmode;
281 pending_load = filename;
282 old_mode = amode;
283 amode = ADVANCE_LOAD;
284 platform::cancel_wait();
285 platform::set_paused(false);
288 void mark_pending_save(std::string filename, int smode, int binary)
290 int tmp = -1;
291 if(smode == SAVE_MOVIE) {
292 //Just do this immediately.
293 do_save_movie(filename, binary);
294 flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
295 return;
297 if(location_special == SPECIAL_SAVEPOINT) {
298 //We can save immediately here.
299 do_save_state(filename, binary);
300 flush_slotinfo(translate_name_mprefix(filename, tmp, -1));
301 return;
303 queued_saves.insert(std::make_pair(filename, binary));
304 messages << "Pending save on '" << filename << "'" << std::endl;
307 struct jukebox_size_listener : public settingvar::listener
309 jukebox_size_listener(settingvar::group& _grp) : grp(_grp) { grp.add_listener(*this); }
310 ~jukebox_size_listener() throw() { grp.remove_listener(*this); };
311 void on_setting_change(settingvar::group& _grp, const settingvar::base& val)
313 if(val.get_iname() == "jukebox-size") {
314 if(save_jukebox_pointer >= (size_t)jukebox_size(_grp))
315 save_jukebox_pointer = 0;
317 update_movie_state();
319 private:
320 settingvar::group& grp;
324 void update_movie_state()
326 auto p = CORE().project.get();
327 bool readonly = false;
329 uint64_t magic[4];
330 our_rom.region->fill_framerate_magic(magic);
331 if(CORE().mlogic)
332 CORE().commentary.frame_number(CORE().mlogic.get_movie().get_current_frame(),
333 1.0 * magic[1] / magic[0]);
334 else
335 CORE().commentary.frame_number(0, 60.0); //Default.
337 auto& _status = CORE().status.get_write();
338 try {
339 if(CORE().mlogic && !system_corrupt) {
340 _status.movie_valid = true;
341 _status.curframe = CORE().mlogic.get_movie().get_current_frame();
342 _status.length = CORE().mlogic.get_movie().get_frame_count();
343 _status.lag = CORE().mlogic.get_movie().get_lag_frames();
344 if(location_special == SPECIAL_FRAME_START)
345 _status.subframe = 0;
346 else if(location_special == SPECIAL_SAVEPOINT)
347 _status.subframe = _lsnes_status::subframe_savepoint;
348 else if(location_special == SPECIAL_FRAME_VIDEO)
349 _status.subframe = _lsnes_status::subframe_video;
350 else
351 _status.subframe = CORE().mlogic.get_movie().next_poll_number();
352 } else {
353 _status.movie_valid = false;
354 _status.curframe = 0;
355 _status.length = 0;
356 _status.lag = 0;
357 _status.subframe = 0;
359 _status.dumping = (information_dispatch::get_dumper_count() > 0);
360 if(amode == ADVANCE_BREAK_PAUSE)
361 _status.pause = _lsnes_status::pause_break;
362 else if(amode == ADVANCE_PAUSE)
363 _status.pause = _lsnes_status::pause_normal;
364 else
365 _status.pause = _lsnes_status::pause_none;
366 if(CORE().mlogic) {
367 auto& mo = CORE().mlogic.get_movie();
368 readonly = mo.readonly_mode();
369 if(system_corrupt)
370 _status.mode = 'C';
371 else if(!readonly)
372 _status.mode = 'R';
373 else if(mo.get_frame_count() >= mo.get_current_frame())
374 _status.mode = 'P';
375 else
376 _status.mode = 'F';
378 if(jukebox_size(CORE().settings) > 0) {
379 _status.saveslot_valid = true;
380 int tmp = -1;
381 std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, -1);
382 _status.saveslot = save_jukebox_pointer + 1;
383 _status.slotinfo = utf8::to32(get_slotinfo(sfilen));
384 } else {
385 _status.saveslot_valid = false;
387 _status.branch_valid = (p != NULL);
388 if(p) _status.branch = utf8::to32(p->get_branch_string());
390 std::string cur_branch = CORE().mlogic ? CORE().mlogic.get_mfile().current_branch() :
392 _status.mbranch_valid = (cur_branch != "");
393 _status.mbranch = utf8::to32(cur_branch);
395 _status.speed = (unsigned)(100 * CORE().framerate.get_realized_multiplier() + 0.5);
397 if(CORE().mlogic && !system_corrupt) {
398 time_t timevalue = static_cast<time_t>(CORE().mlogic.get_mfile().rtc_second);
399 struct tm* time_decompose = gmtime(&timevalue);
400 char datebuffer[512];
401 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
402 _status.rtc = utf8::to32(datebuffer);
403 _status.rtc_valid = true;
404 } else {
405 _status.rtc_valid = false;
408 auto mset = CORE().controls.active_macro_set();
409 bool mfirst = true;
410 std::ostringstream mss;
411 for(auto i: mset) {
412 if(!mfirst) mss << ",";
413 mss << i;
414 mfirst = false;
416 _status.macros = utf8::to32(mss.str());
418 controller_frame c;
419 if(!CORE().mteditor.any_records())
420 c = CORE().mlogic.get_movie().get_controls();
421 else
422 c = CORE().controls.get_committed();
423 _status.inputs.clear();
424 for(unsigned i = 0;; i++) {
425 auto pindex = CORE().controls.lcid_to_pcid(i);
426 if(pindex.first < 0 || !CORE().controls.is_present(pindex.first, pindex.second))
427 break;
428 char32_t buffer[MAX_DISPLAY_LENGTH];
429 c.display(pindex.first, pindex.second, buffer);
430 std::u32string _buffer = buffer;
431 if(readonly && CORE().mteditor.is_enabled()) {
432 multitrack_edit::state st = CORE().mteditor.get(pindex.first, pindex.second);
433 if(st == multitrack_edit::MT_PRESERVE)
434 _buffer += U" (keep)";
435 else if(st == multitrack_edit::MT_OVERWRITE)
436 _buffer += U" (rewrite)";
437 else if(st == multitrack_edit::MT_OR)
438 _buffer += U" (OR)";
439 else if(st == multitrack_edit::MT_XOR)
440 _buffer += U" (XOR)";
441 else
442 _buffer += U" (\?\?\?)";
444 _status.inputs.push_back(_buffer);
446 //Lua variables.
447 _status.lvars = get_lua_watch_vars();
448 //Memory watches.
449 _status.mvars = CORE().mwatch.get_window_vars();
451 _status.valid = true;
452 } catch(...) {
454 CORE().status.put_write();
455 notify_status_update();
458 uint64_t audio_irq_time;
459 uint64_t controller_irq_time;
460 uint64_t frame_irq_time;
463 struct lsnes_callbacks : public emucore_callbacks
465 public:
466 ~lsnes_callbacks() throw()
470 int16_t get_input(unsigned port, unsigned index, unsigned control)
472 int16_t x;
473 x = CORE().mlogic.input_poll(port, index, control);
474 lua_callback_snoop_input(port, index, control, x);
475 return x;
478 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
480 if(!CORE().mlogic.get_movie().readonly_mode()) {
481 controller_frame f = CORE().mlogic.get_movie().get_controls();
482 f.axis3(port, index, control, value);
483 CORE().mlogic.get_movie().set_controls(f);
485 return CORE().mlogic.get_movie().next_input(port, index, control);
488 void notify_latch(std::list<std::string>& args)
490 lua_callback_do_latch(args);
493 void timer_tick(uint32_t increment, uint32_t per_second)
495 if(!CORE().mlogic)
496 return;
497 auto& m = CORE().mlogic.get_mfile();
498 m.rtc_subsecond += increment;
499 while(m.rtc_subsecond >= per_second) {
500 m.rtc_second++;
501 m.rtc_subsecond -= per_second;
505 std::string get_firmware_path()
507 return CORE().setcache.get("firmwarepath");
510 std::string get_base_path()
512 return our_rom.msu1_base;
515 time_t get_time()
517 return CORE().mlogic ? CORE().mlogic.get_mfile().rtc_second : 0;
520 time_t get_randomseed()
522 return random_seed_value;
525 void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
527 lua_callback_do_frame_emulated();
528 location_special = SPECIAL_FRAME_VIDEO;
529 CORE().fbuf.redraw_framebuffer(screen, false, true);
530 uint32_t g = gcd(fps_n, fps_d);
531 fps_n /= g;
532 fps_d /= g;
533 information_dispatch::do_frame(screen, fps_n, fps_d);
536 void action_state_updated()
538 graphics_driver_action_updated();
541 void memory_read(uint64_t addr, uint64_t value)
543 CORE().dbg.do_callback_read(addr, value);
546 void memory_write(uint64_t addr, uint64_t value)
548 CORE().dbg.do_callback_write(addr, value);
551 void memory_execute(uint64_t addr, uint64_t proc)
553 CORE().dbg.do_callback_exec(addr, proc);
556 void memory_trace(uint64_t proc, const char* str, bool insn)
558 CORE().dbg.do_callback_trace(proc, str, insn);
562 namespace
564 lsnes_callbacks lsnes_callbacks_obj;
565 command::fnptr<> segfault(lsnes_cmds, "segfault", "Trigger SIGSEGV", "segfault\nTrigger segmentation fault",
566 []() throw(std::bad_alloc, std::runtime_error) {
567 char* ptr = (char*)0x1234;
568 *ptr = 0;
571 command::fnptr<> div0(lsnes_cmds, "divide-by-0", "Do div0", "divide-by-0\nDo divide by 0",
572 []() throw(std::bad_alloc, std::runtime_error) {
573 static int ptr = 1;
574 static int ptr2 = 0;
575 ptr = ptr / ptr2;
578 command::fnptr<const std::string&> test4(lsnes_cmds, "test4", "test", "test",
579 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
580 std::list<std::string> _args;
581 std::string args2 = args;
582 for(auto& sym : token_iterator_foreach(args, {" ", "\t"}))
583 _args.push_back(sym);
584 lua_callback_do_latch(_args);
586 command::fnptr<> count_rerecords(lsnes_cmds, "count-rerecords", "Count rerecords",
587 "Syntax: count-rerecords\nCounts rerecords.\n",
588 []() throw(std::bad_alloc, std::runtime_error) {
589 std::vector<char> tmp;
590 uint64_t x = CORE().mlogic.get_rrdata().write(tmp);
591 messages << x << " rerecord(s)" << std::endl;
594 command::fnptr<const std::string&> quit_emulator(lsnes_cmds, "quit-emulator", "Quit the emulator",
595 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
596 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
597 amode = ADVANCE_QUIT;
598 quit_magic = QUIT_MAGIC;
599 platform::set_paused(false);
600 platform::cancel_wait();
603 command::fnptr<> unpause_emulator(lsnes_cmds, "unpause-emulator", "Unpause the emulator",
604 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
605 []() throw(std::bad_alloc, std::runtime_error) {
606 amode = ADVANCE_AUTO;
607 platform::set_paused(false);
608 platform::cancel_wait();
611 command::fnptr<> pause_emulator(lsnes_cmds, "pause-emulator", "(Un)pause the emulator",
612 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
613 []() throw(std::bad_alloc, std::runtime_error) {
614 if(amode != ADVANCE_AUTO) {
615 amode = ADVANCE_AUTO;
616 platform::set_paused(false);
617 platform::cancel_wait();
618 } else {
619 platform::cancel_wait();
620 cancel_advance = false;
621 stop_at_frame_active = false;
622 amode = ADVANCE_PAUSE;
626 command::fnptr<> save_jukebox_prev(lsnes_cmds, "cycle-jukebox-backward", "Cycle save jukebox backwards",
627 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
628 []() throw(std::bad_alloc, std::runtime_error) {
629 size_t jbsize = jukebox_size(CORE().settings);
630 if(jbsize == 0)
631 return;
632 if(save_jukebox_pointer == 0)
633 save_jukebox_pointer = jbsize - 1;
634 else
635 save_jukebox_pointer--;
636 if(save_jukebox_pointer >= (size_t)jbsize)
637 save_jukebox_pointer = 0;
638 update_movie_state();
641 command::fnptr<> save_jukebox_next(lsnes_cmds, "cycle-jukebox-forward", "Cycle save jukebox forwards",
642 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
643 []() throw(std::bad_alloc, std::runtime_error) {
644 size_t jbsize = jukebox_size(CORE().settings);
645 if(jbsize == 0)
646 return;
647 if(save_jukebox_pointer + 1 >= (size_t)jbsize)
648 save_jukebox_pointer = 0;
649 else
650 save_jukebox_pointer++;
651 if(save_jukebox_pointer >= (size_t)jbsize)
652 save_jukebox_pointer = 0;
653 update_movie_state();
656 command::fnptr<const std::string&> save_jukebox_set(lsnes_cmds, "set-jukebox-slot", "Set jukebox slot",
657 "Syntax: set-jukebox-slot\nSet jukebox slot\n", [](const std::string& args)
658 throw(std::bad_alloc, std::runtime_error) {
659 if(!regex_match("[1-9][0-9]{0,8}", args))
660 throw std::runtime_error("Bad slot number");
661 uint32_t slot = parse_value<uint32_t>(args);
662 if(slot >= (size_t)jukebox_size(CORE().settings))
663 throw std::runtime_error("Bad slot number");
664 save_jukebox_pointer = slot - 1;
665 update_movie_state();
668 command::fnptr<> load_jukebox(lsnes_cmds, "load-jukebox", "Load save from jukebox",
669 "Syntax: load-jukebox\nLoad save from jukebox\n",
670 []() throw(std::bad_alloc, std::runtime_error) {
671 if(jukebox_size(CORE().settings) == 0)
672 throw std::runtime_error("No slot selected");
673 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
676 command::fnptr<> load_jukebox_readwrite(lsnes_cmds, "load-jukebox-readwrite", "Load save from jukebox in"
677 " recording mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in recording mode\n",
678 []() throw(std::bad_alloc, std::runtime_error) {
679 if(jukebox_size(CORE().settings) == 0)
680 throw std::runtime_error("No slot selected");
681 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
684 command::fnptr<> load_jukebox_readonly(lsnes_cmds, "load-jukebox-readonly", "Load save from jukebox in "
685 "playback mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in playback mode\n",
686 []() throw(std::bad_alloc, std::runtime_error) {
687 if(jukebox_size(CORE().settings) == 0)
688 throw std::runtime_error("No slot selected");
689 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
692 command::fnptr<> load_jukebox_preserve(lsnes_cmds, "load-jukebox-preserve", "Load save from jukebox, "
693 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
694 []() throw(std::bad_alloc, std::runtime_error) {
695 if(jukebox_size(CORE().settings) == 0)
696 throw std::runtime_error("No slot selected");
697 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
700 command::fnptr<> load_jukebox_movie(lsnes_cmds, "load-jukebox-movie", "Load save from jukebox as movie",
701 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
702 []() throw(std::bad_alloc, std::runtime_error) {
703 if(jukebox_size(CORE().settings) == 0)
704 throw std::runtime_error("No slot selected");
705 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
708 command::fnptr<> save_jukebox_c(lsnes_cmds, "save-jukebox", "Save save to jukebox",
709 "Syntax: save-jukebox\nSave save to jukebox\n",
710 []() throw(std::bad_alloc, std::runtime_error) {
711 if(jukebox_size(CORE().settings) == 0)
712 throw std::runtime_error("No slot selected");
713 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE, -1);
716 command::fnptr<> padvance_frame(lsnes_cmds, "+advance-frame", "Advance one frame",
717 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
718 []() throw(std::bad_alloc, std::runtime_error) {
719 amode = ADVANCE_FRAME;
720 cancel_advance = false;
721 advanced_once = false;
722 platform::cancel_wait();
723 platform::set_paused(false);
726 command::fnptr<> nadvance_frame(lsnes_cmds, "-advance-frame", "Advance one frame",
727 "No help available\n",
728 []() throw(std::bad_alloc, std::runtime_error) {
729 cancel_advance = true;
730 platform::cancel_wait();
731 platform::set_paused(false);
734 command::fnptr<> padvance_poll(lsnes_cmds, "+advance-poll", "Advance one subframe",
735 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
736 []() throw(std::bad_alloc, std::runtime_error) {
737 amode = ADVANCE_SUBFRAME;
738 cancel_advance = false;
739 advanced_once = false;
740 platform::cancel_wait();
741 platform::set_paused(false);
744 command::fnptr<> nadvance_poll(lsnes_cmds, "-advance-poll", "Advance one subframe",
745 "No help available\n",
746 []() throw(std::bad_alloc, std::runtime_error) {
747 if(amode == ADVANCE_BREAK_PAUSE)
748 amode = ADVANCE_PAUSE;
749 cancel_advance = true;
750 platform::cancel_wait();
751 platform::set_paused(false);
754 command::fnptr<> advance_skiplag(lsnes_cmds, "advance-skiplag", "Skip to next poll",
755 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
756 []() throw(std::bad_alloc, std::runtime_error) {
757 amode = ADVANCE_SKIPLAG_PENDING;
758 platform::cancel_wait();
759 platform::set_paused(false);
762 command::fnptr<> reset_c(lsnes_cmds, "reset", "Reset the system",
763 "Syntax: reset\nReset\nResets the system in beginning of the next frame.\n",
764 []() throw(std::bad_alloc, std::runtime_error) {
765 int sreset_action = our_rom.rtype->reset_action(false);
766 if(sreset_action < 0) {
767 platform::error_message("Core does not support resets");
768 messages << "Emulator core does not support resets" << std::endl;
769 return;
771 our_rom.rtype->execute_action(sreset_action, std::vector<interface_action_paramval>());
774 command::fnptr<> hreset_c(lsnes_cmds, "reset-hard", "Reset the system",
775 "Syntax: reset-hard\nReset-hard\nHard resets the system in beginning of the next frame.\n",
776 []() throw(std::bad_alloc, std::runtime_error) {
777 int hreset_action = our_rom.rtype->reset_action(true);
778 if(hreset_action < 0) {
779 platform::error_message("Core does not support hard resets");
780 messages << "Emulator core does not support hard resets" << std::endl;
781 return;
783 our_rom.rtype->execute_action(hreset_action, std::vector<interface_action_paramval>());
786 command::fnptr<command::arg_filename> load_c(lsnes_cmds, "load", "Load savestate (current mode)",
787 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
788 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
789 mark_pending_load(args, LOAD_STATE_CURRENT);
792 command::fnptr<command::arg_filename> load_smart_c(lsnes_cmds, "load-smart",
793 "Load savestate (heuristic mode)",
794 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
795 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
796 mark_pending_load(args, LOAD_STATE_DEFAULT);
799 command::fnptr<command::arg_filename> load_state_c(lsnes_cmds, "load-state", "Load savestate (R/W)",
800 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
801 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
802 mark_pending_load(args, LOAD_STATE_RW);
805 command::fnptr<command::arg_filename> load_readonly(lsnes_cmds, "load-readonly", "Load savestate (RO)",
806 "Syntax: load-readonly <file>\nLoads SNES state from <file> in playback mode\n",
807 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
808 mark_pending_load(args, LOAD_STATE_RO);
811 command::fnptr<command::arg_filename> load_preserve(lsnes_cmds, "load-preserve", "Load savestate (preserve "
812 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
813 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
814 mark_pending_load(args, LOAD_STATE_PRESERVE);
817 command::fnptr<command::arg_filename> load_movie_c(lsnes_cmds, "load-movie", "Load movie",
818 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
819 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
820 mark_pending_load(args, LOAD_STATE_MOVIE);
823 command::fnptr<command::arg_filename> load_allbr_c(lsnes_cmds, "load-allbranches", "Load savestate "
824 "(all branches)", "Syntax: load-allbranches <file>\nLoads SNES state from <file> with all "
825 "branches\n",
826 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
827 mark_pending_load(args, LOAD_STATE_ALLBRANCH);
830 command::fnptr<command::arg_filename> save_state(lsnes_cmds, "save-state", "Save state",
831 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
832 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
833 mark_pending_save(args, SAVE_STATE, -1);
836 command::fnptr<command::arg_filename> save_state2(lsnes_cmds, "save-state-binary", "Save state (binary)",
837 "Syntax: save-state-binary <file>\nSaves binary state to <file>\n",
838 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
839 mark_pending_save(args, SAVE_STATE, 1);
842 command::fnptr<command::arg_filename> save_state3(lsnes_cmds, "save-state-zip", "Save state (zip)",
843 "Syntax: save-state-zip <file>\nSaves zip state to <file>\n",
844 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
845 mark_pending_save(args, SAVE_STATE, 0);
848 command::fnptr<command::arg_filename> save_movie(lsnes_cmds, "save-movie", "Save movie",
849 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
850 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
851 mark_pending_save(args, SAVE_MOVIE, -1);
854 command::fnptr<command::arg_filename> save_movie2(lsnes_cmds, "save-movie-binary", "Save movie (binary)",
855 "Syntax: save-movie-binary <file>\nSaves binary movie to <file>\n",
856 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
857 mark_pending_save(args, SAVE_MOVIE, 1);
860 command::fnptr<command::arg_filename> save_movie3(lsnes_cmds, "save-movie-zip", "Save movie (zip)",
861 "Syntax: save-movie-zip <file>\nSaves zip movie to <file>\n",
862 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
863 mark_pending_save(args, SAVE_MOVIE, 0);
866 command::fnptr<> set_rwmode(lsnes_cmds, "set-rwmode", "Switch to recording mode",
867 "Syntax: set-rwmode\nSwitches to recording mode\n",
868 []() throw(std::bad_alloc, std::runtime_error) {
869 lua_callback_movie_lost("readwrite");
870 CORE().mlogic.get_movie().readonly_mode(false);
871 notify_mode_change(false);
872 lua_callback_do_readwrite();
873 update_movie_state();
876 command::fnptr<> set_romode(lsnes_cmds, "set-romode", "Switch to playback mode",
877 "Syntax: set-romode\nSwitches to playback mode\n",
878 []() throw(std::bad_alloc, std::runtime_error) {
879 CORE().mlogic.get_movie().readonly_mode(true);
880 notify_mode_change(true);
881 update_movie_state();
884 command::fnptr<> toggle_rwmode(lsnes_cmds, "toggle-rwmode", "Toggle recording mode",
885 "Syntax: toggle-rwmode\nToggles recording mode\n",
886 []() throw(std::bad_alloc, std::runtime_error) {
887 bool c = CORE().mlogic.get_movie().readonly_mode();
888 if(c)
889 lua_callback_movie_lost("readwrite");
890 CORE().mlogic.get_movie().readonly_mode(!c);
891 notify_mode_change(!c);
892 if(c)
893 lua_callback_do_readwrite();
894 update_movie_state();
897 command::fnptr<> repaint(lsnes_cmds, "repaint", "Redraw the screen",
898 "Syntax: repaint\nRedraws the screen\n",
899 []() throw(std::bad_alloc, std::runtime_error) {
900 CORE().fbuf.redraw_framebuffer();
903 command::fnptr<> tpon(lsnes_cmds, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
904 []() throw(std::bad_alloc, std::runtime_error) {
905 bool tmp = pause_on_end(CORE().settings);
906 pause_on_end(CORE().settings, !tmp);
907 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
910 command::fnptr<> spon(lsnes_cmds, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
911 []() throw(std::bad_alloc, std::runtime_error) {
912 pause_on_end(CORE().settings, true);
913 messages << "Pause-on-end is now ON" << std::endl;
916 command::fnptr<> cpon(lsnes_cmds, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
917 []() throw(std::bad_alloc, std::runtime_error) {
918 pause_on_end(CORE().settings, false);
919 messages << "Pause-on-end is now OFF" << std::endl;
922 command::fnptr<> rewind_movie(lsnes_cmds, "rewind-movie", "Rewind movie to the beginning",
923 "Syntax: rewind-movie\nRewind movie to the beginning\n",
924 []() throw(std::bad_alloc, std::runtime_error) {
925 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
928 command::fnptr<> cancel_save(lsnes_cmds, "cancel-saves", "Cancel all pending saves", "Syntax: "
929 "cancel-save\nCancel pending saves\n",
930 []() throw(std::bad_alloc, std::runtime_error) {
931 queued_saves.clear();
932 messages << "Pending saves canceled." << std::endl;
935 command::fnptr<> flushslots(lsnes_cmds, "flush-slotinfo", "Flush slotinfo cache",
936 "Flush slotinfo cache\n",
937 []() throw(std::bad_alloc, std::runtime_error) {
938 flush_slotinfo();
941 command::fnptr<> mhold1(lsnes_cmds, "+hold-macro", "Hold macro (hold)",
942 "Hold macros enable\n", []() throw(std::bad_alloc, std::runtime_error) {
943 macro_hold_1 = true;
946 command::fnptr<> mhold2(lsnes_cmds, "-hold-macro", "Hold macro (hold)",
947 "Hold macros disable\n", []() throw(std::bad_alloc, std::runtime_error) {
948 macro_hold_1 = false;
951 command::fnptr<> mhold3(lsnes_cmds, "hold-macro", "Hold macro (toggle)",
952 "Hold macros toggle\n", []() throw(std::bad_alloc, std::runtime_error) {
953 macro_hold_2 = !macro_hold_2;
954 if(macro_hold_2)
955 messages << "Macros are held for next frame." << std::endl;
956 else
957 messages << "Macros are not held for next frame." << std::endl;
960 keyboard::invbind_info imhold1(lsnes_invbinds, "+hold-macro", "Macro‣Hold all macros");
961 keyboard::invbind_info imhold2(lsnes_invbinds, "hold-macro", "Macro‣Hold all macros (typed)");
962 keyboard::invbind_info ipause_emulator(lsnes_invbinds, "pause-emulator", "Speed‣(Un)pause");
963 keyboard::invbind_info ijback(lsnes_invbinds, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
964 keyboard::invbind_info ijforward(lsnes_invbinds, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
965 keyboard::invbind_info iloadj(lsnes_invbinds, "load-jukebox", "Load‣Selected slot");
966 keyboard::invbind_info iloadjrw(lsnes_invbinds, "load-jukebox-readwrite",
967 "Load‣Selected slot (recording mode)");
968 keyboard::invbind_info iloadjro(lsnes_invbinds, "load-jukebox-readonly",
969 "Load‣Selected slot (playback mode)");
970 keyboard::invbind_info iloadjp(lsnes_invbinds, "load-jukebox-preserve",
971 "Load‣Selected slot (preserve input)");
972 keyboard::invbind_info iloadjm(lsnes_invbinds, "load-jukebox-movie", "Load‣Selected slot (as movie)");
973 keyboard::invbind_info isavej(lsnes_invbinds, "save-jukebox", "Save‣Selected slot");
974 keyboard::invbind_info iadvframe(lsnes_invbinds, "+advance-frame", "Speed‣Advance frame");
975 keyboard::invbind_info iadvsubframe(lsnes_invbinds, "+advance-poll", "Speed‣Advance subframe");
976 keyboard::invbind_info iskiplag(lsnes_invbinds, "advance-skiplag", "Speed‣Advance poll");
977 keyboard::invbind_info ireset(lsnes_invbinds, "reset", "System‣Reset");
978 keyboard::invbind_info iset_rwmode(lsnes_invbinds, "set-rwmode", "Movie‣Switch to recording");
979 keyboard::invbind_info itoggle_romode(lsnes_invbinds, "set-romode", "Movie‣Switch to playback");
980 keyboard::invbind_info itoggle_rwmode(lsnes_invbinds, "toggle-rwmode", "Movie‣Toggle playback");
981 keyboard::invbind_info irepaint(lsnes_invbinds, "repaint", "System‣Repaint screen");
982 keyboard::invbind_info itogglepause(lsnes_invbinds, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
983 keyboard::invbind_info irewind_movie(lsnes_invbinds, "rewind-movie", "Movie‣Rewind movie");
984 keyboard::invbind_info icancel_saves(lsnes_invbinds, "cancel-saves", "Save‣Cancel pending saves");
985 keyboard::invbind_info iload1(lsnes_invbinds, "load $SLOT:1", "Load‣Slot 1");
986 keyboard::invbind_info iload2(lsnes_invbinds, "load $SLOT:2", "Load‣Slot 2");
987 keyboard::invbind_info iload3(lsnes_invbinds, "load $SLOT:3", "Load‣Slot 3");
988 keyboard::invbind_info iload4(lsnes_invbinds, "load $SLOT:4", "Load‣Slot 4");
989 keyboard::invbind_info iload5(lsnes_invbinds, "load $SLOT:5", "Load‣Slot 5");
990 keyboard::invbind_info iload6(lsnes_invbinds, "load $SLOT:6", "Load‣Slot 6");
991 keyboard::invbind_info iload7(lsnes_invbinds, "load $SLOT:7", "Load‣Slot 7");
992 keyboard::invbind_info iload8(lsnes_invbinds, "load $SLOT:8", "Load‣Slot 8");
993 keyboard::invbind_info iload9(lsnes_invbinds, "load $SLOT:9", "Load‣Slot 9");
994 keyboard::invbind_info iload10(lsnes_invbinds, "load $SLOT:10", "Load‣Slot 10");
995 keyboard::invbind_info iload11(lsnes_invbinds, "load $SLOT:11", "Load‣Slot 11");
996 keyboard::invbind_info iload12(lsnes_invbinds, "load $SLOT:12", "Load‣Slot 12");
997 keyboard::invbind_info iload13(lsnes_invbinds, "load $SLOT:13", "Load‣Slot 13");
998 keyboard::invbind_info iload14(lsnes_invbinds, "load $SLOT:14", "Load‣Slot 14");
999 keyboard::invbind_info iload15(lsnes_invbinds, "load $SLOT:15", "Load‣Slot 15");
1000 keyboard::invbind_info iload16(lsnes_invbinds, "load $SLOT:16", "Load‣Slot 16");
1001 keyboard::invbind_info iload17(lsnes_invbinds, "load $SLOT:17", "Load‣Slot 17");
1002 keyboard::invbind_info iload18(lsnes_invbinds, "load $SLOT:18", "Load‣Slot 18");
1003 keyboard::invbind_info iload19(lsnes_invbinds, "load $SLOT:19", "Load‣Slot 19");
1004 keyboard::invbind_info iload20(lsnes_invbinds, "load $SLOT:20", "Load‣Slot 20");
1005 keyboard::invbind_info iload21(lsnes_invbinds, "load $SLOT:21", "Load‣Slot 21");
1006 keyboard::invbind_info iload22(lsnes_invbinds, "load $SLOT:22", "Load‣Slot 22");
1007 keyboard::invbind_info iload23(lsnes_invbinds, "load $SLOT:23", "Load‣Slot 23");
1008 keyboard::invbind_info iload24(lsnes_invbinds, "load $SLOT:24", "Load‣Slot 24");
1009 keyboard::invbind_info iload25(lsnes_invbinds, "load $SLOT:25", "Load‣Slot 25");
1010 keyboard::invbind_info iload26(lsnes_invbinds, "load $SLOT:26", "Load‣Slot 26");
1011 keyboard::invbind_info iload27(lsnes_invbinds, "load $SLOT:27", "Load‣Slot 27");
1012 keyboard::invbind_info iload28(lsnes_invbinds, "load $SLOT:28", "Load‣Slot 28");
1013 keyboard::invbind_info iload29(lsnes_invbinds, "load $SLOT:29", "Load‣Slot 29");
1014 keyboard::invbind_info iload30(lsnes_invbinds, "load $SLOT:30", "Load‣Slot 30");
1015 keyboard::invbind_info iload31(lsnes_invbinds, "load $SLOT:31", "Load‣Slot 31");
1016 keyboard::invbind_info iload32(lsnes_invbinds, "load $SLOT:32", "Load‣Slot 32");
1017 keyboard::invbind_info isave1(lsnes_invbinds, "save-state $SLOT:1", "Save‣Slot 1");
1018 keyboard::invbind_info isave2(lsnes_invbinds, "save-state $SLOT:2", "Save‣Slot 2");
1019 keyboard::invbind_info isave3(lsnes_invbinds, "save-state $SLOT:3", "Save‣Slot 3");
1020 keyboard::invbind_info isave4(lsnes_invbinds, "save-state $SLOT:4", "Save‣Slot 4");
1021 keyboard::invbind_info isave5(lsnes_invbinds, "save-state $SLOT:5", "Save‣Slot 5");
1022 keyboard::invbind_info isave6(lsnes_invbinds, "save-state $SLOT:6", "Save‣Slot 6");
1023 keyboard::invbind_info isave7(lsnes_invbinds, "save-state $SLOT:7", "Save‣Slot 7");
1024 keyboard::invbind_info isave8(lsnes_invbinds, "save-state $SLOT:8", "Save‣Slot 8");
1025 keyboard::invbind_info isave9(lsnes_invbinds, "save-state $SLOT:9", "Save‣Slot 9");
1026 keyboard::invbind_info isave10(lsnes_invbinds, "save-state $SLOT:10", "Save‣Slot 10");
1027 keyboard::invbind_info isave11(lsnes_invbinds, "save-state $SLOT:11", "Save‣Slot 11");
1028 keyboard::invbind_info isave12(lsnes_invbinds, "save-state $SLOT:12", "Save‣Slot 12");
1029 keyboard::invbind_info isave13(lsnes_invbinds, "save-state $SLOT:13", "Save‣Slot 13");
1030 keyboard::invbind_info isave14(lsnes_invbinds, "save-state $SLOT:14", "Save‣Slot 14");
1031 keyboard::invbind_info isave15(lsnes_invbinds, "save-state $SLOT:15", "Save‣Slot 15");
1032 keyboard::invbind_info isave16(lsnes_invbinds, "save-state $SLOT:16", "Save‣Slot 16");
1033 keyboard::invbind_info isave17(lsnes_invbinds, "save-state $SLOT:17", "Save‣Slot 17");
1034 keyboard::invbind_info isave18(lsnes_invbinds, "save-state $SLOT:18", "Save‣Slot 18");
1035 keyboard::invbind_info isave19(lsnes_invbinds, "save-state $SLOT:19", "Save‣Slot 19");
1036 keyboard::invbind_info isave20(lsnes_invbinds, "save-state $SLOT:20", "Save‣Slot 20");
1037 keyboard::invbind_info isave21(lsnes_invbinds, "save-state $SLOT:21", "Save‣Slot 21");
1038 keyboard::invbind_info isave22(lsnes_invbinds, "save-state $SLOT:22", "Save‣Slot 22");
1039 keyboard::invbind_info isave23(lsnes_invbinds, "save-state $SLOT:23", "Save‣Slot 23");
1040 keyboard::invbind_info isave24(lsnes_invbinds, "save-state $SLOT:24", "Save‣Slot 24");
1041 keyboard::invbind_info isave25(lsnes_invbinds, "save-state $SLOT:25", "Save‣Slot 25");
1042 keyboard::invbind_info isave26(lsnes_invbinds, "save-state $SLOT:26", "Save‣Slot 26");
1043 keyboard::invbind_info isave27(lsnes_invbinds, "save-state $SLOT:27", "Save‣Slot 27");
1044 keyboard::invbind_info isave28(lsnes_invbinds, "save-state $SLOT:28", "Save‣Slot 28");
1045 keyboard::invbind_info isave29(lsnes_invbinds, "save-state $SLOT:29", "Save‣Slot 29");
1046 keyboard::invbind_info isave30(lsnes_invbinds, "save-state $SLOT:30", "Save‣Slot 30");
1047 keyboard::invbind_info isave31(lsnes_invbinds, "save-state $SLOT:31", "Save‣Slot 31");
1048 keyboard::invbind_info isave32(lsnes_invbinds, "save-state $SLOT:32", "Save‣Slot 32");
1049 keyboard::invbind_info islot1(lsnes_invbinds, "set-jukebox-slot 1", "Slot select‣Slot 1");
1050 keyboard::invbind_info islot2(lsnes_invbinds, "set-jukebox-slot 2", "Slot select‣Slot 2");
1051 keyboard::invbind_info islot3(lsnes_invbinds, "set-jukebox-slot 3", "Slot select‣Slot 3");
1052 keyboard::invbind_info islot4(lsnes_invbinds, "set-jukebox-slot 4", "Slot select‣Slot 4");
1053 keyboard::invbind_info islot5(lsnes_invbinds, "set-jukebox-slot 5", "Slot select‣Slot 5");
1054 keyboard::invbind_info islot6(lsnes_invbinds, "set-jukebox-slot 6", "Slot select‣Slot 6");
1055 keyboard::invbind_info islot7(lsnes_invbinds, "set-jukebox-slot 7", "Slot select‣Slot 7");
1056 keyboard::invbind_info islot8(lsnes_invbinds, "set-jukebox-slot 8", "Slot select‣Slot 8");
1057 keyboard::invbind_info islot9(lsnes_invbinds, "set-jukebox-slot 9", "Slot select‣Slot 9");
1058 keyboard::invbind_info islot10(lsnes_invbinds, "set-jukebox-slot 10", "Slot select‣Slot 10");
1059 keyboard::invbind_info islot11(lsnes_invbinds, "set-jukebox-slot 11", "Slot select‣Slot 11");
1060 keyboard::invbind_info islot12(lsnes_invbinds, "set-jukebox-slot 12", "Slot select‣Slot 12");
1061 keyboard::invbind_info islot13(lsnes_invbinds, "set-jukebox-slot 13", "Slot select‣Slot 13");
1062 keyboard::invbind_info islot14(lsnes_invbinds, "set-jukebox-slot 14", "Slot select‣Slot 14");
1063 keyboard::invbind_info islot15(lsnes_invbinds, "set-jukebox-slot 15", "Slot select‣Slot 15");
1064 keyboard::invbind_info islot16(lsnes_invbinds, "set-jukebox-slot 16", "Slot select‣Slot 16");
1065 keyboard::invbind_info islot17(lsnes_invbinds, "set-jukebox-slot 17", "Slot select‣Slot 17");
1066 keyboard::invbind_info islot18(lsnes_invbinds, "set-jukebox-slot 18", "Slot select‣Slot 18");
1067 keyboard::invbind_info islot19(lsnes_invbinds, "set-jukebox-slot 19", "Slot select‣Slot 19");
1068 keyboard::invbind_info islot20(lsnes_invbinds, "set-jukebox-slot 20", "Slot select‣Slot 20");
1069 keyboard::invbind_info islot21(lsnes_invbinds, "set-jukebox-slot 21", "Slot select‣Slot 21");
1070 keyboard::invbind_info islot22(lsnes_invbinds, "set-jukebox-slot 22", "Slot select‣Slot 22");
1071 keyboard::invbind_info islot23(lsnes_invbinds, "set-jukebox-slot 23", "Slot select‣Slot 23");
1072 keyboard::invbind_info islot24(lsnes_invbinds, "set-jukebox-slot 24", "Slot select‣Slot 24");
1073 keyboard::invbind_info islot25(lsnes_invbinds, "set-jukebox-slot 25", "Slot select‣Slot 25");
1074 keyboard::invbind_info islot26(lsnes_invbinds, "set-jukebox-slot 26", "Slot select‣Slot 26");
1075 keyboard::invbind_info islot27(lsnes_invbinds, "set-jukebox-slot 27", "Slot select‣Slot 27");
1076 keyboard::invbind_info islot28(lsnes_invbinds, "set-jukebox-slot 28", "Slot select‣Slot 28");
1077 keyboard::invbind_info islot29(lsnes_invbinds, "set-jukebox-slot 29", "Slot select‣Slot 29");
1078 keyboard::invbind_info islot30(lsnes_invbinds, "set-jukebox-slot 30", "Slot select‣Slot 30");
1079 keyboard::invbind_info islot31(lsnes_invbinds, "set-jukebox-slot 31", "Slot select‣Slot 31");
1080 keyboard::invbind_info islot32(lsnes_invbinds, "set-jukebox-slot 32", "Slot select‣Slot 32");
1082 bool on_quit_prompt = false;
1083 class mywindowcallbacks : public information_dispatch
1085 public:
1086 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks")
1088 closenotify.set(notify_close, [this]() {
1089 if(on_quit_prompt) {
1090 amode = ADVANCE_QUIT;
1091 quit_magic = QUIT_MAGIC;
1092 platform::set_paused(false);
1093 platform::cancel_wait();
1094 return;
1096 on_quit_prompt = true;
1097 try {
1098 amode = ADVANCE_QUIT;
1099 quit_magic = QUIT_MAGIC;
1100 platform::set_paused(false);
1101 platform::cancel_wait();
1102 } catch(...) {
1104 on_quit_prompt = false;
1107 ~mywindowcallbacks() throw() {}
1108 void on_new_dumper(const std::string& n)
1110 update_movie_state();
1112 void on_destroy_dumper(const std::string& n)
1114 update_movie_state();
1116 private:
1117 struct dispatch::target<> closenotify;
1118 } mywcb;
1120 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
1121 //failing.
1122 int handle_load()
1124 std::string old_project = CORE().mlogic ? CORE().mlogic.get_mfile().projectid : "";
1125 jumpback:
1126 if(do_unsafe_rewind && unsafe_rewind_obj) {
1127 if(!CORE().mlogic)
1128 return 0;
1129 uint64_t t = framerate_regulator::get_utime();
1130 std::vector<char> s;
1131 lua_callback_do_unsafe_rewind(s, 0, 0, CORE().mlogic.get_movie(), unsafe_rewind_obj);
1132 notify_mode_change(false);
1133 do_unsafe_rewind = false;
1134 CORE().mlogic.get_mfile().is_savestate = true;
1135 location_special = SPECIAL_SAVEPOINT;
1136 update_movie_state();
1137 messages << "Rewind done in " << (framerate_regulator::get_utime() - t) << " usec."
1138 << std::endl;
1139 return 1;
1141 if(pending_new_project != "") {
1142 std::string id = pending_new_project;
1143 pending_new_project = "";
1144 project_info* old = CORE().project.get();
1145 if(old && old->id == id)
1146 goto nothing_to_do;
1147 try {
1148 auto& p = CORE().project.load(id);
1149 CORE().project.set(&p);
1150 if(CORE().project.get() != old)
1151 delete old;
1152 flush_slotinfo(); //Wrong movie may be stale.
1153 return 1;
1154 } catch(std::exception& e) {
1155 platform::error_message(std::string("Can't switch projects: ") + e.what());
1156 messages << "Can't switch projects: " << e.what() << std::endl;
1157 goto nothing_to_do;
1159 nothing_to_do:
1160 amode = old_mode;
1161 platform::set_paused(amode == ADVANCE_PAUSE);
1162 platform::flush_command_queue();
1163 if(amode == ADVANCE_LOAD)
1164 goto jumpback;
1165 return 0;
1167 if(pending_load != "") {
1168 bool system_was_corrupt = system_corrupt;
1169 system_corrupt = false;
1170 try {
1171 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
1172 !do_load_state(pending_load, loadmode)) {
1173 if(system_was_corrupt)
1174 system_corrupt = system_was_corrupt;
1175 pending_load = "";
1176 return -1;
1178 if(loadmode == LOAD_STATE_BEGINNING)
1179 do_load_rewind();
1180 if(loadmode == LOAD_STATE_ROMRELOAD)
1181 do_load_rom();
1182 } catch(std::exception& e) {
1183 if(!system_corrupt && system_was_corrupt)
1184 system_corrupt = true;
1185 platform::error_message(std::string("Load failed: ") + e.what());
1186 messages << "Load failed: " << e.what() << std::endl;
1188 pending_load = "";
1189 amode = load_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1190 platform::set_paused(load_paused);
1191 load_paused = false;
1192 if(!system_corrupt) {
1193 location_special = SPECIAL_SAVEPOINT;
1194 update_movie_state();
1195 platform::flush_command_queue();
1196 if(is_quitting())
1197 return -1;
1198 if(amode == ADVANCE_LOAD)
1199 goto jumpback;
1201 if(old_project != (CORE().mlogic ? CORE().mlogic.get_mfile().projectid : ""))
1202 flush_slotinfo(); //Wrong movie may be stale.
1203 return 1;
1205 return 0;
1208 //If there are pending saves, perform them.
1209 void handle_saves()
1211 if(!CORE().mlogic)
1212 return;
1213 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
1214 our_rom.rtype->runtosave();
1215 for(auto i : queued_saves) {
1216 do_save_state(i.first, i.second);
1217 int tmp = -1;
1218 flush_slotinfo(translate_name_mprefix(i.first, tmp, -1));
1220 if(do_unsafe_rewind && !unsafe_rewind_obj) {
1221 uint64_t t = framerate_regulator::get_utime();
1222 std::vector<char> s = our_rom.save_core_state(true);
1223 uint64_t secs = CORE().mlogic.get_mfile().rtc_second;
1224 uint64_t ssecs = CORE().mlogic.get_mfile().rtc_subsecond;
1225 lua_callback_do_unsafe_rewind(s, secs, ssecs, CORE().mlogic.get_movie(),
1226 NULL);
1227 do_unsafe_rewind = false;
1228 messages << "Rewind point set in " << (framerate_regulator::get_utime() - t) << " usec." << std::endl;
1231 queued_saves.clear();
1234 bool handle_corrupt()
1236 if(!system_corrupt)
1237 return false;
1238 while(system_corrupt) {
1239 platform::set_paused(true);
1240 platform::flush_command_queue();
1241 handle_load();
1242 if(is_quitting())
1243 return true;
1245 return true;
1249 void init_main_callbacks()
1251 ecore_callbacks = &lsnes_callbacks_obj;
1254 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
1255 std::runtime_error)
1257 lsnes_instance.emu_thread = threads::id();
1258 CORE().iqueue.system_thread_available = true;
1259 //Basic initialization.
1260 dispatch_set_error_streams(&messages.getstream());
1261 emulation_thread = threads::this_id();
1262 jukebox_size_listener jlistener(CORE().settings);
1263 CORE().commentary.init();
1264 CORE().fbuf.init_special_screens();
1265 our_rom = rom;
1266 init_main_callbacks();
1267 initialize_all_builtin_c_cores();
1268 core_core::install_all_handlers();
1270 //Load our given movie.
1271 bool first_round = false;
1272 bool just_did_loadstate = false;
1273 bool used = false;
1274 try {
1275 do_load_state(initial, LOAD_STATE_INITIAL, used);
1276 location_special = SPECIAL_SAVEPOINT;
1277 update_movie_state();
1278 first_round = CORE().mlogic.get_mfile().is_savestate;
1279 just_did_loadstate = first_round;
1280 } catch(std::bad_alloc& e) {
1281 OOM_panic();
1282 } catch(std::exception& e) {
1283 if(!used)
1284 delete &initial;
1285 platform::error_message(std::string("Can't load initial state: ") + e.what());
1286 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
1287 if(load_has_to_succeed) {
1288 messages << "FATAL: Can't load movie" << std::endl;
1289 throw;
1291 system_corrupt = true;
1292 CORE().fbuf.redraw_framebuffer(emu_framebuffer::screen_corrupt);
1295 platform::set_paused(initial.start_paused);
1296 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1297 stop_at_frame_active = false;
1299 lua_run_startup_scripts();
1301 uint64_t time_x = framerate_regulator::get_utime();
1302 while(!is_quitting() || !queued_saves.empty()) {
1303 if(handle_corrupt()) {
1304 first_round = CORE().mlogic && CORE().mlogic.get_mfile().is_savestate;
1305 just_did_loadstate = first_round;
1306 continue;
1308 CORE().framerate.ack_frame_tick(framerate_regulator::get_utime());
1309 if(amode == ADVANCE_SKIPLAG_PENDING)
1310 amode = ADVANCE_SKIPLAG;
1312 if(!first_round) {
1313 CORE().controls.reset_framehold();
1314 if(!macro_hold_1 && !macro_hold_2) {
1315 CORE().controls.advance_macros();
1317 macro_hold_2 = false;
1318 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(false);
1319 CORE().mlogic.new_frame_starting(amode == ADVANCE_SKIPLAG);
1320 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(true);
1321 if(is_quitting() && queued_saves.empty())
1322 break;
1323 handle_saves();
1324 int r = 0;
1325 if(queued_saves.empty())
1326 r = handle_load();
1327 if(r > 0 || system_corrupt) {
1328 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(
1329 CORE().mlogic.get_mfile().is_savestate);
1330 first_round = CORE().mlogic.get_mfile().is_savestate;
1331 if(system_corrupt)
1332 amode = ADVANCE_PAUSE;
1333 else
1334 amode = old_mode;
1335 stop_at_frame_active = false;
1336 just_did_loadstate = first_round;
1337 CORE().controls.reset_framehold();
1338 CORE().dbg.do_callback_frame(CORE().mlogic.get_movie().get_current_frame(), true);
1339 continue;
1340 } else if(r < 0) {
1341 //Not exactly desriable, but this at least won't desync.
1342 stop_at_frame_active = false;
1343 if(is_quitting())
1344 goto out;
1345 amode = ADVANCE_PAUSE;
1348 if(just_did_loadstate) {
1349 //If we just loadstated, we are up to date.
1350 if(is_quitting())
1351 break;
1352 platform::set_paused(amode == ADVANCE_PAUSE);
1353 platform::flush_command_queue();
1354 //We already have done the reset this frame if we are going to do one at all.
1355 CORE().mlogic.get_movie().set_controls(CORE().mlogic.update_controls(true));
1356 CORE().mlogic.get_movie().set_all_DRDY();
1357 just_did_loadstate = false;
1359 frame_irq_time = framerate_regulator::get_utime() - time_x;
1360 CORE().dbg.do_callback_frame(CORE().mlogic.get_movie().get_current_frame(), false);
1361 our_rom.rtype->emulate();
1362 random_mix_timing_entropy();
1363 time_x = framerate_regulator::get_utime();
1364 if(amode == ADVANCE_AUTO)
1365 platform::wait(CORE().framerate.to_wait_frame(framerate_regulator::get_utime()));
1366 first_round = false;
1367 lua_callback_do_frame();
1369 out:
1370 information_dispatch::do_dump_end();
1371 core_core::uninstall_all_handlers();
1372 CORE().commentary.kill();
1373 CORE().iqueue.system_thread_available = false;
1374 //Kill some things to avoid crashes.
1375 CORE().dbg.core_change();
1376 CORE().project.set(NULL, true);
1377 CORE().mwatch.clear_multi(CORE().mwatch.enumerate());
1380 void set_stop_at_frame(uint64_t frame)
1382 stop_at_frame = frame;
1383 stop_at_frame_active = (frame != 0);
1384 amode = ADVANCE_AUTO;
1385 platform::set_paused(false);
1388 void do_flush_slotinfo()
1390 flush_slotinfo();
1393 void switch_projects(const std::string& newproj)
1395 pending_new_project = newproj;
1396 amode = ADVANCE_LOAD;
1397 old_mode = ADVANCE_PAUSE;
1398 platform::cancel_wait();
1399 platform::set_paused(false);
1402 void load_new_rom(const romload_request& req)
1404 if(_load_new_rom(req)) {
1405 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1409 void reload_current_rom()
1411 if(reload_active_rom()) {
1412 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1416 void close_rom()
1418 if(load_null_rom()) {
1419 load_paused = true;
1420 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1424 void do_break_pause()
1426 amode = ADVANCE_BREAK_PAUSE;
1427 update_movie_state();
1428 while(amode == ADVANCE_BREAK_PAUSE) {
1429 platform::set_paused(true);
1430 platform::flush_command_queue();
1434 void convert_break_to_pause()
1436 if(amode == ADVANCE_BREAK_PAUSE) {
1437 amode = ADVANCE_PAUSE;
1438 update_movie_state();
1442 void debug_trash_memory(uint8_t* addr, uint8_t byte)
1444 *addr = byte;