Configurable movie/ROM paths
[lsnes.git] / src / core / mainloop.cpp
blob5e25512206e485d228f6d88761ebbc7a7cddc15e
1 #include "core/bsnes.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/lua.hpp"
9 #include "core/mainloop.hpp"
10 #include "core/movie.hpp"
11 #include "core/moviedata.hpp"
12 #include "core/moviefile.hpp"
13 #include "core/memorymanip.hpp"
14 #include "core/memorywatch.hpp"
15 #include "core/render.hpp"
16 #include "core/rom.hpp"
17 #include "core/rrdata.hpp"
18 #include "core/settings.hpp"
19 #include "core/window.hpp"
21 #include <iomanip>
22 #include <cassert>
23 #include <sstream>
24 #include <iostream>
25 #include <set>
26 #include <sys/time.h>
28 #define SPECIAL_FRAME_START 0
29 #define SPECIAL_FRAME_VIDEO 1
30 #define SPECIAL_SAVEPOINT 2
31 #define SPECIAL_NONE 3
33 void update_movie_state();
34 time_t random_seed_value = 0;
36 namespace
38 enum advance_mode
40 ADVANCE_QUIT, //Quit the emulator.
41 ADVANCE_AUTO, //Normal (possibly slowed down play).
42 ADVANCE_LOAD, //Loading a state.
43 ADVANCE_FRAME, //Frame advance.
44 ADVANCE_SUBFRAME, //Subframe advance.
45 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
46 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
47 ADVANCE_PAUSE, //Unconditional pause.
50 //Previous mouse mask.
51 int prev_mouse_mask = 0;
52 //Flags related to repeating advance.
53 bool advanced_once;
54 bool cancel_advance;
55 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
56 enum advance_mode amode;
57 //Mode and filename of pending load, one of LOAD_* constants.
58 int loadmode;
59 std::string pending_load;
60 //Queued saves (all savestates).
61 std::set<std::string> queued_saves;
62 bool stepping_into_save;
63 //Save jukebox.
64 std::vector<std::string> save_jukebox;
65 size_t save_jukebox_pointer;
66 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
67 long pending_reset_cycles = -1;
68 //Set by every video refresh.
69 bool video_refresh_done;
70 //Special subframe location. One of SPECIAL_* constants.
71 int location_special;
72 //Few settings.
73 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
74 boolean_setting pause_on_end("pause-on-end", false);
75 //Last frame params.
76 bool last_hires = false;
77 bool last_interlace = false;
80 class firmware_path_setting : public setting
82 public:
83 firmware_path_setting() : setting("firmwarepath") { _firmwarepath = "."; default_firmware = true; }
84 void blank() throw(std::bad_alloc, std::runtime_error)
86 _firmwarepath = ".";
87 default_firmware = true;
90 bool is_set() throw()
92 return !default_firmware;
95 void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
97 if(value != "") {
98 _firmwarepath = value;
99 default_firmware = false;
100 } else
101 blank();
104 std::string get() throw(std::bad_alloc)
106 return _firmwarepath;
109 operator std::string() throw(std::bad_alloc)
111 return _firmwarepath;
113 private:
114 std::string _firmwarepath;
115 bool default_firmware;
116 } firmwarepath_setting;
118 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
120 if(lua_requests_subframe_paint)
121 redraw_framebuffer();
123 if(subframe) {
124 if(amode == ADVANCE_SUBFRAME) {
125 if(!cancel_advance && !advanced_once) {
126 platform::wait(advance_timeout_first * 1000);
127 advanced_once = true;
129 if(cancel_advance) {
130 amode = ADVANCE_PAUSE;
131 cancel_advance = false;
133 platform::set_paused(amode == ADVANCE_PAUSE);
134 } else if(amode == ADVANCE_FRAME) {
136 } else {
137 if(amode == ADVANCE_SKIPLAG)
138 amode = ADVANCE_PAUSE;
139 platform::set_paused(amode == ADVANCE_PAUSE);
140 cancel_advance = false;
142 if(amode == ADVANCE_SKIPLAG)
143 amode = ADVANCE_AUTO;
144 location_special = SPECIAL_NONE;
145 update_movie_state();
146 } else {
147 if(amode == ADVANCE_SKIPLAG_PENDING)
148 amode = ADVANCE_SKIPLAG;
149 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
150 if(!cancel_advance) {
151 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
152 (advance_timeout_first * 1000));
153 advanced_once = true;
155 if(cancel_advance) {
156 amode = ADVANCE_PAUSE;
157 cancel_advance = false;
159 platform::set_paused(amode == ADVANCE_PAUSE);
160 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end) {
161 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count() + 1) {
162 amode = ADVANCE_PAUSE;
163 platform::set_paused(true);
165 } else {
166 platform::set_paused((amode == ADVANCE_PAUSE));
167 cancel_advance = false;
169 location_special = SPECIAL_FRAME_START;
170 update_movie_state();
173 information_dispatch::do_status_update();
174 platform::flush_command_queue();
175 if(!subframe && pending_reset_cycles >= 0)
176 controls.reset(pending_reset_cycles);
177 else if(!subframe)
178 controls.reset(-1);
179 controller_frame tmp = controls.commit(movb.get_movie().get_current_frame());
180 lua_callback_do_input(tmp, subframe);
181 return tmp;
184 namespace
186 enum advance_mode old_mode;
188 //Do pending load (automatically unpauses).
189 void mark_pending_load(const std::string& filename, int lmode)
191 loadmode = lmode;
192 pending_load = filename;
193 old_mode = amode;
194 amode = ADVANCE_LOAD;
195 platform::cancel_wait();
196 platform::set_paused(false);
199 void mark_pending_save(const std::string& filename, int smode)
201 if(smode == SAVE_MOVIE) {
202 //Just do this immediately.
203 do_save_movie(filename);
204 return;
206 queued_saves.insert(filename);
207 messages << "Pending save on '" << filename << "'" << std::endl;
210 uint32_t lpalette[0x80000];
211 void init_palette()
213 static bool palette_init = false;
214 if(palette_init)
215 return;
216 palette_init = true;
217 for(unsigned i = 0; i < 0x80000; i++) {
218 unsigned l = (i >> 15) & 0xF;
219 unsigned r = (i >> 0) & 0x1F;
220 unsigned g = (i >> 5) & 0x1F;
221 unsigned b = (i >> 10) & 0x1F;
222 double _l = static_cast<double>(l);
223 double m = 17.0 / 31.0;
224 r = floor(m * r * _l + 0.5);
225 g = floor(m * g * _l + 0.5);
226 b = floor(m * b * _l + 0.5);
227 lpalette[i] = r * 65536 + g * 256 + b;
232 void update_movie_state()
234 auto& _status = platform::get_emustatus();
235 if(!system_corrupt) {
236 std::ostringstream x;
237 x << movb.get_movie().get_current_frame() << "(";
238 if(location_special == SPECIAL_FRAME_START)
239 x << "0";
240 else if(location_special == SPECIAL_SAVEPOINT)
241 x << "S";
242 else if(location_special == SPECIAL_FRAME_VIDEO)
243 x << "V";
244 else
245 x << movb.get_movie().next_poll_number();
246 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
247 _status.set("Frame", x.str());
248 } else
249 _status.set("Frame", "N/A");
250 if(!system_corrupt) {
251 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
252 struct tm* time_decompose = gmtime(&timevalue);
253 char datebuffer[512];
254 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
255 _status.set("RTC", datebuffer);
256 } else {
257 _status.set("RTC", "N/A");
260 std::ostringstream x;
261 auto& mo = movb.get_movie();
262 x << (information_dispatch::get_dumper_count() ? "D" : "-");
263 x << (last_hires ? "H" : "-");
264 x << (last_interlace ? "I" : "-");
265 if(system_corrupt)
266 x << "C";
267 else if(!mo.readonly_mode())
268 x << "R";
269 else if(mo.get_frame_count() >= mo.get_current_frame())
270 x << "P";
271 else
272 x << "F";
273 _status.set("Flags", x.str());
275 if(save_jukebox.size() > 0)
276 _status.set("Saveslot", translate_name_mprefix(save_jukebox[save_jukebox_pointer]));
277 else
278 _status.erase("Saveslot");
280 std::ostringstream x;
281 x << get_framerate();
282 _status.set("SPD%", x.str());
284 do_watch_memory();
286 controller_frame c;
287 if(movb.get_movie().readonly_mode())
288 c = movb.get_movie().get_controls();
289 else
290 c = controls.get_committed();
291 for(unsigned i = 0; i < 8; i++) {
292 unsigned pindex = controls.lcid_to_pcid(i);
293 devicetype_t dtype = controls.pcid_to_type(pindex);
294 if(dtype == DT_NONE)
295 continue;
296 char buffer[MAX_DISPLAY_LENGTH];
297 c.display(pindex, buffer);
298 char y[3] = {'P', 0, 0};
299 y[1] = 49 + i;
300 _status.set(y, buffer);
304 uint64_t audio_irq_time;
305 uint64_t controller_irq_time;
306 uint64_t frame_irq_time;
309 class my_interface : public SNES::Interface
311 string path(SNES::Cartridge::Slot slot, const string &hint)
313 const char* _hint = hint;
314 std::string _hint2 = _hint;
315 std::string fwp = firmwarepath_setting;
316 std::string finalpath = fwp + "/" + _hint2;
317 return finalpath.c_str();
320 time_t currentTime()
322 return our_movie.rtc_second;
325 time_t randomSeed()
327 return random_seed_value;
330 void videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan)
332 // uint64_t time_x = get_utime();
333 last_hires = hires;
334 last_interlace = interlace;
335 init_palette();
336 if(stepping_into_save)
337 messages << "Got video refresh in runtosave, expect desyncs!" << std::endl;
338 video_refresh_done = true;
339 lua_callback_do_frame_emulated();
340 bool region = (SNES::system.region() == SNES::System::Region::PAL);
341 information_dispatch::do_raw_frame(data, hires, interlace, overscan, region ? VIDEO_REGION_PAL :
342 VIDEO_REGION_NTSC);
343 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
344 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
345 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
346 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
347 lcscreen ls(data, hires, interlace, overscan, region);
348 location_special = SPECIAL_FRAME_VIDEO;
349 update_movie_state();
350 redraw_framebuffer(ls, false, true);
351 uint32_t fps_n, fps_d;
352 uint32_t fclocks;
353 if(region)
354 fclocks = interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
355 else
356 fclocks = interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
357 fps_n = SNES::system.cpu_frequency();
358 fps_d = fclocks;
359 uint32_t g = gcd(fps_n, fps_d);
360 fps_n /= g;
361 fps_d /= g;
362 information_dispatch::do_frame(ls, fps_n, fps_d);
363 // time_x = get_utime() - time_x;
364 // std::cerr << "IRQ TIMINGS (microseconds): "
365 // << "V: " << time_x << " "
366 // << "A: " << audio_irq_time << " "
367 // << "C: " << controller_irq_time << " "
368 // << "F: " << frame_irq_time << " "
369 // << "Total: " << (time_x + audio_irq_time + controller_irq_time + frame_irq_time) << std::endl;
370 audio_irq_time = controller_irq_time = 0;
373 void audioSample(int16_t l_sample, int16_t r_sample)
375 // uint64_t time_x = get_utime();
376 uint16_t _l = l_sample;
377 uint16_t _r = r_sample;
378 platform::audio_sample(_l + 32768, _r + 32768);
379 information_dispatch::do_sample(l_sample, r_sample);
380 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
381 our_movie.rtc_subsecond += 768;
382 while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
383 our_movie.rtc_second++;
384 our_movie.rtc_subsecond -= SNES::system.apu_frequency();
386 // audio_irq_time += get_utime() - time_x;
389 int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
391 // uint64_t time_x = get_utime();
392 int16_t x;
393 x = movb.input_poll(port, index, id);
394 lua_callback_snoop_input(port ? 1 : 0, index, id, x);
395 // controller_irq_time += get_utime() - time_x;
396 return x;
400 namespace
402 function_ptr_command<> count_rerecords("count-rerecords", "Count rerecords",
403 "Syntax: count-rerecords\nCounts rerecords.\n",
404 []() throw(std::bad_alloc, std::runtime_error) {
405 std::vector<char> tmp;
406 uint64_t x = rrdata::write(tmp);
407 messages << x << " rerecord(s)" << std::endl;
410 function_ptr_command<const std::string&> quit_emulator("quit-emulator", "Quit the emulator",
411 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
412 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
413 amode = ADVANCE_QUIT;
414 platform::set_paused(false);
415 platform::cancel_wait();
418 function_ptr_command<> pause_emulator("pause-emulator", "(Un)pause the emulator",
419 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
420 []() throw(std::bad_alloc, std::runtime_error) {
421 if(amode != ADVANCE_AUTO) {
422 amode = ADVANCE_AUTO;
423 platform::set_paused(false);
424 platform::cancel_wait();
425 messages << "Unpaused" << std::endl;
426 } else {
427 platform::cancel_wait();
428 cancel_advance = false;
429 amode = ADVANCE_PAUSE;
430 messages << "Paused" << std::endl;
434 function_ptr_command<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
435 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
436 []() throw(std::bad_alloc, std::runtime_error) {
437 if(save_jukebox_pointer == 0)
438 save_jukebox_pointer = save_jukebox.size() - 1;
439 else
440 save_jukebox_pointer--;
441 if(save_jukebox_pointer >= save_jukebox.size())
442 save_jukebox_pointer = 0;
443 update_movie_state();
444 information_dispatch::do_status_update();
447 function_ptr_command<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
448 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
449 []() throw(std::bad_alloc, std::runtime_error) {
450 if(save_jukebox_pointer == save_jukebox.size() - 1)
451 save_jukebox_pointer = 0;
452 else
453 save_jukebox_pointer++;
454 if(save_jukebox_pointer >= save_jukebox.size())
455 save_jukebox_pointer = 0;
456 update_movie_state();
457 information_dispatch::do_status_update();
460 function_ptr_command<arg_filename> add_jukebox("add-jukebox-save", "Add save to jukebox",
461 "Syntax: add-jukebox-save\nAdd save to jukebox\n",
462 [](arg_filename filename) throw(std::bad_alloc, std::runtime_error) {
463 save_jukebox.push_back(filename);
464 update_movie_state();
465 information_dispatch::do_status_update();
468 function_ptr_command<> load_jukebox("load-jukebox", "Load save from jukebox",
469 "Syntax: load-jukebox\nLoad save from jukebox\n",
470 []() throw(std::bad_alloc, std::runtime_error) {
471 if(!save_jukebox.size())
472 throw std::runtime_error("No saves in jukebox");
473 mark_pending_load(save_jukebox[save_jukebox_pointer], LOAD_STATE_CURRENT);
476 function_ptr_command<> save_jukebox_c("save-jukebox", "Save save to jukebox",
477 "Syntax: save-jukebox\nSave save to jukebox\n",
478 []() throw(std::bad_alloc, std::runtime_error) {
479 if(!save_jukebox.size())
480 throw std::runtime_error("No saves in jukebox");
481 mark_pending_save(save_jukebox[save_jukebox_pointer], SAVE_STATE);
484 function_ptr_command<> padvance_frame("+advance-frame", "Advance one frame",
485 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
486 []() throw(std::bad_alloc, std::runtime_error) {
487 amode = ADVANCE_FRAME;
488 cancel_advance = false;
489 advanced_once = false;
490 platform::cancel_wait();
491 platform::set_paused(false);
494 function_ptr_command<> nadvance_frame("-advance-frame", "Advance one frame",
495 "No help available\n",
496 []() throw(std::bad_alloc, std::runtime_error) {
497 cancel_advance = true;
498 platform::cancel_wait();
499 platform::set_paused(false);
502 function_ptr_command<> padvance_poll("+advance-poll", "Advance one subframe",
503 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
504 []() throw(std::bad_alloc, std::runtime_error) {
505 amode = ADVANCE_SUBFRAME;
506 cancel_advance = false;
507 advanced_once = false;
508 platform::cancel_wait();
509 platform::set_paused(false);
512 function_ptr_command<> nadvance_poll("-advance-poll", "Advance one subframe",
513 "No help available\n",
514 []() throw(std::bad_alloc, std::runtime_error) {
515 cancel_advance = true;
516 platform::cancel_wait();
517 platform::set_paused(false);
520 function_ptr_command<> advance_skiplag("advance-skiplag", "Skip to next poll",
521 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
522 []() throw(std::bad_alloc, std::runtime_error) {
523 amode = ADVANCE_SKIPLAG;
524 platform::cancel_wait();
525 platform::set_paused(false);
528 function_ptr_command<> reset_c("reset", "Reset the SNES",
529 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
530 []() throw(std::bad_alloc, std::runtime_error) {
531 pending_reset_cycles = 0;
534 function_ptr_command<arg_filename> load_c("load", "Load savestate (current mode)",
535 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
536 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
537 mark_pending_load(args, LOAD_STATE_CURRENT);
540 function_ptr_command<arg_filename> load_state_c("load-state", "Load savestate (R/W)",
541 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
542 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
543 mark_pending_load(args, LOAD_STATE_RW);
546 function_ptr_command<arg_filename> load_readonly("load-readonly", "Load savestate (RO)",
547 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
548 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
549 mark_pending_load(args, LOAD_STATE_RO);
552 function_ptr_command<arg_filename> load_preserve("load-preserve", "Load savestate (preserve input)",
553 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
554 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
555 mark_pending_load(args, LOAD_STATE_PRESERVE);
558 function_ptr_command<arg_filename> load_movie_c("load-movie", "Load movie",
559 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
560 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
561 mark_pending_load(args, LOAD_STATE_MOVIE);
565 function_ptr_command<arg_filename> save_state("save-state", "Save state",
566 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
567 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
568 mark_pending_save(args, SAVE_STATE);
571 function_ptr_command<arg_filename> save_movie("save-movie", "Save movie",
572 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
573 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
574 mark_pending_save(args, SAVE_MOVIE);
577 function_ptr_command<> set_rwmode("set-rwmode", "Switch to read/write mode",
578 "Syntax: set-rwmode\nSwitches to read/write mode\n",
579 []() throw(std::bad_alloc, std::runtime_error) {
580 movb.get_movie().readonly_mode(false);
581 information_dispatch::do_mode_change(false);
582 lua_callback_do_readwrite();
583 update_movie_state();
584 information_dispatch::do_status_update();
587 function_ptr_command<> set_romode("set-romode", "Switch to read-only mode",
588 "Syntax: set-romode\nSwitches to read-only mode\n",
589 []() throw(std::bad_alloc, std::runtime_error) {
590 movb.get_movie().readonly_mode(true);
591 information_dispatch::do_mode_change(true);
592 update_movie_state();
593 information_dispatch::do_status_update();
596 function_ptr_command<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
597 "Syntax: toggle-rwmode\nToggles read/write mode\n",
598 []() throw(std::bad_alloc, std::runtime_error) {
599 bool c = movb.get_movie().readonly_mode();
600 movb.get_movie().readonly_mode(!c);
601 information_dispatch::do_mode_change(!c);
602 if(c)
603 lua_callback_do_readwrite();
604 update_movie_state();
605 information_dispatch::do_status_update();
608 function_ptr_command<> repaint("repaint", "Redraw the screen",
609 "Syntax: repaint\nRedraws the screen\n",
610 []() throw(std::bad_alloc, std::runtime_error) {
611 redraw_framebuffer();
614 function_ptr_command<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
615 []() throw(std::bad_alloc, std::runtime_error) {
616 bool newstate = !static_cast<bool>(pause_on_end);
617 pause_on_end.set(newstate ? "1" : "0");
618 messages << "Pause-on-end is now " << (newstate ? "ON" : "OFF") << std::endl;
621 function_ptr_command<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
622 "Syntax: rewind-movie\nRewind movie to the beginning\n",
623 []() throw(std::bad_alloc, std::runtime_error) {
624 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING);
627 function_ptr_command<> cancel_save("cancel-saves", "Cancel all pending saves", "Syntax: cancel-save\n"
628 "Cancel pending saves\n",
629 []() throw(std::bad_alloc, std::runtime_error) {
630 queued_saves.clear();
631 messages << "Pending saves canceled." << std::endl;
634 function_ptr_command<> test1("test-1", "no description available", "No help available\n",
635 []() throw(std::bad_alloc, std::runtime_error) {
636 redraw_framebuffer(screen_nosignal);
639 function_ptr_command<> test2("test-2", "no description available", "No help available\n",
640 []() throw(std::bad_alloc, std::runtime_error) {
641 redraw_framebuffer(screen_corrupt);
644 function_ptr_command<> test3("test-3", "no description available", "No help available\n",
645 []() throw(std::bad_alloc, std::runtime_error) {
646 while(1);
650 inverse_key ipause_emulator("pause-emulator", "(Un)pause");
651 inverse_key ijback("cycle-jukebox-backward", "Cycle slot backwards");
652 inverse_key ijforward("cycle-jukebox-forward", "Cycle slot forwards");
653 inverse_key iloadj("load-jukebox", "load selected slot");
654 inverse_key isavej("save-jukebox", "Save selected slot");
655 inverse_key iadvframe("+advance-frame", "Advance frame");
656 inverse_key iadvsubframe("+advance-poll", "Advance subframe");
657 inverse_key iskiplag("advance-skiplag", "Advance to next poll");
658 inverse_key ireset("reset", "System reset");
659 inverse_key iset_rwmode("set-rwmode", "Switch to read/write");
660 inverse_key itoggle_romode("set-romode", "Switch to read-only");
661 inverse_key itoggle_rwmode("toggle-rwmode", "Toggle read-only");
662 inverse_key irepaint("repaint", "Repaint screen");
663 inverse_key itogglepause("toggle-pause-on-end", "Toggle pause-on-end");
664 inverse_key irewind_movie("rewind-movie", "Rewind movie");
665 inverse_key icancel_saves("cancel-saves", "Cancel pending saves");
666 inverse_key iload1("load ${project}1.lsmv", "Load slot 1");
667 inverse_key iload2("load ${project}2.lsmv", "Load slot 2");
668 inverse_key iload3("load ${project}3.lsmv", "Load slot 3");
669 inverse_key iload4("load ${project}4.lsmv", "Load slot 4");
670 inverse_key iload5("load ${project}5.lsmv", "Load slot 5");
671 inverse_key iload6("load ${project}6.lsmv", "Load slot 6");
672 inverse_key iload7("load ${project}7.lsmv", "Load slot 7");
673 inverse_key iload8("load ${project}8.lsmv", "Load slot 8");
674 inverse_key iload9("load ${project}9.lsmv", "Load slot 9");
675 inverse_key iload10("load ${project}10.lsmv", "Load slot 10");
676 inverse_key iload11("load ${project}11.lsmv", "Load slot 11");
677 inverse_key iload12("load ${project}12.lsmv", "Load slot 12");
678 inverse_key iload13("load ${project}13.lsmv", "Load slot 13");
679 inverse_key iload14("load ${project}14.lsmv", "Load slot 14");
680 inverse_key iload15("load ${project}15.lsmv", "Load slot 15");
681 inverse_key iload16("load ${project}16.lsmv", "Load slot 16");
682 inverse_key iload17("load ${project}17.lsmv", "Load slot 17");
683 inverse_key iload18("load ${project}18.lsmv", "Load slot 18");
684 inverse_key iload19("load ${project}19.lsmv", "Load slot 19");
685 inverse_key iload20("load ${project}20.lsmv", "Load slot 20");
686 inverse_key iload21("load ${project}21.lsmv", "Load slot 21");
687 inverse_key iload22("load ${project}22.lsmv", "Load slot 22");
688 inverse_key iload23("load ${project}23.lsmv", "Load slot 23");
689 inverse_key iload24("load ${project}24.lsmv", "Load slot 24");
690 inverse_key iload25("load ${project}25.lsmv", "Load slot 25");
691 inverse_key iload26("load ${project}26.lsmv", "Load slot 26");
692 inverse_key iload27("load ${project}27.lsmv", "Load slot 27");
693 inverse_key iload28("load ${project}28.lsmv", "Load slot 28");
694 inverse_key iload29("load ${project}29.lsmv", "Load slot 29");
695 inverse_key iload30("load ${project}30.lsmv", "Load slot 30");
696 inverse_key iload31("load ${project}31.lsmv", "Load slot 31");
697 inverse_key iload32("load ${project}32.lsmv", "Load slot 32");
698 inverse_key isave1("save-state ${project}1.lsmv", "Save slot 1");
699 inverse_key isave2("save-state ${project}2.lsmv", "Save slot 2");
700 inverse_key isave3("save-state ${project}3.lsmv", "Save slot 3");
701 inverse_key isave4("save-state ${project}4.lsmv", "Save slot 4");
702 inverse_key isave5("save-state ${project}5.lsmv", "Save slot 5");
703 inverse_key isave6("save-state ${project}6.lsmv", "Save slot 6");
704 inverse_key isave7("save-state ${project}7.lsmv", "Save slot 7");
705 inverse_key isave8("save-state ${project}8.lsmv", "Save slot 8");
706 inverse_key isave9("save-state ${project}9.lsmv", "Save slot 9");
707 inverse_key isave10("save-state ${project}10.lsmv", "Save slot 10");
708 inverse_key isave11("save-state ${project}11.lsmv", "Save slot 11");
709 inverse_key isave12("save-state ${project}12.lsmv", "Save slot 12");
710 inverse_key isave13("save-state ${project}13.lsmv", "Save slot 13");
711 inverse_key isave14("save-state ${project}14.lsmv", "Save slot 14");
712 inverse_key isave15("save-state ${project}15.lsmv", "Save slot 15");
713 inverse_key isave16("save-state ${project}16.lsmv", "Save slot 16");
714 inverse_key isave17("save-state ${project}17.lsmv", "Save slot 17");
715 inverse_key isave18("save-state ${project}18.lsmv", "Save slot 18");
716 inverse_key isave19("save-state ${project}19.lsmv", "Save slot 19");
717 inverse_key isave20("save-state ${project}20.lsmv", "Save slot 20");
718 inverse_key isave21("save-state ${project}21.lsmv", "Save slot 21");
719 inverse_key isave22("save-state ${project}22.lsmv", "Save slot 22");
720 inverse_key isave23("save-state ${project}23.lsmv", "Save slot 23");
721 inverse_key isave24("save-state ${project}24.lsmv", "Save slot 24");
722 inverse_key isave25("save-state ${project}25.lsmv", "Save slot 25");
723 inverse_key isave26("save-state ${project}26.lsmv", "Save slot 26");
724 inverse_key isave27("save-state ${project}27.lsmv", "Save slot 27");
725 inverse_key isave28("save-state ${project}28.lsmv", "Save slot 28");
726 inverse_key isave29("save-state ${project}29.lsmv", "Save slot 29");
727 inverse_key isave30("save-state ${project}30.lsmv", "Save slot 30");
728 inverse_key isave31("save-state ${project}31.lsmv", "Save slot 31");
729 inverse_key isave32("save-state ${project}32.lsmv", "Save slot 32");
731 bool on_quit_prompt = false;
732 class mywindowcallbacks : public information_dispatch
734 public:
735 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
736 void on_new_dumper(const std::string& n)
738 update_movie_state();
740 void on_destroy_dumper(const std::string& n)
742 update_movie_state();
744 void on_close() throw()
746 if(on_quit_prompt) {
747 amode = ADVANCE_QUIT;
748 platform::set_paused(false);
749 platform::cancel_wait();
750 return;
752 on_quit_prompt = true;
753 try {
754 amode = ADVANCE_QUIT;
755 platform::set_paused(false);
756 platform::cancel_wait();
757 } catch(...) {
759 on_quit_prompt = false;
761 } mywcb;
763 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
764 //failing.
765 int handle_load()
767 if(pending_load != "") {
768 system_corrupt = false;
769 if(loadmode != LOAD_STATE_BEGINNING && !do_load_state(pending_load, loadmode)) {
770 pending_load = "";
771 return -1;
773 if(loadmode == LOAD_STATE_BEGINNING)
774 do_load_beginning();
775 pending_load = "";
776 pending_reset_cycles = -1;
777 amode = ADVANCE_AUTO;
778 platform::cancel_wait();
779 platform::set_paused(false);
780 if(!system_corrupt) {
781 location_special = SPECIAL_SAVEPOINT;
782 update_movie_state();
783 information_dispatch::do_status_update();
784 platform::flush_command_queue();
786 return 1;
788 return 0;
791 //If there are pending saves, perform them.
792 void handle_saves()
794 if(!queued_saves.empty()) {
795 stepping_into_save = true;
796 SNES::system.runtosave();
797 stepping_into_save = false;
798 for(auto i : queued_saves)
799 do_save_state(i);
801 queued_saves.clear();
804 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
805 bool handle_reset(long cycles)
807 if(cycles < 0)
808 return true;
809 video_refresh_done = false;
810 if(cycles == 0)
811 messages << "SNES reset" << std::endl;
812 else if(cycles > 0) {
813 messages << "SNES delayed reset not implemented (doing immediate reset)" << std::endl;
814 /* ... This code is just too buggy.
815 long cycles_executed = 0;
816 messages << "Executing delayed reset... This can take some time!" << std::endl;
817 while(cycles_executed < cycles && !video_refresh_done) {
818 //Poll inputs once in a while to prevent activating watchdog.
819 if(cycles_executed % 100 == 0)
820 platform::flush_command_queue();
821 SNES::cpu.op_step();
822 cycles_executed++;
824 if(!video_refresh_done)
825 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
826 else
827 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
830 SNES::system.reset();
831 lua_callback_do_reset();
832 redraw_framebuffer(screen_nosignal);
833 if(video_refresh_done) {
834 to_wait_frame(get_utime());
835 return false;
837 return true;
840 bool handle_corrupt()
842 if(!system_corrupt)
843 return false;
844 while(system_corrupt) {
845 platform::cancel_wait();
846 platform::set_paused(true);
847 platform::flush_command_queue();
848 handle_load();
849 if(amode == ADVANCE_QUIT)
850 return true;
852 return true;
856 std::vector<std::string> get_jukebox_names()
858 return save_jukebox;
861 void set_jukebox_names(const std::vector<std::string>& newj)
863 save_jukebox = newj;
864 if(save_jukebox_pointer >= save_jukebox.size())
865 save_jukebox_pointer = 0;
866 update_movie_state();
869 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
870 std::runtime_error)
872 //Basic initialization.
873 init_special_screens();
874 our_rom = &rom;
875 my_interface intrf;
876 auto old_inteface = SNES::interface;
877 SNES::interface = &intrf;
878 SNES::system.init();
880 //Load our given movie.
881 bool first_round = false;
882 bool just_did_loadstate = false;
883 try {
884 do_load_state(initial, LOAD_STATE_DEFAULT);
885 location_special = SPECIAL_SAVEPOINT;
886 update_movie_state();
887 first_round = our_movie.is_savestate;
888 just_did_loadstate = first_round;
889 } catch(std::bad_alloc& e) {
890 OOM_panic();
891 } catch(std::exception& e) {
892 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
893 if(load_has_to_succeed) {
894 messages << "FATAL: Can't load movie" << std::endl;
895 platform::fatal_error();
897 system_corrupt = true;
898 update_movie_state();
899 redraw_framebuffer(screen_corrupt);
902 lua_callback_startup();
904 platform::set_paused(false);
905 amode = ADVANCE_AUTO;
906 uint64_t time_x = get_utime();
907 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
908 if(handle_corrupt()) {
909 first_round = our_movie.is_savestate;
910 just_did_loadstate = first_round;
911 continue;
913 long resetcycles = -1;
914 ack_frame_tick(get_utime());
915 if(amode == ADVANCE_SKIPLAG_PENDING)
916 amode = ADVANCE_SKIPLAG;
918 if(!first_round) {
919 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
920 if(amode == ADVANCE_QUIT && queued_saves.empty())
921 break;
922 bool delayed_reset = (resetcycles > 0);
923 pending_reset_cycles = -1;
924 if(!handle_reset(resetcycles)) {
925 continue;
927 if(!delayed_reset) {
928 handle_saves();
930 int r = 0;
931 if(queued_saves.empty())
932 r = handle_load();
933 if(r > 0 || system_corrupt) {
934 first_round = our_movie.is_savestate;
935 if(system_corrupt)
936 amode = ADVANCE_PAUSE;
937 else
938 amode = old_mode;
939 just_did_loadstate = first_round;
940 continue;
941 } else if(r < 0) {
942 //Not exactly desriable, but this at least won't desync.
943 amode = ADVANCE_PAUSE;
946 if(just_did_loadstate) {
947 //If we just loadstated, we are up to date.
948 if(amode == ADVANCE_QUIT)
949 break;
950 platform::cancel_wait();
951 platform::set_paused(amode == ADVANCE_PAUSE);
952 platform::flush_command_queue();
953 //We already have done the reset this frame if we are going to do one at all.
954 movb.get_movie().set_controls(movb.update_controls(true));
955 movb.get_movie().set_all_DRDY();
956 just_did_loadstate = false;
958 frame_irq_time = get_utime() - time_x;
959 SNES::system.run();
960 time_x = get_utime();
961 if(amode == ADVANCE_AUTO)
962 platform::wait(to_wait_frame(get_utime()));
963 first_round = false;
964 lua_callback_do_frame();
966 information_dispatch::do_dump_end();
967 SNES::interface = old_inteface;