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"
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;
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.
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.
61 std::string pending_load
;
62 //Queued saves (all savestates).
63 std::set
<std::string
> queued_saves
;
64 bool stepping_into_save
;
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.
75 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
76 boolean_setting
pause_on_end("pause-on-end", false);
78 bool last_hires
= false;
79 bool last_interlace
= false;
82 class firmware_path_setting
: public setting
85 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "."; default_firmware
= true; }
86 void blank() throw(std::bad_alloc
, std::runtime_error
)
89 default_firmware
= true;
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
;
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();
123 if(amode
== ADVANCE_SUBFRAME
) {
124 if(!cancel_advance
&& !advanced_once
) {
125 platform::wait(advance_timeout_first
* 1000);
126 advanced_once
= true;
129 amode
= ADVANCE_PAUSE
;
130 cancel_advance
= false;
132 platform::set_paused(amode
== ADVANCE_PAUSE
);
133 } else if(amode
== ADVANCE_FRAME
) {
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();
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;
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);
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
);
178 controller_frame tmp
= controls
.commit(movb
.get_movie().get_current_frame());
179 lua_callback_do_input(tmp
, subframe
);
185 //Do pending load (automatically unpauses).
186 void mark_pending_load(const std::string
& filename
, int 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
);
202 queued_saves
.insert(filename
);
203 messages
<< "Pending save on '" << filename
<< "'" << std::endl
;
206 uint32_t lpalette
[0x80000];
209 static bool palette_init
= false;
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
)
236 else if(location_special
== SPECIAL_SAVEPOINT
)
238 else if(location_special
== SPECIAL_FRAME_VIDEO
)
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());
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
);
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" : "-");
263 else if(!mo
.readonly_mode())
265 else if(mo
.get_frame_count() >= mo
.get_current_frame())
269 _status
.set("Flags", x
.str());
271 if(save_jukebox
.size() > 0)
272 _status
.set("Saveslot", save_jukebox
[save_jukebox_pointer
]);
274 _status
.erase("Saveslot");
276 std::ostringstream x
;
277 x
<< get_framerate();
278 _status
.set("FPS", x
.str());
283 if(movb
.get_movie().readonly_mode())
284 c
= movb
.get_movie().get_controls();
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
);
292 char buffer
[MAX_DISPLAY_LENGTH
];
293 c
.display(pindex
, buffer
);
294 char y
[3] = {'P', 0, 0};
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();
318 return our_movie
.rtc_second
;
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();
330 last_interlace
= interlace
;
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
:
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
;
349 fclocks
= interlace
? DURATION_PAL_FIELD
: DURATION_PAL_FRAME
;
351 fclocks
= interlace
? DURATION_NTSC_FIELD
: DURATION_NTSC_FRAME
;
352 fps_n
= SNES::system
.cpu_frequency();
354 uint32_t g
= gcd(fps_n
, fps_d
);
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();
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;
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
;
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;
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;
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
);
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
<> rewind_movie("rewind-movie", "Rewind movie to the beginning",
619 "Syntax: rewind-movie\nRewind movie to the beginning\n",
620 []() throw(std::bad_alloc
, std::runtime_error
) {
621 mark_pending_load("SOME NONBLANK NAME", LOAD_STATE_BEGINNING
);
624 function_ptr_command
<> test1("test-1", "no description available", "No help available\n",
625 []() throw(std::bad_alloc
, std::runtime_error
) {
626 redraw_framebuffer(screen_nosignal
);
629 function_ptr_command
<> test2("test-2", "no description available", "No help available\n",
630 []() throw(std::bad_alloc
, std::runtime_error
) {
631 redraw_framebuffer(screen_corrupt
);
634 function_ptr_command
<> test3("test-3", "no description available", "No help available\n",
635 []() throw(std::bad_alloc
, std::runtime_error
) {
640 bool on_quit_prompt
= false;
641 class mywindowcallbacks
: public information_dispatch
644 mywindowcallbacks() : information_dispatch("mainloop-window-callbacks") {}
645 void on_new_dumper(const std::string
& n
)
647 update_movie_state();
649 void on_destroy_dumper(const std::string
& n
)
651 update_movie_state();
653 void on_close() throw()
656 amode
= ADVANCE_QUIT
;
657 platform::set_paused(false);
658 platform::cancel_wait();
661 on_quit_prompt
= true;
663 if(platform::modal_message("Really quit?", true)) {
664 amode
= ADVANCE_QUIT
;
665 platform::set_paused(false);
666 platform::cancel_wait();
670 on_quit_prompt
= false;
673 void send_analog(unsigned acid
, int32_t x
, int32_t y
)
675 auto g2
= get_framebuffer_size();
676 if(controls
.acid_is_mouse(acid
)) {
677 controls
.analog(acid
, x
- g2
.first
/ 2, y
- g2
.second
/ 2);
679 controls
.analog(acid
, x
/ 2 , y
/ 2);
682 void on_click(int32_t x
, int32_t y
, uint32_t buttonmask
) throw()
684 if(buttonmask
& ~prev_mouse_mask
& 1)
685 send_analog(0, x
, y
);
686 if(buttonmask
& ~prev_mouse_mask
& 2)
687 send_analog(1, x
, y
);
688 if(buttonmask
& ~prev_mouse_mask
& 4)
689 send_analog(2, x
, y
);
690 prev_mouse_mask
= buttonmask
;
694 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
698 if(pending_load
!= "") {
699 system_corrupt
= false;
700 if(loadmode
!= LOAD_STATE_BEGINNING
&& !do_load_state(pending_load
, loadmode
)) {
704 if(loadmode
== LOAD_STATE_BEGINNING
)
707 pending_reset_cycles
= -1;
708 amode
= ADVANCE_AUTO
;
709 platform::cancel_wait();
710 platform::set_paused(false);
711 if(!system_corrupt
) {
712 location_special
= SPECIAL_SAVEPOINT
;
713 update_movie_state();
714 information_dispatch::do_status_update();
715 platform::flush_command_queue();
722 //If there are pending saves, perform them.
725 if(!queued_saves
.empty()) {
726 stepping_into_save
= true;
727 SNES::system
.runtosave();
728 stepping_into_save
= false;
729 for(auto i
: queued_saves
)
732 queued_saves
.clear();
735 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
736 bool handle_reset(long cycles
)
740 video_refresh_done
= false;
742 messages
<< "SNES reset" << std::endl
;
743 else if(cycles
> 0) {
744 messages
<< "SNES delayed reset not implemented (doing immediate reset)" << std::endl
;
745 /* ... This code is just too buggy.
746 long cycles_executed = 0;
747 messages << "Executing delayed reset... This can take some time!" << std::endl;
748 while(cycles_executed < cycles && !video_refresh_done) {
749 //Poll inputs once in a while to prevent activating watchdog.
750 if(cycles_executed % 100 == 0)
751 platform::flush_command_queue();
755 if(!video_refresh_done)
756 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
758 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
761 SNES::system
.reset();
762 lua_callback_do_reset();
763 redraw_framebuffer(screen_nosignal
);
764 if(video_refresh_done
) {
765 to_wait_frame(get_utime());
771 bool handle_corrupt()
775 while(system_corrupt
) {
776 platform::cancel_wait();
777 platform::set_paused(true);
778 platform::flush_command_queue();
780 if(amode
== ADVANCE_QUIT
)
786 void print_controller_mappings()
788 for(unsigned i
= 0; i
< 8; i
++) {
789 std::string type
= controls
.lcid_to_typestring(i
);
790 messages
<< "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
791 controls
.lcid_to_pcid(i
) << " (" << type
<< ")" << std::endl
;
796 std::vector
<std::string
> get_jukebox_names()
801 void set_jukebox_names(const std::vector
<std::string
>& newj
)
804 if(save_jukebox_pointer
>= save_jukebox
.size())
805 save_jukebox_pointer
= 0;
806 update_movie_state();
809 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
, bool load_has_to_succeed
) throw(std::bad_alloc
,
812 //Basic initialization.
813 init_special_screens();
816 auto old_inteface
= SNES::interface
;
817 SNES::interface
= &intrf
;
820 //Load our given movie.
821 bool first_round
= false;
822 bool just_did_loadstate
= false;
824 do_load_state(initial
, LOAD_STATE_DEFAULT
);
825 location_special
= SPECIAL_SAVEPOINT
;
826 update_movie_state();
827 first_round
= our_movie
.is_savestate
;
828 just_did_loadstate
= first_round
;
829 } catch(std::bad_alloc
& e
) {
831 } catch(std::exception
& e
) {
832 messages
<< "ERROR: Can't load initial state: " << e
.what() << std::endl
;
833 if(load_has_to_succeed
) {
834 messages
<< "FATAL: Can't load movie" << std::endl
;
835 platform::fatal_error();
837 system_corrupt
= true;
838 update_movie_state();
839 redraw_framebuffer(screen_corrupt
);
842 lua_callback_startup();
844 //print_controller_mappings();
845 platform::set_paused(false);
846 amode
= ADVANCE_PAUSE
;
847 uint64_t time_x
= get_utime();
848 while(amode
!= ADVANCE_QUIT
|| !queued_saves
.empty()) {
849 if(handle_corrupt()) {
850 first_round
= our_movie
.is_savestate
;
851 just_did_loadstate
= first_round
;
854 long resetcycles
= -1;
855 ack_frame_tick(get_utime());
856 if(amode
== ADVANCE_SKIPLAG_PENDING
)
857 amode
= ADVANCE_SKIPLAG
;
860 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
861 if(amode
== ADVANCE_QUIT
&& queued_saves
.empty())
863 bool delayed_reset
= (resetcycles
> 0);
864 pending_reset_cycles
= -1;
865 if(!handle_reset(resetcycles
)) {
872 if(queued_saves
.empty())
874 if(r
> 0 || system_corrupt
) {
875 first_round
= our_movie
.is_savestate
;
876 amode
= ADVANCE_PAUSE
;
877 just_did_loadstate
= first_round
;
880 //Not exactly desriable, but this at least won't desync.
881 amode
= ADVANCE_PAUSE
;
884 if(just_did_loadstate
) {
885 //If we just loadstated, we are up to date.
886 if(amode
== ADVANCE_QUIT
)
888 amode
= ADVANCE_PAUSE
;
889 platform::cancel_wait();
890 platform::set_paused(true);
891 platform::flush_command_queue();
892 //We already have done the reset this frame if we are going to do one at all.
893 movb
.get_movie().set_controls(controls
.get(movb
.get_movie().get_current_frame()));
894 just_did_loadstate
= false;
896 frame_irq_time
= get_utime() - time_x
;
898 time_x
= get_utime();
899 if(amode
== ADVANCE_AUTO
)
900 platform::wait(to_wait_frame(get_utime()));
902 lua_callback_do_frame();
904 information_dispatch::do_dump_end();
905 SNES::interface
= old_inteface
;