add savestate.registerload, savestate.registersave, and savestate.loadscriptdata from snes9x lua (test needed! especially for savestate.loadscriptdata, one of the functions I've never used)

This commit is contained in:
gocha 2009-10-25 03:02:00 +00:00
parent ea92db188b
commit 0bc230bd31
4 changed files with 833 additions and 38 deletions

View File

@ -5,6 +5,8 @@ enum LuaCallID
LUACALL_BEFOREEMULATION,
LUACALL_AFTEREMULATION,
LUACALL_BEFOREEXIT,
LUACALL_BEFORESAVE,
LUACALL_AFTERLOAD,
LUACALL_COUNT
};
@ -23,9 +25,43 @@ enum LuaMemHookType
};
void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType);
struct LuaSaveData
{
LuaSaveData() { recordList = 0; }
~LuaSaveData() { ClearRecords(); }
struct Record
{
unsigned int key; // crc32
unsigned int size; // size of data
unsigned char* data;
Record* next;
};
Record* recordList;
void SaveRecord(struct lua_State* L, unsigned int key); // saves Lua stack into a record and pops it
void LoadRecord(struct lua_State* L, unsigned int key, unsigned int itemsToLoad) const; // pushes a record's data onto the Lua stack
void SaveRecordPartial(struct lua_State* L, unsigned int key, int idx); // saves part of the Lua stack (at the given index) into a record and does NOT pop anything
void ExportRecords(void* file) const; // writes all records to an already-open file
void ImportRecords(void* file); // reads records from an already-open file
void ClearRecords(); // deletes all record data
private:
// disallowed, it's dangerous to call this
// (because the memory the destructor deletes isn't refcounted and shouldn't need to be copied)
// so pass LuaSaveDatas by reference and this should never get called
LuaSaveData(const LuaSaveData& copy) {}
};
#define LUA_DATARECORDKEY 42
void CallRegisteredLuaSaveFunctions(int savestateNumber, LuaSaveData& saveData);
void CallRegisteredLuaLoadFunctions(int savestateNumber, const LuaSaveData& saveData);
// Just forward function declarations
//void FCEU_LuaWrite(uint32 addr);
void FCEU_LuaFrameBoundary();
int FCEU_LoadLuaCode(const char *filename);
void FCEU_ReloadLuaCode();
@ -40,15 +76,11 @@ int FCEU_LuaRerecordCountSkip();
void FCEU_LuaGui(uint8 *XBuf);
void FCEU_LuaUpdatePalette();
struct lua_State* FCEU_GetLuaState();
char* FCEU_GetLuaScriptName();
// And some interesting REVERSE declarations!
char *FCEU_GetFreezeFilename(int slot);
// Call this before writing into a buffer passed to FCEU_CheatAddRAM().
// (That way, Lua-based memwatch will work as expected for somebody
// used to FCEU's memwatch.)
//void FCEU_LuaWriteInform(); // DEADBEEF
#endif

View File

@ -21,6 +21,7 @@ extern "C"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <lstate.h>
}
#include "types.h"
@ -172,6 +173,8 @@ static const char* luaCallIDStrings [] =
"CALL_BEFOREEMULATION",
"CALL_AFTEREMULATION",
"CALL_BEFOREEXIT",
"CALL_BEFORESAVE",
"CALL_AFTERLOAD",
};
//make sure we have the right number of strings
@ -435,6 +438,663 @@ static int emu_registerexit(lua_State *L) {
}
// can't remember what the best way of doing this is...
#if defined(i386) || defined(__i386) || defined(__i386__) || defined(M_I86) || defined(_M_IX86) || defined(WIN32)
#define IS_LITTLE_ENDIAN
#endif
// push a value's bytes onto the output stack
template<typename T>
void PushBinaryItem(T item, std::vector<unsigned char>& output)
{
unsigned char* buf = (unsigned char*)&item;
#ifdef IS_LITTLE_ENDIAN
for(int i = sizeof(T); i; i--)
output.push_back(*buf++);
#else
int vecsize = output.size();
for(int i = sizeof(T); i; i--)
output.insert(output.begin() + vecsize, *buf++);
#endif
}
// read a value from the byte stream and advance the stream by its size
template<typename T>
T AdvanceByteStream(const unsigned char*& data, unsigned int& remaining)
{
#ifdef IS_LITTLE_ENDIAN
T rv = *(T*)data;
data += sizeof(T);
#else
T rv; unsigned char* rvptr = (unsigned char*)&rv;
for(int i = sizeof(T)-1; i>=0; i--)
rvptr[i] = *data++;
#endif
remaining -= sizeof(T);
return rv;
}
// advance the byte stream by a certain size without reading a value
void AdvanceByteStream(const unsigned char*& data, unsigned int& remaining, int amount)
{
data += amount;
remaining -= amount;
}
#define LUAEXT_TLONG 30 // 0x1E // 4-byte signed integer
#define LUAEXT_TUSHORT 31 // 0x1F // 2-byte unsigned integer
#define LUAEXT_TSHORT 32 // 0x20 // 2-byte signed integer
#define LUAEXT_TBYTE 33 // 0x21 // 1-byte unsigned integer
#define LUAEXT_TNILS 34 // 0x22 // multiple nils represented by a 4-byte integer (warning: becomes multiple stack entities)
#define LUAEXT_TTABLE 0x40 // 0x40 through 0x4F // tables of different sizes:
#define LUAEXT_BITS_1A 0x01 // size of array part fits in a 1-byte unsigned integer
#define LUAEXT_BITS_2A 0x02 // size of array part fits in a 2-byte unsigned integer
#define LUAEXT_BITS_4A 0x03 // size of array part fits in a 4-byte unsigned integer
#define LUAEXT_BITS_1H 0x04 // size of hash part fits in a 1-byte unsigned integer
#define LUAEXT_BITS_2H 0x08 // size of hash part fits in a 2-byte unsigned integer
#define LUAEXT_BITS_4H 0x0C // size of hash part fits in a 4-byte unsigned integer
#define BITMATCH(x,y) (((x) & (y)) == (y))
static void PushNils(std::vector<unsigned char>& output, int& nilcount)
{
int count = nilcount;
nilcount = 0;
static const int minNilsWorthEncoding = 6; // because a LUAEXT_TNILS entry is 5 bytes
if(count < minNilsWorthEncoding)
{
for(int i = 0; i < count; i++)
output.push_back(LUA_TNIL);
}
else
{
output.push_back(LUAEXT_TNILS);
PushBinaryItem<uint32>(count, output);
}
}
static std::vector<const void*> s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is found, print something like table:parent)
static std::vector<const void*> s_metacallStack; // prevents infinite recursion if something's __tostring returns another table that contains that something (when cycle is found, print the inner result without using __tostring)
static void LuaStackToBinaryConverter(lua_State* L, int i, std::vector<unsigned char>& output)
{
int type = lua_type(L, i);
// the first byte of every serialized item says what Lua type it is
output.push_back(type & 0xFF);
switch(type)
{
default:
{
char errmsg [1024];
sprintf(errmsg, "values of type \"%s\" are not allowed to be returned from registered save functions.\r\n", luaL_typename(L,i));
if(info_print)
info_print(info_uid, errmsg);
else
puts(errmsg);
}
break;
case LUA_TNIL:
// no information necessary beyond the type
break;
case LUA_TBOOLEAN:
// serialize as 0 or 1
output.push_back(lua_toboolean(L,i));
break;
case LUA_TSTRING:
// serialize as a 0-terminated string of characters
{
const char* str = lua_tostring(L,i);
while(*str)
output.push_back(*str++);
output.push_back('\0');
}
break;
case LUA_TNUMBER:
{
double num = (double)lua_tonumber(L,i);
int32 inum = (int32)lua_tointeger(L,i);
if(num != inum)
{
PushBinaryItem(num, output);
}
else
{
if((inum & ~0xFF) == 0)
type = LUAEXT_TBYTE;
else if((uint16)(inum & 0xFFFF) == inum)
type = LUAEXT_TUSHORT;
else if((int16)(inum & 0xFFFF) == inum)
type = LUAEXT_TSHORT;
else
type = LUAEXT_TLONG;
output.back() = type;
switch(type)
{
case LUAEXT_TLONG:
PushBinaryItem<int32>(static_cast<int32>(inum), output);
break;
case LUAEXT_TUSHORT:
PushBinaryItem<uint16>(static_cast<uint16>(inum), output);
break;
case LUAEXT_TSHORT:
PushBinaryItem<int16>(static_cast<int16>(inum), output);
break;
case LUAEXT_TBYTE:
output.push_back(static_cast<uint8>(inum));
break;
}
}
}
break;
case LUA_TTABLE:
// serialize as a type that describes how many bytes are used for storing the counts,
// followed by the number of array entries if any, then the number of hash entries if any,
// then a Lua value per array entry, then a (key,value) pair of Lua values per hashed entry
// note that the structure of table references are not faithfully serialized (yet)
{
int outputTypeIndex = output.size() - 1;
int arraySize = 0;
int hashSize = 0;
if(lua_checkstack(L, 4) && std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L,i)) == s_tableAddressStack.end())
{
s_tableAddressStack.push_back(lua_topointer(L,i));
struct Scope { ~Scope(){ s_tableAddressStack.pop_back(); } } scope;
bool wasnil = false;
int nilcount = 0;
arraySize = lua_objlen(L, i);
int arrayValIndex = lua_gettop(L) + 1;
for(int j = 1; j <= arraySize; j++)
{
lua_rawgeti(L, i, j);
bool isnil = lua_isnil(L, arrayValIndex);
if(isnil)
nilcount++;
else
{
if(wasnil)
PushNils(output, nilcount);
LuaStackToBinaryConverter(L, arrayValIndex, output);
}
lua_pop(L, 1);
wasnil = isnil;
}
if(wasnil)
PushNils(output, nilcount);
if(arraySize)
lua_pushinteger(L, arraySize); // before first key
else
lua_pushnil(L); // before first key
int keyIndex = lua_gettop(L);
int valueIndex = keyIndex + 1;
while(lua_next(L, i))
{
// assert(lua_type(L, keyIndex) && "nil key in Lua table, impossible");
// assert(lua_type(L, valueIndex) && "nil value in Lua table, impossible");
LuaStackToBinaryConverter(L, keyIndex, output);
LuaStackToBinaryConverter(L, valueIndex, output);
lua_pop(L, 1);
hashSize++;
}
}
int outputType = LUAEXT_TTABLE;
if(arraySize & 0xFFFF0000)
outputType |= LUAEXT_BITS_4A;
else if(arraySize & 0xFF00)
outputType |= LUAEXT_BITS_2A;
else if(arraySize & 0xFF)
outputType |= LUAEXT_BITS_1A;
if(hashSize & 0xFFFF0000)
outputType |= LUAEXT_BITS_4H;
else if(hashSize & 0xFF00)
outputType |= LUAEXT_BITS_2H;
else if(hashSize & 0xFF)
outputType |= LUAEXT_BITS_1H;
output[outputTypeIndex] = outputType;
int insertIndex = outputTypeIndex;
if(BITMATCH(outputType,LUAEXT_BITS_4A) || BITMATCH(outputType,LUAEXT_BITS_2A) || BITMATCH(outputType,LUAEXT_BITS_1A))
output.insert(output.begin() + (++insertIndex), arraySize & 0xFF);
if(BITMATCH(outputType,LUAEXT_BITS_4A) || BITMATCH(outputType,LUAEXT_BITS_2A))
output.insert(output.begin() + (++insertIndex), (arraySize & 0xFF00) >> 8);
if(BITMATCH(outputType,LUAEXT_BITS_4A))
output.insert(output.begin() + (++insertIndex), (arraySize & 0x00FF0000) >> 16),
output.insert(output.begin() + (++insertIndex), (arraySize & 0xFF000000) >> 24);
if(BITMATCH(outputType,LUAEXT_BITS_4H) || BITMATCH(outputType,LUAEXT_BITS_2H) || BITMATCH(outputType,LUAEXT_BITS_1H))
output.insert(output.begin() + (++insertIndex), hashSize & 0xFF);
if(BITMATCH(outputType,LUAEXT_BITS_4H) || BITMATCH(outputType,LUAEXT_BITS_2H))
output.insert(output.begin() + (++insertIndex), (hashSize & 0xFF00) >> 8);
if(BITMATCH(outputType,LUAEXT_BITS_4H))
output.insert(output.begin() + (++insertIndex), (hashSize & 0x00FF0000) >> 16),
output.insert(output.begin() + (++insertIndex), (hashSize & 0xFF000000) >> 24);
} break;
}
}
// complements LuaStackToBinaryConverter
void BinaryToLuaStackConverter(lua_State* L, const unsigned char*& data, unsigned int& remaining)
{
// assert(s_dbg_dataSize - (data - s_dbg_dataStart) == remaining);
unsigned char type = AdvanceByteStream<unsigned char>(data, remaining);
switch(type)
{
default:
{
char errmsg [1024];
if(type <= 10 && type != LUA_TTABLE)
sprintf(errmsg, "values of type \"%s\" are not allowed to be loaded into registered load functions. The save state's Lua save data file might be corrupted.\r\n", lua_typename(L,type));
else
sprintf(errmsg, "The save state's Lua save data file seems to be corrupted.\r\n");
if(info_print)
info_print(info_uid, errmsg);
else
puts(errmsg);
}
break;
case LUA_TNIL:
lua_pushnil(L);
break;
case LUA_TBOOLEAN:
lua_pushboolean(L, AdvanceByteStream<uint8>(data, remaining));
break;
case LUA_TSTRING:
lua_pushstring(L, (const char*)data);
AdvanceByteStream(data, remaining, strlen((const char*)data) + 1);
break;
case LUA_TNUMBER:
lua_pushnumber(L, AdvanceByteStream<double>(data, remaining));
break;
case LUAEXT_TLONG:
lua_pushinteger(L, AdvanceByteStream<int32>(data, remaining));
break;
case LUAEXT_TUSHORT:
lua_pushinteger(L, AdvanceByteStream<uint16>(data, remaining));
break;
case LUAEXT_TSHORT:
lua_pushinteger(L, AdvanceByteStream<int16>(data, remaining));
break;
case LUAEXT_TBYTE:
lua_pushinteger(L, AdvanceByteStream<uint8>(data, remaining));
break;
case LUAEXT_TTABLE:
case LUAEXT_TTABLE | LUAEXT_BITS_1A:
case LUAEXT_TTABLE | LUAEXT_BITS_2A:
case LUAEXT_TTABLE | LUAEXT_BITS_4A:
case LUAEXT_TTABLE | LUAEXT_BITS_1H:
case LUAEXT_TTABLE | LUAEXT_BITS_2H:
case LUAEXT_TTABLE | LUAEXT_BITS_4H:
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_1H:
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_1H:
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_1H:
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_2H:
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_2H:
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_2H:
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_4H:
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_4H:
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_4H:
{
unsigned int arraySize = 0;
if(BITMATCH(type,LUAEXT_BITS_4A) || BITMATCH(type,LUAEXT_BITS_2A) || BITMATCH(type,LUAEXT_BITS_1A))
arraySize |= AdvanceByteStream<uint8>(data, remaining);
if(BITMATCH(type,LUAEXT_BITS_4A) || BITMATCH(type,LUAEXT_BITS_2A))
arraySize |= ((uint16)AdvanceByteStream<uint8>(data, remaining)) << 8;
if(BITMATCH(type,LUAEXT_BITS_4A))
arraySize |= ((uint32)AdvanceByteStream<uint8>(data, remaining)) << 16,
arraySize |= ((uint32)AdvanceByteStream<uint8>(data, remaining)) << 24;
unsigned int hashSize = 0;
if(BITMATCH(type,LUAEXT_BITS_4H) || BITMATCH(type,LUAEXT_BITS_2H) || BITMATCH(type,LUAEXT_BITS_1H))
hashSize |= AdvanceByteStream<uint8>(data, remaining);
if(BITMATCH(type,LUAEXT_BITS_4H) || BITMATCH(type,LUAEXT_BITS_2H))
hashSize |= ((uint16)AdvanceByteStream<uint8>(data, remaining)) << 8;
if(BITMATCH(type,LUAEXT_BITS_4H))
hashSize |= ((uint32)AdvanceByteStream<uint8>(data, remaining)) << 16,
hashSize |= ((uint32)AdvanceByteStream<uint8>(data, remaining)) << 24;
lua_checkstack(L, 8);
lua_createtable(L, arraySize, hashSize);
unsigned int n = 1;
while(n <= arraySize)
{
if(*data == LUAEXT_TNILS)
{
AdvanceByteStream(data, remaining, 1);
n += AdvanceByteStream<uint32>(data, remaining);
}
else
{
BinaryToLuaStackConverter(L, data, remaining); // push value
lua_rawseti(L, -2, n); // table[n] = value
n++;
}
}
for(unsigned int h = 1; h <= hashSize; h++)
{
BinaryToLuaStackConverter(L, data, remaining); // push key
BinaryToLuaStackConverter(L, data, remaining); // push value
lua_rawset(L, -3); // table[key] = value
}
}
break;
}
}
static const unsigned char luaBinaryMajorVersion = 9;
static const unsigned char luaBinaryMinorVersion = 1;
unsigned char* LuaStackToBinary(lua_State* L, unsigned int& size)
{
int n = lua_gettop(L);
if(n == 0)
return NULL;
std::vector<unsigned char> output;
output.push_back(luaBinaryMajorVersion);
output.push_back(luaBinaryMinorVersion);
for(int i = 1; i <= n; i++)
LuaStackToBinaryConverter(L, i, output);
unsigned char* rv = new unsigned char [output.size()];
memcpy(rv, &output.front(), output.size());
size = output.size();
return rv;
}
void BinaryToLuaStack(lua_State* L, const unsigned char* data, unsigned int size, unsigned int itemsToLoad)
{
unsigned char major = *data++;
unsigned char minor = *data++;
size -= 2;
if(luaBinaryMajorVersion != major || luaBinaryMinorVersion != minor)
return;
while(size > 0 && itemsToLoad > 0)
{
BinaryToLuaStackConverter(L, data, size);
itemsToLoad--;
}
}
// saves Lua stack into a record and pops it
void LuaSaveData::SaveRecord(lua_State* L, unsigned int key)
{
if(!L)
return;
Record* cur = new Record();
cur->key = key;
cur->data = LuaStackToBinary(L, cur->size);
cur->next = NULL;
lua_settop(L,0);
if(cur->size <= 0)
{
delete cur;
return;
}
Record* last = recordList;
while(last && last->next)
last = last->next;
if(last)
last->next = cur;
else
recordList = cur;
}
// pushes a record's data onto the Lua stack
void LuaSaveData::LoadRecord(struct lua_State* L, unsigned int key, unsigned int itemsToLoad) const
{
if(!L)
return;
Record* cur = recordList;
while(cur)
{
if(cur->key == key)
{
// s_dbg_dataStart = cur->data;
// s_dbg_dataSize = cur->size;
BinaryToLuaStack(L, cur->data, cur->size, itemsToLoad);
return;
}
cur = cur->next;
}
}
// saves part of the Lua stack (at the given index) into a record and does NOT pop anything
void LuaSaveData::SaveRecordPartial(struct lua_State* L, unsigned int key, int idx)
{
if(!L)
return;
if(idx < 0)
idx += lua_gettop(L)+1;
Record* cur = new Record();
cur->key = key;
cur->next = NULL;
if(idx <= lua_gettop(L))
{
std::vector<unsigned char> output;
output.push_back(luaBinaryMajorVersion);
output.push_back(luaBinaryMinorVersion);
LuaStackToBinaryConverter(L, idx, output);
unsigned char* rv = new unsigned char [output.size()];
memcpy(rv, &output.front(), output.size());
cur->size = output.size();
cur->data = rv;
}
if(cur->size <= 0)
{
delete cur;
return;
}
Record* last = recordList;
while(last && last->next)
last = last->next;
if(last)
last->next = cur;
else
recordList = cur;
}
void fwriteint(unsigned int value, FILE* file)
{
for(int i=0;i<4;i++)
{
int w = value & 0xFF;
fwrite(&w, 1, 1, file);
value >>= 8;
}
}
void freadint(unsigned int& value, FILE* file)
{
int rv = 0;
for(int i=0;i<4;i++)
{
int r = 0;
fread(&r, 1, 1, file);
rv |= r << (i*8);
}
value = rv;
}
// writes all records to an already-open file
void LuaSaveData::ExportRecords(void* fileV) const
{
FILE* file = (FILE*)fileV;
if(!file)
return;
Record* cur = recordList;
while(cur)
{
fwriteint(cur->key, file);
fwriteint(cur->size, file);
fwrite(cur->data, cur->size, 1, file);
cur = cur->next;
}
}
// reads records from an already-open file
void LuaSaveData::ImportRecords(void* fileV)
{
FILE* file = (FILE*)fileV;
if(!file)
return;
ClearRecords();
Record rec;
Record* cur = &rec;
Record* last = NULL;
while(1)
{
freadint(cur->key, file);
freadint(cur->size, file);
if(feof(file) || ferror(file))
break;
cur->data = new unsigned char [cur->size];
fread(cur->data, cur->size, 1, file);
Record* next = new Record();
memcpy(next, cur, sizeof(Record));
next->next = NULL;
if(last)
last->next = next;
else
recordList = next;
last = next;
}
}
void LuaSaveData::ClearRecords()
{
Record* cur = recordList;
while(cur)
{
Record* del = cur;
cur = cur->next;
delete[] del->data;
delete del;
}
recordList = NULL;
}
void CallRegisteredLuaSaveFunctions(int savestateNumber, LuaSaveData& saveData)
{
//lua_State* L = FCEU_GetLuaState();
if(L)
{
lua_settop(L, 0);
lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]);
if (lua_isfunction(L, -1))
{
lua_pushinteger(L, savestateNumber);
int ret = lua_pcall(L, 1, LUA_MULTRET, 0);
if (ret != 0) {
// This is grounds for trashing the function
lua_pushnil(L);
lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]);
#ifdef WIN32
MessageBox(hAppWnd, lua_tostring(L, -1), "Lua Error in SAVE function", MB_OK);
#else
fprintf(stderr, "Lua error in registersave function: %s\n", lua_tostring(L, -1));
#endif
}
saveData.SaveRecord(L, LUA_DATARECORDKEY);
}
else
{
lua_pop(L, 1);
}
}
}
void CallRegisteredLuaLoadFunctions(int savestateNumber, const LuaSaveData& saveData)
{
//lua_State* L = FCEU_GetLuaState();
if(L)
{
lua_settop(L, 0);
lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]);
if (lua_isfunction(L, -1))
{
// since the scriptdata can be very expensive to load
// (e.g. the registered save function returned some huge tables)
// check the number of parameters the registered load function expects
// and don't bother loading the parameters it wouldn't receive anyway
int numParamsExpected = (L->top - 1)->value.gc->cl.l.p->numparams; // NOTE: if this line crashes, that means your Lua headers are out of sync with your Lua lib
if(numParamsExpected) numParamsExpected--; // minus one for the savestate number we always pass in
int prevGarbage = lua_gc(L, LUA_GCCOUNT, 0);
lua_pushinteger(L, savestateNumber);
saveData.LoadRecord(L, LUA_DATARECORDKEY, numParamsExpected);
int n = lua_gettop(L) - 1;
int ret = lua_pcall(L, n, 0, 0);
if (ret != 0) {
// This is grounds for trashing the function
lua_pushnil(L);
lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]);
#ifdef WIN32
MessageBox(hAppWnd, lua_tostring(L, -1), "Lua Error in LOAD function", MB_OK);
#else
fprintf(stderr, "Lua error in registerload function: %s\n", lua_tostring(L, -1));
#endif
}
else
{
int newGarbage = lua_gc(L, LUA_GCCOUNT, 0);
if(newGarbage - prevGarbage > 50)
{
// now seems to be a very good time to run the garbage collector
// it might take a while now but that's better than taking 10 whiles 9 loads from now
lua_gc(L, LUA_GCCOLLECT, 0);
}
}
}
else
{
lua_pop(L, 1);
}
}
}
static int rom_readbyte(lua_State *L) { lua_pushinteger(L, FCEU_ReadRomByte(luaL_checkinteger(L,1))); return 1; }
static int rom_readbytesigned(lua_State *L) { lua_pushinteger(L, (signed char)FCEU_ReadRomByte(luaL_checkinteger(L,1))); return 1; }
@ -462,9 +1122,6 @@ static inline bool isalphaorunderscore(char c)
return isalpha(c) || c == '_';
}
static std::vector<const void*> s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is found, print something like table:parent)
static std::vector<const void*> s_metacallStack; // prevents infinite recursion if something's __tostring returns another table that contains that something (when cycle is found, print the inner result without using __tostring)
#define APPENDPRINT { int _n = snprintf(ptr, remaining,
#define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }
static void toCStringConverter(lua_State* L, int i, char*& ptr, int& remaining)
@ -1504,31 +2161,27 @@ static int joypad_set(lua_State *L) {
return 0;
}
// Helper function to convert a savestate object to the filename it represents.
static const char *savestateobj2filename(lua_State *L, int offset) {
// First we get the metatable of the indicated object
int result = lua_getmetatable(L, offset);
//
//// Helper function to convert a savestate object to the filename it represents.
//static char *savestateobj2filename(lua_State *L, int offset) {
//
// // First we get the metatable of the indicated object
// int result = lua_getmetatable(L, offset);
//
// if (!result)
// luaL_error(L, "object not a savestate object");
//
// // Also check that the type entry is set
// lua_getfield(L, -1, "__metatable");
// if (strcmp(lua_tostring(L,-1), "FCEU Savestate") != 0)
// luaL_error(L, "object not a savestate object");
// lua_pop(L,1);
//
// // Now, get the field we want
// lua_getfield(L, -1, "filename");
//
// // Return it
// return (char *) lua_tostring(L, -1);
//}
//
if (!result)
luaL_error(L, "object not a savestate object");
// Also check that the type entry is set
lua_getfield(L, -1, "__metatable");
if (strcmp(lua_tostring(L,-1), "FCEU Savestate") != 0)
luaL_error(L, "object not a savestate object");
lua_pop(L,1);
// Now, get the field we want
lua_getfield(L, -1, "filename");
// Return it
return lua_tostring(L, -1);
}
// Helper function for garbage collection.
static int savestate_gc(lua_State *L) {
@ -1659,6 +2312,54 @@ static int savestate_load(lua_State *L) {
}
static int savestate_registersave(lua_State *L) {
lua_settop(L,1);
if (!lua_isnil(L,1))
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]);
lua_pushvalue(L,1);
lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]);
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1;
}
static int savestate_registerload(lua_State *L) {
lua_settop(L,1);
if (!lua_isnil(L,1))
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]);
lua_pushvalue(L,1);
lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]);
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1;
}
static int savestate_loadscriptdata(lua_State *L) {
const char *filename = savestateobj2filename(L,1);
{
LuaSaveData saveData;
char luaSaveFilename [512];
strncpy(luaSaveFilename, filename, 512);
luaSaveFilename[512-(1+7/*strlen(".luasav")*/)] = '\0';
strcat(luaSaveFilename, ".luasav");
FILE* luaSaveFile = fopen(luaSaveFilename, "rb");
if(luaSaveFile)
{
saveData.ImportRecords(luaSaveFile);
fclose(luaSaveFile);
lua_settop(L, 0);
saveData.LoadRecord(L, LUA_DATARECORDKEY, (unsigned int)-1);
return lua_gettop(L);
}
}
return 0;
}
// int emu.framecount()
//
@ -3546,6 +4247,10 @@ static const struct luaL_reg savestatelib[] = {
{"persist", savestate_persist},
{"load", savestate_load},
{"registersave", savestate_registersave},
{"registerload", savestate_registerload},
{"loadscriptdata", savestate_loadscriptdata},
{NULL,NULL}
};

View File

@ -48,6 +48,9 @@
#include "input.h"
#include "zlib.h"
#include "driver.h"
#ifdef _S9XLUA_H
#include "fceulua.h"
#endif
//TODO - we really need some kind of global platform-specific options api
#ifdef WIN32
@ -75,6 +78,8 @@ char lastLoadstateMade[2048]; //Stores the filename of the last state loaded (ne
bool undoLS = false; //This will be true if a backupstate was made and it was made since ROM was loaded
bool redoLS = false; //This will be true if a backupstate was loaded, meaning redoLoadState can be run
bool internalSaveLoad = false;
#define SFMDATA_SIZE (64)
static SFORMAT SFMDATA[SFMDATA_SIZE];
static int SFEXINDEX;
@ -436,7 +441,7 @@ bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel)
void FCEUSS_Save(const char *fname)
{
std::fstream* st = 0;
char *fn;
char fn[2048];
if(geniestage==1)
{
@ -447,11 +452,12 @@ void FCEUSS_Save(const char *fname)
if(fname) //If filename is given use it.
{
st =FCEUD_UTF8_fstream(fname, "wb");
strcpy(fn, fname);
}
else //Else, generate one
{
//FCEU_PrintError("daCurrentState=%d",CurrentState);
fn = strdup(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0).c_str());
strcpy(fn, FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0).c_str());
//backup existing savestate first
if (CheckFileExists(fn))
@ -464,7 +470,6 @@ void FCEUSS_Save(const char *fname)
undoSS = false; //so backup made so lastSavestateMade does have a backup file, so no undo
st = FCEUD_UTF8_fstream(fn,"wb");
free(fn);
}
if(st == NULL)
@ -473,6 +478,32 @@ void FCEUSS_Save(const char *fname)
return;
}
#ifdef _S9XLUA_H
if (!internalSaveLoad)
{
LuaSaveData saveData;
CallRegisteredLuaSaveFunctions(CurrentState, saveData);
char luaSaveFilename [512];
strncpy(luaSaveFilename, fn, 512);
luaSaveFilename[512-(1+7/*strlen(".luasav")*/)] = '\0';
strcat(luaSaveFilename, ".luasav");
if(saveData.recordList)
{
FILE* luaSaveFile = fopen(luaSaveFilename, "wb");
if(luaSaveFile)
{
saveData.ExportRecords(luaSaveFile);
fclose(luaSaveFile);
}
}
else
{
unlink(luaSaveFilename);
}
}
#endif
if(FCEUMOV_Mode(MOVIEMODE_INACTIVE))
FCEUSS_SaveMS(st,-1);
else
@ -660,6 +691,7 @@ bool FCEUSS_LoadFP(std::istream* is, ENUM_SSLOADPARAMS params)
bool FCEUSS_Load(const char *fname)
{
std::fstream* st;
char fn[2048];
//mbg movie - this needs to be overhauled
////this fixes read-only toggle problems
@ -676,12 +708,13 @@ bool FCEUSS_Load(const char *fname)
if(fname)
{
st=FCEUD_UTF8_fstream(fname, "rb");
strcpy(fn, fname);
}
else
{
string fn = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,fname);
strcpy(fn, FCEU_MakeFName(FCEUMKF_STATE,CurrentState,fname).c_str());
st=FCEUD_UTF8_fstream(fn,"rb");
strcpy(lastLoadstateMade,fn.c_str());
strcpy(lastLoadstateMade,fn);
}
if(st == NULL)
@ -711,6 +744,27 @@ bool FCEUSS_Load(const char *fname)
SaveStateStatus[CurrentState]=1;
}
delete st;
#ifdef _S9XLUA_H
if (!internalSaveLoad)
{
LuaSaveData saveData;
char luaSaveFilename [512];
strncpy(luaSaveFilename, fn, 512);
luaSaveFilename[512-(1+7/*strlen(".luasav")*/)] = '\0';
strcat(luaSaveFilename, ".luasav");
FILE* luaSaveFile = fopen(luaSaveFilename, "rb");
if(luaSaveFile)
{
saveData.ImportRecords(luaSaveFile);
fclose(luaSaveFile);
}
CallRegisteredLuaLoadFunctions(CurrentState, saveData);
}
#endif
#ifdef WIN32
Update_RAM_Search(); // Update_RAM_Watch() is also called.
#endif
@ -997,7 +1051,9 @@ bool CheckBackupSaveStateExist()
void BackupLoadState()
{
string filename = GetBackupFileName();
internalSaveLoad = true;
FCEUSS_Save(filename.c_str());
internalSaveLoad = false;
undoLS = true;
}
@ -1007,7 +1063,9 @@ void LoadBackup()
string filename = GetBackupFileName(); //Get backup filename
if (CheckBackupSaveStateExist())
{
internalSaveLoad = true;
FCEUSS_Load(filename.c_str()); //Load it
internalSaveLoad = false;
redoLS = true; //Flag redoLoadState
undoLS = false; //Flag that LoadBackup cannot be run again
}

Binary file not shown.