added savestate.verify Lua function for automated desync detection (disabled in public release)

This commit is contained in:
nitsuja 2009-08-30 22:46:28 +00:00
parent 5e42454cf4
commit 9c4a2dc20c
3 changed files with 101 additions and 8 deletions

View File

@ -75,7 +75,7 @@ public:
//todo - handle read-only specially?
class EMUFILE_MEMORY : public EMUFILE {
private:
protected:
std::vector<u8> *vec;
s32 pos, len;
bool ownvec;
@ -96,7 +96,9 @@ public:
}
u8* buf() { return &(*vec)[0]; }
std::vector<u8>* get_vec() { return vec; };
virtual FILE *get_fp() { return NULL; }
virtual int fprintf(const char *format, ...) {
@ -133,7 +135,8 @@ public:
u32 todo = std::min(remain,bytes);
memcpy((void*)ptr,buf()+pos,todo);
pos += todo;
if(todo<bytes) failbit = true;
if(todo<bytes)
failbit = true;
return todo;
}
@ -174,7 +177,7 @@ public:
};
class EMUFILE_FILE : public EMUFILE {
private:
protected:
FILE* fp;
public:
@ -182,7 +185,8 @@ public:
EMUFILE_FILE(const char* fname, const char* mode)
{
fp = fopen(fname,mode);
if(!fp) failbit = true;
if(!fp)
failbit = true;
};
virtual ~EMUFILE_FILE() {
@ -211,7 +215,8 @@ public:
virtual size_t _fread(const void *ptr, size_t bytes){
size_t ret = ::fread((void*)ptr, 1, bytes, fp);
if(ret < bytes) failbit = true;
if(ret < bytes)
failbit = true;
return ret;
}
@ -220,7 +225,8 @@ public:
virtual void fwrite(const void *ptr, size_t bytes){
size_t ret = ::fwrite((void*)ptr, 1, bytes, fp);
if(ret < bytes) failbit = true;
if(ret < bytes)
failbit = true;
}
virtual int fseek(int offset, int origin){

View File

@ -1965,6 +1965,90 @@ DEFINE_LUA_FUNCTION(state_savescriptdata, "location")
return state_save(L);
}
#ifndef PUBLIC_RELEASE
#include "gfx3d.h"
class EMUFILE_MEMORY_VERIFIER : public EMUFILE_MEMORY {
public:
EMUFILE_MEMORY_VERIFIER(EMUFILE_MEMORY* underlying) : EMUFILE_MEMORY(underlying->get_vec()) { }
std::vector<std::string> differences;
virtual void fwrite(const void *ptr, size_t bytes)
{
if(!failbit)
{
u8* dst = buf()+pos;
const u8* src = (const u8*)ptr;
int differencesAddedThisCall = 0;
for(int i = pos; i < (int)bytes+pos; i++)
{
if(*src != *dst)
{
if(differences.size() == 100)
failbit = true;
else
{
char temp [256];
sprintf(temp, " " /*"mismatch at "*/ "byte %d(0x%X at 0x%X): %d(0x%X) != %d(0x%X)\n", i, i, dst, *src,*src, *dst,*dst);
if(ptr == GPU_screen || ptr == gfx3d_convertedScreen) // ignore screen-only differences since frame skipping can cause them and it's probably ok
break;
differences.push_back(temp); // <-- probably the best place for a breakpoint
if(++differencesAddedThisCall == 4)
break;
}
}
src++;
dst++;
}
}
pos += bytes;
}
};
// like savestate.save() except instead of actually saving
// it compares against what's already in the savestate
// and throws an error if any differences are found.
// only useful for development (catching desyncs).
DEFINE_LUA_FUNCTION(state_verify, "location[,option]")
{
int type = lua_type(L,1);
switch(type)
{
case LUA_TNUMBER: // numbered save file
default:
{
luaL_error(L, "savestate.verify only works for in-memory saves.");
} return 0;
case LUA_TUSERDATA: // in-memory save slot
{
EMUFILE_MEMORY** ppEmuFile = (EMUFILE_MEMORY**)luaL_checkudata(L, 1, "EMUFILE_MEMORY*");
if((*ppEmuFile)->fail())
luaL_error(L, "failed to verify, savestate object was dead.");
EMUFILE_MEMORY_VERIFIER verifier (*ppEmuFile);
savestate_save(&verifier, 0);
if(verifier.differences.size())
{
fputs("\n", stdout);
for(unsigned int i = 0; i < verifier.differences.size(); i++)
fputs(verifier.differences[i].c_str(), stdout);
luaL_error(L, "failed to verify savestate! %s", verifier.differences[0].c_str());
}
} return 0;
}
}
#endif
//joypad lib
@ -3857,6 +3941,9 @@ static const struct luaL_reg statelib [] =
{"create", state_create},
{"save", state_save},
{"load", state_load},
#ifndef PUBLIC_RELEASE
{"verify", state_verify}, // for desync catching
#endif
// TODO
//{"loadscriptdata", state_loadscriptdata},
//{"savescriptdata", state_savescriptdata},

View File

@ -863,7 +863,7 @@ static void savestate_WriteChunk(EMUFILE* os, int type, void (*saveproc)(EMUFILE
//write the type, size(placeholder), and data
write32le(type,os);
write32le(0,os); // <-- temporary write, re-written later
os->fseek(4, SEEK_CUR); // skip the size, we write that later
saveproc(os);
//get the size