1 #include "mainloop.hpp"
4 #include "moviedata.hpp"
6 #include "framerate.hpp"
7 #include "memorywatch.hpp"
12 #include "moviefile.hpp"
15 #include "settings.hpp"
21 #include "memorymanip.hpp"
26 #include <snes/snes.hpp>
27 #include <ui-libsnes/libsnes.hpp>
28 #include "framerate.hpp"
30 #define LOAD_STATE_RW 0
31 #define LOAD_STATE_RO 1
32 #define LOAD_STATE_PRESERVE 2
33 #define LOAD_STATE_MOVIE 3
34 #define LOAD_STATE_DEFAULT 4
37 #define SPECIAL_FRAME_START 0
38 #define SPECIAL_FRAME_VIDEO 1
39 #define SPECIAL_SAVEPOINT 2
40 #define SPECIAL_NONE 3
42 #define BUTTON_LEFT 0 //Gamepad
43 #define BUTTON_RIGHT 1 //Gamepad
44 #define BUTTON_UP 2 //Gamepad
45 #define BUTTON_DOWN 3 //Gamepad
46 #define BUTTON_A 4 //Gamepad
47 #define BUTTON_B 5 //Gamepad
48 #define BUTTON_X 6 //Gamepad
49 #define BUTTON_Y 7 //Gamepad
50 #define BUTTON_L 8 //Gamepad & Mouse
51 #define BUTTON_R 9 //Gamepad & Mouse
52 #define BUTTON_SELECT 10 //Gamepad
53 #define BUTTON_START 11 //Gamepad & Justifier
54 #define BUTTON_TRIGGER 12 //Superscope.
55 #define BUTTON_CURSOR 13 //Superscope & Justifier
56 #define BUTTON_PAUSE 14 //Superscope
57 #define BUTTON_TURBO 15 //Superscope
59 void update_movie_state();
60 void draw_nosignal(uint16_t* target
);
61 void draw_corrupt(uint16_t* target
);
68 ADVANCE_QUIT
, //Quit the emulator.
69 ADVANCE_AUTO
, //Normal (possibly slowed down play).
70 ADVANCE_FRAME
, //Frame advance.
71 ADVANCE_SUBFRAME
, //Subframe advance.
72 ADVANCE_SKIPLAG
, //Skip lag (oneshot, reverts to normal).
73 ADVANCE_SKIPLAG_PENDING
, //Activate skip lag mode at next frame.
74 ADVANCE_PAUSE
, //Unconditional pause.
77 //Analog input physical controller IDs and types.
78 int analog
[4] = {-1, -1, -1};
79 bool analog_is_mouse
[4] = {false, false, false};
81 std::map
<std::string
, std::string
> memory_watches
;
82 //Previous mouse mask.
83 int prev_mouse_mask
= 0;
84 //Flags related to repeating advance.
88 struct loaded_rom
* our_rom
;
89 //Handle to the graphics system.
93 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
94 enum advance_mode amode
;
95 //Mode and filename of pending load, one of LOAD_* constants.
97 std::string pending_load
;
98 //Queued saves (all savestates).
99 std::set
<std::string
> queued_saves
;
100 bool stepping_into_save
;
102 controls_t curcontrols
;
103 controls_t autoheld_controls
;
104 //Emulator status area.
105 std::map
<std::string
, std::string
>* status
;
106 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
107 long pending_reset_cycles
= -1;
108 //Set by every video refresh.
109 bool video_refresh_done
;
110 //Special subframe location. One of SPECIAL_* constants.
111 int location_special
;
112 //Types of connected controllers.
113 enum porttype_t porttype1
= PT_GAMEPAD
;
114 enum porttype_t porttype2
= PT_NONE
;
115 //System corrupt flag.
117 //Current screen, no signal screen and corrupt screen.
118 lcscreen framebuffer
;
119 lcscreen nosignal_screen
;
120 lcscreen corrupt_screen
;
122 numeric_setting
advance_timeout_first("advance-timeout", 0, 999999999, 500);
123 numeric_setting
savecompression("savecompression", 0, 9, 7);
125 void send_analog_input(int32_t x
, int32_t y
, unsigned index
)
127 if(analog_is_mouse
[index
]) {
129 y
-= (framebuffer
.height
/ 2);
134 if(analog
[index
] < 0) {
135 out(win
) << "No analog controller in slot #" << (index
+ 1) << std::endl
;
138 curcontrols(analog
[index
] >> 2, analog
[index
] & 3, 0) = x
;
139 curcontrols(analog
[index
] >> 2, analog
[index
] & 3, 1) = y
;
142 void redraw_framebuffer()
144 uint32_t hscl
= 1, vscl
= 1;
145 if(framebuffer
.width
< 512)
147 if(framebuffer
.height
< 400)
150 struct lua_render_context lrc
;
156 lrc
.width
= framebuffer
.width
* hscl
;
157 lrc
.height
= framebuffer
.height
* vscl
;
158 lrc
.rshift
= scr
.active_rshift
;
159 lrc
.gshift
= scr
.active_gshift
;
160 lrc
.bshift
= scr
.active_bshift
;
161 lua_callback_do_paint(&lrc
, win
);
162 scr
.reallocate(framebuffer
.width
* hscl
+ lrc
.left_gap
+ lrc
.right_gap
, framebuffer
.height
* vscl
+
163 lrc
.top_gap
+ lrc
.bottom_gap
, lrc
.left_gap
, lrc
.top_gap
);
164 scr
.copy_from(framebuffer
, hscl
, vscl
);
165 //We would want divide by 2, but we'll do it ourselves in order to do mouse.
166 win
->set_window_compensation(lrc
.left_gap
, lrc
.top_gap
, 1, 1);
168 win
->notify_screen_update();
171 void fill_special_frames()
173 uint16_t buf
[512*448];
175 nosignal_screen
= lcscreen(buf
, 512, 448);
177 corrupt_screen
= lcscreen(buf
, 512, 448);
181 class firmware_path_setting
: public setting
184 firmware_path_setting() : setting("firmwarepath") { _firmwarepath
= "./"; default_firmware
= true; }
185 void blank() throw(std::bad_alloc
, std::runtime_error
)
187 _firmwarepath
= "./";
188 default_firmware
= true;
191 bool is_set() throw()
193 return !default_firmware
;
196 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
198 _firmwarepath
= value
;
199 default_firmware
= false;
202 std::string
get() throw(std::bad_alloc
)
204 return _firmwarepath
;
207 operator std::string() throw(std::bad_alloc
)
209 return _firmwarepath
;
212 std::string _firmwarepath
;
213 bool default_firmware
;
214 } firmwarepath_setting
;
216 class mymovielogic
: public movie_logic
219 mymovielogic() : movie_logic(dummy_movie
) {}
221 controls_t
update_controls(bool subframe
) throw(std::bad_alloc
, std::runtime_error
)
223 if(lua_requests_subframe_paint
)
224 redraw_framebuffer();
227 if(amode
== ADVANCE_SUBFRAME
) {
228 if(!cancel_advance
&& !advanced_once
) {
229 win
->wait_msec(advance_timeout_first
);
230 advanced_once
= true;
233 amode
= ADVANCE_PAUSE
;
234 cancel_advance
= false;
236 win
->paused(amode
== ADVANCE_PAUSE
);
237 } else if(amode
== ADVANCE_FRAME
) {
240 win
->paused(amode
== ADVANCE_SKIPLAG
|| amode
== ADVANCE_PAUSE
);
241 cancel_advance
= false;
243 if(amode
== ADVANCE_SKIPLAG
)
244 amode
= ADVANCE_AUTO
;
245 location_special
= SPECIAL_NONE
;
246 update_movie_state();
248 if(amode
== ADVANCE_SKIPLAG_PENDING
)
249 amode
= ADVANCE_SKIPLAG
;
250 if(amode
== ADVANCE_FRAME
|| amode
== ADVANCE_SUBFRAME
) {
251 if(!cancel_advance
) {
252 win
->wait_msec(advanced_once
? to_wait_frame(get_ticks_msec()) :
253 advance_timeout_first
);
254 advanced_once
= true;
257 amode
= ADVANCE_PAUSE
;
258 cancel_advance
= false;
260 win
->paused(amode
== ADVANCE_PAUSE
);
262 win
->paused((amode
== ADVANCE_PAUSE
));
263 cancel_advance
= false;
265 location_special
= SPECIAL_FRAME_START
;
266 update_movie_state();
268 win
->notify_screen_update();
270 if(!subframe
&& pending_reset_cycles
>= 0) {
271 curcontrols(CONTROL_SYSTEM_RESET
) = 1;
272 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = pending_reset_cycles
/ 10000;
273 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = pending_reset_cycles
% 10000;
274 } else if(!subframe
) {
275 curcontrols(CONTROL_SYSTEM_RESET
) = 0;
276 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = 0;
277 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = 0;
279 controls_t tmp
= curcontrols
^ autoheld_controls
;
280 lua_callback_do_input(tmp
, subframe
, win
);
291 //Lookup physical controller id based on UI controller id and given types (-1 if invalid).
292 int lookup_physical_controller(unsigned ui_id
)
294 bool p1multitap
= (porttype1
== PT_MULTITAP
);
295 unsigned p1devs
= port_types
[porttype1
].devices
;
296 unsigned p2devs
= port_types
[porttype2
].devices
;
297 if(ui_id
>= p1devs
+ p2devs
)
303 return 4 + ui_id
- p1devs
;
313 //Look up controller type given UI controller id (note: Non-present controllers give PT_NONE, not the type
314 //of port, multitap controllers give PT_GAMEPAD, not PT_MULTITAP, and justifiers give PT_JUSTIFIER, not
316 enum devicetype_t
lookup_controller_type(unsigned ui_id
)
318 int x
= lookup_physical_controller(ui_id
);
321 enum porttype_t rawtype
= (x
& 4) ? porttype2
: porttype1
;
322 if((x
& 3) < port_types
[rawtype
].devices
)
323 return port_types
[rawtype
].dtype
;
328 void set_analog_controllers()
331 for(unsigned i
= 0; i
< 8; i
++) {
332 enum devicetype_t t
= lookup_controller_type(i
);
333 analog_is_mouse
[index
] = (t
== DT_MOUSE
);
334 if(t
== DT_MOUSE
|| t
== DT_SUPERSCOPE
|| t
== DT_JUSTIFIER
) {
335 analog
[index
++] = lookup_physical_controller(i
);
339 for(; index
< 3; index
++)
343 std::map
<std::string
, std::pair
<unsigned, unsigned>> buttonmap
;
345 const char* buttonnames
[] = {
346 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
350 void init_buttonmap()
355 for(unsigned i
= 0; i
< 8; i
++)
356 for(unsigned j
= 0; j
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); j
++) {
357 std::ostringstream x
;
358 x
<< (i
+ 1) << buttonnames
[j
];
359 buttonmap
[x
.str()] = std::make_pair(i
, j
);
365 void do_button_action(unsigned ui_id
, unsigned button
, short newstate
, bool do_xor
= false)
367 enum devicetype_t p
= lookup_controller_type(ui_id
);
368 int x
= lookup_physical_controller(ui_id
);
372 out(win
) << "No such controller #" << (ui_id
+ 1) << std::endl
;
376 case BUTTON_UP
: bid
= SNES_DEVICE_ID_JOYPAD_UP
; break;
377 case BUTTON_DOWN
: bid
= SNES_DEVICE_ID_JOYPAD_DOWN
; break;
378 case BUTTON_LEFT
: bid
= SNES_DEVICE_ID_JOYPAD_LEFT
; break;
379 case BUTTON_RIGHT
: bid
= SNES_DEVICE_ID_JOYPAD_RIGHT
; break;
380 case BUTTON_A
: bid
= SNES_DEVICE_ID_JOYPAD_A
; break;
381 case BUTTON_B
: bid
= SNES_DEVICE_ID_JOYPAD_B
; break;
382 case BUTTON_X
: bid
= SNES_DEVICE_ID_JOYPAD_X
; break;
383 case BUTTON_Y
: bid
= SNES_DEVICE_ID_JOYPAD_Y
; break;
384 case BUTTON_L
: bid
= SNES_DEVICE_ID_JOYPAD_L
; break;
385 case BUTTON_R
: bid
= SNES_DEVICE_ID_JOYPAD_R
; break;
386 case BUTTON_SELECT
: bid
= SNES_DEVICE_ID_JOYPAD_SELECT
; break;
387 case BUTTON_START
: bid
= SNES_DEVICE_ID_JOYPAD_START
; break;
389 out(win
) << "Invalid button for gamepad" << std::endl
;
395 case BUTTON_L
: bid
= SNES_DEVICE_ID_MOUSE_LEFT
; break;
396 case BUTTON_R
: bid
= SNES_DEVICE_ID_MOUSE_RIGHT
; break;
398 out(win
) << "Invalid button for mouse" << std::endl
;
404 case BUTTON_START
: bid
= SNES_DEVICE_ID_JUSTIFIER_START
; break;
405 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_JUSTIFIER_TRIGGER
; break;
407 out(win
) << "Invalid button for justifier" << std::endl
;
413 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
; break;
414 case BUTTON_CURSOR
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
; break;
415 case BUTTON_PAUSE
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
; break;
416 case BUTTON_TURBO
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TURBO
; break;
418 out(win
) << "Invalid button for superscope" << std::endl
;
424 autoheld_controls((x
& 4) ? 1 : 0, x
& 3, bid
) ^= newstate
;
426 curcontrols((x
& 4) ? 1 : 0, x
& 3, bid
) = newstate
;
430 void do_save_state(const std::string
& filename
) throw(std::bad_alloc
,
433 lua_callback_pre_save(filename
, true, win
);
435 uint64_t origtime
= get_ticks_msec();
436 our_movie
.is_savestate
= true;
437 our_movie
.sram
= save_sram();
438 our_movie
.savestate
= save_core_state();
439 framebuffer
.save(our_movie
.screenshot
);
440 auto s
= movb
.get_movie().save_state();
441 our_movie
.movie_state
.resize(s
.size());
442 memcpy(&our_movie
.movie_state
[0], &s
[0], s
.size());
443 our_movie
.input
= movb
.get_movie().save();
444 our_movie
.save(filename
, savecompression
);
445 uint64_t took
= get_ticks_msec() - origtime
;
446 out(win
) << "Saved state '" << filename
<< "' in " << took
<< "ms." << std::endl
;
447 lua_callback_post_save(filename
, true, win
);
448 } catch(std::bad_alloc
& e
) {
450 } catch(std::exception
& e
) {
451 win
->message(std::string("Save failed: ") + e
.what());
452 lua_callback_err_save(filename
, win
);
457 void do_save_movie(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
459 lua_callback_pre_save(filename
, false, win
);
461 uint64_t origtime
= get_ticks_msec();
462 our_movie
.is_savestate
= false;
463 our_movie
.input
= movb
.get_movie().save();
464 our_movie
.save(filename
, savecompression
);
465 uint64_t took
= get_ticks_msec() - origtime
;
466 out(win
) << "Saved movie '" << filename
<< "' in " << took
<< "ms." << std::endl
;
467 lua_callback_post_save(filename
, false, win
);
468 } catch(std::bad_alloc
& e
) {
470 } catch(std::exception
& e
) {
471 win
->message(std::string("Save failed: ") + e
.what());
472 lua_callback_err_save(filename
, win
);
476 void warn_hash_mismatch(const std::string
& mhash
, const loaded_slot
& slot
, const std::string
& name
)
478 if(mhash
!= slot
.sha256
) {
479 out(win
) << "WARNING: " << name
<< " hash mismatch!" << std::endl
480 << "\tMovie: " << mhash
<< std::endl
481 << "\tOur ROM: " << slot
.sha256
<< std::endl
;
485 void set_dev(bool port
, porttype_t t
, bool set_core
= true)
488 switch(set_core
? t
: PT_INVALID
) {
490 snes_set_controller_port_device(port
, SNES_DEVICE_NONE
);
493 snes_set_controller_port_device(port
, SNES_DEVICE_JOYPAD
);
496 snes_set_controller_port_device(port
, SNES_DEVICE_MULTITAP
);
499 snes_set_controller_port_device(port
, SNES_DEVICE_MOUSE
);
502 snes_set_controller_port_device(port
, SNES_DEVICE_SUPER_SCOPE
);
505 snes_set_controller_port_device(port
, SNES_DEVICE_JUSTIFIER
);
508 snes_set_controller_port_device(port
, SNES_DEVICE_JUSTIFIERS
);
517 set_analog_controllers();
520 //Load state from loaded movie file (does not catch errors).
521 void do_load_state(struct moviefile
& _movie
, int lmode
)
523 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
524 if(gtype::toromtype(_movie
.gametype
) != our_rom
->rtype
)
525 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
526 if(gtype::toromregion(_movie
.gametype
) != our_rom
->orig_region
&& our_rom
->orig_region
!= REGION_AUTO
)
527 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
529 if(_movie
.coreversion
!= bsnes_core_version
) {
530 if(will_load_state
) {
531 std::ostringstream x
;
532 x
<< "ERROR: Emulator core version mismatch!" << std::endl
533 << "\tThis version: " << bsnes_core_version
<< std::endl
534 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
535 throw std::runtime_error(x
.str());
537 out(win
) << "WARNING: Emulator core version mismatch!" << std::endl
538 << "\tThis version: " << bsnes_core_version
<< std::endl
539 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
541 warn_hash_mismatch(_movie
.rom_sha256
, our_rom
->rom
, "ROM #1");
542 warn_hash_mismatch(_movie
.romxml_sha256
, our_rom
->rom_xml
, "XML #1");
543 warn_hash_mismatch(_movie
.slota_sha256
, our_rom
->slota
, "ROM #2");
544 warn_hash_mismatch(_movie
.slotaxml_sha256
, our_rom
->slota_xml
, "XML #2");
545 warn_hash_mismatch(_movie
.slotb_sha256
, our_rom
->slotb
, "ROM #3");
546 warn_hash_mismatch(_movie
.slotbxml_sha256
, our_rom
->slotb_xml
, "XML #3");
548 SNES::config
.random
= false;
549 SNES::config
.expansion_port
= SNES::System::ExpansionPortDevice::None
;
552 if(lmode
== LOAD_STATE_PRESERVE
)
553 newmovie
= movb
.get_movie();
555 newmovie
.load(_movie
.rerecords
, _movie
.projectid
, _movie
.input
);
557 if(will_load_state
) {
558 std::vector
<unsigned char> tmp
;
559 tmp
.resize(_movie
.movie_state
.size());
560 memcpy(&tmp
[0], &_movie
.movie_state
[0], tmp
.size());
561 newmovie
.restore_state(tmp
, true);
565 rrdata::read_base(_movie
.projectid
);
566 rrdata::add_internal();
568 our_rom
->region
= gtype::toromregion(_movie
.gametype
);
571 if(_movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
) {
572 //Load the savestate and movie state.
573 set_dev(false, _movie
.port1
);
574 set_dev(true, _movie
.port2
);
575 load_core_state(_movie
.savestate
);
576 framebuffer
.load(_movie
.screenshot
);
578 load_sram(_movie
.movie_sram
, win
);
579 set_dev(false, _movie
.port1
);
580 set_dev(true, _movie
.port2
);
581 framebuffer
= nosignal_screen
;
583 } catch(std::bad_alloc
& e
) {
585 } catch(std::exception
& e
) {
586 system_corrupt
= true;
590 //Okay, copy the movie data.
592 if(!our_movie
.is_savestate
|| lmode
== LOAD_STATE_MOVIE
) {
593 our_movie
.is_savestate
= false;
594 our_movie
.host_memory
.clear();
596 movb
.get_movie() = newmovie
;
597 //Activate RW mode if needed.
598 if(lmode
== LOAD_STATE_RW
)
599 movb
.get_movie().readonly_mode(false);
600 if(lmode
== LOAD_STATE_DEFAULT
&& !(movb
.get_movie().get_frame_count()))
601 movb
.get_movie().readonly_mode(false);
602 out(win
) << "ROM Type ";
603 switch(our_rom
->rtype
) {
610 case ROMTYPE_BSXSLOTTED
:
611 out(win
) << "BS-X slotted";
613 case ROMTYPE_SUFAMITURBO
:
614 out(win
) << "Sufami Turbo";
617 out(win
) << "Super Game Boy";
620 out(win
) << "Unknown";
623 out(win
) << " region ";
624 switch(our_rom
->region
) {
632 out(win
) << "Unknown";
635 out(win
) << std::endl
;
636 uint64_t mlength
= _movie
.get_movie_length();
639 std::ostringstream x
;
640 if(mlength
> 3600000000000) {
641 x
<< mlength
/ 3600000000000 << ":";
642 mlength
%= 3600000000000;
644 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000000000 << ":";
645 mlength
%= 60000000000;
646 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000000000 << ".";
647 mlength
%= 1000000000;
648 x
<< std::setfill('0') << std::setw(3) << mlength
/ 1000000;
649 out(win
) << "Rerecords " << _movie
.rerecords
<< " length " << x
.str() << " ("
650 << _movie
.get_frame_count() << " frames)" << std::endl
;
653 if(_movie
.gamename
!= "")
654 out(win
) << "Game Name: " << _movie
.gamename
<< std::endl
;
655 for(size_t i
= 0; i
< _movie
.authors
.size(); i
++)
656 out(win
) << "Author: " << _movie
.authors
[i
].first
<< "(" << _movie
.authors
[i
].second
<< ")"
661 void do_load_state(const std::string
& filename
, int lmode
)
663 uint64_t origtime
= get_ticks_msec();
664 lua_callback_pre_load(filename
, win
);
665 struct moviefile mfile
;
667 mfile
= moviefile(filename
);
668 } catch(std::bad_alloc
& e
) {
670 } catch(std::exception
& e
) {
671 win
->message("Can't read movie/savestate '" + filename
+ "': " + e
.what());
672 lua_callback_err_load(filename
, win
);
676 do_load_state(mfile
, lmode
);
677 uint64_t took
= get_ticks_msec() - origtime
;
678 out(win
) << "Loaded '" << filename
<< "' in " << took
<< "ms." << std::endl
;
679 lua_callback_post_load(filename
, our_movie
.is_savestate
, win
);
680 } catch(std::bad_alloc
& e
) {
682 } catch(std::exception
& e
) {
683 win
->message("Can't load movie/savestate '" + filename
+ "': " + e
.what());
684 lua_callback_err_load(filename
, win
);
689 //Do pending load (automatically unpauses).
690 void mark_pending_load(const std::string
& filename
, int lmode
)
693 pending_load
= filename
;
694 amode
= ADVANCE_AUTO
;
699 //Mark pending save (movies save immediately).
700 void mark_pending_save(const std::string
& filename
, int smode
)
702 if(smode
== SAVE_MOVIE
) {
703 //Just do this immediately.
704 do_save_movie(filename
);
707 queued_saves
.insert(filename
);
708 win
->message("Pending save on '" + filename
+ "'");
711 class dump_watch
: public av_snooper::dump_notification
713 void dump_starting() throw()
715 update_movie_state();
717 void dump_ending() throw()
719 update_movie_state();
726 return movb
.get_movie();
729 void update_movie_state()
731 auto& _status
= win
->get_emustatus();
733 std::ostringstream x
;
734 x
<< movb
.get_movie().get_current_frame() << "(";
735 if(location_special
== SPECIAL_FRAME_START
)
737 else if(location_special
== SPECIAL_SAVEPOINT
)
739 else if(location_special
== SPECIAL_FRAME_VIDEO
)
742 x
<< movb
.get_movie().next_poll_number();
743 x
<< ";" << movb
.get_movie().get_lag_frames() << ")/" << movb
.get_movie().get_frame_count();
744 _status
["Frame"] = x
.str();
747 std::ostringstream x
;
748 if(movb
.get_movie().readonly_mode())
752 if(av_snooper::dump_in_progress())
754 _status
["Flags"] = x
.str();
756 for(auto i
= memory_watches
.begin(); i
!= memory_watches
.end(); i
++) {
758 _status
["M[" + i
->first
+ "]"] = evaluate_watch(i
->second
);
763 if(movb
.get_movie().readonly_mode())
764 c
= movb
.get_movie().get_controls();
766 c
= curcontrols
^ autoheld_controls
;
767 for(unsigned i
= 0; i
< 8; i
++) {
768 unsigned pindex
= lookup_physical_controller(i
);
769 unsigned port
= pindex
>> 2;
770 unsigned dev
= pindex
& 3;
771 auto ctype
= lookup_controller_type(i
);
772 std::ostringstream x
;
775 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_LEFT
) ? "l" : " ");
776 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_RIGHT
) ? "r" : " ");
777 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_UP
) ? "u" : " ");
778 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_DOWN
) ? "d" : " ");
779 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_A
) ? "A" : " ");
780 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_B
) ? "B" : " ");
781 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_X
) ? "X" : " ");
782 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_Y
) ? "Y" : " ");
783 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_L
) ? "L" : " ");
784 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_R
) ? "R" : " ");
785 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_START
) ? "S" : " ");
786 x
<< (c(port
, dev
, SNES_DEVICE_ID_JOYPAD_SELECT
) ? "s" : " ");
789 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_X
) << " ";
790 x
<< c(port
, dev
, SNES_DEVICE_ID_MOUSE_Y
) << " ";
791 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_LEFT
) ? "L" : " ");
792 x
<< (c(port
, dev
, SNES_DEVICE_ID_MOUSE_RIGHT
) ? "R" : " ");
795 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_X
) << " ";
796 x
<< c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_Y
) << " ";
797 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
) ? "T" : " ");
798 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
) ? "C" : " ");
799 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_TURBO
) ? "t" : " ");
800 x
<< (c(port
, dev
, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
) ? "P" : " ");
803 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_X
) << " ";
804 x
<< c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_Y
) << " ";
805 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_START
) ? "T" : " ");
806 x
<< (c(port
, dev
, SNES_DEVICE_ID_JUSTIFIER_TRIGGER
) ? "S" : " ");
811 char y
[3] = {'P', 0, 0};
813 _status
[std::string(y
)] = x
.str();
818 class my_interface
: public SNES::Interface
820 string
path(SNES::Cartridge::Slot slot
, const string
&hint
)
822 return static_cast<std::string
>(firmwarepath_setting
).c_str();
825 void video_refresh(const uint16_t *data
, bool hires
, bool interlace
, bool overscan
)
827 if(stepping_into_save
)
828 win
->message("Got video refresh in runtosave, expect desyncs!");
829 video_refresh_done
= true;
830 bool region
= (SNES::system
.region() == SNES::System::Region::PAL
);
831 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
832 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
833 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
834 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
835 lcscreen
ls(data
, hires
, interlace
, overscan
, region
);
837 location_special
= SPECIAL_FRAME_VIDEO
;
838 update_movie_state();
839 redraw_framebuffer();
840 uint32_t fps_n
, fps_d
;
848 av_snooper::frame(ls
, fps_n
, fps_d
, win
);
851 void audio_sample(int16_t l_sample
, int16_t r_sample
)
853 uint16_t _l
= l_sample
;
854 uint16_t _r
= r_sample
;
855 win
->play_audio_sample(_l
+ 32768, _r
+ 32768);
856 av_snooper::sample(_l
, _r
, win
);
859 void audio_sample(uint16_t l_sample
, uint16_t r_sample
)
861 //Yes, this interface is broken. The samples are signed but are passed as unsigned!
862 win
->play_audio_sample(l_sample
+ 32768, r_sample
+ 32768);
863 av_snooper::sample(l_sample
, r_sample
, win
);
866 int16_t input_poll(bool port
, SNES::Input::Device device
, unsigned index
, unsigned id
)
869 x
= movb
.input_poll(port
, index
, id
);
870 //if(id == SNES_DEVICE_ID_JOYPAD_START)
871 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
872 lua_callback_snoop_input(port
? 1 : 0, index
, id
, x
, win
);
879 class quit_emulator_cmd
: public command
882 quit_emulator_cmd() throw(std::bad_alloc
) : command("quit-emulator") {}
883 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
885 if(args
== "/y" || win
->modal_message("Really quit?", true)) {
886 amode
= ADVANCE_QUIT
;
891 std::string
get_short_help() throw(std::bad_alloc
) { return "Quit the emulator"; }
892 std::string
get_long_help() throw(std::bad_alloc
)
894 return "Syntax: quit-emulator [/y]\n"
895 "Quits emulator (/y => don't ask for confirmation).\n";
899 class pause_emulator_cmd
: public command
902 pause_emulator_cmd() throw(std::bad_alloc
) : command("pause-emulator") {}
903 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
906 throw std::runtime_error("This command does not take parameters");
907 if(amode
!= ADVANCE_AUTO
) {
908 amode
= ADVANCE_AUTO
;
911 win
->message("Unpaused");
914 cancel_advance
= false;
915 amode
= ADVANCE_PAUSE
;
916 win
->message("Paused");
919 std::string
get_short_help() throw(std::bad_alloc
) { return "(Un)pause the emulator"; }
920 std::string
get_long_help() throw(std::bad_alloc
)
922 return "Syntax: pause-emulator\n"
923 "(Un)pauses the emulator.\n";
927 class padvance_frame_cmd
: public command
930 padvance_frame_cmd() throw(std::bad_alloc
) : command("+advance-frame") {}
931 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
934 throw std::runtime_error("This command does not take parameters");
935 amode
= ADVANCE_FRAME
;
936 cancel_advance
= false;
937 advanced_once
= false;
941 std::string
get_short_help() throw(std::bad_alloc
) { return "Advance one frame"; }
942 std::string
get_long_help() throw(std::bad_alloc
)
944 return "Syntax: +advance-frame\n"
945 "Advances the emulation by one frame.\n";
949 class nadvance_frame_cmd
: public command
952 nadvance_frame_cmd() throw(std::bad_alloc
) : command("-advance-frame") {}
953 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
956 throw std::runtime_error("This command does not take parameters");
957 cancel_advance
= true;
963 class padvance_poll_cmd
: public command
966 padvance_poll_cmd() throw(std::bad_alloc
) : command("+advance-poll") {}
967 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
970 throw std::runtime_error("This command does not take parameters");
971 amode
= ADVANCE_SUBFRAME
;
972 cancel_advance
= false;
973 advanced_once
= false;
977 std::string
get_short_help() throw(std::bad_alloc
) { return "Advance one subframe"; }
978 std::string
get_long_help() throw(std::bad_alloc
)
980 return "Syntax: +advance-poll\n"
981 "Advances the emulation by one subframe.\n";
985 class nadvance_poll_cmd
: public command
988 nadvance_poll_cmd() throw(std::bad_alloc
) : command("-advance-poll") {}
990 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
993 throw std::runtime_error("This command does not take parameters");
994 cancel_advance
= true;
1000 class advance_skiplag_cmd
: public command
1003 advance_skiplag_cmd() throw(std::bad_alloc
) : command("advance-skiplag") {}
1004 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1007 throw std::runtime_error("This command does not take parameters");
1008 amode
= ADVANCE_SKIPLAG
;
1012 std::string
get_short_help() throw(std::bad_alloc
) { return "Skip to next poll"; }
1013 std::string
get_long_help() throw(std::bad_alloc
)
1015 return "Syntax: advance-skiplag\n"
1016 "Advances the emulation to the next poll.\n";
1020 class reset_cmd
: public command
1023 reset_cmd() throw(std::bad_alloc
) : command("reset") {}
1024 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1027 throw std::runtime_error("This command does not take parameters");
1028 pending_reset_cycles
= 0;
1030 std::string
get_short_help() throw(std::bad_alloc
) { return "Reset the SNES"; }
1031 std::string
get_long_help() throw(std::bad_alloc
)
1033 return "Syntax: reset\n"
1034 "Resets the SNES in beginning of the next frame.\n";
1038 class load_state_cmd
: public command
1041 load_state_cmd() throw(std::bad_alloc
) : command("load-state") {}
1042 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1045 throw std::runtime_error("Filename required");
1046 mark_pending_load(args
, LOAD_STATE_RW
);
1048 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1049 std::string
get_long_help() throw(std::bad_alloc
)
1051 return "Syntax: load-state <file>\n"
1052 "Loads SNES state from <file> in Read/Write mode\n";
1056 class load_readonly_cmd
: public command
1059 load_readonly_cmd() throw(std::bad_alloc
) : command("load-readonly") {}
1060 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1063 throw std::runtime_error("Filename required");
1064 mark_pending_load(args
, LOAD_STATE_RO
);
1066 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1067 std::string
get_long_help() throw(std::bad_alloc
)
1069 return "Syntax: load-readonly <file>\n"
1070 "Loads SNES state from <file> in Read-only mode\n";
1074 class load_preserve_cmd
: public command
1077 load_preserve_cmd() throw(std::bad_alloc
) : command("load-preserve") {}
1078 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1081 throw std::runtime_error("Filename required");
1082 mark_pending_load(args
, LOAD_STATE_PRESERVE
);
1084 std::string
get_short_help() throw(std::bad_alloc
) { return "Load state"; }
1085 std::string
get_long_help() throw(std::bad_alloc
)
1087 return "Syntax: load-preserve <file>\n"
1088 "Loads SNES state from <file> preserving input\n";
1092 class load_movie_cmd
: public command
1095 load_movie_cmd() throw(std::bad_alloc
) : command("load-movie") {}
1096 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1099 throw std::runtime_error("Filename required");
1100 mark_pending_load(args
, LOAD_STATE_MOVIE
);
1102 std::string
get_short_help() throw(std::bad_alloc
) { return "Load movie"; }
1103 std::string
get_long_help() throw(std::bad_alloc
)
1105 return "Syntax: load-movie <file>\n"
1106 "Loads movie from <file>\n";
1110 class save_state_cmd
: public command
1113 save_state_cmd() throw(std::bad_alloc
) : command("save-state") {}
1114 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1117 throw std::runtime_error("Filename required");
1118 mark_pending_save(args
, SAVE_STATE
);
1121 std::string
get_short_help() throw(std::bad_alloc
) { return "Save state"; }
1122 std::string
get_long_help() throw(std::bad_alloc
)
1124 return "Syntax: save-state <file>\n"
1125 "Saves SNES state to <file>\n";
1129 class save_movie_cmd
: public command
1132 save_movie_cmd() throw(std::bad_alloc
) : command("save-movie") {}
1133 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1136 throw std::runtime_error("Filename required");
1137 mark_pending_save(args
, SAVE_MOVIE
);
1139 std::string
get_short_help() throw(std::bad_alloc
) { return "Save movie"; }
1140 std::string
get_long_help() throw(std::bad_alloc
)
1142 return "Syntax: save-movie <file>\n"
1143 "Saves movie to <file>\n";
1147 class set_rwmode_cmd
: public command
1150 set_rwmode_cmd() throw(std::bad_alloc
) : command("set-rwmode") {}
1151 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1154 throw std::runtime_error("This command does not take parameters");
1155 movb
.get_movie().readonly_mode(false);
1156 lua_callback_do_readwrite(win
);
1157 update_movie_state();
1158 win
->notify_screen_update();
1160 std::string
get_short_help() throw(std::bad_alloc
) { return "Switch to read/write mode"; }
1161 std::string
get_long_help() throw(std::bad_alloc
)
1163 return "Syntax: set-rwmode\n"
1164 "Switches to read/write mode\n";
1168 class repainter
: public command
1171 repainter() throw(std::bad_alloc
) : command("repaint") {}
1172 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1175 throw std::runtime_error("This command does not take parameters");
1176 redraw_framebuffer();
1178 std::string
get_short_help() throw(std::bad_alloc
) { return "Redraw the screen"; }
1179 std::string
get_long_help() throw(std::bad_alloc
)
1181 return "Syntax: repaint\n"
1182 "Redraws the screen\n";
1186 class set_gamename_cmd
: public command
1189 set_gamename_cmd() throw(std::bad_alloc
) : command("set-gamename") {}
1190 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1192 our_movie
.gamename
= args
;
1193 out(win
) << "Game name changed to '" << our_movie
.gamename
<< "'" << std::endl
;
1195 std::string
get_short_help() throw(std::bad_alloc
) { return "Set the game name"; }
1196 std::string
get_long_help() throw(std::bad_alloc
)
1198 return "Syntax: set-gamename <name>\n"
1199 "Sets the game name to <name>\n";
1203 class add_watch_command
: public command
1206 add_watch_command() throw(std::bad_alloc
) : command("add-watch") {}
1207 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1209 tokensplitter
t(args
);
1210 std::string name
= t
;
1211 if(name
== "" || t
.tail() == "")
1212 throw std::runtime_error("syntax: add-watch <name> <expr>");
1213 std::cerr
<< "Add watch: '" << name
<< "'" << std::endl
;
1214 memory_watches
[name
] = t
.tail();
1215 update_movie_state();
1217 std::string
get_short_help() throw(std::bad_alloc
) { return "Add a memory watch"; }
1218 std::string
get_long_help() throw(std::bad_alloc
)
1220 return "Syntax: add-watch <name> <expression>\n"
1221 "Adds a new memory watch\n";
1225 class remove_watch_command
: public command
1228 remove_watch_command() throw(std::bad_alloc
) : command("remove-watch") {}
1229 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1231 tokensplitter
t(args
);
1232 std::string name
= t
;
1233 if(name
== "" || t
.tail() != "") {
1234 out(win
) << "syntax: remove-watch <name>" << std::endl
;
1237 std::cerr
<< "Erase watch: '" << name
<< "'" << std::endl
;
1238 memory_watches
.erase(name
);
1239 auto& _status
= win
->get_emustatus();
1240 _status
.erase("M[" + name
+ "]");
1241 update_movie_state(); }
1242 std::string
get_short_help() throw(std::bad_alloc
) { return "Remove a memory watch"; }
1243 std::string
get_long_help() throw(std::bad_alloc
)
1245 return "Syntax: remove-watch <name>\n"
1246 "Removes a memory watch\n";
1250 class test_1
: public command
1253 test_1() throw(std::bad_alloc
) : command("test-1") {}
1254 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1256 framebuffer
= nosignal_screen
;
1257 redraw_framebuffer();
1261 class test_2
: public command
1264 test_2() throw(std::bad_alloc
) : command("test-2") {}
1265 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1267 framebuffer
= corrupt_screen
;
1268 redraw_framebuffer();
1272 class test_3
: public command
1275 test_3() throw(std::bad_alloc
) : command("test-3") {}
1276 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1282 class screenshot_command
: public command
1285 screenshot_command() throw(std::bad_alloc
) : command("take-screenshot") {}
1286 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1289 throw std::runtime_error("Filename required");
1290 framebuffer
.save_png(args
);
1291 out(win
) << "Saved PNG screenshot" << std::endl
;
1293 std::string
get_short_help() throw(std::bad_alloc
) { return "Takes a screenshot"; }
1294 std::string
get_long_help() throw(std::bad_alloc
)
1296 return "Syntax: take-screenshot <file>\n"
1297 "Saves screenshot to PNG file <file>\n";
1301 class mouse_button_handler
: public command
1304 mouse_button_handler() throw(std::bad_alloc
) : command("mouse_button") {}
1305 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1307 tokensplitter
t(args
);
1311 int _x
= atoi(x
.c_str());
1312 int _y
= atoi(y
.c_str());
1313 int _b
= atoi(b
.c_str());
1314 if(_b
& ~prev_mouse_mask
& 1)
1315 send_analog_input(_x
, _y
, 0);
1316 if(_b
& ~prev_mouse_mask
& 2)
1317 send_analog_input(_x
, _y
, 1);
1318 if(_b
& ~prev_mouse_mask
& 4)
1319 send_analog_input(_x
, _y
, 2);
1320 prev_mouse_mask
= _b
;
1324 class button_action
: public command
1327 button_action(const std::string
& cmd
, int _type
, unsigned _controller
, std::string _button
)
1328 throw(std::bad_alloc
)
1333 controller
= _controller
;
1336 ~button_action() throw() {}
1337 void invoke(const std::string
& args
, window
* win
) throw(std::bad_alloc
, std::runtime_error
)
1340 throw std::runtime_error("This command does not take parameters");
1342 if(!buttonmap
.count(button
))
1344 auto i
= buttonmap
[button
];
1345 do_button_action(i
.first
, i
.second
, (type
!= 1) ? 1 : 0, (type
== 2));
1346 update_movie_state();
1347 win
->notify_screen_update();
1349 std::string
get_short_help() throw(std::bad_alloc
)
1351 return "Press/Unpress button";
1353 std::string
get_long_help() throw(std::bad_alloc
)
1355 return "Syntax: " + commandn
+ "\n"
1356 "Presses/Unpresses button\n";
1358 std::string commandn
;
1359 unsigned controller
;
1364 class button_action_helper
1367 button_action_helper()
1369 for(size_t i
= 0; i
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); ++i
)
1370 for(int j
= 0; j
< 3; ++j
)
1371 for(unsigned k
= 0; k
< 8; ++k
) {
1372 std::ostringstream x
, y
;
1385 x
<< buttonnames
[i
];
1387 y
<< buttonnames
[i
];
1388 new button_action(x
.str(), j
, k
, y
.str());
1393 //If there is a pending load, perform it.
1396 if(pending_load
!= "") {
1397 do_load_state(pending_load
, loadmode
);
1398 redraw_framebuffer();
1400 pending_reset_cycles
= -1;
1401 amode
= ADVANCE_AUTO
;
1404 if(!system_corrupt
) {
1405 location_special
= SPECIAL_SAVEPOINT
;
1406 update_movie_state();
1407 win
->notify_screen_update();
1415 //If there are pending saves, perform them.
1418 if(!queued_saves
.empty()) {
1419 stepping_into_save
= true;
1420 SNES::system
.runtosave();
1421 stepping_into_save
= false;
1422 for(auto i
= queued_saves
.begin(); i
!= queued_saves
.end(); i
++)
1425 queued_saves
.clear();
1428 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
1429 bool handle_reset(long cycles
)
1432 win
->message("SNES reset");
1433 SNES::system
.reset();
1434 framebuffer
= nosignal_screen
;
1435 lua_callback_do_reset(win
);
1436 redraw_framebuffer();
1437 } else if(cycles
> 0) {
1438 video_refresh_done
= false;
1439 long cycles_executed
= 0;
1440 out(win
) << "Executing delayed reset... This can take some time!" << std::endl
;
1441 while(cycles_executed
< cycles
&& !video_refresh_done
) {
1442 SNES::cpu
.op_step();
1445 if(!video_refresh_done
)
1446 out(win
) << "SNES reset (delayed " << cycles_executed
<< ")" << std::endl
;
1448 out(win
) << "SNES reset (forced at " << cycles_executed
<< ")" << std::endl
;
1449 SNES::system
.reset();
1450 framebuffer
= nosignal_screen
;
1451 lua_callback_do_reset(win
);
1452 redraw_framebuffer();
1453 if(video_refresh_done
) {
1454 to_wait_frame(get_ticks_msec());
1461 bool handle_corrupt()
1465 while(system_corrupt
) {
1466 framebuffer
= corrupt_screen
;
1467 redraw_framebuffer();
1472 if(amode
== ADVANCE_QUIT
)
1478 void print_controller_mappings()
1480 for(unsigned i
= 0; i
< 8; i
++) {
1481 std::string type
= "unknown";
1482 if(lookup_controller_type(i
) == DT_NONE
)
1483 type
= "disconnected";
1484 if(lookup_controller_type(i
) == DT_GAMEPAD
)
1486 if(lookup_controller_type(i
) == DT_MOUSE
)
1488 if(lookup_controller_type(i
) == DT_SUPERSCOPE
)
1489 type
= "superscope";
1490 if(lookup_controller_type(i
) == DT_JUSTIFIER
)
1492 out(win
) << "Physical controller mapping: Logical " << (i
+ 1) << " is physical " <<
1493 lookup_physical_controller(i
) << " (" << type
<< ")" << std::endl
;
1498 void main_loop(window
* _win
, struct loaded_rom
& rom
, struct moviefile
& initial
) throw(std::bad_alloc
,
1501 //Basic initialization.
1505 auto old_inteface
= SNES::system
.interface
;
1506 SNES::system
.interface
= &intrf
;
1507 status
= &win
->get_emustatus();
1508 fill_special_frames();
1510 //Load our given movie.
1511 bool first_round
= false;
1512 bool just_did_loadstate
= false;
1514 do_load_state(initial
, LOAD_STATE_DEFAULT
);
1515 first_round
= our_movie
.is_savestate
;
1516 just_did_loadstate
= first_round
;
1517 } catch(std::bad_alloc
& e
) {
1519 } catch(std::exception
& e
) {
1520 win
->message(std::string("FATAL: Can't load initial state: ") + e
.what());
1525 lua_callback_startup(win
);
1527 //print_controller_mappings();
1528 av_snooper::add_dump_notifier(dumpwatch
);
1529 win
->set_main_surface(scr
);
1530 redraw_framebuffer();
1532 amode
= ADVANCE_PAUSE
;
1533 while(amode
!= ADVANCE_QUIT
) {
1534 if(handle_corrupt()) {
1535 first_round
= our_movie
.is_savestate
;
1536 just_did_loadstate
= true;
1539 long resetcycles
= -1;
1540 ack_frame_tick(get_ticks_msec());
1541 if(amode
== ADVANCE_SKIPLAG_PENDING
)
1542 amode
= ADVANCE_SKIPLAG
;
1545 resetcycles
= movb
.new_frame_starting(amode
== ADVANCE_SKIPLAG
);
1546 if(amode
== ADVANCE_QUIT
)
1548 bool delayed_reset
= (resetcycles
> 0);
1549 pending_reset_cycles
= -1;
1550 if(!handle_reset(resetcycles
)) {
1553 if(!delayed_reset
) {
1557 first_round
= our_movie
.is_savestate
;
1558 amode
= ADVANCE_PAUSE
;
1559 just_did_loadstate
= first_round
;
1563 if(just_did_loadstate
) {
1564 if(amode
== ADVANCE_QUIT
)
1566 amode
= ADVANCE_PAUSE
;
1567 redraw_framebuffer();
1571 just_did_loadstate
= false;
1574 if(amode
== ADVANCE_AUTO
)
1575 win
->wait_msec(to_wait_frame(get_ticks_msec()));
1576 first_round
= false;
1578 av_snooper::end(win
);
1579 SNES::system
.interface
= old_inteface
;