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"
9 #include "library/string.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/movie.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/moviefile.hpp"
14 #include "core/memorymanip.hpp"
15 #include "core/memorywatch.hpp"
16 #include "core/render.hpp"
17 #include "core/rom.hpp"
18 #include "core/rrdata.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
29 #define SPECIAL_FRAME_START 0
30 #define SPECIAL_FRAME_VIDEO 1
31 #define SPECIAL_SAVEPOINT 2
32 #define SPECIAL_NONE 3
34 void update_movie_state();
35 time_t random_seed_value
= 0;
41 ADVANCE_QUIT
, //Quit the emulator.
42 ADVANCE_AUTO
, //Normal (possibly slowed down play).
43 ADVANCE_LOAD
, //Loading a state.
44 ADVANCE_FRAME
, //Frame advance.
45 ADVANCE_SUBFRAME
, //Subframe advance.
46 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
47 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
48 ADVANCE_PAUSE
, //Unconditional pause.
51 //Previous mouse mask.
52 int prev_mouse_mask
= 0;
53 //Flags related to repeating advance.
56 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
57 enum advance_mode amode
;
58 //Mode and filename of pending load, one of LOAD_* constants.
60 std::string pending_load
;
61 //Queued saves (all savestates).
62 std::set
<std::string
> queued_saves
;
63 bool stepping_into_save
;
65 numeric_setting
jukebox_size("jukebox-size", 0, 999, 12);
66 size_t save_jukebox_pointer
;
67 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
68 long pending_reset_cycles
= -1;
69 //Set by every video refresh.
70 bool video_refresh_done
;
71 //Special subframe location. One of SPECIAL_* constants.
74 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
75 boolean_setting
pause_on_end("pause-on-end", false);
77 bool last_hires
= false;
78 bool last_interlace
= false;
80 std::string
save_jukebox_name(size_t i
)
82 return (stringfmt() << "${project}" << (i
+ 1) << ".lsmv").str();
86 path_setting
firmwarepath_setting("firmwarepath");
88 controller_frame
movie_logic::update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
90 if(lua_requests_subframe_paint
)
94 if(amode
== ADVANCE_SUBFRAME
) {
95 if(!cancel_advance
&& !advanced_once
) {
96 platform::wait(advance_timeout_first
* 1000);
100 amode
= ADVANCE_PAUSE
;
101 cancel_advance
= false;
103 platform::set_paused(amode
== ADVANCE_PAUSE
);
104 } else if(amode
== ADVANCE_FRAME
) {
107 if(amode
== ADVANCE_SKIPLAG
)
108 amode
= ADVANCE_PAUSE
;
109 platform::set_paused(amode
== ADVANCE_PAUSE
);
110 cancel_advance
= false;
112 if(amode
== ADVANCE_SKIPLAG
)
113 amode
= ADVANCE_AUTO
;
114 location_special
= SPECIAL_NONE
;
115 update_movie_state();
117 if(amode
== ADVANCE_SKIPLAG_PENDING
)
118 amode
= ADVANCE_SKIPLAG
;
119 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
120 if(!cancel_advance
) {
121 platform::wait(advanced_once
? to_wait_frame(get_utime()) :
122 (advance_timeout_first
* 1000));
123 advanced_once
= true;
126 amode
= ADVANCE_PAUSE
;
127 cancel_advance
= false;
129 platform::set_paused(amode
== ADVANCE_PAUSE
);
130 } else if(amode
== ADVANCE_AUTO
&& movb
.get_movie().readonly_mode() && pause_on_end
) {
131 if(movb
.get_movie().get_current_frame() == movb
.get_movie().get_frame_count() + 1) {
132 amode
= ADVANCE_PAUSE
;
133 platform::set_paused(true);
136 platform::set_paused((amode
== ADVANCE_PAUSE
));
137 cancel_advance
= false;
139 location_special
= SPECIAL_FRAME_START
;
140 update_movie_state();
143 information_dispatch::do_status_update();
144 platform::flush_command_queue();
145 if(!subframe
&& pending_reset_cycles
>= 0)
146 controls
.reset(pending_reset_cycles
);
149 controller_frame tmp
= controls
.commit(movb
.get_movie().get_current_frame());
150 lua_callback_do_input(tmp
, subframe
);
156 enum advance_mode old_mode
;
158 //Do pending load (automatically unpauses).
159 void mark_pending_load(const std::string
& filename
, int lmode
)
162 pending_load
= filename
;
164 amode
= ADVANCE_LOAD
;
165 platform::cancel_wait();
166 platform::set_paused(false);
169 void mark_pending_save(const std::string
& filename
, int smode
)
171 if(smode
== SAVE_MOVIE
) {
172 //Just do this immediately.
173 do_save_movie(filename
);
176 queued_saves
.insert(filename
);
177 messages
<< "Pending save on '" << filename
<< "'" << std::endl
;
180 uint32_t lpalette
[0x80000];
183 static bool palette_init
= false;
187 for(unsigned i
= 0; i
< 0x80000; i
++) {
188 unsigned l
= (i
>> 15) & 0xF;
189 unsigned r
= (i
>> 0) & 0x1F;
190 unsigned g
= (i
>> 5) & 0x1F;
191 unsigned b
= (i
>> 10) & 0x1F;
192 double _l
= static_cast<double>(l
);
193 double m
= 17.0 / 31.0;
194 r
= floor(m
* r
* _l
+ 0.5);
195 g
= floor(m
* g
* _l
+ 0.5);
196 b
= floor(m
* b
* _l
+ 0.5);
197 lpalette
[i
] = r
* 65536 + g
* 256 + b
;
202 void update_movie_state()
204 auto& _status
= platform::get_emustatus();
205 if(!system_corrupt
) {
206 std::ostringstream x
;
207 x
<< movb
.get_movie().get_current_frame() << "(";
208 if(location_special
== SPECIAL_FRAME_START
)
210 else if(location_special
== SPECIAL_SAVEPOINT
)
212 else if(location_special
== SPECIAL_FRAME_VIDEO
)
215 x
<< movb
.get_movie().next_poll_number();
216 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
217 _status
.set("Frame", x
.str());
219 _status
.set("Frame", "N/A");
220 if(!system_corrupt
) {
221 time_t timevalue
= static_cast<time_t>(our_movie
.rtc_second
);
222 struct tm
* time_decompose
= gmtime(&timevalue
);
223 char datebuffer
[512];
224 strftime(datebuffer
, 511, "%Y%m%d(%a)T%H%M%S", time_decompose
);
225 _status
.set("RTC", datebuffer
);
227 _status
.set("RTC", "N/A");
230 std::ostringstream x
;
231 auto& mo
= movb
.get_movie();
232 x
<< (information_dispatch::get_dumper_count() ? "D" : "-");
233 x
<< (last_hires
? "H" : "-");
234 x
<< (last_interlace
? "I" : "-");
237 else if(!mo
.readonly_mode())
239 else if(mo
.get_frame_count() >= mo
.get_current_frame())
243 _status
.set("Flags", x
.str());
246 _status
.set("Saveslot", translate_name_mprefix(save_jukebox_name(save_jukebox_pointer
)));
248 _status
.erase("Saveslot");
250 std::ostringstream x
;
251 x
<< get_framerate();
252 _status
.set("SPD%", x
.str());
257 if(movb
.get_movie().readonly_mode())
258 c
= movb
.get_movie().get_controls();
260 c
= controls
.get_committed();
261 for(unsigned i
= 0; i
< 8; i
++) {
262 unsigned pindex
= controls
.lcid_to_pcid(i
);
263 devicetype_t dtype
= controls
.pcid_to_type(pindex
);
266 char buffer
[MAX_DISPLAY_LENGTH
];
267 c
.display(pindex
, buffer
);
268 char y
[3] = {'P', 0, 0};
270 _status
.set(y
, buffer
);
274 uint64_t audio_irq_time
;
275 uint64_t controller_irq_time
;
276 uint64_t frame_irq_time
;
279 class my_interface
: public SNES::Interface
281 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
283 const char* _hint
= hint
;
284 std::string _hint2
= _hint
;
285 std::string fwp
= firmwarepath_setting
;
286 std::string finalpath
= fwp
+ "/" + _hint2
;
287 return finalpath
.c_str();
292 return our_movie
.rtc_second
;
297 return random_seed_value
;
300 void videoRefresh(const uint32_t* data
, bool hires
, bool interlace
, bool overscan
)
302 // uint64_t time_x = get_utime();
304 last_interlace
= interlace
;
306 if(stepping_into_save
)
307 messages
<< "Got video refresh in runtosave, expect desyncs!" << std::endl
;
308 video_refresh_done
= true;
309 lua_callback_do_frame_emulated();
310 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
311 information_dispatch::do_raw_frame(data
, hires
, interlace
, overscan
, region
? VIDEO_REGION_PAL
:
313 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
314 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
315 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
316 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
317 lcscreen
ls(data
, hires
, interlace
, overscan
, region
);
318 location_special
= SPECIAL_FRAME_VIDEO
;
319 update_movie_state();
320 redraw_framebuffer(ls
, false, true);
321 uint32_t fps_n
, fps_d
;
324 fclocks
= interlace
? DURATION_PAL_FIELD
: DURATION_PAL_FRAME
;
326 fclocks
= interlace
? DURATION_NTSC_FIELD
: DURATION_NTSC_FRAME
;
327 fps_n
= SNES::system
.cpu_frequency();
329 uint32_t g
= gcd(fps_n
, fps_d
);
332 information_dispatch::do_frame(ls
, fps_n
, fps_d
);
333 // time_x = get_utime() - time_x;
334 // std::cerr << "IRQ TIMINGS (microseconds): "
335 // << "V: " << time_x << " "
336 // << "A: " << audio_irq_time << " "
337 // << "C: " << controller_irq_time << " "
338 // << "F: " << frame_irq_time << " "
339 // << "Total: " << (time_x + audio_irq_time + controller_irq_time + frame_irq_time) << std::endl;
340 audio_irq_time
= controller_irq_time
= 0;
343 void audioSample(int16_t l_sample
, int16_t r_sample
)
345 // uint64_t time_x = get_utime();
346 uint16_t _l
= l_sample
;
347 uint16_t _r
= r_sample
;
348 platform::audio_sample(_l
+ 32768, _r
+ 32768);
349 information_dispatch::do_sample(l_sample
, r_sample
);
350 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
351 our_movie
.rtc_subsecond
+= 768;
352 while(our_movie
.rtc_subsecond
>= SNES::system
.apu_frequency()) {
353 our_movie
.rtc_second
++;
354 our_movie
.rtc_subsecond
-= SNES::system
.apu_frequency();
356 // audio_irq_time += get_utime() - time_x;
359 int16_t inputPoll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
361 // uint64_t time_x = get_utime();
363 x
= movb
.input_poll(port
, index
, id
);
364 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
);
365 // controller_irq_time += get_utime() - time_x;
372 class jukebox_size_listener
: public information_dispatch
375 jukebox_size_listener() : information_dispatch("jukebox-size-listener") {};
376 void on_setting_change(const std::string
& setting
, const std::string
& value
)
378 if(setting
== "jukebox-size") {
379 if(save_jukebox_pointer
>= jukebox_size
)
380 save_jukebox_pointer
= 0;
381 update_movie_state();
384 } _jukebox_size_listener
;
386 function_ptr_command
<> count_rerecords("count-rerecords", "Count rerecords",
387 "Syntax: count-rerecords\nCounts rerecords.\n",
388 []() throw(std::bad_alloc
, std::runtime_error
) {
389 std::vector
<char> tmp
;
390 uint64_t x
= rrdata::write(tmp
);
391 messages
<< x
<< " rerecord(s)" << std::endl
;
394 function_ptr_command
<const std::string
&> quit_emulator("quit-emulator", "Quit the emulator",
395 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
396 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
397 amode
= ADVANCE_QUIT
;
398 platform::set_paused(false);
399 platform::cancel_wait();
402 function_ptr_command
<> pause_emulator("pause-emulator", "(Un)pause the emulator",
403 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
404 []() throw(std::bad_alloc
, std::runtime_error
) {
405 if(amode
!= ADVANCE_AUTO
) {
406 amode
= ADVANCE_AUTO
;
407 platform::set_paused(false);
408 platform::cancel_wait();
409 messages
<< "Unpaused" << std::endl
;
411 platform::cancel_wait();
412 cancel_advance
= false;
413 amode
= ADVANCE_PAUSE
;
414 messages
<< "Paused" << std::endl
;
418 function_ptr_command
<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
419 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
420 []() throw(std::bad_alloc
, std::runtime_error
) {
421 if(jukebox_size
== 0)
423 if(save_jukebox_pointer
== 0)
424 save_jukebox_pointer
= jukebox_size
- 1;
426 save_jukebox_pointer
--;
427 if(save_jukebox_pointer
>= jukebox_size
)
428 save_jukebox_pointer
= 0;
429 update_movie_state();
430 information_dispatch::do_status_update();
433 function_ptr_command
<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
434 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
435 []() throw(std::bad_alloc
, std::runtime_error
) {
436 if(jukebox_size
== 0)
438 if(save_jukebox_pointer
== jukebox_size
- 1)
439 save_jukebox_pointer
= 0;
441 save_jukebox_pointer
++;
442 if(save_jukebox_pointer
>= jukebox_size
)
443 save_jukebox_pointer
= 0;
444 update_movie_state();
445 information_dispatch::do_status_update();
448 function_ptr_command
<> load_jukebox("load-jukebox", "Load save from jukebox",
449 "Syntax: load-jukebox\nLoad save from jukebox\n",
450 []() throw(std::bad_alloc
, std::runtime_error
) {
451 if(jukebox_size
== 0)
452 throw std::runtime_error("No slot selected");
453 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_CURRENT
);
456 function_ptr_command
<> save_jukebox_c("save-jukebox", "Save save to jukebox",
457 "Syntax: save-jukebox\nSave save to jukebox\n",
458 []() throw(std::bad_alloc
, std::runtime_error
) {
459 if(jukebox_size
== 0)
460 throw std::runtime_error("No slot selected");
461 mark_pending_save(save_jukebox_name(save_jukebox_pointer
), SAVE_STATE
);
464 function_ptr_command
<> padvance_frame("+advance-frame", "Advance one frame",
465 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
466 []() throw(std::bad_alloc
, std::runtime_error
) {
467 amode
= ADVANCE_FRAME
;
468 cancel_advance
= false;
469 advanced_once
= false;
470 platform::cancel_wait();
471 platform::set_paused(false);
474 function_ptr_command
<> nadvance_frame("-advance-frame", "Advance one frame",
475 "No help available\n",
476 []() throw(std::bad_alloc
, std::runtime_error
) {
477 cancel_advance
= true;
478 platform::cancel_wait();
479 platform::set_paused(false);
482 function_ptr_command
<> padvance_poll("+advance-poll", "Advance one subframe",
483 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
484 []() throw(std::bad_alloc
, std::runtime_error
) {
485 amode
= ADVANCE_SUBFRAME
;
486 cancel_advance
= false;
487 advanced_once
= false;
488 platform::cancel_wait();
489 platform::set_paused(false);
492 function_ptr_command
<> nadvance_poll("-advance-poll", "Advance one subframe",
493 "No help available\n",
494 []() throw(std::bad_alloc
, std::runtime_error
) {
495 cancel_advance
= true;
496 platform::cancel_wait();
497 platform::set_paused(false);
500 function_ptr_command
<> advance_skiplag("advance-skiplag", "Skip to next poll",
501 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
502 []() throw(std::bad_alloc
, std::runtime_error
) {
503 amode
= ADVANCE_SKIPLAG
;
504 platform::cancel_wait();
505 platform::set_paused(false);
508 function_ptr_command
<> reset_c("reset", "Reset the SNES",
509 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
510 []() throw(std::bad_alloc
, std::runtime_error
) {
511 pending_reset_cycles
= 0;
514 function_ptr_command
<arg_filename
> load_c("load", "Load savestate (current mode)",
515 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
516 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
517 mark_pending_load(args
, LOAD_STATE_CURRENT
);
520 function_ptr_command
<arg_filename
> load_state_c("load-state", "Load savestate (R/W)",
521 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
522 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
523 mark_pending_load(args
, LOAD_STATE_RW
);
526 function_ptr_command
<arg_filename
> load_readonly("load-readonly", "Load savestate (RO)",
527 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
528 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
529 mark_pending_load(args
, LOAD_STATE_RO
);
532 function_ptr_command
<arg_filename
> load_preserve("load-preserve", "Load savestate (preserve input)",
533 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
534 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
535 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
538 function_ptr_command
<arg_filename
> load_movie_c("load-movie", "Load movie",
539 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
540 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
541 mark_pending_load(args
, LOAD_STATE_MOVIE
);
545 function_ptr_command
<arg_filename
> save_state("save-state", "Save state",
546 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
547 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
548 mark_pending_save(args
, SAVE_STATE
);
551 function_ptr_command
<arg_filename
> save_movie("save-movie", "Save movie",
552 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
553 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
554 mark_pending_save(args
, SAVE_MOVIE
);
557 function_ptr_command
<> set_rwmode("set-rwmode", "Switch to read/write mode",
558 "Syntax: set-rwmode\nSwitches to read/write mode\n",
559 []() throw(std::bad_alloc
, std::runtime_error
) {
560 movb
.get_movie().readonly_mode(false);
561 information_dispatch::do_mode_change(false);
562 lua_callback_do_readwrite();
563 update_movie_state();
564 information_dispatch::do_status_update();
567 function_ptr_command
<> set_romode("set-romode", "Switch to read-only mode",
568 "Syntax: set-romode\nSwitches to read-only mode\n",
569 []() throw(std::bad_alloc
, std::runtime_error
) {
570 movb
.get_movie().readonly_mode(true);
571 information_dispatch::do_mode_change(true);
572 update_movie_state();
573 information_dispatch::do_status_update();
576 function_ptr_command
<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
577 "Syntax: toggle-rwmode\nToggles read/write mode\n",
578 []() throw(std::bad_alloc
, std::runtime_error
) {
579 bool c
= movb
.get_movie().readonly_mode();
580 movb
.get_movie().readonly_mode(!c
);
581 information_dispatch::do_mode_change(!c
);
583 lua_callback_do_readwrite();
584 update_movie_state();
585 information_dispatch::do_status_update();
588 function_ptr_command
<> repaint("repaint", "Redraw the screen",
589 "Syntax: repaint\nRedraws the screen\n",
590 []() throw(std::bad_alloc
, std::runtime_error
) {
591 redraw_framebuffer();
594 function_ptr_command
<> tpon("toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
595 []() throw(std::bad_alloc
, std::runtime_error
) {
596 bool newstate
= !static_cast<bool>(pause_on_end
);
597 pause_on_end
.set(newstate
? "1" : "0");
598 messages
<< "Pause-on-end is now " << (newstate
? "ON" : "OFF") << std::endl
;
601 function_ptr_command
<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
602 "Syntax: rewind-movie\nRewind movie to the beginning\n",
603 []() throw(std::bad_alloc
, std::runtime_error
) {
604 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING
);
607 function_ptr_command
<> cancel_save("cancel-saves", "Cancel all pending saves", "Syntax: cancel-save\n"
608 "Cancel pending saves\n",
609 []() throw(std::bad_alloc
, std::runtime_error
) {
610 queued_saves
.clear();
611 messages
<< "Pending saves canceled." << std::endl
;
614 function_ptr_command
<> test1("test-1", "no description available", "No help available\n",
615 []() throw(std::bad_alloc
, std::runtime_error
) {
616 redraw_framebuffer(screen_nosignal
);
619 function_ptr_command
<> test2("test-2", "no description available", "No help available\n",
620 []() throw(std::bad_alloc
, std::runtime_error
) {
621 redraw_framebuffer(screen_corrupt
);
624 function_ptr_command
<> test3("test-3", "no description available", "No help available\n",
625 []() throw(std::bad_alloc
, std::runtime_error
) {
629 inverse_key
ipause_emulator("pause-emulator", "Speed‣(Un)pause");
630 inverse_key
ijback("cycle-jukebox-backward", "Slot select‣Cycle backwards");
631 inverse_key
ijforward("cycle-jukebox-forward", "Slot select‣Cycle forwards");
632 inverse_key
iloadj("load-jukebox", "Load‣Selected slot");
633 inverse_key
isavej("save-jukebox", "Save‣Selected slot");
634 inverse_key
iadvframe("+advance-frame", "Speed‣Advance frame");
635 inverse_key
iadvsubframe("+advance-poll", "Speed‣Advance subframe");
636 inverse_key
iskiplag("advance-skiplag", "Speed‣Advance poll");
637 inverse_key
ireset("reset", "System‣Reset");
638 inverse_key
iset_rwmode("set-rwmode", "Movie‣Switch to read/write");
639 inverse_key
itoggle_romode("set-romode", "Movie‣Switch to read-only");
640 inverse_key
itoggle_rwmode("toggle-rwmode", "Movie‣Toggle read-only");
641 inverse_key
irepaint("repaint", "System‣Repaint screen");
642 inverse_key
itogglepause("toggle-pause-on-end", "Movie‣Toggle pause-on-end");
643 inverse_key
irewind_movie("rewind-movie", "Movie‣Rewind movie");
644 inverse_key
icancel_saves("cancel-saves", "Save‣Cancel pending saves");
645 inverse_key
iload1("load ${project}1.lsmv", "Load‣Slot 1");
646 inverse_key
iload2("load ${project}2.lsmv", "Load‣Slot 2");
647 inverse_key
iload3("load ${project}3.lsmv", "Load‣Slot 3");
648 inverse_key
iload4("load ${project}4.lsmv", "Load‣Slot 4");
649 inverse_key
iload5("load ${project}5.lsmv", "Load‣Slot 5");
650 inverse_key
iload6("load ${project}6.lsmv", "Load‣Slot 6");
651 inverse_key
iload7("load ${project}7.lsmv", "Load‣Slot 7");
652 inverse_key
iload8("load ${project}8.lsmv", "Load‣Slot 8");
653 inverse_key
iload9("load ${project}9.lsmv", "Load‣Slot 9");
654 inverse_key
iload10("load ${project}10.lsmv", "Load‣Slot 10");
655 inverse_key
iload11("load ${project}11.lsmv", "Load‣Slot 11");
656 inverse_key
iload12("load ${project}12.lsmv", "Load‣Slot 12");
657 inverse_key
iload13("load ${project}13.lsmv", "Load‣Slot 13");
658 inverse_key
iload14("load ${project}14.lsmv", "Load‣Slot 14");
659 inverse_key
iload15("load ${project}15.lsmv", "Load‣Slot 15");
660 inverse_key
iload16("load ${project}16.lsmv", "Load‣Slot 16");
661 inverse_key
iload17("load ${project}17.lsmv", "Load‣Slot 17");
662 inverse_key
iload18("load ${project}18.lsmv", "Load‣Slot 18");
663 inverse_key
iload19("load ${project}19.lsmv", "Load‣Slot 19");
664 inverse_key
iload20("load ${project}20.lsmv", "Load‣Slot 20");
665 inverse_key
iload21("load ${project}21.lsmv", "Load‣Slot 21");
666 inverse_key
iload22("load ${project}22.lsmv", "Load‣Slot 22");
667 inverse_key
iload23("load ${project}23.lsmv", "Load‣Slot 23");
668 inverse_key
iload24("load ${project}24.lsmv", "Load‣Slot 24");
669 inverse_key
iload25("load ${project}25.lsmv", "Load‣Slot 25");
670 inverse_key
iload26("load ${project}26.lsmv", "Load‣Slot 26");
671 inverse_key
iload27("load ${project}27.lsmv", "Load‣Slot 27");
672 inverse_key
iload28("load ${project}28.lsmv", "Load‣Slot 28");
673 inverse_key
iload29("load ${project}29.lsmv", "Load‣Slot 29");
674 inverse_key
iload30("load ${project}30.lsmv", "Load‣Slot 30");
675 inverse_key
iload31("load ${project}31.lsmv", "Load‣Slot 31");
676 inverse_key
iload32("load ${project}32.lsmv", "Load‣Slot 32");
677 inverse_key
isave1("save-state ${project}1.lsmv", "Save‣Slot 1");
678 inverse_key
isave2("save-state ${project}2.lsmv", "Save‣Slot 2");
679 inverse_key
isave3("save-state ${project}3.lsmv", "Save‣Slot 3");
680 inverse_key
isave4("save-state ${project}4.lsmv", "Save‣Slot 4");
681 inverse_key
isave5("save-state ${project}5.lsmv", "Save‣Slot 5");
682 inverse_key
isave6("save-state ${project}6.lsmv", "Save‣Slot 6");
683 inverse_key
isave7("save-state ${project}7.lsmv", "Save‣Slot 7");
684 inverse_key
isave8("save-state ${project}8.lsmv", "Save‣Slot 8");
685 inverse_key
isave9("save-state ${project}9.lsmv", "Save‣Slot 9");
686 inverse_key
isave10("save-state ${project}10.lsmv", "Save‣Slot 10");
687 inverse_key
isave11("save-state ${project}11.lsmv", "Save‣Slot 11");
688 inverse_key
isave12("save-state ${project}12.lsmv", "Save‣Slot 12");
689 inverse_key
isave13("save-state ${project}13.lsmv", "Save‣Slot 13");
690 inverse_key
isave14("save-state ${project}14.lsmv", "Save‣Slot 14");
691 inverse_key
isave15("save-state ${project}15.lsmv", "Save‣Slot 15");
692 inverse_key
isave16("save-state ${project}16.lsmv", "Save‣Slot 16");
693 inverse_key
isave17("save-state ${project}17.lsmv", "Save‣Slot 17");
694 inverse_key
isave18("save-state ${project}18.lsmv", "Save‣Slot 18");
695 inverse_key
isave19("save-state ${project}19.lsmv", "Save‣Slot 19");
696 inverse_key
isave20("save-state ${project}20.lsmv", "Save‣Slot 20");
697 inverse_key
isave21("save-state ${project}21.lsmv", "Save‣Slot 21");
698 inverse_key
isave22("save-state ${project}22.lsmv", "Save‣Slot 22");
699 inverse_key
isave23("save-state ${project}23.lsmv", "Save‣Slot 23");
700 inverse_key
isave24("save-state ${project}24.lsmv", "Save‣Slot 24");
701 inverse_key
isave25("save-state ${project}25.lsmv", "Save‣Slot 25");
702 inverse_key
isave26("save-state ${project}26.lsmv", "Save‣Slot 26");
703 inverse_key
isave27("save-state ${project}27.lsmv", "Save‣Slot 27");
704 inverse_key
isave28("save-state ${project}28.lsmv", "Save‣Slot 28");
705 inverse_key
isave29("save-state ${project}29.lsmv", "Save‣Slot 29");
706 inverse_key
isave30("save-state ${project}30.lsmv", "Save‣Slot 30");
707 inverse_key
isave31("save-state ${project}31.lsmv", "Save‣Slot 31");
708 inverse_key
isave32("save-state ${project}32.lsmv", "Save‣Slot 32");
710 bool on_quit_prompt
= false;
711 class mywindowcallbacks
: public information_dispatch
714 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
715 void on_new_dumper(const std::string
& n
)
717 update_movie_state();
719 void on_destroy_dumper(const std::string
& n
)
721 update_movie_state();
723 void on_close() throw()
726 amode
= ADVANCE_QUIT
;
727 platform::set_paused(false);
728 platform::cancel_wait();
731 on_quit_prompt
= true;
733 amode
= ADVANCE_QUIT
;
734 platform::set_paused(false);
735 platform::cancel_wait();
738 on_quit_prompt
= false;
742 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
746 if(pending_load
!= "") {
747 system_corrupt
= false;
748 if(loadmode
!= LOAD_STATE_BEGINNING
&& !do_load_state(pending_load
, loadmode
)) {
752 if(loadmode
== LOAD_STATE_BEGINNING
)
755 pending_reset_cycles
= -1;
756 amode
= ADVANCE_AUTO
;
757 platform::cancel_wait();
758 platform::set_paused(false);
759 if(!system_corrupt
) {
760 location_special
= SPECIAL_SAVEPOINT
;
761 update_movie_state();
762 information_dispatch::do_status_update();
763 platform::flush_command_queue();
770 //If there are pending saves, perform them.
773 if(!queued_saves
.empty()) {
774 stepping_into_save
= true;
775 SNES::system
.runtosave();
776 stepping_into_save
= false;
777 for(auto i
: queued_saves
)
780 queued_saves
.clear();
783 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
784 bool handle_reset(long cycles
)
788 video_refresh_done
= false;
790 messages
<< "SNES reset" << std::endl
;
791 else if(cycles
> 0) {
792 messages
<< "SNES delayed reset not implemented (doing immediate reset)" << std::endl
;
793 /* ... This code is just too buggy.
794 long cycles_executed = 0;
795 messages << "Executing delayed reset... This can take some time!" << std::endl;
796 while(cycles_executed < cycles && !video_refresh_done) {
797 //Poll inputs once in a while to prevent activating watchdog.
798 if(cycles_executed % 100 == 0)
799 platform::flush_command_queue();
803 if(!video_refresh_done)
804 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
806 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
809 SNES::system
.reset();
810 lua_callback_do_reset();
811 redraw_framebuffer(screen_nosignal
);
812 if(video_refresh_done
) {
813 to_wait_frame(get_utime());
819 bool handle_corrupt()
823 while(system_corrupt
) {
824 platform::cancel_wait();
825 platform::set_paused(true);
826 platform::flush_command_queue();
828 if(amode
== ADVANCE_QUIT
)
835 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
, bool load_has_to_succeed
) throw(std::bad_alloc
,
838 //Basic initialization.
839 init_special_screens();
842 auto old_inteface
= SNES::interface
;
843 SNES::interface
= &intrf
;
846 //Load our given movie.
847 bool first_round
= false;
848 bool just_did_loadstate
= false;
850 do_load_state(initial
, LOAD_STATE_DEFAULT
);
851 location_special
= SPECIAL_SAVEPOINT
;
852 update_movie_state();
853 first_round
= our_movie
.is_savestate
;
854 just_did_loadstate
= first_round
;
855 } catch(std::bad_alloc
& e
) {
857 } catch(std::exception
& e
) {
858 messages
<< "ERROR: Can't load initial state: " << e
.what() << std::endl
;
859 if(load_has_to_succeed
) {
860 messages
<< "FATAL: Can't load movie" << std::endl
;
861 platform::fatal_error();
863 system_corrupt
= true;
864 update_movie_state();
865 redraw_framebuffer(screen_corrupt
);
868 lua_callback_startup();
870 platform::set_paused(initial
.start_paused
);
871 amode
= initial
.start_paused
? ADVANCE_PAUSE
: ADVANCE_AUTO
;
872 uint64_t time_x
= get_utime();
873 while(amode
!= ADVANCE_QUIT
|| !queued_saves
.empty()) {
874 if(handle_corrupt()) {
875 first_round
= our_movie
.is_savestate
;
876 just_did_loadstate
= first_round
;
879 long resetcycles
= -1;
880 ack_frame_tick(get_utime());
881 if(amode
== ADVANCE_SKIPLAG_PENDING
)
882 amode
= ADVANCE_SKIPLAG
;
885 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
886 if(amode
== ADVANCE_QUIT
&& queued_saves
.empty())
888 bool delayed_reset
= (resetcycles
> 0);
889 pending_reset_cycles
= -1;
890 if(!handle_reset(resetcycles
)) {
897 if(queued_saves
.empty())
899 if(r
> 0 || system_corrupt
) {
900 first_round
= our_movie
.is_savestate
;
902 amode
= ADVANCE_PAUSE
;
905 just_did_loadstate
= first_round
;
908 //Not exactly desriable, but this at least won't desync.
909 amode
= ADVANCE_PAUSE
;
912 if(just_did_loadstate
) {
913 //If we just loadstated, we are up to date.
914 if(amode
== ADVANCE_QUIT
)
916 platform::cancel_wait();
917 platform::set_paused(amode
== ADVANCE_PAUSE
);
918 platform::flush_command_queue();
919 //We already have done the reset this frame if we are going to do one at all.
920 movb
.get_movie().set_controls(movb
.update_controls(true));
921 movb
.get_movie().set_all_DRDY();
922 just_did_loadstate
= false;
924 frame_irq_time
= get_utime() - time_x
;
926 time_x
= get_utime();
927 if(amode
== ADVANCE_AUTO
)
928 platform::wait(to_wait_frame(get_utime()));
930 lua_callback_do_frame();
932 information_dispatch::do_dump_end();
933 SNES::interface
= old_inteface
;