Merge branch 'rr1-maint'

Conflicts:
	include/core/rom.hpp
	src/core/rom.cpp
This commit is contained in:
Ilari Liusvaara 2012-05-20 13:35:55 +03:00
commit 591770ffa9
19 changed files with 333 additions and 13 deletions

View file

@ -13,5 +13,14 @@ void main_loop(struct loaded_rom& rom, struct moviefile& settings, bool load_has
std::vector<std::string> get_jukebox_names();
void set_jukebox_names(const std::vector<std::string>& newj);
void update_movie_state();
extern std::string msu1_base_path;
/**
* Signal that fast rewind operation is needed.
*
* Parameter ptr: If NULL, saves a state and calls lua_callback_do_unsafe_rewind() with quicksave, movie and NULL.
* If non-NULL, calls lua_callback_do_unsafe_rewind() with movie and this parameter, but without quicksave.
*/
void mainloop_signal_need_rewind(void* ptr);
#endif

View file

@ -230,6 +230,15 @@ public:
* movie, reads all released.
*/
controller_frame read_subframe(uint64_t frame, uint64_t subframe) throw();
/**
* Fast save.
*/
void fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& counters);
/**
* Fast load.
*/
void fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& counters);
private:
//TRUE if readonly mode is active.
bool readonly;

View file

@ -33,4 +33,14 @@ std::string translate_name_mprefix(std::string original, bool forio = false);
extern std::string last_save;
extern movie_logic movb;
/**
* Restore the actual core state from quicksave. Only call in rewind callback.
*
* 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);
#endif

View file

@ -176,7 +176,11 @@ struct loaded_rom
*/
std::vector<loaded_slot> markup_slots;
/**
* Patch the ROMs.
* MSU-1 base.
*/
std::string msu1_base;
/**
* Patch the ROM.
*
* parameter cmdline: The command line.
* throws std::bad_alloc: Not enough memory.
@ -222,7 +226,27 @@ std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector
throw(std::bad_alloc, std::runtime_error);
/**
<<<<<<< HEAD
* Given commandline arguments, load a ROM.
=======
* Saves core state into buffer. WARNING: This takes emulated time.
*
* returns: The saved state.
* throws std::bad_alloc: Not enough memory.
*/
std::vector<char> save_core_state(bool nochecksum = false) throw(std::bad_alloc);
/**
* Loads core state from buffer.
*
* parameter buf: The buffer containing the state.
* throws std::runtime_error: Loading state failed.
*/
void load_core_state(const std::vector<char>& buf, bool nochecksum = false) throw(std::runtime_error);
/**
* Read index of ROMs and add ROMs found to content-searchable storage.
>>>>>>> rr1-maint
*
* parameter cmdline: The command line.
* returns: The loaded ROM set.

View file

@ -89,8 +89,8 @@ struct sram_slot_structure* emucore_sram_slot(size_t index);
size_t emucore_vma_slots();
struct vma_structure* emucore_vma_slot(size_t index);
void emucore_refresh_cart();
std::vector<char> emucore_serialize();
void emucore_unserialize(const std::vector<char>& data);
std::vector<char> emucore_serialize(bool nochecksum = false);
void emucore_unserialize(const std::vector<char>& data, bool nochecksum = false);
void emucore_load_rom(systype_info_structure* rtype, region_info_structure* region,
const std::vector<std::vector<char>>& romslots, const std::vector<std::vector<char>>& markslots);
struct region_info_structure* emucore_current_region();

View file

@ -4,6 +4,7 @@
#include "core/controllerframe.hpp"
#include "core/keymapper.hpp"
#include "core/render.hpp"
#include "core/movie.hpp"
struct lua_State;
@ -88,6 +89,7 @@ void lua_callback_post_save(const std::string& name, bool is_state) throw();
void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw();
void lua_callback_quit() throw();
void lua_callback_keyhook(const std::string& key, const struct keygroup::parameters& p) throw();
void lua_callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void* u);
#define LUA_TIMED_HOOK_IDLE 0
#define LUA_TIMED_HOOK_TIMER 1

View file

@ -0,0 +1,15 @@
#ifndef _lua__unsaferewind__hpp__included__
#define _lua__unsaferewind__hpp__included__
struct lua_unsaferewind
{
std::vector<char> state;
uint64_t frame;
uint64_t lag;
uint64_t ptr;
uint64_t secs;
uint64_t ssecs;
std::vector<uint32_t> pollcounters;
};
#endif

View file

@ -2918,6 +2918,51 @@ Read specifed subframe in specified frame and return data as array (100
elements, numbered 0-99 currently).
\end_layout
\begin_layout Subsubsection
movie.unsafe_rewind([UNSAFEREWIND state])
\end_layout
\begin_layout Standard
Start setting point for unsafe rewind or jump to point of unsafe rewind.
\end_layout
\begin_layout Itemize
If called without argument, causes emulator to start process of setting
unsafe rewind point.
When this has finished, callback on_set_rewind occurs, passing the rewind
state to lua script.
\end_layout
\begin_layout Itemize
If called with argument, causes emulator rewind to passed rewind point as
soon as possible.
Readwrite mode is implicitly activated.
\end_layout
\begin_layout Standard
The following warnings apply to unsafe rewinding:
\end_layout
\begin_layout Itemize
There are no safety checks against misuse (that's what
\begin_inset Quotes eld
\end_inset
unsafe
\begin_inset Quotes erd
\end_inset
comes from)!
\end_layout
\begin_layout Itemize
Only call rewind from timeline rewind point was set from.
\end_layout
\begin_layout Itemize
Only call rewind from after the rewind point was set.
\end_layout
\begin_layout Subsection
Table settings
\end_layout
@ -3341,6 +3386,30 @@ Called when requested by set_idle_timeout() and the timeout has expired
(regardless if emulator is waiting).
\end_layout
\begin_layout Subsubsection
Callback: on_set_rewind(UNSAFEREWIND r)
\end_layout
\begin_layout Standard
Called when unsafe rewind object has been constructed.
\end_layout
\begin_layout Subsubsection
Callback: on_pre_rewind()
\end_layout
\begin_layout Standard
Called just before unsafe rewind is about to occur.
\end_layout
\begin_layout Subsubsection
Callback: on_post_rewind()
\end_layout
\begin_layout Standard
Called just after unsafe rewind has occured.
\end_layout
\begin_layout Section
Memory watch expression syntax
\end_layout

View file

@ -1476,6 +1476,28 @@ Count number of subframes in specified frame (frame numbers are
Read specifed subframe in specified frame and return data as
array (100 elements, numbered 0-99 currently).
8.6.7 movie.unsafe_rewind([UNSAFEREWIND state])
Start setting point for unsafe rewind or jump to point of unsafe
rewind.
• If called without argument, causes emulator to start process of
setting unsafe rewind point. When this has finished, callback
on_set_rewind occurs, passing the rewind state to lua script.
• If called with argument, causes emulator rewind to passed
rewind point as soon as possible. Readwrite mode is implicitly
activated.
The following warnings apply to unsafe rewinding:
• There are no safety checks against misuse (that's what “unsafe”
comes from)!
• Only call rewind from timeline rewind point was set from.
• Only call rewind from after the rewind point was set.
8.7 Table settings
Routines for settings manipulation
@ -1712,6 +1734,18 @@ expired and emulator is waiting.
Called when requested by set_idle_timeout() and the timeout has
expired (regardless if emulator is waiting).
8.10.21 Callback: on_set_rewind(UNSAFEREWIND r)
Called when unsafe rewind object has been constructed.
8.10.22 Callback: on_pre_rewind()
Called just before unsafe rewind is about to occur.
8.10.23 Callback: on_post_rewind()
Called just after unsafe rewind has occured.
9 Memory watch expression syntax
Memory watch expressions are in RPN (Reverse Polish Notation). At

View file

@ -80,6 +80,11 @@ namespace
//Delay reset.
unsigned long long delayreset_cycles_run;
unsigned long long delayreset_cycles_target;
//Unsafe rewind.
bool do_unsafe_rewind = false;
void* unsafe_rewind_obj = NULL;
enum advance_mode old_mode;
bool delayreset_fn()
{
@ -97,6 +102,16 @@ namespace
path_setting firmwarepath_setting("firmwarepath");
void mainloop_signal_need_rewind(void* ptr)
{
if(ptr) {
old_mode = amode;
amode = ADVANCE_LOAD;
}
do_unsafe_rewind = true;
unsafe_rewind_obj = ptr;
}
controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
{
if(lua_requests_subframe_paint)
@ -165,7 +180,6 @@ controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_allo
namespace
{
enum advance_mode old_mode;
//Do pending load (automatically unpauses).
void mark_pending_load(const std::string& filename, int lmode)
@ -286,7 +300,7 @@ void update_movie_state()
uint64_t audio_irq_time;
uint64_t controller_irq_time;
uint64_t frame_irq_time;
std::string msu1_base_path;
class my_interface : public SNES::Interface
{
@ -295,6 +309,23 @@ class my_interface : public SNES::Interface
const char* _hint = hint;
std::string _hint2 = _hint;
std::string fwp = firmwarepath_setting;
regex_results r;
std::string msubase = msu1_base_path;
if(regex_match(".*\\.sfc", msu1_base_path))
msubase = msu1_base_path.substr(0, msu1_base_path.length() - 4);
if(_hint2 == "msu1.rom" || _hint2 == ".msu") {
//MSU-1 main ROM.
std::string x = msubase + ".msu";
messages << "MSU main data file: " << x << std::endl;
return x.c_str();
}
if(r = regex("(track)?(-([0-9])+\\.pcm)", _hint2)) {
//MSU track.
std::string x = msubase + r[2];
messages << "MSU track " << r[3] << "': " << x << std::endl;
return x.c_str();
}
std::string finalpath = fwp + "/" + _hint2;
return finalpath.c_str();
}
@ -736,6 +767,18 @@ namespace
//failing.
int handle_load()
{
if(do_unsafe_rewind && unsafe_rewind_obj) {
uint64_t t = get_utime();
std::vector<char> s;
lua_callback_do_unsafe_rewind(s, 0, 0, movb.get_movie(), unsafe_rewind_obj);
information_dispatch::do_mode_change(false);
do_unsafe_rewind = false;
our_movie.is_savestate = true;
location_special = SPECIAL_SAVEPOINT;
update_movie_state();
messages << "Rewind done in " << (get_utime() - t) << " usec." << std::endl;
return 1;
}
if(pending_load != "") {
system_corrupt = false;
if(loadmode != LOAD_STATE_BEGINNING && !do_load_state(pending_load, loadmode)) {
@ -763,12 +806,21 @@ namespace
//If there are pending saves, perform them.
void handle_saves()
{
if(!queued_saves.empty()) {
if(!queued_saves.empty() || (do_unsafe_rewind && !unsafe_rewind_obj)) {
stepping_into_save = true;
SNES::system.runtosave();
stepping_into_save = false;
for(auto i : queued_saves)
do_save_state(i);
if(do_unsafe_rewind && !unsafe_rewind_obj) {
uint64_t t = get_utime();
std::vector<char> s = emucore_serialize(true);
uint64_t secs = our_movie.rtc_second;
uint64_t ssecs = our_movie.rtc_subsecond;
lua_callback_do_unsafe_rewind(s, secs, ssecs, movb.get_movie(), NULL);
do_unsafe_rewind = false;
messages << "Rewind point set in " << (get_utime() - t) << " usec." << std::endl;
}
}
queued_saves.clear();
}

View file

@ -459,6 +459,25 @@ void movie::reset_state() throw()
clear_caches();
}
void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
{
pollcounters.save_state(_counters);
_frame = current_frame;
_ptr = current_frame_first_subframe;
_lagc = lag_frames;
}
void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
{
readonly = true;
current_frame = _frame;
current_frame_first_subframe = (_ptr <= movie_data.size()) ? _ptr : movie_data.size();
lag_frames = _lagc;
pollcounters.load_state(_counters);
readonly_mode(false);
}
movie_logic::movie_logic() throw()
{
}

View file

@ -291,6 +291,7 @@ void do_load_beginning() throw(std::bad_alloc, std::runtime_error)
redraw_framebuffer(screen_corrupt, true);
throw;
}
information_dispatch::do_mode_change(movb.get_movie().readonly_mode());
our_movie.is_savestate = false;
our_movie.host_memory.clear();
messages << "Movie rewound to beginning." << std::endl;
@ -469,3 +470,11 @@ bool do_load_state(const std::string& filename, int lmode)
}
return true;
}
void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
{
rrdata::add_internal();
our_movie.rtc_second = secs;
our_movie.rtc_subsecond = ssecs;
emucore_unserialize(state, true);
}

View file

@ -1,6 +1,7 @@
#include "core/command.hpp"
#include "core/dispatch.hpp"
#include "core/framerate.hpp"
#include "core/mainloop.hpp"
#include "core/memorymanip.hpp"
#include "core/misc.hpp"
#include "core/rom.hpp"
@ -210,6 +211,10 @@ loaded_rom::loaded_rom(const rom_files& files) throw(std::bad_alloc, std::runtim
markup_slots[i] = loaded_slot(files.markup_slots[i], files.base_file, *rtype->rom_slot(i),
true);
orig_region = region = files.region;
if(main_slots.size() > 1)
msu1_base = resolve_file_relative(files.main_slots[1], files.base_file);
else
msu1_base = resolve_file_relative(files.main_slots[0], files.base_file);
}
void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
@ -234,6 +239,7 @@ void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
auto soundrate = emucore_get_audio_rate();
set_nominal_framerate(1.0 * framerate.first / framerate.second);
information_dispatch::do_sound_rate(soundrate.first, soundrate.second);
msu1_base_path = msu1_base;
refresh_cart_mappings();
}

View file

@ -446,12 +446,14 @@ void emucore_refresh_cart()
delete i;
}
std::vector<char> emucore_serialize()
std::vector<char> emucore_serialize(bool nochecksum)
{
std::vector<char> ret;
serializer s = SNES::system.serialize();
ret.resize(s.size());
memcpy(&ret[0], s.data(), s.size());
if(nochecksum)
return ret;
size_t offset = ret.size();
unsigned char tmp[32];
sha256::hash(tmp, ret);
@ -460,8 +462,14 @@ std::vector<char> emucore_serialize()
return ret;
}
void emucore_unserialize(const std::vector<char>& buf)
void emucore_unserialize(const std::vector<char>& buf, bool nochecksum)
{
if(nochecksum) {
serializer s(reinterpret_cast<const uint8_t*>(&buf[0]), buf.size());
if(!SNES::system.unserialize(s))
throw std::runtime_error("SNES core rejected savestate");
return;
}
if(buf.size() < 32)
throw std::runtime_error("Savestate corrupt");
unsigned char tmp[32];

View file

@ -27,6 +27,8 @@ void lua_callback_keyhook(const std::string& key, const struct keygroup::paramet
void init_lua() throw() {}
void quit_lua() throw() {}
uint64_t lua_timed_hook(int timer) throw() { return 0x7EFFFFFFFFFFFFFFULL; }
void lua_callback_do_unsafe_rewind(const std::vector<char>& save, movie& mov, void* u) {}
bool lua_requests_repaint = false;
bool lua_requests_subframe_paint = false;

View file

@ -2,8 +2,10 @@
#include "core/globalwrap.hpp"
#include "lua/internal.hpp"
#include "lua/lua.hpp"
#include "lua/unsaferewind.hpp"
#include "core/mainloop.hpp"
#include "core/memorymanip.hpp"
#include "core/moviedata.hpp"
#include "core/misc.hpp"
#include <map>
@ -555,7 +557,38 @@ uint64_t lua_timed_hook(int timer) throw()
}
}
void lua_callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void* u)
{
if(u) {
lua_unsaferewind* u2 = reinterpret_cast<lua_obj_pin<lua_unsaferewind>*>(u)->object();
//Load.
try {
if(callback_exists("on_pre_rewind"))
run_lua_cb(0);
mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
if(callback_exists("on_post_rewind"))
run_lua_cb(0);
} catch(...) {
return;
}
} else {
//Save
if(callback_exists("on_set_rewind")) {
lua_unsaferewind* u2 = lua_class<lua_unsaferewind>::create(L);
u2->state = save;
u2->secs = secs,
u2->ssecs = ssecs;
mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
run_lua_cb(1);
}
}
}
bool lua_requests_repaint = false;
bool lua_requests_subframe_paint = false;
bool lua_supported = true;
DECLARE_LUACLASS(lua_unsaferewind, "UNSAFEREWIND");

View file

@ -1,6 +1,8 @@
#include "lua/internal.hpp"
#include "lua/unsaferewind.hpp"
#include "core/movie.hpp"
#include "core/moviedata.hpp"
#include "core/mainloop.hpp"
namespace
{
@ -68,4 +70,20 @@ namespace
lua_pushnumber(LS, our_movie.rtc_subsecond);
return 2;
});
function_ptr_luafun musv("movie.unsafe_rewind", [](lua_State* LS, const std::string& fname) -> int {
if(lua_isnoneornil(LS, 1)) {
//Start process to mark save.
mainloop_signal_need_rewind(NULL);
} else if(lua_class<lua_unsaferewind>::is(LS, 1)) {
//Load the save.
lua_obj_pin<lua_unsaferewind>* u = lua_class<lua_unsaferewind>::pin(LS, 1, fname.c_str());
mainloop_signal_need_rewind(u);
} else {
lua_pushstring(LS, "movie.unsafe_rewind: Expected nil or UNSAFEREWIND as 1st argument");
lua_error(LS);
return 0;
}
});
}

View file

@ -473,7 +473,7 @@ void boot_emulator(loaded_rom& rom, moviefile& movie)
}
wxwin_mainwindow::panel::panel(wxWindow* win)
: wxPanel(win)
: wxPanel(win, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS)
{
this->Connect(wxEVT_PAINT, wxPaintEventHandler(panel::on_paint), NULL, this);
this->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(panel::on_erase), NULL, this);

View file

@ -100,11 +100,11 @@ namespace
int horiz_padding = 60;
wxdialog_pressbutton::wxdialog_pressbutton(wxWindow* parent, const std::string& title)
: wxDialog(parent, wxID_ANY, towxstring(title), wxDefaultPosition, wxSize(-1, -1))
: wxDialog(parent, wxID_ANY, towxstring(title))
{
wxStaticText* t;
wxBoxSizer* s2 = new wxBoxSizer(wxVERTICAL);
wxPanel* p = new wxPanel(this, wxID_ANY);
wxPanel* p = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxWANTS_CHARS);
s2->Add(p, 1, wxGROW);
lastkbdkey = -1;
mouseflag = 0;
@ -116,7 +116,8 @@ namespace
s->Add(0, 0);
s->Add(0, 0);
s->Add(0, 0);
s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign")), 1, wxGROW);
s->Add(t = new wxStaticText(p, wxID_ANY, wxT("Press the key to assign"), wxDefaultPosition,
wxSize(-1, -1), wxWANTS_CHARS), 1, wxGROW);
s->Add(0, 0);
s->Add(0, 0);
s->Add(0, 0);