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"
10 #include "library/string.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/rom.hpp"
18 #include "core/rrdata.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
21 #include "interface/callbacks.hpp"
22 #include "interface/romtype.hpp"
23 #include "library/framebuffer.hpp"
24 #include "library/pixfmt-lrgb.hpp"
34 #define SPECIAL_FRAME_START 0
35 #define SPECIAL_FRAME_VIDEO 1
36 #define SPECIAL_SAVEPOINT 2
37 #define SPECIAL_NONE 3
39 void update_movie_state();
40 time_t random_seed_value
= 0;
42 volatile uint32_t advance_timeout_first
= 500;
43 volatile bool pause_on_end
= false;
49 ADVANCE_QUIT
, //Quit the emulator.
50 ADVANCE_AUTO
, //Normal (possibly slowed down play).
51 ADVANCE_LOAD
, //Loading a state.
52 ADVANCE_FRAME
, //Frame advance.
53 ADVANCE_SUBFRAME
, //Subframe advance.
54 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
55 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
56 ADVANCE_PAUSE
, //Unconditional pause.
59 //Flags related to repeating advance.
62 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
63 enum advance_mode amode
;
64 //Mode and filename of pending load, one of LOAD_* constants.
66 std::string pending_load
;
67 //Queued saves (all savestates).
68 std::set
<std::string
> queued_saves
;
70 size_t jukebox_size
= 12;
71 size_t save_jukebox_pointer
;
72 //Special subframe location. One of SPECIAL_* constants.
76 bool last_hires
= false;
77 bool last_interlace
= false;
79 bool do_unsafe_rewind
= false;
80 void* unsafe_rewind_obj
= NULL
;
82 bool stop_at_frame_active
= false;
83 uint64_t stop_at_frame
= 0;
85 mutex_class firmwarepath_lock
;
86 std::string firmwarepath
= ".";
88 enum advance_mode old_mode
;
90 std::string
save_jukebox_name(size_t i
)
92 return (stringfmt() << "${project}" << (i
+ 1) << ".lsmv").str();
95 class _lsnes_pflag_handler
: public movie::poll_flag
98 ~_lsnes_pflag_handler()
103 return our_rom
->rtype
->get_pflag();
105 void set_pflag(int flag
)
107 our_rom
->rtype
->set_pflag(flag
);
109 } lsnes_pflag_handler
;
112 void mainloop_signal_need_rewind(void* ptr
)
116 amode
= ADVANCE_LOAD
;
118 do_unsafe_rewind
= true;
119 unsafe_rewind_obj
= ptr
;
122 controller_frame
movie_logic::update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
124 if(lua_requests_subframe_paint
)
125 redraw_framebuffer();
128 if(amode
== ADVANCE_SUBFRAME
) {
129 if(!cancel_advance
&& !advanced_once
) {
130 platform::wait(advance_timeout_first
* 1000);
131 advanced_once
= true;
134 stop_at_frame_active
= false;
135 amode
= ADVANCE_PAUSE
;
136 cancel_advance
= false;
138 platform::set_paused(amode
== ADVANCE_PAUSE
);
139 } else if(amode
== ADVANCE_FRAME
) {
142 if(amode
== ADVANCE_SKIPLAG
) {
143 stop_at_frame_active
= false;
144 amode
= ADVANCE_PAUSE
;
146 platform::set_paused(amode
== ADVANCE_PAUSE
);
147 cancel_advance
= false;
149 location_special
= SPECIAL_NONE
;
150 update_movie_state();
152 if(amode
== ADVANCE_SKIPLAG_PENDING
)
153 amode
= ADVANCE_SKIPLAG
;
154 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
155 if(!cancel_advance
) {
156 platform::wait(advanced_once
? to_wait_frame(get_utime()) :
157 (advance_timeout_first
* 1000));
158 advanced_once
= true;
161 stop_at_frame_active
= false;
162 amode
= ADVANCE_PAUSE
;
163 cancel_advance
= false;
165 platform::set_paused(amode
== ADVANCE_PAUSE
);
166 } else if(amode
== ADVANCE_AUTO
&& movb
.get_movie().readonly_mode() && pause_on_end
&&
167 !stop_at_frame_active
) {
168 if(movb
.get_movie().get_current_frame() == movb
.get_movie().get_frame_count()) {
169 stop_at_frame_active
= false;
170 amode
= ADVANCE_PAUSE
;
171 platform::set_paused(true);
173 } else if(amode
== ADVANCE_AUTO
&& stop_at_frame_active
) {
174 if(movb
.get_movie().get_current_frame() >= stop_at_frame
) {
175 stop_at_frame_active
= false;
176 amode
= ADVANCE_PAUSE
;
177 platform::set_paused(true);
180 platform::set_paused((amode
== ADVANCE_PAUSE
));
181 cancel_advance
= false;
183 location_special
= SPECIAL_FRAME_START
;
184 update_movie_state();
187 information_dispatch::do_status_update();
188 platform::flush_command_queue();
189 controller_frame tmp
= controls
.get(movb
.get_movie().get_current_frame());
190 our_rom
->rtype
->pre_emulate_frame(tmp
); //Preset controls, the lua will override if needed.
191 lua_callback_do_input(tmp
, subframe
);
192 controls
.commit(tmp
);
199 //Do pending load (automatically unpauses).
200 void mark_pending_load(const std::string
& filename
, int lmode
)
203 pending_load
= filename
;
205 amode
= ADVANCE_LOAD
;
206 platform::cancel_wait();
207 platform::set_paused(false);
210 void mark_pending_save(const std::string
& filename
, int smode
)
212 if(smode
== SAVE_MOVIE
) {
213 //Just do this immediately.
214 do_save_movie(filename
);
217 if(location_special
== SPECIAL_SAVEPOINT
) {
218 //We can save immediately here.
219 do_save_state(filename
);
222 queued_saves
.insert(filename
);
223 messages
<< "Pending save on '" << filename
<< "'" << std::endl
;
226 bool reload_rom(const std::string
& filename
)
228 std::string filenam
= filename
;
230 filenam
= our_rom
->load_filename
;
232 messages
<< "No ROM loaded" << std::endl
;
236 messages
<< "Loading ROM " << filenam
<< std::endl
;
237 loaded_rom
newrom(filenam
);
239 for(size_t i
= 0; i
< sizeof(our_rom
->romimg
)/sizeof(our_rom
->romimg
[0]); i
++) {
240 our_movie
.romimg_sha256
[i
] = our_rom
->romimg
[i
].sha_256
;
241 our_movie
.romxml_sha256
[i
] = our_rom
->romxml
[i
].sha_256
;
243 } catch(std::exception
& e
) {
244 messages
<< "Can't reload ROM: " << e
.what() << std::endl
;
247 messages
<< "Using core: " << our_rom
->rtype
->get_core_identifier() << std::endl
;
248 information_dispatch::do_core_change();
253 void update_movie_state()
255 static unsigned last_controllers
= 0;
258 our_rom
->region
->fill_framerate_magic(magic
);
259 voice_frame_number(movb
.get_movie().get_current_frame(), 1.0 * magic
[1] / magic
[0]);
261 auto& _status
= platform::get_emustatus();
262 if(!system_corrupt
) {
263 _status
.set("!frame", (stringfmt() << movb
.get_movie().get_current_frame()).str());
264 _status
.set("!length", (stringfmt() << movb
.get_movie().get_frame_count()).str());
265 _status
.set("!lag", (stringfmt() << movb
.get_movie().get_lag_frames()).str());
266 if(location_special
== SPECIAL_FRAME_START
)
267 _status
.set("!subframe", "0");
268 else if(location_special
== SPECIAL_SAVEPOINT
)
269 _status
.set("!subframe", "S");
270 else if(location_special
== SPECIAL_FRAME_VIDEO
)
271 _status
.set("!subframe", "V");
272 else if(location_special
== SPECIAL_FRAME_VIDEO
)
273 _status
.set("!subframe", (stringfmt() << movb
.get_movie().next_poll_number()).str());
275 _status
.set("!frame", "N/A");
276 _status
.set("!length", "N/A");
277 _status
.set("!lag", "N/A");
278 _status
.set("!subframe", "N/A");
281 _status
.set("!dumping", (information_dispatch::get_dumper_count() ? "Y" : ""));
282 auto& mo
= movb
.get_movie();
284 _status
.set("!mode", "C");
285 else if(!mo
.readonly_mode())
286 _status
.set("!mode", "R");
287 else if(mo
.get_frame_count() >= mo
.get_current_frame())
288 _status
.set("!mode", "P");
290 _status
.set("!mode", "F");
293 _status
.set("!saveslot", (stringfmt() << (save_jukebox_pointer
+ 1)).str());
295 _status
.erase("!saveslot");
296 _status
.set("!speed", (stringfmt() << (unsigned)(100 * get_realized_multiplier() + 0.5)).str());
298 if(!system_corrupt
) {
299 time_t timevalue
= static_cast<time_t>(our_movie
.rtc_second
);
300 struct tm
* time_decompose
= gmtime(&timevalue
);
301 char datebuffer
[512];
302 strftime(datebuffer
, 511, "%Y%m%d(%a)T%H%M%S", time_decompose
);
303 _status
.set("RTC", datebuffer
);
305 _status
.set("RTC", "N/A");
310 if(movb
.get_movie().readonly_mode())
311 c
= movb
.get_movie().get_controls();
313 c
= controls
.get_committed();
314 for(unsigned i
= 0;; i
++) {
315 auto pindex
= controls
.lcid_to_pcid(i
);
316 if(pindex
.first
< 0 || !controls
.is_present(pindex
.first
, pindex
.second
)) {
317 for(unsigned j
= i
; j
< last_controllers
; j
++)
318 _status
.erase((stringfmt() << "P" << (j
+ 1)).str());
319 last_controllers
= i
;
322 char buffer
[MAX_DISPLAY_LENGTH
];
323 c
.display(pindex
.first
, pindex
.second
, buffer
);
324 _status
.set((stringfmt() << "P" << (i
+ 1)).str(), buffer
);
328 uint64_t audio_irq_time
;
329 uint64_t controller_irq_time
;
330 uint64_t frame_irq_time
;
332 struct lsnes_callbacks
: public emucore_callbacks
335 ~lsnes_callbacks() throw()
339 int16_t get_input(unsigned port
, unsigned index
, unsigned control
)
342 x
= movb
.input_poll(port
, index
, control
);
343 lua_callback_snoop_input(port
, index
, control
, x
);
347 int16_t set_input(unsigned port
, unsigned index
, unsigned control
, int16_t value
)
349 if(!movb
.get_movie().readonly_mode()) {
350 controller_frame f
= movb
.get_movie().get_controls();
351 f
.axis3(port
, index
, control
, value
);
352 movb
.get_movie().set_controls(f
);
354 return movb
.get_movie().next_input(port
, index
, control
);
357 void timer_tick(uint32_t increment
, uint32_t per_second
)
359 our_movie
.rtc_subsecond
+= increment
;
360 while(our_movie
.rtc_subsecond
>= per_second
) {
361 our_movie
.rtc_second
++;
362 our_movie
.rtc_subsecond
-= per_second
;
366 std::string
get_firmware_path()
368 umutex_class
h(firmwarepath_lock
);
372 std::string
get_base_path()
374 return our_rom
->msu1_base
;
379 return our_movie
.rtc_second
;
382 time_t get_randomseed()
384 return random_seed_value
;
387 void output_frame(framebuffer_raw
& screen
, uint32_t fps_n
, uint32_t fps_d
)
389 lua_callback_do_frame_emulated();
390 location_special
= SPECIAL_FRAME_VIDEO
;
391 update_movie_state();
392 redraw_framebuffer(screen
, false, true);
393 uint32_t g
= gcd(fps_n
, fps_d
);
396 information_dispatch::do_frame(screen
, fps_n
, fps_d
);
400 void set_firmwarepath(const std::string
& fwp
)
402 umutex_class
h(firmwarepath_lock
);
409 std::string
get_firmwarepath()
414 void set_jukebox_size(size_t size
)
417 if(save_jukebox_pointer
>= jukebox_size
)
418 save_jukebox_pointer
= 0;
421 size_t get_jukebox_size()
428 function_ptr_command
<> count_rerecords(lsnes_cmd
, "count-rerecords", "Count rerecords",
429 "Syntax: count-rerecords\nCounts rerecords.\n",
430 []() throw(std::bad_alloc
, std::runtime_error
) {
431 std::vector
<char> tmp
;
432 uint64_t x
= rrdata::write(tmp
);
433 messages
<< x
<< " rerecord(s)" << std::endl
;
436 function_ptr_command
<const std::string
&> quit_emulator(lsnes_cmd
, "quit-emulator", "Quit the emulator",
437 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
438 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
439 amode
= ADVANCE_QUIT
;
440 platform::set_paused(false);
441 platform::cancel_wait();
444 function_ptr_command
<> unpause_emulator(lsnes_cmd
, "unpause-emulator", "Unpause the emulator",
445 "Syntax: unpause-emulator\nUnpauses the emulator.\n",
446 []() throw(std::bad_alloc
, std::runtime_error
) {
447 amode
= ADVANCE_AUTO
;
448 platform::set_paused(false);
449 platform::cancel_wait();
450 messages
<< "Unpaused" << std::endl
;
453 function_ptr_command
<> pause_emulator(lsnes_cmd
, "pause-emulator", "(Un)pause the emulator",
454 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
455 []() throw(std::bad_alloc
, std::runtime_error
) {
456 if(amode
!= ADVANCE_AUTO
) {
457 amode
= ADVANCE_AUTO
;
458 platform::set_paused(false);
459 platform::cancel_wait();
460 messages
<< "Unpaused" << std::endl
;
462 platform::cancel_wait();
463 cancel_advance
= false;
464 stop_at_frame_active
= false;
465 amode
= ADVANCE_PAUSE
;
466 messages
<< "Paused" << std::endl
;
470 function_ptr_command
<> save_jukebox_prev(lsnes_cmd
, "cycle-jukebox-backward", "Cycle save jukebox backwards",
471 "Syntax: cycle-jukebox-backward\nCycle save jukebox backwards\n",
472 []() throw(std::bad_alloc
, std::runtime_error
) {
473 if(jukebox_size
== 0)
475 if(save_jukebox_pointer
== 0)
476 save_jukebox_pointer
= jukebox_size
- 1;
478 save_jukebox_pointer
--;
479 if(save_jukebox_pointer
>= jukebox_size
)
480 save_jukebox_pointer
= 0;
481 update_movie_state();
482 information_dispatch::do_status_update();
485 function_ptr_command
<> save_jukebox_next(lsnes_cmd
, "cycle-jukebox-forward", "Cycle save jukebox forwards",
486 "Syntax: cycle-jukebox-forward\nCycle save jukebox forwards\n",
487 []() throw(std::bad_alloc
, std::runtime_error
) {
488 if(jukebox_size
== 0)
490 if(save_jukebox_pointer
== jukebox_size
- 1)
491 save_jukebox_pointer
= 0;
493 save_jukebox_pointer
++;
494 if(save_jukebox_pointer
>= jukebox_size
)
495 save_jukebox_pointer
= 0;
496 update_movie_state();
497 information_dispatch::do_status_update();
500 function_ptr_command
<> load_jukebox(lsnes_cmd
, "load-jukebox", "Load save from jukebox",
501 "Syntax: load-jukebox\nLoad save from jukebox\n",
502 []() throw(std::bad_alloc
, std::runtime_error
) {
503 if(jukebox_size
== 0)
504 throw std::runtime_error("No slot selected");
505 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_CURRENT
);
508 function_ptr_command
<> load_jukebox_readwrite(lsnes_cmd
, "load-jukebox-readwrite", "Load save from jukebox in"
509 " read-write mode", "Syntax: load-jukebox-readwrite\nLoad save from jukebox in read-write mode\n",
510 []() throw(std::bad_alloc
, std::runtime_error
) {
511 if(jukebox_size
== 0)
512 throw std::runtime_error("No slot selected");
513 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_RW
);
516 function_ptr_command
<> load_jukebox_readonly(lsnes_cmd
, "load-jukebox-readonly", "Load save from jukebox in "
517 "read-only mode", "Syntax: load-jukebox-readonly\nLoad save from jukebox in read-only mode\n",
518 []() throw(std::bad_alloc
, std::runtime_error
) {
519 if(jukebox_size
== 0)
520 throw std::runtime_error("No slot selected");
521 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_RO
);
524 function_ptr_command
<> load_jukebox_preserve(lsnes_cmd
, "load-jukebox-preserve", "Load save from jukebox, "
525 "preserving input", "Syntax: load-jukebox-preserve\nLoad save from jukebox, preserving input\n",
526 []() throw(std::bad_alloc
, std::runtime_error
) {
527 if(jukebox_size
== 0)
528 throw std::runtime_error("No slot selected");
529 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_PRESERVE
);
532 function_ptr_command
<> load_jukebox_movie(lsnes_cmd
, "load-jukebox-movie", "Load save from jukebox as movie",
533 "Syntax: load-jukebox-movie\nLoad save from jukebox as movie\n",
534 []() throw(std::bad_alloc
, std::runtime_error
) {
535 if(jukebox_size
== 0)
536 throw std::runtime_error("No slot selected");
537 mark_pending_load(save_jukebox_name(save_jukebox_pointer
), LOAD_STATE_MOVIE
);
540 function_ptr_command
<> save_jukebox_c(lsnes_cmd
, "save-jukebox", "Save save to jukebox",
541 "Syntax: save-jukebox\nSave save to jukebox\n",
542 []() throw(std::bad_alloc
, std::runtime_error
) {
543 if(jukebox_size
== 0)
544 throw std::runtime_error("No slot selected");
545 mark_pending_save(save_jukebox_name(save_jukebox_pointer
), SAVE_STATE
);
548 function_ptr_command
<> padvance_frame(lsnes_cmd
, "+advance-frame", "Advance one frame",
549 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
550 []() throw(std::bad_alloc
, std::runtime_error
) {
551 amode
= ADVANCE_FRAME
;
552 cancel_advance
= false;
553 advanced_once
= false;
554 platform::cancel_wait();
555 platform::set_paused(false);
558 function_ptr_command
<> nadvance_frame(lsnes_cmd
, "-advance-frame", "Advance one frame",
559 "No help available\n",
560 []() throw(std::bad_alloc
, std::runtime_error
) {
561 cancel_advance
= true;
562 platform::cancel_wait();
563 platform::set_paused(false);
566 function_ptr_command
<> padvance_poll(lsnes_cmd
, "+advance-poll", "Advance one subframe",
567 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
568 []() throw(std::bad_alloc
, std::runtime_error
) {
569 amode
= ADVANCE_SUBFRAME
;
570 cancel_advance
= false;
571 advanced_once
= false;
572 platform::cancel_wait();
573 platform::set_paused(false);
576 function_ptr_command
<> nadvance_poll(lsnes_cmd
, "-advance-poll", "Advance one subframe",
577 "No help available\n",
578 []() throw(std::bad_alloc
, std::runtime_error
) {
579 cancel_advance
= true;
580 platform::cancel_wait();
581 platform::set_paused(false);
584 function_ptr_command
<> advance_skiplag(lsnes_cmd
, "advance-skiplag", "Skip to next poll",
585 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
586 []() throw(std::bad_alloc
, std::runtime_error
) {
587 amode
= ADVANCE_SKIPLAG_PENDING
;
588 platform::cancel_wait();
589 platform::set_paused(false);
592 function_ptr_command
<const std::string
&> reset_c(lsnes_cmd
, "reset", "Reset the system",
593 "Syntax: reset\nReset <delay>\nResets the system in beginning of the next frame.\n",
594 [](const std::string
& x
) throw(std::bad_alloc
, std::runtime_error
) {
595 if(!our_rom
->rtype
->get_reset_support()) {
596 messages
<< "Emulator core does not support resets" << std::endl
;
599 if((our_rom
->rtype
->get_reset_support() & 3) < 2 && x
!= "") {
600 messages
<< "Emulator core does not support delayed resets" << std::endl
;
604 our_rom
->rtype
->request_reset(0, false);
606 our_rom
->rtype
->request_reset(parse_value
<uint32_t>(x
), false);
609 function_ptr_command
<const std::string
&> hreset_c(lsnes_cmd
, "reset-hard", "Reset the system",
610 "Syntax: reset-hard\nReset-hard <delay>\nHard resets the system in beginning of the next frame.\n",
611 [](const std::string
& x
) throw(std::bad_alloc
, std::runtime_error
) {
612 if((our_rom
->rtype
->get_reset_support() & 4) == 0) {
613 messages
<< "Emulator core does not support hard resets" << std::endl
;
616 if((our_rom
->rtype
->get_reset_support() & 3) < 2 && x
!= "") {
617 messages
<< "Emulator core does not support delayed hard resets" << std::endl
;
621 our_rom
->rtype
->request_reset(0, true);
623 our_rom
->rtype
->request_reset(parse_value
<uint32_t>(x
), true);
626 function_ptr_command
<arg_filename
> load_c(lsnes_cmd
, "load", "Load savestate (current mode)",
627 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
628 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
629 mark_pending_load(args
, LOAD_STATE_CURRENT
);
632 function_ptr_command
<arg_filename
> load_smart_c(lsnes_cmd
, "load-smart", "Load savestate (heuristic mode)",
633 "Syntax: load <file>\nLoads SNES state from <file> in heuristic mode\n",
634 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
635 mark_pending_load(args
, LOAD_STATE_DEFAULT
);
638 function_ptr_command
<arg_filename
> load_state_c(lsnes_cmd
, "load-state", "Load savestate (R/W)",
639 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
640 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
641 mark_pending_load(args
, LOAD_STATE_RW
);
644 function_ptr_command
<arg_filename
> load_readonly(lsnes_cmd
, "load-readonly", "Load savestate (RO)",
645 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
646 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
647 mark_pending_load(args
, LOAD_STATE_RO
);
650 function_ptr_command
<arg_filename
> load_preserve(lsnes_cmd
, "load-preserve", "Load savestate (preserve "
651 "input)", "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
652 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
653 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
656 function_ptr_command
<arg_filename
> load_movie_c(lsnes_cmd
, "load-movie", "Load movie",
657 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
658 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
659 mark_pending_load(args
, LOAD_STATE_MOVIE
);
663 function_ptr_command
<arg_filename
> save_state(lsnes_cmd
, "save-state", "Save state",
664 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
665 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
666 mark_pending_save(args
, SAVE_STATE
);
669 function_ptr_command
<arg_filename
> save_movie(lsnes_cmd
, "save-movie", "Save movie",
670 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
671 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
672 mark_pending_save(args
, SAVE_MOVIE
);
675 function_ptr_command
<> set_rwmode(lsnes_cmd
, "set-rwmode", "Switch to read/write mode",
676 "Syntax: set-rwmode\nSwitches to read/write mode\n",
677 []() throw(std::bad_alloc
, std::runtime_error
) {
678 movb
.get_movie().readonly_mode(false);
679 information_dispatch::do_mode_change(false);
680 lua_callback_do_readwrite();
681 update_movie_state();
682 information_dispatch::do_status_update();
685 function_ptr_command
<> set_romode(lsnes_cmd
, "set-romode", "Switch to read-only mode",
686 "Syntax: set-romode\nSwitches to read-only mode\n",
687 []() throw(std::bad_alloc
, std::runtime_error
) {
688 movb
.get_movie().readonly_mode(true);
689 information_dispatch::do_mode_change(true);
690 update_movie_state();
691 information_dispatch::do_status_update();
694 function_ptr_command
<> toggle_rwmode(lsnes_cmd
, "toggle-rwmode", "Toggle read/write mode",
695 "Syntax: toggle-rwmode\nToggles read/write mode\n",
696 []() throw(std::bad_alloc
, std::runtime_error
) {
697 bool c
= movb
.get_movie().readonly_mode();
698 movb
.get_movie().readonly_mode(!c
);
699 information_dispatch::do_mode_change(!c
);
701 lua_callback_do_readwrite();
702 update_movie_state();
703 information_dispatch::do_status_update();
706 function_ptr_command
<> repaint(lsnes_cmd
, "repaint", "Redraw the screen",
707 "Syntax: repaint\nRedraws the screen\n",
708 []() throw(std::bad_alloc
, std::runtime_error
) {
709 redraw_framebuffer();
712 function_ptr_command
<> tpon(lsnes_cmd
, "toggle-pause-on-end", "Toggle pause on end", "Toggle pause on end\n",
713 []() throw(std::bad_alloc
, std::runtime_error
) {
714 pause_on_end
= !pause_on_end
;
715 messages
<< "Pause-on-end is now " << (pause_on_end
? "ON" : "OFF") << std::endl
;
718 function_ptr_command
<> spon(lsnes_cmd
, "set-pause-on-end", "Set pause on end", "Set pause on end\n",
719 []() throw(std::bad_alloc
, std::runtime_error
) {
721 messages
<< "Pause-on-end is now ON" << std::endl
;
724 function_ptr_command
<> cpon(lsnes_cmd
, "clear-pause-on-end", "Clear pause on end", "Clear pause on end\n",
725 []() throw(std::bad_alloc
, std::runtime_error
) {
726 pause_on_end
= false;
727 messages
<< "Pause-on-end is now OFF" << std::endl
;
730 function_ptr_command
<> rewind_movie(lsnes_cmd
, "rewind-movie", "Rewind movie to the beginning",
731 "Syntax: rewind-movie\nRewind movie to the beginning\n",
732 []() throw(std::bad_alloc
, std::runtime_error
) {
733 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING
);
736 function_ptr_command
<const std::string
&> reload_rom2(lsnes_cmd
, "reload-rom", "Reload the ROM image",
737 "Syntax: reload-rom [<file>]\nReload the ROM image from <file>\n",
738 [](const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
) {
739 if(reload_rom(filename
))
740 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_ROMRELOAD
);
743 function_ptr_command
<> cancel_save(lsnes_cmd
, "cancel-saves", "Cancel all pending saves", "Syntax: "
744 "cancel-save\nCancel pending saves\n",
745 []() throw(std::bad_alloc
, std::runtime_error
) {
746 queued_saves
.clear();
747 messages
<< "Pending saves canceled." << std::endl
;
750 inverse_bind
ipause_emulator(lsnes_mapper
, "pause-emulator", "Speed‣(Un)pause");
751 inverse_bind
ijback(lsnes_mapper
, "cycle-jukebox-backward", "Slot select‣Cycle backwards");
752 inverse_bind
ijforward(lsnes_mapper
, "cycle-jukebox-forward", "Slot select‣Cycle forwards");
753 inverse_bind
iloadj(lsnes_mapper
, "load-jukebox", "Load‣Selected slot");
754 inverse_bind
iloadjrw(lsnes_mapper
, "load-jukebox-readwrite", "Load‣Selected slot (readwrite mode)");
755 inverse_bind
iloadjro(lsnes_mapper
, "load-jukebox-readonly", "Load‣Selected slot (readonly mode)");
756 inverse_bind
iloadjp(lsnes_mapper
, "load-jukebox-preserve", "Load‣Selected slot (preserve input)");
757 inverse_bind
iloadjm(lsnes_mapper
, "load-jukebox-movie", "Load‣Selected slot (as movie)");
758 inverse_bind
isavej(lsnes_mapper
, "save-jukebox", "Save‣Selected slot");
759 inverse_bind
iadvframe(lsnes_mapper
, "+advance-frame", "Speed‣Advance frame");
760 inverse_bind
iadvsubframe(lsnes_mapper
, "+advance-poll", "Speed‣Advance subframe");
761 inverse_bind
iskiplag(lsnes_mapper
, "advance-skiplag", "Speed‣Advance poll");
762 inverse_bind
ireset(lsnes_mapper
, "reset", "System‣Reset");
763 inverse_bind
iset_rwmode(lsnes_mapper
, "set-rwmode", "Movie‣Switch to read/write");
764 inverse_bind
itoggle_romode(lsnes_mapper
, "set-romode", "Movie‣Switch to read-only");
765 inverse_bind
itoggle_rwmode(lsnes_mapper
, "toggle-rwmode", "Movie‣Toggle read-only");
766 inverse_bind
irepaint(lsnes_mapper
, "repaint", "System‣Repaint screen");
767 inverse_bind
itogglepause(lsnes_mapper
, "toggle-pause-on-end", "Movie‣Toggle pause-on-end");
768 inverse_bind
irewind_movie(lsnes_mapper
, "rewind-movie", "Movie‣Rewind movie");
769 inverse_bind
icancel_saves(lsnes_mapper
, "cancel-saves", "Save‣Cancel pending saves");
770 inverse_bind
iload1(lsnes_mapper
, "load ${project}1.lsmv", "Load‣Slot 1");
771 inverse_bind
iload2(lsnes_mapper
, "load ${project}2.lsmv", "Load‣Slot 2");
772 inverse_bind
iload3(lsnes_mapper
, "load ${project}3.lsmv", "Load‣Slot 3");
773 inverse_bind
iload4(lsnes_mapper
, "load ${project}4.lsmv", "Load‣Slot 4");
774 inverse_bind
iload5(lsnes_mapper
, "load ${project}5.lsmv", "Load‣Slot 5");
775 inverse_bind
iload6(lsnes_mapper
, "load ${project}6.lsmv", "Load‣Slot 6");
776 inverse_bind
iload7(lsnes_mapper
, "load ${project}7.lsmv", "Load‣Slot 7");
777 inverse_bind
iload8(lsnes_mapper
, "load ${project}8.lsmv", "Load‣Slot 8");
778 inverse_bind
iload9(lsnes_mapper
, "load ${project}9.lsmv", "Load‣Slot 9");
779 inverse_bind
iload10(lsnes_mapper
, "load ${project}10.lsmv", "Load‣Slot 10");
780 inverse_bind
iload11(lsnes_mapper
, "load ${project}11.lsmv", "Load‣Slot 11");
781 inverse_bind
iload12(lsnes_mapper
, "load ${project}12.lsmv", "Load‣Slot 12");
782 inverse_bind
iload13(lsnes_mapper
, "load ${project}13.lsmv", "Load‣Slot 13");
783 inverse_bind
iload14(lsnes_mapper
, "load ${project}14.lsmv", "Load‣Slot 14");
784 inverse_bind
iload15(lsnes_mapper
, "load ${project}15.lsmv", "Load‣Slot 15");
785 inverse_bind
iload16(lsnes_mapper
, "load ${project}16.lsmv", "Load‣Slot 16");
786 inverse_bind
iload17(lsnes_mapper
, "load ${project}17.lsmv", "Load‣Slot 17");
787 inverse_bind
iload18(lsnes_mapper
, "load ${project}18.lsmv", "Load‣Slot 18");
788 inverse_bind
iload19(lsnes_mapper
, "load ${project}19.lsmv", "Load‣Slot 19");
789 inverse_bind
iload20(lsnes_mapper
, "load ${project}20.lsmv", "Load‣Slot 20");
790 inverse_bind
iload21(lsnes_mapper
, "load ${project}21.lsmv", "Load‣Slot 21");
791 inverse_bind
iload22(lsnes_mapper
, "load ${project}22.lsmv", "Load‣Slot 22");
792 inverse_bind
iload23(lsnes_mapper
, "load ${project}23.lsmv", "Load‣Slot 23");
793 inverse_bind
iload24(lsnes_mapper
, "load ${project}24.lsmv", "Load‣Slot 24");
794 inverse_bind
iload25(lsnes_mapper
, "load ${project}25.lsmv", "Load‣Slot 25");
795 inverse_bind
iload26(lsnes_mapper
, "load ${project}26.lsmv", "Load‣Slot 26");
796 inverse_bind
iload27(lsnes_mapper
, "load ${project}27.lsmv", "Load‣Slot 27");
797 inverse_bind
iload28(lsnes_mapper
, "load ${project}28.lsmv", "Load‣Slot 28");
798 inverse_bind
iload29(lsnes_mapper
, "load ${project}29.lsmv", "Load‣Slot 29");
799 inverse_bind
iload30(lsnes_mapper
, "load ${project}30.lsmv", "Load‣Slot 30");
800 inverse_bind
iload31(lsnes_mapper
, "load ${project}31.lsmv", "Load‣Slot 31");
801 inverse_bind
iload32(lsnes_mapper
, "load ${project}32.lsmv", "Load‣Slot 32");
802 inverse_bind
isave1(lsnes_mapper
, "save-state ${project}1.lsmv", "Save‣Slot 1");
803 inverse_bind
isave2(lsnes_mapper
, "save-state ${project}2.lsmv", "Save‣Slot 2");
804 inverse_bind
isave3(lsnes_mapper
, "save-state ${project}3.lsmv", "Save‣Slot 3");
805 inverse_bind
isave4(lsnes_mapper
, "save-state ${project}4.lsmv", "Save‣Slot 4");
806 inverse_bind
isave5(lsnes_mapper
, "save-state ${project}5.lsmv", "Save‣Slot 5");
807 inverse_bind
isave6(lsnes_mapper
, "save-state ${project}6.lsmv", "Save‣Slot 6");
808 inverse_bind
isave7(lsnes_mapper
, "save-state ${project}7.lsmv", "Save‣Slot 7");
809 inverse_bind
isave8(lsnes_mapper
, "save-state ${project}8.lsmv", "Save‣Slot 8");
810 inverse_bind
isave9(lsnes_mapper
, "save-state ${project}9.lsmv", "Save‣Slot 9");
811 inverse_bind
isave10(lsnes_mapper
, "save-state ${project}10.lsmv", "Save‣Slot 10");
812 inverse_bind
isave11(lsnes_mapper
, "save-state ${project}11.lsmv", "Save‣Slot 11");
813 inverse_bind
isave12(lsnes_mapper
, "save-state ${project}12.lsmv", "Save‣Slot 12");
814 inverse_bind
isave13(lsnes_mapper
, "save-state ${project}13.lsmv", "Save‣Slot 13");
815 inverse_bind
isave14(lsnes_mapper
, "save-state ${project}14.lsmv", "Save‣Slot 14");
816 inverse_bind
isave15(lsnes_mapper
, "save-state ${project}15.lsmv", "Save‣Slot 15");
817 inverse_bind
isave16(lsnes_mapper
, "save-state ${project}16.lsmv", "Save‣Slot 16");
818 inverse_bind
isave17(lsnes_mapper
, "save-state ${project}17.lsmv", "Save‣Slot 17");
819 inverse_bind
isave18(lsnes_mapper
, "save-state ${project}18.lsmv", "Save‣Slot 18");
820 inverse_bind
isave19(lsnes_mapper
, "save-state ${project}19.lsmv", "Save‣Slot 19");
821 inverse_bind
isave20(lsnes_mapper
, "save-state ${project}20.lsmv", "Save‣Slot 20");
822 inverse_bind
isave21(lsnes_mapper
, "save-state ${project}21.lsmv", "Save‣Slot 21");
823 inverse_bind
isave22(lsnes_mapper
, "save-state ${project}22.lsmv", "Save‣Slot 22");
824 inverse_bind
isave23(lsnes_mapper
, "save-state ${project}23.lsmv", "Save‣Slot 23");
825 inverse_bind
isave24(lsnes_mapper
, "save-state ${project}24.lsmv", "Save‣Slot 24");
826 inverse_bind
isave25(lsnes_mapper
, "save-state ${project}25.lsmv", "Save‣Slot 25");
827 inverse_bind
isave26(lsnes_mapper
, "save-state ${project}26.lsmv", "Save‣Slot 26");
828 inverse_bind
isave27(lsnes_mapper
, "save-state ${project}27.lsmv", "Save‣Slot 27");
829 inverse_bind
isave28(lsnes_mapper
, "save-state ${project}28.lsmv", "Save‣Slot 28");
830 inverse_bind
isave29(lsnes_mapper
, "save-state ${project}29.lsmv", "Save‣Slot 29");
831 inverse_bind
isave30(lsnes_mapper
, "save-state ${project}30.lsmv", "Save‣Slot 30");
832 inverse_bind
isave31(lsnes_mapper
, "save-state ${project}31.lsmv", "Save‣Slot 31");
833 inverse_bind
isave32(lsnes_mapper
, "save-state ${project}32.lsmv", "Save‣Slot 32");
835 bool on_quit_prompt
= false;
836 class mywindowcallbacks
: public information_dispatch
839 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
840 void on_new_dumper(const std::string
& n
)
842 update_movie_state();
844 void on_destroy_dumper(const std::string
& n
)
846 update_movie_state();
848 void on_close() throw()
851 amode
= ADVANCE_QUIT
;
852 platform::set_paused(false);
853 platform::cancel_wait();
856 on_quit_prompt
= true;
858 amode
= ADVANCE_QUIT
;
859 platform::set_paused(false);
860 platform::cancel_wait();
863 on_quit_prompt
= false;
867 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
871 if(do_unsafe_rewind
&& unsafe_rewind_obj
) {
872 uint64_t t
= get_utime();
874 lua_callback_do_unsafe_rewind(s
, 0, 0, movb
.get_movie(), unsafe_rewind_obj
);
875 information_dispatch::do_mode_change(false);
876 do_unsafe_rewind
= false;
877 our_movie
.is_savestate
= true;
878 location_special
= SPECIAL_SAVEPOINT
;
879 update_movie_state();
880 messages
<< "Rewind done in " << (get_utime() - t
) << " usec." << std::endl
;
883 if(pending_load
!= "") {
884 system_corrupt
= false;
885 if(loadmode
!= LOAD_STATE_BEGINNING
&& loadmode
!= LOAD_STATE_ROMRELOAD
&&
886 !do_load_state(pending_load
, loadmode
)) {
887 movb
.get_movie().set_pflag_handler(&lsnes_pflag_handler
);
892 if(loadmode
== LOAD_STATE_BEGINNING
)
893 do_load_beginning(false);
894 if(loadmode
== LOAD_STATE_ROMRELOAD
)
895 do_load_beginning(true);
896 } catch(std::exception
& e
) {
897 messages
<< "Load failed: " << e
.what() << std::endl
;
899 movb
.get_movie().set_pflag_handler(&lsnes_pflag_handler
);
901 amode
= ADVANCE_AUTO
;
902 platform::cancel_wait();
903 platform::set_paused(false);
904 if(!system_corrupt
) {
905 location_special
= SPECIAL_SAVEPOINT
;
906 update_movie_state();
907 information_dispatch::do_status_update();
908 platform::flush_command_queue();
915 //If there are pending saves, perform them.
918 if(!queued_saves
.empty() || (do_unsafe_rewind
&& !unsafe_rewind_obj
)) {
919 our_rom
->rtype
->runtosave();
920 for(auto i
: queued_saves
)
922 if(do_unsafe_rewind
&& !unsafe_rewind_obj
) {
923 uint64_t t
= get_utime();
924 std::vector
<char> s
= our_rom
->save_core_state(true);
925 uint64_t secs
= our_movie
.rtc_second
;
926 uint64_t ssecs
= our_movie
.rtc_subsecond
;
927 lua_callback_do_unsafe_rewind(s
, secs
, ssecs
, movb
.get_movie(), NULL
);
928 do_unsafe_rewind
= false;
929 messages
<< "Rewind point set in " << (get_utime() - t
) << " usec." << std::endl
;
932 queued_saves
.clear();
935 bool handle_corrupt()
939 while(system_corrupt
) {
940 platform::cancel_wait();
941 platform::set_paused(true);
942 platform::flush_command_queue();
944 if(amode
== ADVANCE_QUIT
)
951 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
, bool load_has_to_succeed
) throw(std::bad_alloc
,
954 //Basic initialization.
956 init_special_screens();
958 lsnes_callbacks lsnes_callbacks_obj
;
959 ecore_callbacks
= &lsnes_callbacks_obj
;
960 movb
.get_movie().set_pflag_handler(&lsnes_pflag_handler
);
961 core_core::install_all_handlers();
963 //Load our given movie.
964 bool first_round
= false;
965 bool just_did_loadstate
= false;
967 do_load_state(initial
, LOAD_STATE_INITIAL
);
968 location_special
= SPECIAL_SAVEPOINT
;
969 update_movie_state();
970 first_round
= our_movie
.is_savestate
;
971 just_did_loadstate
= first_round
;
972 } catch(std::bad_alloc
& e
) {
974 } catch(std::exception
& e
) {
975 messages
<< "ERROR: Can't load initial state: " << e
.what() << std::endl
;
976 if(load_has_to_succeed
) {
977 messages
<< "FATAL: Can't load movie" << std::endl
;
978 platform::fatal_error();
980 system_corrupt
= true;
981 update_movie_state();
982 redraw_framebuffer(screen_corrupt
);
985 movb
.get_movie().set_pflag_handler(&lsnes_pflag_handler
);
986 lua_callback_startup();
988 platform::set_paused(initial
.start_paused
);
989 amode
= initial
.start_paused
? ADVANCE_PAUSE
: ADVANCE_AUTO
;
990 stop_at_frame_active
= false;
991 uint64_t time_x
= get_utime();
992 while(amode
!= ADVANCE_QUIT
|| !queued_saves
.empty()) {
993 if(handle_corrupt()) {
994 first_round
= our_movie
.is_savestate
;
995 just_did_loadstate
= first_round
;
998 ack_frame_tick(get_utime());
999 if(amode
== ADVANCE_SKIPLAG_PENDING
)
1000 amode
= ADVANCE_SKIPLAG
;
1003 controls
.reset_framehold();
1004 movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
1005 if(amode
== ADVANCE_QUIT
&& queued_saves
.empty())
1009 if(queued_saves
.empty())
1011 if(r
> 0 || system_corrupt
) {
1012 first_round
= our_movie
.is_savestate
;
1014 amode
= ADVANCE_PAUSE
;
1017 stop_at_frame_active
= false;
1018 just_did_loadstate
= first_round
;
1019 controls
.reset_framehold();
1022 //Not exactly desriable, but this at least won't desync.
1023 stop_at_frame_active
= false;
1024 amode
= ADVANCE_PAUSE
;
1027 if(just_did_loadstate
) {
1028 //If we just loadstated, we are up to date.
1029 if(amode
== ADVANCE_QUIT
)
1031 platform::cancel_wait();
1032 platform::set_paused(amode
== ADVANCE_PAUSE
);
1033 platform::flush_command_queue();
1034 //We already have done the reset this frame if we are going to do one at all.
1035 movb
.get_movie().set_controls(movb
.update_controls(true));
1036 movb
.get_movie().set_all_DRDY();
1037 just_did_loadstate
= false;
1039 frame_irq_time
= get_utime() - time_x
;
1040 our_rom
->rtype
->emulate();
1041 time_x
= get_utime();
1042 if(amode
== ADVANCE_AUTO
)
1043 platform::wait(to_wait_frame(get_utime()));
1044 first_round
= false;
1045 lua_callback_do_frame();
1047 information_dispatch::do_dump_end();
1048 core_core::uninstall_all_handlers();
1052 void set_stop_at_frame(uint64_t frame
)
1054 stop_at_frame
= frame
;
1055 stop_at_frame_active
= (frame
!= 0);
1056 amode
= ADVANCE_AUTO
;
1057 platform::set_paused(false);