1 #include "mainloop.hpp"
4 #include "controller.hpp"
5 #include "framebuffer.hpp"
6 #include "moviedata.hpp"
9 #include "framerate.hpp"
10 #include "memorywatch.hpp"
15 #include "moviefile.hpp"
18 #include "settings.hpp"
23 #include "memorymanip.hpp"
28 #include <snes/snes.hpp>
29 #include <ui-libsnes/libsnes.hpp>
30 #include "framerate.hpp"
32 #define SPECIAL_FRAME_START 0
33 #define SPECIAL_FRAME_VIDEO 1
34 #define SPECIAL_SAVEPOINT 2
35 #define SPECIAL_NONE 3
37 void update_movie_state();
43 ADVANCE_QUIT
, //Quit the emulator.
44 ADVANCE_AUTO
, //Normal (possibly slowed down play).
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.
53 std::map
<std::string
, std::string
> memory_watches
;
54 //Previous mouse mask.
55 int prev_mouse_mask
= 0;
56 //Flags related to repeating advance.
59 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
60 enum advance_mode amode
;
61 //Mode and filename of pending load, one of LOAD_* constants.
63 std::string pending_load
;
64 //Queued saves (all savestates).
65 std::set
<std::string
> queued_saves
;
66 bool stepping_into_save
;
68 std::vector
<std::string
> save_jukebox
;
69 size_t save_jukebox_pointer
;
70 //Emulator status area.
71 std::map
<std::string
, std::string
>* status
;
72 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
73 long pending_reset_cycles
= -1;
74 //Set by every video refresh.
75 bool video_refresh_done
;
76 //Special subframe location. One of SPECIAL_* constants.
79 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
81 bool last_hires
= false;
82 bool last_interlace
= false;
85 class firmware_path_setting
: public setting
88 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "."; default_firmware
= true; }
89 void blank() throw(std::bad_alloc
, std::runtime_error
)
92 default_firmware
= true;
97 return !default_firmware
;
100 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
102 _firmwarepath
= value
;
103 default_firmware
= false;
106 std::string
get() throw(std::bad_alloc
)
108 return _firmwarepath
;
111 operator std::string() throw(std::bad_alloc
)
113 return _firmwarepath
;
116 std::string _firmwarepath
;
117 bool default_firmware
;
118 } firmwarepath_setting
;
120 controls_t
movie_logic::update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
122 if(lua_requests_subframe_paint
)
123 redraw_framebuffer();
126 if(amode
== ADVANCE_SUBFRAME
) {
127 if(!cancel_advance
&& !advanced_once
) {
128 window::wait_usec(advance_timeout_first
* 1000);
129 advanced_once
= true;
132 amode
= ADVANCE_PAUSE
;
133 cancel_advance
= false;
135 window::paused(amode
== ADVANCE_PAUSE
);
136 } else if(amode
== ADVANCE_FRAME
) {
139 window::paused(amode
== ADVANCE_SKIPLAG
|| amode
== ADVANCE_PAUSE
);
140 cancel_advance
= false;
142 if(amode
== ADVANCE_SKIPLAG
)
143 amode
= ADVANCE_AUTO
;
144 location_special
= SPECIAL_NONE
;
145 update_movie_state();
147 if(amode
== ADVANCE_SKIPLAG_PENDING
)
148 amode
= ADVANCE_SKIPLAG
;
149 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
150 if(!cancel_advance
) {
151 window::wait_usec(advanced_once
? to_wait_frame(get_utime()) :
152 (advance_timeout_first
* 1000));
153 advanced_once
= true;
156 amode
= ADVANCE_PAUSE
;
157 cancel_advance
= false;
159 window::paused(amode
== ADVANCE_PAUSE
);
161 window::paused((amode
== ADVANCE_PAUSE
));
162 cancel_advance
= false;
164 location_special
= SPECIAL_FRAME_START
;
165 update_movie_state();
168 window::notify_screen_update();
169 window::poll_inputs();
170 if(!subframe
&& pending_reset_cycles
>= 0)
171 set_curcontrols_reset(pending_reset_cycles
);
173 set_curcontrols_reset(-1);
174 controls_t tmp
= get_current_controls(movb
.get_movie().get_current_frame());
175 lua_callback_do_input(tmp
, subframe
);
181 //Do pending load (automatically unpauses).
182 void mark_pending_load(const std::string
& filename
, int lmode
)
185 pending_load
= filename
;
186 amode
= ADVANCE_AUTO
;
187 window::cancel_wait();
188 window::paused(false);
191 void mark_pending_save(const std::string
& filename
, int smode
)
193 if(smode
== SAVE_MOVIE
) {
194 //Just do this immediately.
195 do_save_movie(filename
);
198 queued_saves
.insert(filename
);
199 window::message("Pending save on '" + filename
+ "'");
202 class dump_watch
: public av_snooper::dump_notification
204 void dump_starting() throw()
206 update_movie_state();
208 void dump_ending() throw()
210 update_movie_state();
214 uint32_t lpalette
[0x80000];
217 static bool palette_init
= false;
221 for(unsigned i
= 0; i
< 0x80000; i
++) {
222 unsigned l
= (i
>> 15) & 0xF;
223 unsigned r
= (i
>> 0) & 0x1F;
224 unsigned g
= (i
>> 5) & 0x1F;
225 unsigned b
= (i
>> 10) & 0x1F;
226 double _l
= static_cast<double>(l
);
227 double m
= 17.0 / 31.0;
228 r
= floor(m
* r
* _l
+ 0.5);
229 g
= floor(m
* g
* _l
+ 0.5);
230 b
= floor(m
* b
* _l
+ 0.5);
231 lpalette
[i
] = r
* 65536 + g
* 256 + b
;
236 void update_movie_state()
238 auto& _status
= window::get_emustatus();
239 if(!system_corrupt
) {
240 std::ostringstream x
;
241 x
<< movb
.get_movie().get_current_frame() << "(";
242 if(location_special
== SPECIAL_FRAME_START
)
244 else if(location_special
== SPECIAL_SAVEPOINT
)
246 else if(location_special
== SPECIAL_FRAME_VIDEO
)
249 x
<< movb
.get_movie().next_poll_number();
250 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
251 _status
["Frame"] = x
.str();
253 _status
["Frame"] = "N/A";
254 #ifndef NO_TIME_INTERCEPT
255 if(!system_corrupt
) {
256 time_t timevalue
= static_cast<time_t>(our_movie
.rtc_second
);
257 struct tm
* time_decompose
= gmtime(&timevalue
);
258 char datebuffer
[512];
259 strftime(datebuffer
, 511, "%Y%m%d(%a)T%H%M%S", time_decompose
);
260 _status
["RTC"] = datebuffer
;
262 _status
["RTC"] = "N/A";
266 std::ostringstream x
;
267 x
<< (system_corrupt
? "C" : "-");
268 x
<< (av_snooper::dump_in_progress() ? "D" : "-");
269 x
<< (last_hires
? "H" : "-");
270 x
<< (last_interlace
? "I" : "-");
272 x
<< (movb
.get_movie().readonly_mode() ? "P" : "R");
275 _status
["Flags"] = x
.str();
277 if(save_jukebox
.size() > 0)
278 _status
["Saveslot"] = save_jukebox
[save_jukebox_pointer
];
280 _status
.erase("Saveslot");
281 for(auto i
: memory_watches
) {
283 _status
["M[" + i
.first
+ "]"] = evaluate_watch(i
.second
);
289 if(movb
.get_movie().readonly_mode())
290 c
= movb
.get_movie().get_controls();
292 c
= get_current_controls(movb
.get_movie().get_current_frame());
293 for(unsigned i
= 0; i
< 8; i
++) {
294 unsigned pindex
= controller_index_by_logical(i
);
295 unsigned port
= pindex
>> 2;
296 unsigned dev
= pindex
& 3;
297 auto ctype
= controller_type_by_logical(i
);
298 std::ostringstream x
;
301 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_LEFT
) ? "l" : " ");
302 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_RIGHT
) ? "r" : " ");
303 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_UP
) ? "u" : " ");
304 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_DOWN
) ? "d" : " ");
305 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_A
) ? "A" : " ");
306 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_B
) ? "B" : " ");
307 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_X
) ? "X" : " ");
308 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_Y
) ? "Y" : " ");
309 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_L
) ? "L" : " ");
310 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_R
) ? "R" : " ");
311 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_START
) ? "S" : " ");
312 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_SELECT
) ? "s" : " ");
315 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_X
) << " ";
316 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_Y
) << " ";
317 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_LEFT
) ? "L" : " ");
318 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_RIGHT
) ? "R" : " ");
321 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_X
) << " ";
322 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_Y
) << " ";
323 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
) ? "T" : " ");
324 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
) ? "C" : " ");
325 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TURBO
) ? "t" : " ");
326 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
) ? "P" : " ");
329 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_X
) << " ";
330 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_Y
) << " ";
331 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_START
) ? "T" : " ");
332 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_TRIGGER
) ? "S" : " ");
337 char y
[3] = {'P', 0, 0};
339 _status
[std::string(y
)] = x
.str();
344 class my_interface
: public SNES::Interface
346 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
348 const char* _hint
= hint
;
349 std::string _hint2
= _hint
;
350 std::string fwp
= firmwarepath_setting
;
351 std::string finalpath
= fwp
+ "/" + _hint2
;
352 return finalpath
.c_str();
355 void videoRefresh(const uint32_t* data
, bool hires
, bool interlace
, bool overscan
)
358 last_interlace
= interlace
;
360 if(stepping_into_save
)
361 window::message("Got video refresh in runtosave, expect desyncs!");
362 video_refresh_done
= true;
363 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
365 sdump_frame(data
, (hires
? SDUMP_FLAG_HIRES
: 0) | (interlace
? SDUMP_FLAG_INTERLACED
: 0) |
366 (overscan
? SDUMP_FLAG_OVERSCAN
: 0) | (region
? SDUMP_FLAG_PAL
: 0));
367 } catch(std::exception
& e
) {
368 messages
<< "Error dumping frame: " << e
.what() << std::endl
;
370 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
371 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
372 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
373 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
374 lcscreen
ls(data
, hires
, interlace
, overscan
, region
);
376 location_special
= SPECIAL_FRAME_VIDEO
;
377 update_movie_state();
378 redraw_framebuffer();
379 uint32_t fps_n
, fps_d
;
382 fclocks
= interlace
? DURATION_PAL_FIELD
: DURATION_PAL_FRAME
;
384 fclocks
= interlace
? DURATION_NTSC_FIELD
: DURATION_NTSC_FRAME
;
385 fps_n
= SNES::system
.cpu_frequency();
387 uint32_t g
= gcd(fps_n
, fps_d
);
390 av_snooper::frame(ls
, fps_n
, fps_d
, true);
393 void audioSample(int16_t l_sample
, int16_t r_sample
)
396 sdump_sample(l_sample
, r_sample
);
397 } catch(std::exception
& e
) {
398 messages
<< "Error dumping sample: " << e
.what() << std::endl
;
401 uint16_t _l
= l_sample
;
402 uint16_t _r
= r_sample
;
403 window::play_audio_sample(_l
+ 32768, _r
+ 32768);
404 av_snooper::sample(_l
, _r
, true);
405 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
406 our_movie
.rtc_subsecond
+= 768;
407 while(our_movie
.rtc_subsecond
>= SNES::system
.apu_frequency()) {
408 our_movie
.rtc_second
++;
409 our_movie
.rtc_subsecond
-= SNES::system
.apu_frequency();
413 int16_t inputPoll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
416 x
= movb
.input_poll(port
, index
, id
);
417 //if(id == SNES_DEVICE_ID_JOYPAD_START)
418 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
419 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
);
426 function_ptr_command
<> count_rerecords("count-rerecords", "Count rerecords",
427 "Syntax: count-rerecords\nCounts rerecords.\n",
428 []() throw(std::bad_alloc
, std::runtime_error
) {
429 std::vector
<char> tmp
;
430 uint64_t x
= rrdata::write(tmp
);
431 messages
<< x
<< " rerecord(s)" << std::endl
;
434 function_ptr_command
<const std::string
&> quit_emulator("quit-emulator", "Quit the emulator",
435 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
436 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
437 if(args
== "/y" || window::modal_message("Really quit?", true)) {
438 amode
= ADVANCE_QUIT
;
439 window::paused(false);
440 window::cancel_wait();
444 function_ptr_command
<> pause_emulator("pause-emulator", "(Un)pause the emulator",
445 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
446 []() throw(std::bad_alloc
, std::runtime_error
) {
447 if(amode
!= ADVANCE_AUTO
) {
448 amode
= ADVANCE_AUTO
;
449 window::paused(false);
450 window::cancel_wait();
451 window::message("Unpaused");
453 window::cancel_wait();
454 cancel_advance
= false;
455 amode
= ADVANCE_PAUSE
;
456 window::message("Paused");
460 function_ptr_command
<> save_jukebox_prev("cycle-jukebox-backward", "Cycle save jukebox backwards",
461 "Syntax: cycle-jukebox-backwards\nCycle save jukebox backwards\n",
462 []() throw(std::bad_alloc
, std::runtime_error
) {
463 if(save_jukebox_pointer
== 0)
464 save_jukebox_pointer
= save_jukebox
.size() - 1;
466 save_jukebox_pointer
--;
467 if(save_jukebox_pointer
>= save_jukebox
.size())
468 save_jukebox_pointer
= 0;
469 update_movie_state();
470 window::notify_screen_update();
473 function_ptr_command
<> save_jukebox_next("cycle-jukebox-forward", "Cycle save jukebox forwards",
474 "Syntax: cycle-jukebox-forwards\nCycle save jukebox forwards\n",
475 []() throw(std::bad_alloc
, std::runtime_error
) {
476 if(save_jukebox_pointer
== save_jukebox
.size() - 1)
477 save_jukebox_pointer
= 0;
479 save_jukebox_pointer
++;
480 if(save_jukebox_pointer
>= save_jukebox
.size())
481 save_jukebox_pointer
= 0;
482 update_movie_state();
483 window::notify_screen_update();
486 function_ptr_command
<arg_filename
> add_jukebox("add-jukebox-save", "Add save to jukebox",
487 "Syntax: add-jukebox-save\nAdd save to jukebox\n",
488 [](arg_filename filename
) throw(std::bad_alloc
, std::runtime_error
) {
489 save_jukebox
.push_back(filename
);
490 update_movie_state();
491 window::notify_screen_update();
494 function_ptr_command
<> load_jukebox("load-jukebox", "Load save from jukebox",
495 "Syntax: load-jukebox\nLoad save from jukebox\n",
496 []() throw(std::bad_alloc
, std::runtime_error
) {
497 if(!save_jukebox
.size())
498 throw std::runtime_error("No saves in jukebox");
499 mark_pending_load(save_jukebox
[save_jukebox_pointer
], LOAD_STATE_CURRENT
);
502 function_ptr_command
<> save_jukebox_c("save-jukebox", "Save save to jukebox",
503 "Syntax: save-jukebox\nSave save to jukebox\n",
504 []() throw(std::bad_alloc
, std::runtime_error
) {
505 if(!save_jukebox
.size())
506 throw std::runtime_error("No saves in jukebox");
507 mark_pending_save(save_jukebox
[save_jukebox_pointer
], SAVE_STATE
);
510 function_ptr_command
<> padvance_frame("+advance-frame", "Advance one frame",
511 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
512 []() throw(std::bad_alloc
, std::runtime_error
) {
513 amode
= ADVANCE_FRAME
;
514 cancel_advance
= false;
515 advanced_once
= false;
516 window::cancel_wait();
517 window::paused(false);
520 function_ptr_command
<> nadvance_frame("-advance-frame", "Advance one frame",
521 "No help available\n",
522 []() throw(std::bad_alloc
, std::runtime_error
) {
523 cancel_advance
= true;
524 window::cancel_wait();
525 window::paused(false);
528 function_ptr_command
<> padvance_poll("+advance-poll", "Advance one subframe",
529 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
530 []() throw(std::bad_alloc
, std::runtime_error
) {
531 amode
= ADVANCE_SUBFRAME
;
532 cancel_advance
= false;
533 advanced_once
= false;
534 window::cancel_wait();
535 window::paused(false);
538 function_ptr_command
<> nadvance_poll("-advance-poll", "Advance one subframe",
539 "No help available\n",
540 []() throw(std::bad_alloc
, std::runtime_error
) {
541 cancel_advance
= true;
542 window::cancel_wait();
543 window::paused(false);
546 function_ptr_command
<> advance_skiplag("advance-skiplag", "Skip to next poll",
547 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
548 []() throw(std::bad_alloc
, std::runtime_error
) {
549 amode
= ADVANCE_SKIPLAG
;
550 window::cancel_wait();
551 window::paused(false);
554 function_ptr_command
<> reset_c("reset", "Reset the SNES",
555 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
556 []() throw(std::bad_alloc
, std::runtime_error
) {
557 pending_reset_cycles
= 0;
560 function_ptr_command
<arg_filename
> load_c("load", "Load savestate (current mode)",
561 "Syntax: load <file>\nLoads SNES state from <file> in current mode\n",
562 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
563 mark_pending_load(args
, LOAD_STATE_CURRENT
);
566 function_ptr_command
<arg_filename
> load_state_c("load-state", "Load savestate (R/W)",
567 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
568 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
569 mark_pending_load(args
, LOAD_STATE_RW
);
572 function_ptr_command
<arg_filename
> load_readonly("load-readonly", "Load savestate (RO)",
573 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
574 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
575 mark_pending_load(args
, LOAD_STATE_RO
);
578 function_ptr_command
<arg_filename
> load_preserve("load-preserve", "Load savestate (preserve input)",
579 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
580 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
581 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
584 function_ptr_command
<arg_filename
> load_movie_c("load-movie", "Load movie",
585 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
586 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
587 mark_pending_load(args
, LOAD_STATE_MOVIE
);
591 function_ptr_command
<arg_filename
> save_state("save-state", "Save state",
592 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
593 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
594 mark_pending_save(args
, SAVE_STATE
);
597 function_ptr_command
<arg_filename
> save_movie("save-movie", "Save movie",
598 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
599 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
600 mark_pending_save(args
, SAVE_MOVIE
);
603 function_ptr_command
<> set_rwmode("set-rwmode", "Switch to read/write mode",
604 "Syntax: set-rwmode\nSwitches to read/write mode\n",
605 []() throw(std::bad_alloc
, std::runtime_error
) {
606 movb
.get_movie().readonly_mode(false);
607 lua_callback_do_readwrite();
608 update_movie_state();
609 window::notify_screen_update();
612 function_ptr_command
<> set_romode("set-romode", "Switch to read-only mode",
613 "Syntax: set-romode\nSwitches to read-only mode\n",
614 []() throw(std::bad_alloc
, std::runtime_error
) {
615 movb
.get_movie().readonly_mode(true);
616 update_movie_state();
617 window::notify_screen_update();
620 function_ptr_command
<> toggle_rwmode("toggle-rwmode", "Toggle read/write mode",
621 "Syntax: toggle-rwmode\nToggles read/write mode\n",
622 []() throw(std::bad_alloc
, std::runtime_error
) {
623 bool c
= movb
.get_movie().readonly_mode();
624 movb
.get_movie().readonly_mode(!c
);
626 lua_callback_do_readwrite();
627 update_movie_state();
628 window::notify_screen_update();
631 function_ptr_command
<> repaint("repaint", "Redraw the screen",
632 "Syntax: repaint\nRedraws the screen\n",
633 []() throw(std::bad_alloc
, std::runtime_error
) {
634 redraw_framebuffer();
637 function_ptr_command
<tokensplitter
&> add_watch("add-watch", "Add a memory watch",
638 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
639 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
640 std::string name
= t
;
641 if(name
== "" || t
.tail() == "")
642 throw std::runtime_error("syntax: add-watch <name> <expr>");
643 std::cerr
<< "Add watch: '" << name
<< "'" << std::endl
;
644 memory_watches
[name
] = t
.tail();
645 update_movie_state();
648 function_ptr_command
<tokensplitter
&> remove_watch("remove-watch", "Remove a memory watch",
649 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
650 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
651 std::string name
= t
;
652 if(name
== "" || t
.tail() != "") {
653 messages
<< "syntax: remove-watch <name>" << std::endl
;
656 std::cerr
<< "Erase watch: '" << name
<< "'" << std::endl
;
657 memory_watches
.erase(name
);
658 auto& _status
= window::get_emustatus();
659 _status
.erase("M[" + name
+ "]");
660 update_movie_state();
664 function_ptr_command
<> test1("test-1", "no description available", "No help available\n",
665 []() throw(std::bad_alloc
, std::runtime_error
) {
666 framebuffer
= screen_nosignal
;
667 redraw_framebuffer();
670 function_ptr_command
<> test2("test-2", "no description available", "No help available\n",
671 []() throw(std::bad_alloc
, std::runtime_error
) {
672 framebuffer
= screen_corrupt
;
673 redraw_framebuffer();
676 function_ptr_command
<> test3("test-3", "no description available", "No help available\n",
677 []() throw(std::bad_alloc
, std::runtime_error
) {
682 bool on_quit_prompt
= false;
683 class mywindowcallbacks
: public window_callback
686 void on_close() throw()
689 amode
= ADVANCE_QUIT
;
690 window::paused(false);
691 window::cancel_wait();
694 on_quit_prompt
= true;
696 if(window::modal_message("Really quit?", true)) {
697 amode
= ADVANCE_QUIT
;
698 window::paused(false);
699 window::cancel_wait();
703 on_quit_prompt
= false;
706 void on_click(int32_t x
, int32_t y
, uint32_t buttonmask
) throw()
708 if(buttonmask
& ~prev_mouse_mask
& 1)
709 send_analog_input(x
, y
, 0);
710 if(buttonmask
& ~prev_mouse_mask
& 2)
711 send_analog_input(x
, y
, 1);
712 if(buttonmask
& ~prev_mouse_mask
& 4)
713 send_analog_input(x
, y
, 2);
714 prev_mouse_mask
= buttonmask
;
718 //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
722 if(pending_load
!= "") {
723 system_corrupt
= false;
724 if(!do_load_state(pending_load
, loadmode
)) {
728 redraw_framebuffer();
730 pending_reset_cycles
= -1;
731 amode
= ADVANCE_AUTO
;
732 window::cancel_wait();
733 window::paused(false);
734 if(!system_corrupt
) {
735 location_special
= SPECIAL_SAVEPOINT
;
736 update_movie_state();
737 window::notify_screen_update();
738 window::poll_inputs();
745 //If there are pending saves, perform them.
748 if(!queued_saves
.empty()) {
749 stepping_into_save
= true;
750 SNES::system
.runtosave();
751 stepping_into_save
= false;
752 for(auto i
: queued_saves
)
755 queued_saves
.clear();
758 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
759 bool handle_reset(long cycles
)
763 video_refresh_done
= false;
765 window::message("SNES reset");
766 else if(cycles
> 0) {
767 window::message("SNES delayed reset not implemented (doing immediate reset)");
768 /* ... This code is just too buggy.
769 long cycles_executed = 0;
770 messages << "Executing delayed reset... This can take some time!" << std::endl;
771 while(cycles_executed < cycles && !video_refresh_done) {
772 //Poll inputs once in a while to prevent activating watchdog.
773 if(cycles_executed % 100 == 0)
774 window::poll_inputs();
778 if(!video_refresh_done)
779 messages << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
781 messages << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
784 SNES::system
.reset();
785 framebuffer
= screen_nosignal
;
786 lua_callback_do_reset();
787 redraw_framebuffer();
788 if(video_refresh_done
) {
789 to_wait_frame(get_utime());
795 bool handle_corrupt()
799 while(system_corrupt
) {
800 redraw_framebuffer();
801 window::cancel_wait();
802 window::paused(true);
803 window::poll_inputs();
805 if(amode
== ADVANCE_QUIT
)
811 void print_controller_mappings()
813 for(unsigned i
= 0; i
< 8; i
++) {
814 std::string type
= "unknown";
815 if(controller_type_by_logical(i
) == DT_NONE
)
816 type
= "disconnected";
817 if(controller_type_by_logical(i
) == DT_GAMEPAD
)
819 if(controller_type_by_logical(i
) == DT_MOUSE
)
821 if(controller_type_by_logical(i
) == DT_SUPERSCOPE
)
823 if(controller_type_by_logical(i
) == DT_JUSTIFIER
)
825 messages
<< "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
826 controller_index_by_logical(i
) << " (" << type
<< ")" << std::endl
;
831 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
, bool load_has_to_succeed
) throw(std::bad_alloc
,
834 //Basic initialization.
835 init_special_screens();
838 auto old_inteface
= SNES::interface
;
839 SNES::interface
= &intrf
;
840 intrf
.initialize(&intrf
);
841 status
= &window::get_emustatus();
842 window_callback::set_callback_handler(mywcb
);
844 //Load our given movie.
845 bool first_round
= false;
846 bool just_did_loadstate
= false;
848 do_load_state(initial
, LOAD_STATE_DEFAULT
);
849 location_special
= SPECIAL_SAVEPOINT
;
850 update_movie_state();
851 first_round
= our_movie
.is_savestate
;
852 just_did_loadstate
= first_round
;
853 } catch(std::bad_alloc
& e
) {
855 } catch(std::exception
& e
) {
856 messages
<< "ERROR: Can't load initial state: " << e
.what() << std::endl
;
857 if(load_has_to_succeed
) {
858 messages
<< "FATAL: Can't load movie" << std::endl
;
859 window::fatal_error();
861 system_corrupt
= true;
862 update_movie_state();
863 framebuffer
= screen_corrupt
;
864 redraw_framebuffer();
867 lua_callback_startup();
869 //print_controller_mappings();
870 av_snooper::add_dump_notifier(dumpwatch
);
871 window::set_main_surface(main_screen
);
872 redraw_framebuffer();
873 window::paused(false);
874 amode
= ADVANCE_PAUSE
;
875 while(amode
!= ADVANCE_QUIT
) {
876 if(handle_corrupt()) {
877 first_round
= our_movie
.is_savestate
;
878 just_did_loadstate
= first_round
;
881 long resetcycles
= -1;
882 ack_frame_tick(get_utime());
883 if(amode
== ADVANCE_SKIPLAG_PENDING
)
884 amode
= ADVANCE_SKIPLAG
;
887 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
888 if(amode
== ADVANCE_QUIT
)
890 bool delayed_reset
= (resetcycles
> 0);
891 pending_reset_cycles
= -1;
892 if(!handle_reset(resetcycles
)) {
898 int r
= handle_load();
899 if(r
> 0 || system_corrupt
) {
900 first_round
= our_movie
.is_savestate
;
901 amode
= ADVANCE_PAUSE
;
902 just_did_loadstate
= first_round
;
905 //Not exactly desriable, but this at least won't desync.
906 amode
= ADVANCE_PAUSE
;
909 if(just_did_loadstate
) {
910 if(amode
== ADVANCE_QUIT
)
912 amode
= ADVANCE_PAUSE
;
913 redraw_framebuffer();
914 window::cancel_wait();
915 window::paused(true);
916 window::poll_inputs();
917 //We already have done the reset this frame if we are going to do one at all.
918 movb
.get_movie().set_controls(get_current_controls(movb
.get_movie().get_current_frame()));
919 just_did_loadstate
= false;
922 if(amode
== ADVANCE_AUTO
)
923 window::wait_usec(to_wait_frame(get_utime()));
926 av_snooper::end(true);
927 SNES::interface
= old_inteface
;