made savestate loads less fragile by adding some markers and rolling back on a mismatch.

This should make it so if you try to load an incompatible save, it simply doesn't load, instead of crashing dolphin. (I can't guarantee no crash but it's much less likely now)
This commit is contained in:
nitsuja 2011-12-17 16:49:24 -08:00
parent 98d22d0e03
commit c68c8c388c
5 changed files with 55 additions and 6 deletions

View File

@ -170,6 +170,17 @@ public:
// TODO
PanicAlert("Do(linked list<>) does not yet work.");
}
void DoMarker(const char* prevName, u32 arbitraryNumber=0x42)
{
u32 cookie = arbitraryNumber;
Do(cookie);
if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
{
PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
mode = PointerWrap::MODE_MEASURE;
}
}
};

View File

@ -163,6 +163,7 @@ void DoState(PointerWrap &p)
p.Do(fakeDecStartTicks);
p.Do(fakeTBStartValue);
p.Do(fakeTBStartTicks);
p.DoMarker("CoreTimingData");
// OK, here we're gonna need to specialize depending on the mode.
// Should do something generic to serialize linked lists.
switch (p.GetMode()) {
@ -209,6 +210,7 @@ void DoState(PointerWrap &p)
break;
}
}
p.DoMarker("CoreTimingEvents");
}
u64 GetTicks()

View File

@ -89,18 +89,30 @@ namespace HW
void DoState(PointerWrap &p)
{
Memory::DoState(p);
p.DoMarker("Memory");
VideoInterface::DoState(p);
p.DoMarker("VideoInterface");
SerialInterface::DoState(p);
p.DoMarker("SerialInterface");
ProcessorInterface::DoState(p);
p.DoMarker("ProcessorInterface");
DSP::DoState(p);
p.DoMarker("DSP");
DVDInterface::DoState(p);
p.DoMarker("DVDInterface");
GPFifo::DoState(p);
p.DoMarker("GPFifo");
ExpansionInterface::DoState(p);
p.DoMarker("ExpansionInterface");
AudioInterface::DoState(p);
p.DoMarker("AudioInterface");
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{
WII_IPCInterface::DoState(p);
p.DoMarker("WII_IPCInterface");
WII_IPC_HLE_Interface::DoState(p);
p.DoMarker("WII_IPC_HLE_Interface");
}
p.DoMarker("WIIHW");
}
}

View File

@ -373,10 +373,13 @@ void DoState(PointerWrap &p)
p.DoArray(m_pPhysicalRAM, RAM_SIZE);
// p.DoArray(m_pVirtualEFB, EFB_SIZE);
p.DoArray(m_pVirtualL1Cache, L1_CACHE_SIZE);
p.DoMarker("Memory RAM");
if (bFakeVMEM)
p.DoArray(m_pVirtualFakeVMEM, FAKEVMEM_SIZE);
p.DoMarker("Memory FakeVMEM");
if (wii)
p.DoArray(m_pEXRAM, EXRAM_SIZE);
p.DoMarker("Memory EXRAM");
}
void Shutdown()

View File

@ -64,6 +64,7 @@ static std::string g_current_filename, g_last_filename;
// Temporary undo state buffer
static std::vector<u8> g_undo_load_buffer;
static std::vector<u8> g_current_buffer;
static int g_loadDepth = 0;
static std::thread g_save_thread;
@ -74,7 +75,7 @@ static u64 lastCheckedStates[NUM_HOOKS];
static u8 hook;
// Don't forget to increase this after doing changes on the savestate system
static const int STATE_VERSION = 6;
static const int STATE_VERSION = 7;
struct StateHeader
{
@ -121,18 +122,26 @@ void DoState(PointerWrap &p)
}
}
p.DoMarker("Version");
// Begin with video backend, so that it gets a chance to clear it's caches and writeback modified things to RAM
// Pause the video thread in multi-threaded mode
g_video_backend->RunLoop(false);
g_video_backend->DoState(p);
p.DoMarker("video_backend");
if (Core::g_CoreStartupParameter.bWii)
Wiimote::DoState(p.GetPPtr(), p.GetMode());
p.DoMarker("Wiimote");
PowerPC::DoState(p);
p.DoMarker("PowerPC");
HW::DoState(p);
p.DoMarker("HW");
CoreTiming::DoState(p);
p.DoMarker("CoreTiming");
Movie::DoState(p, version<6);
p.DoMarker("Movie");
// Resume the video thread
g_video_backend->RunLoop(true);
@ -353,6 +362,8 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
// Stop the core while we load the state
CCPU::EnableStepping(true);
g_loadDepth++;
Flush();
// Save temp buffer for undo load state
@ -371,20 +382,30 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
DoState(p);
if (p.GetMode() == PointerWrap::MODE_READ)
{
Core::DisplayMessage(StringFromFormat("Loaded state from %s", g_current_filename.c_str()).c_str(), 2000);
if (File::Exists(g_current_filename + ".dtm"))
Movie::LoadInput((g_current_filename + ".dtm").c_str());
else if (!Movie::IsJustStartingRecordingInputFromSaveState())
Movie::EndPlayInput(false);
}
else
{
// failed to load
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
if (File::Exists(g_current_filename + ".dtm"))
Movie::LoadInput((g_current_filename + ".dtm").c_str());
else if (!Movie::IsJustStartingRecordingInputFromSaveState())
Movie::EndPlayInput(false);
// since we're probably in an inconsistent state now (and might crash or whatever), undo.
if(g_loadDepth < 2)
UndoLoadState();
}
}
ResetCounters();
g_op_in_progress = false;
g_loadDepth--;
// resume dat core
CCPU::EnableStepping(false);
}