Fix all warnings -Wall spews
[lsnes.git] / mainloop.cpp
blob5e76e09a0b3c384146f01215a5bb414ef0555abe
1 #include "mainloop.hpp"
2 #include <iomanip>
3 #include "framerate.hpp"
4 #include "memorywatch.hpp"
5 #include "lua.hpp"
6 #include "rrdata.hpp"
7 #include "rom.hpp"
8 #include "movie.hpp"
9 #include "moviefile.hpp"
10 #include "render.hpp"
11 #include "keymapper.hpp"
12 #include "window.hpp"
13 #include "settings.hpp"
14 #include "rom.hpp"
15 #include "movie.hpp"
16 #include "window.hpp"
17 #include <cassert>
18 #include <sstream>
19 #include "memorymanip.hpp"
20 #include "keymapper.hpp"
21 #include "render.hpp"
22 #include "videodumper2.hpp"
23 #include <iostream>
24 #include "lsnes.hpp"
25 #include <sys/time.h>
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
35 #define SAVE_STATE 0
36 #define SAVE_MOVIE 1
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);
64 namespace
66 enum advance_mode
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};
80 //Memory watches.
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.
85 bool advanced_once;
86 bool cancel_advance;
87 //Our ROM.
88 struct loaded_rom* our_rom;
89 //Our movie file.
90 struct moviefile our_movie;
91 //Handle to the graphics system.
92 window* win;
93 //The SNES screen.
94 struct screen scr;
95 //Emulator advance mode. Detemines pauses at start of frame / subframe, etc..
96 enum advance_mode amode;
97 //Mode and filename of pending load, one of LOAD_* constants.
98 int loadmode;
99 std::string pending_load;
100 //Queued saves (all savestates).
101 std::set<std::string> queued_saves;
102 bool stepping_into_save;
103 //Current controls.
104 controls_t curcontrols;
105 controls_t autoheld_controls;
106 //Emulator status area.
107 std::map<std::string, std::string>* status;
108 //Pending reset cycles. -1 if no reset pending, otherwise, cycle count for reset.
109 long pending_reset_cycles = -1;
110 //Set by every video refresh.
111 bool video_refresh_done;
112 //Special subframe location. One of SPECIAL_* constants.
113 int location_special;
114 //Types of connected controllers.
115 enum porttype_t porttype1 = PT_GAMEPAD;
116 enum porttype_t porttype2 = PT_NONE;
117 //System corrupt flag.
118 bool system_corrupt;
119 //Current screen, no signal screen and corrupt screen.
120 lcscreen framebuffer;
121 lcscreen nosignal_screen;
122 lcscreen corrupt_screen;
123 //Few settings.
124 numeric_setting advance_timeout_first("advance-timeout", 0, 999999999, 500);
125 numeric_setting savecompression("savecompression", 0, 9, 7);
127 void send_analog_input(int32_t x, int32_t y, unsigned index)
129 if(analog_is_mouse[index]) {
130 x -= 256;
131 y -= (framebuffer.height / 2);
132 } else {
133 x /= 2;
134 y /= 2;
136 if(analog[index] < 0) {
137 out(win) << "No analog controller in slot #" << (index + 1) << std::endl;
138 return;
140 curcontrols(analog[index] >> 2, analog[index] & 3, 0) = x;
141 curcontrols(analog[index] >> 2, analog[index] & 3, 1) = y;
144 void redraw_framebuffer()
146 uint32_t hscl = 1, vscl = 1;
147 if(framebuffer.width < 512)
148 hscl = 2;
149 if(framebuffer.height < 400)
150 vscl = 2;
151 render_queue rq;
152 struct lua_render_context lrc;
153 lrc.left_gap = 0;
154 lrc.right_gap = 0;
155 lrc.bottom_gap = 0;
156 lrc.top_gap = 0;
157 lrc.queue = &rq;
158 lrc.width = framebuffer.width * hscl;
159 lrc.height = framebuffer.height * vscl;
160 lrc.rshift = scr.active_rshift;
161 lrc.gshift = scr.active_gshift;
162 lrc.bshift = scr.active_bshift;
163 lua_callback_do_paint(&lrc, win);
164 scr.reallocate(framebuffer.width * hscl + lrc.left_gap + lrc.right_gap, framebuffer.height * vscl +
165 lrc.top_gap + lrc.bottom_gap, lrc.left_gap, lrc.top_gap);
166 scr.copy_from(framebuffer, hscl, vscl);
167 //We would want divide by 2, but we'll do it ourselves in order to do mouse.
168 win->set_window_compensation(lrc.left_gap, lrc.top_gap, 1, 1);
169 rq.run(scr);
170 win->notify_screen_update();
173 void fill_special_frames()
175 uint16_t buf[512*448];
176 draw_nosignal(buf);
177 nosignal_screen = lcscreen(buf, 512, 448);
178 draw_corrupt(buf);
179 corrupt_screen = lcscreen(buf, 512, 448);
183 class firmware_path_setting : public setting
185 public:
186 firmware_path_setting() : setting("firmwarepath") { _firmwarepath = "./"; default_firmware = true; }
187 void blank() throw(std::bad_alloc, std::runtime_error)
189 _firmwarepath = "./";
190 default_firmware = true;
193 bool is_set() throw()
195 return !default_firmware;
198 void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
200 _firmwarepath = value;
201 default_firmware = false;
204 std::string get() throw(std::bad_alloc)
206 return _firmwarepath;
209 operator std::string() throw(std::bad_alloc)
211 return _firmwarepath;
213 private:
214 std::string _firmwarepath;
215 bool default_firmware;
216 } firmwarepath_setting;
218 class mymovielogic : public movie_logic
220 public:
221 mymovielogic() : movie_logic(dummy_movie) {}
223 controls_t update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
225 if(lua_requests_subframe_paint)
226 redraw_framebuffer();
228 if(subframe) {
229 if(amode == ADVANCE_SUBFRAME) {
230 if(!cancel_advance && !advanced_once) {
231 win->wait_msec(advance_timeout_first);
232 advanced_once = true;
234 if(cancel_advance) {
235 amode = ADVANCE_PAUSE;
236 cancel_advance = false;
238 win->paused(amode == ADVANCE_PAUSE);
239 } else if(amode == ADVANCE_FRAME) {
241 } else {
242 win->paused(amode == ADVANCE_SKIPLAG || amode == ADVANCE_PAUSE);
243 cancel_advance = false;
245 if(amode == ADVANCE_SKIPLAG)
246 amode = ADVANCE_AUTO;
247 location_special = SPECIAL_NONE;
248 update_movie_state();
249 } else {
250 if(amode == ADVANCE_SKIPLAG_PENDING)
251 amode = ADVANCE_SKIPLAG;
252 if(amode == ADVANCE_FRAME || amode == ADVANCE_SUBFRAME) {
253 if(!cancel_advance) {
254 win->wait_msec(advanced_once ? to_wait_frame(get_ticks_msec()) :
255 advance_timeout_first);
256 advanced_once = true;
258 if(cancel_advance) {
259 amode = ADVANCE_PAUSE;
260 cancel_advance = false;
262 win->paused(amode == ADVANCE_PAUSE);
263 } else {
264 win->paused((amode == ADVANCE_PAUSE));
265 cancel_advance = false;
267 location_special = SPECIAL_FRAME_START;
268 update_movie_state();
270 win->notify_screen_update();
271 win->poll_inputs();
272 if(!subframe && pending_reset_cycles >= 0) {
273 curcontrols(CONTROL_SYSTEM_RESET) = 1;
274 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = pending_reset_cycles / 10000;
275 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = pending_reset_cycles % 10000;
276 } else if(!subframe) {
277 curcontrols(CONTROL_SYSTEM_RESET) = 0;
278 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = 0;
279 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = 0;
281 controls_t tmp = curcontrols ^ autoheld_controls;
282 lua_callback_do_input(tmp, subframe, win);
283 return tmp;
285 private:
286 movie dummy_movie;
289 namespace
291 mymovielogic movb;
293 //Lookup physical controller id based on UI controller id and given types (-1 if invalid).
294 int lookup_physical_controller(unsigned ui_id)
296 bool p1multitap = (porttype1 == PT_MULTITAP);
297 unsigned p1devs = port_types[porttype1].devices;
298 unsigned p2devs = port_types[porttype2].devices;
299 if(ui_id >= p1devs + p2devs)
300 return -1;
301 if(!p1multitap)
302 if(ui_id < p1devs)
303 return ui_id;
304 else
305 return 4 + ui_id - p1devs;
306 else
307 if(ui_id == 0)
308 return 0;
309 else if(ui_id < 5)
310 return ui_id + 3;
311 else
312 return ui_id - 4;
315 //Look up controller type given UI controller id (note: Non-present controllers give PT_NONE, not the type
316 //of port, multitap controllers give PT_GAMEPAD, not PT_MULTITAP, and justifiers give PT_JUSTIFIER, not
317 //PT_JUSTIFIERS).
318 enum devicetype_t lookup_controller_type(unsigned ui_id)
320 int x = lookup_physical_controller(ui_id);
321 if(x < 0)
322 return DT_NONE;
323 enum porttype_t rawtype = (x & 4) ? porttype2 : porttype1;
324 if((x & 3) < port_types[rawtype].devices)
325 return port_types[rawtype].dtype;
326 else
327 return DT_NONE;
330 void set_analog_controllers()
332 unsigned index = 0;
333 for(unsigned i = 0; i < 8; i++) {
334 enum devicetype_t t = lookup_controller_type(i);
335 analog_is_mouse[index] = (t == DT_MOUSE);
336 if(t == DT_MOUSE || t == DT_SUPERSCOPE || t == DT_JUSTIFIER) {
337 analog[index++] = lookup_physical_controller(i);
338 } else
339 analog[index] = -1;
341 for(; index < 3; index++)
342 analog[index] = -1;
345 std::map<std::string, std::pair<unsigned, unsigned>> buttonmap;
347 const char* buttonnames[] = {
348 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
349 "pause", "turbo"
352 void init_buttonmap()
354 static int done = 0;
355 if(done)
356 return;
357 for(unsigned i = 0; i < 8; i++)
358 for(unsigned j = 0; j < sizeof(buttonnames) / sizeof(buttonnames[0]); j++) {
359 std::ostringstream x;
360 x << (i + 1) << buttonnames[j];
361 buttonmap[x.str()] = std::make_pair(i, j);
363 done = 1;
366 //Do button action.
367 void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor = false)
369 enum devicetype_t p = lookup_controller_type(ui_id);
370 int x = lookup_physical_controller(ui_id);
371 int bid = -1;
372 switch(p) {
373 case DT_NONE:
374 out(win) << "No such controller #" << (ui_id + 1) << std::endl;
375 return;
376 case DT_GAMEPAD:
377 switch(button) {
378 case BUTTON_UP: bid = SNES_DEVICE_ID_JOYPAD_UP; break;
379 case BUTTON_DOWN: bid = SNES_DEVICE_ID_JOYPAD_DOWN; break;
380 case BUTTON_LEFT: bid = SNES_DEVICE_ID_JOYPAD_LEFT; break;
381 case BUTTON_RIGHT: bid = SNES_DEVICE_ID_JOYPAD_RIGHT; break;
382 case BUTTON_A: bid = SNES_DEVICE_ID_JOYPAD_A; break;
383 case BUTTON_B: bid = SNES_DEVICE_ID_JOYPAD_B; break;
384 case BUTTON_X: bid = SNES_DEVICE_ID_JOYPAD_X; break;
385 case BUTTON_Y: bid = SNES_DEVICE_ID_JOYPAD_Y; break;
386 case BUTTON_L: bid = SNES_DEVICE_ID_JOYPAD_L; break;
387 case BUTTON_R: bid = SNES_DEVICE_ID_JOYPAD_R; break;
388 case BUTTON_SELECT: bid = SNES_DEVICE_ID_JOYPAD_SELECT; break;
389 case BUTTON_START: bid = SNES_DEVICE_ID_JOYPAD_START; break;
390 default:
391 out(win) << "Invalid button for gamepad" << std::endl;
392 return;
394 break;
395 case DT_MOUSE:
396 switch(button) {
397 case BUTTON_L: bid = SNES_DEVICE_ID_MOUSE_LEFT; break;
398 case BUTTON_R: bid = SNES_DEVICE_ID_MOUSE_RIGHT; break;
399 default:
400 out(win) << "Invalid button for mouse" << std::endl;
401 return;
403 break;
404 case DT_JUSTIFIER:
405 switch(button) {
406 case BUTTON_START: bid = SNES_DEVICE_ID_JUSTIFIER_START; break;
407 case BUTTON_TRIGGER: bid = SNES_DEVICE_ID_JUSTIFIER_TRIGGER; break;
408 default:
409 out(win) << "Invalid button for justifier" << std::endl;
410 return;
412 break;
413 case DT_SUPERSCOPE:
414 switch(button) {
415 case BUTTON_TRIGGER: bid = SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER; break;
416 case BUTTON_CURSOR: bid = SNES_DEVICE_ID_SUPER_SCOPE_CURSOR; break;
417 case BUTTON_PAUSE: bid = SNES_DEVICE_ID_SUPER_SCOPE_PAUSE; break;
418 case BUTTON_TURBO: bid = SNES_DEVICE_ID_SUPER_SCOPE_TURBO; break;
419 default:
420 out(win) << "Invalid button for superscope" << std::endl;
421 return;
423 break;
425 if(do_xor)
426 autoheld_controls((x & 4) ? 1 : 0, x & 3, bid) ^= newstate;
427 else
428 curcontrols((x & 4) ? 1 : 0, x & 3, bid) = newstate;
431 //Recognize and react to [+-]controller commands.
432 bool do_button_action(std::string cmd)
434 init_buttonmap();
435 if(cmd.length() < 12)
436 return false;
437 std::string prefix = cmd.substr(0, 11);
438 if(prefix != "+controller" && prefix != "-controller" && prefix != "controllerh")
439 return false;
440 std::string button = cmd.substr(11);
441 if(!buttonmap.count(button))
442 return false;
443 auto i = buttonmap[button];
444 do_button_action(i.first, i.second, (cmd[0] != '-') ? 1 : 0, (cmd[0] == 'c'));
445 return true;
448 //Save state.
449 void do_save_state(const std::string& filename) throw(std::bad_alloc,
450 std::runtime_error)
452 lua_callback_pre_save(filename, true, win);
453 try {
454 uint64_t origtime = get_ticks_msec();
455 our_movie.is_savestate = true;
456 our_movie.sram = save_sram();
457 our_movie.savestate = save_core_state();
458 framebuffer.save(our_movie.screenshot);
459 auto s = movb.get_movie().save_state();
460 our_movie.movie_state.resize(s.size());
461 memcpy(&our_movie.movie_state[0], &s[0], s.size());
462 our_movie.input = movb.get_movie().save();
463 our_movie.save(filename, savecompression);
464 uint64_t took = get_ticks_msec() - origtime;
465 out(win) << "Saved state '" << filename << "' in " << took << "ms." << std::endl;
466 lua_callback_post_save(filename, true, win);
467 } catch(std::bad_alloc& e) {
468 OOM_panic(win);
469 } catch(std::exception& e) {
470 win->message(std::string("Save failed: ") + e.what());
471 lua_callback_err_save(filename, win);
475 //Save movie.
476 void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
478 lua_callback_pre_save(filename, false, win);
479 try {
480 uint64_t origtime = get_ticks_msec();
481 our_movie.is_savestate = false;
482 our_movie.input = movb.get_movie().save();
483 our_movie.save(filename, savecompression);
484 uint64_t took = get_ticks_msec() - origtime;
485 out(win) << "Saved movie '" << filename << "' in " << took << "ms." << std::endl;
486 lua_callback_post_save(filename, false, win);
487 } catch(std::bad_alloc& e) {
488 OOM_panic(win);
489 } catch(std::exception& e) {
490 win->message(std::string("Save failed: ") + e.what());
491 lua_callback_err_save(filename, win);
495 void warn_hash_mismatch(const std::string& mhash, const loaded_slot& slot, const std::string& name)
497 if(mhash != slot.sha256) {
498 out(win) << "WARNING: " << name << " hash mismatch!" << std::endl
499 << "\tMovie: " << mhash << std::endl
500 << "\tOur ROM: " << slot.sha256 << std::endl;
504 void set_dev(bool port, porttype_t t, bool set_core = true)
506 //return;
507 switch(set_core ? t : PT_INVALID) {
508 case PT_NONE:
509 snes_set_controller_port_device(port, SNES_DEVICE_NONE);
510 break;
511 case PT_GAMEPAD:
512 snes_set_controller_port_device(port, SNES_DEVICE_JOYPAD);
513 break;
514 case PT_MULTITAP:
515 snes_set_controller_port_device(port, SNES_DEVICE_MULTITAP);
516 break;
517 case PT_MOUSE:
518 snes_set_controller_port_device(port, SNES_DEVICE_MOUSE);
519 break;
520 case PT_SUPERSCOPE:
521 snes_set_controller_port_device(port, SNES_DEVICE_SUPER_SCOPE);
522 break;
523 case PT_JUSTIFIER:
524 snes_set_controller_port_device(port, SNES_DEVICE_JUSTIFIER);
525 break;
526 case PT_JUSTIFIERS:
527 snes_set_controller_port_device(port, SNES_DEVICE_JUSTIFIERS);
528 break;
529 case PT_INVALID:
532 if(port)
533 porttype2 = t;
534 else
535 porttype1 = t;
536 set_analog_controllers();
539 //Load state from loaded movie file (does not catch errors).
540 void do_load_state(struct moviefile& _movie, int lmode)
542 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
543 if(gtype::toromtype(_movie.gametype) != our_rom->rtype)
544 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
545 if(gtype::toromregion(_movie.gametype) != our_rom->orig_region && our_rom->orig_region != REGION_AUTO)
546 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
548 if(_movie.coreversion != bsnes_core_version) {
549 if(will_load_state) {
550 std::ostringstream x;
551 x << "ERROR: Emulator core version mismatch!" << std::endl
552 << "\tThis version: " << bsnes_core_version << std::endl
553 << "\tFile is from: " << _movie.coreversion << std::endl;
554 throw std::runtime_error(x.str());
555 } else
556 out(win) << "WARNING: Emulator core version mismatch!" << std::endl
557 << "\tThis version: " << bsnes_core_version << std::endl
558 << "\tFile is from: " << _movie.coreversion << std::endl;
560 warn_hash_mismatch(_movie.rom_sha256, our_rom->rom, "ROM #1");
561 warn_hash_mismatch(_movie.romxml_sha256, our_rom->rom_xml, "XML #1");
562 warn_hash_mismatch(_movie.slota_sha256, our_rom->slota, "ROM #2");
563 warn_hash_mismatch(_movie.slotaxml_sha256, our_rom->slota_xml, "XML #2");
564 warn_hash_mismatch(_movie.slotb_sha256, our_rom->slotb, "ROM #3");
565 warn_hash_mismatch(_movie.slotbxml_sha256, our_rom->slotb_xml, "XML #3");
567 SNES::config.random = false;
568 SNES::config.expansion_port = SNES::System::ExpansionPortDevice::None;
570 movie newmovie;
571 if(lmode == LOAD_STATE_PRESERVE)
572 newmovie = movb.get_movie();
573 else
574 newmovie.load(_movie.rerecords, _movie.projectid, _movie.input);
576 if(will_load_state) {
577 std::vector<unsigned char> tmp;
578 tmp.resize(_movie.movie_state.size());
579 memcpy(&tmp[0], &_movie.movie_state[0], tmp.size());
580 newmovie.restore_state(tmp, true);
583 //Negative return.
584 rrdata::read_base(_movie.projectid);
585 rrdata::add_internal();
586 try {
587 our_rom->region = gtype::toromregion(_movie.gametype);
588 our_rom->load();
590 if(_movie.is_savestate && lmode != LOAD_STATE_MOVIE) {
591 //Load the savestate and movie state.
592 set_dev(false, _movie.port1);
593 set_dev(true, _movie.port2);
594 load_core_state(_movie.savestate);
595 framebuffer.load(_movie.screenshot);
596 } else {
597 load_sram(_movie.movie_sram, win);
598 set_dev(false, _movie.port1);
599 set_dev(true, _movie.port2);
600 framebuffer = nosignal_screen;
602 } catch(std::bad_alloc& e) {
603 OOM_panic(win);
604 } catch(std::exception& e) {
605 system_corrupt = true;
606 throw;
609 //Okay, copy the movie data.
610 our_movie = _movie;
611 if(!our_movie.is_savestate || lmode == LOAD_STATE_MOVIE) {
612 our_movie.is_savestate = false;
613 our_movie.host_memory.clear();
615 movb.get_movie() = newmovie;
616 //Activate RW mode if needed.
617 if(lmode == LOAD_STATE_RW)
618 movb.get_movie().readonly_mode(false);
619 if(lmode == LOAD_STATE_DEFAULT && !(movb.get_movie().get_frame_count()))
620 movb.get_movie().readonly_mode(false);
621 out(win) << "ROM Type ";
622 switch(our_rom->rtype) {
623 case ROMTYPE_SNES:
624 out(win) << "SNES";
625 break;
626 case ROMTYPE_BSX:
627 out(win) << "BS-X";
628 break;
629 case ROMTYPE_BSXSLOTTED:
630 out(win) << "BS-X slotted";
631 break;
632 case ROMTYPE_SUFAMITURBO:
633 out(win) << "Sufami Turbo";
634 break;
635 case ROMTYPE_SGB:
636 out(win) << "Super Game Boy";
637 break;
638 default:
639 out(win) << "Unknown";
640 break;
642 out(win) << " region ";
643 switch(our_rom->region) {
644 case REGION_PAL:
645 out(win) << "PAL";
646 break;
647 case REGION_NTSC:
648 out(win) << "NTSC";
649 break;
650 default:
651 out(win) << "Unknown";
652 break;
654 out(win) << std::endl;
655 uint64_t mlength = _movie.get_movie_length();
657 mlength += 999999;
658 std::ostringstream x;
659 if(mlength > 3600000000000) {
660 x << mlength / 3600000000000 << ":";
661 mlength %= 3600000000000;
663 x << std::setfill('0') << std::setw(2) << mlength / 60000000000 << ":";
664 mlength %= 60000000000;
665 x << std::setfill('0') << std::setw(2) << mlength / 1000000000 << ".";
666 mlength %= 1000000000;
667 x << std::setfill('0') << std::setw(3) << mlength / 1000000;
668 out(win) << "Rerecords " << _movie.rerecords << " length " << x.str() << " ("
669 << _movie.get_frame_count() << " frames)" << std::endl;
672 if(_movie.gamename != "")
673 out(win) << "Game Name: " << _movie.gamename << std::endl;
674 for(size_t i = 0; i < _movie.authors.size(); i++)
675 out(win) << "Author: " << _movie.authors[i].first << "(" << _movie.authors[i].second << ")"
676 << std::endl;
679 //Load state
680 void do_load_state(const std::string& filename, int lmode)
682 uint64_t origtime = get_ticks_msec();
683 lua_callback_pre_load(filename, win);
684 struct moviefile mfile;
685 try {
686 mfile = moviefile(filename);
687 } catch(std::bad_alloc& e) {
688 OOM_panic(win);
689 } catch(std::exception& e) {
690 win->message("Can't read movie/savestate '" + filename + "': " + e.what());
691 lua_callback_err_load(filename, win);
692 return;
694 try {
695 do_load_state(mfile, lmode);
696 uint64_t took = get_ticks_msec() - origtime;
697 out(win) << "Loaded '" << filename << "' in " << took << "ms." << std::endl;
698 lua_callback_post_load(filename, our_movie.is_savestate, win);
699 } catch(std::bad_alloc& e) {
700 OOM_panic(win);
701 } catch(std::exception& e) {
702 win->message("Can't load movie/savestate '" + filename + "': " + e.what());
703 lua_callback_err_load(filename, win);
704 return;
708 //Do pending load (automatically unpauses).
709 void mark_pending_load(const std::string& filename, int lmode)
711 loadmode = lmode;
712 pending_load = filename;
713 amode = ADVANCE_AUTO;
714 win->cancel_wait();
715 win->paused(false);
718 //Mark pending save (movies save immediately).
719 void mark_pending_save(const std::string& filename, int smode)
721 if(smode == SAVE_MOVIE) {
722 //Just do this immediately.
723 do_save_movie(filename);
724 return;
726 queued_saves.insert(filename);
727 win->message("Pending save on '" + filename + "'");
731 std::vector<char>& get_host_memory()
733 return our_movie.host_memory;
736 movie& get_movie()
738 return movb.get_movie();
741 void update_movie_state()
743 auto& _status = win->get_emustatus();
745 std::ostringstream x;
746 x << movb.get_movie().get_current_frame() << "(";
747 if(location_special == SPECIAL_FRAME_START)
748 x << "0";
749 else if(location_special == SPECIAL_SAVEPOINT)
750 x << "S";
751 else if(location_special == SPECIAL_FRAME_VIDEO)
752 x << "V";
753 else
754 x << movb.get_movie().next_poll_number();
755 x << ";" << movb.get_movie().get_lag_frames() << ")/" << movb.get_movie().get_frame_count();
756 _status["Frame"] = x.str();
759 std::ostringstream x;
760 if(movb.get_movie().readonly_mode())
761 x << "PLAY ";
762 else
763 x << "REC ";
764 if(dump_in_progress())
765 x << "CAP ";
766 _status["Flags"] = x.str();
768 for(auto i = memory_watches.begin(); i != memory_watches.end(); i++) {
769 try {
770 _status["M[" + i->first + "]"] = evaluate_watch(i->second);
771 } catch(...) {
774 controls_t c;
775 if(movb.get_movie().readonly_mode())
776 c = movb.get_movie().get_controls();
777 else
778 c = curcontrols ^ autoheld_controls;
779 for(unsigned i = 0; i < 8; i++) {
780 unsigned pindex = lookup_physical_controller(i);
781 unsigned port = pindex >> 2;
782 unsigned dev = pindex & 3;
783 auto ctype = lookup_controller_type(i);
784 std::ostringstream x;
785 switch(ctype) {
786 case DT_GAMEPAD:
787 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_LEFT) ? "l" : " ");
788 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_RIGHT) ? "r" : " ");
789 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_UP) ? "u" : " ");
790 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_DOWN) ? "d" : " ");
791 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_A) ? "A" : " ");
792 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_B) ? "B" : " ");
793 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_X) ? "X" : " ");
794 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_Y) ? "Y" : " ");
795 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_L) ? "L" : " ");
796 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_R) ? "R" : " ");
797 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_START) ? "S" : " ");
798 x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_SELECT) ? "s" : " ");
799 break;
800 case DT_MOUSE:
801 x << c(port, dev, SNES_DEVICE_ID_MOUSE_X) << " ";
802 x << c(port, dev, SNES_DEVICE_ID_MOUSE_Y) << " ";
803 x << (c(port, dev, SNES_DEVICE_ID_MOUSE_LEFT) ? "L" : " ");
804 x << (c(port, dev, SNES_DEVICE_ID_MOUSE_RIGHT) ? "R" : " ");
805 break;
806 case DT_SUPERSCOPE:
807 x << c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_X) << " ";
808 x << c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_Y) << " ";
809 x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER) ? "T" : " ");
810 x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR) ? "C" : " ");
811 x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_TURBO) ? "t" : " ");
812 x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE) ? "P" : " ");
813 break;
814 case DT_JUSTIFIER:
815 x << c(port, dev, SNES_DEVICE_ID_JUSTIFIER_X) << " ";
816 x << c(port, dev, SNES_DEVICE_ID_JUSTIFIER_Y) << " ";
817 x << (c(port, dev, SNES_DEVICE_ID_JUSTIFIER_START) ? "T" : " ");
818 x << (c(port, dev, SNES_DEVICE_ID_JUSTIFIER_TRIGGER) ? "S" : " ");
819 break;
820 case DT_NONE:
821 continue;
823 char y[3] = {'P', 0, 0};
824 y[1] = 49 + i;
825 _status[std::string(y)] = x.str();
830 class my_interface : public SNES::Interface
832 string path(SNES::Cartridge::Slot slot, const string &hint)
834 return static_cast<std::string>(firmwarepath_setting).c_str();
837 void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan)
839 if(stepping_into_save)
840 win->message("Got video refresh in runtosave, expect desyncs!");
841 video_refresh_done = true;
842 bool region = (SNES::system.region() == SNES::System::Region::PAL);
843 //std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
844 //std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
845 //std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
846 //std::cerr << "Frame: region flag is " << (region ? " " : "un") << "set." << std::endl;
847 lcscreen ls(data, hires, interlace, overscan, region);
848 framebuffer = ls;
849 location_special = SPECIAL_FRAME_VIDEO;
850 update_movie_state();
851 redraw_framebuffer();
853 struct lua_render_context lrc;
854 render_queue rq;
855 lrc.left_gap = 0;
856 lrc.right_gap = 0;
857 lrc.bottom_gap = 0;
858 lrc.top_gap = 0;
859 lrc.queue = &rq;
860 lrc.width = framebuffer.width;
861 lrc.height = framebuffer.height;
862 video_fill_shifts(lrc.rshift, lrc.gshift, lrc.bshift);
863 lua_callback_do_video(&lrc, win);
864 dump_frame(framebuffer, &rq, lrc.left_gap, lrc.right_gap, lrc.top_gap, lrc.bottom_gap, region, win);
867 void audio_sample(int16_t l_sample, int16_t r_sample)
869 uint16_t _l = l_sample;
870 uint16_t _r = r_sample;
871 win->play_audio_sample(_l + 32768, _r + 32768);
872 dump_audio_sample(_l, _r, win);
875 void audio_sample(uint16_t l_sample, uint16_t r_sample)
877 //Yes, this interface is broken. The samples are signed but are passed as unsigned!
878 win->play_audio_sample(l_sample + 32768, r_sample + 32768);
879 dump_audio_sample(l_sample, r_sample, win);
882 int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
884 int16_t x;
885 x = movb.input_poll(port, index, id);
886 //if(id == SNES_DEVICE_ID_JOYPAD_START)
887 // std::cerr << "bsnes polling for start on (" << port << "," << index << ")=" << x << std::endl;
888 return x;
892 class mycommandhandler : public aliasexpand_commandhandler
894 public:
895 void docommand2(std::string& cmd, window* win) throw(std::bad_alloc, std::runtime_error)
897 if(is_cmd_prefix(cmd, "quit-emulator")) {
898 tokensplitter t(cmd);
899 std::string dummy = t;
900 std::string tail = t.tail();
901 if(tail == "/y" || win->modal_message("Really quit?", true)) {
902 amode = ADVANCE_QUIT;
903 win->paused(false);
904 win->cancel_wait();
906 } else if(is_cmd_prefix(cmd, "pause-emulator")) {
907 if(amode != ADVANCE_AUTO) {
908 amode = ADVANCE_AUTO;
909 win->paused(false);
910 win->cancel_wait();
911 win->message("Unpaused");
912 } else {
913 win->cancel_wait();
914 cancel_advance = false;
915 amode = ADVANCE_PAUSE;
916 win->message("Paused");
918 } else if(is_cmd_prefix(cmd, "+advance-frame")) {
919 amode = ADVANCE_FRAME;
920 cancel_advance = false;
921 advanced_once = false;
922 win->cancel_wait();
923 win->paused(false);
924 } else if(is_cmd_prefix(cmd, "-advance-frame")) {
925 cancel_advance = true;
926 win->cancel_wait();
927 win->paused(false);
928 } else if(is_cmd_prefix(cmd, "+advance-poll")) {
929 amode = ADVANCE_SUBFRAME;
930 cancel_advance = false;
931 advanced_once = false;
932 win->cancel_wait();
933 win->paused(false);
934 } else if(is_cmd_prefix(cmd, "-advance-poll")) {
935 cancel_advance = true;
936 win->cancel_wait();
937 win->paused(false);
938 } else if(is_cmd_prefix(cmd, "advance-skiplag")) {
939 amode = ADVANCE_SKIPLAG;
940 win->cancel_wait();
941 win->paused(false);
942 } else if(is_cmd_prefix(cmd, "reset")) {
943 pending_reset_cycles = 0;
944 } else if(is_cmd_prefix(cmd, "load-state")) {
945 tokensplitter t(cmd);
946 std::string dummy = t;
947 mark_pending_load(t.tail(), LOAD_STATE_RW);
948 } else if(is_cmd_prefix(cmd, "load-readonly")) {
949 tokensplitter t(cmd);
950 std::string dummy = t;
951 mark_pending_load(t.tail(), LOAD_STATE_RO);
952 } else if(is_cmd_prefix(cmd, "load-preserve")) {
953 tokensplitter t(cmd);
954 std::string dummy = t;
955 mark_pending_load(t.tail(), LOAD_STATE_PRESERVE);
956 } else if(is_cmd_prefix(cmd, "load-movie")) {
957 tokensplitter t(cmd);
958 std::string dummy = t;
959 mark_pending_load(t.tail(), LOAD_STATE_MOVIE);
960 } else if(is_cmd_prefix(cmd, "save-state")) {
961 tokensplitter t(cmd);
962 std::string dummy = t;
963 mark_pending_save(t.tail(), SAVE_STATE);
964 } else if(is_cmd_prefix(cmd, "save-movie")) {
965 tokensplitter t(cmd);
966 std::string dummy = t;
967 mark_pending_save(t.tail(), SAVE_MOVIE);
968 } else if(is_cmd_prefix(cmd, "set-rwmode")) {
969 movb.get_movie().readonly_mode(false);
970 lua_callback_do_readwrite(win);
971 update_movie_state();
972 win->notify_screen_update();
973 } else if(is_cmd_prefix(cmd, "set-gamename")) {
974 tokensplitter t(cmd);
975 std::string dummy = t;
976 our_movie.gamename = t.tail();
977 out(win) << "Game name changed to '" << our_movie.gamename << "'" << std::endl;
978 } else if(is_cmd_prefix(cmd, "get-gamename")) {
979 out(win) << "Game name is '" << our_movie.gamename << "'" << std::endl;
980 } else if(is_cmd_prefix(cmd, "print-authors")) {
981 size_t idx = 0;
982 for(auto i = our_movie.authors.begin(); i != our_movie.authors.end(); i++) {
983 out(win) << (idx++) << ": " << i->first << "|" << i->second << std::endl;
985 out(win) << "End of authors list" << std::endl;
986 } else if(is_cmd_prefix(cmd, "repaint")) {
987 redraw_framebuffer();
988 } else if(is_cmd_prefix(cmd, "add-author")) {
989 tokensplitter t(cmd);
990 std::string dummy = t;
991 fieldsplitter f(t.tail());
992 std::string full = f;
993 std::string nick = f;
994 if(full == "" && nick == "") {
995 out(win) << "syntax: add-author <author>" << std::endl;
996 return;
998 our_movie.authors.push_back(std::make_pair(full, nick));
999 out(win) << (our_movie.authors.size() - 1) << ": " << full << "|" << nick << std::endl;
1000 } else if(is_cmd_prefix(cmd, "remove-author")) {
1001 tokensplitter t(cmd);
1002 std::string dummy = t;
1003 uint64_t index;
1004 try {
1005 index = parse_value<uint64_t>(t.tail());
1006 } catch(std::exception& e) {
1007 out(win) << "syntax: remove-author <authornum>" << std::endl;
1008 return;
1010 if(index >= our_movie.authors.size()) {
1011 out(win) << "No such author" << std::endl;
1012 return;
1014 our_movie.authors.erase(our_movie.authors.begin() + index);
1015 } else if(is_cmd_prefix(cmd, "edit-author")) {
1016 tokensplitter t(cmd);
1017 std::string dummy = t;
1018 uint64_t index;
1019 try {
1020 index = parse_value<uint64_t>(t);
1021 } catch(std::exception& e) {
1022 out(win) << "syntax: edit-author <authornum> <author>" << std::endl;
1023 return;
1025 if(index >= our_movie.authors.size()) {
1026 out(win) << "No such author" << std::endl;
1027 return;
1029 fieldsplitter f(t.tail());
1030 std::string full = f;
1031 std::string nick = f;
1032 if(full == "" && nick == "") {
1033 out(win) << "syntax: edit-author <authornum> <author>" << std::endl;
1034 return;
1036 our_movie.authors[index] = std::make_pair(full, nick);;
1037 } else if(is_cmd_prefix(cmd, "add-watch")) {
1038 tokensplitter t(cmd);
1039 std::string dummy = t;
1040 std::string name = t;
1041 if(name == "" || t.tail() == "") {
1042 out(win) << "syntax: add-watch <name> <expr>" << std::endl;
1043 return;
1045 std::cerr << "Add watch: '" << name << "'" << std::endl;
1046 memory_watches[name] = t.tail();
1047 update_movie_state();
1048 } else if(is_cmd_prefix(cmd, "remove-watch")) {
1049 tokensplitter t(cmd);
1050 std::string dummy = t;
1051 std::string name = t;
1052 if(name == "" || t.tail() != "") {
1053 out(win) << "syntax: remove-watch <name>" << std::endl;
1054 return;
1056 std::cerr << "Erase watch: '" << name << "'" << std::endl;
1057 memory_watches.erase(name);
1058 auto& _status = win->get_emustatus();
1059 _status.erase("M[" + name + "]");
1060 update_movie_state();
1061 } else if(is_cmd_prefix(cmd, "test-1")) {
1062 framebuffer = nosignal_screen;
1063 redraw_framebuffer();
1064 } else if(is_cmd_prefix(cmd, "test-2")) {
1065 framebuffer = corrupt_screen;
1066 redraw_framebuffer();
1067 } else if(is_cmd_prefix(cmd, "test-3")) {
1068 while(1);
1069 } else if(is_cmd_prefix(cmd, "take-screenshot")) {
1070 try {
1071 tokensplitter t(cmd);
1072 std::string dummy = t;
1073 framebuffer.save_png(t.tail());
1074 out(win) << "Saved PNG screenshot" << std::endl;
1075 } catch(std::bad_alloc& e) {
1076 OOM_panic(win);
1077 } catch(std::exception& e) {
1078 out(win) << "Can't save PNG: " << e.what() << std::endl;
1080 } else if(is_cmd_prefix(cmd, "mouse_button")) {
1081 tokensplitter t(cmd);
1082 std::string dummy = t;
1083 std::string x = t;
1084 std::string y = t;
1085 std::string b = t;
1086 int _x = atoi(x.c_str());
1087 int _y = atoi(y.c_str());
1088 int _b = atoi(b.c_str());
1089 if(_b & ~prev_mouse_mask & 1)
1090 send_analog_input(_x, _y, 0);
1091 if(_b & ~prev_mouse_mask & 2)
1092 send_analog_input(_x, _y, 1);
1093 if(_b & ~prev_mouse_mask & 4)
1094 send_analog_input(_x, _y, 2);
1095 prev_mouse_mask = _b;
1096 } else if(do_button_action(cmd)) {
1097 update_movie_state();
1098 win->notify_screen_update();
1099 return;
1101 else if(cmd == "")
1103 else
1104 win->message("Unrecognized command: " + cmd);
1108 namespace
1110 //If there is a pending load, perform it.
1111 bool handle_load()
1113 if(pending_load != "") {
1114 do_load_state(pending_load, loadmode);
1115 redraw_framebuffer();
1116 pending_load = "";
1117 pending_reset_cycles = -1;
1118 amode = ADVANCE_AUTO;
1119 win->cancel_wait();
1120 win->paused(false);
1121 if(!system_corrupt) {
1122 location_special = SPECIAL_SAVEPOINT;
1123 update_movie_state();
1124 win->notify_screen_update();
1125 win->poll_inputs();
1127 return true;
1129 return false;
1132 //If there are pending saves, perform them.
1133 void handle_saves()
1135 if(!queued_saves.empty()) {
1136 stepping_into_save = true;
1137 SNES::system.runtosave();
1138 stepping_into_save = false;
1139 for(auto i = queued_saves.begin(); i != queued_saves.end(); i++)
1140 do_save_state(*i);
1142 queued_saves.clear();
1145 //Do (delayed) reset. Return true if proper, false if forced at frame boundary.
1146 bool handle_reset(long cycles)
1148 if(cycles == 0) {
1149 win->message("SNES reset");
1150 SNES::system.reset();
1151 framebuffer = nosignal_screen;
1152 lua_callback_do_reset(win);
1153 redraw_framebuffer();
1154 } else if(cycles > 0) {
1155 video_refresh_done = false;
1156 long cycles_executed = 0;
1157 out(win) << "Executing delayed reset... This can take some time!" << std::endl;
1158 while(cycles_executed < cycles && !video_refresh_done) {
1159 SNES::cpu.op_step();
1160 cycles_executed++;
1162 if(!video_refresh_done)
1163 out(win) << "SNES reset (delayed " << cycles_executed << ")" << std::endl;
1164 else
1165 out(win) << "SNES reset (forced at " << cycles_executed << ")" << std::endl;
1166 SNES::system.reset();
1167 framebuffer = nosignal_screen;
1168 lua_callback_do_reset(win);
1169 redraw_framebuffer();
1170 if(video_refresh_done) {
1171 to_wait_frame(get_ticks_msec());
1172 return false;
1175 return true;
1178 bool handle_corrupt()
1180 if(!system_corrupt)
1181 return false;
1182 while(system_corrupt) {
1183 framebuffer = corrupt_screen;
1184 redraw_framebuffer();
1185 win->cancel_wait();
1186 win->paused(true);
1187 win->poll_inputs();
1188 handle_load();
1189 if(amode == ADVANCE_QUIT)
1190 return true;
1192 return true;
1195 void print_controller_mappings()
1197 for(unsigned i = 0; i < 8; i++) {
1198 std::string type = "unknown";
1199 if(lookup_controller_type(i) == DT_NONE)
1200 type = "disconnected";
1201 if(lookup_controller_type(i) == DT_GAMEPAD)
1202 type = "gamepad";
1203 if(lookup_controller_type(i) == DT_MOUSE)
1204 type = "mouse";
1205 if(lookup_controller_type(i) == DT_SUPERSCOPE)
1206 type = "superscope";
1207 if(lookup_controller_type(i) == DT_JUSTIFIER)
1208 type = "justifier";
1209 out(win) << "Physical controller mapping: Logical " << (i + 1) << " is physical " <<
1210 lookup_physical_controller(i) << " (" << type << ")" << std::endl;
1215 void main_loop(window* _win, struct loaded_rom& rom, struct moviefile& initial) throw(std::bad_alloc,
1216 std::runtime_error)
1218 //Basic initialization.
1219 win = _win;
1220 our_rom = &rom;
1221 my_interface intrf;
1222 auto old_inteface = SNES::system.interface;
1223 SNES::system.interface = &intrf;
1224 mycommandhandler handler;
1225 win->set_commandhandler(handler);
1226 status = &win->get_emustatus();
1227 fill_special_frames();
1229 //Load our given movie.
1230 bool first_round = false;
1231 bool just_did_loadstate = false;
1232 try {
1233 do_load_state(initial, LOAD_STATE_DEFAULT);
1234 first_round = our_movie.is_savestate;
1235 just_did_loadstate = first_round;
1236 } catch(std::bad_alloc& e) {
1237 OOM_panic(win);
1238 } catch(std::exception& e) {
1239 win->message(std::string("FATAL: Can't load initial state: ") + e.what());
1240 win->fatal_error();
1241 return;
1244 lua_set_commandhandler(handler);
1245 lua_callback_startup(win);
1247 //print_controller_mappings();
1249 win->set_main_surface(scr);
1250 redraw_framebuffer();
1251 win->paused(false);
1252 amode = ADVANCE_PAUSE;
1253 while(amode != ADVANCE_QUIT) {
1254 if(handle_corrupt()) {
1255 first_round = our_movie.is_savestate;
1256 just_did_loadstate = true;
1257 continue;
1259 long resetcycles = -1;
1260 ack_frame_tick(get_ticks_msec());
1261 if(amode == ADVANCE_SKIPLAG_PENDING)
1262 amode = ADVANCE_SKIPLAG;
1264 if(!first_round) {
1265 resetcycles = movb.new_frame_starting(amode == ADVANCE_SKIPLAG);
1266 if(amode == ADVANCE_QUIT)
1267 break;
1268 bool delayed_reset = (resetcycles > 0);
1269 pending_reset_cycles = -1;
1270 if(!handle_reset(resetcycles)) {
1271 continue;
1273 if(!delayed_reset) {
1274 handle_saves();
1276 if(handle_load()) {
1277 first_round = our_movie.is_savestate;
1278 amode = ADVANCE_PAUSE;
1279 just_did_loadstate = first_round;
1280 continue;
1283 if(just_did_loadstate) {
1284 if(amode == ADVANCE_QUIT)
1285 break;
1286 amode = ADVANCE_PAUSE;
1287 redraw_framebuffer();
1288 win->cancel_wait();
1289 win->paused(true);
1290 win->poll_inputs();
1291 just_did_loadstate = false;
1293 SNES::system.run();
1294 if(amode == ADVANCE_AUTO)
1295 win->wait_msec(to_wait_frame(get_ticks_msec()));
1296 first_round = false;
1298 end_vid_dump();
1299 SNES::system.interface = old_inteface;