Compare commits

...
Sign in to create a new pull request.

3 commits
master ... D2

Author SHA1 Message Date
Ilari Liusvaara
59e563723d Simplify unsafe rewind code 2015-02-04 21:27:42 +02:00
Ilari Liusvaara
14ad539f9b Refactor movie dynamic state to dedicated subobject 2015-02-04 21:27:42 +02:00
Ilari Liusvaara
238b9f0909 Support decoupling video/input framerate 2015-02-04 21:27:42 +02:00
31 changed files with 655 additions and 256 deletions

View file

@ -52,6 +52,8 @@ struct _lsnes_status
bool rtc_valid; //RTC time valid? bool rtc_valid; //RTC time valid?
std::u32string rtc; //RTC time. std::u32string rtc; //RTC time.
std::vector<std::u32string> inputs; //Input display. std::vector<std::u32string> inputs; //Input display.
bool vi_valid; //VI count valid?
uint64_t vi_counter; //VI counter.
std::map<std::string, std::u32string> mvars; //Memory watches. std::map<std::string, std::u32string> mvars; //Memory watches.
std::map<std::string, std::u32string> lvars; //Lua variables. std::map<std::string, std::u32string> lvars; //Lua variables.
}; };

View file

@ -129,6 +129,7 @@ struct emulator_instance
status_updater* supdater; status_updater* supdater;
threads::id emu_thread; threads::id emu_thread;
time_t random_seed_value; time_t random_seed_value;
bool vi_prev_frame;
dtor_list D; dtor_list D;
private: private:
emulator_instance(const emulator_instance&); emulator_instance(const emulator_instance&);

View file

@ -45,13 +45,14 @@ std::string translate_name_mprefix(std::string original, int& binary, int save);
extern std::string last_save; extern std::string last_save;
/** /**
* Restore the actual core state from quicksave. Only call in rewind callback. * Quicksave state.
*
* Parameter state: The state to restore.
* Parameter secs: The seconds counter.
* Parameter ssecs: The subsecond counter.
*/ */
void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs); void do_quicksave(moviefile::dynamic_state& dstate, struct movie& mov);
/**
* Quickload state.
*/
void do_quickload(moviefile::dynamic_state& dstate, struct movie& mov);
std::string get_mprefix_for_project(); std::string get_mprefix_for_project();
void set_mprefix_for_project(const std::string& pfx); void set_mprefix_for_project(const std::string& pfx);

View file

@ -25,7 +25,8 @@ enum lsnes_movie_tags
TAG_RAMCONTENT = 0xd3ec3770, TAG_RAMCONTENT = 0xd3ec3770,
TAG_ROMHINT = 0x6f715830, TAG_ROMHINT = 0x6f715830,
TAG_BRANCH = 0xf2e60707, TAG_BRANCH = 0xf2e60707,
TAG_BRANCH_NAME = 0x6dcb2155 TAG_BRANCH_NAME = 0x6dcb2155,
TAG_VICOUNTER = 0x55758e30,
}; };
#endif #endif

View file

@ -52,6 +52,84 @@ struct moviefile
private: private:
branch_extractor* real; branch_extractor* real;
}; };
/**
* Dynamic information (savestate).
*/
struct dynamic_state
{
/**
* Initialize dynamic state to load defaults.
*/
dynamic_state();
/**
* Clear the state.
*/
void clear_state(int64_t sec, int64_t ssec);
/**
* Swap state with another.
*/
void swap_with(dynamic_state& d);
/**
* Contents of SRAM.
*/
std::map<std::string, std::vector<char>> sram;
/**
* Core savestate (if is_savestate is true).
*/
std::vector<char> savestate; //Savestate to load (if is_savestate is true).
/**
* Host memory (if is_savestate is true).
*/
std::vector<char> host_memory;
/**
* Screenshot (if is_savestate is true).
*/
std::vector<char> screenshot;
/**
* Current frame (if is_savestate is true).
*/
uint64_t save_frame;
/**
* Number of lagged frames (if is_savestate is true).
*/
uint64_t lagged_frames;
/**
* Poll counters (if is_savestate is true).
*/
std::vector<uint32_t> pollcounters;
/**
* Poll flag.
*/
unsigned poll_flag;
/**
* Current RTC second.
*/
int64_t rtc_second;
/**
* Current RTC subsecond.
*/
int64_t rtc_subsecond;
/**
* Active macros at savestate.
*/
std::map<std::string, uint64_t> active_macros;
/**
* VI counters valid.
*/
bool vi_valid;
/**
* VI counter.
*/
uint64_t vi_counter;
/**
* VI counter for this frame.
*/
uint32_t vi_this_frame;
/**
* Movie pointer. Only used for quicksave/quickload.
*/
uint64_t movie_ptr;
};
/** /**
* This constructor construct movie structure with default settings. * This constructor construct movie structure with default settings.
* *
@ -157,41 +235,13 @@ struct moviefile
*/ */
bool is_savestate; bool is_savestate;
/** /**
* Contents of SRAM on time of savestate (if is_savestate is true). * The dynamic state.
*/ */
std::map<std::string, std::vector<char>> sram; dynamic_state dynamic;
/**
* Core savestate (if is_savestate is true).
*/
std::vector<char> savestate; //Savestate to load (if is_savestate is true).
/** /**
* Anchoring core savestate (if not empty). * Anchoring core savestate (if not empty).
*/ */
std::vector<char> anchor_savestate; std::vector<char> anchor_savestate;
/**
* Host memory (if is_savestate is true).
*/
std::vector<char> host_memory;
/**
* Screenshot (if is_savestate is true).
*/
std::vector<char> screenshot;
/**
* Current frame (if is_savestate is true).
*/
uint64_t save_frame;
/**
* Number of lagged frames (if is_savestate is true).
*/
uint64_t lagged_frames;
/**
* Poll counters (if is_savestate is true).
*/
std::vector<uint32_t> pollcounters;
/**
* Poll flag.
*/
unsigned poll_flag;
/** /**
* Compressed rrdata. * Compressed rrdata.
*/ */
@ -204,14 +254,6 @@ struct moviefile
* Branches. * Branches.
*/ */
std::map<std::string, portctrl::frame_vector> branches; std::map<std::string, portctrl::frame_vector> branches;
/**
* Current RTC second.
*/
int64_t rtc_second;
/**
* Current RTC subsecond.
*/
int64_t rtc_subsecond;
/** /**
* Movie starting RTC second. * Movie starting RTC second.
*/ */
@ -232,10 +274,6 @@ struct moviefile
* Subtitles. * Subtitles.
*/ */
std::map<moviefile_subtiming, std::string> subtitles; std::map<moviefile_subtiming, std::string> subtitles;
/**
* Active macros at savestate.
*/
std::map<std::string, uint64_t> active_macros;
/** /**
* Get number of frames in movie. * Get number of frames in movie.
* *

View file

@ -109,8 +109,9 @@ extern "C" {
#define LSNES_CORE_CAP1_MEMWATCH 0x00010000U #define LSNES_CORE_CAP1_MEMWATCH 0x00010000U
//Core supports lightguns (By setting lightgun_height/lightgun_width in LSNES_CORE_GET_AV_STATE). //Core supports lightguns (By setting lightgun_height/lightgun_width in LSNES_CORE_GET_AV_STATE).
#define LSNES_CORE_CAP1_LIGHTGUN 0x00020000U #define LSNES_CORE_CAP1_LIGHTGUN 0x00020000U
//Core signals type extensions #1 (VFR flag). Only supported if emu_flags1 >= 3
#define LSNES_CORE_CAP1_TYPEEXT1 0x00040000U
//Reserved capabilities. //Reserved capabilities.
#define LSNES_CORE_CAP1_RESERVED18 0x00040000U
#define LSNES_CORE_CAP1_RESERVED19 0x00080000U #define LSNES_CORE_CAP1_RESERVED19 0x00080000U
#define LSNES_CORE_CAP1_RESERVED20 0x00100000U #define LSNES_CORE_CAP1_RESERVED20 0x00100000U
#define LSNES_CORE_CAP1_RESERVED21 0x00200000U #define LSNES_CORE_CAP1_RESERVED21 0x00200000U
@ -379,6 +380,9 @@ struct lsnes_core_get_type_info
struct lsnes_core_get_type_info_romimage* images; struct lsnes_core_get_type_info_romimage* images;
//Output: List of settings. Terminated by setting with NULL iname. //Output: List of settings. Terminated by setting with NULL iname.
struct lsnes_core_get_type_info_param* settings; struct lsnes_core_get_type_info_param* settings;
//Output: Nonzero if input and output framerates should be decoupled.
//Present only if LSNES_CORE_CAP1_TYPEEXT1 is set.
int is_vfr;
}; };
//Request 3: Request information about region. //Request 3: Request information about region.

View file

@ -619,6 +619,7 @@ public:
void debug_reset() { core->debug_reset(); } void debug_reset() { core->debug_reset(); }
bool isnull() { return core->isnull(); } bool isnull() { return core->isnull(); }
bool safe_to_unload(loadlib::module& mod) { return core->safe_to_unload(mod); } bool safe_to_unload(loadlib::module& mod) { return core->safe_to_unload(mod); }
bool is_vfr() { return t_is_vfr(); }
protected: protected:
/** /**
* Load a ROM slot set. Changes the ROM currently loaded for core. * Load a ROM slot set. Changes the ROM currently loaded for core.
@ -638,6 +639,10 @@ protected:
* Returns: The controller configuration. * Returns: The controller configuration.
*/ */
virtual controller_set t_controllerconfig(std::map<std::string, std::string>& settings) = 0; virtual controller_set t_controllerconfig(std::map<std::string, std::string>& settings) = 0;
/**
* Is VFR (defaults to false).
*/
virtual bool t_is_vfr();
private: private:
core_type(const core_type&); core_type(const core_type&);
core_type& operator=(const core_type&); core_type& operator=(const core_type&);

View file

@ -73,8 +73,7 @@ struct lua_state
void callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw(); void callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw();
void callback_quit() throw(); void callback_quit() throw();
void callback_keyhook(const std::string& key, keyboard::key& p) throw(); void callback_keyhook(const std::string& key, keyboard::key& p) throw();
void callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void callback_do_unsafe_rewind(movie& mov, void* u);
void* u);
bool callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type); bool callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type);
void callback_movie_lost(const char* what); void callback_movie_lost(const char* what);
void callback_do_latch(std::list<std::string>& args); void callback_do_latch(std::list<std::string>& args);

View file

@ -3,22 +3,16 @@
#include "library/lua-base.hpp" #include "library/lua-base.hpp"
#include "library/string.hpp" #include "library/string.hpp"
#include "core/moviefile.hpp"
struct lua_unsaferewind struct lua_unsaferewind
{ {
lua_unsaferewind(lua::state& L); lua_unsaferewind(lua::state& L);
static size_t overcommit() { return 0; } static size_t overcommit() { return 0; }
std::vector<char> state; moviefile::dynamic_state dstate;
uint64_t frame;
uint64_t lag;
uint64_t ptr;
uint64_t secs;
uint64_t ssecs;
std::vector<uint32_t> pollcounters;
std::vector<char> hostmemory;
std::string print() std::string print()
{ {
return (stringfmt() << "to frame " << frame).str(); return (stringfmt() << "to frame " << dstate.save_frame).str();
} }
}; };

12
lua.lyx
View file

@ -6264,6 +6264,18 @@ Syntax: number movie.currentframe()
Return number of current frame. Return number of current frame.
\end_layout \end_layout
\begin_layout Subsection
movie.currentvi: Get current VI number
\end_layout
\begin_layout Itemize
Syntax: number movie.currentvi()
\end_layout
\begin_layout Standard
Return number of current VI.
\end_layout
\begin_layout Subsection \begin_layout Subsection
movie.framecount: Get move frame count movie.framecount: Get move frame count
\end_layout \end_layout

View file

@ -147,7 +147,7 @@ void status_updater::update()
_status.speed = (unsigned)(100 * framerate.get_realized_multiplier() + 0.5); _status.speed = (unsigned)(100 * framerate.get_realized_multiplier() + 0.5);
if(mlogic && !runmode.is_corrupt()) { if(mlogic && !runmode.is_corrupt()) {
time_t timevalue = static_cast<time_t>(mlogic.get_mfile().rtc_second); time_t timevalue = static_cast<time_t>(mlogic.get_mfile().dynamic.rtc_second);
struct tm* time_decompose = gmtime(&timevalue); struct tm* time_decompose = gmtime(&timevalue);
char datebuffer[512]; char datebuffer[512];
strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose); strftime(datebuffer, 511, "%Y%m%d(%a)T%H%M%S", time_decompose);
@ -195,6 +195,9 @@ void status_updater::update()
} }
_status.inputs.push_back(_buffer); _status.inputs.push_back(_buffer);
} }
//VI counts. Only display if type is VFR.
_status.vi_valid = mlogic.get_mfile().gametype->get_type().is_vfr();
_status.vi_counter = mlogic.get_mfile().dynamic.vi_counter;
//Lua variables. //Lua variables.
_status.lvars = lua2.get_watch_vars(); _status.lvars = lua2.get_watch_vars();
//Memory watches. //Memory watches.

View file

@ -122,6 +122,7 @@ emulator_instance::emulator_instance()
settings->add_set(lsnes_setgrp); settings->add_set(lsnes_setgrp);
dispatch->set_error_streams(&messages.getstream()); dispatch->set_error_streams(&messages.getstream());
random_seed_value = 0; random_seed_value = 0;
vi_prev_frame = false;
} }
emulator_instance::~emulator_instance() emulator_instance::~emulator_instance()

View file

@ -97,7 +97,8 @@ portctrl::frame movie_logic::update_controls(bool subframe, bool forced) throw(s
if(core.lua2->requests_subframe_paint) if(core.lua2->requests_subframe_paint)
core.fbuf->redraw_framebuffer(); core.fbuf->redraw_framebuffer();
if(subframe) { //If VI did not occur last frame, do subframe anyway.
if(subframe || !core.vi_prev_frame) {
if(core.runmode->is_advance_subframe()) { if(core.runmode->is_advance_subframe()) {
//Note that platform::wait() may change value of cancel flag. //Note that platform::wait() may change value of cancel flag.
if(!core.runmode->test_cancel()) { if(!core.runmode->test_cancel()) {
@ -163,6 +164,8 @@ portctrl::frame movie_logic::update_controls(bool subframe, bool forced) throw(s
} }
core.runmode->set_point(emulator_runmode::P_START); core.runmode->set_point(emulator_runmode::P_START);
core.supdater->update(); core.supdater->update();
//Clear the previous VI flag, so next poll will be subframe.
core.vi_prev_frame = false;
} }
platform::flush_command_queue(); platform::flush_command_queue();
portctrl::frame tmp = core.controls->get(core.mlogic->get_movie().get_current_frame()); portctrl::frame tmp = core.controls->get(core.mlogic->get_movie().get_current_frame());
@ -249,10 +252,10 @@ public:
if(!*core.mlogic) if(!*core.mlogic)
return; return;
auto& m = core.mlogic->get_mfile(); auto& m = core.mlogic->get_mfile();
m.rtc_subsecond += increment; m.dynamic.rtc_subsecond += increment;
while(m.rtc_subsecond >= per_second) { while(m.dynamic.rtc_subsecond >= per_second) {
m.rtc_second++; m.dynamic.rtc_second++;
m.rtc_subsecond -= per_second; m.dynamic.rtc_subsecond -= per_second;
} }
} }
@ -269,7 +272,7 @@ public:
time_t get_time() time_t get_time()
{ {
auto& core = CORE(); auto& core = CORE();
return *core.mlogic ? core.mlogic->get_mfile().rtc_second : 0; return *core.mlogic ? core.mlogic->get_mfile().dynamic.rtc_second : 0;
} }
time_t get_randomseed() time_t get_randomseed()
@ -280,7 +283,14 @@ public:
void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d) void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
{ {
auto& core = CORE(); auto& core = CORE();
core.lua2->callback_do_frame_emulated(); //VI occured.
auto& mfile = core.mlogic->get_mfile();
mfile.dynamic.vi_this_frame++;
mfile.dynamic.vi_counter++;
//This is done elsewhere for VFR.
if(!mfile.gametype->get_type().is_vfr())
core.lua2->callback_do_frame_emulated();
core.runmode->set_point(emulator_runmode::P_VIDEO); core.runmode->set_point(emulator_runmode::P_VIDEO);
core.fbuf->redraw_framebuffer(screen, false, true); core.fbuf->redraw_framebuffer(screen, false, true);
auto rate = core.rom->get_audio_rate(); auto rate = core.rom->get_audio_rate();
@ -686,7 +696,7 @@ namespace
//If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load //If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
//failing. //failing.
int handle_load() int _handle_load()
{ {
auto& core = CORE(); auto& core = CORE();
std::string old_project = *core.mlogic ? core.mlogic->get_mfile().projectid : ""; std::string old_project = *core.mlogic ? core.mlogic->get_mfile().projectid : "";
@ -695,8 +705,7 @@ jumpback:
if(!*core.mlogic) if(!*core.mlogic)
return 0; return 0;
uint64_t t = framerate_regulator::get_utime(); uint64_t t = framerate_regulator::get_utime();
std::vector<char> s; core.lua2->callback_do_unsafe_rewind(core.mlogic->get_movie(), unsafe_rewind_obj);
core.lua2->callback_do_unsafe_rewind(s, 0, 0, core.mlogic->get_movie(), unsafe_rewind_obj);
core.dispatch->mode_change(false); core.dispatch->mode_change(false);
do_unsafe_rewind = false; do_unsafe_rewind = false;
core.mlogic->get_mfile().is_savestate = true; core.mlogic->get_mfile().is_savestate = true;
@ -772,6 +781,17 @@ nothing_to_do:
return 0; return 0;
} }
int handle_load()
{
auto& core = CORE();
int r = _handle_load();
if(r > 0) {
//Set the vi prev frame flag, as not to mess up frame advance at first oppurtunity.
core.vi_prev_frame = true;
}
return r;
}
//If there are pending saves, perform them. //If there are pending saves, perform them.
void handle_saves() void handle_saves()
{ {
@ -787,11 +807,7 @@ nothing_to_do:
} }
if(do_unsafe_rewind && !unsafe_rewind_obj) { if(do_unsafe_rewind && !unsafe_rewind_obj) {
uint64_t t = framerate_regulator::get_utime(); uint64_t t = framerate_regulator::get_utime();
std::vector<char> s = core.rom->save_core_state(true); core.lua2->callback_do_unsafe_rewind(core.mlogic->get_movie(), NULL);
uint64_t secs = core.mlogic->get_mfile().rtc_second;
uint64_t ssecs = core.mlogic->get_mfile().rtc_subsecond;
core.lua2->callback_do_unsafe_rewind(s, secs, ssecs, core.mlogic->get_movie(),
NULL);
do_unsafe_rewind = false; do_unsafe_rewind = false;
messages << "Rewind point set in " << (framerate_regulator::get_utime() - t) messages << "Rewind point set in " << (framerate_regulator::get_utime() - t)
<< " usec." << std::endl; << " usec." << std::endl;
@ -865,6 +881,10 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
core.runmode->set_pause_cond(initial.start_paused); core.runmode->set_pause_cond(initial.start_paused);
platform::set_paused(core.runmode->is_paused()); platform::set_paused(core.runmode->is_paused());
stop_at_frame_active = false; stop_at_frame_active = false;
//Always set VI last frame flag when loading any savestate or movie, as not to distrupt frame advance at
//first oppurtunity. And since we loadstated above, VI count this frame is set apporiately.
core.vi_prev_frame = true;
core.lua2->run_startup_scripts(); core.lua2->run_startup_scripts();
@ -874,7 +894,9 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
just_did_loadstate = first_round; just_did_loadstate = first_round;
continue; continue;
} }
core.framerate->ack_frame_tick(framerate_regulator::get_utime()); //Only consider frames with VI real frames.
if(core.vi_prev_frame)
core.framerate->ack_frame_tick(framerate_regulator::get_utime());
core.runmode->decay_skiplag(); core.runmode->decay_skiplag();
if(!first_round) { if(!first_round) {
@ -921,9 +943,20 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
just_did_loadstate = false; just_did_loadstate = false;
} }
core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), false); core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), false);
//VI count this frame should be 0 here.
core.rom->emulate(); core.rom->emulate();
//This is done elsewhere for non-VFR.
if(core.mlogic->get_mfile().gametype->get_type().is_vfr())
core.lua2->callback_do_frame_emulated();
//Reset the vi this frame count to 0, as it is only meaningful inside emulate.
//Also, if VIs occured, set flag informing that so frame advance can stop only on output frames.
auto& VIs = core.mlogic->get_mfile().dynamic.vi_this_frame;
if(VIs > 0)
core.vi_prev_frame = true;
VIs = 0;
random_mix_timing_entropy(); random_mix_timing_entropy();
if(core.runmode->is_freerunning()) //Only wait if VI, to get about proper framerate.
if(core.runmode->is_freerunning() && core.vi_prev_frame)
platform::wait(core.framerate->to_wait_frame(framerate_regulator::get_utime())); platform::wait(core.framerate->to_wait_frame(framerate_regulator::get_utime()));
first_round = false; first_round = false;
core.lua2->callback_do_frame(); core.lua2->callback_do_frame();

View file

@ -230,23 +230,23 @@ void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc
try { try {
uint64_t origtime = framerate_regulator::get_utime(); uint64_t origtime = framerate_regulator::get_utime();
target.is_savestate = true; target.is_savestate = true;
target.sram = core.rom->save_sram(); target.dynamic.sram = core.rom->save_sram();
for(size_t i = 0; i < ROM_SLOT_COUNT; i++) { for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
target.romimg_sha256[i] = core.rom->romimg[i].sha_256.read(); target.romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
target.romxml_sha256[i] = core.rom->romxml[i].sha_256.read(); target.romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
target.namehint[i] = core.rom->romimg[i].namehint; target.namehint[i] = core.rom->romimg[i].namehint;
} }
target.savestate = core.rom->save_core_state(); target.dynamic.savestate = core.rom->save_core_state();
core.fbuf->get_framebuffer().save(target.screenshot); core.fbuf->get_framebuffer().save(target.dynamic.screenshot);
core.mlogic->get_movie().save_state(target.projectid, target.save_frame, core.mlogic->get_movie().save_state(target.projectid, target.dynamic.save_frame,
target.lagged_frames, target.pollcounters); target.dynamic.lagged_frames, target.dynamic.pollcounters);
target.poll_flag = core.rom->get_pflag(); target.dynamic.poll_flag = core.rom->get_pflag();
auto prj = core.project->get(); auto prj = core.project->get();
if(prj) { if(prj) {
target.gamename = prj->gamename; target.gamename = prj->gamename;
target.authors = prj->authors; target.authors = prj->authors;
} }
target.active_macros = core.controls->get_macro_frames(); target.dynamic.active_macros = core.controls->get_macro_frames();
target.save(filename2, SET_savecompression(*core.settings), binary > 0, target.save(filename2, SET_savecompression(*core.settings), binary > 0,
core.mlogic->get_rrdata()); core.mlogic->get_rrdata());
uint64_t took = framerate_regulator::get_utime() - origtime; uint64_t took = framerate_regulator::get_utime() - origtime;
@ -289,7 +289,6 @@ void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc
target.gamename = prj->gamename; target.gamename = prj->gamename;
target.authors = prj->authors; target.authors = prj->authors;
} }
target.active_macros.clear();
target.save(filename2, SET_savecompression(*core.settings), binary > 0, target.save(filename2, SET_savecompression(*core.settings), binary > 0,
core.mlogic->get_rrdata()); core.mlogic->get_rrdata());
uint64_t took = framerate_regulator::get_utime() - origtime; uint64_t took = framerate_regulator::get_utime() - origtime;
@ -422,16 +421,17 @@ namespace
//Load the savestate and movie state. //Load the savestate and movie state.
//Set the core ports in order to avoid port state being reinitialized when loading. //Set the core ports in order to avoid port state being reinitialized when loading.
core.controls->set_ports(portset); core.controls->set_ports(portset);
core.rom->load_core_state(_movie.savestate); core.rom->load_core_state(_movie.dynamic.savestate);
core.rom->set_pflag(_movie.poll_flag); core.rom->set_pflag(_movie.dynamic.poll_flag);
core.controls->set_macro_frames(_movie.active_macros); core.controls->set_macro_frames(_movie.dynamic.active_macros);
} else { } else {
//Reload the ROM in order to rewind to the beginning. //Reload the ROM in order to rewind to the beginning.
core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond); core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
//Reset things to beginning.
_movie.is_savestate = false;
_movie.dynamic.clear_state(_movie.movie_rtc_second, _movie.movie_rtc_subsecond);
//Load the SRAM and volatile RAM. Or anchor savestate if any. //Load the SRAM and volatile RAM. Or anchor savestate if any.
core.controls->set_ports(portset); core.controls->set_ports(portset);
_movie.rtc_second = _movie.movie_rtc_second;
_movie.rtc_subsecond = _movie.movie_rtc_subsecond;
if(!_movie.anchor_savestate.empty()) { if(!_movie.anchor_savestate.empty()) {
core.rom->load_core_state(_movie.anchor_savestate); core.rom->load_core_state(_movie.anchor_savestate);
} else { } else {
@ -443,6 +443,16 @@ namespace
core.controls->set_macro_frames(std::map<std::string, uint64_t>()); core.controls->set_macro_frames(std::map<std::string, uint64_t>());
} }
} }
//Clear the data that should be cleared when movie is at beginning.
void clear_runtime_movie_data(emulator_instance& core)
{
auto& mf = core.mlogic->get_mfile();
mf.is_savestate = false;
mf.dynamic.clear_state(mf.movie_rtc_second, mf.movie_rtc_subsecond);
core.mlogic->get_movie().reset_state();
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
}
} }
void do_load_rom() throw(std::bad_alloc, std::runtime_error) void do_load_rom() throw(std::bad_alloc, std::runtime_error)
@ -474,10 +484,7 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
core.mlogic->get_mfile().romimg_sha256[i] = core.rom->romimg[i].sha_256.read(); core.mlogic->get_mfile().romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
core.mlogic->get_mfile().romxml_sha256[i] = core.rom->romxml[i].sha_256.read(); core.mlogic->get_mfile().romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
} }
core.mlogic->get_mfile().is_savestate = false; clear_runtime_movie_data(core);
core.mlogic->get_mfile().host_memory.clear();
core.mlogic->get_movie().reset_state();
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
core.lua2->callback_do_rewind(); core.lua2->callback_do_rewind();
} catch(std::bad_alloc& e) { } catch(std::bad_alloc& e) {
OOM_panic(); OOM_panic();
@ -490,23 +497,15 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
//The more complicated Read-Write case. //The more complicated Read-Write case.
//We need to create a new movie and movie file. //We need to create a new movie and movie file.
temporary_handle<moviefile> _movie; temporary_handle<moviefile> _movie;
_movie.get()->force_corrupt = false;
_movie.get()->gametype = NULL; //Not yet known.
_movie.get()->coreversion = core.rom->get_core_identifier(); _movie.get()->coreversion = core.rom->get_core_identifier();
_movie.get()->projectid = get_random_hexstring(40); _movie.get()->projectid = get_random_hexstring(40);
_movie.get()->rerecords = "0";
_movie.get()->rerecords_mem = 0;
for(size_t i = 0; i < ROM_SLOT_COUNT; i++) { for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
_movie.get()->namehint[i] = core.rom->romimg[i].namehint; _movie.get()->namehint[i] = core.rom->romimg[i].namehint;
_movie.get()->romimg_sha256[i] = core.rom->romimg[i].sha_256.read(); _movie.get()->romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
_movie.get()->romxml_sha256[i] = core.rom->romxml[i].sha_256.read(); _movie.get()->romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
} }
_movie.get()->is_savestate = false; _movie.get()->movie_rtc_second = _movie.get()->dynamic.rtc_second;
_movie.get()->save_frame = 0; _movie.get()->movie_rtc_subsecond = _movie.get()->dynamic.rtc_subsecond;
_movie.get()->lagged_frames = 0;
_movie.get()->poll_flag = false;
_movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
_movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
_movie.get()->start_paused = false; _movie.get()->start_paused = false;
_movie.get()->lazy_project_create = true; _movie.get()->lazy_project_create = true;
portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom); portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
@ -570,10 +569,7 @@ void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
core.dispatch->mode_change(true); core.dispatch->mode_change(true);
try { try {
handle_load_core(core.mlogic->get_mfile(), portset, false); handle_load_core(core.mlogic->get_mfile(), portset, false);
core.mlogic->get_mfile().is_savestate = false; clear_runtime_movie_data(core);
core.mlogic->get_mfile().host_memory.clear();
core.mlogic->get_movie().reset_state();
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
core.lua2->callback_do_rewind(); core.lua2->callback_do_rewind();
} catch(std::bad_alloc& e) { } catch(std::bad_alloc& e) {
OOM_panic(); OOM_panic();
@ -605,8 +601,8 @@ void do_load_state_preserve(struct moviefile& _movie)
newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input); newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
if(will_load_state) if(will_load_state)
newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true, newmovie.get()->restore_state(_movie.dynamic.save_frame, _movie.dynamic.lagged_frames,
_movie.input, _movie.projectid); _movie.dynamic.pollcounters, true, _movie.input, _movie.projectid);
//Count a rerecord. //Count a rerecord.
if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create) if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
@ -627,19 +623,15 @@ void do_load_state_preserve(struct moviefile& _movie)
//Set new movie. //Set new movie.
core.mlogic->set_movie(*(newmovie()), true); core.mlogic->set_movie(*(newmovie()), true);
//Some fields MUST be taken from movie or one gets desyncs. //Take dynamic state from new file (to avoid desyncs). If loading movie, it has been cleared.
core.mlogic->get_mfile().is_savestate = _movie.is_savestate; core.mlogic->get_mfile().is_savestate = _movie.is_savestate;
core.mlogic->get_mfile().rtc_second = _movie.rtc_second; core.mlogic->get_mfile().dynamic.swap_with(_movie.dynamic);
core.mlogic->get_mfile().rtc_subsecond = _movie.rtc_subsecond;
std::swap(core.mlogic->get_mfile().host_memory, _movie.host_memory);
if(!will_load_state)
core.mlogic->get_mfile().host_memory.clear();
try { try {
//Paint the screen. //Paint the screen.
framebuffer::raw tmp; framebuffer::raw tmp;
if(will_load_state) { if(will_load_state) {
tmp.load(_movie.screenshot); tmp.load(_movie.dynamic.screenshot);
core.fbuf->redraw_framebuffer(tmp); core.fbuf->redraw_framebuffer(tmp);
} else } else
core.fbuf->redraw_framebuffer(core.rom->draw_cover()); core.fbuf->redraw_framebuffer(core.rom->draw_cover());
@ -691,8 +683,8 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input); newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
newmovie.get()->set_pflag_handler(&lsnes_pflag_handler); newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
if(will_load_state) if(will_load_state)
newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true, newmovie.get()->restore_state(_movie.dynamic.save_frame, _movie.dynamic.lagged_frames,
NULL, _movie.projectid); _movie.dynamic.pollcounters, true, NULL, _movie.projectid);
//Copy the other branches. //Copy the other branches.
if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) { if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) {
@ -752,12 +744,6 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
throw; throw;
} }
//If loaded a movie, clear the is savestate and rrdata.
if(!will_load_state) {
_movie.is_savestate = false;
_movie.host_memory.clear();
}
core.lua2->callback_movie_lost("load"); core.lua2->callback_movie_lost("load");
//Copy the data. //Copy the data.
@ -766,6 +752,15 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
core.mlogic->set_mfile(_movie, true); core.mlogic->set_mfile(_movie, true);
used = true; used = true;
//Set up VI counter if none is available.
if(!_movie.dynamic.vi_valid) {
//If no VI counter available, set it to frame-1 and there have been 0 this frame.
uint64_t f = core.mlogic->get_movie().get_current_frame();
_movie.dynamic.vi_counter = (f > 0) ? (f - 1) : 0;
_movie.dynamic.vi_this_frame = 0;
_movie.dynamic.vi_valid = true;
}
set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid)); set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
//Activate RW mode if needed. //Activate RW mode if needed.
@ -783,7 +778,7 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
{ {
framebuffer::raw tmp; framebuffer::raw tmp;
if(will_load_state) { if(will_load_state) {
tmp.load(_movie.screenshot); tmp.load(_movie.dynamic.screenshot);
core.fbuf->redraw_framebuffer(tmp); core.fbuf->redraw_framebuffer(tmp);
} else } else
core.fbuf->redraw_framebuffer(core.rom->draw_cover()); core.fbuf->redraw_framebuffer(core.rom->draw_cover());
@ -879,18 +874,30 @@ bool do_load_state(const std::string& filename, int lmode)
return true; return true;
} }
void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs) void do_quicksave(moviefile::dynamic_state& dstate, struct movie& mov)
{ {
auto& core = CORE(); auto& core = CORE();
//Force unlazy rrdata. auto& mf = core.mlogic->get_mfile();
core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid), dstate = mf.dynamic;
false); dstate.savestate = core.rom->save_core_state(true);
core.mlogic->get_rrdata().add((*core.nrrdata)()); mov.fast_save(dstate.save_frame, dstate.movie_ptr, dstate.lagged_frames, dstate.pollcounters);
core.mlogic->get_mfile().rtc_second = secs;
core.mlogic->get_mfile().rtc_subsecond = ssecs;
core.rom->load_core_state(state, true);
} }
void do_quickload(moviefile::dynamic_state& dstate, struct movie& mov)
{
auto& core = CORE();
auto& mf = core.mlogic->get_mfile();
//Force unlazy rrdata.
core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid), false);
core.mlogic->get_rrdata().add((*core.nrrdata)());
core.rom->load_core_state(dstate.savestate, true);
mov.fast_load(dstate.save_frame, dstate.movie_ptr, dstate.lagged_frames, dstate.pollcounters);
mf.dynamic = dstate;
}
rrdata::rrdata() rrdata::rrdata()
: init(false) : init(false)
{ {

View file

@ -20,6 +20,8 @@
void moviefile::brief_info::binary_io(int _stream) void moviefile::brief_info::binary_io(int _stream)
{ {
bool vi_override = false;
binarystream::input in(_stream); binarystream::input in(_stream);
sysregion = in.string(); sysregion = in.string();
//Discard the settings. //Discard the settings.
@ -32,8 +34,14 @@ void moviefile::brief_info::binary_io(int _stream)
this->corename = s.string_implicit(); this->corename = s.string_implicit();
}},{TAG_PROJECT_ID, [this](binarystream::input& s) { }},{TAG_PROJECT_ID, [this](binarystream::input& s) {
this->projectid = s.string_implicit(); this->projectid = s.string_implicit();
}},{TAG_SAVESTATE, [this](binarystream::input& s) { }},{TAG_SAVESTATE, [this, &vi_override](binarystream::input& s) {
//TAG_VICOUNTER overrides this.
if(!vi_override)
this->current_frame = s.number();
}},{TAG_VICOUNTER, [this, &vi_override](binarystream::input& s) {
//TAG_VICOUNTER overrides TAG_SAVESTATE.
this->current_frame = s.number(); this->current_frame = s.number();
vi_override = true;
}},{TAG_RRDATA, [this](binarystream::input& s) { }},{TAG_RRDATA, [this](binarystream::input& s) {
std::vector<char> c_rrdata; std::vector<char> c_rrdata;
s.blob_implicit(c_rrdata); s.blob_implicit(c_rrdata);
@ -118,28 +126,29 @@ void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, st
}); });
if(is_savestate) { if(is_savestate) {
out.extension(TAG_SAVESTATE, [this](binarystream::output& s) { out.extension(TAG_SAVESTATE, [this](binarystream::output& s) {
s.number(this->save_frame); s.number(this->dynamic.save_frame);
s.number(this->lagged_frames); s.number(this->dynamic.lagged_frames);
s.number(this->rtc_second); s.number(this->dynamic.rtc_second);
s.number(this->rtc_subsecond); s.number(this->dynamic.rtc_subsecond);
s.number(this->pollcounters.size()); s.number(this->dynamic.pollcounters.size());
for(auto i : this->pollcounters) for(auto i : this->dynamic.pollcounters)
s.number32(i); s.number32(i);
s.byte(this->poll_flag ? 0x01 : 0x00); s.byte(this->dynamic.poll_flag ? 0x01 : 0x00);
s.blob_implicit(this->savestate); s.blob_implicit(this->dynamic.savestate);
}, true, out.numberbytes(save_frame) + out.numberbytes(lagged_frames) + out.numberbytes(rtc_second) + }, true, out.numberbytes(dynamic.save_frame) + out.numberbytes(dynamic.lagged_frames) +
out.numberbytes(rtc_subsecond) + out.numberbytes(pollcounters.size()) + out.numberbytes(dynamic.rtc_second) + out.numberbytes(dynamic.rtc_subsecond) +
4 * pollcounters.size() + 1 + savestate.size()); out.numberbytes(dynamic.pollcounters.size()) +
4 * dynamic.pollcounters.size() + 1 + dynamic.savestate.size());
out.extension(TAG_HOSTMEMORY, [this](binarystream::output& s) { out.extension(TAG_HOSTMEMORY, [this](binarystream::output& s) {
s.blob_implicit(this->host_memory); s.blob_implicit(this->dynamic.host_memory);
}); });
out.extension(TAG_SCREENSHOT, [this](binarystream::output& s) { out.extension(TAG_SCREENSHOT, [this](binarystream::output& s) {
s.blob_implicit(this->screenshot); s.blob_implicit(this->dynamic.screenshot);
}, true, screenshot.size()); }, true, dynamic.screenshot.size());
for(auto i : sram) { for(auto i : dynamic.sram) {
out.extension(TAG_SAVE_SRAM, [&i](binarystream::output& s) { out.extension(TAG_SAVE_SRAM, [&i](binarystream::output& s) {
s.string(i.first); s.string(i.first);
s.blob_implicit(i.second); s.blob_implicit(i.second);
@ -164,7 +173,7 @@ void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, st
s.string_implicit(i.second); s.string_implicit(i.second);
}); });
for(auto i : active_macros) for(auto i : dynamic.active_macros)
out.extension(TAG_MACRO, [&i](binarystream::output& s) { out.extension(TAG_MACRO, [&i](binarystream::output& s) {
s.number(i.second); s.number(i.second);
s.string_implicit(i.first); s.string_implicit(i.first);
@ -177,6 +186,11 @@ void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, st
}); });
} }
out.extension(TAG_VICOUNTER, [this](binarystream::output& s) {
s.number(dynamic.vi_counter);
s.number(dynamic.vi_this_frame);
});
int64_t next_bnum = 0; int64_t next_bnum = 0;
std::map<std::string, uint64_t> branch_table; std::map<std::string, uint64_t> branch_table;
for(auto& i : branches) { for(auto& i : branches) {
@ -212,6 +226,7 @@ void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc,
auto ctrldata = gametype->get_type().controllerconfig(settings); auto ctrldata = gametype->get_type().controllerconfig(settings);
portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex()); portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
input = NULL; input = NULL;
dynamic.vi_valid = false;
in.extension({ in.extension({
{TAG_ANCHOR_SAVE, [this](binarystream::input& s) { {TAG_ANCHOR_SAVE, [this](binarystream::input& s) {
@ -225,10 +240,10 @@ void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc,
}},{TAG_GAMENAME, [this](binarystream::input& s) { }},{TAG_GAMENAME, [this](binarystream::input& s) {
this->gamename = s.string_implicit(); this->gamename = s.string_implicit();
}},{TAG_HOSTMEMORY, [this](binarystream::input& s) { }},{TAG_HOSTMEMORY, [this](binarystream::input& s) {
s.blob_implicit(this->host_memory); s.blob_implicit(this->dynamic.host_memory);
}},{TAG_MACRO, [this](binarystream::input& s) { }},{TAG_MACRO, [this](binarystream::input& s) {
uint64_t n = s.number(); uint64_t n = s.number();
this->active_macros[s.string_implicit()] = n; this->dynamic.active_macros[s.string_implicit()] = n;
}},{TAG_BRANCH_NAME, [this, &branch_table, &next_bnum, &next_branch](binarystream::input& s) { }},{TAG_BRANCH_NAME, [this, &branch_table, &next_bnum, &next_branch](binarystream::input& s) {
branch_table[next_bnum++] = next_branch = s.string_implicit(); branch_table[next_bnum++] = next_branch = s.string_implicit();
}},{TAG_MOVIE, [this, &ports, &next_branch](binarystream::input& s) { }},{TAG_MOVIE, [this, &ports, &next_branch](binarystream::input& s) {
@ -269,25 +284,29 @@ void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc,
this->rerecords = (stringfmt() << rrdata_set::count(c_rrdata)).str(); this->rerecords = (stringfmt() << rrdata_set::count(c_rrdata)).str();
}},{TAG_SAVE_SRAM, [this](binarystream::input& s) { }},{TAG_SAVE_SRAM, [this](binarystream::input& s) {
std::string a = s.string(); std::string a = s.string();
s.blob_implicit(this->sram[a]); s.blob_implicit(this->dynamic.sram[a]);
}},{TAG_SAVESTATE, [this](binarystream::input& s) { }},{TAG_SAVESTATE, [this](binarystream::input& s) {
this->is_savestate = true; this->is_savestate = true;
this->save_frame = s.number(); this->dynamic.save_frame = s.number();
this->lagged_frames = s.number(); this->dynamic.lagged_frames = s.number();
this->rtc_second = s.number(); this->dynamic.rtc_second = s.number();
this->rtc_subsecond = s.number(); this->dynamic.rtc_subsecond = s.number();
this->pollcounters.resize(s.number()); this->dynamic.pollcounters.resize(s.number());
for(auto& i : this->pollcounters) for(auto& i : this->dynamic.pollcounters)
i = s.number32(); i = s.number32();
this->poll_flag = (s.byte() != 0); this->dynamic.poll_flag = (s.byte() != 0);
s.blob_implicit(this->savestate); s.blob_implicit(this->dynamic.savestate);
}},{TAG_SCREENSHOT, [this](binarystream::input& s) { }},{TAG_SCREENSHOT, [this](binarystream::input& s) {
s.blob_implicit(this->screenshot); s.blob_implicit(this->dynamic.screenshot);
}},{TAG_SUBTITLE, [this](binarystream::input& s) { }},{TAG_SUBTITLE, [this](binarystream::input& s) {
uint64_t f = s.number(); uint64_t f = s.number();
uint64_t l = s.number(); uint64_t l = s.number();
std::string x = s.string_implicit(); std::string x = s.string_implicit();
this->subtitles[moviefile_subtiming(f, l)] = x; this->subtitles[moviefile_subtiming(f, l)] = x;
}},{TAG_VICOUNTER, [this](binarystream::input& s) {
dynamic.vi_counter = s.number();
dynamic.vi_this_frame = s.number();
dynamic.vi_valid = true;
}} }}
}, binarystream::null_default); }, binarystream::null_default);

View file

@ -200,9 +200,12 @@ void moviefile::brief_info::load(zip::reader& r)
r.read_linefile("gametype", sysregion); r.read_linefile("gametype", sysregion);
r.read_linefile("coreversion", corename); r.read_linefile("coreversion", corename);
r.read_linefile("projectid", projectid); r.read_linefile("projectid", projectid);
if(r.has_member("savestate")) if(r.has_member("savestate")) {
r.read_numeric_file("saveframe", current_frame); if(r.has_member("vicount"))
else r.read_numeric_file("vicount", current_frame);
else
r.read_numeric_file("saveframe", current_frame);
} else
current_frame = 0; current_frame = 0;
r.read_numeric_file("rerecords", rerecords); r.read_numeric_file("rerecords", rerecords);
r.read_linefile("rom.sha256", hash[0], true); r.read_linefile("rom.sha256", hash[0], true);
@ -242,6 +245,10 @@ void moviefile::load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, s
auto ctrldata = gametype->get_type().controllerconfig(settings); auto ctrldata = gametype->get_type().controllerconfig(settings);
portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex()); portctrl::type_set& ports = portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
dynamic.vi_valid = true;
dynamic.vi_counter = 0;
dynamic.vi_this_frame = 0;
branches.clear(); branches.clear();
r.read_linefile("gamename", gamename, true); r.read_linefile("gamename", gamename, true);
r.read_linefile("projectid", projectid); r.read_linefile("projectid", projectid);
@ -266,34 +273,42 @@ void moviefile::load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, s
movie_rtc_subsecond = DEFAULT_RTC_SUBSECOND; movie_rtc_subsecond = DEFAULT_RTC_SUBSECOND;
r.read_numeric_file("starttime.second", movie_rtc_second, true); r.read_numeric_file("starttime.second", movie_rtc_second, true);
r.read_numeric_file("starttime.subsecond", movie_rtc_subsecond, true); r.read_numeric_file("starttime.subsecond", movie_rtc_subsecond, true);
rtc_second = movie_rtc_second; dynamic.rtc_second = movie_rtc_second;
rtc_subsecond = movie_rtc_subsecond; dynamic.rtc_subsecond = movie_rtc_subsecond;
if(r.has_member("savestate.anchor")) if(r.has_member("savestate.anchor"))
r.read_raw_file("savestate.anchor", anchor_savestate); r.read_raw_file("savestate.anchor", anchor_savestate);
if(r.has_member("savestate")) { if(r.has_member("savestate")) {
dynamic.vi_valid = false;
if(r.has_member("vicounter")) {
r.read_numeric_file("vicounter", dynamic.vi_counter);
r.read_numeric_file("vithisframe", dynamic.vi_this_frame);
dynamic.vi_valid = true;
}
is_savestate = true; is_savestate = true;
r.read_numeric_file("saveframe", save_frame, true); r.read_numeric_file("saveframe", dynamic.save_frame, true);
r.read_numeric_file("lagcounter", lagged_frames, true); r.read_numeric_file("lagcounter", dynamic.lagged_frames, true);
read_pollcounters(r, "pollcounters", pollcounters); read_pollcounters(r, "pollcounters", dynamic.pollcounters);
if(r.has_member("hostmemory")) if(r.has_member("hostmemory"))
r.read_raw_file("hostmemory", host_memory); r.read_raw_file("hostmemory", dynamic.host_memory);
r.read_raw_file("savestate", savestate); else
dynamic.host_memory.clear();
r.read_raw_file("savestate", dynamic.savestate);
for(auto name : r) for(auto name : r)
if(name.length() >= 5 && name.substr(0, 5) == "sram.") if(name.length() >= 5 && name.substr(0, 5) == "sram.")
r.read_raw_file(name, sram[name.substr(5)]); r.read_raw_file(name, dynamic.sram[name.substr(5)]);
r.read_raw_file("screenshot", screenshot); r.read_raw_file("screenshot", dynamic.screenshot);
//If these can't be read, just use some (wrong) values. //If these can't be read, just use some (wrong) values.
r.read_numeric_file("savetime.second", rtc_second, true); r.read_numeric_file("savetime.second", dynamic.rtc_second, true);
r.read_numeric_file("savetime.subsecond", rtc_subsecond, true); r.read_numeric_file("savetime.subsecond", dynamic.rtc_subsecond, true);
uint64_t _poll_flag = 2; //Legacy behaviour is the default. uint64_t _poll_flag = 2; //Legacy behaviour is the default.
r.read_numeric_file("pollflag", _poll_flag, true); r.read_numeric_file("pollflag", _poll_flag, true);
poll_flag = _poll_flag; dynamic.poll_flag = _poll_flag;
active_macros = read_active_macros(r, "macros"); dynamic.active_macros = read_active_macros(r, "macros");
} }
for(auto name : r) for(auto name : r)
if(name.length() >= 8 && name.substr(0, 8) == "initram.") if(name.length() >= 8 && name.substr(0, 8) == "initram.")
r.read_raw_file(name, ramcontent[name.substr(8)]); r.read_raw_file(name, ramcontent[name.substr(8)]);
if(rtc_subsecond < 0 || movie_rtc_subsecond < 0) if(dynamic.rtc_subsecond < 0 || movie_rtc_subsecond < 0)
throw std::runtime_error("Invalid RTC subsecond value"); throw std::runtime_error("Invalid RTC subsecond value");
std::string name = r.find_first(); std::string name = r.find_first();
for(auto name : r) for(auto name : r)

View file

@ -173,20 +173,22 @@ void moviefile::save(zip::writer& w, rrdata_set& rrd) throw(std::bad_alloc, std:
w.write_numeric_file("starttime.second", movie_rtc_second); w.write_numeric_file("starttime.second", movie_rtc_second);
w.write_numeric_file("starttime.subsecond", movie_rtc_subsecond); w.write_numeric_file("starttime.subsecond", movie_rtc_subsecond);
if(!anchor_savestate.empty()) if(!anchor_savestate.empty())
w.write_raw_file("savestate.anchor", anchor_savestate); w.write_raw_file("savestate.anchor", anchor_savestate);
if(is_savestate) { if(is_savestate) {
w.write_numeric_file("saveframe", save_frame); w.write_numeric_file("vicounter", dynamic.vi_counter);
w.write_numeric_file("lagcounter", lagged_frames); w.write_numeric_file("vithisframe", dynamic.vi_this_frame);
write_pollcounters(w, "pollcounters", pollcounters); w.write_numeric_file("saveframe", dynamic.save_frame);
w.write_raw_file("hostmemory", host_memory); w.write_numeric_file("lagcounter", dynamic.lagged_frames);
w.write_raw_file("savestate", savestate); write_pollcounters(w, "pollcounters", dynamic.pollcounters);
w.write_raw_file("screenshot", screenshot); w.write_raw_file("hostmemory", dynamic.host_memory);
for(auto i : sram) w.write_raw_file("savestate", dynamic.savestate);
w.write_raw_file("screenshot", dynamic.screenshot);
for(auto i : dynamic.sram)
w.write_raw_file("sram." + i.first, i.second); w.write_raw_file("sram." + i.first, i.second);
w.write_numeric_file("savetime.second", rtc_second); w.write_numeric_file("savetime.second", dynamic.rtc_second);
w.write_numeric_file("savetime.subsecond", rtc_subsecond); w.write_numeric_file("savetime.subsecond", dynamic.rtc_subsecond);
w.write_numeric_file("pollflag", poll_flag); w.write_numeric_file("pollflag", dynamic.poll_flag);
write_active_macros(w, "macros", active_macros); write_active_macros(w, "macros", dynamic.active_macros);
} }
for(auto i : ramcontent) for(auto i : ramcontent)
w.write_raw_file("initram." + i.first, i.second); w.write_raw_file("initram." + i.first, i.second);

View file

@ -75,7 +75,7 @@ moviefile::brief_info::brief_info(const std::string& filename)
sysregion = mv.gametype->get_name(); sysregion = mv.gametype->get_name();
corename = mv.coreversion; corename = mv.coreversion;
projectid = mv.projectid; projectid = mv.projectid;
current_frame = mv.is_savestate ? mv.save_frame : 0; current_frame = mv.is_savestate ? mv.dynamic.save_frame : 0;
rerecords = mv.rerecords_mem; rerecords = mv.rerecords_mem;
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) { for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
hash[i] = mv.romimg_sha256[i]; hash[i] = mv.romimg_sha256[i];
@ -101,6 +101,55 @@ moviefile::brief_info::brief_info(const std::string& filename)
load(r); load(r);
} }
moviefile::dynamic_state::dynamic_state()
{
save_frame = 0;
lagged_frames = 0;
rtc_second = DEFAULT_RTC_SECOND;
rtc_subsecond = DEFAULT_RTC_SUBSECOND;
poll_flag = 0;
vi_valid = true;
vi_counter = 0;
vi_this_frame = 0;
}
void moviefile::dynamic_state::clear_state(int64_t sec, int64_t ssec)
{
sram.clear();
savestate.clear();
host_memory.clear();
screenshot.clear();
save_frame = 0;
lagged_frames = 0;
for(auto& i : pollcounters)
i = 0;
poll_flag = 0;
rtc_second = sec;
rtc_subsecond = ssec;
active_macros.clear();
vi_valid = true;
vi_counter = 0;
vi_this_frame = 0;
}
void moviefile::dynamic_state::swap_with(dynamic_state& d)
{
std::swap(sram, d.sram);
std::swap(savestate, d.savestate);
std::swap(host_memory, d.host_memory);
std::swap(screenshot, d.screenshot);
std::swap(save_frame, d.save_frame);
std::swap(lagged_frames, d.lagged_frames);
std::swap(pollcounters, d.pollcounters);
std::swap(poll_flag, d.poll_flag);
std::swap(rtc_second, d.rtc_second);
std::swap(rtc_subsecond, d.rtc_subsecond);
std::swap(active_macros, d.active_macros);
std::swap(vi_valid, d.vi_valid);
std::swap(vi_counter, d.vi_counter);
std::swap(vi_this_frame, d.vi_this_frame);
}
moviefile::moviefile() throw(std::bad_alloc) moviefile::moviefile() throw(std::bad_alloc)
{ {
force_corrupt = false; force_corrupt = false;
@ -109,12 +158,12 @@ moviefile::moviefile() throw(std::bad_alloc)
coreversion = ""; coreversion = "";
projectid = ""; projectid = "";
rerecords = "0"; rerecords = "0";
rerecords_mem = 0;
is_savestate = false; is_savestate = false;
movie_rtc_second = rtc_second = DEFAULT_RTC_SECOND; movie_rtc_second = DEFAULT_RTC_SECOND;
movie_rtc_subsecond = rtc_subsecond = DEFAULT_RTC_SUBSECOND; movie_rtc_subsecond = DEFAULT_RTC_SUBSECOND;
start_paused = false; start_paused = false;
lazy_project_create = true; lazy_project_create = true;
poll_flag = 0;
} }
moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_settings, uint64_t rtc_sec, moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_settings, uint64_t rtc_sec,
@ -125,12 +174,12 @@ moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_sett
coreversion = rom.get_core_identifier(); coreversion = rom.get_core_identifier();
projectid = get_random_hexstring(40); projectid = get_random_hexstring(40);
rerecords = "0"; rerecords = "0";
rerecords_mem = 0;
is_savestate = false; is_savestate = false;
movie_rtc_second = rtc_second = rtc_sec; movie_rtc_second = dynamic.rtc_second = rtc_sec;
movie_rtc_subsecond = rtc_subsecond = rtc_subsec; movie_rtc_subsecond = dynamic.rtc_subsecond = rtc_subsec;
start_paused = false; start_paused = false;
lazy_project_create = true; lazy_project_create = true;
poll_flag = 0;
settings = c_settings; settings = c_settings;
input = NULL; input = NULL;
auto ctrldata = rom.controllerconfig(settings); auto ctrldata = rom.controllerconfig(settings);
@ -158,7 +207,6 @@ moviefile::moviefile(const std::string& movie, core_type& romtype) throw(std::ba
return; return;
} }
input = NULL; input = NULL;
poll_flag = false;
start_paused = false; start_paused = false;
force_corrupt = false; force_corrupt = false;
is_savestate = false; is_savestate = false;
@ -303,15 +351,8 @@ void moviefile::copy_fields(const moviefile& mv)
movie_sram = mv.movie_sram; movie_sram = mv.movie_sram;
ramcontent = mv.ramcontent; ramcontent = mv.ramcontent;
is_savestate = mv.is_savestate; is_savestate = mv.is_savestate;
sram = mv.sram; dynamic = mv.dynamic;
savestate = mv.savestate;
anchor_savestate = mv.anchor_savestate; anchor_savestate = mv.anchor_savestate;
host_memory = mv.host_memory;
screenshot = mv.screenshot;
save_frame = mv.save_frame;
lagged_frames = mv.lagged_frames;
pollcounters = mv.pollcounters;
poll_flag = mv.poll_flag;
c_rrdata = mv.c_rrdata; c_rrdata = mv.c_rrdata;
branches = mv.branches; branches = mv.branches;
@ -321,14 +362,11 @@ void moviefile::copy_fields(const moviefile& mv)
if(mv.branches.count(i.first) && &mv.branches.find(i.first)->second == mv.input) if(mv.branches.count(i.first) && &mv.branches.find(i.first)->second == mv.input)
input = &i.second; input = &i.second;
rtc_second = mv.rtc_second;
rtc_subsecond = mv.rtc_subsecond;
movie_rtc_second = mv.movie_rtc_second; movie_rtc_second = mv.movie_rtc_second;
movie_rtc_subsecond = mv.movie_rtc_subsecond; movie_rtc_subsecond = mv.movie_rtc_subsecond;
start_paused = mv.start_paused; start_paused = mv.start_paused;
lazy_project_create = mv.lazy_project_create; lazy_project_create = mv.lazy_project_create;
subtitles = mv.subtitles; subtitles = mv.subtitles;
active_macros = mv.active_macros;
} }
void moviefile::fork_branch(const std::string& oldname, const std::string& newname) void moviefile::fork_branch(const std::string& oldname, const std::string& newname)

View file

@ -151,8 +151,7 @@ namespace
m.start_paused = true; m.start_paused = true;
m.movie_rtc_second = p.movie_rtc_second; m.movie_rtc_second = p.movie_rtc_second;
m.movie_rtc_subsecond = p.movie_rtc_subsecond; m.movie_rtc_subsecond = p.movie_rtc_subsecond;
m.save_frame = 0; m.dynamic = moviefile::dynamic_state();
m.lagged_frames = 0;
m.anchor_savestate = p.anchor_savestate; m.anchor_savestate = p.anchor_savestate;
m.movie_sram = p.movie_sram; m.movie_sram = p.movie_sram;
m.authors = p.authors; m.authors = p.authors;

View file

@ -234,7 +234,6 @@ void UI_save_movie(emulator_instance& inst, std::ostringstream& stream)
inst.mlogic->get_mfile().gamename = prj->gamename; inst.mlogic->get_mfile().gamename = prj->gamename;
inst.mlogic->get_mfile().authors = prj->authors; inst.mlogic->get_mfile().authors = prj->authors;
} }
inst.mlogic->get_mfile().active_macros.clear();
inst.mlogic->get_mfile().save(stream, inst.mlogic->get_rrdata()); inst.mlogic->get_mfile().save(stream, inst.mlogic->get_rrdata());
}); });
} }

View file

@ -1,4 +1,4 @@
OBJECTS=test.$(OBJECT_SUFFIX) OBJECTS=test.$(OBJECT_SUFFIX) test2.$(OBJECT_SUFFIX)
.PRECIOUS: %.$(OBJECT_SUFFIX) %.files .PRECIOUS: %.$(OBJECT_SUFFIX) %.files

View file

@ -0,0 +1,212 @@
/***************************************************************************
* Copyright (C) 2012-2013 by Ilari Liusvaara *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "lsnes.hpp"
#include <sstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "core/audioapi.hpp"
#include "core/misc.hpp"
#include "core/command.hpp"
#include "core/controllerframe.hpp"
#include "core/dispatch.hpp"
#include "core/framebuffer.hpp"
#include "core/instance.hpp"
#include "core/messages.hpp"
#include "interface/callbacks.hpp"
#include "interface/cover.hpp"
#include "interface/romtype.hpp"
#include "library/framebuffer-pixfmt-rgb32.hpp"
#include "library/string.hpp"
#include "library/portctrl-data.hpp"
#include "library/serialization.hpp"
#include "library/minmax.hpp"
#include "library/framebuffer.hpp"
#include "library/hex.hpp"
namespace
{
uint32_t cover_fbmem[480 * 432];
bool pflag = false;
//Framebuffer.
struct framebuffer::info cover_fbinfo = {
&framebuffer::pixfmt_rgb32, //Format.
(char*)cover_fbmem, //Memory.
480, 432, 1920, //Physical size.
480, 432, 1920, //Logical size.
0, 0 //Offset.
};
struct interface_device_reg test_registers[] = {
{NULL, NULL, NULL}
};
#include "ports.inc"
controller_set test_controllerconfig(std::map<std::string, std::string>& settings)
{
std::map<std::string, std::string> _settings = settings;
controller_set r;
r.ports.push_back(&psystem);
r.ports.push_back(&ptype1);
r.ports.push_back(&ptype2);
r.logical_map.push_back(std::make_pair(1, 0));
r.logical_map.push_back(std::make_pair(2, 0));
return r;
}
void redraw_cover_fbinfo()
{
for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++)
cover_fbmem[i] = 0x00000000;
cover_render_string(cover_fbmem, 0, 0, "TEST MODE", 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
void redraw_screen()
{
for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++)
cover_fbmem[i] = 0x00000000;
{
std::ostringstream str;
unsigned k = 0;
for(unsigned i = 0; i < 6; i++)
str << ecore_callbacks->get_input(1, 0, i) << " ";
for(unsigned i = 0; i < 15; i++)
if(ecore_callbacks->get_input(1, 0, i + 6)) k |= (1 << i);
str << hex::to16(k);
cover_render_string(cover_fbmem, 0, 0, str.str(), 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
{
std::ostringstream str;
unsigned k = 0;
for(unsigned i = 0; i < 6; i++)
str << ecore_callbacks->get_input(2, 0, i) << " ";
for(unsigned i = 0; i < 15; i++)
if(ecore_callbacks->get_input(2, 0, i + 6)) k |= (1 << i);
str << hex::to16(k);
cover_render_string(cover_fbmem, 0, 16, str.str(), 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
}
struct _test_core2 : public core_core, public core_type, public core_region, public core_sysregion
{
_test_core2()
: core_core({&psystem, &ptype1, &ptype2}, {
{0, "xyzzy", "xyzzy", {
{"Magic", "enum:[\"foo\",\"bar\",\"baz\",[\"qux\",\"zot\"]]"}
}}
}),
core_type({{
.iname = "test2",
.hname = "test2",
.id = 0,
.sysname = "Test2",
.bios = NULL,
.regions = {this},
.images = {{"rom", "Cartridge ROM", 1, 0, 0, "test2"}},
.settings = {},
.core = this,
}}),
core_region({{"world", "World", 0, 0, false, {1, 1000}, {0}}}),
core_sysregion("test2", *this, *this) { hide(); }
std::string c_core_identifier() { return "TEST2"; }
bool c_set_region(core_region& region) { return (&region == this); }
std::pair<uint32_t, uint32_t> c_video_rate() { return std::make_pair(60, 1); }
double c_get_PAR() { return 1.0; }
std::pair<uint32_t, uint32_t> c_audio_rate() { return std::make_pair(48000, 1); }
std::map<std::string, std::vector<char>> c_save_sram() throw(std::bad_alloc) {
std::map<std::string, std::vector<char>> s;
return s;
}
void c_load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc) {}
void c_serialize(std::vector<char>& out) { out.resize(1); out[0] = fmod; }
void c_unserialize(const char* in, size_t insize) { fmod = in[0]; }
core_region& c_get_region() { return *this; }
void c_power() {}
void c_unload_cartridge() {}
std::pair<uint32_t, uint32_t> c_get_scale_factors(uint32_t width, uint32_t height) {
return std::make_pair(max(512 / width, (uint32_t)1), max(448 / height, (uint32_t)1));
}
void c_install_handler() {}
void c_uninstall_handler() {}
void c_emulate() {
int16_t audio[800] = {0};
pflag = false;
redraw_screen();
framebuffer::info inf;
inf.type = &framebuffer::pixfmt_rgb32;
inf.mem = const_cast<char*>(reinterpret_cast<const char*>(cover_fbmem));
inf.physwidth = 480;
inf.physheight = 432;
inf.physstride = 1920;
inf.width = 480;
inf.height = 432;
inf.stride = 1920;
inf.offset_x = 0;
inf.offset_y = 0;
framebuffer::raw ls(inf);
fmod++;
if(fmod == 50) fmod = 0;
if(fmod == 0 || fmod == 16 || fmod == 33)
ecore_callbacks->output_frame(ls, 60,1);
CORE().audio->submit_buffer(audio, 48, false, 48000);
}
void c_runtosave() {}
bool c_get_pflag() { return pflag; }
void c_set_pflag(bool _pflag) { pflag = _pflag; }
framebuffer::raw& c_draw_cover() {
static framebuffer::raw x(cover_fbinfo);
redraw_cover_fbinfo();
return x;
}
std::string c_get_core_shortname() { return "test"; }
void c_pre_emulate_frame(portctrl::frame& cf) {}
void c_execute_action(unsigned id, const std::vector<interface_action_paramval>& p)
{
if(id == 0)
messages << "ID #0, choice: " << p[0].i << std::endl;
}
const interface_device_reg* c_get_registers() { return test_registers; }
int t_load_rom(core_romimage* images, std::map<std::string, std::string>& settings,
uint64_t rtc_sec, uint64_t rtc_subsec)
{
return 0;
}
controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
{
return test_controllerconfig(settings);
}
bool t_is_vfr() { return true; }
std::pair<uint64_t, uint64_t> c_get_bus_map() { return std::make_pair(0, 0); }
std::list<core_vma_info> c_vma_list() { return std::list<core_vma_info>(); }
std::set<std::string> c_srams() { return std::set<std::string>(); }
unsigned c_action_flags(unsigned id) { return 1; }
int c_reset_action(bool hard) { return -1; }
void c_set_debug_flags(uint64_t addr, unsigned int sflags, unsigned int cflags) {}
void c_set_cheat(uint64_t addr, uint64_t value, bool set) {}
void c_debug_reset() {}
std::vector<std::string> c_get_trace_cpus()
{
return std::vector<std::string>();
}
unsigned char fmod;
} test2_core;
}

View file

@ -668,6 +668,10 @@ failed:
{ {
internal_pflag = true; internal_pflag = true;
} }
bool supports_vfr_sel()
{
return (caps1 & LSNES_CORE_CAP1_TYPEEXT1);
}
private: private:
std::string fullname; std::string fullname;
std::string shortname; std::string shortname;
@ -691,8 +695,9 @@ failed:
struct c_core_type : public core_type struct c_core_type : public core_type
{ {
c_core_type(c_lib_init& lib, core_type_params& p, std::map<unsigned, portctrl::type*> _ports, c_core_type(c_lib_init& lib, core_type_params& p, std::map<unsigned, portctrl::type*> _ports,
unsigned _rcount, unsigned _id) unsigned _rcount, unsigned _id, bool _vfr)
: core_type(p), ports(_ports), entrypoint(lib.get_entrypoint()), rcount(_rcount), id(_id) : core_type(p), ports(_ports), entrypoint(lib.get_entrypoint()), rcount(_rcount), id(_id),
vfr(_vfr)
{ {
} }
~c_core_type() throw() ~c_core_type() throw()
@ -749,6 +754,10 @@ failed:
} }
return cset; return cset;
} }
bool t_is_vfr()
{
return vfr;
}
private: private:
void copy_settings(std::vector<char>& tmpmem, std::vector<lsnes_core_system_setting>& tmpmem2, void copy_settings(std::vector<char>& tmpmem, std::vector<lsnes_core_system_setting>& tmpmem2,
std::map<std::string, std::string>& settings) std::map<std::string, std::string>& settings)
@ -779,6 +788,7 @@ failed:
entrypoint_fn entrypoint; entrypoint_fn entrypoint;
unsigned rcount; unsigned rcount;
unsigned id; unsigned id;
bool vfr;
}; };
std::vector<char> msgbuf; std::vector<char> msgbuf;
@ -1141,12 +1151,13 @@ no_parameters:
if(!cores.count(_core)) if(!cores.count(_core))
throw std::runtime_error("create_type: Unknown core"); throw std::runtime_error("create_type: Unknown core");
p.core = cores[_core]; p.core = cores[_core];
bool is_vfr = dynamic_cast<c_core_core*>(p.core)->supports_vfr_sel() && r.is_vfr;
for(unsigned* reg = r.regions; *reg != 0xFFFFFFFFU; reg++) { for(unsigned* reg = r.regions; *reg != 0xFFFFFFFFU; reg++) {
if(!regions.count(*reg)) if(!regions.count(*reg))
throw std::runtime_error("create_type: Unknown region"); throw std::runtime_error("create_type: Unknown region");
p.regions.push_back(regions[*reg]); p.regions.push_back(regions[*reg]);
} }
return new c_core_type(lib, p, cores[_core]->get_ports(), rcount, type); return new c_core_type(lib, p, cores[_core]->get_ports(), rcount, type, is_vfr);
} }
std::map<const void*, std::list<core_sysregion*>> bylib_sysregion; std::map<const void*, std::list<core_sysregion*>> bylib_sysregion;
@ -1184,7 +1195,7 @@ no_parameters:
//Enumerate what the thing supports. //Enumerate what the thing supports.
entrypoint_fn entrypoint(fn); entrypoint_fn entrypoint(fn);
lsnes_core_enumerate_cores r; lsnes_core_enumerate_cores r;
r.emu_flags1 = 2; r.emu_flags1 = 3;
r.message = callback_message; r.message = callback_message;
r.get_input = callback_get_input; r.get_input = callback_get_input;
r.notify_action_update = callback_notify_action_update; r.notify_action_update = callback_notify_action_update;

View file

@ -582,6 +582,11 @@ std::vector<std::string> core_core::get_trace_cpus()
return c_get_trace_cpus(); return c_get_trace_cpus();
} }
bool core_type::t_is_vfr()
{
return false;
}
emucore_callbacks::~emucore_callbacks() throw() emucore_callbacks::~emucore_callbacks() throw()
{ {
} }

View file

@ -13,7 +13,7 @@ namespace
P(address); P(address);
auto& h = CORE().mlogic->get_mfile().host_memory; auto& h = CORE().mlogic->get_mfile().dynamic.host_memory;
if(address + sizeof(S) > h.size()) { if(address + sizeof(S) > h.size()) {
L.pushboolean(0); L.pushboolean(0);
return 1; return 1;
@ -30,7 +30,7 @@ namespace
P(address, value); P(address, value);
auto& h = CORE().mlogic->get_mfile().host_memory; auto& h = CORE().mlogic->get_mfile().dynamic.host_memory;
if(address + sizeof(S) > h.size()) if(address + sizeof(S) > h.size())
h.resize(address + sizeof(S)); h.resize(address + sizeof(S));
serialization::write_endian<S>(&h[address], value, 1); serialization::write_endian<S>(&h[address], value, 1);

View file

@ -418,8 +418,7 @@ uint64_t lua_state::timed_hook(int timer) throw()
return 0; return 0;
} }
void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void lua_state::callback_do_unsafe_rewind(movie& mov, void* u)
void* u)
{ {
auto& core = CORE(); auto& core = CORE();
if(u) { if(u) {
@ -428,9 +427,7 @@ void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_
try { try {
run_callback(*on_pre_rewind); run_callback(*on_pre_rewind);
run_callback(*on_movie_lost, "unsaferewind"); run_callback(*on_movie_lost, "unsaferewind");
mainloop_restore_state(u2->state, u2->secs, u2->ssecs); do_quickload(u2->dstate, mov);
mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
try { core.mlogic->get_mfile().host_memory = u2->hostmemory; } catch(...) {}
run_callback(*on_post_rewind); run_callback(*on_post_rewind);
delete reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u); delete reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u);
} catch(...) { } catch(...) {
@ -438,14 +435,9 @@ void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_
} }
} else { } else {
//Save //Save
run_callback(*on_set_rewind, lua::state::fn_tag([&core, save, secs, ssecs, &mov](lua::state& L) -> run_callback(*on_set_rewind, lua::state::fn_tag([&core, &mov](lua::state& L) -> int {
int {
lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(*core.lua); lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(*core.lua);
u2->state = save; do_quicksave(u2->dstate, mov);
u2->secs = secs,
u2->ssecs = ssecs;
u2->hostmemory = core.mlogic->get_mfile().host_memory;
mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
return 1; return 1;
})); }));
} }

View file

@ -760,7 +760,7 @@ namespace
if((size_t)(daddr + rows * size) < daddr) if((size_t)(daddr + rows * size) < daddr)
throw std::runtime_error("Size to copy too large"); throw std::runtime_error("Size to copy too large");
auto& h = core.mlogic->get_mfile().host_memory; auto& h = core.mlogic->get_mfile().dynamic.host_memory;
if(daddr + rows * size > h.size()) { if(daddr + rows * size > h.size()) {
equals = false; equals = false;
h.resize(daddr + rows * size); h.resize(daddr + rows * size);

View file

@ -380,7 +380,7 @@ namespace
throw std::runtime_error("Source out of range"); throw std::runtime_error("Source out of range");
//Calculate new size of target. //Calculate new size of target.
auto& h = core.mlogic->get_mfile().host_memory; auto& h = core.mlogic->get_mfile().dynamic.host_memory;
size_t rsize = size * rows; size_t rsize = size * rows;
if(size && rsize / size != rows) if(size && rsize / size != rows)
throw std::runtime_error("Copy size out of range"); throw std::runtime_error("Copy size out of range");

View file

@ -13,6 +13,13 @@ namespace
return 1; return 1;
} }
int currentvi(lua::state& L, lua::parameters& P)
{
auto& m = CORE().mlogic->get_mfile();
L.pushnumber(m.dynamic.vi_counter);
return 1;
}
int lagcounter(lua::state& L, lua::parameters& P) int lagcounter(lua::state& L, lua::parameters& P)
{ {
auto& m = CORE().mlogic->get_movie(); auto& m = CORE().mlogic->get_movie();
@ -79,8 +86,8 @@ namespace
int read_rtc(lua::state& L, lua::parameters& P) int read_rtc(lua::state& L, lua::parameters& P)
{ {
auto& core = CORE(); auto& core = CORE();
L.pushnumber(core.mlogic->get_mfile().rtc_second); L.pushnumber(core.mlogic->get_mfile().dynamic.rtc_second);
L.pushnumber(core.mlogic->get_mfile().rtc_subsecond); L.pushnumber(core.mlogic->get_mfile().dynamic.rtc_subsecond);
return 2; return 2;
} }
@ -112,30 +119,25 @@ namespace
if(!mfile.is_savestate) if(!mfile.is_savestate)
throw std::runtime_error("movie.to_rewind only allows savestates"); throw std::runtime_error("movie.to_rewind only allows savestates");
lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(L); lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(L);
u2->state = mfile.savestate; u2->dstate = mfile.dynamic;
if(u2->state.size() >= 32) if(u2->dstate.savestate.size() >= 32)
u2->state.resize(u2->state.size() - 32); u2->dstate.savestate.resize(u2->dstate.savestate.size() - 32);
u2->secs = mfile.rtc_second;
u2->ssecs = mfile.rtc_subsecond;
u2->pollcounters = mfile.pollcounters;
u2->lag = mfile.lagged_frames;
u2->frame = mfile.save_frame;
u2->hostmemory = mfile.host_memory;
//Now the remaining field ptr is somewhat nastier. //Now the remaining field ptr is somewhat nastier.
uint64_t f = 0; uint64_t f = 0;
uint64_t s = mfile.input->size(); uint64_t s = mfile.input->size();
u2->ptr = 0; u2->dstate.movie_ptr = 0;
while(++f < u2->frame) { while(++f < u2->dstate.save_frame) {
if(u2->ptr < s) if(u2->dstate.movie_ptr < s)
u2->ptr++; u2->dstate.movie_ptr++;
while(u2->ptr < s && !(*mfile.input)[u2->ptr].sync()) while(u2->dstate.movie_ptr < s && !(*mfile.input)[u2->dstate.movie_ptr].sync())
u2->ptr++; u2->dstate.movie_ptr++;
} }
return 1; return 1;
} }
lua::functions LUA_movie_fns(lua_func_misc, "movie", { lua::functions LUA_movie_fns(lua_func_misc, "movie", {
{"currentframe", currentframe}, {"currentframe", currentframe},
{"currentvi", currentvi},
{"lagcount", lagcounter}, {"lagcount", lagcounter},
{"framecount", framecount}, {"framecount", framecount},
{"rerecords", rerecords}, {"rerecords", rerecords},

View file

@ -1310,6 +1310,8 @@ void wxwin_mainwindow::update_statusbar()
s << "Frame: " << vars.curframe; s << "Frame: " << vars.curframe;
else else
s << "Frame: " << vars.curframe << "/" << vars.length; s << "Frame: " << vars.curframe << "/" << vars.length;
if(vars.vi_valid)
s << " VI: " << vars.vi_counter;
s << " Lag: " << vars.lag; s << " Lag: " << vars.lag;
if(vars.subframe == _lsnes_status::subframe_savepoint) if(vars.subframe == _lsnes_status::subframe_savepoint)
s << " Subframe: S"; s << " Subframe: S";

View file

@ -698,8 +698,10 @@ struct moviefile& wxwin_project::make_movie()
} }
} }
f.is_savestate = false; f.is_savestate = false;
f.movie_rtc_second = f.rtc_second = boost::lexical_cast<int64_t>(tostdstring(rtc_sec->GetValue())); f.movie_rtc_second = boost::lexical_cast<int64_t>(tostdstring(rtc_sec->GetValue()));
f.movie_rtc_subsecond = f.rtc_subsecond = boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue())); f.movie_rtc_subsecond = boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue()));
f.dynamic.rtc_second = f.movie_rtc_second;
f.dynamic.rtc_subsecond = f.movie_rtc_subsecond;
if(f.movie_rtc_subsecond < 0) if(f.movie_rtc_subsecond < 0)
throw std::runtime_error("RTC subsecond must be positive"); throw std::runtime_error("RTC subsecond must be positive");
auto ctrldata = inst.rom->controllerconfig(f.settings); auto ctrldata = inst.rom->controllerconfig(f.settings);