Improve randomness of get_random_hexstring (on I386)
[lsnes.git] / src / core / mainloop.cpp
bloba87ecb9eb572ae503c5115333c2d69f6900edf5a
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framebuffer.hpp"
7 #include "core/framerate.hpp"
8 #include "core/inthread.hpp"
9 #include "core/multitrack.hpp"
10 #include "lua/lua.hpp"
11 #include "library/string.hpp"
12 #include "core/mainloop.hpp"
13 #include "core/movie.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/moviefile.hpp"
16 #include "core/memorymanip.hpp"
17 #include "core/memorywatch.hpp"
18 #include "core/project.hpp"
19 #include "core/rom.hpp"
20 #include "core/romloader.hpp"
21 #include "core/rrdata.hpp"
22 #include "core/settings.hpp"
23 #include "core/window.hpp"
24 #include "interface/callbacks.hpp"
25 #include "interface/romtype.hpp"
26 #include "library/framebuffer.hpp"
27 #include "library/pixfmt-lrgb.hpp"
28 #include "library/zip.hpp"
30 #include <iomanip>
31 #include <cassert>
32 #include <sstream>
33 #include <iostream>
34 #include <limits>
35 #include <set>
36 #include <sys/time.h>
38 #define SPECIAL_FRAME_START 0
39 #define SPECIAL_FRAME_VIDEO 1
40 #define SPECIAL_SAVEPOINT 2
41 #define SPECIAL_NONE 3
43 void update_movie_state();
44 time_t random_seed_value = 0;
46 setting_var<setting_var_model_bool<setting_yes_no>> jukebox_dflt_binary(lsnes_vset, "jukebox-default-binary",
47 "Movie‣Saving‣Saveslots binary", true);
48 setting_var<setting_var_model_bool<setting_yes_no>> movie_dflt_binary(lsnes_vset, "movie-default-binary",
49 "Movie‣Saving‣Movies binary", false);
50 setting_var<setting_var_model_bool<setting_yes_no>> save_dflt_binary(lsnes_vset, "savestate-default-binary",
51 "Movie‣Saving‣Savestates binary", false);
53 namespace
55 setting_var<setting_var_model_int<0,999999>> advance_timeout_first(lsnes_vset, "advance-timeout",
56 "Delays‣First frame advance", 500);
57 setting_var<setting_var_model_int<0,999999>> advance_timeout_subframe(lsnes_vset, "advance-subframe-timeout",
58 "Delays‣Subframe advance", 100);
59 setting_var<setting_var_model_bool<setting_yes_no>> pause_on_end(lsnes_vset, "pause-on-end",
60 "Movie‣Pause on end", false);
61 setting_var<setting_var_model_int<0,999999999>> jukebox_size(lsnes_vset, "jukebox-size",
62 "Movie‣Number of save slots", 12);
64 enum advance_mode
66 ADVANCE_QUIT, //Quit the emulator.
67 ADVANCE_AUTO, //Normal (possibly slowed down play).
68 ADVANCE_LOAD, //Loading a state.
69 ADVANCE_FRAME, //Frame advance.
70 ADVANCE_SUBFRAME, //Subframe advance.
71 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
72 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
73 ADVANCE_PAUSE, //Unconditional pause.
76 //Our thread.
77 threadid_class emulation_thread;
78 //Flags related to repeating advance.
79 bool advanced_once;
80 bool cancel_advance;
81 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
82 enum advance_mode amode;
83 //Mode and filename of pending load, one of LOAD_* constants.
84 bool load_paused;
85 int loadmode;
86 std::string pending_load;
87 std::string pending_new_project;
88 //Queued saves (all savestates).
89 std::set<std::pair<std::string, int>> queued_saves;
90 //Save jukebox.
91 size_t save_jukebox_pointer;
92 //Special subframe location. One of SPECIAL_* constants.
93 int location_special;
94 //Last frame params.
95 bool last_hires = false;
96 bool last_interlace = false;
97 //Unsafe rewind.
98 bool do_unsafe_rewind = false;
99 void* unsafe_rewind_obj = NULL;
100 //Stop at frame.
101 bool stop_at_frame_active = false;
102 uint64_t stop_at_frame = 0;
103 //Macro hold.
104 bool macro_hold_1;
105 bool macro_hold_2;
107 enum advance_mode old_mode;
109 std::string save_jukebox_name(size_t i)
111 return (stringfmt() << "${project}" << (i + 1) << ".lsmv").str();
114 std::map<std::string, std::string> slotinfo_cache;
116 std::string vector_to_string(const std::vector<char>& x)
118 std::string y(x.begin(), x.end());
119 while(y.length() > 0 && y[y.length() - 1] < 32)
120 y = y.substr(0, y.length() - 1);
121 return y;
124 std::string get_slotinfo(const std::string& _filename)
126 std::string filename = resolve_relative_path(_filename);
127 if(!slotinfo_cache.count(filename)) {
128 std::ostringstream out;
129 try {
130 moviefile::brief_info info(filename);
131 if(our_movie.projectid == info.projectid)
132 out << info.rerecords << "R/" << info.current_frame << "F";
133 else
134 out << "Wrong movie";
135 } catch(...) {
136 out << "Nonexistent";
138 slotinfo_cache[filename] = out.str();
140 return slotinfo_cache[filename];
143 void flush_slotinfo(const std::string& filename)
145 slotinfo_cache.erase(resolve_relative_path(filename));
148 void flush_slotinfo()
150 slotinfo_cache.clear();
153 class _lsnes_pflag_handler : public movie::poll_flag
155 public:
156 ~_lsnes_pflag_handler()
159 int get_pflag()
161 return our_rom.rtype->get_pflag();
163 void set_pflag(int flag)
165 our_rom.rtype->set_pflag(flag);
167 } lsnes_pflag_handler;
170 void mainloop_signal_need_rewind(void* ptr)
172 if(ptr) {
173 old_mode = amode;
174 amode = ADVANCE_LOAD;
176 do_unsafe_rewind = true;
177 unsafe_rewind_obj = ptr;
180 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
182 if(lua_requests_subframe_paint)
183 redraw_framebuffer();
185 if(subframe) {
186 if(amode == ADVANCE_SUBFRAME) {
187 if(!cancel_advance) {
188 if(!advanced_once)
189 platform::wait(advance_timeout_first * 1000);
190 else
191 platform::wait(advance_timeout_subframe * 1000);
192 advanced_once = true;
194 if(cancel_advance) {
195 stop_at_frame_active = false;
196 amode = ADVANCE_PAUSE;
197 cancel_advance = false;
199 platform::set_paused(amode == ADVANCE_PAUSE);
200 } else if(amode == ADVANCE_FRAME) {
202 } else {
203 if(amode == ADVANCE_SKIPLAG) {
204 stop_at_frame_active = false;
205 amode = ADVANCE_PAUSE;
207 platform::set_paused(amode == ADVANCE_PAUSE);
208 cancel_advance = false;
210 location_special = SPECIAL_NONE;
211 update_movie_state();
212 } else {
213 if(amode == ADVANCE_SKIPLAG_PENDING)
214 amode = ADVANCE_SKIPLAG;
215 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
216 if(!cancel_advance) {
217 uint64_t wait = 0;
218 if(!advanced_once)
219 wait = advance_timeout_first * 1000;
220 else if(amode == ADVANCE_SUBFRAME)
221 wait = advance_timeout_subframe * 1000;
222 else
223 wait = to_wait_frame(get_utime());
224 platform::wait(wait);
225 advanced_once = true;
227 if(cancel_advance) {
228 stop_at_frame_active = false;
229 amode = ADVANCE_PAUSE;
230 cancel_advance = false;
232 platform::set_paused(amode == ADVANCE_PAUSE);
233 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end &&
234 !stop_at_frame_active) {
235 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count()) {
236 stop_at_frame_active = false;
237 amode = ADVANCE_PAUSE;
238 platform::set_paused(true);
240 } else if(amode == ADVANCE_AUTO && stop_at_frame_active) {
241 if(movb.get_movie().get_current_frame() >= stop_at_frame) {
242 stop_at_frame_active = false;
243 amode = ADVANCE_PAUSE;
244 platform::set_paused(true);
246 } else {
247 platform::set_paused((amode == ADVANCE_PAUSE));
248 cancel_advance = false;
250 location_special = SPECIAL_FRAME_START;
251 update_movie_state();
253 platform::flush_command_queue();
254 controller_frame tmp = controls.get(movb.get_movie().get_current_frame());
255 our_rom.rtype->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
256 lua_callback_do_input(tmp, subframe);
257 multitrack_editor.process_frame(tmp);
258 controls.commit(tmp);
259 return tmp;
262 namespace
265 //Do pending load (automatically unpauses).
266 void mark_pending_load(const std::string& filename, int lmode)
268 loadmode = lmode;
269 pending_load = filename;
270 old_mode = amode;
271 amode = ADVANCE_LOAD;
272 platform::cancel_wait();
273 platform::set_paused(false);
276 void mark_pending_save(const std::string& filename, int smode, int binary)
278 int tmp = -1;
279 if(smode == SAVE_MOVIE) {
280 //Just do this immediately.
281 do_save_movie(filename, binary);
282 flush_slotinfo(translate_name_mprefix(filename, tmp, false));
283 return;
285 if(location_special == SPECIAL_SAVEPOINT) {
286 //We can save immediately here.
287 do_save_state(filename, binary);
288 flush_slotinfo(translate_name_mprefix(filename, tmp, false));
289 return;
291 queued_saves.insert(std::make_pair(filename, binary));
292 messages << "Pending save on '" << filename << "'" << std::endl;
295 struct jukebox_size_listener : public setting_var_listener
297 jukebox_size_listener() { lsnes_vset.add_listener(*this); }
298 ~jukebox_size_listener() throw() {lsnes_vset.remove_listener(*this); };
299 void on_setting_change(setting_var_group& grp, const setting_var_base& val)
301 if(val.get_iname() == "jukebox-size") {
302 if(save_jukebox_pointer >= jukebox_size)
303 save_jukebox_pointer = 0;
305 update_movie_state();
310 void update_movie_state()
312 bool readonly = false;
313 static unsigned last_controllers = 0;
315 uint64_t magic[4];
316 our_rom.region->fill_framerate_magic(magic);
317 voice_frame_number(movb.get_movie().get_current_frame(), 1.0 * magic[1] / magic[0]);
319 auto& _status = platform::get_emustatus();
320 if(!system_corrupt) {
321 _status.set("!frame", (stringfmt() << movb.get_movie().get_current_frame()).str());
322 _status.set("!length", (stringfmt() << movb.get_movie().get_frame_count()).str());
323 _status.set("!lag", (stringfmt() << movb.get_movie().get_lag_frames()).str());
324 if(location_special == SPECIAL_FRAME_START)
325 _status.set("!subframe", "0");
326 else if(location_special == SPECIAL_SAVEPOINT)
327 _status.set("!subframe", "S");
328 else if(location_special == SPECIAL_FRAME_VIDEO)
329 _status.set("!subframe", "V");
330 else
331 _status.set("!subframe", (stringfmt() << movb.get_movie().next_poll_number()).str());
332 } else {
333 _status.set("!frame", "N/A");
334 _status.set("!length", "N/A");
335 _status.set("!lag", "N/A");
336 _status.set("!subframe", "N/A");
339 _status.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
340 auto& mo = movb.get_movie();
341 readonly = mo.readonly_mode();
342 if(system_corrupt)
343 _status.set("!mode", "C");
344 else if(!readonly)
345 _status.set("!mode", "R");
346 else if(mo.get_frame_count() >= mo.get_current_frame())
347 _status.set("!mode", "P");
348 else
349 _status.set("!mode", "F");
351 if(jukebox_size > 0) {
352 int tmp = -1;
353 std::string sfilen = translate_name_mprefix(save_jukebox_name(save_jukebox_pointer), tmp, false);
354 _status.set("!saveslot", (stringfmt() << (save_jukebox_pointer + 1)).str());
355 _status.set("!saveslotinfo", get_slotinfo(sfilen));
356 } else {
357 _status.erase("!saveslot");
358 _status.erase("!saveslotinfo");
360 _status.set("!speed", (stringfmt() << (unsigned)(100 * get_realized_multiplier() + 0.5)).str());
362 if(!system_corrupt) {
363 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
364 struct tm* time_decompose = gmtime(&timevalue);
365 char datebuffer[512];
366 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
367 _status.set("RTC", datebuffer);
368 } else {
369 _status.set("RTC", "N/A");
372 auto mset = controls.active_macro_set();
373 bool mfirst = true;
374 std::ostringstream mss;
375 for(auto i: mset) {
376 if(!mfirst) mss << ",";
377 mss << i;
378 mfirst = false;
380 _status.set("!macros", mss.str());
382 do_watch_memory();
384 controller_frame c;
385 if(!multitrack_editor.any_records())
386 c = movb.get_movie().get_controls();
387 else
388 c = controls.get_committed();
389 for(unsigned i = 0;; i++) {
390 auto pindex = controls.lcid_to_pcid(i);
391 if(pindex.first < 0 || !controls.is_present(pindex.first, pindex.second)) {
392 for(unsigned j = i; j < last_controllers; j++)
393 _status.erase((stringfmt() << "P" << (j + 1)).str());
394 last_controllers = i;
395 break;
397 char32_t buffer[MAX_DISPLAY_LENGTH];
398 c.display(pindex.first, pindex.second, buffer);
399 std::u32string _buffer = buffer;
400 if(readonly && multitrack_editor.is_enabled()) {
401 multitrack_edit::state st = multitrack_editor.get(pindex.first, pindex.second);
402 if(st == multitrack_edit::MT_PRESERVE)
403 _buffer += U" (keep)";
404 else if(st == multitrack_edit::MT_OVERWRITE)
405 _buffer += U" (rewrite)";
406 else if(st == multitrack_edit::MT_OR)
407 _buffer += U" (OR)";
408 else if(st == multitrack_edit::MT_XOR)
409 _buffer += U" (XOR)";
410 else
411 _buffer += U" (\?\?\?)";
413 _status.set((stringfmt() << "P" << (i + 1)).str(), _buffer);
415 notify_status_update();
418 uint64_t audio_irq_time;
419 uint64_t controller_irq_time;
420 uint64_t frame_irq_time;
423 struct lsnes_callbacks : public emucore_callbacks
425 public:
426 ~lsnes_callbacks() throw()
430 int16_t get_input(unsigned port, unsigned index, unsigned control)
432 int16_t x;
433 x = movb.input_poll(port, index, control);
434 lua_callback_snoop_input(port, index, control, x);
435 return x;
438 int16_t set_input(unsigned port, unsigned index, unsigned control, int16_t value)
440 if(!movb.get_movie().readonly_mode()) {
441 controller_frame f = movb.get_movie().get_controls();
442 f.axis3(port, index, control, value);
443 movb.get_movie().set_controls(f);
445 return movb.get_movie().next_input(port, index, control);
448 void notify_latch(std::list<std::string>& args)
450 lua_callback_do_latch(args);
453 void timer_tick(uint32_t increment, uint32_t per_second)
455 our_movie.rtc_subsecond += increment;
456 while(our_movie.rtc_subsecond >= per_second) {
457 our_movie.rtc_second++;
458 our_movie.rtc_subsecond -= per_second;
462 std::string get_firmware_path()
464 return lsnes_vset["firmwarepath"].str();
467 std::string get_base_path()
469 return our_rom.msu1_base;
472 time_t get_time()
474 return our_movie.rtc_second;
477 time_t get_randomseed()
479 return random_seed_value;
482 void output_frame(framebuffer_raw& screen, uint32_t fps_n, uint32_t fps_d)
484 lua_callback_do_frame_emulated();
485 location_special = SPECIAL_FRAME_VIDEO;
486 update_movie_state();
487 redraw_framebuffer(screen, false, true);
488 uint32_t g = gcd(fps_n, fps_d);
489 fps_n /= g;
490 fps_d /= g;
491 information_dispatch::do_frame(screen, fps_n, fps_d);
494 void action_state_updated()
496 graphics_driver_action_updated();
500 namespace
502 function_ptr_command<const std::string&> test4(lsnes_cmd, "test4", "test", "test",
503 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
504 std::list<std::string> _args;
505 std::string args2 = args;
506 while(args2 != "") {
507 std::string sym;
508 extract_token(args2, sym, " \t");
509 _args.push_back(sym);
511 lua_callback_do_latch(_args);
513 function_ptr_command<> count_rerecords(lsnes_cmd, "count-rerecords", "Count rerecords",
514 "Syntax: count-rerecords\nCounts rerecords.\n",
515 []() throw(std::bad_alloc, std::runtime_error) {
516 std::vector<char> tmp;
517 uint64_t x = rrdata.write(tmp);
518 messages << x << " rerecord(s)" << std::endl;
521 function_ptr_command<const std::string&> quit_emulator(lsnes_cmd, "quit-emulator", "Quit the emulator",
522 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
523 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
524 amode = ADVANCE_QUIT;
525 platform::set_paused(false);
526 platform::cancel_wait();
529 function_ptr_command<> unpause_emulator(lsnes_cmd, "unpause-emulator", "Unpause the emulator",
530 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
531 []() throw(std::bad_alloc, std::runtime_error) {
532 amode = ADVANCE_AUTO;
533 platform::set_paused(false);
534 platform::cancel_wait();
535 messages << "Unpaused" << std::endl;
538 function_ptr_command<> pause_emulator(lsnes_cmd, "pause-emulator", "(Un)pause the emulator",
539 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
540 []() throw(std::bad_alloc, std::runtime_error) {
541 if(amode != ADVANCE_AUTO) {
542 amode = ADVANCE_AUTO;
543 platform::set_paused(false);
544 platform::cancel_wait();
545 messages << "Unpaused" << std::endl;
546 } else {
547 platform::cancel_wait();
548 cancel_advance = false;
549 stop_at_frame_active = false;
550 amode = ADVANCE_PAUSE;
551 messages << "Paused" << std::endl;
555 function_ptr_command<> save_jukebox_prev(lsnes_cmd, "cycle-jukebox-backward", "Cycle save jukebox backwards",
556 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
557 []() throw(std::bad_alloc, std::runtime_error) {
558 if(jukebox_size == 0)
559 return;
560 if(save_jukebox_pointer == 0)
561 save_jukebox_pointer = jukebox_size - 1;
562 else
563 save_jukebox_pointer--;
564 if(save_jukebox_pointer >= jukebox_size)
565 save_jukebox_pointer = 0;
566 update_movie_state();
569 function_ptr_command<> save_jukebox_next(lsnes_cmd, "cycle-jukebox-forward", "Cycle save jukebox forwards",
570 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
571 []() throw(std::bad_alloc, std::runtime_error) {
572 if(jukebox_size == 0)
573 return;
574 if(save_jukebox_pointer >= jukebox_size - 1)
575 save_jukebox_pointer = 0;
576 else
577 save_jukebox_pointer++;
578 if(save_jukebox_pointer >= jukebox_size)
579 save_jukebox_pointer = 0;
580 update_movie_state();
583 function_ptr_command<> load_jukebox(lsnes_cmd, "load-jukebox", "Load save from jukebox",
584 "Syntax: load-jukebox\nLoad save from jukebox\n",
585 []() throw(std::bad_alloc, std::runtime_error) {
586 if(jukebox_size == 0)
587 throw std::runtime_error("No slot selected");
588 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_CURRENT);
591 function_ptr_command<> load_jukebox_readwrite(lsnes_cmd, "load-jukebox-readwrite", "Load save from jukebox in"
592 " read-write mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
593 []() throw(std::bad_alloc, std::runtime_error) {
594 if(jukebox_size == 0)
595 throw std::runtime_error("No slot selected");
596 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RW);
599 function_ptr_command<> load_jukebox_readonly(lsnes_cmd, "load-jukebox-readonly", "Load save from jukebox in "
600 "read-only mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
601 []() throw(std::bad_alloc, std::runtime_error) {
602 if(jukebox_size == 0)
603 throw std::runtime_error("No slot selected");
604 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_RO);
607 function_ptr_command<> load_jukebox_preserve(lsnes_cmd, "load-jukebox-preserve", "Load save from jukebox, "
608 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
609 []() throw(std::bad_alloc, std::runtime_error) {
610 if(jukebox_size == 0)
611 throw std::runtime_error("No slot selected");
612 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_PRESERVE);
615 function_ptr_command<> load_jukebox_movie(lsnes_cmd, "load-jukebox-movie", "Load save from jukebox as movie",
616 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
617 []() throw(std::bad_alloc, std::runtime_error) {
618 if(jukebox_size == 0)
619 throw std::runtime_error("No slot selected");
620 mark_pending_load(save_jukebox_name(save_jukebox_pointer), LOAD_STATE_MOVIE);
623 function_ptr_command<> save_jukebox_c(lsnes_cmd, "save-jukebox", "Save save to jukebox",
624 "Syntax: save-jukebox\nSave save to jukebox\n",
625 []() throw(std::bad_alloc, std::runtime_error) {
626 if(jukebox_size == 0)
627 throw std::runtime_error("No slot selected");
628 mark_pending_save(save_jukebox_name(save_jukebox_pointer), SAVE_STATE, -1);
631 function_ptr_command<> padvance_frame(lsnes_cmd, "+advance-frame", "Advance one frame",
632 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
633 []() throw(std::bad_alloc, std::runtime_error) {
634 amode = ADVANCE_FRAME;
635 cancel_advance = false;
636 advanced_once = false;
637 platform::cancel_wait();
638 platform::set_paused(false);
641 function_ptr_command<> nadvance_frame(lsnes_cmd, "-advance-frame", "Advance one frame",
642 "No help available\n",
643 []() throw(std::bad_alloc, std::runtime_error) {
644 cancel_advance = true;
645 platform::cancel_wait();
646 platform::set_paused(false);
649 function_ptr_command<> padvance_poll(lsnes_cmd, "+advance-poll", "Advance one subframe",
650 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
651 []() throw(std::bad_alloc, std::runtime_error) {
652 amode = ADVANCE_SUBFRAME;
653 cancel_advance = false;
654 advanced_once = false;
655 platform::cancel_wait();
656 platform::set_paused(false);
659 function_ptr_command<> nadvance_poll(lsnes_cmd, "-advance-poll", "Advance one subframe",
660 "No help available\n",
661 []() throw(std::bad_alloc, std::runtime_error) {
662 cancel_advance = true;
663 platform::cancel_wait();
664 platform::set_paused(false);
667 function_ptr_command<> advance_skiplag(lsnes_cmd, "advance-skiplag", "Skip to next poll",
668 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
669 []() throw(std::bad_alloc, std::runtime_error) {
670 amode = ADVANCE_SKIPLAG_PENDING;
671 platform::cancel_wait();
672 platform::set_paused(false);
675 function_ptr_command<> reset_c(lsnes_cmd, "reset", "Reset the system",
676 "Syntax: reset\nReset\nResets the system in beginning of the next frame.\n",
677 []() throw(std::bad_alloc, std::runtime_error) {
678 int sreset_action = our_rom.rtype->reset_action(false);
679 if(sreset_action < 0) {
680 platform::error_message("Core does not support resets");
681 messages << "Emulator core does not support resets" << std::endl;
682 return;
684 our_rom.rtype->execute_action(sreset_action, std::vector<interface_action_paramval>());
687 function_ptr_command<> hreset_c(lsnes_cmd, "reset-hard", "Reset the system",
688 "Syntax: reset-hard\nReset-hard\nHard resets the system in beginning of the next frame.\n",
689 []() throw(std::bad_alloc, std::runtime_error) {
690 int hreset_action = our_rom.rtype->reset_action(true);
691 if(hreset_action < 0) {
692 platform::error_message("Core does not support hard resets");
693 messages << "Emulator core does not support hard resets" << std::endl;
694 return;
696 our_rom.rtype->execute_action(hreset_action, std::vector<interface_action_paramval>());
699 function_ptr_command<arg_filename> load_c(lsnes_cmd, "load", "Load savestate (current mode)",
700 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
701 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
702 mark_pending_load(args, LOAD_STATE_CURRENT);
705 function_ptr_command<arg_filename> load_smart_c(lsnes_cmd, "load-smart", "Load savestate (heuristic mode)",
706 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
707 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
708 mark_pending_load(args, LOAD_STATE_DEFAULT);
711 function_ptr_command<arg_filename> load_state_c(lsnes_cmd, "load-state", "Load savestate (R/W)",
712 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
713 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
714 mark_pending_load(args, LOAD_STATE_RW);
717 function_ptr_command<arg_filename> load_readonly(lsnes_cmd, "load-readonly", "Load savestate (RO)",
718 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
719 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
720 mark_pending_load(args, LOAD_STATE_RO);
723 function_ptr_command<arg_filename> load_preserve(lsnes_cmd, "load-preserve", "Load savestate (preserve "
724 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
725 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
726 mark_pending_load(args, LOAD_STATE_PRESERVE);
729 function_ptr_command<arg_filename> load_movie_c(lsnes_cmd, "load-movie", "Load movie",
730 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
731 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
732 mark_pending_load(args, LOAD_STATE_MOVIE);
736 function_ptr_command<arg_filename> save_state(lsnes_cmd, "save-state", "Save state",
737 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
738 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
739 mark_pending_save(args, SAVE_STATE, -1);
742 function_ptr_command<arg_filename> save_state2(lsnes_cmd, "save-state-binary", "Save state (binary)",
743 "Syntax: save-state-binary <file>\nSaves binary state to <file>\n",
744 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
745 mark_pending_save(args, SAVE_STATE, 1);
748 function_ptr_command<arg_filename> save_state3(lsnes_cmd, "save-state-zip", "Save state (zip)",
749 "Syntax: save-state-zip <file>\nSaves zip state to <file>\n",
750 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
751 mark_pending_save(args, SAVE_STATE, 0);
754 function_ptr_command<arg_filename> save_movie(lsnes_cmd, "save-movie", "Save movie",
755 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
756 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
757 mark_pending_save(args, SAVE_MOVIE, -1);
760 function_ptr_command<arg_filename> save_movie2(lsnes_cmd, "save-movie-binary", "Save movie (binary)",
761 "Syntax: save-movie-binary <file>\nSaves binary movie to <file>\n",
762 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
763 mark_pending_save(args, SAVE_MOVIE, 1);
766 function_ptr_command<arg_filename> save_movie3(lsnes_cmd, "save-movie-zip", "Save movie (zip)",
767 "Syntax: save-movie-zip <file>\nSaves zip movie to <file>\n",
768 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
769 mark_pending_save(args, SAVE_MOVIE, 0);
772 function_ptr_command<> set_rwmode(lsnes_cmd, "set-rwmode", "Switch to read/write mode",
773 "Syntax: set-rwmode\nSwitches to read/write mode\n",
774 []() throw(std::bad_alloc, std::runtime_error) {
775 lua_callback_movie_lost("readwrite");
776 movb.get_movie().readonly_mode(false);
777 notify_mode_change(false);
778 lua_callback_do_readwrite();
779 update_movie_state();
782 function_ptr_command<> set_romode(lsnes_cmd, "set-romode", "Switch to read-only mode",
783 "Syntax: set-romode\nSwitches to read-only mode\n",
784 []() throw(std::bad_alloc, std::runtime_error) {
785 movb.get_movie().readonly_mode(true);
786 notify_mode_change(true);
787 update_movie_state();
790 function_ptr_command<> toggle_rwmode(lsnes_cmd, "toggle-rwmode", "Toggle read/write mode",
791 "Syntax: toggle-rwmode\nToggles read/write mode\n",
792 []() throw(std::bad_alloc, std::runtime_error) {
793 bool c = movb.get_movie().readonly_mode();
794 if(c)
795 lua_callback_movie_lost("readwrite");
796 movb.get_movie().readonly_mode(!c);
797 notify_mode_change(!c);
798 if(c)
799 lua_callback_do_readwrite();
800 update_movie_state();
803 function_ptr_command<> repaint(lsnes_cmd, "repaint", "Redraw the screen",
804 "Syntax: repaint\nRedraws the screen\n",
805 []() throw(std::bad_alloc, std::runtime_error) {
806 redraw_framebuffer();
809 function_ptr_command<> tpon(lsnes_cmd, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
810 []() throw(std::bad_alloc, std::runtime_error) {
811 bool tmp = pause_on_end;
812 pause_on_end.set(!tmp);
813 messages << "Pause-on-end is now " << (tmp ? "OFF" : "ON") << std::endl;
816 function_ptr_command<> spon(lsnes_cmd, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
817 []() throw(std::bad_alloc, std::runtime_error) {
818 pause_on_end.set(true);
819 messages << "Pause-on-end is now ON" << std::endl;
822 function_ptr_command<> cpon(lsnes_cmd, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
823 []() throw(std::bad_alloc, std::runtime_error) {
824 pause_on_end.set(false);
825 messages << "Pause-on-end is now OFF" << std::endl;
828 function_ptr_command<> rewind_movie(lsnes_cmd, "rewind-movie", "Rewind movie to the beginning",
829 "Syntax: rewind-movie\nRewind movie to the beginning\n",
830 []() throw(std::bad_alloc, std::runtime_error) {
831 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
834 function_ptr_command<> cancel_save(lsnes_cmd, "cancel-saves", "Cancel all pending saves", "Syntax: "
835 "cancel-save\nCancel pending saves\n",
836 []() throw(std::bad_alloc, std::runtime_error) {
837 queued_saves.clear();
838 messages << "Pending saves canceled." << std::endl;
841 function_ptr_command<> flushslots(lsnes_cmd, "flush-slotinfo", "Flush slotinfo cache",
842 "Flush slotinfo cache\n",
843 []() throw(std::bad_alloc, std::runtime_error) {
844 flush_slotinfo();
847 function_ptr_command<> mhold1(lsnes_cmd, "+hold-macro", "Hold macro (hold)",
848 "Hold macros enable\n", []() throw(std::bad_alloc, std::runtime_error) {
849 macro_hold_1 = true;
852 function_ptr_command<> mhold2(lsnes_cmd, "-hold-macro", "Hold macro (hold)",
853 "Hold macros disable\n", []() throw(std::bad_alloc, std::runtime_error) {
854 macro_hold_1 = false;
857 function_ptr_command<> mhold3(lsnes_cmd, "hold-macro", "Hold macro (toggle)",
858 "Hold macros toggle\n", []() throw(std::bad_alloc, std::runtime_error) {
859 macro_hold_2 = !macro_hold_2;
860 if(macro_hold_2)
861 messages << "Macros are held for next frame." << std::endl;
862 else
863 messages << "Macros are not held for next frame." << std::endl;
866 inverse_bind imhold1(lsnes_mapper, "+hold-macro", "Macro‣Hold all macros");
867 inverse_bind imhold2(lsnes_mapper, "hold-macro", "Macro‣Hold all macros (typed)");
868 inverse_bind ipause_emulator(lsnes_mapper, "pause-emulator", "Speed‣(Un)pause");
869 inverse_bind ijback(lsnes_mapper, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
870 inverse_bind ijforward(lsnes_mapper, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
871 inverse_bind iloadj(lsnes_mapper, "load-jukebox", "Load‣Selected slot");
872 inverse_bind iloadjrw(lsnes_mapper, "load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
873 inverse_bind iloadjro(lsnes_mapper, "load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
874 inverse_bind iloadjp(lsnes_mapper, "load-jukebox-preserve", "Load‣Selected slot (preserve input)");
875 inverse_bind iloadjm(lsnes_mapper, "load-jukebox-movie", "Load‣Selected slot (as movie)");
876 inverse_bind isavej(lsnes_mapper, "save-jukebox", "Save‣Selected slot");
877 inverse_bind iadvframe(lsnes_mapper, "+advance-frame", "Speed‣Advance frame");
878 inverse_bind iadvsubframe(lsnes_mapper, "+advance-poll", "Speed‣Advance subframe");
879 inverse_bind iskiplag(lsnes_mapper, "advance-skiplag", "Speed‣Advance poll");
880 inverse_bind ireset(lsnes_mapper, "reset", "System‣Reset");
881 inverse_bind iset_rwmode(lsnes_mapper, "set-rwmode", "Movie‣Switch to read/write");
882 inverse_bind itoggle_romode(lsnes_mapper, "set-romode", "Movie‣Switch to read-only");
883 inverse_bind itoggle_rwmode(lsnes_mapper, "toggle-rwmode", "Movie‣Toggle read-only");
884 inverse_bind irepaint(lsnes_mapper, "repaint", "System‣Repaint screen");
885 inverse_bind itogglepause(lsnes_mapper, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
886 inverse_bind irewind_movie(lsnes_mapper, "rewind-movie", "Movie‣Rewind movie");
887 inverse_bind icancel_saves(lsnes_mapper, "cancel-saves", "Save‣Cancel pending saves");
888 inverse_bind iload1(lsnes_mapper, "load ${project}1.lsmv", "Load‣Slot 1");
889 inverse_bind iload2(lsnes_mapper, "load ${project}2.lsmv", "Load‣Slot 2");
890 inverse_bind iload3(lsnes_mapper, "load ${project}3.lsmv", "Load‣Slot 3");
891 inverse_bind iload4(lsnes_mapper, "load ${project}4.lsmv", "Load‣Slot 4");
892 inverse_bind iload5(lsnes_mapper, "load ${project}5.lsmv", "Load‣Slot 5");
893 inverse_bind iload6(lsnes_mapper, "load ${project}6.lsmv", "Load‣Slot 6");
894 inverse_bind iload7(lsnes_mapper, "load ${project}7.lsmv", "Load‣Slot 7");
895 inverse_bind iload8(lsnes_mapper, "load ${project}8.lsmv", "Load‣Slot 8");
896 inverse_bind iload9(lsnes_mapper, "load ${project}9.lsmv", "Load‣Slot 9");
897 inverse_bind iload10(lsnes_mapper, "load ${project}10.lsmv", "Load‣Slot 10");
898 inverse_bind iload11(lsnes_mapper, "load ${project}11.lsmv", "Load‣Slot 11");
899 inverse_bind iload12(lsnes_mapper, "load ${project}12.lsmv", "Load‣Slot 12");
900 inverse_bind iload13(lsnes_mapper, "load ${project}13.lsmv", "Load‣Slot 13");
901 inverse_bind iload14(lsnes_mapper, "load ${project}14.lsmv", "Load‣Slot 14");
902 inverse_bind iload15(lsnes_mapper, "load ${project}15.lsmv", "Load‣Slot 15");
903 inverse_bind iload16(lsnes_mapper, "load ${project}16.lsmv", "Load‣Slot 16");
904 inverse_bind iload17(lsnes_mapper, "load ${project}17.lsmv", "Load‣Slot 17");
905 inverse_bind iload18(lsnes_mapper, "load ${project}18.lsmv", "Load‣Slot 18");
906 inverse_bind iload19(lsnes_mapper, "load ${project}19.lsmv", "Load‣Slot 19");
907 inverse_bind iload20(lsnes_mapper, "load ${project}20.lsmv", "Load‣Slot 20");
908 inverse_bind iload21(lsnes_mapper, "load ${project}21.lsmv", "Load‣Slot 21");
909 inverse_bind iload22(lsnes_mapper, "load ${project}22.lsmv", "Load‣Slot 22");
910 inverse_bind iload23(lsnes_mapper, "load ${project}23.lsmv", "Load‣Slot 23");
911 inverse_bind iload24(lsnes_mapper, "load ${project}24.lsmv", "Load‣Slot 24");
912 inverse_bind iload25(lsnes_mapper, "load ${project}25.lsmv", "Load‣Slot 25");
913 inverse_bind iload26(lsnes_mapper, "load ${project}26.lsmv", "Load‣Slot 26");
914 inverse_bind iload27(lsnes_mapper, "load ${project}27.lsmv", "Load‣Slot 27");
915 inverse_bind iload28(lsnes_mapper, "load ${project}28.lsmv", "Load‣Slot 28");
916 inverse_bind iload29(lsnes_mapper, "load ${project}29.lsmv", "Load‣Slot 29");
917 inverse_bind iload30(lsnes_mapper, "load ${project}30.lsmv", "Load‣Slot 30");
918 inverse_bind iload31(lsnes_mapper, "load ${project}31.lsmv", "Load‣Slot 31");
919 inverse_bind iload32(lsnes_mapper, "load ${project}32.lsmv", "Load‣Slot 32");
920 inverse_bind isave1(lsnes_mapper, "save-state ${project}1.lsmv", "Save‣Slot 1");
921 inverse_bind isave2(lsnes_mapper, "save-state ${project}2.lsmv", "Save‣Slot 2");
922 inverse_bind isave3(lsnes_mapper, "save-state ${project}3.lsmv", "Save‣Slot 3");
923 inverse_bind isave4(lsnes_mapper, "save-state ${project}4.lsmv", "Save‣Slot 4");
924 inverse_bind isave5(lsnes_mapper, "save-state ${project}5.lsmv", "Save‣Slot 5");
925 inverse_bind isave6(lsnes_mapper, "save-state ${project}6.lsmv", "Save‣Slot 6");
926 inverse_bind isave7(lsnes_mapper, "save-state ${project}7.lsmv", "Save‣Slot 7");
927 inverse_bind isave8(lsnes_mapper, "save-state ${project}8.lsmv", "Save‣Slot 8");
928 inverse_bind isave9(lsnes_mapper, "save-state ${project}9.lsmv", "Save‣Slot 9");
929 inverse_bind isave10(lsnes_mapper, "save-state ${project}10.lsmv", "Save‣Slot 10");
930 inverse_bind isave11(lsnes_mapper, "save-state ${project}11.lsmv", "Save‣Slot 11");
931 inverse_bind isave12(lsnes_mapper, "save-state ${project}12.lsmv", "Save‣Slot 12");
932 inverse_bind isave13(lsnes_mapper, "save-state ${project}13.lsmv", "Save‣Slot 13");
933 inverse_bind isave14(lsnes_mapper, "save-state ${project}14.lsmv", "Save‣Slot 14");
934 inverse_bind isave15(lsnes_mapper, "save-state ${project}15.lsmv", "Save‣Slot 15");
935 inverse_bind isave16(lsnes_mapper, "save-state ${project}16.lsmv", "Save‣Slot 16");
936 inverse_bind isave17(lsnes_mapper, "save-state ${project}17.lsmv", "Save‣Slot 17");
937 inverse_bind isave18(lsnes_mapper, "save-state ${project}18.lsmv", "Save‣Slot 18");
938 inverse_bind isave19(lsnes_mapper, "save-state ${project}19.lsmv", "Save‣Slot 19");
939 inverse_bind isave20(lsnes_mapper, "save-state ${project}20.lsmv", "Save‣Slot 20");
940 inverse_bind isave21(lsnes_mapper, "save-state ${project}21.lsmv", "Save‣Slot 21");
941 inverse_bind isave22(lsnes_mapper, "save-state ${project}22.lsmv", "Save‣Slot 22");
942 inverse_bind isave23(lsnes_mapper, "save-state ${project}23.lsmv", "Save‣Slot 23");
943 inverse_bind isave24(lsnes_mapper, "save-state ${project}24.lsmv", "Save‣Slot 24");
944 inverse_bind isave25(lsnes_mapper, "save-state ${project}25.lsmv", "Save‣Slot 25");
945 inverse_bind isave26(lsnes_mapper, "save-state ${project}26.lsmv", "Save‣Slot 26");
946 inverse_bind isave27(lsnes_mapper, "save-state ${project}27.lsmv", "Save‣Slot 27");
947 inverse_bind isave28(lsnes_mapper, "save-state ${project}28.lsmv", "Save‣Slot 28");
948 inverse_bind isave29(lsnes_mapper, "save-state ${project}29.lsmv", "Save‣Slot 29");
949 inverse_bind isave30(lsnes_mapper, "save-state ${project}30.lsmv", "Save‣Slot 30");
950 inverse_bind isave31(lsnes_mapper, "save-state ${project}31.lsmv", "Save‣Slot 31");
951 inverse_bind isave32(lsnes_mapper, "save-state ${project}32.lsmv", "Save‣Slot 32");
953 bool on_quit_prompt = false;
954 class mywindowcallbacks : public information_dispatch
956 public:
957 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks")
959 closenotify.set(notify_close, [this]() {
960 if(on_quit_prompt) {
961 amode = ADVANCE_QUIT;
962 platform::set_paused(false);
963 platform::cancel_wait();
964 return;
966 on_quit_prompt = true;
967 try {
968 amode = ADVANCE_QUIT;
969 platform::set_paused(false);
970 platform::cancel_wait();
971 } catch(...) {
973 on_quit_prompt = false;
976 ~mywindowcallbacks() throw() {}
977 void on_new_dumper(const std::string& n)
979 update_movie_state();
981 void on_destroy_dumper(const std::string& n)
983 update_movie_state();
985 private:
986 struct dispatch_target<> closenotify;
987 } mywcb;
989 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
990 //failing.
991 int handle_load()
993 std::string old_project = our_movie.projectid;
994 jumpback:
995 if(do_unsafe_rewind && unsafe_rewind_obj) {
996 uint64_t t = get_utime();
997 std::vector<char> s;
998 lua_callback_do_unsafe_rewind(s, 0, 0, movb.get_movie(), unsafe_rewind_obj);
999 notify_mode_change(false);
1000 do_unsafe_rewind = false;
1001 our_movie.is_savestate = true;
1002 location_special = SPECIAL_SAVEPOINT;
1003 update_movie_state();
1004 messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
1005 return 1;
1007 if(pending_new_project != "") {
1008 std::string id = pending_new_project;
1009 pending_new_project = "";
1010 project_info* old = project_get();
1011 if(old && old->id == id)
1012 goto nothing_to_do;
1013 try {
1014 auto& p = project_load(id);
1015 project_set(&p);
1016 if(project_get() != old)
1017 delete old;
1018 flush_slotinfo(); //Wrong movie may be stale.
1019 return 1;
1020 } catch(std::exception& e) {
1021 platform::error_message(std::string("Can't switch projects: ") + e.what());
1022 messages << "Can't switch projects: " << e.what() << std::endl;
1023 goto nothing_to_do;
1025 nothing_to_do:
1026 amode = old_mode;
1027 platform::set_paused(amode == ADVANCE_PAUSE);
1028 platform::flush_command_queue();
1029 if(amode == ADVANCE_LOAD)
1030 goto jumpback;
1031 return 0;
1033 if(pending_load != "") {
1034 system_corrupt = false;
1035 if(loadmode != LOAD_STATE_BEGINNING && loadmode != LOAD_STATE_ROMRELOAD &&
1036 !do_load_state(pending_load, loadmode)) {
1037 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
1038 pending_load = "";
1039 return -1;
1041 try {
1042 if(loadmode == LOAD_STATE_BEGINNING)
1043 do_load_beginning(false);
1044 if(loadmode == LOAD_STATE_ROMRELOAD)
1045 do_load_beginning(true);
1046 } catch(std::exception& e) {
1047 platform::error_message(std::string("Load failed: ") + e.what());
1048 messages << "Load failed: " << e.what() << std::endl;
1050 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
1051 pending_load = "";
1052 amode = load_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1053 platform::set_paused(load_paused);
1054 load_paused = false;
1055 if(!system_corrupt) {
1056 location_special = SPECIAL_SAVEPOINT;
1057 update_movie_state();
1058 platform::flush_command_queue();
1059 if(amode == ADVANCE_QUIT)
1060 return -1;
1061 if(amode == ADVANCE_LOAD)
1062 goto jumpback;
1064 if(old_project != our_movie.projectid)
1065 flush_slotinfo(); //Wrong movie may be stale.
1066 return 1;
1068 return 0;
1071 //If there are pending saves, perform them.
1072 void handle_saves()
1074 if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
1075 our_rom.rtype->runtosave();
1076 for(auto i : queued_saves) {
1077 do_save_state(i.first, i.second);
1078 int tmp = -1;
1079 flush_slotinfo(translate_name_mprefix(i.first, tmp, false));
1081 if(do_unsafe_rewind && !unsafe_rewind_obj) {
1082 uint64_t t = get_utime();
1083 std::vector<char> s = our_rom.save_core_state(true);
1084 uint64_t secs = our_movie.rtc_second;
1085 uint64_t ssecs = our_movie.rtc_subsecond;
1086 lua_callback_do_unsafe_rewind(s, secs, ssecs, movb.get_movie(), NULL);
1087 do_unsafe_rewind = false;
1088 messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
1091 queued_saves.clear();
1094 bool handle_corrupt()
1096 if(!system_corrupt)
1097 return false;
1098 while(system_corrupt) {
1099 platform::set_paused(true);
1100 platform::flush_command_queue();
1101 handle_load();
1102 if(amode == ADVANCE_QUIT)
1103 return true;
1105 return true;
1109 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
1110 std::runtime_error)
1112 platform::system_thread_available(true);
1113 //Basic initialization.
1114 dispatch_set_error_streams(&messages.getstream());
1115 emulation_thread = this_thread_id();
1116 jukebox_size_listener jlistener;
1117 voicethread_task();
1118 init_special_screens();
1119 our_rom = rom;
1120 lsnes_callbacks lsnes_callbacks_obj;
1121 ecore_callbacks = &lsnes_callbacks_obj;
1122 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
1123 core_core::install_all_handlers();
1125 //Load our given movie.
1126 bool first_round = false;
1127 bool just_did_loadstate = false;
1128 try {
1129 do_load_state(initial, LOAD_STATE_INITIAL);
1130 location_special = SPECIAL_SAVEPOINT;
1131 update_movie_state();
1132 first_round = our_movie.is_savestate;
1133 just_did_loadstate = first_round;
1134 } catch(std::bad_alloc& e) {
1135 OOM_panic();
1136 } catch(std::exception& e) {
1137 platform::error_message(std::string("Can't load initial state: ") + e.what());
1138 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
1139 if(load_has_to_succeed) {
1140 messages << "FATAL: Can't load movie" << std::endl;
1141 platform::fatal_error();
1143 system_corrupt = true;
1144 update_movie_state();
1145 redraw_framebuffer(screen_corrupt);
1148 movb.get_movie().set_pflag_handler(&lsnes_pflag_handler);
1149 lua_callback_startup();
1151 platform::set_paused(initial.start_paused);
1152 amode = initial.start_paused ? ADVANCE_PAUSE : ADVANCE_AUTO;
1153 stop_at_frame_active = false;
1154 uint64_t time_x = get_utime();
1155 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
1156 if(handle_corrupt()) {
1157 first_round = our_movie.is_savestate;
1158 just_did_loadstate = first_round;
1159 continue;
1161 ack_frame_tick(get_utime());
1162 if(amode == ADVANCE_SKIPLAG_PENDING)
1163 amode = ADVANCE_SKIPLAG;
1165 if(!first_round) {
1166 controls.reset_framehold();
1167 movb.get_movie().get_pollcounters().set_framepflag(false);
1168 movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
1169 movb.get_movie().get_pollcounters().set_framepflag(true);
1170 if(!macro_hold_1 && !macro_hold_2) {
1171 controls.advance_macros();
1173 macro_hold_2 = false;
1174 if(amode == ADVANCE_QUIT && queued_saves.empty())
1175 break;
1176 handle_saves();
1177 int r = 0;
1178 if(queued_saves.empty())
1179 r = handle_load();
1180 if(r > 0 || system_corrupt) {
1181 movb.get_movie().get_pollcounters().set_framepflag(our_movie.is_savestate);
1182 first_round = our_movie.is_savestate;
1183 if(system_corrupt)
1184 amode = ADVANCE_PAUSE;
1185 else
1186 amode = old_mode;
1187 stop_at_frame_active = false;
1188 just_did_loadstate = first_round;
1189 controls.reset_framehold();
1190 continue;
1191 } else if(r < 0) {
1192 //Not exactly desriable, but this at least won't desync.
1193 stop_at_frame_active = false;
1194 if(amode == ADVANCE_QUIT)
1195 return;
1196 amode = ADVANCE_PAUSE;
1199 if(just_did_loadstate) {
1200 //If we just loadstated, we are up to date.
1201 if(amode == ADVANCE_QUIT)
1202 break;
1203 platform::set_paused(amode == ADVANCE_PAUSE);
1204 platform::flush_command_queue();
1205 //We already have done the reset this frame if we are going to do one at all.
1206 movb.get_movie().set_controls(movb.update_controls(true));
1207 movb.get_movie().set_all_DRDY();
1208 just_did_loadstate = false;
1210 frame_irq_time = get_utime() - time_x;
1211 our_rom.rtype->emulate();
1212 random_mix_timing_entropy();
1213 time_x = get_utime();
1214 if(amode == ADVANCE_AUTO)
1215 platform::wait(to_wait_frame(get_utime()));
1216 first_round = false;
1217 lua_callback_do_frame();
1219 information_dispatch::do_dump_end();
1220 core_core::uninstall_all_handlers();
1221 voicethread_kill();
1222 platform::system_thread_available(false);
1225 void set_stop_at_frame(uint64_t frame)
1227 stop_at_frame = frame;
1228 stop_at_frame_active = (frame != 0);
1229 amode = ADVANCE_AUTO;
1230 platform::set_paused(false);
1233 void do_flush_slotinfo()
1235 flush_slotinfo();
1238 void switch_projects(const std::string& newproj)
1240 pending_new_project = newproj;
1241 amode = ADVANCE_LOAD;
1242 old_mode = ADVANCE_PAUSE;
1243 platform::cancel_wait();
1244 platform::set_paused(false);
1247 void load_new_rom(const romload_request& req)
1249 if(_load_new_rom(req)) {
1250 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1254 void reload_current_rom()
1256 if(reload_active_rom()) {
1257 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);
1261 void close_rom()
1263 if(load_null_rom()) {
1264 load_paused = true;
1265 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD);