1 #include "mainloop.hpp"
4 #include "controller.hpp"
5 #include "framebuffer.hpp"
6 #include "moviedata.hpp"
8 #include "framerate.hpp"
9 #include "memorywatch.hpp"
14 #include "moviefile.hpp"
17 #include "settings.hpp"
22 #include "memorymanip.hpp"
27 #include <snes/snes.hpp>
28 #include <ui-libsnes/libsnes.hpp>
29 #include "framerate.hpp"
31 #define SPECIAL_FRAME_START 0
32 #define SPECIAL_FRAME_VIDEO 1
33 #define SPECIAL_SAVEPOINT 2
34 #define SPECIAL_NONE 3
36 #define BUTTON_LEFT 0 //Gamepad
37 #define BUTTON_RIGHT 1 //Gamepad
38 #define BUTTON_UP 2 //Gamepad
39 #define BUTTON_DOWN 3 //Gamepad
40 #define BUTTON_A 4 //Gamepad
41 #define BUTTON_B 5 //Gamepad
42 #define BUTTON_X 6 //Gamepad
43 #define BUTTON_Y 7 //Gamepad
44 #define BUTTON_L 8 //Gamepad & Mouse
45 #define BUTTON_R 9 //Gamepad & Mouse
46 #define BUTTON_SELECT 10 //Gamepad
47 #define BUTTON_START 11 //Gamepad & Justifier
48 #define BUTTON_TRIGGER 12 //Superscope.
49 #define BUTTON_CURSOR 13 //Superscope & Justifier
50 #define BUTTON_PAUSE 14 //Superscope
51 #define BUTTON_TURBO 15 //Superscope
53 void update_movie_state();
59 ADVANCE_QUIT
, //Quit the emulator.
60 ADVANCE_AUTO
, //Normal (possibly slowed down play).
61 ADVANCE_FRAME
, //Frame advance.
62 ADVANCE_SUBFRAME
, //Subframe advance.
63 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
64 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
65 ADVANCE_PAUSE
, //Unconditional pause.
69 std::map
<std::string
, std::string
> memory_watches
;
70 //Previous mouse mask.
71 int prev_mouse_mask
= 0;
72 //Flags related to repeating advance.
75 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
76 enum advance_mode amode
;
77 //Mode and filename of pending load, one of LOAD_* constants.
79 std::string pending_load
;
80 //Queued saves (all savestates).
81 std::set
<std::string
> queued_saves
;
82 bool stepping_into_save
;
84 controls_t curcontrols
;
85 controls_t autoheld_controls
;
86 //Emulator status area.
87 std::map
<std::string
, std::string
>* status
;
88 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
89 long pending_reset_cycles
= -1;
90 //Set by every video refresh.
91 bool video_refresh_done
;
92 //Special subframe location. One of SPECIAL_* constants.
95 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
97 void send_analog_input(int32_t x
, int32_t y
, unsigned index
)
99 if(controller_ismouse_by_analog(index
)) {
101 y
-= (framebuffer
.height
/ 2);
106 int aindex
= controller_index_by_analog(index
);
108 messages
<< "No analog controller in slot #" << (index
+ 1) << std::endl
;
111 curcontrols(aindex
>> 2, aindex
& 3, 0) = x
;
112 curcontrols(aindex
>> 2, aindex
& 3, 1) = y
;
117 class firmware_path_setting
: public setting
120 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "./"; default_firmware
= true; }
121 void blank() throw(std::bad_alloc
, std::runtime_error
)
123 _firmwarepath
= "./";
124 default_firmware
= true;
127 bool is_set() throw()
129 return !default_firmware
;
132 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
134 _firmwarepath
= value
;
135 default_firmware
= false;
138 std::string
get() throw(std::bad_alloc
)
140 return _firmwarepath
;
143 operator std::string() throw(std::bad_alloc
)
145 return _firmwarepath
;
148 std::string _firmwarepath
;
149 bool default_firmware
;
150 } firmwarepath_setting
;
152 controls_t
movie_logic::update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
154 if(lua_requests_subframe_paint
)
155 redraw_framebuffer();
158 if(amode
== ADVANCE_SUBFRAME
) {
159 if(!cancel_advance
&& !advanced_once
) {
160 window::wait_msec(advance_timeout_first
);
161 advanced_once
= true;
164 amode
= ADVANCE_PAUSE
;
165 cancel_advance
= false;
167 window::paused(amode
== ADVANCE_PAUSE
);
168 } else if(amode
== ADVANCE_FRAME
) {
171 window::paused(amode
== ADVANCE_SKIPLAG
|| amode
== ADVANCE_PAUSE
);
172 cancel_advance
= false;
174 if(amode
== ADVANCE_SKIPLAG
)
175 amode
= ADVANCE_AUTO
;
176 location_special
= SPECIAL_NONE
;
177 update_movie_state();
179 if(amode
== ADVANCE_SKIPLAG_PENDING
)
180 amode
= ADVANCE_SKIPLAG
;
181 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
182 if(!cancel_advance
) {
183 window::wait_msec(advanced_once
? to_wait_frame(get_ticks_msec()) :
184 advance_timeout_first
);
185 advanced_once
= true;
188 amode
= ADVANCE_PAUSE
;
189 cancel_advance
= false;
191 window::paused(amode
== ADVANCE_PAUSE
);
193 window::paused((amode
== ADVANCE_PAUSE
));
194 cancel_advance
= false;
196 location_special
= SPECIAL_FRAME_START
;
197 update_movie_state();
199 window::notify_screen_update();
200 window::poll_inputs();
201 if(!subframe
&& pending_reset_cycles
>= 0) {
202 curcontrols(CONTROL_SYSTEM_RESET
) = 1;
203 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = pending_reset_cycles
/ 10000;
204 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = pending_reset_cycles
% 10000;
205 } else if(!subframe
) {
206 curcontrols(CONTROL_SYSTEM_RESET
) = 0;
207 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = 0;
208 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = 0;
210 controls_t tmp
= curcontrols
^ autoheld_controls
;
211 lua_callback_do_input(tmp
, subframe
);
217 std::map
<std::string
, std::pair
<unsigned, unsigned>> buttonmap
;
219 const char* buttonnames
[] = {
220 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
224 void init_buttonmap()
229 for(unsigned i
= 0; i
< 8; i
++)
230 for(unsigned j
= 0; j
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); j
++) {
231 std::ostringstream x
;
232 x
<< (i
+ 1) << buttonnames
[j
];
233 buttonmap
[x
.str()] = std::make_pair(i
, j
);
239 void do_button_action(unsigned ui_id
, unsigned button
, short newstate
, bool do_xor
= false)
241 enum devicetype_t p
= controller_type_by_logical(ui_id
);
242 int x
= controller_index_by_logical(ui_id
);
246 messages
<< "No such controller #" << (ui_id
+ 1) << std::endl
;
250 case BUTTON_UP
: bid
= SNES_DEVICE_ID_JOYPAD_UP
; break;
251 case BUTTON_DOWN
: bid
= SNES_DEVICE_ID_JOYPAD_DOWN
; break;
252 case BUTTON_LEFT
: bid
= SNES_DEVICE_ID_JOYPAD_LEFT
; break;
253 case BUTTON_RIGHT
: bid
= SNES_DEVICE_ID_JOYPAD_RIGHT
; break;
254 case BUTTON_A
: bid
= SNES_DEVICE_ID_JOYPAD_A
; break;
255 case BUTTON_B
: bid
= SNES_DEVICE_ID_JOYPAD_B
; break;
256 case BUTTON_X
: bid
= SNES_DEVICE_ID_JOYPAD_X
; break;
257 case BUTTON_Y
: bid
= SNES_DEVICE_ID_JOYPAD_Y
; break;
258 case BUTTON_L
: bid
= SNES_DEVICE_ID_JOYPAD_L
; break;
259 case BUTTON_R
: bid
= SNES_DEVICE_ID_JOYPAD_R
; break;
260 case BUTTON_SELECT
: bid
= SNES_DEVICE_ID_JOYPAD_SELECT
; break;
261 case BUTTON_START
: bid
= SNES_DEVICE_ID_JOYPAD_START
; break;
263 messages
<< "Invalid button for gamepad" << std::endl
;
269 case BUTTON_L
: bid
= SNES_DEVICE_ID_MOUSE_LEFT
; break;
270 case BUTTON_R
: bid
= SNES_DEVICE_ID_MOUSE_RIGHT
; break;
272 messages
<< "Invalid button for mouse" << std::endl
;
278 case BUTTON_START
: bid
= SNES_DEVICE_ID_JUSTIFIER_START
; break;
279 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_JUSTIFIER_TRIGGER
; break;
281 messages
<< "Invalid button for justifier" << std::endl
;
287 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
; break;
288 case BUTTON_CURSOR
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
; break;
289 case BUTTON_PAUSE
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
; break;
290 case BUTTON_TURBO
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TURBO
; break;
292 messages
<< "Invalid button for superscope" << std::endl
;
298 autoheld_controls((x
& 4) ? 1 : 0, x
& 3, bid
) ^= newstate
;
300 curcontrols((x
& 4) ? 1 : 0, x
& 3, bid
) = newstate
;
304 //Do pending load (automatically unpauses).
305 void mark_pending_load(const std::string
& filename
, int lmode
)
308 pending_load
= filename
;
309 amode
= ADVANCE_AUTO
;
310 window::cancel_wait();
311 window::paused(false);
314 //Mark pending save (movies save immediately).
315 void mark_pending_save(const std::string
& filename
, int smode
)
317 if(smode
== SAVE_MOVIE
) {
318 //Just do this immediately.
319 do_save_movie(filename
);
322 queued_saves
.insert(filename
);
323 window::message("Pending save on '" + filename
+ "'");
326 class dump_watch
: public av_snooper::dump_notification
328 void dump_starting() throw()
330 update_movie_state();
332 void dump_ending() throw()
334 update_movie_state();
339 void update_movie_state()
341 auto& _status
= window::get_emustatus();
343 std::ostringstream x
;
344 x
<< movb
.get_movie().get_current_frame() << "(";
345 if(location_special
== SPECIAL_FRAME_START
)
347 else if(location_special
== SPECIAL_SAVEPOINT
)
349 else if(location_special
== SPECIAL_FRAME_VIDEO
)
352 x
<< movb
.get_movie().next_poll_number();
353 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
354 _status
["Frame"] = x
.str();
357 std::ostringstream x
;
358 if(movb
.get_movie().readonly_mode())
362 if(av_snooper::dump_in_progress())
364 _status
["Flags"] = x
.str();
366 for(auto i
= memory_watches
.begin(); i
!= memory_watches
.end(); i
++) {
368 _status
["M[" + i
->first
+ "]"] = evaluate_watch(i
->second
);
373 if(movb
.get_movie().readonly_mode())
374 c
= movb
.get_movie().get_controls();
376 c
= curcontrols
^ autoheld_controls
;
377 for(unsigned i
= 0; i
< 8; i
++) {
378 unsigned pindex
= controller_index_by_logical(i
);
379 unsigned port
= pindex
>> 2;
380 unsigned dev
= pindex
& 3;
381 auto ctype
= controller_type_by_logical(i
);
382 std::ostringstream x
;
385 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_LEFT
) ? "l" : " ");
386 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_RIGHT
) ? "r" : " ");
387 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_UP
) ? "u" : " ");
388 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_DOWN
) ? "d" : " ");
389 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_A
) ? "A" : " ");
390 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_B
) ? "B" : " ");
391 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_X
) ? "X" : " ");
392 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_Y
) ? "Y" : " ");
393 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_L
) ? "L" : " ");
394 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_R
) ? "R" : " ");
395 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_START
) ? "S" : " ");
396 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_SELECT
) ? "s" : " ");
399 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_X
) << " ";
400 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_Y
) << " ";
401 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_LEFT
) ? "L" : " ");
402 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_RIGHT
) ? "R" : " ");
405 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_X
) << " ";
406 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_Y
) << " ";
407 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
) ? "T" : " ");
408 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
) ? "C" : " ");
409 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TURBO
) ? "t" : " ");
410 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
) ? "P" : " ");
413 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_X
) << " ";
414 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_Y
) << " ";
415 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_START
) ? "T" : " ");
416 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_TRIGGER
) ? "S" : " ");
421 char y
[3] = {'P', 0, 0};
423 _status
[std::string(y
)] = x
.str();
428 class my_interface
: public SNES::Interface
430 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
432 return static_cast<std::string
>(firmwarepath_setting
).c_str();
435 void video_refresh(const uint16_t *data
, bool hires
, bool interlace
, bool overscan
)
437 if(stepping_into_save
)
438 window::message("Got video refresh in runtosave, expect desyncs!");
439 video_refresh_done
= true;
440 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
441 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
442 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
443 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
444 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
445 lcscreen
ls(data
, hires
, interlace
, overscan
, region
);
447 location_special
= SPECIAL_FRAME_VIDEO
;
448 update_movie_state();
449 redraw_framebuffer();
450 uint32_t fps_n
, fps_d
;
458 av_snooper::frame(ls
, fps_n
, fps_d
, true);
461 void audio_sample(int16_t l_sample
, int16_t r_sample
)
463 uint16_t _l
= l_sample
;
464 uint16_t _r
= r_sample
;
465 window::play_audio_sample(_l
+ 32768, _r
+ 32768);
466 av_snooper::sample(_l
, _r
, true);
469 void audio_sample(uint16_t l_sample
, uint16_t r_sample
)
471 //Yes, this interface is broken. The samples are signed but are passed as unsigned!
472 window::play_audio_sample(l_sample
+ 32768, r_sample
+ 32768);
473 av_snooper::sample(l_sample
, r_sample
, true);
476 int16_t input_poll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
479 x
= movb
.input_poll(port
, index
, id
);
480 //if(id == SNES_DEVICE_ID_JOYPAD_START)
481 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
482 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
);
489 function_ptr_command
<const std::string
&> quit_emulator("quit-emulator", "Quit the emulator",
490 "Syntax: quit-emulator [/y]\nQuits emulator (/y => don't ask for confirmation).\n",
491 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
492 if(args
== "/y" || window::modal_message("Really quit?", true)) {
493 amode
= ADVANCE_QUIT
;
494 window::paused(false);
495 window::cancel_wait();
499 function_ptr_command
<> pause_emulator("pause-emulator", "(Un)pause the emulator",
500 "Syntax: pause-emulator\n(Un)pauses the emulator.\n",
501 []() throw(std::bad_alloc
, std::runtime_error
) {
502 if(amode
!= ADVANCE_AUTO
) {
503 amode
= ADVANCE_AUTO
;
504 window::paused(false);
505 window::cancel_wait();
506 window::message("Unpaused");
508 window::cancel_wait();
509 cancel_advance
= false;
510 amode
= ADVANCE_PAUSE
;
511 window::message("Paused");
515 function_ptr_command
<> padvance_frame("+advance-frame", "Advance one frame",
516 "Syntax: +advance-frame\nAdvances the emulation by one frame.\n",
517 []() throw(std::bad_alloc
, std::runtime_error
) {
518 amode
= ADVANCE_FRAME
;
519 cancel_advance
= false;
520 advanced_once
= false;
521 window::cancel_wait();
522 window::paused(false);
525 function_ptr_command
<> nadvance_frame("-advance-frame", "Advance one frame",
526 "No help available\n",
527 []() throw(std::bad_alloc
, std::runtime_error
) {
528 cancel_advance
= true;
529 window::cancel_wait();
530 window::paused(false);
533 function_ptr_command
<> padvance_poll("+advance-poll", "Advance one subframe",
534 "Syntax: +advance-poll\nAdvances the emulation by one subframe.\n",
535 []() throw(std::bad_alloc
, std::runtime_error
) {
536 amode
= ADVANCE_SUBFRAME
;
537 cancel_advance
= false;
538 advanced_once
= false;
539 window::cancel_wait();
540 window::paused(false);
543 function_ptr_command
<> nadvance_poll("-advance-poll", "Advance one subframe",
544 "No help available\n",
545 []() throw(std::bad_alloc
, std::runtime_error
) {
546 cancel_advance
= true;
547 window::cancel_wait();
548 window::paused(false);
551 function_ptr_command
<> advance_skiplag("advance-skiplag", "Skip to next poll",
552 "Syntax: advance-skiplag\nAdvances the emulation to the next poll.\n",
553 []() throw(std::bad_alloc
, std::runtime_error
) {
554 amode
= ADVANCE_SKIPLAG
;
555 window::cancel_wait();
556 window::paused(false);
559 function_ptr_command
<> reset_c("reset", "Reset the SNES",
560 "Syntax: reset\nResets the SNES in beginning of the next frame.\n",
561 []() throw(std::bad_alloc
, std::runtime_error
) {
562 pending_reset_cycles
= 0;
565 function_ptr_command
<arg_filename
> load_state_c("load-state", "Load savestate (R/W)",
566 "Syntax: load-state <file>\nLoads SNES state from <file> in Read/Write mode\n",
567 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
568 mark_pending_load(args
, LOAD_STATE_RW
);
571 function_ptr_command
<arg_filename
> load_readonly("load-readonly", "Load savestate (RO)",
572 "Syntax: load-readonly <file>\nLoads SNES state from <file> in read-only mode\n",
573 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
574 mark_pending_load(args
, LOAD_STATE_RO
);
577 function_ptr_command
<arg_filename
> load_preserve("load-preserve", "Load savestate (preserve input)",
578 "Syntax: load-preserve <file>\nLoads SNES state from <file> preserving input\n",
579 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
580 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
583 function_ptr_command
<arg_filename
> load_movie_c("load-movie", "Load movie",
584 "Syntax: load-movie <file>\nLoads SNES movie from <file>\n",
585 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
586 mark_pending_load(args
, LOAD_STATE_MOVIE
);
590 function_ptr_command
<arg_filename
> save_state("save-state", "Save state",
591 "Syntax: save-state <file>\nSaves SNES state to <file>\n",
592 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
593 mark_pending_save(args
, SAVE_STATE
);
596 function_ptr_command
<arg_filename
> save_movie("save-movie", "Save movie",
597 "Syntax: save-movie <file>\nSaves SNES movie to <file>\n",
598 [](arg_filename args
) throw(std::bad_alloc
, std::runtime_error
) {
599 mark_pending_save(args
, SAVE_MOVIE
);
602 function_ptr_command
<> set_rwmode("set-rwmode", "Switch to read/write mode",
603 "Syntax: set-rwmode\nSwitches to read/write mode\n",
604 []() throw(std::bad_alloc
, std::runtime_error
) {
605 movb
.get_movie().readonly_mode(false);
606 lua_callback_do_readwrite();
607 update_movie_state();
608 window::notify_screen_update();
611 function_ptr_command
<> repaint("repaint", "Redraw the screen",
612 "Syntax: repaint\nRedraws the screen\n",
613 []() throw(std::bad_alloc
, std::runtime_error
) {
614 redraw_framebuffer();
617 function_ptr_command
<const std::string
&> add_watch("add-watch", "Add a memory watch",
618 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
619 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
620 tokensplitter
t(args
);
621 std::string name
= t
;
622 if(name
== "" || t
.tail() == "")
623 throw std::runtime_error("syntax: add-watch <name> <expr>");
624 std::cerr
<< "Add watch: '" << name
<< "'" << std::endl
;
625 memory_watches
[name
] = t
.tail();
626 update_movie_state();
629 function_ptr_command
<const std::string
&> remove_watch("remove-watch", "Remove a memory watch",
630 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
631 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
632 tokensplitter
t(args
);
633 std::string name
= t
;
634 if(name
== "" || t
.tail() != "") {
635 messages
<< "syntax: remove-watch <name>" << std::endl
;
638 std::cerr
<< "Erase watch: '" << name
<< "'" << std::endl
;
639 memory_watches
.erase(name
);
640 auto& _status
= window::get_emustatus();
641 _status
.erase("M[" + name
+ "]");
642 update_movie_state();
645 function_ptr_command
<> test1("test-1", "no description available", "No help available\n",
646 []() throw(std::bad_alloc
, std::runtime_error
) {
647 framebuffer
= screen_nosignal
;
648 redraw_framebuffer();
651 function_ptr_command
<> test2("test-2", "no description available", "No help available\n",
652 []() throw(std::bad_alloc
, std::runtime_error
) {
653 framebuffer
= screen_corrupt
;
654 redraw_framebuffer();
657 function_ptr_command
<> test3("test-3", "no description available", "No help available\n",
658 []() throw(std::bad_alloc
, std::runtime_error
) {
662 function_ptr_command
<const std::string
&> mouse_button_handler("mouse_button", "no description available",
663 "No help available\n",
664 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
665 tokensplitter
t(args
);
669 int _x
= atoi(x
.c_str());
670 int _y
= atoi(y
.c_str());
671 int _b
= atoi(b
.c_str());
672 if(_b
& ~prev_mouse_mask
& 1)
673 send_analog_input(_x
, _y
, 0);
674 if(_b
& ~prev_mouse_mask
& 2)
675 send_analog_input(_x
, _y
, 1);
676 if(_b
& ~prev_mouse_mask
& 4)
677 send_analog_input(_x
, _y
, 2);
678 prev_mouse_mask
= _b
;
681 class button_action
: public command
684 button_action(const std::string
& cmd
, int _type
, unsigned _controller
, std::string _button
)
685 throw(std::bad_alloc
)
690 controller
= _controller
;
693 ~button_action() throw() {}
694 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
697 throw std::runtime_error("This command does not take parameters");
699 if(!buttonmap
.count(button
))
701 auto i
= buttonmap
[button
];
702 do_button_action(i
.first
, i
.second
, (type
!= 1) ? 1 : 0, (type
== 2));
703 update_movie_state();
704 window::notify_screen_update();
706 std::string
get_short_help() throw(std::bad_alloc
)
708 return "Press/Unpress button";
710 std::string
get_long_help() throw(std::bad_alloc
)
712 return "Syntax: " + commandn
+ "\n"
713 "Presses/Unpresses button\n";
715 std::string commandn
;
721 class button_action_helper
724 button_action_helper()
726 for(size_t i
= 0; i
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); ++i
)
727 for(int j
= 0; j
< 3; ++j
)
728 for(unsigned k
= 0; k
< 8; ++k
) {
729 std::ostringstream x
, y
;
745 new button_action(x
.str(), j
, k
, y
.str());
750 //If there is a pending load, perform it.
753 if(pending_load
!= "") {
754 do_load_state(pending_load
, loadmode
);
755 redraw_framebuffer();
757 pending_reset_cycles
= -1;
758 amode
= ADVANCE_AUTO
;
759 window::cancel_wait();
760 window::paused(false);
761 if(!system_corrupt
) {
762 location_special
= SPECIAL_SAVEPOINT
;
763 update_movie_state();
764 window::notify_screen_update();
765 window::poll_inputs();
772 //If there are pending saves, perform them.
775 if(!queued_saves
.empty()) {
776 stepping_into_save
= true;
777 SNES::system
.runtosave();
778 stepping_into_save
= false;
779 for(auto i
= queued_saves
.begin(); i
!= queued_saves
.end(); i
++)
782 queued_saves
.clear();
785 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
786 bool handle_reset(long cycles
)
789 window::message("SNES reset");
790 SNES::system
.reset();
791 framebuffer
= screen_nosignal
;
792 lua_callback_do_reset();
793 redraw_framebuffer();
794 } else if(cycles
> 0) {
795 video_refresh_done
= false;
796 long cycles_executed
= 0;
797 messages
<< "Executing delayed reset... This can take some time!" << std::endl
;
798 while(cycles_executed
< cycles
&& !video_refresh_done
) {
802 if(!video_refresh_done
)
803 messages
<< "SNES reset (delayed " << cycles_executed
<< ")" << std::endl
;
805 messages
<< "SNES reset (forced at " << cycles_executed
<< ")" << std::endl
;
806 SNES::system
.reset();
807 framebuffer
= screen_nosignal
;
808 lua_callback_do_reset();
809 redraw_framebuffer();
810 if(video_refresh_done
) {
811 to_wait_frame(get_ticks_msec());
818 bool handle_corrupt()
822 while(system_corrupt
) {
823 redraw_framebuffer();
824 window::cancel_wait();
825 window::paused(true);
826 window::poll_inputs();
828 if(amode
== ADVANCE_QUIT
)
834 void print_controller_mappings()
836 for(unsigned i
= 0; i
< 8; i
++) {
837 std::string type
= "unknown";
838 if(controller_type_by_logical(i
) == DT_NONE
)
839 type
= "disconnected";
840 if(controller_type_by_logical(i
) == DT_GAMEPAD
)
842 if(controller_type_by_logical(i
) == DT_MOUSE
)
844 if(controller_type_by_logical(i
) == DT_SUPERSCOPE
)
846 if(controller_type_by_logical(i
) == DT_JUSTIFIER
)
848 messages
<< "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
849 controller_index_by_logical(i
) << " (" << type
<< ")" << std::endl
;
854 void main_loop(struct loaded_rom
& rom
, struct moviefile
& initial
) throw(std::bad_alloc
,
857 //Basic initialization.
858 init_special_screens();
861 auto old_inteface
= SNES::system
.interface
;
862 SNES::system
.interface
= &intrf
;
863 status
= &window::get_emustatus();
865 //Load our given movie.
866 bool first_round
= false;
867 bool just_did_loadstate
= false;
869 do_load_state(initial
, LOAD_STATE_DEFAULT
);
870 first_round
= our_movie
.is_savestate
;
871 just_did_loadstate
= first_round
;
872 } catch(std::bad_alloc
& e
) {
874 } catch(std::exception
& e
) {
875 messages
<< "FATAL: Can't load initial state: " << e
.what() << std::endl
;
880 lua_callback_startup();
882 //print_controller_mappings();
883 av_snooper::add_dump_notifier(dumpwatch
);
884 window::set_main_surface(main_screen
);
885 redraw_framebuffer();
886 window::paused(false);
887 amode
= ADVANCE_PAUSE
;
888 while(amode
!= ADVANCE_QUIT
) {
889 if(handle_corrupt()) {
890 first_round
= our_movie
.is_savestate
;
891 just_did_loadstate
= true;
894 long resetcycles
= -1;
895 ack_frame_tick(get_ticks_msec());
896 if(amode
== ADVANCE_SKIPLAG_PENDING
)
897 amode
= ADVANCE_SKIPLAG
;
900 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
901 if(amode
== ADVANCE_QUIT
)
903 bool delayed_reset
= (resetcycles
> 0);
904 pending_reset_cycles
= -1;
905 if(!handle_reset(resetcycles
)) {
912 first_round
= our_movie
.is_savestate
;
913 amode
= ADVANCE_PAUSE
;
914 just_did_loadstate
= first_round
;
918 if(just_did_loadstate
) {
919 if(amode
== ADVANCE_QUIT
)
921 amode
= ADVANCE_PAUSE
;
922 redraw_framebuffer();
923 window::cancel_wait();
924 window::paused(true);
925 window::poll_inputs();
926 just_did_loadstate
= false;
929 if(amode
== ADVANCE_AUTO
)
930 window::wait_msec(to_wait_frame(get_ticks_msec()));
933 av_snooper::end(true);
934 SNES::system
.interface
= old_inteface
;