1 #include "mainloop.hpp"
5 #include "framerate.hpp"
6 #include "memorywatch.hpp"
11 #include "moviefile.hpp"
13 #include "keymapper.hpp"
15 #include "settings.hpp"
21 #include "memorymanip.hpp"
22 #include "keymapper.hpp"
27 #include <snes/snes.hpp>
28 #include <ui-libsnes/libsnes.hpp>
29 #include "framerate.hpp"
31 #define LOAD_STATE_RW 0
32 #define LOAD_STATE_RO 1
33 #define LOAD_STATE_PRESERVE 2
34 #define LOAD_STATE_MOVIE 3
35 #define LOAD_STATE_DEFAULT 4
38 #define SPECIAL_FRAME_START 0
39 #define SPECIAL_FRAME_VIDEO 1
40 #define SPECIAL_SAVEPOINT 2
41 #define SPECIAL_NONE 3
43 #define BUTTON_LEFT 0 //Gamepad
44 #define BUTTON_RIGHT 1 //Gamepad
45 #define BUTTON_UP 2 //Gamepad
46 #define BUTTON_DOWN 3 //Gamepad
47 #define BUTTON_A 4 //Gamepad
48 #define BUTTON_B 5 //Gamepad
49 #define BUTTON_X 6 //Gamepad
50 #define BUTTON_Y 7 //Gamepad
51 #define BUTTON_L 8 //Gamepad & Mouse
52 #define BUTTON_R 9 //Gamepad & Mouse
53 #define BUTTON_SELECT 10 //Gamepad
54 #define BUTTON_START 11 //Gamepad & Justifier
55 #define BUTTON_TRIGGER 12 //Superscope.
56 #define BUTTON_CURSOR 13 //Superscope & Justifier
57 #define BUTTON_PAUSE 14 //Superscope
58 #define BUTTON_TURBO 15 //Superscope
60 void update_movie_state();
61 void draw_nosignal(uint16_t* target
);
62 void draw_corrupt(uint16_t* target
);
69 ADVANCE_QUIT
, //Quit the emulator.
70 ADVANCE_AUTO
, //Normal (possibly slowed down play).
71 ADVANCE_FRAME
, //Frame advance.
72 ADVANCE_SUBFRAME
, //Subframe advance.
73 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
74 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
75 ADVANCE_PAUSE
, //Unconditional pause.
78 //Analog input physical controller IDs and types.
79 int analog
[4] = {-1, -1, -1};
80 bool analog_is_mouse
[4] = {false, false, false};
82 std::map
<std::string
, std::string
> memory_watches
;
83 //Previous mouse mask.
84 int prev_mouse_mask
= 0;
85 //Flags related to repeating advance.
89 struct loaded_rom
* our_rom
;
91 struct moviefile our_movie
;
92 //Handle to the graphics system.
96 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
97 enum advance_mode amode
;
98 //Mode and filename of pending load, one of LOAD_* constants.
100 std::string pending_load
;
101 //Queued saves (all savestates).
102 std::set
<std::string
> queued_saves
;
103 bool stepping_into_save
;
105 controls_t curcontrols
;
106 controls_t autoheld_controls
;
107 //Emulator status area.
108 std::map
<std::string
, std::string
>* status
;
109 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
110 long pending_reset_cycles
= -1;
111 //Set by every video refresh.
112 bool video_refresh_done
;
113 //Special subframe location. One of SPECIAL_* constants.
114 int location_special
;
115 //Types of connected controllers.
116 enum porttype_t porttype1
= PT_GAMEPAD
;
117 enum porttype_t porttype2
= PT_NONE
;
118 //System corrupt flag.
120 //Current screen, no signal screen and corrupt screen.
121 lcscreen framebuffer
;
122 lcscreen nosignal_screen
;
123 lcscreen corrupt_screen
;
125 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
126 numeric_setting
savecompression("savecompression", 0, 9, 7);
128 void send_analog_input(int32_t x
, int32_t y
, unsigned index
)
130 if(analog_is_mouse
[index
]) {
132 y
-= (framebuffer
.height
/ 2);
137 if(analog
[index
] < 0) {
138 out(win
) << "No analog controller in slot #" << (index
+ 1) << std::endl
;
141 curcontrols(analog
[index
] >> 2, analog
[index
] & 3, 0) = x
;
142 curcontrols(analog
[index
] >> 2, analog
[index
] & 3, 1) = y
;
145 void redraw_framebuffer()
147 uint32_t hscl
= 1, vscl
= 1;
148 if(framebuffer
.width
< 512)
150 if(framebuffer
.height
< 400)
153 struct lua_render_context lrc
;
159 lrc
.width
= framebuffer
.width
* hscl
;
160 lrc
.height
= framebuffer
.height
* vscl
;
161 lrc
.rshift
= scr
.active_rshift
;
162 lrc
.gshift
= scr
.active_gshift
;
163 lrc
.bshift
= scr
.active_bshift
;
164 lua_callback_do_paint(&lrc
, win
);
165 scr
.reallocate(framebuffer
.width
* hscl
+ lrc
.left_gap
+ lrc
.right_gap
, framebuffer
.height
* vscl
+
166 lrc
.top_gap
+ lrc
.bottom_gap
, lrc
.left_gap
, lrc
.top_gap
);
167 scr
.copy_from(framebuffer
, hscl
, vscl
);
168 //We would want divide by 2, but we'll do it ourselves in order to do mouse.
169 win
->set_window_compensation(lrc
.left_gap
, lrc
.top_gap
, 1, 1);
171 win
->notify_screen_update();
174 void fill_special_frames()
176 uint16_t buf
[512*448];
178 nosignal_screen
= lcscreen(buf
, 512, 448);
180 corrupt_screen
= lcscreen(buf
, 512, 448);
184 class firmware_path_setting
: public setting
187 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "./"; default_firmware
= true; }
188 void blank() throw(std::bad_alloc
, std::runtime_error
)
190 _firmwarepath
= "./";
191 default_firmware
= true;
194 bool is_set() throw()
196 return !default_firmware
;
199 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
201 _firmwarepath
= value
;
202 default_firmware
= false;
205 std::string
get() throw(std::bad_alloc
)
207 return _firmwarepath
;
210 operator std::string() throw(std::bad_alloc
)
212 return _firmwarepath
;
215 std::string _firmwarepath
;
216 bool default_firmware
;
217 } firmwarepath_setting
;
219 class mymovielogic
: public movie_logic
222 mymovielogic() : movie_logic(dummy_movie
) {}
224 controls_t
update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
226 if(lua_requests_subframe_paint
)
227 redraw_framebuffer();
230 if(amode
== ADVANCE_SUBFRAME
) {
231 if(!cancel_advance
&& !advanced_once
) {
232 win
->wait_msec(advance_timeout_first
);
233 advanced_once
= true;
236 amode
= ADVANCE_PAUSE
;
237 cancel_advance
= false;
239 win
->paused(amode
== ADVANCE_PAUSE
);
240 } else if(amode
== ADVANCE_FRAME
) {
243 win
->paused(amode
== ADVANCE_SKIPLAG
|| amode
== ADVANCE_PAUSE
);
244 cancel_advance
= false;
246 if(amode
== ADVANCE_SKIPLAG
)
247 amode
= ADVANCE_AUTO
;
248 location_special
= SPECIAL_NONE
;
249 update_movie_state();
251 if(amode
== ADVANCE_SKIPLAG_PENDING
)
252 amode
= ADVANCE_SKIPLAG
;
253 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
254 if(!cancel_advance
) {
255 win
->wait_msec(advanced_once
? to_wait_frame(get_ticks_msec()) :
256 advance_timeout_first
);
257 advanced_once
= true;
260 amode
= ADVANCE_PAUSE
;
261 cancel_advance
= false;
263 win
->paused(amode
== ADVANCE_PAUSE
);
265 win
->paused((amode
== ADVANCE_PAUSE
));
266 cancel_advance
= false;
268 location_special
= SPECIAL_FRAME_START
;
269 update_movie_state();
271 win
->notify_screen_update();
273 if(!subframe
&& pending_reset_cycles
>= 0) {
274 curcontrols(CONTROL_SYSTEM_RESET
) = 1;
275 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = pending_reset_cycles
/ 10000;
276 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = pending_reset_cycles
% 10000;
277 } else if(!subframe
) {
278 curcontrols(CONTROL_SYSTEM_RESET
) = 0;
279 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = 0;
280 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = 0;
282 controls_t tmp
= curcontrols
^ autoheld_controls
;
283 lua_callback_do_input(tmp
, subframe
, win
);
294 //Lookup physical controller id based on UI controller id and given types (-1 if invalid).
295 int lookup_physical_controller(unsigned ui_id
)
297 bool p1multitap
= (porttype1
== PT_MULTITAP
);
298 unsigned p1devs
= port_types
[porttype1
].devices
;
299 unsigned p2devs
= port_types
[porttype2
].devices
;
300 if(ui_id
>= p1devs
+ p2devs
)
306 return 4 + ui_id
- p1devs
;
316 //Look up controller type given UI controller id (note: Non-present controllers give PT_NONE, not the type
317 //of port, multitap controllers give PT_GAMEPAD, not PT_MULTITAP, and justifiers give PT_JUSTIFIER, not
319 enum devicetype_t
lookup_controller_type(unsigned ui_id
)
321 int x
= lookup_physical_controller(ui_id
);
324 enum porttype_t rawtype
= (x
& 4) ? porttype2
: porttype1
;
325 if((x
& 3) < port_types
[rawtype
].devices
)
326 return port_types
[rawtype
].dtype
;
331 void set_analog_controllers()
334 for(unsigned i
= 0; i
< 8; i
++) {
335 enum devicetype_t t
= lookup_controller_type(i
);
336 analog_is_mouse
[index
] = (t
== DT_MOUSE
);
337 if(t
== DT_MOUSE
|| t
== DT_SUPERSCOPE
|| t
== DT_JUSTIFIER
) {
338 analog
[index
++] = lookup_physical_controller(i
);
342 for(; index
< 3; index
++)
346 std::map
<std::string
, std::pair
<unsigned, unsigned>> buttonmap
;
348 const char* buttonnames
[] = {
349 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
353 void init_buttonmap()
358 for(unsigned i
= 0; i
< 8; i
++)
359 for(unsigned j
= 0; j
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); j
++) {
360 std::ostringstream x
;
361 x
<< (i
+ 1) << buttonnames
[j
];
362 buttonmap
[x
.str()] = std::make_pair(i
, j
);
368 void do_button_action(unsigned ui_id
, unsigned button
, short newstate
, bool do_xor
= false)
370 enum devicetype_t p
= lookup_controller_type(ui_id
);
371 int x
= lookup_physical_controller(ui_id
);
375 out(win
) << "No such controller #" << (ui_id
+ 1) << std::endl
;
379 case BUTTON_UP
: bid
= SNES_DEVICE_ID_JOYPAD_UP
; break;
380 case BUTTON_DOWN
: bid
= SNES_DEVICE_ID_JOYPAD_DOWN
; break;
381 case BUTTON_LEFT
: bid
= SNES_DEVICE_ID_JOYPAD_LEFT
; break;
382 case BUTTON_RIGHT
: bid
= SNES_DEVICE_ID_JOYPAD_RIGHT
; break;
383 case BUTTON_A
: bid
= SNES_DEVICE_ID_JOYPAD_A
; break;
384 case BUTTON_B
: bid
= SNES_DEVICE_ID_JOYPAD_B
; break;
385 case BUTTON_X
: bid
= SNES_DEVICE_ID_JOYPAD_X
; break;
386 case BUTTON_Y
: bid
= SNES_DEVICE_ID_JOYPAD_Y
; break;
387 case BUTTON_L
: bid
= SNES_DEVICE_ID_JOYPAD_L
; break;
388 case BUTTON_R
: bid
= SNES_DEVICE_ID_JOYPAD_R
; break;
389 case BUTTON_SELECT
: bid
= SNES_DEVICE_ID_JOYPAD_SELECT
; break;
390 case BUTTON_START
: bid
= SNES_DEVICE_ID_JOYPAD_START
; break;
392 out(win
) << "Invalid button for gamepad" << std::endl
;
398 case BUTTON_L
: bid
= SNES_DEVICE_ID_MOUSE_LEFT
; break;
399 case BUTTON_R
: bid
= SNES_DEVICE_ID_MOUSE_RIGHT
; break;
401 out(win
) << "Invalid button for mouse" << std::endl
;
407 case BUTTON_START
: bid
= SNES_DEVICE_ID_JUSTIFIER_START
; break;
408 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_JUSTIFIER_TRIGGER
; break;
410 out(win
) << "Invalid button for justifier" << std::endl
;
416 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
; break;
417 case BUTTON_CURSOR
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
; break;
418 case BUTTON_PAUSE
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
; break;
419 case BUTTON_TURBO
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TURBO
; break;
421 out(win
) << "Invalid button for superscope" << std::endl
;
427 autoheld_controls((x
& 4) ? 1 : 0, x
& 3, bid
) ^= newstate
;
429 curcontrols((x
& 4) ? 1 : 0, x
& 3, bid
) = newstate
;
433 void do_save_state(const std::string
& filename
) throw(std::bad_alloc
,
436 lua_callback_pre_save(filename
, true, win
);
438 uint64_t origtime
= get_ticks_msec();
439 our_movie
.is_savestate
= true;
440 our_movie
.sram
= save_sram();
441 our_movie
.savestate
= save_core_state();
442 framebuffer
.save(our_movie
.screenshot
);
443 auto s
= movb
.get_movie().save_state();
444 our_movie
.movie_state
.resize(s
.size());
445 memcpy(&our_movie
.movie_state
[0], &s
[0], s
.size());
446 our_movie
.input
= movb
.get_movie().save();
447 our_movie
.save(filename
, savecompression
);
448 uint64_t took
= get_ticks_msec() - origtime
;
449 out(win
) << "Saved state '" << filename
<< "' in " << took
<< "ms." << std::endl
;
450 lua_callback_post_save(filename
, true, win
);
451 } catch(std::bad_alloc
& e
) {
453 } catch(std::exception
& e
) {
454 win
->message(std::string("Save failed: ") + e
.what());
455 lua_callback_err_save(filename
, win
);
460 void do_save_movie(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
462 lua_callback_pre_save(filename
, false, win
);
464 uint64_t origtime
= get_ticks_msec();
465 our_movie
.is_savestate
= false;
466 our_movie
.input
= movb
.get_movie().save();
467 our_movie
.save(filename
, savecompression
);
468 uint64_t took
= get_ticks_msec() - origtime
;
469 out(win
) << "Saved movie '" << filename
<< "' in " << took
<< "ms." << std::endl
;
470 lua_callback_post_save(filename
, false, win
);
471 } catch(std::bad_alloc
& e
) {
473 } catch(std::exception
& e
) {
474 win
->message(std::string("Save failed: ") + e
.what());
475 lua_callback_err_save(filename
, win
);
479 void warn_hash_mismatch(const std::string
& mhash
, const loaded_slot
& slot
, const std::string
& name
)
481 if(mhash
!= slot
.sha256
) {
482 out(win
) << "WARNING: " << name
<< " hash mismatch!" << std::endl
483 << "\tMovie: " << mhash
<< std::endl
484 << "\tOur ROM: " << slot
.sha256
<< std::endl
;
488 void set_dev(bool port
, porttype_t t
, bool set_core
= true)
491 switch(set_core
? t
: PT_INVALID
) {
493 snes_set_controller_port_device(port
, SNES_DEVICE_NONE
);
496 snes_set_controller_port_device(port
, SNES_DEVICE_JOYPAD
);
499 snes_set_controller_port_device(port
, SNES_DEVICE_MULTITAP
);
502 snes_set_controller_port_device(port
, SNES_DEVICE_MOUSE
);
505 snes_set_controller_port_device(port
, SNES_DEVICE_SUPER_SCOPE
);
508 snes_set_controller_port_device(port
, SNES_DEVICE_JUSTIFIER
);
511 snes_set_controller_port_device(port
, SNES_DEVICE_JUSTIFIERS
);
520 set_analog_controllers();
523 //Load state from loaded movie file (does not catch errors).
524 void do_load_state(struct moviefile
& _movie
, int lmode
)
526 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
527 if(gtype::toromtype(_movie
.gametype
) != our_rom
->rtype
)
528 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
529 if(gtype::toromregion(_movie
.gametype
) != our_rom
->orig_region
&& our_rom
->orig_region
!= REGION_AUTO
)
530 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
532 if(_movie
.coreversion
!= bsnes_core_version
) {
533 if(will_load_state
) {
534 std::ostringstream x
;
535 x
<< "ERROR: Emulator core version mismatch!" << std::endl
536 << "\tThis version: " << bsnes_core_version
<< std::endl
537 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
538 throw std::runtime_error(x
.str());
540 out(win
) << "WARNING: Emulator core version mismatch!" << std::endl
541 << "\tThis version: " << bsnes_core_version
<< std::endl
542 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
544 warn_hash_mismatch(_movie
.rom_sha256
, our_rom
->rom
, "ROM #1");
545 warn_hash_mismatch(_movie
.romxml_sha256
, our_rom
->rom_xml
, "XML #1");
546 warn_hash_mismatch(_movie
.slota_sha256
, our_rom
->slota
, "ROM #2");
547 warn_hash_mismatch(_movie
.slotaxml_sha256
, our_rom
->slota_xml
, "XML #2");
548 warn_hash_mismatch(_movie
.slotb_sha256
, our_rom
->slotb
, "ROM #3");
549 warn_hash_mismatch(_movie
.slotbxml_sha256
, our_rom
->slotb_xml
, "XML #3");
551 SNES::config
.random
= false;
552 SNES::config
.expansion_port
= SNES::System::ExpansionPortDevice::None
;
555 if(lmode
== LOAD_STATE_PRESERVE
)
556 newmovie
= movb
.get_movie();
558 newmovie
.load(_movie
.rerecords
, _movie
.projectid
, _movie
.input
);
560 if(will_load_state
) {
561 std::vector
<unsigned char> tmp
;
562 tmp
.resize(_movie
.movie_state
.size());
563 memcpy(&tmp
[0], &_movie
.movie_state
[0], tmp
.size());
564 newmovie
.restore_state(tmp
, true);
568 rrdata::read_base(_movie
.projectid
);
569 rrdata::add_internal();
571 our_rom
->region
= gtype::toromregion(_movie
.gametype
);
574 if(_movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
) {
575 //Load the savestate and movie state.
576 set_dev(false, _movie
.port1
);
577 set_dev(true, _movie
.port2
);
578 load_core_state(_movie
.savestate
);
579 framebuffer
.load(_movie
.screenshot
);
581 load_sram(_movie
.movie_sram
, win
);
582 set_dev(false, _movie
.port1
);
583 set_dev(true, _movie
.port2
);
584 framebuffer
= nosignal_screen
;
586 } catch(std::bad_alloc
& e
) {
588 } catch(std::exception
& e
) {
589 system_corrupt
= true;
593 //Okay, copy the movie data.
595 if(!our_movie
.is_savestate
|| lmode
== LOAD_STATE_MOVIE
) {
596 our_movie
.is_savestate
= false;
597 our_movie
.host_memory
.clear();
599 movb
.get_movie() = newmovie
;
600 //Activate RW mode if needed.
601 if(lmode
== LOAD_STATE_RW
)
602 movb
.get_movie().readonly_mode(false);
603 if(lmode
== LOAD_STATE_DEFAULT
&& !(movb
.get_movie().get_frame_count()))
604 movb
.get_movie().readonly_mode(false);
605 out(win
) << "ROM Type ";
606 switch(our_rom
->rtype
) {
613 case ROMTYPE_BSXSLOTTED
:
614 out(win
) << "BS-X slotted";
616 case ROMTYPE_SUFAMITURBO
:
617 out(win
) << "Sufami Turbo";
620 out(win
) << "Super Game Boy";
623 out(win
) << "Unknown";
626 out(win
) << " region ";
627 switch(our_rom
->region
) {
635 out(win
) << "Unknown";
638 out(win
) << std::endl
;
639 uint64_t mlength
= _movie
.get_movie_length();
642 std::ostringstream x
;
643 if(mlength
> 3600000000000) {
644 x
<< mlength
/ 3600000000000 << ":";
645 mlength
%= 3600000000000;
647 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000000000 << ":";
648 mlength
%= 60000000000;
649 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000000000 << ".";
650 mlength
%= 1000000000;
651 x
<< std::setfill('0') << std::setw(3) << mlength
/ 1000000;
652 out(win
) << "Rerecords " << _movie
.rerecords
<< " length " << x
.str() << " ("
653 << _movie
.get_frame_count() << " frames)" << std::endl
;
656 if(_movie
.gamename
!= "")
657 out(win
) << "Game Name: " << _movie
.gamename
<< std::endl
;
658 for(size_t i
= 0; i
< _movie
.authors
.size(); i
++)
659 out(win
) << "Author: " << _movie
.authors
[i
].first
<< "(" << _movie
.authors
[i
].second
<< ")"
664 void do_load_state(const std::string
& filename
, int lmode
)
666 uint64_t origtime
= get_ticks_msec();
667 lua_callback_pre_load(filename
, win
);
668 struct moviefile mfile
;
670 mfile
= moviefile(filename
);
671 } catch(std::bad_alloc
& e
) {
673 } catch(std::exception
& e
) {
674 win
->message("Can't read movie/savestate '" + filename
+ "': " + e
.what());
675 lua_callback_err_load(filename
, win
);
679 do_load_state(mfile
, lmode
);
680 uint64_t took
= get_ticks_msec() - origtime
;
681 out(win
) << "Loaded '" << filename
<< "' in " << took
<< "ms." << std::endl
;
682 lua_callback_post_load(filename
, our_movie
.is_savestate
, win
);
683 } catch(std::bad_alloc
& e
) {
685 } catch(std::exception
& e
) {
686 win
->message("Can't load movie/savestate '" + filename
+ "': " + e
.what());
687 lua_callback_err_load(filename
, win
);
692 //Do pending load (automatically unpauses).
693 void mark_pending_load(const std::string
& filename
, int lmode
)
696 pending_load
= filename
;
697 amode
= ADVANCE_AUTO
;
702 //Mark pending save (movies save immediately).
703 void mark_pending_save(const std::string
& filename
, int smode
)
705 if(smode
== SAVE_MOVIE
) {
706 //Just do this immediately.
707 do_save_movie(filename
);
710 queued_saves
.insert(filename
);
711 win
->message("Pending save on '" + filename
+ "'");
714 class dump_watch
: public av_snooper::dump_notification
716 void dump_starting() throw()
718 update_movie_state();
720 void dump_ending() throw()
722 update_movie_state();
727 std::vector
<char>& get_host_memory()
729 return our_movie
.host_memory
;
734 return movb
.get_movie();
737 void update_movie_state()
739 auto& _status
= win
->get_emustatus();
741 std::ostringstream x
;
742 x
<< movb
.get_movie().get_current_frame() << "(";
743 if(location_special
== SPECIAL_FRAME_START
)
745 else if(location_special
== SPECIAL_SAVEPOINT
)
747 else if(location_special
== SPECIAL_FRAME_VIDEO
)
750 x
<< movb
.get_movie().next_poll_number();
751 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
752 _status
["Frame"] = x
.str();
755 std::ostringstream x
;
756 if(movb
.get_movie().readonly_mode())
760 if(av_snooper::dump_in_progress())
762 _status
["Flags"] = x
.str();
764 for(auto i
= memory_watches
.begin(); i
!= memory_watches
.end(); i
++) {
766 _status
["M[" + i
->first
+ "]"] = evaluate_watch(i
->second
);
771 if(movb
.get_movie().readonly_mode())
772 c
= movb
.get_movie().get_controls();
774 c
= curcontrols
^ autoheld_controls
;
775 for(unsigned i
= 0; i
< 8; i
++) {
776 unsigned pindex
= lookup_physical_controller(i
);
777 unsigned port
= pindex
>> 2;
778 unsigned dev
= pindex
& 3;
779 auto ctype
= lookup_controller_type(i
);
780 std::ostringstream x
;
783 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_LEFT
) ? "l" : " ");
784 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_RIGHT
) ? "r" : " ");
785 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_UP
) ? "u" : " ");
786 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_DOWN
) ? "d" : " ");
787 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_A
) ? "A" : " ");
788 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_B
) ? "B" : " ");
789 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_X
) ? "X" : " ");
790 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_Y
) ? "Y" : " ");
791 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_L
) ? "L" : " ");
792 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_R
) ? "R" : " ");
793 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_START
) ? "S" : " ");
794 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_SELECT
) ? "s" : " ");
797 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_X
) << " ";
798 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_Y
) << " ";
799 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_LEFT
) ? "L" : " ");
800 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_RIGHT
) ? "R" : " ");
803 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_X
) << " ";
804 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_Y
) << " ";
805 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
) ? "T" : " ");
806 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
) ? "C" : " ");
807 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TURBO
) ? "t" : " ");
808 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
) ? "P" : " ");
811 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_X
) << " ";
812 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_Y
) << " ";
813 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_START
) ? "T" : " ");
814 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_TRIGGER
) ? "S" : " ");
819 char y
[3] = {'P', 0, 0};
821 _status
[std::string(y
)] = x
.str();
826 class my_interface
: public SNES::Interface
828 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
830 return static_cast<std::string
>(firmwarepath_setting
).c_str();
833 void video_refresh(const uint16_t *data
, bool hires
, bool interlace
, bool overscan
)
835 if(stepping_into_save
)
836 win
->message("Got video refresh in runtosave, expect desyncs!");
837 video_refresh_done
= true;
838 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
839 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
840 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
841 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
842 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
843 lcscreen
ls(data
, hires
, interlace
, overscan
, region
);
845 location_special
= SPECIAL_FRAME_VIDEO
;
846 update_movie_state();
847 redraw_framebuffer();
848 uint32_t fps_n
, fps_d
;
856 av_snooper::frame(ls
, fps_n
, fps_d
, win
);
859 void audio_sample(int16_t l_sample
, int16_t r_sample
)
861 uint16_t _l
= l_sample
;
862 uint16_t _r
= r_sample
;
863 win
->play_audio_sample(_l
+ 32768, _r
+ 32768);
864 av_snooper::sample(_l
, _r
, win
);
867 void audio_sample(uint16_t l_sample
, uint16_t r_sample
)
869 //Yes, this interface is broken. The samples are signed but are passed as unsigned!
870 win
->play_audio_sample(l_sample
+ 32768, r_sample
+ 32768);
871 av_snooper::sample(l_sample
, r_sample
, win
);
874 int16_t input_poll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
877 x
= movb
.input_poll(port
, index
, id
);
878 //if(id == SNES_DEVICE_ID_JOYPAD_START)
879 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
880 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
, win
);
887 class quit_emulator_cmd
: public command
890 quit_emulator_cmd() throw(std::bad_alloc
) : command("quit-emulator") {}
891 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
893 if(args
== "/y" || win
->modal_message("Really quit?", true)) {
894 amode
= ADVANCE_QUIT
;
899 std::string
get_short_help() throw(std::bad_alloc
) { return "Quit the emulator"; }
900 std::string
get_long_help() throw(std::bad_alloc
)
902 return "Syntax: quit-emulator [/y]\n"
903 "Quits emulator (/y => don't ask for confirmation).\n";
907 class pause_emulator_cmd
: public command
910 pause_emulator_cmd() throw(std::bad_alloc
) : command("pause-emulator") {}
911 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
914 throw std::runtime_error("This command does not take parameters");
915 if(amode
!= ADVANCE_AUTO
) {
916 amode
= ADVANCE_AUTO
;
919 win
->message("Unpaused");
922 cancel_advance
= false;
923 amode
= ADVANCE_PAUSE
;
924 win
->message("Paused");
927 std::string
get_short_help() throw(std::bad_alloc
) { return "(Un)pause the emulator"; }
928 std::string
get_long_help() throw(std::bad_alloc
)
930 return "Syntax: pause-emulator\n"
931 "(Un)pauses the emulator.\n";
935 class padvance_frame_cmd
: public command
938 padvance_frame_cmd() throw(std::bad_alloc
) : command("+advance-frame") {}
939 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
942 throw std::runtime_error("This command does not take parameters");
943 amode
= ADVANCE_FRAME
;
944 cancel_advance
= false;
945 advanced_once
= false;
949 std::string
get_short_help() throw(std::bad_alloc
) { return "Advance one frame"; }
950 std::string
get_long_help() throw(std::bad_alloc
)
952 return "Syntax: +advance-frame\n"
953 "Advances the emulation by one frame.\n";
957 class nadvance_frame_cmd
: public command
960 nadvance_frame_cmd() throw(std::bad_alloc
) : command("-advance-frame") {}
961 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
964 throw std::runtime_error("This command does not take parameters");
965 cancel_advance
= true;
971 class padvance_poll_cmd
: public command
974 padvance_poll_cmd() throw(std::bad_alloc
) : command("+advance-poll") {}
975 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
978 throw std::runtime_error("This command does not take parameters");
979 amode
= ADVANCE_SUBFRAME
;
980 cancel_advance
= false;
981 advanced_once
= false;
985 std::string
get_short_help() throw(std::bad_alloc
) { return "Advance one subframe"; }
986 std::string
get_long_help() throw(std::bad_alloc
)
988 return "Syntax: +advance-poll\n"
989 "Advances the emulation by one subframe.\n";
993 class nadvance_poll_cmd
: public command
996 nadvance_poll_cmd() throw(std::bad_alloc
) : command("-advance-poll") {}
998 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1001 throw std::runtime_error("This command does not take parameters");
1002 cancel_advance
= true;
1008 class advance_skiplag_cmd
: public command
1011 advance_skiplag_cmd() throw(std::bad_alloc
) : command("advance-skiplag") {}
1012 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1015 throw std::runtime_error("This command does not take parameters");
1016 amode
= ADVANCE_SKIPLAG
;
1020 std::string
get_short_help() throw(std::bad_alloc
) { return "Skip to next poll"; }
1021 std::string
get_long_help() throw(std::bad_alloc
)
1023 return "Syntax: advance-skiplag\n"
1024 "Advances the emulation to the next poll.\n";
1028 class reset_cmd
: public command
1031 reset_cmd() throw(std::bad_alloc
) : command("reset") {}
1032 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1035 throw std::runtime_error("This command does not take parameters");
1036 pending_reset_cycles
= 0;
1038 std::string
get_short_help() throw(std::bad_alloc
) { return "Reset the SNES"; }
1039 std::string
get_long_help() throw(std::bad_alloc
)
1041 return "Syntax: reset\n"
1042 "Resets the SNES in beginning of the next frame.\n";
1046 class load_state_cmd
: public command
1049 load_state_cmd() throw(std::bad_alloc
) : command("load-state") {}
1050 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1053 throw std::runtime_error("Filename required");
1054 mark_pending_load(args
, LOAD_STATE_RW
);
1056 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1057 std::string
get_long_help() throw(std::bad_alloc
)
1059 return "Syntax: load-state <file>\n"
1060 "Loads SNES state from <file> in Read/Write mode\n";
1064 class load_readonly_cmd
: public command
1067 load_readonly_cmd() throw(std::bad_alloc
) : command("load-readonly") {}
1068 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1071 throw std::runtime_error("Filename required");
1072 mark_pending_load(args
, LOAD_STATE_RO
);
1074 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1075 std::string
get_long_help() throw(std::bad_alloc
)
1077 return "Syntax: load-readonly <file>\n"
1078 "Loads SNES state from <file> in Read-only mode\n";
1082 class load_preserve_cmd
: public command
1085 load_preserve_cmd() throw(std::bad_alloc
) : command("load-preserve") {}
1086 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1089 throw std::runtime_error("Filename required");
1090 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
1092 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1093 std::string
get_long_help() throw(std::bad_alloc
)
1095 return "Syntax: load-preserve <file>\n"
1096 "Loads SNES state from <file> preserving input\n";
1100 class load_movie_cmd
: public command
1103 load_movie_cmd() throw(std::bad_alloc
) : command("load-movie") {}
1104 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1107 throw std::runtime_error("Filename required");
1108 mark_pending_load(args
, LOAD_STATE_MOVIE
);
1110 std::string
get_short_help() throw(std::bad_alloc
) { return "Load movie"; }
1111 std::string
get_long_help() throw(std::bad_alloc
)
1113 return "Syntax: load-movie <file>\n"
1114 "Loads movie from <file>\n";
1118 class save_state_cmd
: public command
1121 save_state_cmd() throw(std::bad_alloc
) : command("save-state") {}
1122 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1125 throw std::runtime_error("Filename required");
1126 mark_pending_save(args
, SAVE_STATE
);
1129 std::string
get_short_help() throw(std::bad_alloc
) { return "Save state"; }
1130 std::string
get_long_help() throw(std::bad_alloc
)
1132 return "Syntax: save-state <file>\n"
1133 "Saves SNES state to <file>\n";
1137 class save_movie_cmd
: public command
1140 save_movie_cmd() throw(std::bad_alloc
) : command("save-movie") {}
1141 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1144 throw std::runtime_error("Filename required");
1145 mark_pending_save(args
, SAVE_MOVIE
);
1147 std::string
get_short_help() throw(std::bad_alloc
) { return "Save movie"; }
1148 std::string
get_long_help() throw(std::bad_alloc
)
1150 return "Syntax: save-movie <file>\n"
1151 "Saves movie to <file>\n";
1155 class set_rwmode_cmd
: public command
1158 set_rwmode_cmd() throw(std::bad_alloc
) : command("set-rwmode") {}
1159 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1162 throw std::runtime_error("This command does not take parameters");
1163 movb
.get_movie().readonly_mode(false);
1164 lua_callback_do_readwrite(win
);
1165 update_movie_state();
1166 win
->notify_screen_update();
1168 std::string
get_short_help() throw(std::bad_alloc
) { return "Switch to read/write mode"; }
1169 std::string
get_long_help() throw(std::bad_alloc
)
1171 return "Syntax: set-rwmode\n"
1172 "Switches to read/write mode\n";
1176 class set_gamename_cmd
: public command
1179 set_gamename_cmd() throw(std::bad_alloc
) : command("set-gamename") {}
1180 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1182 our_movie
.gamename
= args
;
1183 out(win
) << "Game name changed to '" << our_movie
.gamename
<< "'" << std::endl
;
1185 std::string
get_short_help() throw(std::bad_alloc
) { return "Set the game name"; }
1186 std::string
get_long_help() throw(std::bad_alloc
)
1188 return "Syntax: set-gamename <name>\n"
1189 "Sets the game name to <name>\n";
1193 class get_gamename_cmd
: public command
1196 get_gamename_cmd() throw(std::bad_alloc
) : command("get-gamename") {}
1197 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1200 throw std::runtime_error("This command does not take parameters");
1201 out(win
) << "Game name is '" << our_movie
.gamename
<< "'" << std::endl
;
1203 std::string
get_short_help() throw(std::bad_alloc
) { return "Get the game name"; }
1204 std::string
get_long_help() throw(std::bad_alloc
)
1206 return "Syntax: get-gamename\n"
1207 "Prints the game name\n";
1211 class print_authors_cmd
: public command
1214 print_authors_cmd() throw(std::bad_alloc
) : command("show-authors") {}
1215 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1218 throw std::runtime_error("This command does not take parameters");
1220 for(auto i
= our_movie
.authors
.begin(); i
!= our_movie
.authors
.end(); i
++) {
1221 out(win
) << (idx
++) << ": " << i
->first
<< "|" << i
->second
<< std::endl
;
1223 out(win
) << "End of authors list" << std::endl
;
1225 std::string
get_short_help() throw(std::bad_alloc
) { return "Show the run authors"; }
1226 std::string
get_long_help() throw(std::bad_alloc
)
1228 return "Syntax: show-authors\n"
1229 "Shows the run authors\n";
1233 class repainter
: public command
1236 repainter() throw(std::bad_alloc
) : command("repaint") {}
1237 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1240 throw std::runtime_error("This command does not take parameters");
1241 redraw_framebuffer();
1243 std::string
get_short_help() throw(std::bad_alloc
) { return "Redraw the screen"; }
1244 std::string
get_long_help() throw(std::bad_alloc
)
1246 return "Syntax: repaint\n"
1247 "Redraws the screen\n";
1251 class add_author_command
: public command
1254 add_author_command() throw(std::bad_alloc
) : command("add-author") {}
1255 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1257 tokensplitter
t(args
);
1258 fieldsplitter
f(t
.tail());
1259 std::string full
= f
;
1260 std::string nick
= f
;
1261 if(full
== "" && nick
== "")
1262 throw std::runtime_error("Bad author name");
1263 our_movie
.authors
.push_back(std::make_pair(full
, nick
));
1264 out(win
) << (our_movie
.authors
.size() - 1) << ": " << full
<< "|" << nick
<< std::endl
;
1266 std::string
get_short_help() throw(std::bad_alloc
) { return "Add an author"; }
1267 std::string
get_long_help() throw(std::bad_alloc
)
1269 return "Syntax: add-author <fullname>\n"
1270 "Syntax: add-author |<nickname>\n"
1271 "Syntax: add-author <fullname>|<nickname>\n"
1272 "Adds a new author\n";
1276 class remove_author_command
: public command
1279 remove_author_command() throw(std::bad_alloc
) : command("remove-author") {}
1280 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1282 tokensplitter
t(args
);
1283 uint64_t index
= parse_value
<uint64_t>(t
.tail());
1284 if(index
>= our_movie
.authors
.size())
1285 throw std::runtime_error("No such author");
1286 our_movie
.authors
.erase(our_movie
.authors
.begin() + index
);
1288 std::string
get_short_help() throw(std::bad_alloc
) { return "Remove an author"; }
1289 std::string
get_long_help() throw(std::bad_alloc
)
1291 return "Syntax: remove-author <id>\n"
1292 "Removes author with ID <id>\n";
1296 class edit_author_command
: public command
1299 edit_author_command() throw(std::bad_alloc
) : command("edit-author") {}
1300 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1302 tokensplitter
t(args
);
1303 uint64_t index
= parse_value
<uint64_t>(t
);
1304 if(index
>= our_movie
.authors
.size())
1305 throw std::runtime_error("No such author");
1306 fieldsplitter
f(t
.tail());
1307 std::string full
= f
;
1308 std::string nick
= f
;
1309 if(full
== "" && nick
== "") {
1310 out(win
) << "syntax: edit-author <authornum> <author>" << std::endl
;
1313 our_movie
.authors
[index
] = std::make_pair(full
, nick
);
1315 std::string
get_short_help() throw(std::bad_alloc
) { return "Edit an author"; }
1316 std::string
get_long_help() throw(std::bad_alloc
)
1318 return "Syntax: edit-author <authorid> <fullname>\n"
1319 "Syntax: edit-author <authorid> |<nickname>\n"
1320 "Syntax: edit-author <authorid> <fullname>|<nickname>\n"
1321 "Edits author name\n";
1325 class add_watch_command
: public command
1328 add_watch_command() throw(std::bad_alloc
) : command("add-watch") {}
1329 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1331 tokensplitter
t(args
);
1332 std::string name
= t
;
1333 if(name
== "" || t
.tail() == "")
1334 throw std::runtime_error("syntax: add-watch <name> <expr>");
1335 std::cerr
<< "Add watch: '" << name
<< "'" << std::endl
;
1336 memory_watches
[name
] = t
.tail();
1337 update_movie_state();
1339 std::string
get_short_help() throw(std::bad_alloc
) { return "Add a memory watch"; }
1340 std::string
get_long_help() throw(std::bad_alloc
)
1342 return "Syntax: add-watch <name> <expression>\n"
1343 "Adds a new memory watch\n";
1347 class remove_watch_command
: public command
1350 remove_watch_command() throw(std::bad_alloc
) : command("remove-watch") {}
1351 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1353 tokensplitter
t(args
);
1354 std::string name
= t
;
1355 if(name
== "" || t
.tail() != "") {
1356 out(win
) << "syntax: remove-watch <name>" << std::endl
;
1359 std::cerr
<< "Erase watch: '" << name
<< "'" << std::endl
;
1360 memory_watches
.erase(name
);
1361 auto& _status
= win
->get_emustatus();
1362 _status
.erase("M[" + name
+ "]");
1363 update_movie_state(); }
1364 std::string
get_short_help() throw(std::bad_alloc
) { return "Remove a memory watch"; }
1365 std::string
get_long_help() throw(std::bad_alloc
)
1367 return "Syntax: remove-watch <name>\n"
1368 "Removes a memory watch\n";
1372 class test_1
: public command
1375 test_1() throw(std::bad_alloc
) : command("test-1") {}
1376 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1378 framebuffer
= nosignal_screen
;
1379 redraw_framebuffer();
1383 class test_2
: public command
1386 test_2() throw(std::bad_alloc
) : command("test-2") {}
1387 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1389 framebuffer
= corrupt_screen
;
1390 redraw_framebuffer();
1394 class test_3
: public command
1397 test_3() throw(std::bad_alloc
) : command("test-3") {}
1398 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1404 class screenshot_command
: public command
1407 screenshot_command() throw(std::bad_alloc
) : command("take-screenshot") {}
1408 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1411 throw std::runtime_error("Filename required");
1412 framebuffer
.save_png(args
);
1413 out(win
) << "Saved PNG screenshot" << std::endl
;
1415 std::string
get_short_help() throw(std::bad_alloc
) { return "Takes a screenshot"; }
1416 std::string
get_long_help() throw(std::bad_alloc
)
1418 return "Syntax: take-screenshot <file>\n"
1419 "Saves screenshot to PNG file <file>\n";
1423 class mouse_button_handler
: public command
1426 mouse_button_handler() throw(std::bad_alloc
) : command("mouse_button") {}
1427 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1429 tokensplitter
t(args
);
1433 int _x
= atoi(x
.c_str());
1434 int _y
= atoi(y
.c_str());
1435 int _b
= atoi(b
.c_str());
1436 if(_b
& ~prev_mouse_mask
& 1)
1437 send_analog_input(_x
, _y
, 0);
1438 if(_b
& ~prev_mouse_mask
& 2)
1439 send_analog_input(_x
, _y
, 1);
1440 if(_b
& ~prev_mouse_mask
& 4)
1441 send_analog_input(_x
, _y
, 2);
1442 prev_mouse_mask
= _b
;
1446 class button_action
: public command
1449 button_action(const std::string
& cmd
, int _type
, unsigned _controller
, std::string _button
)
1450 throw(std::bad_alloc
)
1455 controller
= _controller
;
1458 ~button_action() throw() {}
1459 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1462 throw std::runtime_error("This command does not take parameters");
1464 if(!buttonmap
.count(button
))
1466 auto i
= buttonmap
[button
];
1467 do_button_action(i
.first
, i
.second
, (type
!= 1) ? 1 : 0, (type
== 2));
1468 update_movie_state();
1469 win
->notify_screen_update();
1471 std::string
get_short_help() throw(std::bad_alloc
)
1473 return "Press/Unpress button";
1475 std::string
get_long_help() throw(std::bad_alloc
)
1477 return "Syntax: " + commandn
+ "\n"
1478 "Presses/Unpresses button\n";
1480 std::string commandn
;
1481 unsigned controller
;
1486 class button_action_helper
1489 button_action_helper()
1491 for(size_t i
= 0; i
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); ++i
)
1492 for(int j
= 0; j
< 3; ++j
)
1493 for(unsigned k
= 0; k
< 8; ++k
) {
1494 std::ostringstream x
, y
;
1507 x
<< buttonnames
[i
];
1509 y
<< buttonnames
[i
];
1510 new button_action(x
.str(), j
, k
, y
.str());
1515 //If there is a pending load, perform it.
1518 if(pending_load
!= "") {
1519 do_load_state(pending_load
, loadmode
);
1520 redraw_framebuffer();
1522 pending_reset_cycles
= -1;
1523 amode
= ADVANCE_AUTO
;
1526 if(!system_corrupt
) {
1527 location_special
= SPECIAL_SAVEPOINT
;
1528 update_movie_state();
1529 win
->notify_screen_update();
1537 //If there are pending saves, perform them.
1540 if(!queued_saves
.empty()) {
1541 stepping_into_save
= true;
1542 SNES::system
.runtosave();
1543 stepping_into_save
= false;
1544 for(auto i
= queued_saves
.begin(); i
!= queued_saves
.end(); i
++)
1547 queued_saves
.clear();
1550 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
1551 bool handle_reset(long cycles
)
1554 win
->message("SNES reset");
1555 SNES::system
.reset();
1556 framebuffer
= nosignal_screen
;
1557 lua_callback_do_reset(win
);
1558 redraw_framebuffer();
1559 } else if(cycles
> 0) {
1560 video_refresh_done
= false;
1561 long cycles_executed
= 0;
1562 out(win
) << "Executing delayed reset... This can take some time!" << std::endl
;
1563 while(cycles_executed
< cycles
&& !video_refresh_done
) {
1564 SNES::cpu
.op_step();
1567 if(!video_refresh_done
)
1568 out(win
) << "SNES reset (delayed " << cycles_executed
<< ")" << std::endl
;
1570 out(win
) << "SNES reset (forced at " << cycles_executed
<< ")" << std::endl
;
1571 SNES::system
.reset();
1572 framebuffer
= nosignal_screen
;
1573 lua_callback_do_reset(win
);
1574 redraw_framebuffer();
1575 if(video_refresh_done
) {
1576 to_wait_frame(get_ticks_msec());
1583 bool handle_corrupt()
1587 while(system_corrupt
) {
1588 framebuffer
= corrupt_screen
;
1589 redraw_framebuffer();
1594 if(amode
== ADVANCE_QUIT
)
1600 void print_controller_mappings()
1602 for(unsigned i
= 0; i
< 8; i
++) {
1603 std::string type
= "unknown";
1604 if(lookup_controller_type(i
) == DT_NONE
)
1605 type
= "disconnected";
1606 if(lookup_controller_type(i
) == DT_GAMEPAD
)
1608 if(lookup_controller_type(i
) == DT_MOUSE
)
1610 if(lookup_controller_type(i
) == DT_SUPERSCOPE
)
1611 type
= "superscope";
1612 if(lookup_controller_type(i
) == DT_JUSTIFIER
)
1614 out(win
) << "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
1615 lookup_physical_controller(i
) << " (" << type
<< ")" << std::endl
;
1620 void main_loop(window
* _win
, struct loaded_rom
& rom
, struct moviefile
& initial
) throw(std::bad_alloc
,
1623 //Basic initialization.
1627 auto old_inteface
= SNES::system
.interface
;
1628 SNES::system
.interface
= &intrf
;
1629 status
= &win
->get_emustatus();
1630 fill_special_frames();
1632 //Load our given movie.
1633 bool first_round
= false;
1634 bool just_did_loadstate
= false;
1636 do_load_state(initial
, LOAD_STATE_DEFAULT
);
1637 first_round
= our_movie
.is_savestate
;
1638 just_did_loadstate
= first_round
;
1639 } catch(std::bad_alloc
& e
) {
1641 } catch(std::exception
& e
) {
1642 win
->message(std::string("FATAL: Can't load initial state: ") + e
.what());
1647 lua_callback_startup(win
);
1649 //print_controller_mappings();
1650 av_snooper::add_dump_notifier(dumpwatch
);
1651 win
->set_main_surface(scr
);
1652 redraw_framebuffer();
1654 amode
= ADVANCE_PAUSE
;
1655 while(amode
!= ADVANCE_QUIT
) {
1656 if(handle_corrupt()) {
1657 first_round
= our_movie
.is_savestate
;
1658 just_did_loadstate
= true;
1661 long resetcycles
= -1;
1662 ack_frame_tick(get_ticks_msec());
1663 if(amode
== ADVANCE_SKIPLAG_PENDING
)
1664 amode
= ADVANCE_SKIPLAG
;
1667 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
1668 if(amode
== ADVANCE_QUIT
)
1670 bool delayed_reset
= (resetcycles
> 0);
1671 pending_reset_cycles
= -1;
1672 if(!handle_reset(resetcycles
)) {
1675 if(!delayed_reset
) {
1679 first_round
= our_movie
.is_savestate
;
1680 amode
= ADVANCE_PAUSE
;
1681 just_did_loadstate
= first_round
;
1685 if(just_did_loadstate
) {
1686 if(amode
== ADVANCE_QUIT
)
1688 amode
= ADVANCE_PAUSE
;
1689 redraw_framebuffer();
1693 just_did_loadstate
= false;
1696 if(amode
== ADVANCE_AUTO
)
1697 win
->wait_msec(to_wait_frame(get_ticks_msec()));
1698 first_round
= false;
1700 av_snooper::end(win
);
1701 SNES::system
.interface
= old_inteface
;