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:
parent
ea92db188b
commit
0bc230bd31
|
@ -5,6 +5,8 @@ enum LuaCallID
|
||||||
LUACALL_BEFOREEMULATION,
|
LUACALL_BEFOREEMULATION,
|
||||||
LUACALL_AFTEREMULATION,
|
LUACALL_AFTEREMULATION,
|
||||||
LUACALL_BEFOREEXIT,
|
LUACALL_BEFOREEXIT,
|
||||||
|
LUACALL_BEFORESAVE,
|
||||||
|
LUACALL_AFTERLOAD,
|
||||||
|
|
||||||
LUACALL_COUNT
|
LUACALL_COUNT
|
||||||
};
|
};
|
||||||
|
@ -23,9 +25,43 @@ enum LuaMemHookType
|
||||||
};
|
};
|
||||||
void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType);
|
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
|
// Just forward function declarations
|
||||||
|
|
||||||
//void FCEU_LuaWrite(uint32 addr);
|
|
||||||
void FCEU_LuaFrameBoundary();
|
void FCEU_LuaFrameBoundary();
|
||||||
int FCEU_LoadLuaCode(const char *filename);
|
int FCEU_LoadLuaCode(const char *filename);
|
||||||
void FCEU_ReloadLuaCode();
|
void FCEU_ReloadLuaCode();
|
||||||
|
@ -40,15 +76,11 @@ int FCEU_LuaRerecordCountSkip();
|
||||||
void FCEU_LuaGui(uint8 *XBuf);
|
void FCEU_LuaGui(uint8 *XBuf);
|
||||||
void FCEU_LuaUpdatePalette();
|
void FCEU_LuaUpdatePalette();
|
||||||
|
|
||||||
|
struct lua_State* FCEU_GetLuaState();
|
||||||
char* FCEU_GetLuaScriptName();
|
char* FCEU_GetLuaScriptName();
|
||||||
|
|
||||||
// And some interesting REVERSE declarations!
|
// And some interesting REVERSE declarations!
|
||||||
char *FCEU_GetFreezeFilename(int slot);
|
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
|
#endif
|
||||||
|
|
|
@ -21,6 +21,7 @@ extern "C"
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
|
#include <lstate.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
@ -172,6 +173,8 @@ static const char* luaCallIDStrings [] =
|
||||||
"CALL_BEFOREEMULATION",
|
"CALL_BEFOREEMULATION",
|
||||||
"CALL_AFTEREMULATION",
|
"CALL_AFTEREMULATION",
|
||||||
"CALL_BEFOREEXIT",
|
"CALL_BEFOREEXIT",
|
||||||
|
"CALL_BEFORESAVE",
|
||||||
|
"CALL_AFTERLOAD",
|
||||||
};
|
};
|
||||||
|
|
||||||
//make sure we have the right number of strings
|
//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_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; }
|
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 == '_';
|
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 APPENDPRINT { int _n = snprintf(ptr, remaining,
|
||||||
#define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }
|
#define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }
|
||||||
static void toCStringConverter(lua_State* L, int i, char*& ptr, int& remaining)
|
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;
|
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);
|
||||||
|
|
||||||
//
|
if (!result)
|
||||||
//// Helper function to convert a savestate object to the filename it represents.
|
luaL_error(L, "object not a savestate object");
|
||||||
//static char *savestateobj2filename(lua_State *L, int offset) {
|
|
||||||
//
|
// Also check that the type entry is set
|
||||||
// // First we get the metatable of the indicated object
|
lua_getfield(L, -1, "__metatable");
|
||||||
// int result = lua_getmetatable(L, offset);
|
if (strcmp(lua_tostring(L,-1), "FCEU Savestate") != 0)
|
||||||
//
|
luaL_error(L, "object not a savestate object");
|
||||||
// if (!result)
|
lua_pop(L,1);
|
||||||
// luaL_error(L, "object not a savestate object");
|
|
||||||
//
|
// Now, get the field we want
|
||||||
// // Also check that the type entry is set
|
lua_getfield(L, -1, "filename");
|
||||||
// lua_getfield(L, -1, "__metatable");
|
|
||||||
// if (strcmp(lua_tostring(L,-1), "FCEU Savestate") != 0)
|
// Return it
|
||||||
// luaL_error(L, "object not a savestate object");
|
return lua_tostring(L, -1);
|
||||||
// lua_pop(L,1);
|
}
|
||||||
//
|
|
||||||
// // Now, get the field we want
|
|
||||||
// lua_getfield(L, -1, "filename");
|
|
||||||
//
|
|
||||||
// // Return it
|
|
||||||
// return (char *) lua_tostring(L, -1);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
|
|
||||||
// Helper function for garbage collection.
|
// Helper function for garbage collection.
|
||||||
static int savestate_gc(lua_State *L) {
|
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()
|
// int emu.framecount()
|
||||||
//
|
//
|
||||||
|
@ -3546,6 +4247,10 @@ static const struct luaL_reg savestatelib[] = {
|
||||||
{"persist", savestate_persist},
|
{"persist", savestate_persist},
|
||||||
{"load", savestate_load},
|
{"load", savestate_load},
|
||||||
|
|
||||||
|
{"registersave", savestate_registersave},
|
||||||
|
{"registerload", savestate_registerload},
|
||||||
|
{"loadscriptdata", savestate_loadscriptdata},
|
||||||
|
|
||||||
{NULL,NULL}
|
{NULL,NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
|
#ifdef _S9XLUA_H
|
||||||
|
#include "fceulua.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
//TODO - we really need some kind of global platform-specific options api
|
//TODO - we really need some kind of global platform-specific options api
|
||||||
#ifdef WIN32
|
#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 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 redoLS = false; //This will be true if a backupstate was loaded, meaning redoLoadState can be run
|
||||||
|
|
||||||
|
bool internalSaveLoad = false;
|
||||||
|
|
||||||
#define SFMDATA_SIZE (64)
|
#define SFMDATA_SIZE (64)
|
||||||
static SFORMAT SFMDATA[SFMDATA_SIZE];
|
static SFORMAT SFMDATA[SFMDATA_SIZE];
|
||||||
static int SFEXINDEX;
|
static int SFEXINDEX;
|
||||||
|
@ -436,7 +441,7 @@ bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel)
|
||||||
void FCEUSS_Save(const char *fname)
|
void FCEUSS_Save(const char *fname)
|
||||||
{
|
{
|
||||||
std::fstream* st = 0;
|
std::fstream* st = 0;
|
||||||
char *fn;
|
char fn[2048];
|
||||||
|
|
||||||
if(geniestage==1)
|
if(geniestage==1)
|
||||||
{
|
{
|
||||||
|
@ -447,11 +452,12 @@ void FCEUSS_Save(const char *fname)
|
||||||
if(fname) //If filename is given use it.
|
if(fname) //If filename is given use it.
|
||||||
{
|
{
|
||||||
st =FCEUD_UTF8_fstream(fname, "wb");
|
st =FCEUD_UTF8_fstream(fname, "wb");
|
||||||
|
strcpy(fn, fname);
|
||||||
}
|
}
|
||||||
else //Else, generate one
|
else //Else, generate one
|
||||||
{
|
{
|
||||||
//FCEU_PrintError("daCurrentState=%d",CurrentState);
|
//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
|
//backup existing savestate first
|
||||||
if (CheckFileExists(fn))
|
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
|
undoSS = false; //so backup made so lastSavestateMade does have a backup file, so no undo
|
||||||
|
|
||||||
st = FCEUD_UTF8_fstream(fn,"wb");
|
st = FCEUD_UTF8_fstream(fn,"wb");
|
||||||
free(fn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(st == NULL)
|
if(st == NULL)
|
||||||
|
@ -473,6 +478,32 @@ void FCEUSS_Save(const char *fname)
|
||||||
return;
|
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))
|
if(FCEUMOV_Mode(MOVIEMODE_INACTIVE))
|
||||||
FCEUSS_SaveMS(st,-1);
|
FCEUSS_SaveMS(st,-1);
|
||||||
else
|
else
|
||||||
|
@ -660,6 +691,7 @@ bool FCEUSS_LoadFP(std::istream* is, ENUM_SSLOADPARAMS params)
|
||||||
bool FCEUSS_Load(const char *fname)
|
bool FCEUSS_Load(const char *fname)
|
||||||
{
|
{
|
||||||
std::fstream* st;
|
std::fstream* st;
|
||||||
|
char fn[2048];
|
||||||
|
|
||||||
//mbg movie - this needs to be overhauled
|
//mbg movie - this needs to be overhauled
|
||||||
////this fixes read-only toggle problems
|
////this fixes read-only toggle problems
|
||||||
|
@ -676,12 +708,13 @@ bool FCEUSS_Load(const char *fname)
|
||||||
if(fname)
|
if(fname)
|
||||||
{
|
{
|
||||||
st=FCEUD_UTF8_fstream(fname, "rb");
|
st=FCEUD_UTF8_fstream(fname, "rb");
|
||||||
|
strcpy(fn, fname);
|
||||||
}
|
}
|
||||||
else
|
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");
|
st=FCEUD_UTF8_fstream(fn,"rb");
|
||||||
strcpy(lastLoadstateMade,fn.c_str());
|
strcpy(lastLoadstateMade,fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(st == NULL)
|
if(st == NULL)
|
||||||
|
@ -711,6 +744,27 @@ bool FCEUSS_Load(const char *fname)
|
||||||
SaveStateStatus[CurrentState]=1;
|
SaveStateStatus[CurrentState]=1;
|
||||||
}
|
}
|
||||||
delete st;
|
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
|
#ifdef WIN32
|
||||||
Update_RAM_Search(); // Update_RAM_Watch() is also called.
|
Update_RAM_Search(); // Update_RAM_Watch() is also called.
|
||||||
#endif
|
#endif
|
||||||
|
@ -997,7 +1051,9 @@ bool CheckBackupSaveStateExist()
|
||||||
void BackupLoadState()
|
void BackupLoadState()
|
||||||
{
|
{
|
||||||
string filename = GetBackupFileName();
|
string filename = GetBackupFileName();
|
||||||
|
internalSaveLoad = true;
|
||||||
FCEUSS_Save(filename.c_str());
|
FCEUSS_Save(filename.c_str());
|
||||||
|
internalSaveLoad = false;
|
||||||
undoLS = true;
|
undoLS = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1007,7 +1063,9 @@ void LoadBackup()
|
||||||
string filename = GetBackupFileName(); //Get backup filename
|
string filename = GetBackupFileName(); //Get backup filename
|
||||||
if (CheckBackupSaveStateExist())
|
if (CheckBackupSaveStateExist())
|
||||||
{
|
{
|
||||||
|
internalSaveLoad = true;
|
||||||
FCEUSS_Load(filename.c_str()); //Load it
|
FCEUSS_Load(filename.c_str()); //Load it
|
||||||
|
internalSaveLoad = false;
|
||||||
redoLS = true; //Flag redoLoadState
|
redoLS = true; //Flag redoLoadState
|
||||||
undoLS = false; //Flag that LoadBackup cannot be run again
|
undoLS = false; //Flag that LoadBackup cannot be run again
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue