added savestate.verify Lua function for automated desync detection (disabled in public release)
This commit is contained in:
parent
5e42454cf4
commit
9c4a2dc20c
|
@ -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){
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue