Save states: Stabilize loads (no more LZO crashes, hopefully), make sure we don't crash if user loads state too quickly in succession

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4276 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard
2009-09-15 18:54:10 +00:00
parent adbfea0a09
commit e6b87febe5
2 changed files with 67 additions and 43 deletions

View File

@ -52,6 +52,7 @@ static unsigned char __LZO_MMODEL out [ OUT_LEN ];
static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS); static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS);
static bool state_op_in_progress = false;
static int ev_Save, ev_BufferSave; static int ev_Save, ev_BufferSave;
static int ev_Load, ev_BufferLoad; static int ev_Load, ev_BufferLoad;
@ -75,7 +76,8 @@ void DoState(PointerWrap &p)
{ {
u32 cookie = 0xBAADBABE + version; u32 cookie = 0xBAADBABE + version;
p.Do(cookie); p.Do(cookie);
if (cookie != 0xBAADBABE + version) { if (cookie != 0xBAADBABE + version)
{
PanicAlert("Can't load states from other versions."); PanicAlert("Can't load states from other versions.");
return; return;
} }
@ -84,7 +86,7 @@ void DoState(PointerWrap &p)
pm.GetVideo()->DoState(p.GetPPtr(), p.GetMode()); pm.GetVideo()->DoState(p.GetPPtr(), p.GetMode());
pm.GetDSP()->DoState(p.GetPPtr(), p.GetMode()); pm.GetDSP()->DoState(p.GetPPtr(), p.GetMode());
pm.GetPad(0)->DoState(p.GetPPtr(), p.GetMode()); pm.GetPad(0)->DoState(p.GetPPtr(), p.GetMode());
if(Core::g_CoreStartupParameter.bWii) if (Core::g_CoreStartupParameter.bWii)
pm.GetWiimote(0)->DoState(p.GetPPtr(), p.GetMode()); pm.GetWiimote(0)->DoState(p.GetPPtr(), p.GetMode());
PowerPC::DoState(p); PowerPC::DoState(p);
HW::DoState(p); HW::DoState(p);
@ -93,7 +95,7 @@ void DoState(PointerWrap &p)
void LoadBufferStateCallback(u64 userdata, int cyclesLate) void LoadBufferStateCallback(u64 userdata, int cyclesLate)
{ {
if(!cur_buffer || !*cur_buffer) { if (!cur_buffer || !*cur_buffer) {
Core::DisplayMessage("State does not exist", 1000); Core::DisplayMessage("State does not exist", 1000);
return; return;
} }
@ -107,13 +109,14 @@ void LoadBufferStateCallback(u64 userdata, int cyclesLate)
cur_buffer = NULL; cur_buffer = NULL;
Core::DisplayMessage("Loaded state", 2000); Core::DisplayMessage("Loaded state", 2000);
state_op_in_progress = false;
} }
void SaveBufferStateCallback(u64 userdata, int cyclesLate) void SaveBufferStateCallback(u64 userdata, int cyclesLate)
{ {
size_t sz; size_t sz;
if(!cur_buffer) { if (!cur_buffer) {
Core::DisplayMessage("Error saving state", 1000); Core::DisplayMessage("Error saving state", 1000);
return; return;
} }
@ -126,8 +129,8 @@ void SaveBufferStateCallback(u64 userdata, int cyclesLate)
DoState(p); DoState(p);
sz = (size_t)ptr; sz = (size_t)ptr;
if(*cur_buffer) if (*cur_buffer)
delete[] (*cur_buffer); delete [] (*cur_buffer);
*cur_buffer = new u8[sz]; *cur_buffer = new u8[sz];
ptr = *cur_buffer; ptr = *cur_buffer;
@ -135,6 +138,7 @@ void SaveBufferStateCallback(u64 userdata, int cyclesLate)
DoState(p); DoState(p);
cur_buffer = NULL; cur_buffer = NULL;
state_op_in_progress = false;
} }
THREAD_RETURN CompressAndDumpState(void *pArgs) THREAD_RETURN CompressAndDumpState(void *pArgs)
@ -149,16 +153,16 @@ THREAD_RETURN CompressAndDumpState(void *pArgs)
delete saveArg; delete saveArg;
// Moving to last overwritten save-state // Moving to last overwritten save-state
if(File::Exists(cur_filename.c_str())) { if (File::Exists(cur_filename.c_str())) {
if(File::Exists(FULL_STATESAVES_DIR "lastState.sav")) if (File::Exists(FULL_STATESAVES_DIR "lastState.sav"))
File::Delete(FULL_STATESAVES_DIR "lastState.sav"); File::Delete(FULL_STATESAVES_DIR "lastState.sav");
if(!File::Rename(cur_filename.c_str(), FULL_STATESAVES_DIR "lastState.sav")) if (!File::Rename(cur_filename.c_str(), FULL_STATESAVES_DIR "lastState.sav"))
Core::DisplayMessage("Failed to move previous state to state undo backup", 1000); Core::DisplayMessage("Failed to move previous state to state undo backup", 1000);
} }
FILE *f = fopen(filename.c_str(), "wb"); FILE *f = fopen(filename.c_str(), "wb");
if(f == NULL) { if (f == NULL) {
Core::DisplayMessage("Could not save state", 2000); Core::DisplayMessage("Could not save state", 2000);
delete [] buffer; delete [] buffer;
return 0; return 0;
@ -169,31 +173,33 @@ THREAD_RETURN CompressAndDumpState(void *pArgs)
header.sz = bCompressed ? sz : 0; header.sz = bCompressed ? sz : 0;
fwrite(&header, sizeof(state_header), 1, f); fwrite(&header, sizeof(state_header), 1, f);
if (bCompressed) { if (bCompressed) {
lzo_uint cur_len; lzo_uint cur_len = 0;
lzo_uint i = 0; lzo_uint i = 0;
for(;;) { for (;;) {
if((i + IN_LEN) >= sz) if ((i + IN_LEN) >= sz)
cur_len = sz - i; cur_len = sz - i;
else else
cur_len = IN_LEN; cur_len = IN_LEN;
if(lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK) if (lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK)
PanicAlert("Internal LZO Error - compression failed"); PanicAlert("Internal LZO Error - compression failed");
// The size of the data to write is 'out_len' // The size of the data to write is 'out_len'
fwrite(&out_len, sizeof(int), 1, f); fwrite(&out_len, sizeof(int), 1, f);
fwrite(out, out_len, 1, f); fwrite(out, out_len, 1, f);
if(cur_len != IN_LEN) if (cur_len != IN_LEN)
break; break;
i += cur_len; i += cur_len;
} }
} else }
else
{
fwrite(buffer, sz, 1, f); fwrite(buffer, sz, 1, f);
}
fclose(f); fclose(f);
@ -202,23 +208,28 @@ THREAD_RETURN CompressAndDumpState(void *pArgs)
Core::DisplayMessage(StringFromFormat("Saved State to %s", Core::DisplayMessage(StringFromFormat("Saved State to %s",
filename.c_str()).c_str(), 2000); filename.c_str()).c_str(), 2000);
state_op_in_progress = false;
return 0; return 0;
} }
void SaveStateCallback(u64 userdata, int cyclesLate) void SaveStateCallback(u64 userdata, int cyclesLate)
{ {
// If already saving state, wait for it to finish // If already saving state, wait for it to finish
if(saveThread) if (saveThread)
{ {
delete saveThread; delete saveThread;
saveThread = NULL; saveThread = NULL;
} }
jit.ClearCache(); jit.ClearCache();
// Measure the size of the buffer.
u8 *ptr = 0; u8 *ptr = 0;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p); DoState(p);
size_t sz = (size_t)ptr; size_t sz = (size_t)ptr;
// Then actually do the write.
u8 *buffer = new u8[sz]; u8 *buffer = new u8[sz];
ptr = buffer; ptr = buffer;
p.SetMode(PointerWrap::MODE_WRITE); p.SetMode(PointerWrap::MODE_WRITE);
@ -235,13 +246,10 @@ void SaveStateCallback(u64 userdata, int cyclesLate)
void LoadStateCallback(u64 userdata, int cyclesLate) void LoadStateCallback(u64 userdata, int cyclesLate)
{ {
lzo_uint cur_len;
lzo_uint new_len;
bool bCompressedState; bool bCompressedState;
// If saving state, wait for it to finish // If saving state, wait for it to finish
if(saveThread) if (saveThread)
{ {
delete saveThread; delete saveThread;
saveThread = NULL; saveThread = NULL;
@ -252,7 +260,8 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
SaveBufferStateCallback(userdata, cyclesLate); SaveBufferStateCallback(userdata, cyclesLate);
FILE *f = fopen(cur_filename.c_str(), "rb"); FILE *f = fopen(cur_filename.c_str(), "rb");
if (!f) { if (!f)
{
Core::DisplayMessage("State not found", 2000); Core::DisplayMessage("State not found", 2000);
return; return;
} }
@ -263,7 +272,7 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
fread(&header, sizeof(state_header), 1, f); fread(&header, sizeof(state_header), 1, f);
if(memcmp(Core::GetStartupParameter().GetUniqueID().c_str(), header.gameID, 6)) if (memcmp(Core::GetStartupParameter().GetUniqueID().c_str(), header.gameID, 6))
{ {
char gameID[7] = {0}; char gameID[7] = {0};
memcpy(gameID, header.gameID, 6); memcpy(gameID, header.gameID, 6);
@ -277,43 +286,46 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
sz = header.sz; sz = header.sz;
bCompressedState = (sz != 0); bCompressedState = (sz != 0);
if (bCompressedState)
if (bCompressedState) { {
Core::DisplayMessage("Decompressing State...", 500); Core::DisplayMessage("Decompressing State...", 500);
lzo_uint i = 0; lzo_uint i = 0;
buffer = new u8[sz]; buffer = new u8[sz];
if(!buffer) { if (!buffer) {
PanicAlert("Error allocating buffer"); PanicAlert("Error allocating buffer");
return; return;
} }
while (true)
{
for (;;) { lzo_uint cur_len = 0;
lzo_uint new_len = 0;
if (fread(&cur_len, 1, sizeof(int), f) == 0) if (fread(&cur_len, 1, sizeof(int), f) == 0)
break; break;
if (feof(f))
break; // don't know if this happens.
fread(out, 1, cur_len, f); fread(out, 1, cur_len, f);
int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL); int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL);
if(res != LZO_E_OK) { if (res != LZO_E_OK)
{
// This doesn't seem to happen anymore.
PanicAlert("Internal LZO Error - decompression failed (%d) (%d, %d) \n" PanicAlert("Internal LZO Error - decompression failed (%d) (%d, %d) \n"
"Try loading the state again", res, i, new_len); "Try loading the state again", res, i, new_len);
fclose(f); fclose(f);
delete [] buffer;
delete[] buffer;
return; return;
} }
// The size of the data to read to our buffer is 'new_len' // The size of the data to read to our buffer is 'new_len'
i += new_len; i += new_len;
} }
} else { }
else
{
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
sz = (int)(ftell(f) - sizeof(int)); sz = (int)(ftell(f) - sizeof(int));
fseek(f, sizeof(int), SEEK_SET); fseek(f, sizeof(int), SEEK_SET);
buffer = new u8[sz]; buffer = new u8[sz];
int x; int x;
if ((x = (int)fread(buffer, 1, sz, f)) != (int)sz) if ((x = (int)fread(buffer, 1, sz, f)) != (int)sz)
PanicAlert("wtf? %d %d", x, sz); PanicAlert("wtf? %d %d", x, sz);
@ -344,13 +356,13 @@ void State_Init()
void State_Shutdown() void State_Shutdown()
{ {
if(saveThread) if (saveThread)
{ {
delete saveThread; delete saveThread;
saveThread = NULL; saveThread = NULL;
} }
if(undoLoad) if (undoLoad)
{ {
delete[] undoLoad; delete[] undoLoad;
undoLoad = NULL; undoLoad = NULL;
@ -364,6 +376,9 @@ std::string MakeStateFilename(int state_number)
void State_SaveAs(const std::string &filename) void State_SaveAs(const std::string &filename)
{ {
if (state_op_in_progress)
return;
state_op_in_progress = true;
cur_filename = filename; cur_filename = filename;
lastFilename = filename; lastFilename = filename;
CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save); CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save);
@ -376,6 +391,9 @@ void State_Save(int slot)
void State_LoadAs(const std::string &filename) void State_LoadAs(const std::string &filename)
{ {
if (state_op_in_progress)
return;
state_op_in_progress = true;
cur_filename = filename; cur_filename = filename;
CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load); CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load);
} }
@ -387,7 +405,7 @@ void State_Load(int slot)
void State_LoadLastSaved() void State_LoadLastSaved()
{ {
if(lastFilename.empty()) if (lastFilename.empty())
Core::DisplayMessage("There is no last saved state", 2000); Core::DisplayMessage("There is no last saved state", 2000);
else else
State_LoadAs(lastFilename); State_LoadAs(lastFilename);
@ -395,12 +413,18 @@ void State_LoadLastSaved()
void State_LoadFromBuffer(u8 **buffer) void State_LoadFromBuffer(u8 **buffer)
{ {
if (state_op_in_progress)
return;
state_op_in_progress = true;
cur_buffer = buffer; cur_buffer = buffer;
CoreTiming::ScheduleEvent_Threadsafe(0, ev_BufferLoad); CoreTiming::ScheduleEvent_Threadsafe(0, ev_BufferLoad);
} }
void State_SaveToBuffer(u8 **buffer) void State_SaveToBuffer(u8 **buffer)
{ {
if (state_op_in_progress)
return;
state_op_in_progress = true;
cur_buffer = buffer; cur_buffer = buffer;
CoreTiming::ScheduleEvent_Threadsafe(0, ev_BufferSave); CoreTiming::ScheduleEvent_Threadsafe(0, ev_BufferSave);
} }

View File

@ -39,13 +39,13 @@ void State_Load(int slot);
void State_SaveAs(const std::string &filename); void State_SaveAs(const std::string &filename);
void State_LoadAs(const std::string &filename); void State_LoadAs(const std::string &filename);
void State_LoadFromBuffer(u8 **buffer);
void State_SaveToBuffer(u8 **buffer);
void State_LoadLastSaved(); void State_LoadLastSaved();
void State_UndoSaveState(); void State_UndoSaveState();
void State_UndoLoadState(); void State_UndoLoadState();
void State_LoadFromBuffer(u8 **buffer);
void State_SaveToBuffer(u8 **buffer);
typedef struct typedef struct
{ {