Split random number functions from misc.cpp to dedicated file
[lsnes.git] / src / core / mainloop.cpp
blobdd0e5210d0e311e3bcdaaee8501982449936d563
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 SPECIAL_FRAME_START 0
43 #define SPECIAL_FRAME_VIDEO 1
44 #define SPECIAL_SAVEPOINT 2
45 #define SPECIAL_NONE 3
47 void update_movie_state();
48 time_t random_seed_value = 0;
50 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> jukebox_dflt_binary(lsnes_vset,
51 "jukebox-default-binary", "Movie‣Saving‣Saveslots binary", true);
52 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> movie_dflt_binary(lsnes_vset, "movie-default-binary",
53 "Movie‣Saving‣Movies binary", false);
54 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> save_dflt_binary(lsnes_vset,
55 "savestate-default-binary", "Movie‣Saving‣Savestates binary", false);
57 namespace
59 settingvar::variable<settingvar::model_int<0,999999>> advance_timeout_first(lsnes_vset, "advance-timeout",
60 "Delays‣First frame advance", 500);
61 settingvar::variable<settingvar::model_int<0,999999>> advance_timeout_subframe(lsnes_vset,
62 "advance-subframe-timeout", "Delays‣Subframe advance", 100);
63 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> pause_on_end(lsnes_vset, "pause-on-end",
64 "Movie‣Pause on end", false);
65 settingvar::variable<settingvar::model_int<0,999999999>> jukebox_size(lsnes_vset, "jukebox-size",
66 "Movie‣Number of save slots", 12);
68 enum advance_mode
70 ADVANCE_QUIT, //Quit the emulator.
71 ADVANCE_AUTO, //Normal (possibly slowed down play).
72 ADVANCE_LOAD, //Loading a state.
73 ADVANCE_FRAME, //Frame advance.
74 ADVANCE_SUBFRAME, //Subframe advance.
75 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
76 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
77 ADVANCE_PAUSE, //Unconditional pause.
80 //Our thread.
81 threads::id emulation_thread;
82 //Flags related to repeating advance.
83 bool advanced_once;
84 bool cancel_advance;
85 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
86 enum advance_mode amode;
87 enum advance_mode old_mode;
88 //Mode and filename of pending load, one of LOAD_* constants.
89 bool load_paused;
90 int loadmode;
91 std::string pending_load;
92 std::string pending_new_project;
93 //Queued saves (all savestates).
94 std::set<std::pair<std::string, int>> queued_saves;
95 //Save jukebox.
96 size_t save_jukebox_pointer;
97 //Special subframe location. One of SPECIAL_* constants.
98 int location_special;
99 //Unsafe rewind.
100 bool do_unsafe_rewind = false;
101 void* unsafe_rewind_obj = NULL;
102 //Stop at frame.
103 bool stop_at_frame_active = false;
104 uint64_t stop_at_frame = 0;
105 //Macro hold.
106 bool macro_hold_1;
107 bool macro_hold_2;
108 //In break pause.
109 bool break_pause = false;
112 std::string save_jukebox_name(size_t i)
114 return (stringfmt() << "$SLOT:" << (i + 1)).str();
117 std::map<std::string, std::string> slotinfo_cache;
119 std::string vector_to_string(const std::vector<char>& x)
121 std::string y(x.begin(), x.end());
122 while(y.length() > 0 && y[y.length() - 1] < 32)
123 y = y.substr(0, y.length() - 1);
124 return y;
127 std::string get_slotinfo(const std::string& _filename)
129 std::string filename = resolve_relative_path(_filename);
130 if(!slotinfo_cache.count(filename)) {
131 std::ostringstream out;
132 try {
133 moviefile::brief_info info(filename);
134 if(!CORE().mlogic)
135 out << "No movie";
136 else if(CORE().mlogic.get_mfile().projectid == info.projectid)
137 out << info.rerecords << "R/" << info.current_frame << "F";
138 else
139 out << "Wrong movie";
140 } catch(...) {
141 out << "Nonexistent";
143 slotinfo_cache[filename] = out.str();
145 return slotinfo_cache[filename];
148 void flush_slotinfo(const std::string& filename)
150 slotinfo_cache.erase(resolve_relative_path(filename));
153 void flush_slotinfo()
155 slotinfo_cache.clear();
159 void mainloop_signal_need_rewind(void* ptr)
161 if(ptr) {
162 old_mode = amode;
163 amode = ADVANCE_LOAD;
165 do_unsafe_rewind = true;
166 unsafe_rewind_obj = ptr;
169 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
171 if(lua_requests_subframe_paint)
172 redraw_framebuffer();
174 if(subframe) {
175 if(amode == ADVANCE_SUBFRAME) {
176 if(!cancel_advance) {
177 if(!advanced_once)
178 platform::wait(advance_timeout_first * 1000);
179 else
180 platform::wait(advance_timeout_subframe * 1000);
181 advanced_once = true;
183 if(cancel_advance) {
184 stop_at_frame_active = false;
185 amode = ADVANCE_PAUSE;
186 cancel_advance = false;
188 platform::set_paused(amode == ADVANCE_PAUSE);
189 } else if(amode == ADVANCE_FRAME) {
191 } else {
192 if(amode == ADVANCE_SKIPLAG) {
193 stop_at_frame_active = false;
194 amode = ADVANCE_PAUSE;
196 platform::set_paused(amode == ADVANCE_PAUSE);
197 cancel_advance = false;
199 location_special = SPECIAL_NONE;
200 update_movie_state();
201 } else {
202 if(amode == ADVANCE_SKIPLAG_PENDING)
203 amode = ADVANCE_SKIPLAG;
204 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
205 if(!cancel_advance) {
206 uint64_t wait = 0;
207 if(!advanced_once)
208 wait = advance_timeout_first * 1000;
209 else if(amode == ADVANCE_SUBFRAME)
210 wait = advance_timeout_subframe * 1000;
211 else
212 wait = to_wait_frame(get_utime());
213 platform::wait(wait);
214 advanced_once = true;
216 if(cancel_advance) {
217 stop_at_frame_active = false;
218 amode = ADVANCE_PAUSE;
219 cancel_advance = false;
221 platform::set_paused(amode == ADVANCE_PAUSE);
222 } else if(amode == ADVANCE_AUTO && CORE().mlogic.get_movie().readonly_mode() &&
223 pause_on_end && !stop_at_frame_active) {
224 if(CORE().mlogic.get_movie().get_current_frame() ==
225 CORE().mlogic.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(CORE().mlogic.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(CORE().mlogic.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 CORE().mteditor.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 >= (size_t)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;
307 uint64_t magic[4];
308 our_rom.region->fill_framerate_magic(magic);
309 if(CORE().mlogic)
310 CORE().commentary.frame_number(CORE().mlogic.get_movie().get_current_frame(),
311 1.0 * magic[1] / magic[0]);
312 else
313 CORE().commentary.frame_number(0, 60.0); //Default.
315 auto& _status = CORE().status.get_write();
316 try {
317 if(CORE().mlogic && !system_corrupt) {
318 _status.movie_valid = true;
319 _status.curframe = CORE().mlogic.get_movie().get_current_frame();
320 _status.length = CORE().mlogic.get_movie().get_frame_count();
321 _status.lag = CORE().mlogic.get_movie().get_lag_frames();
322 if(location_special == SPECIAL_FRAME_START)
323 _status.subframe = 0;
324 else if(location_special == SPECIAL_SAVEPOINT)
325 _status.subframe = _lsnes_status::subframe_savepoint;
326 else if(location_special == SPECIAL_FRAME_VIDEO)
327 _status.subframe = _lsnes_status::subframe_video;
328 else
329 _status.subframe = CORE().mlogic.get_movie().next_poll_number();
330 } else {
331 _status.movie_valid = false;
332 _status.curframe = 0;
333 _status.length = 0;
334 _status.lag = 0;
335 _status.subframe = 0;
337 _status.dumping = (information_dispatch::get_dumper_count() > 0);
338 if(break_pause)
339 _status.pause = _lsnes_status::pause_break;
340 else if(amode == ADVANCE_PAUSE)
341 _status.pause = _lsnes_status::pause_normal;
342 else
343 _status.pause = _lsnes_status::pause_none;
344 if(CORE().mlogic) {
345 auto& mo = CORE().mlogic.get_movie();
346 readonly = mo.readonly_mode();
347 if(system_corrupt)
348 _status.mode = 'C';
349 else if(!readonly)
350 _status.mode = 'R';
351 else if(mo.get_frame_count() >= mo.get_current_frame())
352 _status.mode = 'P';
353 else
354 _status.mode = 'F';
356 if(jukebox_size > 0) {
357 _status.saveslot_valid = true;
358 int tmp = -1;
359 std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, -1);
360 _status.saveslot = save_jukebox_pointer + 1;
361 _status.slotinfo = utf8::to32(get_slotinfo(sfilen));
362 } else {
363 _status.saveslot_valid = false;
365 _status.branch_valid = (p != NULL);
366 if(p) _status.branch = utf8::to32(p->get_branch_string());
368 std::string cur_branch = CORE().mlogic ? CORE().mlogic.get_mfile().current_branch() :
370 _status.mbranch_valid = (cur_branch != "");
371 _status.mbranch = utf8::to32(cur_branch);
373 _status.speed = (unsigned)(100 * get_realized_multiplier() + 0.5);
375 if(CORE().mlogic && !system_corrupt) {
376 time_t timevalue = static_cast<time_t>(CORE().mlogic.get_mfile().rtc_second);
377 struct tm* time_decompose = gmtime(&timevalue);
378 char datebuffer[512];
379 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
380 _status.rtc = utf8::to32(datebuffer);
381 _status.rtc_valid = true;
382 } else {
383 _status.rtc_valid = false;
386 auto mset = controls.active_macro_set();
387 bool mfirst = true;
388 std::ostringstream mss;
389 for(auto i: mset) {
390 if(!mfirst) mss << ",";
391 mss << i;
392 mfirst = false;
394 _status.macros = utf8::to32(mss.str());
396 controller_frame c;
397 if(!CORE().mteditor.any_records())
398 c = CORE().mlogic.get_movie().get_controls();
399 else
400 c = controls.get_committed();
401 _status.inputs.clear();
402 for(unsigned i = 0;; i++) {
403 auto pindex = controls.lcid_to_pcid(i);
404 if(pindex.first < 0 || !controls.is_present(pindex.first, pindex.second))
405 break;
406 char32_t buffer[MAX_DISPLAY_LENGTH];
407 c.display(pindex.first, pindex.second, buffer);
408 std::u32string _buffer = buffer;
409 if(readonly && CORE().mteditor.is_enabled()) {
410 multitrack_edit::state st = CORE().mteditor.get(pindex.first, pindex.second);
411 if(st == multitrack_edit::MT_PRESERVE)
412 _buffer += U" (keep)";
413 else if(st == multitrack_edit::MT_OVERWRITE)
414 _buffer += U" (rewrite)";
415 else if(st == multitrack_edit::MT_OR)
416 _buffer += U" (OR)";
417 else if(st == multitrack_edit::MT_XOR)
418 _buffer += U" (XOR)";
419 else
420 _buffer += U" (\?\?\?)";
422 _status.inputs.push_back(_buffer);
424 //Lua variables.
425 _status.lvars = get_lua_watch_vars();
426 //Memory watches.
427 _status.mvars = CORE().mwatch.get_window_vars();
429 _status.valid = true;
430 } catch(...) {
432 CORE().status.put_write();
433 notify_status_update();
436 uint64_t audio_irq_time;
437 uint64_t controller_irq_time;
438 uint64_t frame_irq_time;
441 struct lsnes_callbacks : public emucore_callbacks
443 public:
444 ~lsnes_callbacks() throw()
448 int16_t get_input(unsigned port, unsigned index, unsigned control)
450 int16_t x;
451 x = CORE().mlogic.input_poll(port, index, control);
452 lua_callback_snoop_input(port, index, control, x);
453 return x;
456 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
458 if(!CORE().mlogic.get_movie().readonly_mode()) {
459 controller_frame f = CORE().mlogic.get_movie().get_controls();
460 f.axis3(port, index, control, value);
461 CORE().mlogic.get_movie().set_controls(f);
463 return CORE().mlogic.get_movie().next_input(port, index, control);
466 void notify_latch(std::list<std::string>& args)
468 lua_callback_do_latch(args);
471 void timer_tick(uint32_t increment, uint32_t per_second)
473 if(!CORE().mlogic)
474 return;
475 auto& m = CORE().mlogic.get_mfile();
476 m.rtc_subsecond += increment;
477 while(m.rtc_subsecond >= per_second) {
478 m.rtc_second++;
479 m.rtc_subsecond -= per_second;
483 std::string get_firmware_path()
485 return CORE().setcache.get("firmwarepath");
488 std::string get_base_path()
490 return our_rom.msu1_base;
493 time_t get_time()
495 return CORE().mlogic ? CORE().mlogic.get_mfile().rtc_second : 0;
498 time_t get_randomseed()
500 return random_seed_value;
503 void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
505 lua_callback_do_frame_emulated();
506 location_special = SPECIAL_FRAME_VIDEO;
507 redraw_framebuffer(screen, false, true);
508 uint32_t g = gcd(fps_n, fps_d);
509 fps_n /= g;
510 fps_d /= g;
511 information_dispatch::do_frame(screen, fps_n, fps_d);
514 void action_state_updated()
516 graphics_driver_action_updated();
519 void memory_read(uint64_t addr, uint64_t value)
521 debug_fire_callback_read(addr, value);
524 void memory_write(uint64_t addr, uint64_t value)
526 debug_fire_callback_write(addr, value);
529 void memory_execute(uint64_t addr, uint64_t proc)
531 debug_fire_callback_exec(addr, proc);
534 void memory_trace(uint64_t proc, const char* str, bool insn)
536 debug_fire_callback_trace(proc, str, insn);
540 namespace
542 lsnes_callbacks lsnes_callbacks_obj;
544 command::fnptr<const std::string&> test4(lsnes_cmd, "test4", "test", "test",
545 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
546 std::list<std::string> _args;
547 std::string args2 = args;
548 for(auto& sym : token_iterator_foreach(args, {" ", "\t"}))
549 _args.push_back(sym);
550 lua_callback_do_latch(_args);
552 command::fnptr<> count_rerecords(lsnes_cmd, "count-rerecords", "Count rerecords",
553 "Syntax: count-rerecords\nCounts rerecords.\n",
554 []() throw(std::bad_alloc, std::runtime_error) {
555 std::vector<char> tmp;
556 uint64_t x = CORE().mlogic.get_rrdata().write(tmp);
557 messages << x << " rerecord(s)" << std::endl;
560 command::fnptr<const std::string&> quit_emulator(lsnes_cmd, "quit-emulator", "Quit the emulator",
561 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
562 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
563 amode = ADVANCE_QUIT;
564 platform::set_paused(false);
565 platform::cancel_wait();
568 command::fnptr<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
569 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
570 []() throw(std::bad_alloc, std::runtime_error) {
571 break_pause = false;
572 amode = ADVANCE_AUTO;
573 platform::set_paused(false);
574 platform::cancel_wait();
577 command::fnptr<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
578 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
579 []() throw(std::bad_alloc, std::runtime_error) {
580 if(amode != ADVANCE_AUTO || break_pause) {
581 break_pause = false;
582 amode = ADVANCE_AUTO;
583 platform::set_paused(false);
584 platform::cancel_wait();
585 } else {
586 platform::cancel_wait();
587 cancel_advance = false;
588 stop_at_frame_active = false;
589 amode = ADVANCE_PAUSE;
593 command::fnptr<> save_jukebox_prev(lsnes_cmd, "cycle-jukebox-backward", "Cycle save jukebox backwards",
594 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
595 []() throw(std::bad_alloc, std::runtime_error) {
596 if(jukebox_size == 0)
597 return;
598 if(save_jukebox_pointer == 0)
599 save_jukebox_pointer = jukebox_size - 1;
600 else
601 save_jukebox_pointer--;
602 if(save_jukebox_pointer >= (size_t)jukebox_size)
603 save_jukebox_pointer = 0;
604 update_movie_state();
607 command::fnptr<> save_jukebox_next(lsnes_cmd, "cycle-jukebox-forward", "Cycle save jukebox forwards",
608 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
609 []() throw(std::bad_alloc, std::runtime_error) {
610 if(jukebox_size == 0)
611 return;
612 if(save_jukebox_pointer + 1 >= (size_t)jukebox_size)
613 save_jukebox_pointer = 0;
614 else
615 save_jukebox_pointer++;
616 if(save_jukebox_pointer >= (size_t)jukebox_size)
617 save_jukebox_pointer = 0;
618 update_movie_state();
621 command::fnptr<const std::string&> save_jukebox_set(lsnes_cmd, "set-jukebox-slot", "Set jukebox slot",
622 "Syntax: set-jukebox-slot\nSet jukebox slot\n", [](const std::string& args)
623 throw(std::bad_alloc, std::runtime_error) {
624 if(!regex_match("[1-9][0-9]{0,8}", args))
625 throw std::runtime_error("Bad slot number");
626 uint32_t slot = parse_value<uint32_t>(args);
627 if(slot >= (size_t)jukebox_size)
628 throw std::runtime_error("Bad slot number");
629 save_jukebox_pointer = slot - 1;
630 update_movie_state();
633 command::fnptr<> load_jukebox(lsnes_cmd, "load-jukebox", "Load save from jukebox",
634 "Syntax: load-jukebox\nLoad save from jukebox\n",
635 []() throw(std::bad_alloc, std::runtime_error) {
636 if(jukebox_size == 0)
637 throw std::runtime_error("No slot selected");
638 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
641 command::fnptr<> load_jukebox_readwrite(lsnes_cmd, "load-jukebox-readwrite", "Load save from jukebox in"
642 " read-write mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
643 []() throw(std::bad_alloc, std::runtime_error) {
644 if(jukebox_size == 0)
645 throw std::runtime_error("No slot selected");
646 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
649 command::fnptr<> load_jukebox_readonly(lsnes_cmd, "load-jukebox-readonly", "Load save from jukebox in "
650 "read-only mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
651 []() throw(std::bad_alloc, std::runtime_error) {
652 if(jukebox_size == 0)
653 throw std::runtime_error("No slot selected");
654 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
657 command::fnptr<> load_jukebox_preserve(lsnes_cmd, "load-jukebox-preserve", "Load save from jukebox, "
658 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
659 []() throw(std::bad_alloc, std::runtime_error) {
660 if(jukebox_size == 0)
661 throw std::runtime_error("No slot selected");
662 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
665 command::fnptr<> load_jukebox_movie(lsnes_cmd, "load-jukebox-movie", "Load save from jukebox as movie",
666 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
667 []() throw(std::bad_alloc, std::runtime_error) {
668 if(jukebox_size == 0)
669 throw std::runtime_error("No slot selected");
670 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
673 command::fnptr<> save_jukebox_c(lsnes_cmd, "save-jukebox", "Save save to jukebox",
674 "Syntax: save-jukebox\nSave save to jukebox\n",
675 []() throw(std::bad_alloc, std::runtime_error) {
676 if(jukebox_size == 0)
677 throw std::runtime_error("No slot selected");
678 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE, -1);
681 command::fnptr<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
682 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
683 []() throw(std::bad_alloc, std::runtime_error) {
684 if(break_pause)
685 break_pause = false;
686 amode = ADVANCE_FRAME;
687 cancel_advance = false;
688 advanced_once = false;
689 platform::cancel_wait();
690 platform::set_paused(false);
693 command::fnptr<> nadvance_frame(lsnes_cmd, "-advance-frame", "Advance one frame",
694 "No help available\n",
695 []() throw(std::bad_alloc, std::runtime_error) {
696 cancel_advance = true;
697 platform::cancel_wait();
698 platform::set_paused(false);
701 command::fnptr<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
702 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
703 []() throw(std::bad_alloc, std::runtime_error) {
704 if(break_pause)
705 break_pause = false;
706 amode = ADVANCE_SUBFRAME;
707 cancel_advance = false;
708 advanced_once = false;
709 platform::cancel_wait();
710 platform::set_paused(false);
713 command::fnptr<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
714 "No help available\n",
715 []() throw(std::bad_alloc, std::runtime_error) {
716 if(break_pause)
717 break_pause = false;
718 cancel_advance = true;
719 platform::cancel_wait();
720 platform::set_paused(false);
723 command::fnptr<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
724 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
725 []() throw(std::bad_alloc, std::runtime_error) {
726 if(break_pause)
727 break_pause = false;
728 amode = ADVANCE_SKIPLAG_PENDING;
729 platform::cancel_wait();
730 platform::set_paused(false);
733 command::fnptr<> reset_c(lsnes_cmd, "reset", "Reset the system",
734 "Syntax: reset\nReset\nResets the system in beginning of the next frame.\n",
735 []() throw(std::bad_alloc, std::runtime_error) {
736 int sreset_action = our_rom.rtype->reset_action(false);
737 if(sreset_action < 0) {
738 platform::error_message("Core does not support resets");
739 messages << "Emulator core does not support resets" << std::endl;
740 return;
742 our_rom.rtype->execute_action(sreset_action, std::vector<interface_action_paramval>());
745 command::fnptr<> hreset_c(lsnes_cmd, "reset-hard", "Reset the system",
746 "Syntax: reset-hard\nReset-hard\nHard resets the system in beginning of the next frame.\n",
747 []() throw(std::bad_alloc, std::runtime_error) {
748 int hreset_action = our_rom.rtype->reset_action(true);
749 if(hreset_action < 0) {
750 platform::error_message("Core does not support hard resets");
751 messages << "Emulator core does not support hard resets" << std::endl;
752 return;
754 our_rom.rtype->execute_action(hreset_action, std::vector<interface_action_paramval>());
757 command::fnptr<command::arg_filename> load_c(lsnes_cmd, "load", "Load savestate (current mode)",
758 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
759 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
760 mark_pending_load(args, LOAD_STATE_CURRENT);
763 command::fnptr<command::arg_filename> load_smart_c(lsnes_cmd, "load-smart", "Load savestate (heuristic mode)",
764 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
765 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
766 mark_pending_load(args, LOAD_STATE_DEFAULT);
769 command::fnptr<command::arg_filename> load_state_c(lsnes_cmd, "load-state", "Load savestate (R/W)",
770 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
771 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
772 mark_pending_load(args, LOAD_STATE_RW);
775 command::fnptr<command::arg_filename> load_readonly(lsnes_cmd, "load-readonly", "Load savestate (RO)",
776 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
777 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
778 mark_pending_load(args, LOAD_STATE_RO);
781 command::fnptr<command::arg_filename> load_preserve(lsnes_cmd, "load-preserve", "Load savestate (preserve "
782 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
783 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
784 mark_pending_load(args, LOAD_STATE_PRESERVE);
787 command::fnptr<command::arg_filename> load_movie_c(lsnes_cmd, "load-movie", "Load movie",
788 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
789 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
790 mark_pending_load(args, LOAD_STATE_MOVIE);
793 command::fnptr<command::arg_filename> load_allbr_c(lsnes_cmd, "load-allbranches", "Load savestate "
794 "(all branches)", "Syntax: load-allbranches <file>\nLoads SNES state from <file> with all "
795 "branches\n",
796 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
797 mark_pending_load(args, LOAD_STATE_ALLBRANCH);
800 command::fnptr<command::arg_filename> save_state(lsnes_cmd, "save-state", "Save state",
801 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
802 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
803 mark_pending_save(args, SAVE_STATE, -1);
806 command::fnptr<command::arg_filename> save_state2(lsnes_cmd, "save-state-binary", "Save state (binary)",
807 "Syntax: save-state-binary <file>\nSaves binary state to <file>\n",
808 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
809 mark_pending_save(args, SAVE_STATE, 1);
812 command::fnptr<command::arg_filename> save_state3(lsnes_cmd, "save-state-zip", "Save state (zip)",
813 "Syntax: save-state-zip <file>\nSaves zip state to <file>\n",
814 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
815 mark_pending_save(args, SAVE_STATE, 0);
818 command::fnptr<command::arg_filename> save_movie(lsnes_cmd, "save-movie", "Save movie",
819 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
820 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
821 mark_pending_save(args, SAVE_MOVIE, -1);
824 command::fnptr<command::arg_filename> save_movie2(lsnes_cmd, "save-movie-binary", "Save movie (binary)",
825 "Syntax: save-movie-binary <file>\nSaves binary movie to <file>\n",
826 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
827 mark_pending_save(args, SAVE_MOVIE, 1);
830 command::fnptr<command::arg_filename> save_movie3(lsnes_cmd, "save-movie-zip", "Save movie (zip)",
831 "Syntax: save-movie-zip <file>\nSaves zip movie to <file>\n",
832 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
833 mark_pending_save(args, SAVE_MOVIE, 0);
836 command::fnptr<> set_rwmode(lsnes_cmd, "set-rwmode", "Switch to read/write mode",
837 "Syntax: set-rwmode\nSwitches to read/write mode\n",
838 []() throw(std::bad_alloc, std::runtime_error) {
839 lua_callback_movie_lost("readwrite");
840 CORE().mlogic.get_movie().readonly_mode(false);
841 notify_mode_change(false);
842 lua_callback_do_readwrite();
843 update_movie_state();
846 command::fnptr<> set_romode(lsnes_cmd, "set-romode", "Switch to read-only mode",
847 "Syntax: set-romode\nSwitches to read-only mode\n",
848 []() throw(std::bad_alloc, std::runtime_error) {
849 CORE().mlogic.get_movie().readonly_mode(true);
850 notify_mode_change(true);
851 update_movie_state();
854 command::fnptr<> toggle_rwmode(lsnes_cmd, "toggle-rwmode", "Toggle read/write mode",
855 "Syntax: toggle-rwmode\nToggles read/write mode\n",
856 []() throw(std::bad_alloc, std::runtime_error) {
857 bool c = CORE().mlogic.get_movie().readonly_mode();
858 if(c)
859 lua_callback_movie_lost("readwrite");
860 CORE().mlogic.get_movie().readonly_mode(!c);
861 notify_mode_change(!c);
862 if(c)
863 lua_callback_do_readwrite();
864 update_movie_state();
867 command::fnptr<> repaint(lsnes_cmd, "repaint", "Redraw the screen",
868 "Syntax: repaint\nRedraws the screen\n",
869 []() throw(std::bad_alloc, std::runtime_error) {
870 redraw_framebuffer();
873 command::fnptr<> tpon(lsnes_cmd, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
874 []() throw(std::bad_alloc, std::runtime_error) {
875 bool tmp = pause_on_end;
876 pause_on_end.set(!tmp);
877 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
880 command::fnptr<> spon(lsnes_cmd, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
881 []() throw(std::bad_alloc, std::runtime_error) {
882 pause_on_end.set(true);
883 messages << "Pause-on-end is now ON" << std::endl;
886 command::fnptr<> cpon(lsnes_cmd, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
887 []() throw(std::bad_alloc, std::runtime_error) {
888 pause_on_end.set(false);
889 messages << "Pause-on-end is now OFF" << std::endl;
892 command::fnptr<> rewind_movie(lsnes_cmd, "rewind-movie", "Rewind movie to the beginning",
893 "Syntax: rewind-movie\nRewind movie to the beginning\n",
894 []() throw(std::bad_alloc, std::runtime_error) {
895 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
898 command::fnptr<> cancel_save(lsnes_cmd, "cancel-saves", "Cancel all pending saves", "Syntax: "
899 "cancel-save\nCancel pending saves\n",
900 []() throw(std::bad_alloc, std::runtime_error) {
901 queued_saves.clear();
902 messages << "Pending saves canceled." << std::endl;
905 command::fnptr<> flushslots(lsnes_cmd, "flush-slotinfo", "Flush slotinfo cache",
906 "Flush slotinfo cache\n",
907 []() throw(std::bad_alloc, std::runtime_error) {
908 flush_slotinfo();
911 command::fnptr<> mhold1(lsnes_cmd, "+hold-macro", "Hold macro (hold)",
912 "Hold macros enable\n", []() throw(std::bad_alloc, std::runtime_error) {
913 macro_hold_1 = true;
916 command::fnptr<> mhold2(lsnes_cmd, "-hold-macro", "Hold macro (hold)",
917 "Hold macros disable\n", []() throw(std::bad_alloc, std::runtime_error) {
918 macro_hold_1 = false;
921 command::fnptr<> mhold3(lsnes_cmd, "hold-macro", "Hold macro (toggle)",
922 "Hold macros toggle\n", []() throw(std::bad_alloc, std::runtime_error) {
923 macro_hold_2 = !macro_hold_2;
924 if(macro_hold_2)
925 messages << "Macros are held for next frame." << std::endl;
926 else
927 messages << "Macros are not held for next frame." << std::endl;
930 keyboard::invbind imhold1(lsnes_mapper, "+hold-macro", "Macro‣Hold all macros");
931 keyboard::invbind imhold2(lsnes_mapper, "hold-macro", "Macro‣Hold all macros (typed)");
932 keyboard::invbind ipause_emulator(lsnes_mapper, "pause-emulator", "Speed‣(Un)pause");
933 keyboard::invbind ijback(lsnes_mapper, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
934 keyboard::invbind ijforward(lsnes_mapper, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
935 keyboard::invbind iloadj(lsnes_mapper, "load-jukebox", "Load‣Selected slot");
936 keyboard::invbind iloadjrw(lsnes_mapper, "load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
937 keyboard::invbind iloadjro(lsnes_mapper, "load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
938 keyboard::invbind iloadjp(lsnes_mapper, "load-jukebox-preserve", "Load‣Selected slot (preserve input)");
939 keyboard::invbind iloadjm(lsnes_mapper, "load-jukebox-movie", "Load‣Selected slot (as movie)");
940 keyboard::invbind isavej(lsnes_mapper, "save-jukebox", "Save‣Selected slot");
941 keyboard::invbind iadvframe(lsnes_mapper, "+advance-frame", "Speed‣Advance frame");
942 keyboard::invbind iadvsubframe(lsnes_mapper, "+advance-poll", "Speed‣Advance subframe");
943 keyboard::invbind iskiplag(lsnes_mapper, "advance-skiplag", "Speed‣Advance poll");
944 keyboard::invbind ireset(lsnes_mapper, "reset", "System‣Reset");
945 keyboard::invbind iset_rwmode(lsnes_mapper, "set-rwmode", "Movie‣Switch to read/write");
946 keyboard::invbind itoggle_romode(lsnes_mapper, "set-romode", "Movie‣Switch to read-only");
947 keyboard::invbind itoggle_rwmode(lsnes_mapper, "toggle-rwmode", "Movie‣Toggle read-only");
948 keyboard::invbind irepaint(lsnes_mapper, "repaint", "System‣Repaint screen");
949 keyboard::invbind itogglepause(lsnes_mapper, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
950 keyboard::invbind irewind_movie(lsnes_mapper, "rewind-movie", "Movie‣Rewind movie");
951 keyboard::invbind icancel_saves(lsnes_mapper, "cancel-saves", "Save‣Cancel pending saves");
952 keyboard::invbind iload1(lsnes_mapper, "load $SLOT:1", "Load‣Slot 1");
953 keyboard::invbind iload2(lsnes_mapper, "load $SLOT:2", "Load‣Slot 2");
954 keyboard::invbind iload3(lsnes_mapper, "load $SLOT:3", "Load‣Slot 3");
955 keyboard::invbind iload4(lsnes_mapper, "load $SLOT:4", "Load‣Slot 4");
956 keyboard::invbind iload5(lsnes_mapper, "load $SLOT:5", "Load‣Slot 5");
957 keyboard::invbind iload6(lsnes_mapper, "load $SLOT:6", "Load‣Slot 6");
958 keyboard::invbind iload7(lsnes_mapper, "load $SLOT:7", "Load‣Slot 7");
959 keyboard::invbind iload8(lsnes_mapper, "load $SLOT:8", "Load‣Slot 8");
960 keyboard::invbind iload9(lsnes_mapper, "load $SLOT:9", "Load‣Slot 9");
961 keyboard::invbind iload10(lsnes_mapper, "load $SLOT:10", "Load‣Slot 10");
962 keyboard::invbind iload11(lsnes_mapper, "load $SLOT:11", "Load‣Slot 11");
963 keyboard::invbind iload12(lsnes_mapper, "load $SLOT:12", "Load‣Slot 12");
964 keyboard::invbind iload13(lsnes_mapper, "load $SLOT:13", "Load‣Slot 13");
965 keyboard::invbind iload14(lsnes_mapper, "load $SLOT:14", "Load‣Slot 14");
966 keyboard::invbind iload15(lsnes_mapper, "load $SLOT:15", "Load‣Slot 15");
967 keyboard::invbind iload16(lsnes_mapper, "load $SLOT:16", "Load‣Slot 16");
968 keyboard::invbind iload17(lsnes_mapper, "load $SLOT:17", "Load‣Slot 17");
969 keyboard::invbind iload18(lsnes_mapper, "load $SLOT:18", "Load‣Slot 18");
970 keyboard::invbind iload19(lsnes_mapper, "load $SLOT:19", "Load‣Slot 19");
971 keyboard::invbind iload20(lsnes_mapper, "load $SLOT:20", "Load‣Slot 20");
972 keyboard::invbind iload21(lsnes_mapper, "load $SLOT:21", "Load‣Slot 21");
973 keyboard::invbind iload22(lsnes_mapper, "load $SLOT:22", "Load‣Slot 22");
974 keyboard::invbind iload23(lsnes_mapper, "load $SLOT:23", "Load‣Slot 23");
975 keyboard::invbind iload24(lsnes_mapper, "load $SLOT:24", "Load‣Slot 24");
976 keyboard::invbind iload25(lsnes_mapper, "load $SLOT:25", "Load‣Slot 25");
977 keyboard::invbind iload26(lsnes_mapper, "load $SLOT:26", "Load‣Slot 26");
978 keyboard::invbind iload27(lsnes_mapper, "load $SLOT:27", "Load‣Slot 27");
979 keyboard::invbind iload28(lsnes_mapper, "load $SLOT:28", "Load‣Slot 28");
980 keyboard::invbind iload29(lsnes_mapper, "load $SLOT:29", "Load‣Slot 29");
981 keyboard::invbind iload30(lsnes_mapper, "load $SLOT:30", "Load‣Slot 30");
982 keyboard::invbind iload31(lsnes_mapper, "load $SLOT:31", "Load‣Slot 31");
983 keyboard::invbind iload32(lsnes_mapper, "load $SLOT:32", "Load‣Slot 32");
984 keyboard::invbind isave1(lsnes_mapper, "save-state $SLOT:1", "Save‣Slot 1");
985 keyboard::invbind isave2(lsnes_mapper, "save-state $SLOT:2", "Save‣Slot 2");
986 keyboard::invbind isave3(lsnes_mapper, "save-state $SLOT:3", "Save‣Slot 3");
987 keyboard::invbind isave4(lsnes_mapper, "save-state $SLOT:4", "Save‣Slot 4");
988 keyboard::invbind isave5(lsnes_mapper, "save-state $SLOT:5", "Save‣Slot 5");
989 keyboard::invbind isave6(lsnes_mapper, "save-state $SLOT:6", "Save‣Slot 6");
990 keyboard::invbind isave7(lsnes_mapper, "save-state $SLOT:7", "Save‣Slot 7");
991 keyboard::invbind isave8(lsnes_mapper, "save-state $SLOT:8", "Save‣Slot 8");
992 keyboard::invbind isave9(lsnes_mapper, "save-state $SLOT:9", "Save‣Slot 9");
993 keyboard::invbind isave10(lsnes_mapper, "save-state $SLOT:10", "Save‣Slot 10");
994 keyboard::invbind isave11(lsnes_mapper, "save-state $SLOT:11", "Save‣Slot 11");
995 keyboard::invbind isave12(lsnes_mapper, "save-state $SLOT:12", "Save‣Slot 12");
996 keyboard::invbind isave13(lsnes_mapper, "save-state $SLOT:13", "Save‣Slot 13");
997 keyboard::invbind isave14(lsnes_mapper, "save-state $SLOT:14", "Save‣Slot 14");
998 keyboard::invbind isave15(lsnes_mapper, "save-state $SLOT:15", "Save‣Slot 15");
999 keyboard::invbind isave16(lsnes_mapper, "save-state $SLOT:16", "Save‣Slot 16");
1000 keyboard::invbind isave17(lsnes_mapper, "save-state $SLOT:17", "Save‣Slot 17");
1001 keyboard::invbind isave18(lsnes_mapper, "save-state $SLOT:18", "Save‣Slot 18");
1002 keyboard::invbind isave19(lsnes_mapper, "save-state $SLOT:19", "Save‣Slot 19");
1003 keyboard::invbind isave20(lsnes_mapper, "save-state $SLOT:20", "Save‣Slot 20");
1004 keyboard::invbind isave21(lsnes_mapper, "save-state $SLOT:21", "Save‣Slot 21");
1005 keyboard::invbind isave22(lsnes_mapper, "save-state $SLOT:22", "Save‣Slot 22");
1006 keyboard::invbind isave23(lsnes_mapper, "save-state $SLOT:23", "Save‣Slot 23");
1007 keyboard::invbind isave24(lsnes_mapper, "save-state $SLOT:24", "Save‣Slot 24");
1008 keyboard::invbind isave25(lsnes_mapper, "save-state $SLOT:25", "Save‣Slot 25");
1009 keyboard::invbind isave26(lsnes_mapper, "save-state $SLOT:26", "Save‣Slot 26");
1010 keyboard::invbind isave27(lsnes_mapper, "save-state $SLOT:27", "Save‣Slot 27");
1011 keyboard::invbind isave28(lsnes_mapper, "save-state $SLOT:28", "Save‣Slot 28");
1012 keyboard::invbind isave29(lsnes_mapper, "save-state $SLOT:29", "Save‣Slot 29");
1013 keyboard::invbind isave30(lsnes_mapper, "save-state $SLOT:30", "Save‣Slot 30");
1014 keyboard::invbind isave31(lsnes_mapper, "save-state $SLOT:31", "Save‣Slot 31");
1015 keyboard::invbind isave32(lsnes_mapper, "save-state $SLOT:32", "Save‣Slot 32");
1016 keyboard::invbind islot1(lsnes_mapper, "set-jukebox-slot 1", "Slot select‣Slot 1");
1017 keyboard::invbind islot2(lsnes_mapper, "set-jukebox-slot 2", "Slot select‣Slot 2");
1018 keyboard::invbind islot3(lsnes_mapper, "set-jukebox-slot 3", "Slot select‣Slot 3");
1019 keyboard::invbind islot4(lsnes_mapper, "set-jukebox-slot 4", "Slot select‣Slot 4");
1020 keyboard::invbind islot5(lsnes_mapper, "set-jukebox-slot 5", "Slot select‣Slot 5");
1021 keyboard::invbind islot6(lsnes_mapper, "set-jukebox-slot 6", "Slot select‣Slot 6");
1022 keyboard::invbind islot7(lsnes_mapper, "set-jukebox-slot 7", "Slot select‣Slot 7");
1023 keyboard::invbind islot8(lsnes_mapper, "set-jukebox-slot 8", "Slot select‣Slot 8");
1024 keyboard::invbind islot9(lsnes_mapper, "set-jukebox-slot 9", "Slot select‣Slot 9");
1025 keyboard::invbind islot10(lsnes_mapper, "set-jukebox-slot 10", "Slot select‣Slot 10");
1026 keyboard::invbind islot11(lsnes_mapper, "set-jukebox-slot 11", "Slot select‣Slot 11");
1027 keyboard::invbind islot12(lsnes_mapper, "set-jukebox-slot 12", "Slot select‣Slot 12");
1028 keyboard::invbind islot13(lsnes_mapper, "set-jukebox-slot 13", "Slot select‣Slot 13");
1029 keyboard::invbind islot14(lsnes_mapper, "set-jukebox-slot 14", "Slot select‣Slot 14");
1030 keyboard::invbind islot15(lsnes_mapper, "set-jukebox-slot 15", "Slot select‣Slot 15");
1031 keyboard::invbind islot16(lsnes_mapper, "set-jukebox-slot 16", "Slot select‣Slot 16");
1032 keyboard::invbind islot17(lsnes_mapper, "set-jukebox-slot 17", "Slot select‣Slot 17");
1033 keyboard::invbind islot18(lsnes_mapper, "set-jukebox-slot 18", "Slot select‣Slot 18");
1034 keyboard::invbind islot19(lsnes_mapper, "set-jukebox-slot 19", "Slot select‣Slot 19");
1035 keyboard::invbind islot20(lsnes_mapper, "set-jukebox-slot 20", "Slot select‣Slot 20");
1036 keyboard::invbind islot21(lsnes_mapper, "set-jukebox-slot 21", "Slot select‣Slot 21");
1037 keyboard::invbind islot22(lsnes_mapper, "set-jukebox-slot 22", "Slot select‣Slot 22");
1038 keyboard::invbind islot23(lsnes_mapper, "set-jukebox-slot 23", "Slot select‣Slot 23");
1039 keyboard::invbind islot24(lsnes_mapper, "set-jukebox-slot 24", "Slot select‣Slot 24");
1040 keyboard::invbind islot25(lsnes_mapper, "set-jukebox-slot 25", "Slot select‣Slot 25");
1041 keyboard::invbind islot26(lsnes_mapper, "set-jukebox-slot 26", "Slot select‣Slot 26");
1042 keyboard::invbind islot27(lsnes_mapper, "set-jukebox-slot 27", "Slot select‣Slot 27");
1043 keyboard::invbind islot28(lsnes_mapper, "set-jukebox-slot 28", "Slot select‣Slot 28");
1044 keyboard::invbind islot29(lsnes_mapper, "set-jukebox-slot 29", "Slot select‣Slot 29");
1045 keyboard::invbind islot30(lsnes_mapper, "set-jukebox-slot 30", "Slot select‣Slot 30");
1046 keyboard::invbind islot31(lsnes_mapper, "set-jukebox-slot 31", "Slot select‣Slot 31");
1047 keyboard::invbind islot32(lsnes_mapper, "set-jukebox-slot 32", "Slot select‣Slot 32");
1049 bool on_quit_prompt = false;
1050 class mywindowcallbacks : public information_dispatch
1052 public:
1053 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks")
1055 closenotify.set(notify_close, [this]() {
1056 if(on_quit_prompt) {
1057 amode = ADVANCE_QUIT;
1058 platform::set_paused(false);
1059 platform::cancel_wait();
1060 return;
1062 on_quit_prompt = true;
1063 try {
1064 amode = ADVANCE_QUIT;
1065 platform::set_paused(false);
1066 platform::cancel_wait();
1067 } catch(...) {
1069 on_quit_prompt = false;
1072 ~mywindowcallbacks() throw() {}
1073 void on_new_dumper(const std::string& n)
1075 update_movie_state();
1077 void on_destroy_dumper(const std::string& n)
1079 update_movie_state();
1081 private:
1082 struct dispatch::target<> closenotify;
1083 } mywcb;
1085 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
1086 //failing.
1087 int handle_load()
1089 std::string old_project = CORE().mlogic ? CORE().mlogic.get_mfile().projectid : "";
1090 jumpback:
1091 if(do_unsafe_rewind && unsafe_rewind_obj) {
1092 if(!CORE().mlogic)
1093 return 0;
1094 uint64_t t = get_utime();
1095 std::vector<char> s;
1096 lua_callback_do_unsafe_rewind(s, 0, 0, CORE().mlogic.get_movie(), unsafe_rewind_obj);
1097 notify_mode_change(false);
1098 do_unsafe_rewind = false;
1099 CORE().mlogic.get_mfile().is_savestate = true;
1100 location_special = SPECIAL_SAVEPOINT;
1101 update_movie_state();
1102 messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
1103 return 1;
1105 if(pending_new_project != "") {
1106 std::string id = pending_new_project;
1107 pending_new_project = "";
1108 project_info* old = project_get();
1109 if(old && old->id == id)
1110 goto nothing_to_do;
1111 try {
1112 auto& p = project_load(id);
1113 project_set(&p);
1114 if(project_get() != old)
1115 delete old;
1116 flush_slotinfo(); //Wrong movie may be stale.
1117 return 1;
1118 } catch(std::exception& e) {
1119 platform::error_message(std::string("Can't switch projects: ") + e.what());
1120 messages << "Can't switch projects: " << e.what() << std::endl;
1121 goto nothing_to_do;
1123 nothing_to_do:
1124 amode = old_mode;
1125 platform::set_paused(amode == ADVANCE_PAUSE);
1126 platform::flush_command_queue();
1127 if(amode == ADVANCE_LOAD)
1128 goto jumpback;
1129 return 0;
1131 if(pending_load != "") {
1132 bool system_was_corrupt = system_corrupt;
1133 system_corrupt = false;
1134 try {
1135 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
1136 !do_load_state(pending_load, loadmode)) {
1137 if(system_was_corrupt)
1138 system_corrupt = system_was_corrupt;
1139 pending_load = "";
1140 return -1;
1142 if(loadmode == LOAD_STATE_BEGINNING)
1143 do_load_rewind();
1144 if(loadmode == LOAD_STATE_ROMRELOAD)
1145 do_load_rom();
1146 } catch(std::exception& e) {
1147 if(!system_corrupt && system_was_corrupt)
1148 system_corrupt = true;
1149 platform::error_message(std::string("Load failed: ") + e.what());
1150 messages << "Load failed: " << e.what() << std::endl;
1152 pending_load = "";
1153 amode = load_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1154 platform::set_paused(load_paused);
1155 load_paused = false;
1156 if(!system_corrupt) {
1157 location_special = SPECIAL_SAVEPOINT;
1158 update_movie_state();
1159 platform::flush_command_queue();
1160 if(amode == ADVANCE_QUIT)
1161 return -1;
1162 if(amode == ADVANCE_LOAD)
1163 goto jumpback;
1165 if(old_project != (CORE().mlogic ? CORE().mlogic.get_mfile().projectid : ""))
1166 flush_slotinfo(); //Wrong movie may be stale.
1167 return 1;
1169 return 0;
1172 //If there are pending saves, perform them.
1173 void handle_saves()
1175 if(!CORE().mlogic)
1176 return;
1177 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
1178 our_rom.rtype->runtosave();
1179 for(auto i : queued_saves) {
1180 do_save_state(i.first, i.second);
1181 int tmp = -1;
1182 flush_slotinfo(translate_name_mprefix(i.first, tmp, -1));
1184 if(do_unsafe_rewind && !unsafe_rewind_obj) {
1185 uint64_t t = get_utime();
1186 std::vector<char> s = our_rom.save_core_state(true);
1187 uint64_t secs = CORE().mlogic.get_mfile().rtc_second;
1188 uint64_t ssecs = CORE().mlogic.get_mfile().rtc_subsecond;
1189 lua_callback_do_unsafe_rewind(s, secs, ssecs, CORE().mlogic.get_movie(),
1190 NULL);
1191 do_unsafe_rewind = false;
1192 messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
1195 queued_saves.clear();
1198 bool handle_corrupt()
1200 if(!system_corrupt)
1201 return false;
1202 while(system_corrupt) {
1203 platform::set_paused(true);
1204 platform::flush_command_queue();
1205 handle_load();
1206 if(amode == ADVANCE_QUIT)
1207 return true;
1209 return true;
1213 void init_main_callbacks()
1215 ecore_callbacks = &lsnes_callbacks_obj;
1218 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
1219 std::runtime_error)
1221 platform::system_thread_available(true);
1222 //Basic initialization.
1223 dispatch_set_error_streams(&messages.getstream());
1224 emulation_thread = threads::this_id();
1225 jukebox_size_listener jlistener;
1226 CORE().commentary.init();
1227 init_special_screens();
1228 our_rom = rom;
1229 init_main_callbacks();
1230 initialize_all_builtin_c_cores();
1231 core_core::install_all_handlers();
1233 //Load our given movie.
1234 bool first_round = false;
1235 bool just_did_loadstate = false;
1236 bool used = false;
1237 try {
1238 do_load_state(initial, LOAD_STATE_INITIAL, used);
1239 location_special = SPECIAL_SAVEPOINT;
1240 update_movie_state();
1241 first_round = CORE().mlogic.get_mfile().is_savestate;
1242 just_did_loadstate = first_round;
1243 } catch(std::bad_alloc& e) {
1244 OOM_panic();
1245 } catch(std::exception& e) {
1246 if(!used)
1247 delete &initial;
1248 platform::error_message(std::string("Can't load initial state: ") + e.what());
1249 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
1250 if(load_has_to_succeed) {
1251 messages << "FATAL: Can't load movie" << std::endl;
1252 throw;
1254 system_corrupt = true;
1255 redraw_framebuffer(screen_corrupt);
1258 platform::set_paused(initial.start_paused);
1259 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1260 stop_at_frame_active = false;
1262 lua_run_startup_scripts();
1264 uint64_t time_x = get_utime();
1265 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
1266 if(handle_corrupt()) {
1267 first_round = CORE().mlogic && CORE().mlogic.get_mfile().is_savestate;
1268 just_did_loadstate = first_round;
1269 continue;
1271 ack_frame_tick(get_utime());
1272 if(amode == ADVANCE_SKIPLAG_PENDING)
1273 amode = ADVANCE_SKIPLAG;
1275 if(!first_round) {
1276 controls.reset_framehold();
1277 if(!macro_hold_1 && !macro_hold_2) {
1278 controls.advance_macros();
1280 macro_hold_2 = false;
1281 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(false);
1282 CORE().mlogic.new_frame_starting(amode == ADVANCE_SKIPLAG);
1283 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(true);
1284 if(amode == ADVANCE_QUIT && queued_saves.empty())
1285 break;
1286 handle_saves();
1287 int r = 0;
1288 if(queued_saves.empty())
1289 r = handle_load();
1290 if(r > 0 || system_corrupt) {
1291 CORE().mlogic.get_movie().get_pollcounters().set_framepflag(
1292 CORE().mlogic.get_mfile().is_savestate);
1293 first_round = CORE().mlogic.get_mfile().is_savestate;
1294 if(system_corrupt)
1295 amode = ADVANCE_PAUSE;
1296 else
1297 amode = old_mode;
1298 stop_at_frame_active = false;
1299 just_did_loadstate = first_round;
1300 controls.reset_framehold();
1301 debug_fire_callback_frame(CORE().mlogic.get_movie().get_current_frame(),
1302 true);
1303 continue;
1304 } else if(r < 0) {
1305 //Not exactly desriable, but this at least won't desync.
1306 stop_at_frame_active = false;
1307 if(amode == ADVANCE_QUIT)
1308 return;
1309 amode = ADVANCE_PAUSE;
1312 if(just_did_loadstate) {
1313 //If we just loadstated, we are up to date.
1314 if(amode == ADVANCE_QUIT)
1315 break;
1316 platform::set_paused(amode == ADVANCE_PAUSE);
1317 platform::flush_command_queue();
1318 //We already have done the reset this frame if we are going to do one at all.
1319 CORE().mlogic.get_movie().set_controls(CORE().mlogic.update_controls(true));
1320 CORE().mlogic.get_movie().set_all_DRDY();
1321 just_did_loadstate = false;
1323 frame_irq_time = get_utime() - time_x;
1324 debug_fire_callback_frame(CORE().mlogic.get_movie().get_current_frame(), false);
1325 our_rom.rtype->emulate();
1326 random_mix_timing_entropy();
1327 time_x = get_utime();
1328 if(amode == ADVANCE_AUTO)
1329 platform::wait(to_wait_frame(get_utime()));
1330 first_round = false;
1331 lua_callback_do_frame();
1333 information_dispatch::do_dump_end();
1334 core_core::uninstall_all_handlers();
1335 CORE().commentary.kill();
1336 platform::system_thread_available(false);
1337 //Kill some things to avoid crashes.
1338 debug_core_change();
1339 project_set(NULL, true);
1340 CORE().mwatch.clear_multi(CORE().mwatch.enumerate());
1343 void set_stop_at_frame(uint64_t frame)
1345 stop_at_frame = frame;
1346 stop_at_frame_active = (frame != 0);
1347 amode = ADVANCE_AUTO;
1348 platform::set_paused(false);
1351 void do_flush_slotinfo()
1353 flush_slotinfo();
1356 void switch_projects(const std::string& newproj)
1358 pending_new_project = newproj;
1359 amode = ADVANCE_LOAD;
1360 old_mode = ADVANCE_PAUSE;
1361 platform::cancel_wait();
1362 platform::set_paused(false);
1365 void load_new_rom(const romload_request& req)
1367 if(_load_new_rom(req)) {
1368 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1372 void reload_current_rom()
1374 if(reload_active_rom()) {
1375 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1379 void close_rom()
1381 if(load_null_rom()) {
1382 load_paused = true;
1383 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1387 void do_break_pause()
1389 break_pause = true;
1390 update_movie_state();
1391 while(break_pause) {
1392 platform::set_paused(true);
1393 platform::flush_command_queue();
1397 void convert_break_to_pause()
1399 if(break_pause) {
1400 amode = ADVANCE_PAUSE;
1401 break_pause = false;
1402 update_movie_state();