When showing input in readwrite mode, show last sent input
[lsnes.git] / src / core / mainloop.cpp
blobbaa87b4d29058d2a1f2b300725c6e27dd63952e8
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
5 #include "core/command.hpp"
6 #include "core/controller.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/framebuffer.hpp"
9 #include "core/framerate.hpp"
10 #include "core/lua.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/movie.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/moviefile.hpp"
15 #include "core/memorymanip.hpp"
16 #include "core/memorywatch.hpp"
17 #include "core/render.hpp"
18 #include "core/rom.hpp"
19 #include "core/rrdata.hpp"
20 #include "core/settings.hpp"
21 #include "core/window.hpp"
23 #include <iomanip>
24 #include <cassert>
25 #include <sstream>
26 #include <iostream>
27 #include <set>
28 #include <sys/time.h>
30 #define SPECIAL_FRAME_START 0
31 #define SPECIAL_FRAME_VIDEO 1
32 #define SPECIAL_SAVEPOINT 2
33 #define SPECIAL_NONE 3
35 void update_movie_state();
36 time_t random_seed_value = 0;
38 namespace
40 enum advance_mode
42 ADVANCE_QUIT, //Quit the emulator.
43 ADVANCE_AUTO, //Normal (possibly slowed down play).
44 ADVANCE_LOAD, //Loading a state.
45 ADVANCE_FRAME, //Frame advance.
46 ADVANCE_SUBFRAME, //Subframe advance.
47 ADVANCE_SKIPLAG, //Skip lag (oneshot, reverts to normal).
48 ADVANCE_SKIPLAG_PENDING, //Activate skip lag mode at next frame.
49 ADVANCE_PAUSE, //Unconditional pause.
52 //Previous mouse mask.
53 int prev_mouse_mask = 0;
54 //Flags related to repeating advance.
55 bool advanced_once;
56 bool cancel_advance;
57 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
58 enum advance_mode amode;
59 //Mode and filename of pending load, one of LOAD_* constants.
60 int loadmode;
61 std::string pending_load;
62 //Queued saves (all savestates).
63 std::set<std::string> queued_saves;
64 bool stepping_into_save;
65 //Save jukebox.
66 std::vector<std::string> save_jukebox;
67 size_t save_jukebox_pointer;
68 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
69 long pending_reset_cycles = -1;
70 //Set by every video refresh.
71 bool video_refresh_done;
72 //Special subframe location. One of SPECIAL_* constants.
73 int location_special;
74 //Few settings.
75 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
76 boolean_setting pause_on_end("pause-on-end", false);
77 //Last frame params.
78 bool last_hires = false;
79 bool last_interlace = false;
82 class firmware_path_setting : public setting
84 public:
85 firmware_path_setting() : setting("firmwarepath") { _firmwarepath = "."; default_firmware = true; }
86 void blank() throw(std::bad_alloc, std::runtime_error)
88 _firmwarepath = ".";
89 default_firmware = true;
92 bool is_set() throw()
94 return !default_firmware;
97 void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
99 _firmwarepath = value;
100 default_firmware = false;
103 std::string get() throw(std::bad_alloc)
105 return _firmwarepath;
108 operator std::string() throw(std::bad_alloc)
110 return _firmwarepath;
112 private:
113 std::string _firmwarepath;
114 bool default_firmware;
115 } firmwarepath_setting;
117 controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
119 if(lua_requests_subframe_paint)
120 redraw_framebuffer();
122 if(subframe) {
123 if(amode == ADVANCE_SUBFRAME) {
124 if(!cancel_advance && !advanced_once) {
125 platform::wait(advance_timeout_first * 1000);
126 advanced_once = true;
128 if(cancel_advance) {
129 amode = ADVANCE_PAUSE;
130 cancel_advance = false;
132 platform::set_paused(amode == ADVANCE_PAUSE);
133 } else if(amode == ADVANCE_FRAME) {
135 } else {
136 if(amode == ADVANCE_SKIPLAG)
137 amode = ADVANCE_PAUSE;
138 platform::set_paused(amode == ADVANCE_PAUSE);
139 cancel_advance = false;
141 if(amode == ADVANCE_SKIPLAG)
142 amode = ADVANCE_AUTO;
143 location_special = SPECIAL_NONE;
144 update_movie_state();
145 } else {
146 if(amode == ADVANCE_SKIPLAG_PENDING)
147 amode = ADVANCE_SKIPLAG;
148 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
149 if(!cancel_advance) {
150 platform::wait(advanced_once ? to_wait_frame(get_utime()) :
151 (advance_timeout_first * 1000));
152 advanced_once = true;
154 if(cancel_advance) {
155 amode = ADVANCE_PAUSE;
156 cancel_advance = false;
158 platform::set_paused(amode == ADVANCE_PAUSE);
159 } else if(amode == ADVANCE_AUTO && movb.get_movie().readonly_mode() && pause_on_end) {
160 if(movb.get_movie().get_current_frame() == movb.get_movie().get_frame_count() + 1) {
161 amode = ADVANCE_PAUSE;
162 platform::set_paused(true);
164 } else {
165 platform::set_paused((amode == ADVANCE_PAUSE));
166 cancel_advance = false;
168 location_special = SPECIAL_FRAME_START;
169 update_movie_state();
172 information_dispatch::do_status_update();
173 platform::flush_command_queue();
174 if(!subframe && pending_reset_cycles >= 0)
175 controls.reset(pending_reset_cycles);
176 else if(!subframe)
177 controls.reset(-1);
178 controller_frame tmp = controls.commit(movb.get_movie().get_current_frame());
179 lua_callback_do_input(tmp, subframe);
180 return tmp;
183 namespace
185 //Do pending load (automatically unpauses).
186 void mark_pending_load(const std::string& filename, int lmode)
188 loadmode = lmode;
189 pending_load = filename;
190 amode = ADVANCE_LOAD;
191 platform::cancel_wait();
192 platform::set_paused(false);
195 void mark_pending_save(const std::string& filename, int smode)
197 if(smode == SAVE_MOVIE) {
198 //Just do this immediately.
199 do_save_movie(filename);
200 return;
202 queued_saves.insert(filename);
203 messages << "Pending save on '" << filename << "'" << std::endl;
206 uint32_t lpalette[0x80000];
207 void init_palette()
209 static bool palette_init = false;
210 if(palette_init)
211 return;
212 palette_init = true;
213 for(unsigned i = 0; i < 0x80000; i++) {
214 unsigned l = (i >> 15) & 0xF;
215 unsigned r = (i >> 0) & 0x1F;
216 unsigned g = (i >> 5) & 0x1F;
217 unsigned b = (i >> 10) & 0x1F;
218 double _l = static_cast<double>(l);
219 double m = 17.0 / 31.0;
220 r = floor(m * r * _l + 0.5);
221 g = floor(m * g * _l + 0.5);
222 b = floor(m * b * _l + 0.5);
223 lpalette[i] = r * 65536 + g * 256 + b;
228 void update_movie_state()
230 auto& _status = platform::get_emustatus();
231 if(!system_corrupt) {
232 std::ostringstream x;
233 x << movb.get_movie().get_current_frame() << "(";
234 if(location_special == SPECIAL_FRAME_START)
235 x << "0";
236 else if(location_special == SPECIAL_SAVEPOINT)
237 x << "S";
238 else if(location_special == SPECIAL_FRAME_VIDEO)
239 x << "V";
240 else
241 x << movb.get_movie().next_poll_number();
242 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
243 _status.set("Frame", x.str());
244 } else
245 _status.set("Frame", "N/A");
246 if(!system_corrupt) {
247 time_t timevalue = static_cast<time_t>(our_movie.rtc_second);
248 struct tm* time_decompose = gmtime(&timevalue);
249 char datebuffer[512];
250 strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
251 _status.set("RTC", datebuffer);
252 } else {
253 _status.set("RTC", "N/A");
256 std::ostringstream x;
257 auto& mo = movb.get_movie();
258 x << (information_dispatch::get_dumper_count() ? "D" : "-");
259 x << (last_hires ? "H" : "-");
260 x << (last_interlace ? "I" : "-");
261 if(system_corrupt)
262 x << "C";
263 else if(!mo.readonly_mode())
264 x << "R";
265 else if(mo.get_frame_count() >= mo.get_current_frame())
266 x << "P";
267 else
268 x << "F";
269 _status.set("Flags", x.str());
271 if(save_jukebox.size() > 0)
272 _status.set("Saveslot", save_jukebox[save_jukebox_pointer]);
273 else
274 _status.erase("Saveslot");
276 std::ostringstream x;
277 x << get_framerate();
278 _status.set("FPS", x.str());
280 do_watch_memory();
282 controller_frame c;
283 if(movb.get_movie().readonly_mode())
284 c = movb.get_movie().get_controls();
285 else
286 c = controls.get_committed();
287 for(unsigned i = 0; i < 8; i++) {
288 unsigned pindex = controls.lcid_to_pcid(i);
289 devicetype_t dtype = controls.pcid_to_type(pindex);
290 if(dtype == DT_NONE)
291 continue;
292 char buffer[MAX_DISPLAY_LENGTH];
293 c.display(pindex, buffer);
294 char y[3] = {'P', 0, 0};
295 y[1] = 49 + i;
296 _status.set(y, buffer);
300 uint64_t audio_irq_time;
301 uint64_t controller_irq_time;
302 uint64_t frame_irq_time;
305 class my_interface : public SNES::Interface
307 string path(SNES::Cartridge::Slot slot, const string &hint)
309 const char* _hint = hint;
310 std::string _hint2 = _hint;
311 std::string fwp = firmwarepath_setting;
312 std::string finalpath = fwp + "/" + _hint2;
313 return finalpath.c_str();
316 time_t currentTime()
318 return our_movie.rtc_second;
321 time_t randomSeed()
323 return random_seed_value;
326 void videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan)
328 // uint64_t time_x = get_utime();
329 last_hires = hires;
330 last_interlace = interlace;
331 init_palette();
332 if(stepping_into_save)
333 messages << "Got video refresh in runtosave, expect desyncs!" << std::endl;
334 video_refresh_done = true;
335 bool region = (SNES::system.region() == SNES::System::Region::PAL);
336 information_dispatch::do_raw_frame(data, hires, interlace, overscan, region ? VIDEO_REGION_PAL :
337 VIDEO_REGION_NTSC);
338 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
339 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
340 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
341 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
342 lcscreen ls(data, hires, interlace, overscan, region);
343 location_special = SPECIAL_FRAME_VIDEO;
344 update_movie_state();
345 redraw_framebuffer(ls);
346 uint32_t fps_n, fps_d;
347 uint32_t fclocks;
348 if(region)
349 fclocks = interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
350 else
351 fclocks = interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
352 fps_n = SNES::system.cpu_frequency();
353 fps_d = fclocks;
354 uint32_t g = gcd(fps_n, fps_d);
355 fps_n /= g;
356 fps_d /= g;
357 information_dispatch::do_frame(ls, fps_n, fps_d);
358 // time_x = get_utime() - time_x;
359 // std::cerr << "IRQ TIMINGS (microseconds): "
360 // << "V: " << time_x << " "
361 // << "A: " << audio_irq_time << " "
362 // << "C: " << controller_irq_time << " "
363 // << "F: " << frame_irq_time << " "
364 // << "Total: " << (time_x + audio_irq_time + controller_irq_time + frame_irq_time) << std::endl;
365 audio_irq_time = controller_irq_time = 0;
368 void audioSample(int16_t l_sample, int16_t r_sample)
370 // uint64_t time_x = get_utime();
371 uint16_t _l = l_sample;
372 uint16_t _r = r_sample;
373 platform::audio_sample(_l + 32768, _r + 32768);
374 information_dispatch::do_sample(l_sample, r_sample);
375 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
376 our_movie.rtc_subsecond += 768;
377 while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
378 our_movie.rtc_second++;
379 our_movie.rtc_subsecond -= SNES::system.apu_frequency();
381 // audio_irq_time += get_utime() - time_x;
384 int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
386 // uint64_t time_x = get_utime();
387 int16_t x;
388 x = movb.input_poll(port, index, id);
389 lua_callback_snoop_input(port ? 1 : 0, index, id, x);
390 // controller_irq_time += get_utime() - time_x;
391 return x;
395 namespace
397 function_ptr_command<> count_rerecords("count-rerecords", "Count rerecords",
398 "Syntax: count-rerecords\nCounts rerecords.\n",
399 []() throw(std::bad_alloc, std::runtime_error) {
400 std::vector<char> tmp;
401 uint64_t x = rrdata::write(tmp);
402 messages << x << " rerecord(s)" << std::endl;
405 function_ptr_command<const std::string&> quit_emulator("quit-emulator", "Quit the emulator",
406 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
407 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
408 if(args == "/y" || platform::modal_message("Really quit?", true)) {
409 amode = ADVANCE_QUIT;
410 platform::set_paused(false);
411 platform::cancel_wait();
415 function_ptr_command<> pause_emulator("pause-emulator", "(Un)pause the emulator",
416 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
417 []() throw(std::bad_alloc, std::runtime_error) {
418 if(amode != ADVANCE_AUTO) {
419 amode = ADVANCE_AUTO;
420 platform::set_paused(false);
421 platform::cancel_wait();
422 messages << "Unpaused" << std::endl;
423 } else {
424 platform::cancel_wait();
425 cancel_advance = false;
426 amode = ADVANCE_PAUSE;
427 messages << "Paused" << std::endl;
431 function_ptr_command<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
432 "Syntax: cycle-jukebox-backwards\nCycle save jukebox backwards\n",
433 []() throw(std::bad_alloc, std::runtime_error) {
434 if(save_jukebox_pointer == 0)
435 save_jukebox_pointer = save_jukebox.size() - 1;
436 else
437 save_jukebox_pointer--;
438 if(save_jukebox_pointer >= save_jukebox.size())
439 save_jukebox_pointer = 0;
440 update_movie_state();
441 information_dispatch::do_status_update();
444 function_ptr_command<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
445 "Syntax: cycle-jukebox-forwards\nCycle save jukebox forwards\n",
446 []() throw(std::bad_alloc, std::runtime_error) {
447 if(save_jukebox_pointer == save_jukebox.size() - 1)
448 save_jukebox_pointer = 0;
449 else
450 save_jukebox_pointer++;
451 if(save_jukebox_pointer >= save_jukebox.size())
452 save_jukebox_pointer = 0;
453 update_movie_state();
454 information_dispatch::do_status_update();
457 function_ptr_command<arg_filename> add_jukebox("add-jukebox-save", "Add save to jukebox",
458 "Syntax: add-jukebox-save\nAdd save to jukebox\n",
459 [](arg_filename filename) throw(std::bad_alloc, std::runtime_error) {
460 save_jukebox.push_back(filename);
461 update_movie_state();
462 information_dispatch::do_status_update();
465 function_ptr_command<> load_jukebox("load-jukebox", "Load save from jukebox",
466 "Syntax: load-jukebox\nLoad save from jukebox\n",
467 []() throw(std::bad_alloc, std::runtime_error) {
468 if(!save_jukebox.size())
469 throw std::runtime_error("No saves in jukebox");
470 mark_pending_load(save_jukebox[save_jukebox_pointer], LOAD_STATE_CURRENT);
473 function_ptr_command<> save_jukebox_c("save-jukebox", "Save save to jukebox",
474 "Syntax: save-jukebox\nSave save to jukebox\n",
475 []() throw(std::bad_alloc, std::runtime_error) {
476 if(!save_jukebox.size())
477 throw std::runtime_error("No saves in jukebox");
478 mark_pending_save(save_jukebox[save_jukebox_pointer], SAVE_STATE);
481 function_ptr_command<> padvance_frame("+advance-frame", "Advance one frame",
482 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
483 []() throw(std::bad_alloc, std::runtime_error) {
484 amode = ADVANCE_FRAME;
485 cancel_advance = false;
486 advanced_once = false;
487 platform::cancel_wait();
488 platform::set_paused(false);
491 function_ptr_command<> nadvance_frame("-advance-frame", "Advance one frame",
492 "No help available\n",
493 []() throw(std::bad_alloc, std::runtime_error) {
494 cancel_advance = true;
495 platform::cancel_wait();
496 platform::set_paused(false);
499 function_ptr_command<> padvance_poll("+advance-poll", "Advance one subframe",
500 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
501 []() throw(std::bad_alloc, std::runtime_error) {
502 amode = ADVANCE_SUBFRAME;
503 cancel_advance = false;
504 advanced_once = false;
505 platform::cancel_wait();
506 platform::set_paused(false);
509 function_ptr_command<> nadvance_poll("-advance-poll", "Advance one subframe",
510 "No help available\n",
511 []() throw(std::bad_alloc, std::runtime_error) {
512 cancel_advance = true;
513 platform::cancel_wait();
514 platform::set_paused(false);
517 function_ptr_command<> advance_skiplag("advance-skiplag", "Skip to next poll",
518 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
519 []() throw(std::bad_alloc, std::runtime_error) {
520 amode = ADVANCE_SKIPLAG;
521 platform::cancel_wait();
522 platform::set_paused(false);
525 function_ptr_command<> reset_c("reset", "Reset the SNES",
526 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
527 []() throw(std::bad_alloc, std::runtime_error) {
528 pending_reset_cycles = 0;
531 function_ptr_command<arg_filename> load_c("load", "Load savestate (current mode)",
532 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
533 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
534 mark_pending_load(args, LOAD_STATE_CURRENT);
537 function_ptr_command<arg_filename> load_state_c("load-state", "Load savestate (R/W)",
538 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
539 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
540 mark_pending_load(args, LOAD_STATE_RW);
543 function_ptr_command<arg_filename> load_readonly("load-readonly", "Load savestate (RO)",
544 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
545 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
546 mark_pending_load(args, LOAD_STATE_RO);
549 function_ptr_command<arg_filename> load_preserve("load-preserve", "Load savestate (preserve input)",
550 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
551 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
552 mark_pending_load(args, LOAD_STATE_PRESERVE);
555 function_ptr_command<arg_filename> load_movie_c("load-movie", "Load movie",
556 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
557 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
558 mark_pending_load(args, LOAD_STATE_MOVIE);
562 function_ptr_command<arg_filename> save_state("save-state", "Save state",
563 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
564 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
565 mark_pending_save(args, SAVE_STATE);
568 function_ptr_command<arg_filename> save_movie("save-movie", "Save movie",
569 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
570 [](arg_filename args) throw(std::bad_alloc, std::runtime_error) {
571 mark_pending_save(args, SAVE_MOVIE);
574 function_ptr_command<> set_rwmode("set-rwmode", "Switch to read/write mode",
575 "Syntax: set-rwmode\nSwitches to read/write mode\n",
576 []() throw(std::bad_alloc, std::runtime_error) {
577 movb.get_movie().readonly_mode(false);
578 information_dispatch::do_mode_change(false);
579 lua_callback_do_readwrite();
580 update_movie_state();
581 information_dispatch::do_status_update();
584 function_ptr_command<> set_romode("set-romode", "Switch to read-only mode",
585 "Syntax: set-romode\nSwitches to read-only mode\n",
586 []() throw(std::bad_alloc, std::runtime_error) {
587 movb.get_movie().readonly_mode(true);
588 information_dispatch::do_mode_change(true);
589 update_movie_state();
590 information_dispatch::do_status_update();
593 function_ptr_command<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
594 "Syntax: toggle-rwmode\nToggles read/write mode\n",
595 []() throw(std::bad_alloc, std::runtime_error) {
596 bool c = movb.get_movie().readonly_mode();
597 movb.get_movie().readonly_mode(!c);
598 information_dispatch::do_mode_change(!c);
599 if(c)
600 lua_callback_do_readwrite();
601 update_movie_state();
602 information_dispatch::do_status_update();
605 function_ptr_command<> repaint("repaint", "Redraw the screen",
606 "Syntax: repaint\nRedraws the screen\n",
607 []() throw(std::bad_alloc, std::runtime_error) {
608 redraw_framebuffer();
611 function_ptr_command<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
612 []() throw(std::bad_alloc, std::runtime_error) {
613 bool newstate = !static_cast<bool>(pause_on_end);
614 pause_on_end.set(newstate ? "1" : "0");
615 messages << "Pause-on-end is now " << (newstate ? "ON" : "OFF") << std::endl;
618 function_ptr_command<> test1("test-1", "no description available", "No help available\n",
619 []() throw(std::bad_alloc, std::runtime_error) {
620 redraw_framebuffer(screen_nosignal);
623 function_ptr_command<> test2("test-2", "no description available", "No help available\n",
624 []() throw(std::bad_alloc, std::runtime_error) {
625 redraw_framebuffer(screen_corrupt);
628 function_ptr_command<> test3("test-3", "no description available", "No help available\n",
629 []() throw(std::bad_alloc, std::runtime_error) {
630 while(1);
634 bool on_quit_prompt = false;
635 class mywindowcallbacks : public information_dispatch
637 public:
638 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
639 void on_new_dumper(const std::string& n)
641 update_movie_state();
643 void on_destroy_dumper(const std::string& n)
645 update_movie_state();
647 void on_close() throw()
649 if(on_quit_prompt) {
650 amode = ADVANCE_QUIT;
651 platform::set_paused(false);
652 platform::cancel_wait();
653 return;
655 on_quit_prompt = true;
656 try {
657 if(platform::modal_message("Really quit?", true)) {
658 amode = ADVANCE_QUIT;
659 platform::set_paused(false);
660 platform::cancel_wait();
662 } catch(...) {
664 on_quit_prompt = false;
667 void send_analog(unsigned acid, int32_t x, int32_t y)
669 auto g2 = get_framebuffer_size();
670 if(controls.acid_is_mouse(acid)) {
671 controls.analog(acid, x - g2.first / 2, y - g2.second / 2);
672 } else
673 controls.analog(acid, x / 2 , y / 2);
676 void on_click(int32_t x, int32_t y, uint32_t buttonmask) throw()
678 if(buttonmask & ~prev_mouse_mask & 1)
679 send_analog(0, x, y);
680 if(buttonmask & ~prev_mouse_mask & 2)
681 send_analog(1, x, y);
682 if(buttonmask & ~prev_mouse_mask & 4)
683 send_analog(2, x, y);
684 prev_mouse_mask = buttonmask;
686 } mywcb;
688 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
689 //failing.
690 int handle_load()
692 if(pending_load != "") {
693 system_corrupt = false;
694 if(!do_load_state(pending_load, loadmode)) {
695 pending_load = "";
696 return -1;
698 pending_load = "";
699 pending_reset_cycles = -1;
700 amode = ADVANCE_AUTO;
701 platform::cancel_wait();
702 platform::set_paused(false);
703 if(!system_corrupt) {
704 location_special = SPECIAL_SAVEPOINT;
705 update_movie_state();
706 information_dispatch::do_status_update();
707 platform::flush_command_queue();
709 return 1;
711 return 0;
714 //If there are pending saves, perform them.
715 void handle_saves()
717 if(!queued_saves.empty()) {
718 stepping_into_save = true;
719 SNES::system.runtosave();
720 stepping_into_save = false;
721 for(auto i : queued_saves)
722 do_save_state(i);
724 queued_saves.clear();
727 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
728 bool handle_reset(long cycles)
730 if(cycles < 0)
731 return true;
732 video_refresh_done = false;
733 if(cycles == 0)
734 messages << "SNES reset" << std::endl;
735 else if(cycles > 0) {
736 messages << "SNES delayed reset not implemented (doing immediate reset)" << std::endl;
737 /* ... This code is just too buggy.
738 long cycles_executed = 0;
739 messages << "Executing delayed reset... This can take some time!" << std::endl;
740 while(cycles_executed < cycles && !video_refresh_done) {
741 //Poll inputs once in a while to prevent activating watchdog.
742 if(cycles_executed % 100 == 0)
743 platform::flush_command_queue();
744 SNES::cpu.op_step();
745 cycles_executed++;
747 if(!video_refresh_done)
748 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
749 else
750 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
753 SNES::system.reset();
754 lua_callback_do_reset();
755 redraw_framebuffer(screen_nosignal);
756 if(video_refresh_done) {
757 to_wait_frame(get_utime());
758 return false;
760 return true;
763 bool handle_corrupt()
765 if(!system_corrupt)
766 return false;
767 while(system_corrupt) {
768 platform::cancel_wait();
769 platform::set_paused(true);
770 platform::flush_command_queue();
771 handle_load();
772 if(amode == ADVANCE_QUIT)
773 return true;
775 return true;
778 void print_controller_mappings()
780 for(unsigned i = 0; i < 8; i++) {
781 std::string type = controls.lcid_to_typestring(i);
782 messages << "Physical controller mapping: Logical " << (i + 1) << " is physical " <<
783 controls.lcid_to_pcid(i) << " (" << type << ")" << std::endl;
788 std::vector<std::string> get_jukebox_names()
790 return save_jukebox;
793 void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_to_succeed) throw(std::bad_alloc,
794 std::runtime_error)
796 //Basic initialization.
797 init_special_screens();
798 our_rom = &rom;
799 my_interface intrf;
800 auto old_inteface = SNES::interface;
801 SNES::interface = &intrf;
802 SNES::system.init();
804 //Load our given movie.
805 bool first_round = false;
806 bool just_did_loadstate = false;
807 try {
808 do_load_state(initial, LOAD_STATE_DEFAULT);
809 location_special = SPECIAL_SAVEPOINT;
810 update_movie_state();
811 first_round = our_movie.is_savestate;
812 just_did_loadstate = first_round;
813 } catch(std::bad_alloc& e) {
814 OOM_panic();
815 } catch(std::exception& e) {
816 messages << "ERROR: Can't load initial state: " << e.what() << std::endl;
817 if(load_has_to_succeed) {
818 messages << "FATAL: Can't load movie" << std::endl;
819 platform::fatal_error();
821 system_corrupt = true;
822 update_movie_state();
823 redraw_framebuffer(screen_corrupt);
826 lua_callback_startup();
828 //print_controller_mappings();
829 platform::set_paused(false);
830 amode = ADVANCE_PAUSE;
831 uint64_t time_x = get_utime();
832 while(amode != ADVANCE_QUIT || !queued_saves.empty()) {
833 if(handle_corrupt()) {
834 first_round = our_movie.is_savestate;
835 just_did_loadstate = first_round;
836 continue;
838 long resetcycles = -1;
839 ack_frame_tick(get_utime());
840 if(amode == ADVANCE_SKIPLAG_PENDING)
841 amode = ADVANCE_SKIPLAG;
843 if(!first_round) {
844 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
845 if(amode == ADVANCE_QUIT && queued_saves.empty())
846 break;
847 bool delayed_reset = (resetcycles > 0);
848 pending_reset_cycles = -1;
849 if(!handle_reset(resetcycles)) {
850 continue;
852 if(!delayed_reset) {
853 handle_saves();
855 int r = 0;
856 if(queued_saves.empty())
857 r = handle_load();
858 if(r > 0 || system_corrupt) {
859 first_round = our_movie.is_savestate;
860 amode = ADVANCE_PAUSE;
861 just_did_loadstate = first_round;
862 continue;
863 } else if(r < 0) {
864 //Not exactly desriable, but this at least won't desync.
865 amode = ADVANCE_PAUSE;
868 if(just_did_loadstate) {
869 //If we just loadstated, we are up to date.
870 if(amode == ADVANCE_QUIT)
871 break;
872 amode = ADVANCE_PAUSE;
873 platform::cancel_wait();
874 platform::set_paused(true);
875 platform::flush_command_queue();
876 //We already have done the reset this frame if we are going to do one at all.
877 movb.get_movie().set_controls(controls.get(movb.get_movie().get_current_frame()));
878 just_did_loadstate = false;
880 frame_irq_time = get_utime() - time_x;
881 SNES::system.run();
882 time_x = get_utime();
883 if(amode == ADVANCE_AUTO)
884 platform::wait(to_wait_frame(get_utime()));
885 first_round = false;
886 lua_callback_do_frame();
888 information_dispatch::do_dump_end();
889 SNES::interface = old_inteface;