From ff35d7118c0a61a472b74d87337ee62eac993ce1 Mon Sep 17 00:00:00 2001 From: Adrian Astley Date: Wed, 17 Sep 2014 15:49:44 -0500 Subject: SWORD25: Create a set of functions for manually serializing a double Since we can't assume IEEE. --- engines/sword25/util/double_serializer.cpp | 138 +++++++++++++++++++++++++++++ engines/sword25/util/double_serializer.h | 95 ++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 engines/sword25/util/double_serializer.cpp create mode 100644 engines/sword25/util/double_serializer.h diff --git a/engines/sword25/util/double_serializer.cpp b/engines/sword25/util/double_serializer.cpp new file mode 100644 index 0000000000..d7ba4f3052 --- /dev/null +++ b/engines/sword25/util/double_serializer.cpp @@ -0,0 +1,138 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/double_serializer.h" + +#include "common/scummsys.h" + + +namespace Util { + +SerializedDouble encodeDouble(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); + + SerializedDouble returnValue; + returnValue.significandOne = significandOne; // SignificandOne + returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + significandTwo; // SignificandTwo + returnValue.exponent = (int16)exponent; + return returnValue; +} + +double decodeDouble(SerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)value.exponent; + double expandedsignificandOne = (double)value.significandOne; + double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +uint64 encodeDouble_64(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the significand into the integer range + double shiftedsignificand = ldexp(abs(significand), 53); + + // Combine everything using the IEEE standard + uint64 uintsignificand = (uint64)shiftedsignificand; + return ((uint64)(value < 0 ? 1 : 0) << 63) | // Sign + ((uint64)(exponent + 1023) << 52) | // Exponent stored as an offset to 1023 + (uintsignificand & 0x000FFFFFFFFFFFFF); // significand with MSB inferred +} + +double decodeDouble_64(uint64 value) { + // Expand the exponent and significand + int exponent = (int)((value >> 52) & 0x7FF) - 1023; + double expandedsignificand = (double)(0x10000000000000 /* Inferred MSB */ | (value & 0x000FFFFFFFFFFFFF)); + + // Deflate the significand + int temp; + double significand = frexp(expandedsignificand, &temp); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value & 0x8000000000000000) == 0x8000000000000000) ? -returnValue : returnValue; +} + +CompactSerializedDouble encodeDouble_Compact(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 21)); + + CompactSerializedDouble returnValue; + returnValue.signAndSignificandOne = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + (significandOne & 0x7FFFFFFF); // significandOne with MSB inferred + // Exponent stored as an offset to 1023 + returnValue.exponentAndSignificandTwo = ((uint32)(exponent + 1023) << 21) | significandTwo; + + return returnValue; +} + +double decodeDouble_Compact(CompactSerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)(value.exponentAndSignificandTwo >> 21) - 1023; + double expandedsignificandOne = (double)(0x80000000 /* Inferred MSB */ | (value.signAndSignificandOne & 0x7FFFFFFF)); + double expandedsignificandTwo = (double)(value.exponentAndSignificandTwo & 0x1FFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandOne & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serializer.h b/engines/sword25/util/double_serializer.h new file mode 100644 index 0000000000..e90338c369 --- /dev/null +++ b/engines/sword25/util/double_serializer.h @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef DOUBLE_SERIALIZATION_H +#define DOUBLE_SERIALIZATION_H + +#include "common/types.h" + + +namespace Util { + +struct SerializedDouble { + uint32 significandOne; + uint32 signAndSignificandTwo; + int16 exponent; +}; + +struct CompactSerializedDouble { + uint32 signAndSignificandOne; + uint32 exponentAndSignificandTwo; +}; + +/** + * Encodes a double as two uint32 and a one int16 + * + * Supports denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +SerializedDouble encodeDouble(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble(SerializedDouble value); + +/** + * Encodes a double as a uint64 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +uint64 encodeDouble_64(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_64(uint64 value); + +/** + * Encodes a double as two uint32 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +CompactSerializedDouble encodeDouble_Compact(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_Compact(CompactSerializedDouble value); + +} // End of namespace Sword25 + +#endif -- cgit v1.2.3 From efcd6196eeaa2bff468bbed8d07040e60ca3c136 Mon Sep 17 00:00:00 2001 From: Adrian Astley Date: Wed, 17 Sep 2014 15:52:26 -0500 Subject: SWORD25: Create a function for serializing lua objects This function is very similar to the Pluto function. However, this code is much cleaner and is endian-safe --- engines/sword25/util/lua_serialization.h | 42 ++ engines/sword25/util/lua_serializer.cpp | 852 +++++++++++++++++++++++++++++++ 2 files changed, 894 insertions(+) create mode 100644 engines/sword25/util/lua_serialization.h create mode 100644 engines/sword25/util/lua_serializer.cpp diff --git a/engines/sword25/util/lua_serialization.h b/engines/sword25/util/lua_serialization.h new file mode 100644 index 0000000000..0f0c3bd0b2 --- /dev/null +++ b/engines/sword25/util/lua_serialization.h @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LUA_SERIALIZATION_H +#define LUA_SERIALIZATION_H + +#include "sword25/util/lua/lua.h" + + +namespace Common { +class WriteStream; +} + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +void serializeLua(lua_State *luaState, Common::WriteStream *writeStream); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp new file mode 100644 index 0000000000..b2bbca6be9 --- /dev/null +++ b/engines/sword25/util/lua_serializer.cpp @@ -0,0 +1,852 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_serialization.h" + +#include "sword25/util/double_serializer.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" + + +namespace Lua { + +#define NUMTYPES 9 + +static const char* typenames[] = { + "nil", + "boolean", + "lightuserdata", + "number", + "string", + "table", + "function", + "userdata", + "thread" +}; + +#define PERMANENT_TYPE 101 + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +static StkId getobject(lua_State *luaState, int stackpos) { + if(stackpos > 0) { + lua_assert(luaState->base+stackpos-1 < luaState->top); + return luaState->base+stackpos-1; + } else { + lua_assert(L->top-stackpos >= L->base); + return luaState->top+stackpos; + } +} + + +struct SerializationInfo { + lua_State *luaState; + Common::WriteStream *writeStream; + uint counter; +}; + +static void serializeObject(SerializationInfo *info); + +static void serializeBoolean(SerializationInfo *info); +static void serializeLightUserData(SerializationInfo *info); +static void serializeNumber(SerializationInfo *info); +static void serializeString(SerializationInfo *info); +static void serializeTable(SerializationInfo *info); +static void serializeFunction(SerializationInfo *info); +static void serializeThread(SerializationInfo *info); +static void serializeProto(SerializationInfo *info); +static void serializeUpValue(SerializationInfo *info); +static void serializeUserData(SerializationInfo *info); + + +void serializeLua(lua_State *luaState, Common::WriteStream *writeStream) { + SerializationInfo info; + info.luaState = luaState; + info.writeStream = writeStream; + info.counter = 0u; + + // The process starts with the lua stack as follows: + // >>>>> permTbl rootObj + // That's the table of permanents and the root object to be serialized + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 4); + assert(lua_gettop(luaState) == 2); + // And that the root isn't nil + assert(!lua_isnil(luaState, 2)); + + // Create a table to hold indexes of everything that's serialized + // This allows us to only serialize an object once + // Every other time, just reference the index + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl + + // Now we're going to make the table weakly keyed. This prevents the + // GC from visiting it and trying to mark things it doesn't want to + // mark in tables, e.g. upvalues. All objects in the table are + // a priori reachable, so it doesn't matter that we do this. + + // Create the metatable + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_pushstring(luaState, "__mode"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" + + lua_pushstring(luaState, "k"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k" + + lua_settable(luaState, 4); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_setmetatable(luaState, 3); + // >>>>> permTbl rootObj indexTbl + + // Swap the indexTable and the rootObj + lua_insert(luaState, 2); + // >>>>> permTbl indexTbl rootObj + + // Serialize the root recursively + serializeObject(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void serializeObject(SerializationInfo *info) { + // The stack can potentially have many things on it + // The object we want to serialize is the item on the top of the stack + // >>>>> permTbl indexTbl rootObj ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // If the object has already been written, don't write it again + // Instead write the index of the object from the indexTbl + + // Check the indexTbl + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_rawget(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj ?index? + + // If the index isn't nil, the object has already been written + if (!lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + + // Retrieve the index from the stack + uint *index = (uint *)lua_touserdata(info->luaState, -1); + + // Write out the index + info->writeStream->writeUint32LE(*index); + + // Pop the index off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + + // If the object itself is nil, then write out a zero as a placeholder + if (lua_isnil(info->luaState, -1)) { + info->writeStream->writeByte(0); + + return; + } + + // Add the object to the indexTbl + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint)); + *ref = ++(info->counter); + // >>>>> permTbl indexTbl rootObj ...... obj obj index + + lua_rawset(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj + + + // Write out the index + info->writeStream->writeUint32LE(info->counter); + + + // Objects that are in the permanents table are serialized in a special way + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey? + + if (!lua_isnil(info->luaState, -1)) { + // Write out the type + info->writeStream->writeSint32LE(PERMANENT_TYPE); + + // Serialize the key + serializeObject(info); + + // Pop the key off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Query the type of the object + int objType = lua_type(info->luaState, -1); + + // Write it out + info->writeStream->writeSint32LE(objType); + + // Serialize the object by its type + + switch (objType) { + case LUA_TBOOLEAN: + serializeBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + serializeNumber(info); + break; + case LUA_TSTRING: + serializeString(info); + break; + case LUA_TTABLE: + serializeTable(info); + break; + case LUA_TFUNCTION: + serializeFunction(info); + break; + case LUA_TTHREAD: + serializeThread(info); + break; + case LUA_TPROTO: + serializeProto(info); + break; + case LUA_TUPVAL: + serializeUpValue(info); + break; + case LUA_TUSERDATA: + serializeUserData(info); + break; + default: + assert(0); + } +} + +static void serializeBoolean(SerializationInfo *info) { + int value = lua_toboolean(info->luaState, -1); + + info->writeStream->writeSint32LE(value); +} + +static void serializeNumber(SerializationInfo *info) { + lua_Number value = lua_tonumber(info->luaState, -1); + +#if 1 + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); +#else + // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. + // Also, I don't know if we can assume all compilers use IEEE double + // Therefore, I have chosen to store the double as a string. + Common::String buffer = Common::String::format("%f", value); + + info->writeStream->write(buffer.c_str(), buffer.size()); +#endif + +} + +static void serializeString(SerializationInfo *info) { + // Hard cast to a uint32 to force size_t to an explicit size + // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems + uint32 length = static_cast(lua_strlen(info->luaState, -1)); + info->writeStream->writeUint32LE(length); + + const char* str = lua_tostring(info->luaState, -1); + info->writeStream->write(str, length); +} + +/* Choose whether to do a regular or special persistence based on an object's + * metatable. "default" is whether the object, if it doesn't have a __persist + * entry, is literally persistable or not. + * Pushes the unpersist closure and returns true if special persistence is + * used. */ +static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 4); + + // Check whether we should persist literally, or via the __persist metafunction + if (!lua_getmetatable(info->luaState, -1)) { + if (defaction) { + // Write out a flag declaring that the metatable doesn't exist + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + } + } + + // >>>>> permTbl indexTbl ...... obj metaTbl + lua_pushstring(info->luaState, "__persist"); + // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist" + + lua_rawget(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist? + + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl nil + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + if (defaction) { + // Write out a flag declaring there is no persistence metafunction + info->writeStream->writeSint32LE(0); + + return 0; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return 0; /* not reached */ + } + + } else if (lua_isboolean(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl bool + if (lua_toboolean(info->luaState, -1)) { + // Write out a flag declaring such + info->writeStream->writeSint32LE(0); + + // >>>>> permTbl indexTbl ...... obj metaTbl true */ + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return false; + } else { + lua_pushstring(info->luaState, "Metatable forbade persistence"); + lua_error(info->luaState); + + return false; /* not reached */ + } + } else if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl __persist + lua_pushvalue(info->luaState, -3); + // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj + + // >>>>> permTbl indexTbl ...... obj metaTbl ?func? + + if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist function did not return a function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl func + + // Write out a flag that the function exists + info->writeStream->writeSint32LE(1); + + // Serialize the function + serializeObject(info); + + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return true; +} + +static void serializeTable(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... tbl + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 1)) { + return; + } + + // >>>>> permTbl indexTbl ...... tbl + + // First, serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl nil + + // Now, persist all k/v pairs + while (lua_next(info->luaState, -2)) { + // >>>>> permTbl indexTbl ...... tbl k v */ + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl k v k */ + + // Serialize the key + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k */ + } + + // >>>>> permTbl indexTbl ...... tbl + + // Terminate the list with a nil + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +static void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +static void pushUpVal(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +static void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +static void serializeFunction(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... func + Closure *cl = clvalue(getobject(info->luaState, -1)); + lua_checkstack(info->luaState, 2); + + if (cl->c.isC) { + /* It's a C function. For now, we aren't going to allow + * persistence of C closures, even if the "C proto" is + * already in the permanents table. */ + lua_pushstring(info->luaState, "Attempt to persist a C function"); + lua_error(info->luaState); + } else { + // It's a Lua closure + + // We don't really _NEED_ the number of upvals, but it'll simplify things a bit + info->writeStream->writeByte(cl->l.p->nups); + + // Serialize the prototype + pushProto(info->luaState, cl->l.p); + // >>>>> permTbl indexTbl ...... func proto */ + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // Serialize upvalue values (not the upvalue objects themselves) + for (byte i=0; il.p->nups; i++) { + // >>>>> permTbl indexTbl ...... func + pushUpVal(info->luaState, cl->l.upvals[i]); + // >>>>> permTbl indexTbl ...... func upval + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func + + // Serialize function environment + lua_getfenv(info->luaState, -1); + // >>>>> permTbl indexTbl ...... func fenv + + if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) { + // Function has the default fenv + + // >>>>> permTbl indexTbl ...... func _G + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + } + + // >>>>> permTbl indexTbl ...... func fenv/nil + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +/* Appends one stack to another stack, but the stack is reversed in the process */ +static size_t appendStackToStack_rev(lua_State *from, lua_State *to) { + for (StkId id = from->top - 1; id >= from->stack; --id) { + setobj2s(to, to->top, id); + to->top++; + } + + return from->top - from->stack; +} + +static void serializeThread(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... thread + lua_State *threadState = lua_tothread(info->luaState, -1); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, threadState->top - threadState->stack + 1); + + if (info->luaState == threadState) { + lua_pushstring(info->luaState, "Can't persist currently running thread"); + lua_error(info->luaState); + return; /* not reached */ + } + + // Persist the stack + + // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint stackSize = static_cast(appendStackToStack_rev(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + serializeObject(info); + + lua_pop(info->luaState, 1); + } + + // >>>>> permTbl indexTbl ...... thread + + // Now, serialize the CallInfo stack + + // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 numFrames = static_cast((threadState->ci - threadState->base_ci) + 1); + info->writeStream->writeUint32LE(numFrames); + + for (uint32 i = 0; i < numFrames; i++) { + CallInfo *ci = threadState->base_ci + i; + + // Same argument as above about truncation + uint32 stackBase = static_cast(ci->base - threadState->stack); + uint32 stackFunc = static_cast(ci->func - threadState->stack); + uint32 stackTop = static_cast(ci->top - threadState->stack); + + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + info->writeStream->writeUint32LE(stackTop); + + info->writeStream->writeSint32LE(ci->nresults); + + uint32 savedpc = (ci != threadState->base_ci) ? static_cast(ci->savedpc - ci_func(ci)->l.p->code) : 0u; + info->writeStream->writeUint32LE(savedpc); + } + + + // Serialize the state's other parameters, with the exception of upval stuff + + assert(threadState->nCcalls <= 1); + info->writeStream->writeByte(threadState->status); + + // Same argument as above about truncation + uint32 stackBase = static_cast(threadState->base - threadState->stack); + uint32 stackFunc = static_cast(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast(threadState->errfunc); + info->writeStream->writeUint32LE(stackOffset); + + // Finally, record upvalues which need to be reopened + // See the comment above serializeUpVal() for why we do this + + UpVal *upVal; + + // >>>>> permTbl indexTbl ...... thread + for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { + upVal = gco2uv(gcObject); + + /* Make sure upvalue is really open */ + assert(upVal->v != &upVal->u.value); + + pushUpVal(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread +} + +static void serializeProto(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... proto + Proto *proto = gco2p(getobject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Serialize constant refs */ + info->writeStream->writeSint32LE(proto->sizek); + + for (int i = 0; i < proto->sizek; ++i) { + pushObject(info->luaState, &proto->k[i]); + // >>>>> permTbl indexTbl ...... proto const + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize inner Proto refs + info->writeStream->writeSint32LE(proto->sizep); + + for (int i = 0; i < proto->sizep; ++i) + { + pushProto(info->luaState, proto->p[i]); + // >>>>> permTbl indexTbl ...... proto subProto */ + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast(sizeof(Instruction) * proto->sizecode); + info->writeStream->write(proto->code, len); + + + // Serialize upvalue names + info->writeStream->writeSint32LE(proto->sizeupvalues); + + for (int i = 0; i < proto->sizeupvalues; ++i) + { + pushString(info->luaState, proto->upvalues[i]); + // >>>>> permTbl indexTbl ...... proto str + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + + // Serialize local variable infos + info->writeStream->writeSint32LE(proto->sizelocvars); + + for (int i = 0; i < proto->sizelocvars; ++i) { + pushString(info->luaState, proto->locvars[i].varname); + // >>>>> permTbl indexTbl ...... proto str + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + info->writeStream->writeSint32LE(proto->locvars[i].startpc); + info->writeStream->writeSint32LE(proto->locvars[i].endpc); + } + + + // Serialize source string + pushString(info->luaState, proto->source); + // >>>>> permTbl indexTbl ...... proto sourceStr + + serializeObject(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + uint32 len = static_cast(sizeof(int) * proto->sizelineinfo); + info->writeStream->write(proto->lineinfo, len); + } + + // Serialize linedefined and lastlinedefined + info->writeStream->writeSint32LE(proto->linedefined); + info->writeStream->writeSint32LE(proto->lastlinedefined); + + + // Serialize misc values + info->writeStream->writeByte(proto->nups); + info->writeStream->writeByte(proto->numparams); + info->writeStream->writeByte(proto->is_vararg); + info->writeStream->writeByte(proto->maxstacksize); +} + +/* Upvalues are tricky. Here's why. + * + * A particular upvalue may be either "open", in which case its member v + * points into a thread's stack, or "closed" in which case it points to the + * upvalue itself. An upvalue is closed under any of the following conditions: + * -- The function that initially declared the variable "local" returns + * -- The thread in which the closure was created is garbage collected + * + * To make things wackier, just because a thread is reachable by Lua doesn't + * mean it's in our root set. We need to be able to treat an open upvalue + * from an unreachable thread as a closed upvalue. + * + * The solution: + * (a) For the purposes of serializing, don't indicate whether an upvalue is + * closed or not. + * (b) When unserializing, pretend that all upvalues are closed. + * (c) When serializing, persist all open upvalues referenced by a thread + * that is persisted, and tag each one with the corresponding stack position + * (d) When unserializing, "reopen" each of these upvalues as the thread is + * unserialized + */ +static void serializeUpValue(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... upval + assert(ttype(getobject(info->luaState, -1)) == LUA_TUPVAL); + UpVal *upValue = gco2uv(getobject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // We can't permit the upValue to linger around on the stack, as Lua + // will bail if its GC finds it. + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... + + pushObject(info->luaState, upValue->v); + // >>>>> permTbl indexTbl ...... obj + + serializeObject(info); + // >>>>> permTbl indexTbl ...... obj +} + +static void serializeUserData(SerializationInfo *info) { + // >>>>> permTbl rootObj ...... udata + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 0)) { + return; + } + + // Use literal persistence + + // Hard cast to a uint32 length + // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems + uint32 length = static_cast(uvalue(getobject(info->luaState, -1))->len); + info->writeStream->writeUint32LE(length); + + info->writeStream->write(lua_touserdata(info->luaState, -1), length); + + // Serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl rootObj ...... udata metaTbl/nil + serializeObject(info); + + lua_pop(info->luaState, 1); + /* perms reftbl ... udata */ +} + + +} // End of namespace Lua -- cgit v1.2.3 From dedfd7aa84360b5950b653109b3679c7f469aebc Mon Sep 17 00:00:00 2001 From: Adrian Astley Date: Fri, 19 Dec 2014 13:38:26 -0600 Subject: SWORD25: Move common functions to their own set of files so they can be shared --- engines/sword25/util/lua_serialization_util.cpp | 77 +++++++++++++++++++++ engines/sword25/util/lua_serialization_util.h | 43 ++++++++++++ engines/sword25/util/lua_serializer.cpp | 91 +++++-------------------- 3 files changed, 138 insertions(+), 73 deletions(-) create mode 100644 engines/sword25/util/lua_serialization_util.cpp create mode 100644 engines/sword25/util/lua_serialization_util.h diff --git a/engines/sword25/util/lua_serialization_util.cpp b/engines/sword25/util/lua_serialization_util.cpp new file mode 100644 index 0000000000..80009aff60 --- /dev/null +++ b/engines/sword25/util/lua_serialization_util.cpp @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_serialization_util.h" + +#include "common/scummsys.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" + + +namespace Lua { + +void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +void pushUpValue(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +StkId getObject(lua_State *luaState, int stackpos) { + if(stackpos > 0) { + lua_assert(luaState->base+stackpos-1 < luaState->top); + return luaState->base+stackpos-1; + } else { + lua_assert(L->top-stackpos >= L->base); + return luaState->top+stackpos; + } +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_serialization_util.h b/engines/sword25/util/lua_serialization_util.h new file mode 100644 index 0000000000..6c55d0dd53 --- /dev/null +++ b/engines/sword25/util/lua_serialization_util.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LUA_SERIALIZATION_UTIL_H +#define LUA_SERIALIZATION_UTIL_H + + +struct lua_State; + +#include "lua/lobject.h" + +typedef TValue *StkId; + +namespace Lua { + +void pushObject(lua_State *luaState, TValue *obj); +void pushProto(lua_State *luaState, Proto *proto); +void pushUpValue(lua_State *luaState, UpVal *upval); +void pushString(lua_State *luaState, TString *str); +StkId getObject(lua_State *luaState, int stackpos); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp index b2bbca6be9..8c61383fba 100644 --- a/engines/sword25/util/lua_serializer.cpp +++ b/engines/sword25/util/lua_serializer.cpp @@ -23,44 +23,19 @@ #include "sword25/util/lua_serialization.h" #include "sword25/util/double_serializer.h" +#include "sword25/util/lua_serialization_util.h" #include "common/stream.h" #include "lua/lobject.h" #include "lua/lstate.h" +#include "lua/lgc.h" namespace Lua { -#define NUMTYPES 9 - -static const char* typenames[] = { - "nil", - "boolean", - "lightuserdata", - "number", - "string", - "table", - "function", - "userdata", - "thread" -}; - #define PERMANENT_TYPE 101 -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -static StkId getobject(lua_State *luaState, int stackpos) { - if(stackpos > 0) { - lua_assert(luaState->base+stackpos-1 < luaState->top); - return luaState->base+stackpos-1; - } else { - lua_assert(L->top-stackpos >= L->base); - return luaState->top+stackpos; - } -} - - struct SerializationInfo { lua_State *luaState; Common::WriteStream *writeStream; @@ -70,7 +45,6 @@ struct SerializationInfo { static void serializeObject(SerializationInfo *info); static void serializeBoolean(SerializationInfo *info); -static void serializeLightUserData(SerializationInfo *info); static void serializeNumber(SerializationInfo *info); static void serializeString(SerializationInfo *info); static void serializeTable(SerializationInfo *info); @@ -319,13 +293,15 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { // Check whether we should persist literally, or via the __persist metafunction if (!lua_getmetatable(info->luaState, -1)) { if (defaction) { - // Write out a flag declaring that the metatable doesn't exist + // Write out a flag declaring that the object isn't special and should be persisted normally info->writeStream->writeSint32LE(0); return false; } else { lua_pushstring(info->luaState, "Type not literally persistable by default"); lua_error(info->luaState); + + return false; // Not reached } } @@ -342,21 +318,21 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { // >>>>> permTbl indexTbl ...... obj if (defaction) { - // Write out a flag declaring there is no persistence metafunction + // Write out a flag declaring that the object isn't special and should be persisted normally info->writeStream->writeSint32LE(0); - return 0; + return false; } else { lua_pushstring(info->luaState, "Type not literally persistable by default"); lua_error(info->luaState); - return 0; /* not reached */ + return false; // Return false } } else if (lua_isboolean(info->luaState, -1)) { // >>>>> permTbl indexTbl ...... obj metaTbl bool if (lua_toboolean(info->luaState, -1)) { - // Write out a flag declaring such + // Write out a flag declaring that the object isn't special and should be persisted normally info->writeStream->writeSint32LE(0); // >>>>> permTbl indexTbl ...... obj metaTbl true */ @@ -368,7 +344,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { lua_pushstring(info->luaState, "Metatable forbade persistence"); lua_error(info->luaState); - return false; /* not reached */ + return false; // Not reached } } else if (!lua_isfunction(info->luaState, -1)) { lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); @@ -460,40 +436,9 @@ static void serializeTable(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... tbl } -static void pushObject(lua_State *luaState, TValue *obj) { - setobj2s(luaState, luaState->top, obj); - - api_check(luaState, luaState->top < luaState->ci->top); - luaState->top++; -} - -static void pushProto(lua_State *luaState, Proto *proto) { - TValue obj; - setptvalue(luaState, &obj, proto); - - pushObject(luaState, &obj); -} - -static void pushUpVal(lua_State *luaState, UpVal *upval) { - TValue obj; - - obj.value.gc = cast(GCObject *, upval); - obj.tt = LUA_TUPVAL; - checkliveness(G(L), obj); - - pushObject(luaState, &obj); -} - -static void pushString(lua_State *luaState, TString *str) { - TValue o; - setsvalue(luaState, &o, str); - - pushObject(luaState, &o); -} - static void serializeFunction(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... func - Closure *cl = clvalue(getobject(info->luaState, -1)); + Closure *cl = clvalue(getObject(info->luaState, -1)); lua_checkstack(info->luaState, 2); if (cl->c.isC) { @@ -520,7 +465,7 @@ static void serializeFunction(SerializationInfo *info) { // Serialize upvalue values (not the upvalue objects themselves) for (byte i=0; il.p->nups; i++) { // >>>>> permTbl indexTbl ...... func - pushUpVal(info->luaState, cl->l.upvals[i]); + pushUpValue(info->luaState, cl->l.upvals[i]); // >>>>> permTbl indexTbl ...... func upval serializeObject(info); @@ -580,7 +525,7 @@ static void serializeThread(SerializationInfo *info) { // Persist the stack // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems - uint stackSize = static_cast(appendStackToStack_rev(threadState, info->luaState)); + uint32 stackSize = static_cast(appendStackToStack_rev(threadState, info->luaState)); info->writeStream->writeUint32LE(stackSize); // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ @@ -644,7 +589,7 @@ static void serializeThread(SerializationInfo *info) { /* Make sure upvalue is really open */ assert(upVal->v != &upVal->u.value); - pushUpVal(info->luaState, upVal); + pushUpValue(info->luaState, upVal); // >>>>> permTbl indexTbl ...... thread upVal serializeObject(info); @@ -670,7 +615,7 @@ static void serializeThread(SerializationInfo *info) { static void serializeProto(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... proto - Proto *proto = gco2p(getobject(info->luaState, -1)->value.gc); + Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); @@ -797,8 +742,8 @@ static void serializeProto(SerializationInfo *info) { */ static void serializeUpValue(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... upval - assert(ttype(getobject(info->luaState, -1)) == LUA_TUPVAL); - UpVal *upValue = gco2uv(getobject(info->luaState, -1)->value.gc); + assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); + UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); @@ -831,7 +776,7 @@ static void serializeUserData(SerializationInfo *info) { // Hard cast to a uint32 length // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems - uint32 length = static_cast(uvalue(getobject(info->luaState, -1))->len); + uint32 length = static_cast(uvalue(getObject(info->luaState, -1))->len); info->writeStream->writeUint32LE(length); info->writeStream->write(lua_touserdata(info->luaState, -1), length); -- cgit v1.2.3 From de20880d9d3dbe9ebbc26848d7a672c104495aeb Mon Sep 17 00:00:00 2001 From: Adrian Astley Date: Fri, 19 Dec 2014 13:41:58 -0600 Subject: SWORD25: Re-write the pluto unserializing function(s) --- engines/sword25/util/lua_serialization.h | 2 + engines/sword25/util/lua_unserializer.cpp | 1007 +++++++++++++++++++++++++++++ 2 files changed, 1009 insertions(+) create mode 100644 engines/sword25/util/lua_unserializer.cpp diff --git a/engines/sword25/util/lua_serialization.h b/engines/sword25/util/lua_serialization.h index 0f0c3bd0b2..549ea7968d 100644 --- a/engines/sword25/util/lua_serialization.h +++ b/engines/sword25/util/lua_serialization.h @@ -28,6 +28,7 @@ namespace Common { class WriteStream; +class ReadStream; } @@ -36,6 +37,7 @@ namespace Lua { #define PERMANENT_TYPE 101 void serializeLua(lua_State *luaState, Common::WriteStream *writeStream); +void unserializeLua(lua_State *luaState, Common::ReadStream *readStream); } // End of namespace Lua diff --git a/engines/sword25/util/lua_unserializer.cpp b/engines/sword25/util/lua_unserializer.cpp new file mode 100644 index 0000000000..c561a3d99f --- /dev/null +++ b/engines/sword25/util/lua_unserializer.cpp @@ -0,0 +1,1007 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_serialization.h" + +#include "sword25/util/double_serializer.h" +#include "sword25/util/lua_serialization_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +struct UnSerializationInfo { + lua_State *luaState; + Common::ReadStream *readStream; +}; + +static void unserializeObject(UnSerializationInfo *info); + +static void unserializeBoolean(UnSerializationInfo *info); +static void unserializeNumber(UnSerializationInfo *info); +static void unserializeString(UnSerializationInfo *info); +static void unserializeTable(UnSerializationInfo *info, int index); +static void unserializeFunction(UnSerializationInfo *info, int index); +static void unserializeThread(UnSerializationInfo *info, int index); +static void unserializeProto(UnSerializationInfo *info, int index); +static void unserializeUpValue(UnSerializationInfo *info, int index); +static void unserializeUserData(UnSerializationInfo *info, int index); +static void unserializePermanent(UnSerializationInfo *info, int index); + + +void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { + UnSerializationInfo info; + info.luaState = luaState; + info.readStream = readStream; + + // The process starts with the lua stack as follows: + // >>>>> permTbl + // That's the table of permanents + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 3); + + // Create a table to hold indexes of everything thats already been read + lua_newtable(luaState); + // >>>>> permTbl indexTbl + + // Prevent garbage collection while we unserialize + lua_gc(luaState, LUA_GCSTOP, 0); + + // Unserialize the root object + unserializeObject(&info); + // >>>>> permTbl indexTbl rootObj + + // Re-start garbage collection + lua_gc(luaState, LUA_GCRESTART, 0); + + // Remove the indexTbl + lua_replace(luaState, 2); + // >>>>> permTbl rootObj +} + +/* The object is left on the stack. This is primarily used by unpersist, but + * may be used by GCed objects that may incur cycles in order to preregister + * the object. */ +static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... obj index + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj index obj + + // Push the k/v pair into the indexTbl + lua_settable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj +} + +static void unserializeObject(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte isARealValue = info->readStream->readByte(); + if(isARealValue) { + int index = info->readStream->readSint32LE(); + int type = info->readStream->readSint32LE(); + + switch(type) { + case LUA_TBOOLEAN: + unserializeBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + unserializeNumber(info); + break; + case LUA_TSTRING: + unserializeString(info); + break; + case LUA_TTABLE: + unserializeTable(info, index); + break; + case LUA_TFUNCTION: + unserializeFunction(info, index); + break; + case LUA_TTHREAD: + unserializeThread(info, index); + break; + case LUA_TPROTO: + unserializeProto(info, index); + break; + case LUA_TUPVAL: + unserializeUpValue(info, index); + break; + case LUA_TUSERDATA: + unserializeUserData(info, index); + break; + case PERMANENT_TYPE: + unserializePermanent(info, index); + break; + default: + assert(0); + } + + + // >>>>> permTbl indexTbl ...... obj + assert(lua_type(info->luaState, -1) == type || + type == PERMANENT_TYPE || + // Remember, upvalues get a special dispensation, as described in boxUpValue + (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); + + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... obj + } else { + int index = info->readStream->readSint32LE(); + + if(index == 0) { + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... nil + } else { + // Fetch the object from the indexTbl + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... index + + lua_gettable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... ?obj? + + assert(!lua_isnil(info->luaState, -1)); + } + // >>>>> permTbl indexTbl ...... obj/nil + } + + // >>>>> permTbl indexTbl ...... obj/nil +} + +static void unserializeBoolean(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int value = info->readStream->readSint32LE(); + + lua_pushboolean(info->luaState, value); + // >>>>> permTbl indexTbl ...... bool +} + +static void unserializeNumber(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // Read the serialized double + Util::SerializedDouble serializedValue; + serializedValue.significandOne = info->readStream->readUint32LE(); + serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); + serializedValue.exponent = info->readStream->readSint16LE(); + + lua_Number value = Util::decodeDouble(serializedValue); + + lua_pushnumber(info->luaState, value); + // >>>>> permTbl indexTbl ...... num +} + +static void unserializeString(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + uint32 length = info->readStream->readUint32LE(); + char *string = new char[length]; + + info->readStream->read(string, length); + lua_pushlstring(info->luaState, string, length); + + // >>>>> permTbl indexTbl ...... string + + delete[] string; +} + +static void unserializeSpecialTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + unserializeObject(info); + + // >>>>> permTbl indexTbl ...... spfunc + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void unserializeLiteralTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Preregister table for handling of cycles + lua_newtable(info->luaState); + + // >>>>> permTbl indexTbl ...... tbl + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + + // Unserialize metatable + unserializeObject(info); + // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? + + if (lua_istable(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl metaTbl + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl + } else { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + } + // >>>>> permTbl indexTbl ...... tbl + + + while (1) { + // >>>>> permTbl indexTbl ...... tbl + unserializeObject(info); + // >>>>> permTbl indexTbl ...... tbl key/nil + + // The table serialization is nil terminated + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + break; + } + + // >>>>> permTbl indexTbl ...... tbl key + unserializeObject(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unserializeTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int isSpecial = info->readStream->readSint32LE(); + + if (isSpecial) { + unserializeSpecialTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } else { + unserializeLiteralTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } +} + + + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *newLClosure(lua_State *luaState, byte numUpValues, Table *env) { + Closure *newClosure = (Closure *)lua_malloc(luaState, sizeLclosure(numUpValues)); + + lua_linkObjToGC(luaState, obj2gco(newClosure), LUA_TFUNCTION); + + newClosure->l.isC = 0; + newClosure->l.env = env; + newClosure->l.nupvalues = numUpValues; + + while (numUpValues--) { + newClosure->l.upvals[numUpValues] = NULL; + } + + return newClosure; +} + +static void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +static Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +static UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +static void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +void unserializeFunction(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte numUpValues = info->readStream->readByte(); + + LClosure *lclosure = (LClosure *)newLClosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + pushClosure(info->luaState, (Closure *)lclosure); + // >>>>> permTbl indexTbl ...... func + + // Put *some* proto in the closure, before the GC can find it + lclosure->p = makeFakeProto(info->luaState, numUpValues); + + //Also, we need to temporarily fill the upvalues + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + + for(byte i = 0; i < numUpValues; ++i) { + lclosure->upvals[i] = createUpValue(info->luaState, -1); + } + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // I can't see offhand how a function would ever get to be self- + // referential, but just in case let's register it early + registerObjectInIndexTable(info, index); + + // Now that it's safe, we can get the real proto + unserializeObject(info); + // >>>>> permTbl indexTbl ...... func proto + + lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + for(byte i = 0; i < numUpValues; ++i) { + // >>>>> permTbl indexTbl ...... func + unserializeObject(info); + // >>>>> permTbl indexTbl ...... func func2 + + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... func upValue + lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // Finally, the fenv + unserializeObject(info); + + // >>>>> permTbl indexTbl ...... func ?fenv/nil? + if(!lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... func fenv + lua_setfenv(info->luaState, -2); + // >>>>> permTbl indexTbl ...... func + } else { + // >>>>> permTbl indexTbl ...... func nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func +} + +static void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void unboxUpVal(lua_State *luaState) { + // >>>>> ... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)(&getObject(luaState, -1)->value.gc->cl); + uv = lcl->upvals[0]; + lua_pop(luaState, 1); + // >>>>> ... + pushUpValue(luaState, uv); + // >>>>> ... upVal +} + +/* Does basically the opposite of luaC_link(). + * Right now this function is rather inefficient; it requires traversing the + * entire root GC set in order to find one object. If the GC list were doubly + * linked this would be much easier, but there's no reason for Lua to have + * that. */ +static void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if(G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while(prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +void unserializeThread(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + lua_State *L2; + uint32 stacklimit = 0; + + L2 = lua_newthread(info->luaState); + lua_checkstack(info->luaState, 3); + + // L1: permTbl indexTbl ...... thread + // L2: (empty) + registerObjectInIndexTable(info, index); + + // First, deserialize the object stack + uint32 stackSize = info->readStream->readUint32LE(); + lua_growstack(info->luaState, (int)stackSize); + + // Make sure that the first stack element (a nil, representing + // the imaginary top-level C function) is written to the very, + // very bottom of the stack + L2->top--; + for(uint32 i = 0; i < stackSize; ++i) { + unserializeObject(info); + // L1: permTbl indexTbl ...... thread obj* + } + + lua_xmove(info->luaState, L2, stackSize); + // L1: permTbl indexTbl ...... thread + // L2: obj* + + // Hereafter, stacks refer to L1 + + + // Now, deserialize the CallInfo stack + + uint32 numFrames = info->readStream->readUint32LE(); + + lua_reallocCallInfo(L2, numFrames*2); + for(uint32 i = 0; i < numFrames; ++i) { + CallInfo *ci = L2->base_ci + i; + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stackfunc = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + ci->nresults = info->readStream->readSint32LE(); + + uint32 savedpc = info->readStream->readUint32LE(); + + if (stacklimit < stacktop) { + stacklimit = stacktop; + } + + ci->base = L2->stack + stackbase; + ci->func = L2->stack + stackfunc; + ci->top = L2->stack + stacktop; + ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; + ci->tailcalls = 0; + + // Update the pointer each time, to keep the GC happy + L2->ci = ci; + } + + // >>>>> permTbl indexTbl ...... thread + // Deserialize the state's other parameters, with the exception of upval stuff + + L2->savedpc = L2->ci->savedpc; + L2->status = info->readStream->readByte(); + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + + L2->errfunc = info->readStream->readUint32LE(); + + L2->base = L2->stack + stackbase; + L2->top = L2->stack + stacktop; + + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal* uv; + GCObject **nextslot = &L2->openupval; + global_State *g = G(L2); + + while (true) { + unserializeObject(info); + // >>>>> permTbl indexTbl ...... thread upVal/nil + + // The list is terminated by a nil + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... thread nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + break; + } + + // >>>>> permTbl indexTbl ...... thread boxedUpVal + unboxUpVal(info->luaState); + // >>>>> permTbl indexTbl ...... thread boxedUpVal + + uv = &(getObject(info->luaState, -1)->value.gc->uv); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + uint32 stackpos = info->readStream->readUint32LE(); + uv->v = L2->stack + stackpos; + + GCUnlink(info->luaState, (GCObject *)uv); + + uv->marked = luaC_white(g); + *nextslot = (GCObject *)uv; + nextslot = &uv->next; + uv->u.l.prev = &G(L2)->uvhead; + uv->u.l.next = G(L2)->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + G(L2)->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + } + *nextslot = NULL; + + // The stack must be valid at least to the highest value among the CallInfos + // 'top' and the values up to there must be filled with 'nil' + lua_checkstack(L2, (int)stacklimit); + for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { + setnilvalue(o); + } +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +void unserializeProto(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // We have to be careful. The GC expects a lot out of protos. In particular, we need + // to give the function a valid string for its source, and valid code, even before we + // actually read in the real code. + TString *source = lua_newlstr(info->luaState, "", 0); + Proto *p = lua_newproto(info->luaState); + p->source = source; + p->sizecode=1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->maxstacksize = 2; + p->sizek = 0; + p->sizep = 0; + + lua_checkstack(info->luaState, 2); + + pushProto(info->luaState, p); + // >>>>> permTbl indexTbl ...... proto + + // We don't need to register early, since protos can never ever be + // involved in cyclic references + + // Read in constant references + int sizek = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); + for(int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserializeObject(info); + // >>>>> permTbl indexTbl ...... proto k + + setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); + p->sizek++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + // Read in sub-proto references + + int sizep = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); + for(int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserializeObject(info); + // >>>>> permTbl indexTbl ...... proto subproto + + p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; + p->sizep++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + + // Read in code + p->sizecode = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); + info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); + + + /* Read in upvalue names */ + p->sizeupvalues = info->readStream->readSint32LE(); + if (p->sizeupvalues) { + lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); + for(int i = 0; i < p->sizeupvalues; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserializeObject(info); + // >>>>> permTbl indexTbl ...... proto str + + p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in local variable infos + p->sizelocvars = info->readStream->readSint32LE(); + if (p->sizelocvars) { + lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); + for(int i = 0; i < p->sizelocvars; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserializeObject(info); + // >>>>> permTbl indexTbl ...... proto str + + p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + p->locvars[i].startpc = info->readStream->readSint32LE(); + p->locvars[i].endpc = info->readStream->readSint32LE(); + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in source string + unserializeObject(info); + // >>>>> permTbl indexTbl ...... proto sourceStr + + p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Read in line numbers + p->sizelineinfo = info->readStream->readSint32LE(); + if (p->sizelineinfo) { + lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); + info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); + } + + + /* Read in linedefined and lastlinedefined */ + p->linedefined = info->readStream->readSint32LE(); + p->lastlinedefined = info->readStream->readSint32LE(); + + // Read in misc values + p->nups = info->readStream->readByte(); + p->numparams = info->readStream->readByte(); + p->is_vararg = info->readStream->readByte(); + p->maxstacksize = info->readStream->readByte(); +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_link(luaState, obj2gco(c), LUA_TFUNCTION); + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +static UpVal *makeUpVal(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +/** + * The GC is not fond of finding upvalues in tables. We get around this + * during persistence using a weakly keyed table, so that the GC doesn't + * bother to mark them. This won't work in unpersisting, however, since + * if we make the values weak they'll be collected (since nothing else + * references them). Our solution, during unpersisting, is to represent + * upvalues as dummy functions, each with one upvalue. + */ +static void boxupval_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpVal(luaState, -1); + lua_pop(luaState, 1); +} + +static void boxupval_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + +void unserializeUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(upi->L, 2); + + boxupval_start(upi->L); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unserializeObject(info); + // >>>>> permTbl indexTbl ...... func obj + + boxupval_finish(upi->L); + // >>>>> permTbl indexTbl ...... func +} + +void unserializeUserData(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + int isspecial = info->readStream->readSint32LE(); + if(isspecial) { + unserializeObject(info); + // >>>>> permTbl indexTbl ...... specialFunc + + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... udata + } else { + uint32 length = info->readStream->readUint32LE(); + lua_newuserdata(info->luaState, length); + // >>>>> permTbl indexTbl ...... udata + registerObjectInIndexTable(info, index); + + info->readStream->read(lua_touserdata(upi->L, -1), length); + + unserializeObject(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(upi->L, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unserializePermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unserializeObject(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua -- cgit v1.2.3 From 2c7a87a4e3181f228c43b92ffb15ae47401f64a7 Mon Sep 17 00:00:00 2001 From: Adrian Astley Date: Fri, 19 Dec 2014 14:51:00 -0600 Subject: SWORD25: Fix code formatting ... with AStyle --- engines/sword25/util/lua_serialization_util.cpp | 10 +- engines/sword25/util/lua_serializer.cpp | 62 ++++++------ engines/sword25/util/lua_unserializer.cpp | 127 ++++++++++++------------ 3 files changed, 100 insertions(+), 99 deletions(-) diff --git a/engines/sword25/util/lua_serialization_util.cpp b/engines/sword25/util/lua_serialization_util.cpp index 80009aff60..80c5f86b20 100644 --- a/engines/sword25/util/lua_serialization_util.cpp +++ b/engines/sword25/util/lua_serialization_util.cpp @@ -65,12 +65,12 @@ void pushString(lua_State *luaState, TString *str) { /* A simple reimplementation of the unfortunately static function luaA_index. * Does not support the global table, registry, or upvalues. */ StkId getObject(lua_State *luaState, int stackpos) { - if(stackpos > 0) { - lua_assert(luaState->base+stackpos-1 < luaState->top); - return luaState->base+stackpos-1; + if (stackpos > 0) { + lua_assert(luaState->base + stackpos - 1 < luaState->top); + return luaState->base + stackpos - 1; } else { - lua_assert(L->top-stackpos >= L->base); - return luaState->top+stackpos; + lua_assert(L->top - stackpos >= L->base); + return luaState->top + stackpos; } } diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp index 8c61383fba..c6c5f99342 100644 --- a/engines/sword25/util/lua_serializer.cpp +++ b/engines/sword25/util/lua_serializer.cpp @@ -81,7 +81,7 @@ void serializeLua(lua_State *luaState, Common::WriteStream *writeStream) { // GC from visiting it and trying to mark things it doesn't want to // mark in tables, e.g. upvalues. All objects in the table are // a priori reachable, so it doesn't matter that we do this. - + // Create the metatable lua_newtable(luaState); // >>>>> permTbl rootObj indexTbl metaTbl @@ -253,22 +253,22 @@ static void serializeBoolean(SerializationInfo *info) { static void serializeNumber(SerializationInfo *info) { lua_Number value = lua_tonumber(info->luaState, -1); - -#if 1 - Util::SerializedDouble serializedValue(Util::encodeDouble(value)); - - info->writeStream->writeUint32LE(serializedValue.significandOne); - info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); - info->writeStream->writeSint16LE(serializedValue.exponent); -#else - // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. - // Also, I don't know if we can assume all compilers use IEEE double - // Therefore, I have chosen to store the double as a string. - Common::String buffer = Common::String::format("%f", value); - - info->writeStream->write(buffer.c_str(), buffer.size()); -#endif - + + #if 1 + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); + #else + // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. + // Also, I don't know if we can assume all compilers use IEEE double + // Therefore, I have chosen to store the double as a string. + Common::String buffer = Common::String::format("%f", value); + + info->writeStream->write(buffer.c_str(), buffer.size()); + #endif + } static void serializeString(SerializationInfo *info) { @@ -277,7 +277,7 @@ static void serializeString(SerializationInfo *info) { uint32 length = static_cast(lua_strlen(info->luaState, -1)); info->writeStream->writeUint32LE(length); - const char* str = lua_tostring(info->luaState, -1); + const char *str = lua_tostring(info->luaState, -1); info->writeStream->write(str, length); } @@ -350,7 +350,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); lua_error(info->luaState); } - + // >>>>> permTbl indexTbl ...... obj metaTbl __persist lua_pushvalue(info->luaState, -3); // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj @@ -369,7 +369,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { // Serialize the function serializeObject(info); - + lua_pop(info->luaState, 2); // >>>>> permTbl indexTbl ...... obj @@ -396,11 +396,11 @@ static void serializeTable(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ serializeObject(info); - + lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl - + lua_pushnil(info->luaState); // >>>>> permTbl indexTbl ...... tbl nil @@ -456,14 +456,14 @@ static void serializeFunction(SerializationInfo *info) { // Serialize the prototype pushProto(info->luaState, cl->l.p); // >>>>> permTbl indexTbl ...... func proto */ - + serializeObject(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func - + // Serialize upvalue values (not the upvalue objects themselves) - for (byte i=0; il.p->nups; i++) { + for (byte i = 0; i < cl->l.p->nups; i++) { // >>>>> permTbl indexTbl ...... func pushUpValue(info->luaState, cl->l.upvals[i]); // >>>>> permTbl indexTbl ...... func upval @@ -536,7 +536,7 @@ static void serializeThread(SerializationInfo *info) { } // >>>>> permTbl indexTbl ...... thread - + // Now, serialize the CallInfo stack // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems @@ -550,7 +550,7 @@ static void serializeThread(SerializationInfo *info) { uint32 stackBase = static_cast(ci->base - threadState->stack); uint32 stackFunc = static_cast(ci->func - threadState->stack); uint32 stackTop = static_cast(ci->top - threadState->stack); - + info->writeStream->writeUint32LE(stackBase); info->writeStream->writeUint32LE(stackFunc); info->writeStream->writeUint32LE(stackTop); @@ -560,7 +560,7 @@ static void serializeThread(SerializationInfo *info) { uint32 savedpc = (ci != threadState->base_ci) ? static_cast(ci->savedpc - ci_func(ci)->l.p->code) : 0u; info->writeStream->writeUint32LE(savedpc); } - + // Serialize the state's other parameters, with the exception of upval stuff @@ -671,9 +671,9 @@ static void serializeProto(SerializationInfo *info) { lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto } - - // Serialize local variable infos + + // Serialize local variable infos info->writeStream->writeSint32LE(proto->sizelocvars); for (int i = 0; i < proto->sizelocvars; ++i) { @@ -748,7 +748,7 @@ static void serializeUpValue(SerializationInfo *info) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); - // We can't permit the upValue to linger around on the stack, as Lua + // We can't permit the upValue to linger around on the stack, as Lua // will bail if its GC finds it. lua_pop(info->luaState, 1); diff --git a/engines/sword25/util/lua_unserializer.cpp b/engines/sword25/util/lua_unserializer.cpp index c561a3d99f..69cb764dd2 100644 --- a/engines/sword25/util/lua_unserializer.cpp +++ b/engines/sword25/util/lua_unserializer.cpp @@ -79,7 +79,7 @@ void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { // Re-start garbage collection lua_gc(luaState, LUA_GCRESTART, 0); - + // Remove the indexTbl lua_replace(luaState, 2); // >>>>> permTbl rootObj @@ -106,17 +106,17 @@ static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { } static void unserializeObject(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... + // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); byte isARealValue = info->readStream->readByte(); - if(isARealValue) { + if (isARealValue) { int index = info->readStream->readSint32LE(); int type = info->readStream->readSint32LE(); - switch(type) { + switch (type) { case LUA_TBOOLEAN: unserializeBoolean(info); break; @@ -156,9 +156,9 @@ static void unserializeObject(UnSerializationInfo *info) { assert(0); } - + // >>>>> permTbl indexTbl ...... obj - assert(lua_type(info->luaState, -1) == type || + assert(lua_type(info->luaState, -1) == type || type == PERMANENT_TYPE || // Remember, upvalues get a special dispensation, as described in boxUpValue (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); @@ -168,7 +168,7 @@ static void unserializeObject(UnSerializationInfo *info) { } else { int index = info->readStream->readSint32LE(); - if(index == 0) { + if (index == 0) { lua_pushnil(info->luaState); // >>>>> permTbl indexTbl ...... nil } else { @@ -184,7 +184,7 @@ static void unserializeObject(UnSerializationInfo *info) { } // >>>>> permTbl indexTbl ...... obj/nil } - + // >>>>> permTbl indexTbl ...... obj/nil } @@ -201,8 +201,8 @@ static void unserializeBoolean(UnSerializationInfo *info) { } static void unserializeNumber(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - + // >>>>> permTbl indexTbl ...... + // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); @@ -220,7 +220,7 @@ static void unserializeNumber(UnSerializationInfo *info) { static void unserializeString(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... - + // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); @@ -231,7 +231,7 @@ static void unserializeString(UnSerializationInfo *info) { lua_pushlstring(info->luaState, string, length); // >>>>> permTbl indexTbl ...... string - + delete[] string; } @@ -264,7 +264,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { // Unserialize metatable unserializeObject(info); // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? - + if (lua_istable(info->luaState, -1)) { // >>>>> permTbl indexTbl ...... tbl metaTbl lua_setmetatable(info->luaState, -2); @@ -343,7 +343,7 @@ void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { obj->gch.tt = type; } -#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) Closure *newLClosure(lua_State *luaState, byte numUpValues, Table *env) { Closure *newClosure = (Closure *)lua_malloc(luaState, sizeLclosure(numUpValues)); @@ -355,7 +355,7 @@ Closure *newLClosure(lua_State *luaState, byte numUpValues, Table *env) { newClosure->l.nupvalues = numUpValues; while (numUpValues--) { - newClosure->l.upvals[numUpValues] = NULL; + newClosure->l.upvals[numUpValues] = NULL; } return newClosure; @@ -431,7 +431,8 @@ static UpVal *createUpValue(lua_State *luaState, int stackpos) { upValue->u.l.next = NULL; const TValue *o2 = (TValue *)getObject(luaState, stackpos); - upValue->v->value = o2->value; upValue->v->tt = o2->tt; + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; checkliveness(G(L), upValue->v); return upValue; @@ -471,7 +472,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { lua_pushnil(info->luaState); // >>>>> permTbl indexTbl ...... func nil - for(byte i = 0; i < numUpValues; ++i) { + for (byte i = 0; i < numUpValues; ++i) { lclosure->upvals[i] = createUpValue(info->luaState, -1); } @@ -491,7 +492,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func - for(byte i = 0; i < numUpValues; ++i) { + for (byte i = 0; i < numUpValues; ++i) { // >>>>> permTbl indexTbl ...... func unserializeObject(info); // >>>>> permTbl indexTbl ...... func func2 @@ -508,7 +509,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { unserializeObject(info); // >>>>> permTbl indexTbl ...... func ?fenv/nil? - if(!lua_isnil(info->luaState, -1)) { + if (!lua_isnil(info->luaState, -1)) { // >>>>> permTbl indexTbl ...... func fenv lua_setfenv(info->luaState, -2); // >>>>> permTbl indexTbl ...... func @@ -517,7 +518,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func } - + // >>>>> permTbl indexTbl ...... func } @@ -547,7 +548,7 @@ void lua_reallocstack(lua_State *L, int newsize) { void lua_growstack(lua_State *L, int n) { // Double size is enough? - if (n <= L->stacksize) { + if (n <= L->stacksize) { lua_reallocstack(L, 2 * L->stacksize); } else { lua_reallocstack(L, L->stacksize + n); @@ -583,13 +584,13 @@ void unboxUpVal(lua_State *luaState) { * that. */ static void GCUnlink(lua_State *luaState, GCObject *gco) { GCObject *prevslot; - if(G(luaState)->rootgc == gco) { + if (G(luaState)->rootgc == gco) { G(luaState)->rootgc = G(luaState)->rootgc->gch.next; return; } prevslot = G(luaState)->rootgc; - while(prevslot->gch.next != gco) { + while (prevslot->gch.next != gco) { prevslot = prevslot->gch.next; } @@ -604,7 +605,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { L2 = lua_newthread(info->luaState); lua_checkstack(info->luaState, 3); - + // L1: permTbl indexTbl ...... thread // L2: (empty) registerObjectInIndexTable(info, index); @@ -612,14 +613,14 @@ void unserializeThread(UnSerializationInfo *info, int index) { // First, deserialize the object stack uint32 stackSize = info->readStream->readUint32LE(); lua_growstack(info->luaState, (int)stackSize); - + // Make sure that the first stack element (a nil, representing // the imaginary top-level C function) is written to the very, // very bottom of the stack L2->top--; - for(uint32 i = 0; i < stackSize; ++i) { + for (uint32 i = 0; i < stackSize; ++i) { unserializeObject(info); - // L1: permTbl indexTbl ...... thread obj* + // L1: permTbl indexTbl ...... thread obj* } lua_xmove(info->luaState, L2, stackSize); @@ -628,18 +629,18 @@ void unserializeThread(UnSerializationInfo *info, int index) { // Hereafter, stacks refer to L1 - + // Now, deserialize the CallInfo stack uint32 numFrames = info->readStream->readUint32LE(); - lua_reallocCallInfo(L2, numFrames*2); - for(uint32 i = 0; i < numFrames; ++i) { + lua_reallocCallInfo(L2, numFrames * 2); + for (uint32 i = 0; i < numFrames; ++i) { CallInfo *ci = L2->base_ci + i; uint32 stackbase = info->readStream->readUint32LE(); uint32 stackfunc = info->readStream->readUint32LE(); uint32 stacktop = info->readStream->readUint32LE(); - + ci->nresults = info->readStream->readSint32LE(); uint32 savedpc = info->readStream->readUint32LE(); @@ -653,7 +654,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { ci->top = L2->stack + stacktop; ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; ci->tailcalls = 0; - + // Update the pointer each time, to keep the GC happy L2->ci = ci; } @@ -668,12 +669,12 @@ void unserializeThread(UnSerializationInfo *info, int index) { L2->errfunc = info->readStream->readUint32LE(); - + L2->base = L2->stack + stackbase; L2->top = L2->stack + stacktop; - // Finally, "reopen" upvalues. See serializeUpVal() for why we do this - UpVal* uv; + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal *uv; GCObject **nextslot = &L2->openupval; global_State *g = G(L2); @@ -688,7 +689,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... thread break; } - + // >>>>> permTbl indexTbl ...... thread boxedUpVal unboxUpVal(info->luaState); // >>>>> permTbl indexTbl ...... thread boxedUpVal @@ -765,15 +766,15 @@ Proto *lua_newproto(lua_State *luaState) { void unserializeProto(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... - - // We have to be careful. The GC expects a lot out of protos. In particular, we need - // to give the function a valid string for its source, and valid code, even before we - // actually read in the real code. + + // We have to be careful. The GC expects a lot out of protos. In particular, we need + // to give the function a valid string for its source, and valid code, even before we + // actually read in the real code. TString *source = lua_newlstr(info->luaState, "", 0); Proto *p = lua_newproto(info->luaState); p->source = source; - p->sizecode=1; - p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->sizecode = 1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); p->maxstacksize = 2; p->sizek = 0; @@ -782,18 +783,18 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_checkstack(info->luaState, 2); pushProto(info->luaState, p); - // >>>>> permTbl indexTbl ...... proto + // >>>>> permTbl indexTbl ...... proto // We don't need to register early, since protos can never ever be - // involved in cyclic references + // involved in cyclic references // Read in constant references int sizek = info->readStream->readSint32LE(); lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); - for(int i = 0; i < sizek; ++i) { - // >>>>> permTbl indexTbl ...... proto + for (int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto unserializeObject(info); - // >>>>> permTbl indexTbl ...... proto k + // >>>>> permTbl indexTbl ...... proto k setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); p->sizek++; @@ -807,8 +808,8 @@ void unserializeProto(UnSerializationInfo *info, int index) { int sizep = info->readStream->readSint32LE(); lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); - for(int i = 0; i < sizep; ++i) { - // >>>>> permTbl indexTbl ...... proto + for (int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto unserializeObject(info); // >>>>> permTbl indexTbl ...... proto subproto @@ -816,22 +817,22 @@ void unserializeProto(UnSerializationInfo *info, int index) { p->sizep++; lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto + // >>>>> permTbl indexTbl ...... proto } - // >>>>> permTbl indexTbl ...... proto + // >>>>> permTbl indexTbl ...... proto // Read in code p->sizecode = info->readStream->readSint32LE(); lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); - + /* Read in upvalue names */ p->sizeupvalues = info->readStream->readSint32LE(); if (p->sizeupvalues) { lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); - for(int i = 0; i < p->sizeupvalues; ++i) { + for (int i = 0; i < p->sizeupvalues; ++i) { // >>>>> permTbl indexTbl ...... proto unserializeObject(info); // >>>>> permTbl indexTbl ...... proto str @@ -847,7 +848,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { p->sizelocvars = info->readStream->readSint32LE(); if (p->sizelocvars) { lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); - for(int i = 0; i < p->sizelocvars; ++i) { + for (int i = 0; i < p->sizelocvars; ++i) { // >>>>> permTbl indexTbl ...... proto unserializeObject(info); // >>>>> permTbl indexTbl ...... proto str @@ -876,7 +877,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); } - + /* Read in linedefined and lastlinedefined */ p->linedefined = info->readStream->readSint32LE(); @@ -895,7 +896,7 @@ Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTab c->l.isC = 0; c->l.env = elementTable; c->l.nupvalues = cast_byte(numElements); - + while (numElements--) { c->l.upvals[numElements] = NULL; } @@ -916,13 +917,13 @@ static UpVal *makeUpVal(lua_State *luaState, int stackPos) { return uv; } -/** +/** * The GC is not fond of finding upvalues in tables. We get around this * during persistence using a weakly keyed table, so that the GC doesn't * bother to mark them. This won't work in unpersisting, however, since * if we make the values weak they'll be collected (since nothing else * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. + * upvalues as dummy functions, each with one upvalue. */ static void boxupval_start(lua_State *luaState) { LClosure *closure; @@ -931,7 +932,7 @@ static void boxupval_start(lua_State *luaState) { // >>>>> ...... func closure->p = makeFakeProto(luaState, 1); - // Temporarily initialize the upvalue to nil + // Temporarily initialize the upvalue to nil lua_pushnil(luaState); closure->upvals[0] = makeUpVal(luaState, -1); lua_pop(luaState, 1); @@ -947,7 +948,7 @@ static void boxupval_finish(lua_State *luaState) { } void unserializeUpValue(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... + // >>>>> permTbl indexTbl ...... lua_checkstack(upi->L, 2); boxupval_start(upi->L); @@ -966,9 +967,9 @@ void unserializeUserData(UnSerializationInfo *info, int index) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); - + int isspecial = info->readStream->readSint32LE(); - if(isspecial) { + if (isspecial) { unserializeObject(info); // >>>>> permTbl indexTbl ...... specialFunc @@ -996,7 +997,7 @@ void unserializePermanent(UnSerializationInfo *info, int index) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); - + unserializeObject(info); // >>>>> permTbl indexTbl ...... permKey -- cgit v1.2.3 From 2fb116f10eaa1bf682a220c33016fc61492af4fe Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 13:32:44 -0600 Subject: SWORD25: Move all lua serialization helper functions to their own file --- engines/sword25/util/lua_serialization_util.cpp | 269 +++++++++++++++++ engines/sword25/util/lua_serialization_util.h | 55 ++++ engines/sword25/util/lua_serializer.cpp | 56 ++-- engines/sword25/util/lua_unserializer.cpp | 368 ++---------------------- 4 files changed, 376 insertions(+), 372 deletions(-) diff --git a/engines/sword25/util/lua_serialization_util.cpp b/engines/sword25/util/lua_serialization_util.cpp index 80c5f86b20..fc3f73eca6 100644 --- a/engines/sword25/util/lua_serialization_util.cpp +++ b/engines/sword25/util/lua_serialization_util.cpp @@ -27,10 +27,20 @@ #include "lua/lobject.h" #include "lua/lstate.h" #include "lua/lgc.h" +#include "lua/lopcodes.h" namespace Lua { +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + void pushObject(lua_State *luaState, TValue *obj) { setobj2s(luaState, luaState->top, obj); @@ -74,4 +84,263 @@ StkId getObject(lua_State *luaState, int stackpos) { } } +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); + + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +size_t appendStackToStack_reverse(lua_State *from, lua_State *to) { + for (StkId id = from->top - 1; id >= from->stack; --id) { + setobj2s(to, to->top, id); + to->top++; + } + + return from->top - from->stack; +} + +void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if (G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while (prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +UpVal *makeUpValue(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +void boxUpValue_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpValue(luaState, -1); + lua_pop(luaState, 1); +} + +void boxUpValue_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + } // End of namespace Lua diff --git a/engines/sword25/util/lua_serialization_util.h b/engines/sword25/util/lua_serialization_util.h index 6c55d0dd53..345996f606 100644 --- a/engines/sword25/util/lua_serialization_util.h +++ b/engines/sword25/util/lua_serialization_util.h @@ -32,12 +32,67 @@ typedef TValue *StkId; namespace Lua { +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); + void pushObject(lua_State *luaState, TValue *obj); void pushProto(lua_State *luaState, Proto *proto); void pushUpValue(lua_State *luaState, UpVal *upval); void pushString(lua_State *luaState, TString *str); + StkId getObject(lua_State *luaState, int stackpos); +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); +void pushClosure(lua_State *luaState, Closure *closure); + +Proto *createProto(lua_State *luaState); +Proto *makeFakeProto(lua_State *L, lu_byte nups); + +TString *createString(lua_State *luaState, const char *str, size_t len); + +UpVal *createUpValue(lua_State *luaState, int stackpos); +void unboxUpValue(lua_State *luaState); + +/* Appends one stack to another stack, but the stack is reversed in the process */ +size_t appendStackToStack_reverse(lua_State *from, lua_State *to); +void correctStack(lua_State *L, TValue *oldstack); +void lua_reallocstack(lua_State *L, int newsize); +void lua_growstack(lua_State *L, int n); + +void lua_reallocCallInfo(lua_State *lauState, int newsize); + +/* Does basically the opposite of luaC_link(). + * Right now this function is rather inefficient; it requires traversing the + * entire root GC set in order to find one object. If the GC list were doubly + * linked this would be much easier, but there's no reason for Lua to have + * that. */ +void GCUnlink(lua_State *luaState, GCObject *gco); + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); +Proto *lua_newproto(lua_State *luaState) ; + +UpVal *makeUpValue(lua_State *luaState, int stackPos); +/** + * The GC is not fond of finding upvalues in tables. We get around this + * during persistence using a weakly keyed table, so that the GC doesn't + * bother to mark them. This won't work in unpersisting, however, since + * if we make the values weak they'll be collected (since nothing else + * references them). Our solution, during unpersisting, is to represent + * upvalues as dummy functions, each with one upvalue. + */ +void boxUpValue_start(lua_State *luaState); +void boxUpValue_finish(lua_State *luaState); + } // End of namespace Lua #endif diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp index c6c5f99342..55b6257324 100644 --- a/engines/sword25/util/lua_serializer.cpp +++ b/engines/sword25/util/lua_serializer.cpp @@ -42,7 +42,7 @@ struct SerializationInfo { uint counter; }; -static void serializeObject(SerializationInfo *info); +static void serialize(SerializationInfo *info); static void serializeBoolean(SerializationInfo *info); static void serializeNumber(SerializationInfo *info); @@ -103,14 +103,14 @@ void serializeLua(lua_State *luaState, Common::WriteStream *writeStream) { // >>>>> permTbl indexTbl rootObj // Serialize the root recursively - serializeObject(&info); + serialize(&info); // Return the stack back to the original state lua_remove(luaState, 2); // >>>>> permTbl rootObj } -static void serializeObject(SerializationInfo *info) { +static void serialize(SerializationInfo *info) { // The stack can potentially have many things on it // The object we want to serialize is the item on the top of the stack // >>>>> permTbl indexTbl rootObj ...... obj @@ -188,7 +188,7 @@ static void serializeObject(SerializationInfo *info) { info->writeStream->writeSint32LE(PERMANENT_TYPE); // Serialize the key - serializeObject(info); + serialize(info); // Pop the key off the stack lua_pop(info->luaState, 1); @@ -368,7 +368,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { info->writeStream->writeSint32LE(1); // Serialize the function - serializeObject(info); + serialize(info); lua_pop(info->luaState, 2); // >>>>> permTbl indexTbl ...... obj @@ -395,7 +395,7 @@ static void serializeTable(SerializationInfo *info) { } // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl @@ -412,13 +412,13 @@ static void serializeTable(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... tbl k v k */ // Serialize the key - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl k v */ // Serialize the value - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl k */ @@ -430,7 +430,7 @@ static void serializeTable(SerializationInfo *info) { lua_pushnil(info->luaState); // >>>>> permTbl indexTbl ...... tbl - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl @@ -457,7 +457,7 @@ static void serializeFunction(SerializationInfo *info) { pushProto(info->luaState, cl->l.p); // >>>>> permTbl indexTbl ...... func proto */ - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func @@ -468,7 +468,7 @@ static void serializeFunction(SerializationInfo *info) { pushUpValue(info->luaState, cl->l.upvals[i]); // >>>>> permTbl indexTbl ...... func upval - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func @@ -492,23 +492,13 @@ static void serializeFunction(SerializationInfo *info) { } // >>>>> permTbl indexTbl ...... func fenv/nil - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func } } -/* Appends one stack to another stack, but the stack is reversed in the process */ -static size_t appendStackToStack_rev(lua_State *from, lua_State *to) { - for (StkId id = from->top - 1; id >= from->stack; --id) { - setobj2s(to, to->top, id); - to->top++; - } - - return from->top - from->stack; -} - static void serializeThread(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... thread lua_State *threadState = lua_tothread(info->luaState, -1); @@ -525,12 +515,12 @@ static void serializeThread(SerializationInfo *info) { // Persist the stack // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems - uint32 stackSize = static_cast(appendStackToStack_rev(threadState, info->luaState)); + uint32 stackSize = static_cast(appendStackToStack_reverse(threadState, info->luaState)); info->writeStream->writeUint32LE(stackSize); // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ for (; stackSize > 0; --stackSize) { - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); } @@ -592,7 +582,7 @@ static void serializeThread(SerializationInfo *info) { pushUpValue(info->luaState, upVal); // >>>>> permTbl indexTbl ...... thread upVal - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... thread @@ -607,7 +597,7 @@ static void serializeThread(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... thread nil // Use nil as a terminator - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... thread @@ -627,7 +617,7 @@ static void serializeProto(SerializationInfo *info) { pushObject(info->luaState, &proto->k[i]); // >>>>> permTbl indexTbl ...... proto const - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -643,7 +633,7 @@ static void serializeProto(SerializationInfo *info) { pushProto(info->luaState, proto->p[i]); // >>>>> permTbl indexTbl ...... proto subProto */ - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -666,7 +656,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->upvalues[i]); // >>>>> permTbl indexTbl ...... proto str - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -680,7 +670,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->locvars[i].varname); // >>>>> permTbl indexTbl ...... proto str - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -694,7 +684,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->source); // >>>>> permTbl indexTbl ...... proto sourceStr - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -757,7 +747,7 @@ static void serializeUpValue(SerializationInfo *info) { pushObject(info->luaState, upValue->v); // >>>>> permTbl indexTbl ...... obj - serializeObject(info); + serialize(info); // >>>>> permTbl indexTbl ...... obj } @@ -787,7 +777,7 @@ static void serializeUserData(SerializationInfo *info) { } // >>>>> permTbl rootObj ...... udata metaTbl/nil - serializeObject(info); + serialize(info); lua_pop(info->luaState, 1); /* perms reftbl ... udata */ diff --git a/engines/sword25/util/lua_unserializer.cpp b/engines/sword25/util/lua_unserializer.cpp index 69cb764dd2..803c79c8c2 100644 --- a/engines/sword25/util/lua_unserializer.cpp +++ b/engines/sword25/util/lua_unserializer.cpp @@ -40,7 +40,7 @@ struct UnSerializationInfo { Common::ReadStream *readStream; }; -static void unserializeObject(UnSerializationInfo *info); +static void unserialize(UnSerializationInfo *info); static void unserializeBoolean(UnSerializationInfo *info); static void unserializeNumber(UnSerializationInfo *info); @@ -74,7 +74,7 @@ void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { lua_gc(luaState, LUA_GCSTOP, 0); // Unserialize the root object - unserializeObject(&info); + unserialize(&info); // >>>>> permTbl indexTbl rootObj // Re-start garbage collection @@ -85,7 +85,7 @@ void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { // >>>>> permTbl rootObj } -/* The object is left on the stack. This is primarily used by unpersist, but +/* The object is left on the stack. This is primarily used by unserialize, but * may be used by GCed objects that may incur cycles in order to preregister * the object. */ static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { @@ -105,7 +105,7 @@ static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... obj } -static void unserializeObject(UnSerializationInfo *info) { +static void unserialize(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -241,7 +241,7 @@ static void unserializeSpecialTable(UnSerializationInfo *info, int index) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... spfunc lua_call(info->luaState, 0, 1); @@ -262,7 +262,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... tbl // Unserialize metatable - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? if (lua_istable(info->luaState, -1)) { @@ -279,7 +279,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { while (1) { // >>>>> permTbl indexTbl ...... tbl - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... tbl key/nil // The table serialization is nil terminated @@ -292,7 +292,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { } // >>>>> permTbl indexTbl ...... tbl key - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... tbl value lua_rawset(info->luaState, -3); @@ -317,142 +317,6 @@ void unserializeTable(UnSerializationInfo *info, int index) { } } - - -void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { - global_State *globalState = G(luaState); - - block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); - globalState->totalbytes = (globalState->totalbytes - osize) + nsize; - - return block; -} - -#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) -#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) -#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) -#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) -#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) - -void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { - global_State *globalState = G(luaState); - - obj->gch.next = globalState->rootgc; - globalState->rootgc = obj; - obj->gch.marked = luaC_white(globalState); - obj->gch.tt = type; -} - -#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) - -Closure *newLClosure(lua_State *luaState, byte numUpValues, Table *env) { - Closure *newClosure = (Closure *)lua_malloc(luaState, sizeLclosure(numUpValues)); - - lua_linkObjToGC(luaState, obj2gco(newClosure), LUA_TFUNCTION); - - newClosure->l.isC = 0; - newClosure->l.env = env; - newClosure->l.nupvalues = numUpValues; - - while (numUpValues--) { - newClosure->l.upvals[numUpValues] = NULL; - } - - return newClosure; -} - -static void pushClosure(lua_State *luaState, Closure *closure) { - TValue obj; - setclvalue(luaState, &obj, closure); - pushObject(luaState, &obj); -} - -Proto *createProto(lua_State *luaState) { - Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); - - newProto->k = NULL; - newProto->sizek = 0; - newProto->p = NULL; - newProto->sizep = 0; - newProto->code = NULL; - newProto->sizecode = 0; - newProto->sizelineinfo = 0; - newProto->sizeupvalues = 0; - newProto->nups = 0; - newProto->upvalues = NULL; - newProto->numparams = 0; - newProto->is_vararg = 0; - newProto->maxstacksize = 0; - newProto->lineinfo = NULL; - newProto->sizelocvars = 0; - newProto->locvars = NULL; - newProto->linedefined = 0; - newProto->lastlinedefined = 0; - newProto->source = NULL; - - return newProto; -} - -TString *createString(lua_State *luaState, const char *str, size_t len) { - TString *res; - lua_pushlstring(luaState, str, len); - - res = rawtsvalue(luaState->top - 1); - lua_pop(luaState, 1); - - return res; -} - -static Proto *makeFakeProto(lua_State *L, lu_byte nups) { - Proto *p = createProto(L); - - p->sizelineinfo = 1; - p->lineinfo = lua_newVector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = lua_newVector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = createString(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -static UpVal *createUpValue(lua_State *luaState, int stackpos) { - UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); - lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); - upValue->tt = LUA_TUPVAL; - upValue->v = &upValue->u.value; - upValue->u.l.prev = NULL; - upValue->u.l.next = NULL; - - const TValue *o2 = (TValue *)getObject(luaState, stackpos); - upValue->v->value = o2->value; - upValue->v->tt = o2->tt; - checkliveness(G(L), upValue->v); - - return upValue; -} - -static void unboxUpValue(lua_State *luaState) { - // >>>>> ...... func - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getObject(luaState, -1)); - uv = lcl->upvals[0]; - - lua_pop(luaState, 1); - // >>>>> ...... - - pushUpValue(luaState, uv); - // >>>>> ...... upValue -} - void unserializeFunction(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... @@ -461,7 +325,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { byte numUpValues = info->readStream->readByte(); - LClosure *lclosure = (LClosure *)newLClosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); pushClosure(info->luaState, (Closure *)lclosure); // >>>>> permTbl indexTbl ...... func @@ -484,7 +348,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { registerObjectInIndexTable(info, index); // Now that it's safe, we can get the real proto - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... func proto lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); @@ -494,7 +358,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { for (byte i = 0; i < numUpValues; ++i) { // >>>>> permTbl indexTbl ...... func - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... func func2 unboxUpValue(info->luaState); @@ -506,7 +370,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { } // Finally, the fenv - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... func ?fenv/nil? if (!lua_isnil(info->luaState, -1)) { @@ -522,81 +386,6 @@ void unserializeFunction(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... func } -static void correctStack(lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - -void lua_reallocstack(lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - - lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack + newsize; - correctStack(L, oldstack); -} - -void lua_growstack(lua_State *L, int n) { - // Double size is enough? - if (n <= L->stacksize) { - lua_reallocstack(L, 2 * L->stacksize); - } else { - lua_reallocstack(L, L->stacksize + n); - } -} - -void lua_reallocCallInfo(lua_State *lauState, int newsize) { - CallInfo *oldci = lauState->base_ci; - lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); - - lauState->size_ci = newsize; - lauState->ci = (lauState->ci - oldci) + lauState->base_ci; - lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; -} - -void unboxUpVal(lua_State *luaState) { - // >>>>> ... func - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)(&getObject(luaState, -1)->value.gc->cl); - uv = lcl->upvals[0]; - lua_pop(luaState, 1); - // >>>>> ... - pushUpValue(luaState, uv); - // >>>>> ... upVal -} - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -static void GCUnlink(lua_State *luaState, GCObject *gco) { - GCObject *prevslot; - if (G(luaState)->rootgc == gco) { - G(luaState)->rootgc = G(luaState)->rootgc->gch.next; - return; - } - - prevslot = G(luaState)->rootgc; - while (prevslot->gch.next != gco) { - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - void unserializeThread(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... @@ -619,7 +408,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { // very bottom of the stack L2->top--; for (uint32 i = 0; i < stackSize; ++i) { - unserializeObject(info); + unserialize(info); // L1: permTbl indexTbl ...... thread obj* } @@ -679,7 +468,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { global_State *g = G(L2); while (true) { - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... thread upVal/nil // The list is terminated by a nil @@ -691,7 +480,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { } // >>>>> permTbl indexTbl ...... thread boxedUpVal - unboxUpVal(info->luaState); + unboxUpValue(info->luaState); // >>>>> permTbl indexTbl ...... thread boxedUpVal uv = &(getObject(info->luaState, -1)->value.gc->uv); @@ -722,48 +511,6 @@ void unserializeThread(UnSerializationInfo *info, int index) { } } -TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { - lua_pushlstring(luaState, str, len); - TString *luaStr = &(luaState->top - 1)->value.gc->ts; - - lua_pop(luaState, 1); - - return luaStr; -} - -void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { - global_State *g = G(luaState); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *lua_newproto(lua_State *luaState) { - Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_link(luaState, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - void unserializeProto(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... @@ -793,7 +540,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); for (int i = 0; i < sizek; ++i) { // >>>>> permTbl indexTbl ...... proto - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... proto k setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); @@ -810,7 +557,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); for (int i = 0; i < sizep; ++i) { // >>>>> permTbl indexTbl ...... proto - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... proto subproto p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; @@ -834,7 +581,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); for (int i = 0; i < p->sizeupvalues; ++i) { // >>>>> permTbl indexTbl ...... proto - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... proto str p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -850,7 +597,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); for (int i = 0; i < p->sizelocvars; ++i) { // >>>>> permTbl indexTbl ...... proto - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... proto str p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -864,7 +611,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... proto // Read in source string - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... proto sourceStr p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -890,75 +637,18 @@ void unserializeProto(UnSerializationInfo *info, int index) { p->maxstacksize = info->readStream->readByte(); } -Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { - Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); - lua_link(luaState, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = elementTable; - c->l.nupvalues = cast_byte(numElements); - - while (numElements--) { - c->l.upvals[numElements] = NULL; - } - - return c; -} - -static UpVal *makeUpVal(lua_State *luaState, int stackPos) { - UpVal *uv = lua_new(luaState, UpVal); - lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - - setobj(luaState, uv->v, getObject(luaState, stackPos)); - - return uv; -} - -/** - * The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. - */ -static void boxupval_start(lua_State *luaState) { - LClosure *closure; - closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); - pushClosure(luaState, (Closure *)closure); - // >>>>> ...... func - closure->p = makeFakeProto(luaState, 1); - - // Temporarily initialize the upvalue to nil - lua_pushnil(luaState); - closure->upvals[0] = makeUpVal(luaState, -1); - lua_pop(luaState, 1); -} - -static void boxupval_finish(lua_State *luaState) { - // >>>>> ...... func obj - LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); - - lcl->upvals[0]->u.value = *getObject(luaState, -1); - lua_pop(luaState, 1); - // >>>>> ...... func -} - void unserializeUpValue(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... - lua_checkstack(upi->L, 2); + lua_checkstack(info->luaState, 2); - boxupval_start(upi->L); + boxUpValue_start(info->luaState); // >>>>> permTbl indexTbl ...... func registerObjectInIndexTable(info, index); - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... func obj - boxupval_finish(upi->L); + boxUpValue_finish(info->luaState); // >>>>> permTbl indexTbl ...... func } @@ -970,7 +660,7 @@ void unserializeUserData(UnSerializationInfo *info, int index) { int isspecial = info->readStream->readSint32LE(); if (isspecial) { - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... specialFunc lua_call(info->luaState, 0, 1); @@ -981,12 +671,12 @@ void unserializeUserData(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... udata registerObjectInIndexTable(info, index); - info->readStream->read(lua_touserdata(upi->L, -1), length); + info->readStream->read(lua_touserdata(info->luaState, -1), length); - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... udata metaTable/nil - lua_setmetatable(upi->L, -2); + lua_setmetatable(info->luaState, -2); // >>>>> permTbl indexTbl ...... udata } // >>>>> permTbl indexTbl ...... udata @@ -998,7 +688,7 @@ void unserializePermanent(UnSerializationInfo *info, int index) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); - unserializeObject(info); + unserialize(info); // >>>>> permTbl indexTbl ...... permKey lua_gettable(info->luaState, 1); -- cgit v1.2.3 From 0b8482b55fb38147927d2c27478aae59c0f98dab Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 13:42:02 -0600 Subject: SWORD25: Update module.mk with lua serialization changes --- engines/sword25/module.mk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 234baec165..0135f53783 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -82,9 +82,10 @@ MODULE_OBJS := \ util/lua/lvm.o \ util/lua/lzio.o \ util/lua/scummvm_file.o \ - util/pluto/pdep.o \ - util/pluto/pluto.o \ - util/pluto/plzio.o + util/double_serializer.o \ + util/lua_serialization_util.o \ + util/lua_serializer.o \ + util/lua_unserializer.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) -- cgit v1.2.3 From a188b31d15711bd86725ca4feb0dc36fb2ab71ac Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 13:42:45 -0600 Subject: SWORD25: Use new lua serialization functions to persist state --- engines/sword25/kernel/outputpersistenceblock.cpp | 7 +++ engines/sword25/kernel/outputpersistenceblock.h | 1 + engines/sword25/script/luascript.cpp | 61 +++++------------------ 3 files changed, 20 insertions(+), 49 deletions(-) diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp index 9003b5d58a..3e25fce5c7 100644 --- a/engines/sword25/kernel/outputpersistenceblock.cpp +++ b/engines/sword25/kernel/outputpersistenceblock.cpp @@ -41,6 +41,13 @@ OutputPersistenceBlock::OutputPersistenceBlock() { _data.reserve(INITIAL_BUFFER_SIZE); } +void OutputPersistenceBlock::write(const void *data, uint32 size) { + writeMarker(BLOCK_MARKER); + + write(size); + rawWrite(data, size); +} + void OutputPersistenceBlock::write(int32 value) { writeMarker(SINT_MARKER); value = TO_LE_32(value); diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h index c7d8dfc6aa..bdbd20d3e0 100644 --- a/engines/sword25/kernel/outputpersistenceblock.h +++ b/engines/sword25/kernel/outputpersistenceblock.h @@ -41,6 +41,7 @@ class OutputPersistenceBlock : public PersistenceBlock { public: OutputPersistenceBlock(); + void write(const void *data, uint32 size); void write(int32 value); void write(uint32 value); void write(float value); diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp index f62a08005b..6cf287c643 100644 --- a/engines/sword25/script/luascript.cpp +++ b/engines/sword25/script/luascript.cpp @@ -29,7 +29,7 @@ * */ -#include "common/array.h" +#include "common/memstream.h" #include "common/debug-channels.h" #include "sword25/sword25.h" @@ -43,7 +43,7 @@ #include "sword25/util/lua/lua.h" #include "sword25/util/lua/lualib.h" #include "sword25/util/lua/lauxlib.h" -#include "sword25/util/pluto/pluto.h" +#include "sword25/util/lua_serialization.h" namespace Sword25 { @@ -112,10 +112,6 @@ bool LuaScriptEngine::init() { // Place the error handler function in the Lua registry, and remember the index _pcallErrorhandlerRegistryIndex = luaL_ref(_state, LUA_REGISTRYINDEX); - // Initialize the Pluto-Persistence library - luaopen_pluto(_state); - lua_pop(_state, 1); - // Initialize debugging callback if (DebugMan.isDebugChannelEnabled(kDebugScript)) { int mask = 0; @@ -383,19 +379,8 @@ bool pushPermanentsTable(lua_State *L, PERMANENT_TABLE_TYPE tableType) { return true; } -} - -namespace { -int chunkwriter(lua_State *L, const void *p, size_t sz, void *ud) { - Common::Array & chunkData = *reinterpret_cast * >(ud); - const byte *buffer = reinterpret_cast(p); - while (sz--) - chunkData.push_back(*buffer++); - - return 1; -} -} +} // End of anonymous namespace bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -409,12 +394,12 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { pushPermanentsTable(_state, PTT_PERSIST); lua_getglobal(_state, "_G"); - // Lua persists and stores the data in a Common::Array - Common::Array chunkData; - pluto_persist(_state, chunkwriter, &chunkData); + // Lua persists and stores the data in a WriteStream + Common::MemoryWriteStreamDynamic writeStream; + Lua::serializeLua(_state, &writeStream); // Persistenzdaten in den Writer schreiben. - writer.writeByteArray(chunkData); + writer.write(writeStream.getData(), writeStream.size()); // Die beiden Tabellen vom Stack nehmen. lua_pop(_state, 2); @@ -424,24 +409,6 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { namespace { -struct ChunkreaderData { - void *BufferPtr; - size_t Size; - bool BufferReturned; -}; - -const char *chunkreader(lua_State *L, void *ud, size_t *sz) { - ChunkreaderData &cd = *reinterpret_cast(ud); - - if (!cd.BufferReturned) { - cd.BufferReturned = true; - *sz = cd.Size; - return reinterpret_cast(cd.BufferPtr); - } else { - return 0; - } -} - void clearGlobalTable(lua_State *L, const char **exceptions) { // Iterate over all elements of the global table lua_pushvalue(L, LUA_GLOBALSINDEX); @@ -479,7 +446,8 @@ void clearGlobalTable(lua_State *L, const char **exceptions) { // Perform garbage collection, so that all removed elements are deleted lua_gc(L, LUA_GCCOLLECT, 0); } -} + +} // End of anonymous namespace bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -512,14 +480,9 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Persisted Lua data Common::Array chunkData; reader.readByteArray(chunkData); + Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO); - // Chunk-Reader initialisation. It is used with pluto_unpersist to restore read data - ChunkreaderData cd; - cd.BufferPtr = &chunkData[0]; - cd.Size = chunkData.size(); - cd.BufferReturned = false; - - pluto_unpersist(_state, chunkreader, &cd); + Lua::unserializeLua(_state, &readStream); // Permanents-Table is removed from stack lua_remove(_state, -2); @@ -527,7 +490,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // The read elements in the global table about lua_pushnil(_state); while (lua_next(_state, -2) != 0) { - // The referenec to the global table (_G) must not be overwritten, or ticks from Lua total + // The reference to the global table (_G) must not be overwritten, or ticks from Lua total bool isGlobalReference = lua_isstring(_state, -2) && strcmp(lua_tostring(_state, -2), "_G") == 0; if (!isGlobalReference) { lua_pushvalue(_state, -2); -- cgit v1.2.3 From 97c35714ce3986b99848a780f6b195a63f8910b7 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 15:19:29 -0600 Subject: SWORD25: Rename lua serialization functions to use 'persist' in order to match the rest of the engine --- engines/sword25/module.mk | 6 +- engines/sword25/script/luascript.cpp | 6 +- engines/sword25/util/lua_persist.cpp | 787 ++++++++++++++++++++++++ engines/sword25/util/lua_persistence.h | 44 ++ engines/sword25/util/lua_persistence_util.cpp | 346 +++++++++++ engines/sword25/util/lua_persistence_util.h | 98 +++ engines/sword25/util/lua_serialization.h | 44 -- engines/sword25/util/lua_serialization_util.cpp | 346 ----------- engines/sword25/util/lua_serialization_util.h | 98 --- engines/sword25/util/lua_serializer.cpp | 787 ------------------------ engines/sword25/util/lua_unpersist.cpp | 698 +++++++++++++++++++++ engines/sword25/util/lua_unserializer.cpp | 698 --------------------- 12 files changed, 1979 insertions(+), 1979 deletions(-) create mode 100644 engines/sword25/util/lua_persist.cpp create mode 100644 engines/sword25/util/lua_persistence.h create mode 100644 engines/sword25/util/lua_persistence_util.cpp create mode 100644 engines/sword25/util/lua_persistence_util.h delete mode 100644 engines/sword25/util/lua_serialization.h delete mode 100644 engines/sword25/util/lua_serialization_util.cpp delete mode 100644 engines/sword25/util/lua_serialization_util.h delete mode 100644 engines/sword25/util/lua_serializer.cpp create mode 100644 engines/sword25/util/lua_unpersist.cpp delete mode 100644 engines/sword25/util/lua_unserializer.cpp diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 0135f53783..129b4f040e 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -83,9 +83,9 @@ MODULE_OBJS := \ util/lua/lzio.o \ util/lua/scummvm_file.o \ util/double_serializer.o \ - util/lua_serialization_util.o \ - util/lua_serializer.o \ - util/lua_unserializer.o + util/lua_persistence_util.o \ + util/lua_persist.o \ + util/lua_unpersist.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp index 6cf287c643..e93289596b 100644 --- a/engines/sword25/script/luascript.cpp +++ b/engines/sword25/script/luascript.cpp @@ -43,7 +43,7 @@ #include "sword25/util/lua/lua.h" #include "sword25/util/lua/lualib.h" #include "sword25/util/lua/lauxlib.h" -#include "sword25/util/lua_serialization.h" +#include "sword25/util/lua_persistence.h" namespace Sword25 { @@ -396,7 +396,7 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { // Lua persists and stores the data in a WriteStream Common::MemoryWriteStreamDynamic writeStream; - Lua::serializeLua(_state, &writeStream); + Lua::persistLua(_state, &writeStream); // Persistenzdaten in den Writer schreiben. writer.write(writeStream.getData(), writeStream.size()); @@ -482,7 +482,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { reader.readByteArray(chunkData); Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO); - Lua::unserializeLua(_state, &readStream); + Lua::unpersistLua(_state, &readStream); // Permanents-Table is removed from stack lua_remove(_state, -2); diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp new file mode 100644 index 0000000000..b9c0b13e11 --- /dev/null +++ b/engines/sword25/util/lua_persist.cpp @@ -0,0 +1,787 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serializer.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +struct SerializationInfo { + lua_State *luaState; + Common::WriteStream *writeStream; + uint counter; +}; + +static void serialize(SerializationInfo *info); + +static void serializeBoolean(SerializationInfo *info); +static void serializeNumber(SerializationInfo *info); +static void serializeString(SerializationInfo *info); +static void serializeTable(SerializationInfo *info); +static void serializeFunction(SerializationInfo *info); +static void serializeThread(SerializationInfo *info); +static void serializeProto(SerializationInfo *info); +static void serializeUpValue(SerializationInfo *info); +static void serializeUserData(SerializationInfo *info); + + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { + SerializationInfo info; + info.luaState = luaState; + info.writeStream = writeStream; + info.counter = 0u; + + // The process starts with the lua stack as follows: + // >>>>> permTbl rootObj + // That's the table of permanents and the root object to be serialized + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 4); + assert(lua_gettop(luaState) == 2); + // And that the root isn't nil + assert(!lua_isnil(luaState, 2)); + + // Create a table to hold indexes of everything that's serialized + // This allows us to only serialize an object once + // Every other time, just reference the index + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl + + // Now we're going to make the table weakly keyed. This prevents the + // GC from visiting it and trying to mark things it doesn't want to + // mark in tables, e.g. upvalues. All objects in the table are + // a priori reachable, so it doesn't matter that we do this. + + // Create the metatable + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_pushstring(luaState, "__mode"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" + + lua_pushstring(luaState, "k"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k" + + lua_settable(luaState, 4); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_setmetatable(luaState, 3); + // >>>>> permTbl rootObj indexTbl + + // Swap the indexTable and the rootObj + lua_insert(luaState, 2); + // >>>>> permTbl indexTbl rootObj + + // Serialize the root recursively + serialize(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void serialize(SerializationInfo *info) { + // The stack can potentially have many things on it + // The object we want to serialize is the item on the top of the stack + // >>>>> permTbl indexTbl rootObj ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // If the object has already been written, don't write it again + // Instead write the index of the object from the indexTbl + + // Check the indexTbl + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_rawget(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj ?index? + + // If the index isn't nil, the object has already been written + if (!lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + + // Retrieve the index from the stack + uint *index = (uint *)lua_touserdata(info->luaState, -1); + + // Write out the index + info->writeStream->writeUint32LE(*index); + + // Pop the index off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + + // If the object itself is nil, then write out a zero as a placeholder + if (lua_isnil(info->luaState, -1)) { + info->writeStream->writeByte(0); + + return; + } + + // Add the object to the indexTbl + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint)); + *ref = ++(info->counter); + // >>>>> permTbl indexTbl rootObj ...... obj obj index + + lua_rawset(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj + + + // Write out the index + info->writeStream->writeUint32LE(info->counter); + + + // Objects that are in the permanents table are serialized in a special way + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey? + + if (!lua_isnil(info->luaState, -1)) { + // Write out the type + info->writeStream->writeSint32LE(PERMANENT_TYPE); + + // Serialize the key + serialize(info); + + // Pop the key off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Query the type of the object + int objType = lua_type(info->luaState, -1); + + // Write it out + info->writeStream->writeSint32LE(objType); + + // Serialize the object by its type + + switch (objType) { + case LUA_TBOOLEAN: + serializeBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + serializeNumber(info); + break; + case LUA_TSTRING: + serializeString(info); + break; + case LUA_TTABLE: + serializeTable(info); + break; + case LUA_TFUNCTION: + serializeFunction(info); + break; + case LUA_TTHREAD: + serializeThread(info); + break; + case LUA_TPROTO: + serializeProto(info); + break; + case LUA_TUPVAL: + serializeUpValue(info); + break; + case LUA_TUSERDATA: + serializeUserData(info); + break; + default: + assert(0); + } +} + +static void serializeBoolean(SerializationInfo *info) { + int value = lua_toboolean(info->luaState, -1); + + info->writeStream->writeSint32LE(value); +} + +static void serializeNumber(SerializationInfo *info) { + lua_Number value = lua_tonumber(info->luaState, -1); + + #if 1 + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); + #else + // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. + // Also, I don't know if we can assume all compilers use IEEE double + // Therefore, I have chosen to store the double as a string. + Common::String buffer = Common::String::format("%f", value); + + info->writeStream->write(buffer.c_str(), buffer.size()); + #endif + +} + +static void serializeString(SerializationInfo *info) { + // Hard cast to a uint32 to force size_t to an explicit size + // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems + uint32 length = static_cast(lua_strlen(info->luaState, -1)); + info->writeStream->writeUint32LE(length); + + const char *str = lua_tostring(info->luaState, -1); + info->writeStream->write(str, length); +} + +/* Choose whether to do a regular or special persistence based on an object's + * metatable. "default" is whether the object, if it doesn't have a __persist + * entry, is literally persistable or not. + * Pushes the unpersist closure and returns true if special persistence is + * used. */ +static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 4); + + // Check whether we should persist literally, or via the __persist metafunction + if (!lua_getmetatable(info->luaState, -1)) { + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Not reached + } + } + + // >>>>> permTbl indexTbl ...... obj metaTbl + lua_pushstring(info->luaState, "__persist"); + // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist" + + lua_rawget(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist? + + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl nil + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Return false + } + + } else if (lua_isboolean(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl bool + if (lua_toboolean(info->luaState, -1)) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + // >>>>> permTbl indexTbl ...... obj metaTbl true */ + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return false; + } else { + lua_pushstring(info->luaState, "Metatable forbade persistence"); + lua_error(info->luaState); + + return false; // Not reached + } + } else if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl __persist + lua_pushvalue(info->luaState, -3); + // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj + + // >>>>> permTbl indexTbl ...... obj metaTbl ?func? + + if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist function did not return a function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl func + + // Write out a flag that the function exists + info->writeStream->writeSint32LE(1); + + // Serialize the function + serialize(info); + + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return true; +} + +static void serializeTable(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... tbl + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 1)) { + return; + } + + // >>>>> permTbl indexTbl ...... tbl + + // First, serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl nil + + // Now, persist all k/v pairs + while (lua_next(info->luaState, -2)) { + // >>>>> permTbl indexTbl ...... tbl k v */ + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl k v k */ + + // Serialize the key + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k */ + } + + // >>>>> permTbl indexTbl ...... tbl + + // Terminate the list with a nil + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void serializeFunction(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... func + Closure *cl = clvalue(getObject(info->luaState, -1)); + lua_checkstack(info->luaState, 2); + + if (cl->c.isC) { + /* It's a C function. For now, we aren't going to allow + * persistence of C closures, even if the "C proto" is + * already in the permanents table. */ + lua_pushstring(info->luaState, "Attempt to persist a C function"); + lua_error(info->luaState); + } else { + // It's a Lua closure + + // We don't really _NEED_ the number of upvals, but it'll simplify things a bit + info->writeStream->writeByte(cl->l.p->nups); + + // Serialize the prototype + pushProto(info->luaState, cl->l.p); + // >>>>> permTbl indexTbl ...... func proto */ + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // Serialize upvalue values (not the upvalue objects themselves) + for (byte i = 0; i < cl->l.p->nups; i++) { + // >>>>> permTbl indexTbl ...... func + pushUpValue(info->luaState, cl->l.upvals[i]); + // >>>>> permTbl indexTbl ...... func upval + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func + + // Serialize function environment + lua_getfenv(info->luaState, -1); + // >>>>> permTbl indexTbl ...... func fenv + + if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) { + // Function has the default fenv + + // >>>>> permTbl indexTbl ...... func _G + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + } + + // >>>>> permTbl indexTbl ...... func fenv/nil + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +static void serializeThread(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... thread + lua_State *threadState = lua_tothread(info->luaState, -1); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, threadState->top - threadState->stack + 1); + + if (info->luaState == threadState) { + lua_pushstring(info->luaState, "Can't persist currently running thread"); + lua_error(info->luaState); + return; /* not reached */ + } + + // Persist the stack + + // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 stackSize = static_cast(appendStackToStack_reverse(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + serialize(info); + + lua_pop(info->luaState, 1); + } + + // >>>>> permTbl indexTbl ...... thread + + // Now, serialize the CallInfo stack + + // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 numFrames = static_cast((threadState->ci - threadState->base_ci) + 1); + info->writeStream->writeUint32LE(numFrames); + + for (uint32 i = 0; i < numFrames; i++) { + CallInfo *ci = threadState->base_ci + i; + + // Same argument as above about truncation + uint32 stackBase = static_cast(ci->base - threadState->stack); + uint32 stackFunc = static_cast(ci->func - threadState->stack); + uint32 stackTop = static_cast(ci->top - threadState->stack); + + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + info->writeStream->writeUint32LE(stackTop); + + info->writeStream->writeSint32LE(ci->nresults); + + uint32 savedpc = (ci != threadState->base_ci) ? static_cast(ci->savedpc - ci_func(ci)->l.p->code) : 0u; + info->writeStream->writeUint32LE(savedpc); + } + + + // Serialize the state's other parameters, with the exception of upval stuff + + assert(threadState->nCcalls <= 1); + info->writeStream->writeByte(threadState->status); + + // Same argument as above about truncation + uint32 stackBase = static_cast(threadState->base - threadState->stack); + uint32 stackFunc = static_cast(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast(threadState->errfunc); + info->writeStream->writeUint32LE(stackOffset); + + // Finally, record upvalues which need to be reopened + // See the comment above serializeUpVal() for why we do this + + UpVal *upVal; + + // >>>>> permTbl indexTbl ...... thread + for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { + upVal = gco2uv(gcObject); + + /* Make sure upvalue is really open */ + assert(upVal->v != &upVal->u.value); + + pushUpValue(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread +} + +static void serializeProto(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... proto + Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Serialize constant refs */ + info->writeStream->writeSint32LE(proto->sizek); + + for (int i = 0; i < proto->sizek; ++i) { + pushObject(info->luaState, &proto->k[i]); + // >>>>> permTbl indexTbl ...... proto const + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize inner Proto refs + info->writeStream->writeSint32LE(proto->sizep); + + for (int i = 0; i < proto->sizep; ++i) + { + pushProto(info->luaState, proto->p[i]); + // >>>>> permTbl indexTbl ...... proto subProto */ + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast(sizeof(Instruction) * proto->sizecode); + info->writeStream->write(proto->code, len); + + + // Serialize upvalue names + info->writeStream->writeSint32LE(proto->sizeupvalues); + + for (int i = 0; i < proto->sizeupvalues; ++i) + { + pushString(info->luaState, proto->upvalues[i]); + // >>>>> permTbl indexTbl ...... proto str + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + + // Serialize local variable infos + info->writeStream->writeSint32LE(proto->sizelocvars); + + for (int i = 0; i < proto->sizelocvars; ++i) { + pushString(info->luaState, proto->locvars[i].varname); + // >>>>> permTbl indexTbl ...... proto str + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + info->writeStream->writeSint32LE(proto->locvars[i].startpc); + info->writeStream->writeSint32LE(proto->locvars[i].endpc); + } + + + // Serialize source string + pushString(info->luaState, proto->source); + // >>>>> permTbl indexTbl ...... proto sourceStr + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + uint32 len = static_cast(sizeof(int) * proto->sizelineinfo); + info->writeStream->write(proto->lineinfo, len); + } + + // Serialize linedefined and lastlinedefined + info->writeStream->writeSint32LE(proto->linedefined); + info->writeStream->writeSint32LE(proto->lastlinedefined); + + + // Serialize misc values + info->writeStream->writeByte(proto->nups); + info->writeStream->writeByte(proto->numparams); + info->writeStream->writeByte(proto->is_vararg); + info->writeStream->writeByte(proto->maxstacksize); +} + +/* Upvalues are tricky. Here's why. + * + * A particular upvalue may be either "open", in which case its member v + * points into a thread's stack, or "closed" in which case it points to the + * upvalue itself. An upvalue is closed under any of the following conditions: + * -- The function that initially declared the variable "local" returns + * -- The thread in which the closure was created is garbage collected + * + * To make things wackier, just because a thread is reachable by Lua doesn't + * mean it's in our root set. We need to be able to treat an open upvalue + * from an unreachable thread as a closed upvalue. + * + * The solution: + * (a) For the purposes of serializing, don't indicate whether an upvalue is + * closed or not. + * (b) When unserializing, pretend that all upvalues are closed. + * (c) When serializing, persist all open upvalues referenced by a thread + * that is persisted, and tag each one with the corresponding stack position + * (d) When unserializing, "reopen" each of these upvalues as the thread is + * unserialized + */ +static void serializeUpValue(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... upval + assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); + UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // We can't permit the upValue to linger around on the stack, as Lua + // will bail if its GC finds it. + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... + + pushObject(info->luaState, upValue->v); + // >>>>> permTbl indexTbl ...... obj + + serialize(info); + // >>>>> permTbl indexTbl ...... obj +} + +static void serializeUserData(SerializationInfo *info) { + // >>>>> permTbl rootObj ...... udata + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 0)) { + return; + } + + // Use literal persistence + + // Hard cast to a uint32 length + // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems + uint32 length = static_cast(uvalue(getObject(info->luaState, -1))->len); + info->writeStream->writeUint32LE(length); + + info->writeStream->write(lua_touserdata(info->luaState, -1), length); + + // Serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl rootObj ...... udata metaTbl/nil + serialize(info); + + lua_pop(info->luaState, 1); + /* perms reftbl ... udata */ +} + + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h new file mode 100644 index 0000000000..cf7d7e03ca --- /dev/null +++ b/engines/sword25/util/lua_persistence.h @@ -0,0 +1,44 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LUA_SERIALIZATION_H +#define LUA_SERIALIZATION_H + +#include "sword25/util/lua/lua.h" + + +namespace Common { +class WriteStream; +class ReadStream; +} + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream); +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_persistence_util.cpp b/engines/sword25/util/lua_persistence_util.cpp new file mode 100644 index 0000000000..8365f5d483 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -0,0 +1,346 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_persistence_util.h" + +#include "common/scummsys.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + +void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +void pushUpValue(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +StkId getObject(lua_State *luaState, int stackpos) { + if (stackpos > 0) { + lua_assert(luaState->base + stackpos - 1 < luaState->top); + return luaState->base + stackpos - 1; + } else { + lua_assert(L->top - stackpos >= L->base); + return luaState->top + stackpos; + } +} + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); + + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +size_t appendStackToStack_reverse(lua_State *from, lua_State *to) { + for (StkId id = from->top - 1; id >= from->stack; --id) { + setobj2s(to, to->top, id); + to->top++; + } + + return from->top - from->stack; +} + +void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if (G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while (prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +UpVal *makeUpValue(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +void boxUpValue_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpValue(luaState, -1); + lua_pop(luaState, 1); +} + +void boxUpValue_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h new file mode 100644 index 0000000000..345996f606 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.h @@ -0,0 +1,98 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distri8buted in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LUA_SERIALIZATION_UTIL_H +#define LUA_SERIALIZATION_UTIL_H + + +struct lua_State; + +#include "lua/lobject.h" + +typedef TValue *StkId; + +namespace Lua { + +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); + +void pushObject(lua_State *luaState, TValue *obj); +void pushProto(lua_State *luaState, Proto *proto); +void pushUpValue(lua_State *luaState, UpVal *upval); +void pushString(lua_State *luaState, TString *str); + +StkId getObject(lua_State *luaState, int stackpos); + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); +void pushClosure(lua_State *luaState, Closure *closure); + +Proto *createProto(lua_State *luaState); +Proto *makeFakeProto(lua_State *L, lu_byte nups); + +TString *createString(lua_State *luaState, const char *str, size_t len); + +UpVal *createUpValue(lua_State *luaState, int stackpos); +void unboxUpValue(lua_State *luaState); + +/* Appends one stack to another stack, but the stack is reversed in the process */ +size_t appendStackToStack_reverse(lua_State *from, lua_State *to); +void correctStack(lua_State *L, TValue *oldstack); +void lua_reallocstack(lua_State *L, int newsize); +void lua_growstack(lua_State *L, int n); + +void lua_reallocCallInfo(lua_State *lauState, int newsize); + +/* Does basically the opposite of luaC_link(). + * Right now this function is rather inefficient; it requires traversing the + * entire root GC set in order to find one object. If the GC list were doubly + * linked this would be much easier, but there's no reason for Lua to have + * that. */ +void GCUnlink(lua_State *luaState, GCObject *gco); + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); +Proto *lua_newproto(lua_State *luaState) ; + +UpVal *makeUpValue(lua_State *luaState, int stackPos); +/** + * The GC is not fond of finding upvalues in tables. We get around this + * during persistence using a weakly keyed table, so that the GC doesn't + * bother to mark them. This won't work in unpersisting, however, since + * if we make the values weak they'll be collected (since nothing else + * references them). Our solution, during unpersisting, is to represent + * upvalues as dummy functions, each with one upvalue. + */ +void boxUpValue_start(lua_State *luaState); +void boxUpValue_finish(lua_State *luaState); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_serialization.h b/engines/sword25/util/lua_serialization.h deleted file mode 100644 index 549ea7968d..0000000000 --- a/engines/sword25/util/lua_serialization.h +++ /dev/null @@ -1,44 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef LUA_SERIALIZATION_H -#define LUA_SERIALIZATION_H - -#include "sword25/util/lua/lua.h" - - -namespace Common { -class WriteStream; -class ReadStream; -} - - -namespace Lua { - -#define PERMANENT_TYPE 101 - -void serializeLua(lua_State *luaState, Common::WriteStream *writeStream); -void unserializeLua(lua_State *luaState, Common::ReadStream *readStream); - -} // End of namespace Lua - -#endif diff --git a/engines/sword25/util/lua_serialization_util.cpp b/engines/sword25/util/lua_serialization_util.cpp deleted file mode 100644 index fc3f73eca6..0000000000 --- a/engines/sword25/util/lua_serialization_util.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distri8buted in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "sword25/util/lua_serialization_util.h" - -#include "common/scummsys.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" -#include "lua/lopcodes.h" - - -namespace Lua { - -void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { - global_State *globalState = G(luaState); - - block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); - globalState->totalbytes = (globalState->totalbytes - osize) + nsize; - - return block; -} - -void pushObject(lua_State *luaState, TValue *obj) { - setobj2s(luaState, luaState->top, obj); - - api_check(luaState, luaState->top < luaState->ci->top); - luaState->top++; -} - -void pushProto(lua_State *luaState, Proto *proto) { - TValue obj; - setptvalue(luaState, &obj, proto); - - pushObject(luaState, &obj); -} - -void pushUpValue(lua_State *luaState, UpVal *upval) { - TValue obj; - - obj.value.gc = cast(GCObject *, upval); - obj.tt = LUA_TUPVAL; - checkliveness(G(L), obj); - - pushObject(luaState, &obj); -} - -void pushString(lua_State *luaState, TString *str) { - TValue o; - setsvalue(luaState, &o, str); - - pushObject(luaState, &o); -} - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -StkId getObject(lua_State *luaState, int stackpos) { - if (stackpos > 0) { - lua_assert(luaState->base + stackpos - 1 < luaState->top); - return luaState->base + stackpos - 1; - } else { - lua_assert(L->top - stackpos >= L->base); - return luaState->top + stackpos; - } -} - -void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { - global_State *globalState = G(luaState); - - obj->gch.next = globalState->rootgc; - globalState->rootgc = obj; - obj->gch.marked = luaC_white(globalState); - obj->gch.tt = type; -} - -Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { - Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); - lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); - - c->l.isC = 0; - c->l.env = elementTable; - c->l.nupvalues = cast_byte(numElements); - - while (numElements--) { - c->l.upvals[numElements] = NULL; - } - - return c; -} - -void pushClosure(lua_State *luaState, Closure *closure) { - TValue obj; - setclvalue(luaState, &obj, closure); - pushObject(luaState, &obj); -} - -Proto *createProto(lua_State *luaState) { - Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); - - newProto->k = NULL; - newProto->sizek = 0; - newProto->p = NULL; - newProto->sizep = 0; - newProto->code = NULL; - newProto->sizecode = 0; - newProto->sizelineinfo = 0; - newProto->sizeupvalues = 0; - newProto->nups = 0; - newProto->upvalues = NULL; - newProto->numparams = 0; - newProto->is_vararg = 0; - newProto->maxstacksize = 0; - newProto->lineinfo = NULL; - newProto->sizelocvars = 0; - newProto->locvars = NULL; - newProto->linedefined = 0; - newProto->lastlinedefined = 0; - newProto->source = NULL; - - return newProto; -} - -TString *createString(lua_State *luaState, const char *str, size_t len) { - TString *res; - lua_pushlstring(luaState, str, len); - - res = rawtsvalue(luaState->top - 1); - lua_pop(luaState, 1); - - return res; -} - -Proto *makeFakeProto(lua_State *L, lu_byte nups) { - Proto *p = createProto(L); - - p->sizelineinfo = 1; - p->lineinfo = lua_newVector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = lua_newVector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = createString(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -UpVal *createUpValue(lua_State *luaState, int stackpos) { - UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); - lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); - upValue->tt = LUA_TUPVAL; - upValue->v = &upValue->u.value; - upValue->u.l.prev = NULL; - upValue->u.l.next = NULL; - - const TValue *o2 = (TValue *)getObject(luaState, stackpos); - upValue->v->value = o2->value; - upValue->v->tt = o2->tt; - checkliveness(G(L), upValue->v); - - return upValue; -} - -void unboxUpValue(lua_State *luaState) { - // >>>>> ...... func - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getObject(luaState, -1)); - uv = lcl->upvals[0]; - - lua_pop(luaState, 1); - // >>>>> ...... - - pushUpValue(luaState, uv); - // >>>>> ...... upValue -} - -size_t appendStackToStack_reverse(lua_State *from, lua_State *to) { - for (StkId id = from->top - 1; id >= from->stack; --id) { - setobj2s(to, to->top, id); - to->top++; - } - - return from->top - from->stack; -} - -void correctStack(lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - -void lua_reallocstack(lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - - lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack + newsize; - correctStack(L, oldstack); -} - -void lua_growstack(lua_State *L, int n) { - // Double size is enough? - if (n <= L->stacksize) { - lua_reallocstack(L, 2 * L->stacksize); - } else { - lua_reallocstack(L, L->stacksize + n); - } -} - -void lua_reallocCallInfo(lua_State *lauState, int newsize) { - CallInfo *oldci = lauState->base_ci; - lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); - - lauState->size_ci = newsize; - lauState->ci = (lauState->ci - oldci) + lauState->base_ci; - lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; -} - -void GCUnlink(lua_State *luaState, GCObject *gco) { - GCObject *prevslot; - if (G(luaState)->rootgc == gco) { - G(luaState)->rootgc = G(luaState)->rootgc->gch.next; - return; - } - - prevslot = G(luaState)->rootgc; - while (prevslot->gch.next != gco) { - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { - lua_pushlstring(luaState, str, len); - TString *luaStr = &(luaState->top - 1)->value.gc->ts; - - lua_pop(luaState, 1); - - return luaStr; -} - -void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { - global_State *g = G(luaState); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *lua_newproto(lua_State *luaState) { - Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_link(luaState, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - -UpVal *makeUpValue(lua_State *luaState, int stackPos) { - UpVal *uv = lua_new(luaState, UpVal); - lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - - setobj(luaState, uv->v, getObject(luaState, stackPos)); - - return uv; -} - -void boxUpValue_start(lua_State *luaState) { - LClosure *closure; - closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); - pushClosure(luaState, (Closure *)closure); - // >>>>> ...... func - closure->p = makeFakeProto(luaState, 1); - - // Temporarily initialize the upvalue to nil - lua_pushnil(luaState); - closure->upvals[0] = makeUpValue(luaState, -1); - lua_pop(luaState, 1); -} - -void boxUpValue_finish(lua_State *luaState) { - // >>>>> ...... func obj - LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); - - lcl->upvals[0]->u.value = *getObject(luaState, -1); - lua_pop(luaState, 1); - // >>>>> ...... func -} - -} // End of namespace Lua diff --git a/engines/sword25/util/lua_serialization_util.h b/engines/sword25/util/lua_serialization_util.h deleted file mode 100644 index 345996f606..0000000000 --- a/engines/sword25/util/lua_serialization_util.h +++ /dev/null @@ -1,98 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distri8buted in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef LUA_SERIALIZATION_UTIL_H -#define LUA_SERIALIZATION_UTIL_H - - -struct lua_State; - -#include "lua/lobject.h" - -typedef TValue *StkId; - -namespace Lua { - -#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) -#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) -#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) -#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) -#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) - -void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); - -void pushObject(lua_State *luaState, TValue *obj); -void pushProto(lua_State *luaState, Proto *proto); -void pushUpValue(lua_State *luaState, UpVal *upval); -void pushString(lua_State *luaState, TString *str); - -StkId getObject(lua_State *luaState, int stackpos); - -void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); - -#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) - -Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); -void pushClosure(lua_State *luaState, Closure *closure); - -Proto *createProto(lua_State *luaState); -Proto *makeFakeProto(lua_State *L, lu_byte nups); - -TString *createString(lua_State *luaState, const char *str, size_t len); - -UpVal *createUpValue(lua_State *luaState, int stackpos); -void unboxUpValue(lua_State *luaState); - -/* Appends one stack to another stack, but the stack is reversed in the process */ -size_t appendStackToStack_reverse(lua_State *from, lua_State *to); -void correctStack(lua_State *L, TValue *oldstack); -void lua_reallocstack(lua_State *L, int newsize); -void lua_growstack(lua_State *L, int n); - -void lua_reallocCallInfo(lua_State *lauState, int newsize); - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -void GCUnlink(lua_State *luaState, GCObject *gco); - -TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); -void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); -Proto *lua_newproto(lua_State *luaState) ; - -UpVal *makeUpValue(lua_State *luaState, int stackPos); -/** - * The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. - */ -void boxUpValue_start(lua_State *luaState); -void boxUpValue_finish(lua_State *luaState); - -} // End of namespace Lua - -#endif diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp deleted file mode 100644 index 55b6257324..0000000000 --- a/engines/sword25/util/lua_serializer.cpp +++ /dev/null @@ -1,787 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "sword25/util/lua_serialization.h" - -#include "sword25/util/double_serializer.h" -#include "sword25/util/lua_serialization_util.h" - -#include "common/stream.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" - - -namespace Lua { - -#define PERMANENT_TYPE 101 - -struct SerializationInfo { - lua_State *luaState; - Common::WriteStream *writeStream; - uint counter; -}; - -static void serialize(SerializationInfo *info); - -static void serializeBoolean(SerializationInfo *info); -static void serializeNumber(SerializationInfo *info); -static void serializeString(SerializationInfo *info); -static void serializeTable(SerializationInfo *info); -static void serializeFunction(SerializationInfo *info); -static void serializeThread(SerializationInfo *info); -static void serializeProto(SerializationInfo *info); -static void serializeUpValue(SerializationInfo *info); -static void serializeUserData(SerializationInfo *info); - - -void serializeLua(lua_State *luaState, Common::WriteStream *writeStream) { - SerializationInfo info; - info.luaState = luaState; - info.writeStream = writeStream; - info.counter = 0u; - - // The process starts with the lua stack as follows: - // >>>>> permTbl rootObj - // That's the table of permanents and the root object to be serialized - - // Make sure there is enough room on the stack - lua_checkstack(luaState, 4); - assert(lua_gettop(luaState) == 2); - // And that the root isn't nil - assert(!lua_isnil(luaState, 2)); - - // Create a table to hold indexes of everything that's serialized - // This allows us to only serialize an object once - // Every other time, just reference the index - lua_newtable(luaState); - // >>>>> permTbl rootObj indexTbl - - // Now we're going to make the table weakly keyed. This prevents the - // GC from visiting it and trying to mark things it doesn't want to - // mark in tables, e.g. upvalues. All objects in the table are - // a priori reachable, so it doesn't matter that we do this. - - // Create the metatable - lua_newtable(luaState); - // >>>>> permTbl rootObj indexTbl metaTbl - - lua_pushstring(luaState, "__mode"); - // >>>>> permTbl rootObj indexTbl metaTbl "__mode" - - lua_pushstring(luaState, "k"); - // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k" - - lua_settable(luaState, 4); - // >>>>> permTbl rootObj indexTbl metaTbl - - lua_setmetatable(luaState, 3); - // >>>>> permTbl rootObj indexTbl - - // Swap the indexTable and the rootObj - lua_insert(luaState, 2); - // >>>>> permTbl indexTbl rootObj - - // Serialize the root recursively - serialize(&info); - - // Return the stack back to the original state - lua_remove(luaState, 2); - // >>>>> permTbl rootObj -} - -static void serialize(SerializationInfo *info) { - // The stack can potentially have many things on it - // The object we want to serialize is the item on the top of the stack - // >>>>> permTbl indexTbl rootObj ...... obj - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - // If the object has already been written, don't write it again - // Instead write the index of the object from the indexTbl - - // Check the indexTbl - lua_pushvalue(info->luaState, -1); - // >>>>> permTbl indexTbl rootObj ...... obj obj - - lua_rawget(info->luaState, 2); - // >>>>> permTbl indexTbl rootObj ...... obj ?index? - - // If the index isn't nil, the object has already been written - if (!lua_isnil(info->luaState, -1)) { - // Write out a flag that indicates that it's an index - info->writeStream->writeByte(0); - - // Retrieve the index from the stack - uint *index = (uint *)lua_touserdata(info->luaState, -1); - - // Write out the index - info->writeStream->writeUint32LE(*index); - - // Pop the index off the stack - lua_pop(info->luaState, 1); - - return; - } - - // Pop the nil off the stack - lua_pop(info->luaState, 1); - - // Write out a flag that indicates that this is a real object - info->writeStream->writeByte(1); - - // If the object itself is nil, then write out a zero as a placeholder - if (lua_isnil(info->luaState, -1)) { - info->writeStream->writeByte(0); - - return; - } - - // Add the object to the indexTbl - - lua_pushvalue(info->luaState, -1); - // >>>>> permTbl indexTbl rootObj ...... obj obj - - uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint)); - *ref = ++(info->counter); - // >>>>> permTbl indexTbl rootObj ...... obj obj index - - lua_rawset(info->luaState, 2); - // >>>>> permTbl indexTbl rootObj ...... obj - - - // Write out the index - info->writeStream->writeUint32LE(info->counter); - - - // Objects that are in the permanents table are serialized in a special way - - lua_pushvalue(info->luaState, -1); - // >>>>> permTbl indexTbl rootObj ...... obj obj - - lua_gettable(info->luaState, 1); - // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey? - - if (!lua_isnil(info->luaState, -1)) { - // Write out the type - info->writeStream->writeSint32LE(PERMANENT_TYPE); - - // Serialize the key - serialize(info); - - // Pop the key off the stack - lua_pop(info->luaState, 1); - - return; - } - - // Pop the nil off the stack - lua_pop(info->luaState, 1); - - // Query the type of the object - int objType = lua_type(info->luaState, -1); - - // Write it out - info->writeStream->writeSint32LE(objType); - - // Serialize the object by its type - - switch (objType) { - case LUA_TBOOLEAN: - serializeBoolean(info); - break; - case LUA_TLIGHTUSERDATA: - // You can't serialize a pointer - // It would be meaningless on the next run - assert(0); - break; - case LUA_TNUMBER: - serializeNumber(info); - break; - case LUA_TSTRING: - serializeString(info); - break; - case LUA_TTABLE: - serializeTable(info); - break; - case LUA_TFUNCTION: - serializeFunction(info); - break; - case LUA_TTHREAD: - serializeThread(info); - break; - case LUA_TPROTO: - serializeProto(info); - break; - case LUA_TUPVAL: - serializeUpValue(info); - break; - case LUA_TUSERDATA: - serializeUserData(info); - break; - default: - assert(0); - } -} - -static void serializeBoolean(SerializationInfo *info) { - int value = lua_toboolean(info->luaState, -1); - - info->writeStream->writeSint32LE(value); -} - -static void serializeNumber(SerializationInfo *info) { - lua_Number value = lua_tonumber(info->luaState, -1); - - #if 1 - Util::SerializedDouble serializedValue(Util::encodeDouble(value)); - - info->writeStream->writeUint32LE(serializedValue.significandOne); - info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); - info->writeStream->writeSint16LE(serializedValue.exponent); - #else - // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. - // Also, I don't know if we can assume all compilers use IEEE double - // Therefore, I have chosen to store the double as a string. - Common::String buffer = Common::String::format("%f", value); - - info->writeStream->write(buffer.c_str(), buffer.size()); - #endif - -} - -static void serializeString(SerializationInfo *info) { - // Hard cast to a uint32 to force size_t to an explicit size - // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems - uint32 length = static_cast(lua_strlen(info->luaState, -1)); - info->writeStream->writeUint32LE(length); - - const char *str = lua_tostring(info->luaState, -1); - info->writeStream->write(str, length); -} - -/* Choose whether to do a regular or special persistence based on an object's - * metatable. "default" is whether the object, if it doesn't have a __persist - * entry, is literally persistable or not. - * Pushes the unpersist closure and returns true if special persistence is - * used. */ -static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 4); - - // Check whether we should persist literally, or via the __persist metafunction - if (!lua_getmetatable(info->luaState, -1)) { - if (defaction) { - // Write out a flag declaring that the object isn't special and should be persisted normally - info->writeStream->writeSint32LE(0); - - return false; - } else { - lua_pushstring(info->luaState, "Type not literally persistable by default"); - lua_error(info->luaState); - - return false; // Not reached - } - } - - // >>>>> permTbl indexTbl ...... obj metaTbl - lua_pushstring(info->luaState, "__persist"); - // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist" - - lua_rawget(info->luaState, -2); - // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist? - - if (lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... obj metaTbl nil - lua_pop(info->luaState, 2); - // >>>>> permTbl indexTbl ...... obj - - if (defaction) { - // Write out a flag declaring that the object isn't special and should be persisted normally - info->writeStream->writeSint32LE(0); - - return false; - } else { - lua_pushstring(info->luaState, "Type not literally persistable by default"); - lua_error(info->luaState); - - return false; // Return false - } - - } else if (lua_isboolean(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... obj metaTbl bool - if (lua_toboolean(info->luaState, -1)) { - // Write out a flag declaring that the object isn't special and should be persisted normally - info->writeStream->writeSint32LE(0); - - // >>>>> permTbl indexTbl ...... obj metaTbl true */ - lua_pop(info->luaState, 2); - // >>>>> permTbl indexTbl ...... obj - - return false; - } else { - lua_pushstring(info->luaState, "Metatable forbade persistence"); - lua_error(info->luaState); - - return false; // Not reached - } - } else if (!lua_isfunction(info->luaState, -1)) { - lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); - lua_error(info->luaState); - } - - // >>>>> permTbl indexTbl ...... obj metaTbl __persist - lua_pushvalue(info->luaState, -3); - // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj - - // >>>>> permTbl indexTbl ...... obj metaTbl ?func? - - if (!lua_isfunction(info->luaState, -1)) { - lua_pushstring(info->luaState, "__persist function did not return a function"); - lua_error(info->luaState); - } - - // >>>>> permTbl indexTbl ...... obj metaTbl func - - // Write out a flag that the function exists - info->writeStream->writeSint32LE(1); - - // Serialize the function - serialize(info); - - lua_pop(info->luaState, 2); - // >>>>> permTbl indexTbl ...... obj - - return true; -} - -static void serializeTable(SerializationInfo *info) { - // >>>>> permTbl indexTbl ...... tbl - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 3); - - // Test if the object needs special serialization - if (serializeSpecialObject(info, 1)) { - return; - } - - // >>>>> permTbl indexTbl ...... tbl - - // First, serialize the metatable (if any) - if (!lua_getmetatable(info->luaState, -1)) { - lua_pushnil(info->luaState); - } - - // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl - - - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... tbl nil - - // Now, persist all k/v pairs - while (lua_next(info->luaState, -2)) { - // >>>>> permTbl indexTbl ...... tbl k v */ - - lua_pushvalue(info->luaState, -2); - // >>>>> permTbl indexTbl ...... tbl k v k */ - - // Serialize the key - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl k v */ - - // Serialize the value - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl k */ - } - - // >>>>> permTbl indexTbl ...... tbl - - // Terminate the list with a nil - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... tbl - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl -} - -static void serializeFunction(SerializationInfo *info) { - // >>>>> permTbl indexTbl ...... func - Closure *cl = clvalue(getObject(info->luaState, -1)); - lua_checkstack(info->luaState, 2); - - if (cl->c.isC) { - /* It's a C function. For now, we aren't going to allow - * persistence of C closures, even if the "C proto" is - * already in the permanents table. */ - lua_pushstring(info->luaState, "Attempt to persist a C function"); - lua_error(info->luaState); - } else { - // It's a Lua closure - - // We don't really _NEED_ the number of upvals, but it'll simplify things a bit - info->writeStream->writeByte(cl->l.p->nups); - - // Serialize the prototype - pushProto(info->luaState, cl->l.p); - // >>>>> permTbl indexTbl ...... func proto */ - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - - // Serialize upvalue values (not the upvalue objects themselves) - for (byte i = 0; i < cl->l.p->nups; i++) { - // >>>>> permTbl indexTbl ...... func - pushUpValue(info->luaState, cl->l.upvals[i]); - // >>>>> permTbl indexTbl ...... func upval - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } - - // >>>>> permTbl indexTbl ...... func - - // Serialize function environment - lua_getfenv(info->luaState, -1); - // >>>>> permTbl indexTbl ...... func fenv - - if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) { - // Function has the default fenv - - // >>>>> permTbl indexTbl ...... func _G - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... func nil - } - - // >>>>> permTbl indexTbl ...... func fenv/nil - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } -} - -static void serializeThread(SerializationInfo *info) { - // >>>>> permTbl indexTbl ...... thread - lua_State *threadState = lua_tothread(info->luaState, -1); - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, threadState->top - threadState->stack + 1); - - if (info->luaState == threadState) { - lua_pushstring(info->luaState, "Can't persist currently running thread"); - lua_error(info->luaState); - return; /* not reached */ - } - - // Persist the stack - - // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems - uint32 stackSize = static_cast(appendStackToStack_reverse(threadState, info->luaState)); - info->writeStream->writeUint32LE(stackSize); - - // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ - for (; stackSize > 0; --stackSize) { - serialize(info); - - lua_pop(info->luaState, 1); - } - - // >>>>> permTbl indexTbl ...... thread - - // Now, serialize the CallInfo stack - - // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems - uint32 numFrames = static_cast((threadState->ci - threadState->base_ci) + 1); - info->writeStream->writeUint32LE(numFrames); - - for (uint32 i = 0; i < numFrames; i++) { - CallInfo *ci = threadState->base_ci + i; - - // Same argument as above about truncation - uint32 stackBase = static_cast(ci->base - threadState->stack); - uint32 stackFunc = static_cast(ci->func - threadState->stack); - uint32 stackTop = static_cast(ci->top - threadState->stack); - - info->writeStream->writeUint32LE(stackBase); - info->writeStream->writeUint32LE(stackFunc); - info->writeStream->writeUint32LE(stackTop); - - info->writeStream->writeSint32LE(ci->nresults); - - uint32 savedpc = (ci != threadState->base_ci) ? static_cast(ci->savedpc - ci_func(ci)->l.p->code) : 0u; - info->writeStream->writeUint32LE(savedpc); - } - - - // Serialize the state's other parameters, with the exception of upval stuff - - assert(threadState->nCcalls <= 1); - info->writeStream->writeByte(threadState->status); - - // Same argument as above about truncation - uint32 stackBase = static_cast(threadState->base - threadState->stack); - uint32 stackFunc = static_cast(threadState->top - threadState->stack); - info->writeStream->writeUint32LE(stackBase); - info->writeStream->writeUint32LE(stackFunc); - - // Same argument as above about truncation - uint32 stackOffset = static_cast(threadState->errfunc); - info->writeStream->writeUint32LE(stackOffset); - - // Finally, record upvalues which need to be reopened - // See the comment above serializeUpVal() for why we do this - - UpVal *upVal; - - // >>>>> permTbl indexTbl ...... thread - for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { - upVal = gco2uv(gcObject); - - /* Make sure upvalue is really open */ - assert(upVal->v != &upVal->u.value); - - pushUpValue(info->luaState, upVal); - // >>>>> permTbl indexTbl ...... thread upVal - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - - // Same argument as above about truncation - uint32 stackpos = static_cast(upVal->v - threadState->stack); - info->writeStream->writeUint32LE(stackpos); - } - - // >>>>> permTbl indexTbl ...... thread - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... thread nil - - // Use nil as a terminator - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread -} - -static void serializeProto(SerializationInfo *info) { - // >>>>> permTbl indexTbl ...... proto - Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - // Serialize constant refs */ - info->writeStream->writeSint32LE(proto->sizek); - - for (int i = 0; i < proto->sizek; ++i) { - pushObject(info->luaState, &proto->k[i]); - // >>>>> permTbl indexTbl ...... proto const - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - - // >>>>> permTbl indexTbl ...... proto - - // Serialize inner Proto refs - info->writeStream->writeSint32LE(proto->sizep); - - for (int i = 0; i < proto->sizep; ++i) - { - pushProto(info->luaState, proto->p[i]); - // >>>>> permTbl indexTbl ...... proto subProto */ - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - - // >>>>> permTbl indexTbl ...... proto - - // Serialize the code - info->writeStream->writeSint32LE(proto->sizecode); - - uint32 len = static_cast(sizeof(Instruction) * proto->sizecode); - info->writeStream->write(proto->code, len); - - - // Serialize upvalue names - info->writeStream->writeSint32LE(proto->sizeupvalues); - - for (int i = 0; i < proto->sizeupvalues; ++i) - { - pushString(info->luaState, proto->upvalues[i]); - // >>>>> permTbl indexTbl ...... proto str - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - - - // Serialize local variable infos - info->writeStream->writeSint32LE(proto->sizelocvars); - - for (int i = 0; i < proto->sizelocvars; ++i) { - pushString(info->luaState, proto->locvars[i].varname); - // >>>>> permTbl indexTbl ...... proto str - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - info->writeStream->writeSint32LE(proto->locvars[i].startpc); - info->writeStream->writeSint32LE(proto->locvars[i].endpc); - } - - - // Serialize source string - pushString(info->luaState, proto->source); - // >>>>> permTbl indexTbl ...... proto sourceStr - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - // Serialize line numbers - info->writeStream->writeSint32LE(proto->sizelineinfo); - - if (proto->sizelineinfo) { - uint32 len = static_cast(sizeof(int) * proto->sizelineinfo); - info->writeStream->write(proto->lineinfo, len); - } - - // Serialize linedefined and lastlinedefined - info->writeStream->writeSint32LE(proto->linedefined); - info->writeStream->writeSint32LE(proto->lastlinedefined); - - - // Serialize misc values - info->writeStream->writeByte(proto->nups); - info->writeStream->writeByte(proto->numparams); - info->writeStream->writeByte(proto->is_vararg); - info->writeStream->writeByte(proto->maxstacksize); -} - -/* Upvalues are tricky. Here's why. - * - * A particular upvalue may be either "open", in which case its member v - * points into a thread's stack, or "closed" in which case it points to the - * upvalue itself. An upvalue is closed under any of the following conditions: - * -- The function that initially declared the variable "local" returns - * -- The thread in which the closure was created is garbage collected - * - * To make things wackier, just because a thread is reachable by Lua doesn't - * mean it's in our root set. We need to be able to treat an open upvalue - * from an unreachable thread as a closed upvalue. - * - * The solution: - * (a) For the purposes of serializing, don't indicate whether an upvalue is - * closed or not. - * (b) When unserializing, pretend that all upvalues are closed. - * (c) When serializing, persist all open upvalues referenced by a thread - * that is persisted, and tag each one with the corresponding stack position - * (d) When unserializing, "reopen" each of these upvalues as the thread is - * unserialized - */ -static void serializeUpValue(SerializationInfo *info) { - // >>>>> permTbl indexTbl ...... upval - assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); - UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - // We can't permit the upValue to linger around on the stack, as Lua - // will bail if its GC finds it. - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... - - pushObject(info->luaState, upValue->v); - // >>>>> permTbl indexTbl ...... obj - - serialize(info); - // >>>>> permTbl indexTbl ...... obj -} - -static void serializeUserData(SerializationInfo *info) { - // >>>>> permTbl rootObj ...... udata - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - // Test if the object needs special serialization - if (serializeSpecialObject(info, 0)) { - return; - } - - // Use literal persistence - - // Hard cast to a uint32 length - // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems - uint32 length = static_cast(uvalue(getObject(info->luaState, -1))->len); - info->writeStream->writeUint32LE(length); - - info->writeStream->write(lua_touserdata(info->luaState, -1), length); - - // Serialize the metatable (if any) - if (!lua_getmetatable(info->luaState, -1)) { - lua_pushnil(info->luaState); - } - - // >>>>> permTbl rootObj ...... udata metaTbl/nil - serialize(info); - - lua_pop(info->luaState, 1); - /* perms reftbl ... udata */ -} - - -} // End of namespace Lua diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp new file mode 100644 index 0000000000..aa924ff7c5 --- /dev/null +++ b/engines/sword25/util/lua_unpersist.cpp @@ -0,0 +1,698 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serializer.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +struct UnSerializationInfo { + lua_State *luaState; + Common::ReadStream *readStream; +}; + +static void unserialize(UnSerializationInfo *info); + +static void unserializeBoolean(UnSerializationInfo *info); +static void unserializeNumber(UnSerializationInfo *info); +static void unserializeString(UnSerializationInfo *info); +static void unserializeTable(UnSerializationInfo *info, int index); +static void unserializeFunction(UnSerializationInfo *info, int index); +static void unserializeThread(UnSerializationInfo *info, int index); +static void unserializeProto(UnSerializationInfo *info, int index); +static void unserializeUpValue(UnSerializationInfo *info, int index); +static void unserializeUserData(UnSerializationInfo *info, int index); +static void unserializePermanent(UnSerializationInfo *info, int index); + + +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { + UnSerializationInfo info; + info.luaState = luaState; + info.readStream = readStream; + + // The process starts with the lua stack as follows: + // >>>>> permTbl + // That's the table of permanents + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 3); + + // Create a table to hold indexes of everything thats already been read + lua_newtable(luaState); + // >>>>> permTbl indexTbl + + // Prevent garbage collection while we unserialize + lua_gc(luaState, LUA_GCSTOP, 0); + + // Unserialize the root object + unserialize(&info); + // >>>>> permTbl indexTbl rootObj + + // Re-start garbage collection + lua_gc(luaState, LUA_GCRESTART, 0); + + // Remove the indexTbl + lua_replace(luaState, 2); + // >>>>> permTbl rootObj +} + +/* The object is left on the stack. This is primarily used by unserialize, but + * may be used by GCed objects that may incur cycles in order to preregister + * the object. */ +static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... obj index + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj index obj + + // Push the k/v pair into the indexTbl + lua_settable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj +} + +static void unserialize(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte isARealValue = info->readStream->readByte(); + if (isARealValue) { + int index = info->readStream->readSint32LE(); + int type = info->readStream->readSint32LE(); + + switch (type) { + case LUA_TBOOLEAN: + unserializeBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + unserializeNumber(info); + break; + case LUA_TSTRING: + unserializeString(info); + break; + case LUA_TTABLE: + unserializeTable(info, index); + break; + case LUA_TFUNCTION: + unserializeFunction(info, index); + break; + case LUA_TTHREAD: + unserializeThread(info, index); + break; + case LUA_TPROTO: + unserializeProto(info, index); + break; + case LUA_TUPVAL: + unserializeUpValue(info, index); + break; + case LUA_TUSERDATA: + unserializeUserData(info, index); + break; + case PERMANENT_TYPE: + unserializePermanent(info, index); + break; + default: + assert(0); + } + + + // >>>>> permTbl indexTbl ...... obj + assert(lua_type(info->luaState, -1) == type || + type == PERMANENT_TYPE || + // Remember, upvalues get a special dispensation, as described in boxUpValue + (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); + + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... obj + } else { + int index = info->readStream->readSint32LE(); + + if (index == 0) { + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... nil + } else { + // Fetch the object from the indexTbl + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... index + + lua_gettable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... ?obj? + + assert(!lua_isnil(info->luaState, -1)); + } + // >>>>> permTbl indexTbl ...... obj/nil + } + + // >>>>> permTbl indexTbl ...... obj/nil +} + +static void unserializeBoolean(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int value = info->readStream->readSint32LE(); + + lua_pushboolean(info->luaState, value); + // >>>>> permTbl indexTbl ...... bool +} + +static void unserializeNumber(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // Read the serialized double + Util::SerializedDouble serializedValue; + serializedValue.significandOne = info->readStream->readUint32LE(); + serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); + serializedValue.exponent = info->readStream->readSint16LE(); + + lua_Number value = Util::decodeDouble(serializedValue); + + lua_pushnumber(info->luaState, value); + // >>>>> permTbl indexTbl ...... num +} + +static void unserializeString(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + uint32 length = info->readStream->readUint32LE(); + char *string = new char[length]; + + info->readStream->read(string, length); + lua_pushlstring(info->luaState, string, length); + + // >>>>> permTbl indexTbl ...... string + + delete[] string; +} + +static void unserializeSpecialTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + unserialize(info); + + // >>>>> permTbl indexTbl ...... spfunc + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void unserializeLiteralTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Preregister table for handling of cycles + lua_newtable(info->luaState); + + // >>>>> permTbl indexTbl ...... tbl + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + + // Unserialize metatable + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? + + if (lua_istable(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl metaTbl + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl + } else { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + } + // >>>>> permTbl indexTbl ...... tbl + + + while (1) { + // >>>>> permTbl indexTbl ...... tbl + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl key/nil + + // The table serialization is nil terminated + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + break; + } + + // >>>>> permTbl indexTbl ...... tbl key + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unserializeTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int isSpecial = info->readStream->readSint32LE(); + + if (isSpecial) { + unserializeSpecialTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } else { + unserializeLiteralTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unserializeFunction(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte numUpValues = info->readStream->readByte(); + + LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + pushClosure(info->luaState, (Closure *)lclosure); + // >>>>> permTbl indexTbl ...... func + + // Put *some* proto in the closure, before the GC can find it + lclosure->p = makeFakeProto(info->luaState, numUpValues); + + //Also, we need to temporarily fill the upvalues + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + + for (byte i = 0; i < numUpValues; ++i) { + lclosure->upvals[i] = createUpValue(info->luaState, -1); + } + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // I can't see offhand how a function would ever get to be self- + // referential, but just in case let's register it early + registerObjectInIndexTable(info, index); + + // Now that it's safe, we can get the real proto + unserialize(info); + // >>>>> permTbl indexTbl ...... func proto + + lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + for (byte i = 0; i < numUpValues; ++i) { + // >>>>> permTbl indexTbl ...... func + unserialize(info); + // >>>>> permTbl indexTbl ...... func func2 + + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... func upValue + lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // Finally, the fenv + unserialize(info); + + // >>>>> permTbl indexTbl ...... func ?fenv/nil? + if (!lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... func fenv + lua_setfenv(info->luaState, -2); + // >>>>> permTbl indexTbl ...... func + } else { + // >>>>> permTbl indexTbl ...... func nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func +} + +void unserializeThread(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + lua_State *L2; + uint32 stacklimit = 0; + + L2 = lua_newthread(info->luaState); + lua_checkstack(info->luaState, 3); + + // L1: permTbl indexTbl ...... thread + // L2: (empty) + registerObjectInIndexTable(info, index); + + // First, deserialize the object stack + uint32 stackSize = info->readStream->readUint32LE(); + lua_growstack(info->luaState, (int)stackSize); + + // Make sure that the first stack element (a nil, representing + // the imaginary top-level C function) is written to the very, + // very bottom of the stack + L2->top--; + for (uint32 i = 0; i < stackSize; ++i) { + unserialize(info); + // L1: permTbl indexTbl ...... thread obj* + } + + lua_xmove(info->luaState, L2, stackSize); + // L1: permTbl indexTbl ...... thread + // L2: obj* + + // Hereafter, stacks refer to L1 + + + // Now, deserialize the CallInfo stack + + uint32 numFrames = info->readStream->readUint32LE(); + + lua_reallocCallInfo(L2, numFrames * 2); + for (uint32 i = 0; i < numFrames; ++i) { + CallInfo *ci = L2->base_ci + i; + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stackfunc = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + ci->nresults = info->readStream->readSint32LE(); + + uint32 savedpc = info->readStream->readUint32LE(); + + if (stacklimit < stacktop) { + stacklimit = stacktop; + } + + ci->base = L2->stack + stackbase; + ci->func = L2->stack + stackfunc; + ci->top = L2->stack + stacktop; + ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; + ci->tailcalls = 0; + + // Update the pointer each time, to keep the GC happy + L2->ci = ci; + } + + // >>>>> permTbl indexTbl ...... thread + // Deserialize the state's other parameters, with the exception of upval stuff + + L2->savedpc = L2->ci->savedpc; + L2->status = info->readStream->readByte(); + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + + L2->errfunc = info->readStream->readUint32LE(); + + L2->base = L2->stack + stackbase; + L2->top = L2->stack + stacktop; + + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal *uv; + GCObject **nextslot = &L2->openupval; + global_State *g = G(L2); + + while (true) { + unserialize(info); + // >>>>> permTbl indexTbl ...... thread upVal/nil + + // The list is terminated by a nil + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... thread nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + break; + } + + // >>>>> permTbl indexTbl ...... thread boxedUpVal + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... thread boxedUpVal + + uv = &(getObject(info->luaState, -1)->value.gc->uv); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + uint32 stackpos = info->readStream->readUint32LE(); + uv->v = L2->stack + stackpos; + + GCUnlink(info->luaState, (GCObject *)uv); + + uv->marked = luaC_white(g); + *nextslot = (GCObject *)uv; + nextslot = &uv->next; + uv->u.l.prev = &G(L2)->uvhead; + uv->u.l.next = G(L2)->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + G(L2)->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + } + *nextslot = NULL; + + // The stack must be valid at least to the highest value among the CallInfos + // 'top' and the values up to there must be filled with 'nil' + lua_checkstack(L2, (int)stacklimit); + for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { + setnilvalue(o); + } +} + +void unserializeProto(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // We have to be careful. The GC expects a lot out of protos. In particular, we need + // to give the function a valid string for its source, and valid code, even before we + // actually read in the real code. + TString *source = lua_newlstr(info->luaState, "", 0); + Proto *p = lua_newproto(info->luaState); + p->source = source; + p->sizecode = 1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->maxstacksize = 2; + p->sizek = 0; + p->sizep = 0; + + lua_checkstack(info->luaState, 2); + + pushProto(info->luaState, p); + // >>>>> permTbl indexTbl ...... proto + + // We don't need to register early, since protos can never ever be + // involved in cyclic references + + // Read in constant references + int sizek = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); + for (int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto k + + setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); + p->sizek++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + // Read in sub-proto references + + int sizep = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); + for (int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto subproto + + p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; + p->sizep++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + + // Read in code + p->sizecode = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); + info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); + + + /* Read in upvalue names */ + p->sizeupvalues = info->readStream->readSint32LE(); + if (p->sizeupvalues) { + lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); + for (int i = 0; i < p->sizeupvalues; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto str + + p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in local variable infos + p->sizelocvars = info->readStream->readSint32LE(); + if (p->sizelocvars) { + lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); + for (int i = 0; i < p->sizelocvars; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto str + + p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + p->locvars[i].startpc = info->readStream->readSint32LE(); + p->locvars[i].endpc = info->readStream->readSint32LE(); + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in source string + unserialize(info); + // >>>>> permTbl indexTbl ...... proto sourceStr + + p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Read in line numbers + p->sizelineinfo = info->readStream->readSint32LE(); + if (p->sizelineinfo) { + lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); + info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); + } + + + /* Read in linedefined and lastlinedefined */ + p->linedefined = info->readStream->readSint32LE(); + p->lastlinedefined = info->readStream->readSint32LE(); + + // Read in misc values + p->nups = info->readStream->readByte(); + p->numparams = info->readStream->readByte(); + p->is_vararg = info->readStream->readByte(); + p->maxstacksize = info->readStream->readByte(); +} + +void unserializeUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(info->luaState, 2); + + boxUpValue_start(info->luaState); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unserialize(info); + // >>>>> permTbl indexTbl ...... func obj + + boxUpValue_finish(info->luaState); + // >>>>> permTbl indexTbl ...... func +} + +void unserializeUserData(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + int isspecial = info->readStream->readSint32LE(); + if (isspecial) { + unserialize(info); + // >>>>> permTbl indexTbl ...... specialFunc + + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... udata + } else { + uint32 length = info->readStream->readUint32LE(); + lua_newuserdata(info->luaState, length); + // >>>>> permTbl indexTbl ...... udata + registerObjectInIndexTable(info, index); + + info->readStream->read(lua_touserdata(info->luaState, -1), length); + + unserialize(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unserializePermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unserialize(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_unserializer.cpp b/engines/sword25/util/lua_unserializer.cpp deleted file mode 100644 index 803c79c8c2..0000000000 --- a/engines/sword25/util/lua_unserializer.cpp +++ /dev/null @@ -1,698 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "sword25/util/lua_serialization.h" - -#include "sword25/util/double_serializer.h" -#include "sword25/util/lua_serialization_util.h" - -#include "common/stream.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" -#include "lua/lopcodes.h" - - -namespace Lua { - -struct UnSerializationInfo { - lua_State *luaState; - Common::ReadStream *readStream; -}; - -static void unserialize(UnSerializationInfo *info); - -static void unserializeBoolean(UnSerializationInfo *info); -static void unserializeNumber(UnSerializationInfo *info); -static void unserializeString(UnSerializationInfo *info); -static void unserializeTable(UnSerializationInfo *info, int index); -static void unserializeFunction(UnSerializationInfo *info, int index); -static void unserializeThread(UnSerializationInfo *info, int index); -static void unserializeProto(UnSerializationInfo *info, int index); -static void unserializeUpValue(UnSerializationInfo *info, int index); -static void unserializeUserData(UnSerializationInfo *info, int index); -static void unserializePermanent(UnSerializationInfo *info, int index); - - -void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { - UnSerializationInfo info; - info.luaState = luaState; - info.readStream = readStream; - - // The process starts with the lua stack as follows: - // >>>>> permTbl - // That's the table of permanents - - // Make sure there is enough room on the stack - lua_checkstack(luaState, 3); - - // Create a table to hold indexes of everything thats already been read - lua_newtable(luaState); - // >>>>> permTbl indexTbl - - // Prevent garbage collection while we unserialize - lua_gc(luaState, LUA_GCSTOP, 0); - - // Unserialize the root object - unserialize(&info); - // >>>>> permTbl indexTbl rootObj - - // Re-start garbage collection - lua_gc(luaState, LUA_GCRESTART, 0); - - // Remove the indexTbl - lua_replace(luaState, 2); - // >>>>> permTbl rootObj -} - -/* The object is left on the stack. This is primarily used by unserialize, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... obj - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - lua_pushlightuserdata(info->luaState, (void *)index); - // >>>>> permTbl indexTbl ...... obj index - - lua_pushvalue(info->luaState, -2); - // >>>>> permTbl indexTbl ...... obj index obj - - // Push the k/v pair into the indexTbl - lua_settable(info->luaState, 2); - // >>>>> permTbl indexTbl ...... obj -} - -static void unserialize(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - byte isARealValue = info->readStream->readByte(); - if (isARealValue) { - int index = info->readStream->readSint32LE(); - int type = info->readStream->readSint32LE(); - - switch (type) { - case LUA_TBOOLEAN: - unserializeBoolean(info); - break; - case LUA_TLIGHTUSERDATA: - // You can't serialize a pointer - // It would be meaningless on the next run - assert(0); - break; - case LUA_TNUMBER: - unserializeNumber(info); - break; - case LUA_TSTRING: - unserializeString(info); - break; - case LUA_TTABLE: - unserializeTable(info, index); - break; - case LUA_TFUNCTION: - unserializeFunction(info, index); - break; - case LUA_TTHREAD: - unserializeThread(info, index); - break; - case LUA_TPROTO: - unserializeProto(info, index); - break; - case LUA_TUPVAL: - unserializeUpValue(info, index); - break; - case LUA_TUSERDATA: - unserializeUserData(info, index); - break; - case PERMANENT_TYPE: - unserializePermanent(info, index); - break; - default: - assert(0); - } - - - // >>>>> permTbl indexTbl ...... obj - assert(lua_type(info->luaState, -1) == type || - type == PERMANENT_TYPE || - // Remember, upvalues get a special dispensation, as described in boxUpValue - (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); - - registerObjectInIndexTable(info, index); - // >>>>> permTbl indexTbl ...... obj - } else { - int index = info->readStream->readSint32LE(); - - if (index == 0) { - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... nil - } else { - // Fetch the object from the indexTbl - - lua_pushlightuserdata(info->luaState, (void *)index); - // >>>>> permTbl indexTbl ...... index - - lua_gettable(info->luaState, 2); - // >>>>> permTbl indexTbl ...... ?obj? - - assert(!lua_isnil(info->luaState, -1)); - } - // >>>>> permTbl indexTbl ...... obj/nil - } - - // >>>>> permTbl indexTbl ...... obj/nil -} - -static void unserializeBoolean(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - int value = info->readStream->readSint32LE(); - - lua_pushboolean(info->luaState, value); - // >>>>> permTbl indexTbl ...... bool -} - -static void unserializeNumber(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - // Read the serialized double - Util::SerializedDouble serializedValue; - serializedValue.significandOne = info->readStream->readUint32LE(); - serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); - serializedValue.exponent = info->readStream->readSint16LE(); - - lua_Number value = Util::decodeDouble(serializedValue); - - lua_pushnumber(info->luaState, value); - // >>>>> permTbl indexTbl ...... num -} - -static void unserializeString(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - uint32 length = info->readStream->readUint32LE(); - char *string = new char[length]; - - info->readStream->read(string, length); - lua_pushlstring(info->luaState, string, length); - - // >>>>> permTbl indexTbl ...... string - - delete[] string; -} - -static void unserializeSpecialTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - unserialize(info); - - // >>>>> permTbl indexTbl ...... spfunc - lua_call(info->luaState, 0, 1); - // >>>>> permTbl indexTbl ...... tbl -} - -static void unserializeLiteralTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 3); - - // Preregister table for handling of cycles - lua_newtable(info->luaState); - - // >>>>> permTbl indexTbl ...... tbl - registerObjectInIndexTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - - // Unserialize metatable - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? - - if (lua_istable(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... tbl metaTbl - lua_setmetatable(info->luaState, -2); - // >>>>> permTbl indexTbl ...... tbl - } else { - // >>>>> permTbl indexTbl ...... tbl nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl - } - // >>>>> permTbl indexTbl ...... tbl - - - while (1) { - // >>>>> permTbl indexTbl ...... tbl - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl key/nil - - // The table serialization is nil terminated - if (lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... tbl nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl - - break; - } - - // >>>>> permTbl indexTbl ...... tbl key - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl value - - lua_rawset(info->luaState, -3); - // >>>>> permTbl indexTbl ...... tbl - } -} - -void unserializeTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - int isSpecial = info->readStream->readSint32LE(); - - if (isSpecial) { - unserializeSpecialTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - } else { - unserializeLiteralTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - } -} - -void unserializeFunction(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - byte numUpValues = info->readStream->readByte(); - - LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); - pushClosure(info->luaState, (Closure *)lclosure); - // >>>>> permTbl indexTbl ...... func - - // Put *some* proto in the closure, before the GC can find it - lclosure->p = makeFakeProto(info->luaState, numUpValues); - - //Also, we need to temporarily fill the upvalues - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... func nil - - for (byte i = 0; i < numUpValues; ++i) { - lclosure->upvals[i] = createUpValue(info->luaState, -1); - } - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - - // I can't see offhand how a function would ever get to be self- - // referential, but just in case let's register it early - registerObjectInIndexTable(info, index); - - // Now that it's safe, we can get the real proto - unserialize(info); - // >>>>> permTbl indexTbl ...... func proto - - lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - - for (byte i = 0; i < numUpValues; ++i) { - // >>>>> permTbl indexTbl ...... func - unserialize(info); - // >>>>> permTbl indexTbl ...... func func2 - - unboxUpValue(info->luaState); - // >>>>> permTbl indexTbl ...... func upValue - lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } - - // Finally, the fenv - unserialize(info); - - // >>>>> permTbl indexTbl ...... func ?fenv/nil? - if (!lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... func fenv - lua_setfenv(info->luaState, -2); - // >>>>> permTbl indexTbl ...... func - } else { - // >>>>> permTbl indexTbl ...... func nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } - - // >>>>> permTbl indexTbl ...... func -} - -void unserializeThread(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - lua_State *L2; - uint32 stacklimit = 0; - - L2 = lua_newthread(info->luaState); - lua_checkstack(info->luaState, 3); - - // L1: permTbl indexTbl ...... thread - // L2: (empty) - registerObjectInIndexTable(info, index); - - // First, deserialize the object stack - uint32 stackSize = info->readStream->readUint32LE(); - lua_growstack(info->luaState, (int)stackSize); - - // Make sure that the first stack element (a nil, representing - // the imaginary top-level C function) is written to the very, - // very bottom of the stack - L2->top--; - for (uint32 i = 0; i < stackSize; ++i) { - unserialize(info); - // L1: permTbl indexTbl ...... thread obj* - } - - lua_xmove(info->luaState, L2, stackSize); - // L1: permTbl indexTbl ...... thread - // L2: obj* - - // Hereafter, stacks refer to L1 - - - // Now, deserialize the CallInfo stack - - uint32 numFrames = info->readStream->readUint32LE(); - - lua_reallocCallInfo(L2, numFrames * 2); - for (uint32 i = 0; i < numFrames; ++i) { - CallInfo *ci = L2->base_ci + i; - uint32 stackbase = info->readStream->readUint32LE(); - uint32 stackfunc = info->readStream->readUint32LE(); - uint32 stacktop = info->readStream->readUint32LE(); - - ci->nresults = info->readStream->readSint32LE(); - - uint32 savedpc = info->readStream->readUint32LE(); - - if (stacklimit < stacktop) { - stacklimit = stacktop; - } - - ci->base = L2->stack + stackbase; - ci->func = L2->stack + stackfunc; - ci->top = L2->stack + stacktop; - ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; - ci->tailcalls = 0; - - // Update the pointer each time, to keep the GC happy - L2->ci = ci; - } - - // >>>>> permTbl indexTbl ...... thread - // Deserialize the state's other parameters, with the exception of upval stuff - - L2->savedpc = L2->ci->savedpc; - L2->status = info->readStream->readByte(); - uint32 stackbase = info->readStream->readUint32LE(); - uint32 stacktop = info->readStream->readUint32LE(); - - - L2->errfunc = info->readStream->readUint32LE(); - - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - - // Finally, "reopen" upvalues. See serializeUpVal() for why we do this - UpVal *uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - - while (true) { - unserialize(info); - // >>>>> permTbl indexTbl ...... thread upVal/nil - - // The list is terminated by a nil - if (lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... thread nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - break; - } - - // >>>>> permTbl indexTbl ...... thread boxedUpVal - unboxUpValue(info->luaState); - // >>>>> permTbl indexTbl ...... thread boxedUpVal - - uv = &(getObject(info->luaState, -1)->value.gc->uv); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - - uint32 stackpos = info->readStream->readUint32LE(); - uv->v = L2->stack + stackpos; - - GCUnlink(info->luaState, (GCObject *)uv); - - uv->marked = luaC_white(g); - *nextslot = (GCObject *)uv; - nextslot = &uv->next; - uv->u.l.prev = &G(L2)->uvhead; - uv->u.l.next = G(L2)->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - G(L2)->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - } - *nextslot = NULL; - - // The stack must be valid at least to the highest value among the CallInfos - // 'top' and the values up to there must be filled with 'nil' - lua_checkstack(L2, (int)stacklimit); - for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { - setnilvalue(o); - } -} - -void unserializeProto(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // We have to be careful. The GC expects a lot out of protos. In particular, we need - // to give the function a valid string for its source, and valid code, even before we - // actually read in the real code. - TString *source = lua_newlstr(info->luaState, "", 0); - Proto *p = lua_newproto(info->luaState); - p->source = source; - p->sizecode = 1; - p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(info->luaState, 2); - - pushProto(info->luaState, p); - // >>>>> permTbl indexTbl ...... proto - - // We don't need to register early, since protos can never ever be - // involved in cyclic references - - // Read in constant references - int sizek = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); - for (int i = 0; i < sizek; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto k - - setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); - p->sizek++; - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - // >>>>> permTbl indexTbl ...... proto - - // Read in sub-proto references - - int sizep = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); - for (int i = 0; i < sizep; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto subproto - - p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; - p->sizep++; - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - // >>>>> permTbl indexTbl ...... proto - - - // Read in code - p->sizecode = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); - info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); - - - /* Read in upvalue names */ - p->sizeupvalues = info->readStream->readSint32LE(); - if (p->sizeupvalues) { - lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); - for (int i = 0; i < p->sizeupvalues; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto str - - p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - } - // >>>>> permTbl indexTbl ...... proto - - // Read in local variable infos - p->sizelocvars = info->readStream->readSint32LE(); - if (p->sizelocvars) { - lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); - for (int i = 0; i < p->sizelocvars; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto str - - p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - p->locvars[i].startpc = info->readStream->readSint32LE(); - p->locvars[i].endpc = info->readStream->readSint32LE(); - } - } - // >>>>> permTbl indexTbl ...... proto - - // Read in source string - unserialize(info); - // >>>>> permTbl indexTbl ...... proto sourceStr - - p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - // Read in line numbers - p->sizelineinfo = info->readStream->readSint32LE(); - if (p->sizelineinfo) { - lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); - info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); - } - - - /* Read in linedefined and lastlinedefined */ - p->linedefined = info->readStream->readSint32LE(); - p->lastlinedefined = info->readStream->readSint32LE(); - - // Read in misc values - p->nups = info->readStream->readByte(); - p->numparams = info->readStream->readByte(); - p->is_vararg = info->readStream->readByte(); - p->maxstacksize = info->readStream->readByte(); -} - -void unserializeUpValue(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - lua_checkstack(info->luaState, 2); - - boxUpValue_start(info->luaState); - // >>>>> permTbl indexTbl ...... func - registerObjectInIndexTable(info, index); - - unserialize(info); - // >>>>> permTbl indexTbl ...... func obj - - boxUpValue_finish(info->luaState); - // >>>>> permTbl indexTbl ...... func -} - -void unserializeUserData(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - int isspecial = info->readStream->readSint32LE(); - if (isspecial) { - unserialize(info); - // >>>>> permTbl indexTbl ...... specialFunc - - lua_call(info->luaState, 0, 1); - // >>>>> permTbl indexTbl ...... udata - } else { - uint32 length = info->readStream->readUint32LE(); - lua_newuserdata(info->luaState, length); - // >>>>> permTbl indexTbl ...... udata - registerObjectInIndexTable(info, index); - - info->readStream->read(lua_touserdata(info->luaState, -1), length); - - unserialize(info); - // >>>>> permTbl indexTbl ...... udata metaTable/nil - - lua_setmetatable(info->luaState, -2); - // >>>>> permTbl indexTbl ...... udata - } - // >>>>> permTbl indexTbl ...... udata -} - -void unserializePermanent(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - unserialize(info); - // >>>>> permTbl indexTbl ...... permKey - - lua_gettable(info->luaState, 1); - // >>>>> permTbl indexTbl ...... perm -} - -} // End of namespace Lua -- cgit v1.2.3 From 08e3f21a8df184bc697e6c03adf968d0cbdbac21 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 15:22:43 -0600 Subject: SWORD25: Rename double serialization file to better represent what it is AKA functions, rather than a class --- engines/sword25/module.mk | 2 +- engines/sword25/util/double_serialization.cpp | 138 ++++++++++++++++++++++++++ engines/sword25/util/double_serialization.h | 95 ++++++++++++++++++ engines/sword25/util/double_serializer.cpp | 138 -------------------------- engines/sword25/util/double_serializer.h | 95 ------------------ engines/sword25/util/lua_persist.cpp | 2 +- engines/sword25/util/lua_unpersist.cpp | 2 +- 7 files changed, 236 insertions(+), 236 deletions(-) create mode 100644 engines/sword25/util/double_serialization.cpp create mode 100644 engines/sword25/util/double_serialization.h delete mode 100644 engines/sword25/util/double_serializer.cpp delete mode 100644 engines/sword25/util/double_serializer.h diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 129b4f040e..0842eb9aa8 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -82,7 +82,7 @@ MODULE_OBJS := \ util/lua/lvm.o \ util/lua/lzio.o \ util/lua/scummvm_file.o \ - util/double_serializer.o \ + util/double_serialization.o \ util/lua_persistence_util.o \ util/lua_persist.o \ util/lua_unpersist.o diff --git a/engines/sword25/util/double_serialization.cpp b/engines/sword25/util/double_serialization.cpp new file mode 100644 index 0000000000..48fd75cb33 --- /dev/null +++ b/engines/sword25/util/double_serialization.cpp @@ -0,0 +1,138 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sword25/util/double_serialization.h" + +#include "common/scummsys.h" + + +namespace Util { + +SerializedDouble encodeDouble(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); + + SerializedDouble returnValue; + returnValue.significandOne = significandOne; // SignificandOne + returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + significandTwo; // SignificandTwo + returnValue.exponent = (int16)exponent; + return returnValue; +} + +double decodeDouble(SerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)value.exponent; + double expandedsignificandOne = (double)value.significandOne; + double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +uint64 encodeDouble_64(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the significand into the integer range + double shiftedsignificand = ldexp(abs(significand), 53); + + // Combine everything using the IEEE standard + uint64 uintsignificand = (uint64)shiftedsignificand; + return ((uint64)(value < 0 ? 1 : 0) << 63) | // Sign + ((uint64)(exponent + 1023) << 52) | // Exponent stored as an offset to 1023 + (uintsignificand & 0x000FFFFFFFFFFFFF); // significand with MSB inferred +} + +double decodeDouble_64(uint64 value) { + // Expand the exponent and significand + int exponent = (int)((value >> 52) & 0x7FF) - 1023; + double expandedsignificand = (double)(0x10000000000000 /* Inferred MSB */ | (value & 0x000FFFFFFFFFFFFF)); + + // Deflate the significand + int temp; + double significand = frexp(expandedsignificand, &temp); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value & 0x8000000000000000) == 0x8000000000000000) ? -returnValue : returnValue; +} + +CompactSerializedDouble encodeDouble_Compact(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 21)); + + CompactSerializedDouble returnValue; + returnValue.signAndSignificandOne = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + (significandOne & 0x7FFFFFFF); // significandOne with MSB inferred + // Exponent stored as an offset to 1023 + returnValue.exponentAndSignificandTwo = ((uint32)(exponent + 1023) << 21) | significandTwo; + + return returnValue; +} + +double decodeDouble_Compact(CompactSerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)(value.exponentAndSignificandTwo >> 21) - 1023; + double expandedsignificandOne = (double)(0x80000000 /* Inferred MSB */ | (value.signAndSignificandOne & 0x7FFFFFFF)); + double expandedsignificandTwo = (double)(value.exponentAndSignificandTwo & 0x1FFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandOne & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serialization.h b/engines/sword25/util/double_serialization.h new file mode 100644 index 0000000000..e90338c369 --- /dev/null +++ b/engines/sword25/util/double_serialization.h @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef DOUBLE_SERIALIZATION_H +#define DOUBLE_SERIALIZATION_H + +#include "common/types.h" + + +namespace Util { + +struct SerializedDouble { + uint32 significandOne; + uint32 signAndSignificandTwo; + int16 exponent; +}; + +struct CompactSerializedDouble { + uint32 signAndSignificandOne; + uint32 exponentAndSignificandTwo; +}; + +/** + * Encodes a double as two uint32 and a one int16 + * + * Supports denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +SerializedDouble encodeDouble(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble(SerializedDouble value); + +/** + * Encodes a double as a uint64 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +uint64 encodeDouble_64(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_64(uint64 value); + +/** + * Encodes a double as two uint32 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +CompactSerializedDouble encodeDouble_Compact(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_Compact(CompactSerializedDouble value); + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/util/double_serializer.cpp b/engines/sword25/util/double_serializer.cpp deleted file mode 100644 index d7ba4f3052..0000000000 --- a/engines/sword25/util/double_serializer.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "sword25/util/double_serializer.h" - -#include "common/scummsys.h" - - -namespace Util { - -SerializedDouble encodeDouble(double value) { - // Split the value into its significand and exponent - int exponent; - double significand = frexp(value, &exponent); - - // Shift the the first part of the significand into the integer range - double shiftedsignificandPart = ldexp(abs(significand), 32); - uint32 significandOne = uint32(floor(shiftedsignificandPart)); - - // Shift the remainder of the significand into the integer range - shiftedsignificandPart -= significandOne; - uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); - - SerializedDouble returnValue; - returnValue.significandOne = significandOne; // SignificandOne - returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign - significandTwo; // SignificandTwo - returnValue.exponent = (int16)exponent; - return returnValue; -} - -double decodeDouble(SerializedDouble value) { - // Expand the exponent and the parts of the significand - int exponent = (int)value.exponent; - double expandedsignificandOne = (double)value.significandOne; - double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); - - // Deflate the significand - double shiftedsignificand = ldexp(expandedsignificandTwo, -21); - double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); - - // Re-calculate the actual double - double returnValue = ldexp(significand, exponent); - - // Check the sign bit and return - return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; -} - -uint64 encodeDouble_64(double value) { - // Split the value into its significand and exponent - int exponent; - double significand = frexp(value, &exponent); - - // Shift the significand into the integer range - double shiftedsignificand = ldexp(abs(significand), 53); - - // Combine everything using the IEEE standard - uint64 uintsignificand = (uint64)shiftedsignificand; - return ((uint64)(value < 0 ? 1 : 0) << 63) | // Sign - ((uint64)(exponent + 1023) << 52) | // Exponent stored as an offset to 1023 - (uintsignificand & 0x000FFFFFFFFFFFFF); // significand with MSB inferred -} - -double decodeDouble_64(uint64 value) { - // Expand the exponent and significand - int exponent = (int)((value >> 52) & 0x7FF) - 1023; - double expandedsignificand = (double)(0x10000000000000 /* Inferred MSB */ | (value & 0x000FFFFFFFFFFFFF)); - - // Deflate the significand - int temp; - double significand = frexp(expandedsignificand, &temp); - - // Re-calculate the actual double - double returnValue = ldexp(significand, exponent); - - // Check the sign bit and return - return ((value & 0x8000000000000000) == 0x8000000000000000) ? -returnValue : returnValue; -} - -CompactSerializedDouble encodeDouble_Compact(double value) { - // Split the value into its significand and exponent - int exponent; - double significand = frexp(value, &exponent); - - // Shift the the first part of the significand into the integer range - double shiftedsignificandPart = ldexp(abs(significand), 32); - uint32 significandOne = uint32(floor(shiftedsignificandPart)); - - // Shift the remainder of the significand into the integer range - shiftedsignificandPart -= significandOne; - uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 21)); - - CompactSerializedDouble returnValue; - returnValue.signAndSignificandOne = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign - (significandOne & 0x7FFFFFFF); // significandOne with MSB inferred - // Exponent stored as an offset to 1023 - returnValue.exponentAndSignificandTwo = ((uint32)(exponent + 1023) << 21) | significandTwo; - - return returnValue; -} - -double decodeDouble_Compact(CompactSerializedDouble value) { - // Expand the exponent and the parts of the significand - int exponent = (int)(value.exponentAndSignificandTwo >> 21) - 1023; - double expandedsignificandOne = (double)(0x80000000 /* Inferred MSB */ | (value.signAndSignificandOne & 0x7FFFFFFF)); - double expandedsignificandTwo = (double)(value.exponentAndSignificandTwo & 0x1FFFFF); - - // Deflate the significand - double shiftedsignificand = ldexp(expandedsignificandTwo, -21); - double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); - - // Re-calculate the actual double - double returnValue = ldexp(significand, exponent); - - // Check the sign bit and return - return ((value.signAndSignificandOne & 0x80000000) == 0x80000000) ? -returnValue : returnValue; -} - -} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serializer.h b/engines/sword25/util/double_serializer.h deleted file mode 100644 index e90338c369..0000000000 --- a/engines/sword25/util/double_serializer.h +++ /dev/null @@ -1,95 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef DOUBLE_SERIALIZATION_H -#define DOUBLE_SERIALIZATION_H - -#include "common/types.h" - - -namespace Util { - -struct SerializedDouble { - uint32 significandOne; - uint32 signAndSignificandTwo; - int16 exponent; -}; - -struct CompactSerializedDouble { - uint32 signAndSignificandOne; - uint32 exponentAndSignificandTwo; -}; - -/** - * Encodes a double as two uint32 and a one int16 - * - * Supports denormalized numbers. Does NOT support NaN, or Inf - * - * @param value The value to encode - * @return The encoded value - */ -SerializedDouble encodeDouble(double value); -/** - * Decodes a previously encoded double - * - * @param value The value to decode - * @return The decoded value - */ -double decodeDouble(SerializedDouble value); - -/** - * Encodes a double as a uint64 - * - * Does NOT support denormalized numbers. Does NOT support NaN, or Inf - * - * @param value The value to encode - * @return The encoded value - */ -uint64 encodeDouble_64(double value); -/** - * Decodes a previously encoded double - * - * @param value The value to decode - * @return The decoded value - */ -double decodeDouble_64(uint64 value); - -/** - * Encodes a double as two uint32 - * - * Does NOT support denormalized numbers. Does NOT support NaN, or Inf - * - * @param value The value to encode - * @return The encoded value - */ -CompactSerializedDouble encodeDouble_Compact(double value); -/** - * Decodes a previously encoded double - * - * @param value The value to decode - * @return The decoded value - */ -double decodeDouble_Compact(CompactSerializedDouble value); - -} // End of namespace Sword25 - -#endif diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp index b9c0b13e11..939dbf38a8 100644 --- a/engines/sword25/util/lua_persist.cpp +++ b/engines/sword25/util/lua_persist.cpp @@ -22,7 +22,7 @@ #include "sword25/util/lua_persistence.h" -#include "sword25/util/double_serializer.h" +#include "sword25/util/double_serialization.h" #include "sword25/util/lua_persistence_util.h" #include "common/stream.h" diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp index aa924ff7c5..8d644302f9 100644 --- a/engines/sword25/util/lua_unpersist.cpp +++ b/engines/sword25/util/lua_unpersist.cpp @@ -22,7 +22,7 @@ #include "sword25/util/lua_persistence.h" -#include "sword25/util/double_serializer.h" +#include "sword25/util/double_serialization.h" #include "sword25/util/lua_persistence_util.h" #include "common/stream.h" -- cgit v1.2.3 From 8668707f160fa171edfa68df036f28c7ba4e9a88 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 15:11:17 -0600 Subject: SWORD25: Fix how nils are persisted The unpersist code expects nils to be represented as an index with value 0. The persist code incorrectly wrote out this data --- engines/sword25/util/lua_persist.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp index 939dbf38a8..6d758067ad 100644 --- a/engines/sword25/util/lua_persist.cpp +++ b/engines/sword25/util/lua_persist.cpp @@ -59,7 +59,7 @@ void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { SerializationInfo info; info.luaState = luaState; info.writeStream = writeStream; - info.counter = 0u; + info.counter = 1u; // The process starts with the lua stack as follows: // >>>>> permTbl rootObj @@ -145,19 +145,22 @@ static void serialize(SerializationInfo *info) { return; } - // Pop the nil off the stack + // Pop the index/nil off the stack lua_pop(info->luaState, 1); - // Write out a flag that indicates that this is a real object - info->writeStream->writeByte(1); - - // If the object itself is nil, then write out a zero as a placeholder + // If the obj itself is nil, we represent it as an index of 0 if (lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index info->writeStream->writeByte(0); + // Write out the index + info->writeStream->writeUint32LE(0); return; } + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + // Add the object to the indexTbl lua_pushvalue(info->luaState, -1); -- cgit v1.2.3 From 67114c3e7eafdd6ba8c7bc799a40f789acc1efa3 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 15:30:55 -0600 Subject: SWORD25: Remove old lua persistence files --- engines/sword25/util/pluto/CHANGELOG | 37 - engines/sword25/util/pluto/FILEFORMAT | 168 --- engines/sword25/util/pluto/README | 133 -- engines/sword25/util/pluto/THANKS | 9 - engines/sword25/util/pluto/pdep.cpp | 112 -- engines/sword25/util/pluto/pdep/README | 5 - engines/sword25/util/pluto/pdep/lzio.h | 65 - engines/sword25/util/pluto/pdep/pdep.h | 42 - engines/sword25/util/pluto/pluto.cpp | 2083 -------------------------------- engines/sword25/util/pluto/pluto.h | 25 - engines/sword25/util/pluto/plzio.cpp | 74 -- 11 files changed, 2753 deletions(-) delete mode 100644 engines/sword25/util/pluto/CHANGELOG delete mode 100644 engines/sword25/util/pluto/FILEFORMAT delete mode 100644 engines/sword25/util/pluto/README delete mode 100644 engines/sword25/util/pluto/THANKS delete mode 100644 engines/sword25/util/pluto/pdep.cpp delete mode 100644 engines/sword25/util/pluto/pdep/README delete mode 100644 engines/sword25/util/pluto/pdep/lzio.h delete mode 100644 engines/sword25/util/pluto/pdep/pdep.h delete mode 100644 engines/sword25/util/pluto/pluto.cpp delete mode 100644 engines/sword25/util/pluto/pluto.h delete mode 100644 engines/sword25/util/pluto/plzio.cpp diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG deleted file mode 100644 index 1be321f898..0000000000 --- a/engines/sword25/util/pluto/CHANGELOG +++ /dev/null @@ -1,37 +0,0 @@ -$Id$ - --- 2.4 -- -* Changed upval unboxing to allow upvals which contain func-housed cycles -* Added stack checking to all stack-growing functions -* Serialized debug information for functions - --- 2.3 -- -* Added LUALIB_API declaration for luaopen_pluto - --- 2.2 -- -* Rolled all internal Lua dependencies into the Pluto distribution -* Made the unit tests depend on dynamically loading Pluto - --- 2.1 -- -* Various fixes to make the GC happy -* stack size always expanded where necessary -* fixed some memory leaks -* GC disabled during unpersist -* callstack initialized for traversal - -This changelog is maintained as of version 2.0alpha1. -Earlier versions are changelogged on the LuaForge site. - --- 2.0 -- -* Fixed a few format changes to 5.1.3 -* Fixed myriad warnings -* GCC compliance: not incrementing cast results -* Fix for self-referring upvals -* Renamed loading function to work with Lua module system -* Loading tables with __newindex works -* unpersist makes buffer copy - --- 2.0alpha1 -- -* Fixed all outstanding 5.0->5.1 conversion issues -* Made heavier use of size_t in preference to int -* Fixed GC/Upval issue (thanks to Eric Jacobs) diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT deleted file mode 100644 index e7716675c7..0000000000 --- a/engines/sword25/util/pluto/FILEFORMAT +++ /dev/null @@ -1,168 +0,0 @@ -$Id$ - -pluto_persist() produces a "hunk" of objects. Here's the file format adhered -to by the function, and expected by pluto_unpersist(). - -As a developer, I feel that where file format information is given it is of -utmost importance that that information precisely and accurately reflects the -actual operation of the application. Therefore, if you find any discrepancy -between this and actual operation, please lambast me thoroughly over email. - -Pseudo-C is used to express the file format. Padding is assumed to be -nonexistent. The keyword "one_of" is used to express a concept similar to -"union", except that its size is the size of the actual datatype chosen. Thus, -objects which contain, directly or indirectly, a one_of, may vary in size. - - -struct Object { - int firstTime; /* Whether this is the first time the object - is being referenced */ - one_of { - RealObject o; /* if firstTime == 1 */ - Reference r; /* if firstTime == 0 */ - }; -}; - -struct Reference { - int ref; /* The index the object was registered with */ -}; - -struct RealObject { - int type; /* The type of the object */ - one_of { - Boolean b; /* If type == LUA_TBOOLEAN */ - LightUserData l; /* If type == LUA_TLIGHTUSERDATA */ - Number n; /* If type == LUA_TNUMBER */ - String s; /* If type == LUA_TSTRING */ - Table t; /* If type == LUA_TTABLE */ - Function f; /* If type == LUA_TFUNCTION */ - Userdata u; /* If type == LUA_TUSERDATA */ - Thread th; /* If type == LUA_TTHREAD */ - Proto p; /* If type == LUA_TPROTO (from lobject.h) */ - Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */ - }; /* The actual object */ -}; - -struct Boolean { - int32 bvalue; /* 0 for false, 1 for true */ -}; - -struct LightUserData { - void* luvalue; /* The actual, literal pointer */ -}; - -struct Number { - lua_Number nvalue; /* The actual number */ -}; - -struct String { - int length; /* The length of the string */ - char str[length]; /* The actual string (not null terminated) */ -}; - -struct Table { - int isspecial; /* 1 if SP is used; 0 otherwise */ - one_of { - Closure c; /* if isspecial == 1; closure to refill the table */ - LiteralTable t; /* if isspecial == 0; literal table info */ - }; -}; - -struct LiteralTable { - Object metatable; /* nil for default metatable */ - Pair p[]; /* key/value pairs */ - Object nil = nil; /* Nil reference to terminate */ -}; - -struct Pair { - Object key; - Object value; -}; - -struct Function { /* Actually a closure */ - lu_byte nups; /* Number of upvalues the function uses */ - Object proto; /* The proto this function uses */ - Object upvals[nups]; /* All upvalues */ - Object fenv; /* The FEnv (nil for the global table) -}; - -struct Upval { - Object obj; /* The object this upval refers to */ -} - -struct Userdata { - int isSpecial; /* 1 for special persistence, 0 for literal - one_of { - LiteralUserdata lu; /* if is_special is 0 */ - SpecialUserdata su; /* if is_special is 1 */ - }; -}; - -struct LiteralUserdata { - Object metatable; /* The metatable (nil for default) */ - int length; /* Size of the data */ - char data[length]; /* The actual data */ -}; - -struct SpecialUserdata { - int length; /* The size of the data */ - Object func; /* The closure used to fill the userdata */ -}; - -struct Thread { - int stacksize; /* The size of the stack filled with objects, - * including the "nil" that is hidden below - * the bottom of the stack visible to C */ - Object stack[stacksize];/* Indices of all stack values, bottom up */ - int callinfosize; /* Number of elements in the CallInfo stack */ - CallInfo callinfostack[callinfosize]; /* The CallInfo stack */ - int base; /* base = L->base - L->stack; */ - int top; /* top = L->top - L->stack; */ - OpenUpval openupvals[]; /* Upvalues to open */ - Object nil = nil; /* To terminate the open upvalues list */ -}; - -struct OpenUpval { - Object upval; /* The upvalue */ - int stackpos; /* The stack position to "reopen" it to */ - -}; - -struct CallInfo { - int base; /* base = ci->base - L->stack; */ - int top; /* top = ci->top - L->stack; */ - int pc; /* pc = ci->pc - proto->code; */ - int state; /* flags used by the CallInfo */ -}; - -struct Proto { - int sizek; /* Number of constants referenced */ - Object k[sizek]; /* Constants referenced */ - int sizep; /* Number of inner Protos referenced */ - Object p[sizep]; /* Inner Protos referenced */ - int sizecode; /* Number of instructions in code */ - Instruction code[sizecode]; /* The proto's code */ - ProtoDebug debuginfo; /* Debug information for the proto */ - lu_byte nups; /* Number of upvalues used */ - lu_byte numparams; /* Number of parameters taken */ - lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */ - lu_byte maxstacksize; /* Size of stack reserved for the function */ -}; - -struct ProtoDebug { - int sizeupvals; /* Number of upvalue names */ - Object upvals; /* Upvalue names */ - int sizelocvars; /* Number of local variable names */ - LocVar[sizelocvars]; /* Local variable names */ - Object source; /* The source code */ - int sizelineinfo; /* Number of opcode-line mappings */ - int lineinfo[sizelineinfo]; /* opcode-line mappings */ - int linedefined; /* Start of line range */ - int lastlinedefined; /* End of line range */ -}; - -struct LocVar { - Object name; /* Name of the local variable */ - int startpc; /* Point where variable is active */ - int endpc; /* Point where variable is dead */ -}; diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README deleted file mode 100644 index 838fce498b..0000000000 --- a/engines/sword25/util/pluto/README +++ /dev/null @@ -1,133 +0,0 @@ -$Id$ - -PLUTO - Heavy duty persistence for Lua - -Pluto is a library which allows users to write arbitrarily large portions -of the "Lua universe" into a flat file, and later read them back into the -same or a different Lua universe. Object references are appropriately -handled, such that the file contains everything needed to recreate the -objects in question. - -Pluto has the following major features: -* Can persist any Lua function -* Can persist threads -* Works with any Lua chunkreader/chunkwriter -* Support for "invariant" permanent objects, of all datatypes -* Can invoke metafunctions for custom persistence of tables and userdata - -Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua -5.0, please use version 1.2 of Pluto. - -Starting with version 2.2, Pluto no longer depends on the Lua sources. -Instead, it subsumes the required headers into its own codebase. -As a result, it may not work properly with Lua version 5.1.4 or later. - -Pluto may have bugs. Users are advised to define lua_assert in -luaconf.h to something useful when compiling in debug mode, to catch -assertions by Pluto and Lua. - -The Pluto library consists of two public functions. - -int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) - -This function recursively persists the Lua object in stack position 2 -and all other objects which are directly or indirectly referenced by -it, except those referenced in the permanent object table. The data -is written using the chunk-writer given, and that writer is passed -the arbitrary pointer value ud. - -The Lua stack must contain exactly and only these two items, in order: - -1. A table of permanent objects, that should not be persisted. For each -permanent object, the object itself should be the key, and a unique -object of any type should be the value. Likely candidates for this table -include Lua functions (including those in the Lua libraries) that are -loaded at load-time. It must include all non-persistable objects that -are referenced by the object to be persisted. The table is not modified -by the function. Objects in this table are considered "opaque" and are -not examined or descended into. Objects should not appear in the table -multiple times; the result of doing this is undefined (though probably -harmless). NOTE: If you are planning to persist threads, keep in mind -that all yielded threads have coroutine.yield on the tops of their -stacks. Since it's a C function, it should be put here. For complex -permanents, it may be a good idea to use the __index meta-function of -the permanents table to "search" for permanents. - -2. The single object to be persisted. In many cases, this will be the -global table. For more flexibility, however, it may be something like a -table built for the occasion, with various values to keep track of. The -object may not be nil. - - -int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) - -This function loads in a Lua object and places it on top of the stack. All -objects directly or indirectly referenced by it are also loaded. - -The Lua stack must contain, as its top value, a table of permanent -objects. This table should be like the permanent object table used when -persisting, but with the key and value of each pair reversed. These -objects are used as substitutes for those referenced in their positions -when persisting, and under most circumstances should be identical objects -to those referenced in the permanents table used for persisting. It's -okay for multiple keys to refer to the same object. - - -RUNNING PLUTO FROM LUA: -It is also possible to invoke pluto from a Lua script. The C function -pluto_open() will register pluto.persist and pluto.unpersist, lua functions -which operate on strings. The first takes a permanents table and a root -object, and returns a string; the second takes a permanents table and a -string, and returns the root object. - -An error will be raised if pluto.persist is called from a thread which is -itself referenced by the root object. - -SPECIAL PERSISTENCE: -Tables and userdata have special persistence semantics. These semantics are -keyed to the value of the object's metatable's __persist member, if any. This -member may be any of the following four values: -1. Boolean "true": The table or userdata is persisted literally; tables are -persisted member-by-member, and userdata are written out as literal data. -2. Boolean "false": An error is returned, indicating that the object cannot -be persisted. -3. A function: This function should take one argument, the object in question, -and return one result, a closure. This "fixup closure", in turn, will be -persisted, and during unpersistence will be called. The closure will be -responsible for recreating the object with the appropriate data, based on -its upvalues. -4. Nil, or no metatable. In the case of tables, the table is literally -persisted. In the case of userdata, an error is returned. - -Here's an example of special persistence for a simple 3d vector object: - -vec = { x = 2, y = 1, z = 4 } -setmetatable(vec, { __persist = function(oldtbl) - local x = oldtbl.x - local y = oldtbl.y - local z = oldtbl.z - local mt = getmetatable(oldtbl) - return function() - newtbl = {} - newtbl.x = x - newtbl.y = y - newtbl.z = z - setmetatable(newtbl, mt) - return newtbl - end -end }) - -Note how x, y, z, and the mt are explicitly pulled out of the table. It is -important that the fixup closure returned not reference the original table -directly, as that table would again be persisted as an upvalue, leading to an -infinite loop. Also note that the object's metatable is NOT automatically -persisted; it is necessary for the fixup closure to reset it, if it wants. - -LIMITATIONS/TODO: -* Light userdata are persisted literally, as their pointer values. This -may or may not be what you want. -* Closures of C functions may not be persisted. Once it becomes possible -to specify a C function "proto" as a permanent object, this restriction -will be relaxed. - -BUGS: None known. Emphasis on the 'known'. diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS deleted file mode 100644 index 443713fa61..0000000000 --- a/engines/sword25/util/pluto/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -Pluto is surprisingly robust and useful. This would not be the case without -the hard work and helpfulness of the following people, mentioned in no -particular order: - -Ivko Stanilov -Goran Adrinek -Eric Jacobs -Anolan Milanes -Malte Thiesen diff --git a/engines/sword25/util/pluto/pdep.cpp b/engines/sword25/util/pluto/pdep.cpp deleted file mode 100644 index a32c43b42d..0000000000 --- a/engines/sword25/util/pluto/pdep.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is derived from the Lua source code. Please see lua.h for -the copyright statement. -*/ - -#include "pdep/pdep.h" - -#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} - -void pdep_pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); - api_incr_top(L); -} - -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { - global_State *g = G(L); - lua_assert((osize == 0) == (block == NULL)); - block = (*g->frealloc)(g->ud, block, osize, nsize); - lua_assert((nsize == 0) == (block == NULL)); - g->totalbytes = (g->totalbytes - osize) + nsize; - return block; -} - -void pdep_link (lua_State *L, GCObject *o, lu_byte tt) { - global_State *g = G(L); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *pdep_newproto (lua_State *L) { - Proto *f = pdep_new(L, Proto); - pdep_link(L, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems))); - pdep_link(L, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = e; - c->l.nupvalues = cast_byte(nelems); - while (nelems--) c->l.upvals[nelems] = NULL; - return c; -} - -static void correctstack (lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - - -void pdep_reallocstack (lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack+newsize; - correctstack(L, oldstack); -} - -void pdep_growstack (lua_State *L, int n) { - if (n <= L->stacksize) /* double size is enough? */ - pdep_reallocstack(L, 2*L->stacksize); - else - pdep_reallocstack(L, L->stacksize + n); -} - -void pdep_reallocCI (lua_State *L, int newsize) { - CallInfo *oldci = L->base_ci; - pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); - L->size_ci = newsize; - L->ci = (L->ci - oldci) + L->base_ci; - L->end_ci = L->base_ci + L->size_ci - 1; -} - -TString *pdep_newlstr (lua_State *L, const char *str, size_t l) { - TString *res; - lua_pushlstring(L, str, l); - res = rawtsvalue(L->top-1); - lua_pop(L, 1); - return res; -} diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README deleted file mode 100644 index 3592754da0..0000000000 --- a/engines/sword25/util/pluto/pdep/README +++ /dev/null @@ -1,5 +0,0 @@ -These files are directly copied from the Lua distribution, with the -exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed. - -As such, unlike the rest of Pluto, they are released under the -same terms as Lua. See "lua.h" for the copyright notice. diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h deleted file mode 100644 index 2e37f8d202..0000000000 --- a/engines/sword25/util/pluto/pdep/lzio.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -** $Id$ -** Buffered streams -** See Copyright Notice in lua.h -*/ - - -#ifndef lzio_h -#define lzio_h - -#include "sword25/util/lua/lua.h" - - -#define EOZ (-1) /* end of stream */ - -typedef struct Zio ZIO; - -#define char2int(c) cast(int, cast(unsigned char, (c))) - -#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z)) - -typedef struct Mbuffer { - char *buffer; - size_t n; - size_t buffsize; -} Mbuffer; - -#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) - -#define pdep_buffer(buff) ((buff)->buffer) -#define pdep_sizebuffer(buff) ((buff)->buffsize) -#define pdep_bufflen(buff) ((buff)->n) - -#define pdep_resetbuffer(buff) ((buff)->n = 0) - - -#define pdep_resizebuffer(L, buff, size) \ - (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) - -#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0) - - -LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n); -LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, - void *data); -LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */ -LUAI_FUNC int pdep_lookahead (ZIO *z); - - - -/* --------- Private Part ------------------ */ - -struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; - void* data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ -}; - - -LUAI_FUNC int pdep_fill (ZIO *z); - -#endif diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h deleted file mode 100644 index 664fc812b5..0000000000 --- a/engines/sword25/util/pluto/pdep/pdep.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef PDEP_H -#define PDEP_H - -#include "sword25/util/lua/lua.h" -#include "sword25/util/pluto/pdep/lzio.h" -#include "sword25/util/lua/ldo.h" -#include "sword25/util/lua/lfunc.h" -#include "sword25/util/lua/lgc.h" -#include "sword25/util/lua/llimits.h" -#include "sword25/util/lua/lobject.h" -#include "sword25/util/lua/lopcodes.h" -#include "sword25/util/lua/lstate.h" -#include "sword25/util/lua/lstring.h" -#include "sword25/util/lua/lauxlib.h" - - -#define pdep_reallocv(L,b,on,n,e) \ - pdep_realloc_(L, (b), (on)*(e), (n)*(e)) -#define pdep_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t)))) -#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t)) -#define pdep_newvector(L,n,t) \ - cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t))) -#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t))) -#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t)) -#define pdep_checkstack(L,n) \ - if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ - pdep_growstack(L, n); \ - else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1); - - -void pdep_pushobject (lua_State *L, const TValue *o); -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize); -void pdep_link (lua_State *L, GCObject *o, lu_byte tt); -Proto *pdep_newproto (lua_State *L); -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e); -void pdep_reallocstack (lua_State *L, int newsize); -void pdep_growstack (lua_State *L, int n); -void pdep_reallocCI (lua_State *L, int newsize); -TString *pdep_newlstr (lua_State *L, const char *str, size_t l); - -#endif diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp deleted file mode 100644 index cbe16b0d5b..0000000000 --- a/engines/sword25/util/pluto/pluto.cpp +++ /dev/null @@ -1,2083 +0,0 @@ -/* $Id$ */ - -/* Tamed Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Instrumented by Stefan Reich (info@luaos.net) - * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) - */ - -#include "sword25/util/lua/lua.h" -#include "pluto.h" - -#undef TOTEXT - -#define USE_PDEP - -#ifdef USE_PDEP -#include "pdep/pdep.h" -#define LIF(prefix, name) pdep ## _ ## name -#else -#include "lapi.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "llimits.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "lauxlib.h" -#define LIF(prefix, name) lua ## prefix ## _ ## name -#endif - -#include - - -/* Define this if you want size_t values to be written in 64-bit - (even on 32-bit systems). Should eliminate at least one source of - 32/64 bit incompatibility. */ -#define SIZES64 - - -/* #define PLUTO_DEBUG */ - - -#ifdef SIZES64 -#define VERSION "Tamed Pluto 1.0 with SIZES64 flag" -#else -#define VERSION "Tamed Pluto 1.0" -#endif - - -#ifdef PLUTO_DEBUG -#include -#endif - -#define PLUTO_TPERMANENT 101 - -#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); } - -#define NUMTYPES 9 -static const char* typenames[] = { - "nil", - "boolean", - "lightuserdata", - "number", - "string", - "table", - "function", - "userdata", - "thread" -}; - -static int humanReadable = 0; -#define hrBufSize 200 -static char hrBuf[hrBufSize]; - -typedef struct PersistInfo_t { - lua_State *L; - int counter; - lua_Chunkwriter writer; - void *ud; -#ifdef PLUTO_DEBUG - int level; -#endif -} PersistInfo; - -#ifdef PLUTO_DEBUG -void printindent(int indent) -{ - int il; - for(il=0; ilwriter(pi->L, hrBuf, strlen(hrBuf), ud); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - snprintf(hrBuf, hrBufSize, "%X%X", (b >> 4) & 0xF, b & 0xF); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } - snprintf(hrBuf, hrBufSize, "\n"); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } else { - pi->writer(pi->L, p, size, ud); - } -#ifdef TOTEXT - int i; - printf(" pi_write %d ", (int) size); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - printf("%X%X", (b >> 4) & 0xF, b & 0xF); - } - printf("\n"); -#endif -} - -static void hrOut(PersistInfo *pi) { - pi->writer(pi->L, hrBuf, strlen(hrBuf), pi->ud); -} - -static void write_size(PersistInfo *pi, size_t *val) -{ -#ifdef SIZES64 - int64 longval; /* yeah, you really need long long to get 8 bytes on win32... duh. */ - longval = *val; - pi_write(pi, &longval, 8, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size64 %ld\n", longval); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size64 %ld\n", longval); -#endif -#else - pi_write(pi, val, sizeof(size_t), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size %ld\n", *((size_t *)val)); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size %ld\n", *val); -#endif -#endif -} - -static void read_size(ZIO *zio, size_t *val) -{ -#ifdef SIZES64 - int64 longval; - verify(LIF(Z,read)(zio, &longval, 8) == 0); - *val = longval; -#else - verify(LIF(Z,read)(zio, val, sizeof(size_t)) == 0); -#endif -} - - -/* Mutual recursion requires prototype */ -static void persist(PersistInfo *pi); - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -static StkId getobject(lua_State *L, int stackpos) -{ - if(stackpos > 0) { - lua_assert(L->base+stackpos-1 < L->top); - return L->base+stackpos-1; - } else { - lua_assert(L->top-stackpos >= L->base); - return L->top+stackpos; - } -} - -/* Choose whether to do a regular or special persistence based on an object's - * metatable. "default" is whether the object, if it doesn't have a __persist - * entry, is literally persistable or not. - * Pushes the unpersist closure and returns true if special persistence is - * used. */ -static int persistspecialobject(PersistInfo *pi, int defaction) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 4); - /* Check whether we should persist literally, or via the __persist - * metafunction */ - if(!lua_getmetatable(pi->L, -1)) { - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - } - } - /* perms reftbl sptbl ... obj mt */ - lua_pushstring(pi->L, "__persist"); - /* perms reftbl sptbl ... obj mt "__persist" */ - lua_rawget(pi->L, -2); - /* perms reftbl sptbl ... obj mt __persist? */ - if(lua_isnil(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt nil */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero2\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero2\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(lua_isboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt bool */ - if(lua_toboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt true */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero3\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero3\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Metatable forbade persistence"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist not nil, boolean, or function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt __persist */ - lua_pushvalue(pi->L, -3); - /* perms reftbl ... obj mt __persist obj */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(pi->L, (void *)pi->writer); - lua_pushlightuserdata(pi->L, pi->ud); - /* perms reftbl ... obj mt __persist obj ud */ - lua_call(pi->L, 3, 1); - /* perms reftbl ... obj mt func? */ -#else - lua_call(pi->L, 1, 1); - /* perms reftbl ... obj mt func? */ -#endif - /* perms reftbl ... obj mt func? */ - if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist function did not return a function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt func */ - { - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_one\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_one\n"); -#endif - } - persist(pi); - /* perms reftbl ... obj mt func */ - lua_pop(pi->L, 2); - /* perms reftbl ... obj */ - return 1; -} - -static void persisttable(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persisttable\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persisttable\n"); -#endif - - /* perms reftbl ... tbl */ - lua_checkstack(pi->L, 3); - if(persistspecialobject(pi, 1)) { - /* perms reftbl ... tbl */ - return; - } - /* perms reftbl ... tbl */ - /* First, persist the metatable (if any) */ - if(!lua_getmetatable(pi->L, -1)) { - lua_pushnil(pi->L); - } - /* perms reftbl ... tbl mt/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ - - /* Now, persist all k/v pairs */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - while(lua_next(pi->L, -2)) { - /* perms reftbl ... tbl k v */ - lua_pushvalue(pi->L, -2); - /* perms reftbl ... tbl k v k */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k v */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k */ - } - /* perms reftbl ... tbl */ - /* Terminate list */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ -} - -static void persistuserdata(PersistInfo *pi) { - /* perms reftbl ... udata */ - lua_checkstack(pi->L, 2); - if(persistspecialobject(pi, 0)) { - /* perms reftbl ... udata */ - return; - } else { - /* Use literal persistence */ - size_t length = uvalue(getobject(pi->L, -1))->len; - write_size(pi, &length); - pi_write(pi, lua_touserdata(pi->L, -1), length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistuserdata %ld\n", (long) length); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistuserdata %ld\n", (long) length); -#endif - if(!lua_getmetatable(pi->L, -1)) { - /* perms reftbl ... udata */ - lua_pushnil(pi->L); - /* perms reftbl ... udata mt/nil */ - } - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... udata */ - } -} - - -static Proto *toproto(lua_State *L, int stackpos) -{ - return gco2p(getobject(L, stackpos)->value.gc); -} - -static UpVal *toupval(lua_State *L, int stackpos) -{ - lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL); - return gco2uv(getobject(L, stackpos)->value.gc); -} - -static void pushproto(lua_State *L, Proto *proto) -{ - TValue o; - setptvalue(L, &o, proto); - LIF(A,pushobject)(L, &o); -} - -#define setuvvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \ - checkliveness(G(L),i_o); } - -static void pushupval(lua_State *L, UpVal *upval) -{ - TValue o; - setuvvalue(L, &o, upval); - LIF(A,pushobject)(L, &o); -} - -static void pushclosure(lua_State *L, Closure *closure) -{ - TValue o; - setclvalue(L, &o, closure); - LIF(A,pushobject)(L, &o); -} - -static void pushstring(lua_State *L, TString *s) -{ - TValue o; - setsvalue(L, &o, s); - LIF(A,pushobject)(L, &o); -} - -static void persistfunction(PersistInfo *pi) -{ - /* perms reftbl ... func */ - Closure *cl = clvalue(getobject(pi->L, -1)); - lua_checkstack(pi->L, 2); - if(cl->c.isC) { - /* It's a C function. For now, we aren't going to allow - * persistence of C closures, even if the "C proto" is - * already in the permanents table. */ - lua_pushstring(pi->L, "Attempt to persist a C function"); - lua_error(pi->L); - } else { - /* It's a Lua closure. */ - { - /* We don't really _NEED_ the number of upvals, - * but it'll simplify things a bit */ - pi_write(pi, &cl->l.p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); -#endif - } - /* Persist prototype */ - { - pushproto(pi->L, cl->l.p); - /* perms reftbl ... func proto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* Persist upvalue values (not the upvalue objects - * themselves) */ - { - int i; - for(i=0; il.p->nups; i++) { - /* perms reftbl ... func */ - pushupval(pi->L, cl->l.upvals[i]); - /* perms reftbl ... func upval */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - } - /* Persist function environment */ - { - lua_getfenv(pi->L, -1); - /* perms reftbl ... func fenv */ - if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) { - /* Function has the default fenv */ - /* perms reftbl ... func _G */ - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - lua_pushnil(pi->L); - /* perms reftbl ... func nil */ - } - /* perms reftbl ... func fenv/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - } -} - - -/* Upvalues are tricky. Here's why. - * - * A particular upvalue may be either "open", in which case its member v - * points into a thread's stack, or "closed" in which case it points to the - * upvalue itself. An upvalue is closed under any of the following conditions: - * -- The function that initially declared the variable "local" returns - * -- The thread in which the closure was created is garbage collected - * - * To make things wackier, just because a thread is reachable by Lua doesn't - * mean it's in our root set. We need to be able to treat an open upvalue - * from an unreachable thread as a closed upvalue. - * - * The solution: - * (a) For the purposes of persisting, don't indicate whether an upvalue is - * closed or not. - * (b) When unpersisting, pretend that all upvalues are closed. - * (c) When persisting, persist all open upvalues referenced by a thread - * that is persisted, and tag each one with the corresponding stack position - * (d) When unpersisting, "reopen" each of these upvalues as the thread is - * unpersisted - */ -static void persistupval(PersistInfo *pi) -{ - /* perms reftbl ... upval */ - UpVal *uv = toupval(pi->L, -1); - lua_checkstack(pi->L, 1); - - /* We can't permit the upval to linger around on the stack, as Lua - * will bail if its GC finds it. */ - - lua_pop(pi->L, 1); - /* perms reftbl ... */ - LIF(A,pushobject)(pi->L, uv->v); - /* perms reftbl ... obj */ - persist(pi); - /* perms reftbl ... obj */ -} - -static void persistproto(PersistInfo *pi) -{ - /* perms reftbl ... proto */ - Proto *p = toproto(pi->L, -1); - lua_checkstack(pi->L, 2); - - /* Persist constant refs */ - { - int i; - pi_write(pi, &p->sizek, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizek %d\n", p->sizek); - hrOut(pi); - } - #ifdef TOTEXT - printf("persistproto_sizek %d\n", p->sizek); - #endif - for(i=0; isizek; i++) { - LIF(A,pushobject)(pi->L, &p->k[i]); - /* perms reftbl ... proto const */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* serialize inner Proto refs */ - { - int i; - pi_write(pi, &p->sizep, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizep %d\n", p->sizep); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizep %d\n", p->sizep); -#endif - for(i=0; isizep; i++) - { - pushproto(pi->L, p->p[i]); - /* perms reftbl ... proto subproto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* Serialize code */ - { - int len; - pi_write(pi, &p->sizecode, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizecode %d\n", p->sizecode); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizecode %d\n", p->sizecode); -#endif - len = sizeof(Instruction) * p->sizecode; - pi_write(pi, p->code, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_code %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_code %d\n", len); -#endif - } - - /* Serialize upvalue names */ - { - int i; - pi_write(pi, &p->sizeupvalues, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_upvalues %d\n", p->sizeupvalues); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_upvalues %d\n", p->sizeupvalues); -#endif - for(i=0; isizeupvalues; i++) - { - pushstring(pi->L, p->upvalues[i]); - persist(pi); - lua_pop(pi->L, 1); - } - } - /* Serialize local variable infos */ - { - int i; - pi_write(pi, &p->sizelocvars, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelocvars %d\n", p->sizelocvars); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelocvars %d\n", p->sizelocvars); -#endif - for(i=0; isizelocvars; i++) - { - pushstring(pi->L, p->locvars[i].varname); - persist(pi); - lua_pop(pi->L, 1); - - pi_write(pi, &p->locvars[i].startpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_startpc %d\n", p->locvars[i].startpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_startpc %d\n", p->locvars[i].startpc); -#endif - pi_write(pi, &p->locvars[i].endpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_endpc %d\n", p->locvars[i].endpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_endpc %d\n", p->locvars[i].endpc); -#endif - } - } - - /* Serialize source string */ - pushstring(pi->L, p->source); - persist(pi); - lua_pop(pi->L, 1); - - /* Serialize line numbers */ - { - pi_write(pi, &p->sizelineinfo, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelineinfo %d\n", p->sizelineinfo); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelineinfo %d\n", p->sizelineinfo); -#endif - if (p->sizelineinfo) - { - int len; - len = sizeof(int) * p->sizelineinfo; - pi_write(pi, p->lineinfo, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lineinfo %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lineinfo %d\n", len); -#endif - } - } - - /* Serialize linedefined and lastlinedefined */ - pi_write(pi, &p->linedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_linedefined %d\n", p->linedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_linedefined %d\n", p->linedefined); -#endif - pi_write(pi, &p->lastlinedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lastlinedefined %d\n", p->lastlinedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lastlinedefined %d\n", p->lastlinedefined); -#endif - - /* Serialize misc values */ - { - pi_write(pi, &p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_nups %d\n", (int) p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_nups %d\n", (int) p->nups); -#endif - pi_write(pi, &p->numparams, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_numparams %d\n", (int) p->numparams); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_numparams %d\n", (int) p->numparams); -#endif - pi_write(pi, &p->is_vararg, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_is_vararg %d\n", (int) p->is_vararg); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_is_vararg %d\n", (int) p->is_vararg); -#endif - pi_write(pi, &p->maxstacksize, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_maxstacksize %d\n", (int) p->maxstacksize); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_maxstacksize %d\n", (int) p->maxstacksize); -#endif - } - /* We do not currently persist upvalue names, local variable names, - * variable lifetimes, line info, or source code. */ -} - -/* Copies a stack, but the stack is reversed in the process - */ -static size_t revappendstack(lua_State *from, lua_State *to) -{ - StkId o; - for(o=from->top-1; o>=from->stack; o--) { - setobj2s(to, to->top, o); - to->top++; - } - return from->top - from->stack; -} - -/* Persist all stack members - */ -static void persistthread(PersistInfo *pi) -{ - size_t posremaining; - lua_State *L2; - /* perms reftbl ... thr */ - L2 = lua_tothread(pi->L, -1); - lua_checkstack(pi->L, L2->top - L2->stack + 1); - if(pi->L == L2) { - lua_pushstring(pi->L, "Can't persist currently running thread"); - lua_error(pi->L); - return; /* not reached */ - } - - /* Persist the stack */ - posremaining = revappendstack(L2, pi->L); - /* perms reftbl ... thr (rev'ed contents of L2) */ - write_size(pi, &posremaining); - for(; posremaining > 0; posremaining--) { - persist(pi); - lua_pop(pi->L, 1); - } - /* perms reftbl ... thr */ - /* Now, persist the CallInfo stack. */ - { - size_t i, numframes = (L2->ci - L2->base_ci) + 1; - write_size(pi, &numframes); - for(i=0; ibase_ci + i; - size_t stackbase = ci->base - L2->stack; - size_t stackfunc = ci->func - L2->stack; - size_t stacktop = ci->top - L2->stack; - size_t savedpc = (ci != L2->base_ci) ? - ci->savedpc - ci_func(ci)->l.p->code : - 0; - write_size(pi, &stackbase); - write_size(pi, &stackfunc); - write_size(pi, &stacktop); - pi_write(pi, &ci->nresults, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread %d\n", ci->nresults); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread %d\n", ci->nresults); -#endif - write_size(pi, &savedpc); - } - } - - /* Serialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase = L2->base - L2->stack; - size_t stacktop = L2->top - L2->stack; - lua_assert(L2->nCcalls <= 1); - pi_write(pi, &L2->status, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread_status %d\n", (int) L2->status); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread_status %d\n", (int) L2->status); -#endif - write_size(pi, &stackbase); - write_size(pi, &stacktop); - - // ptrdiff_t changes sizes based on 32/64 bit - // Hard cast to 64 bit size if SIZE64 is defined -#ifdef SIZES64 - uint64 ptrIndex = static_cast(L2->errfunc); - pi_write(pi, &ptrIndex, sizeof(uint64), pi->ud); -#else - pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); -#endif - //write_size(pi, (size_t *)&L2->errfunc); - } - - /* Finally, record upvalues which need to be reopened */ - /* See the comment above persistupval() for why we do this */ - { - GCObject *gco; - UpVal *uv; - /* perms reftbl ... thr */ - for(gco = L2->openupval; gco != NULL; gco = uv->next) { - size_t stackpos; - uv = gco2uv(gco); - - /* Make sure upvalue is really open */ - lua_assert(uv->v != &uv->u.value); - pushupval(pi->L, uv); - /* perms reftbl ... thr uv */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - stackpos = uv->v - L2->stack; - write_size(pi, &stackpos); - } - /* perms reftbl ... thr */ - lua_pushnil(pi->L); - /* perms reftbl ... thr nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - } - /* perms reftbl ... thr */ -} - -static void persistboolean(PersistInfo *pi) -{ - int b = lua_toboolean(pi->L, -1); - pi_write(pi, &b, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistboolean %d\n", b); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistboolean %d\n", b); -#endif -} - -static void persistlightuserdata(PersistInfo *pi) -{ - void *p = lua_touserdata(pi->L, -1); - pi_write(pi, &p, sizeof(void *), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistlightuserdata %p\n", p); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistlightuserdata %d\n", (int) p); -#endif -} - -static void persistnumber(PersistInfo *pi) -{ - lua_Number n = lua_tonumber(pi->L, -1); - pi_write(pi, &n, sizeof(lua_Number), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); -#endif -} - -static void persiststring(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring\n"); -#endif - size_t length = lua_strlen(pi->L, -1); - write_size(pi, &length); - const char* s = lua_tostring(pi->L, -1); - pi_write(pi, s, length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring %d \"%s\"\n", (int)length, s); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring %d \"%s\"\n", length, s); -#endif -} - -/* Top-level delegating persist function - */ -static void persist(PersistInfo *pi) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 2); - /* If the object has already been written, write a reference to it */ - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_rawget(pi->L, 2); - /* perms reftbl ... obj ref? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj ref */ - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_seenobject\n"); -#endif - int *ref = (int *)lua_touserdata(pi->L, -1); - pi_write(pi, ref, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_touserdata_ref %d\n", ref); -#endif - lua_pop(pi->L, 1); - /* perms reftbl ... obj ref */ -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 %d\n", ref); -#endif - return; - } - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - /* If the object is nil, write the pseudoreference 0 */ - if(lua_isnil(pi->L, -1)) { - int zero = 0; - /* firsttime */ - pi_write(pi, &zero, sizeof(int), pi->ud); - /* ref */ - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_nil (last 2 lines)\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_nil (last 2 lines)\n"); -#endif -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 0\n"); -#endif - return; - } - { - /* indicate that it's the first time */ - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_newobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_newobject\n"); -#endif - } - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - int *ref = (int *)lua_newuserdata(pi->L, sizeof(int)); - *ref = ++(pi->counter); - /* perms reftbl ... obj obj ref */ - lua_rawset(pi->L, 2); - /* perms reftbl ... obj */ - - pi_write(pi, &pi->counter, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_counter %d\n", pi->counter); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_counter %d\n", pi->counter); -#endif - - - /* At this point, we'll give the permanents table a chance to play. */ - { - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_gettable(pi->L, 1); - /* perms reftbl ... obj permkey? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj permkey */ - int type = PLUTO_TPERMANENT; -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d PERM\n", pi->counter); - pi->level++; -#endif - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_permtype %d\n", type); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_permtype %d\n", type); -#endif - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - pi->level--; -#endif - return; - } else { - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - } - /* perms reftbl ... obj */ - } - { - int type = lua_type(pi->L, -1); - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); -#endif - -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d %d\n", pi->counter, type); - pi->level++; -#endif - } - - switch(lua_type(pi->L, -1)) { - case LUA_TBOOLEAN: - persistboolean(pi); - break; - case LUA_TLIGHTUSERDATA: - persistlightuserdata(pi); - break; - case LUA_TNUMBER: - persistnumber(pi); - break; - case LUA_TSTRING: - persiststring(pi); - break; - case LUA_TTABLE: - persisttable(pi); - break; - case LUA_TFUNCTION: - persistfunction(pi); - break; - case LUA_TTHREAD: - persistthread(pi); - break; - case LUA_TPROTO: - persistproto(pi); - break; - case LUA_TUPVAL: - persistupval(pi); - break; - case LUA_TUSERDATA: - persistuserdata(pi); - break; - default: - lua_assert(0); - } -#ifdef PLUTO_DEBUG - pi->level--; -#endif -} - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) -{ - PersistInfo pi; - - pi.counter = 0; - pi.L = L; - pi.writer = writer; - pi.ud = ud; -#ifdef PLUTO_DEBUG - pi.level = 0; -#endif - - lua_checkstack(L, 4); - /* perms? rootobj? ...? */ - lua_assert(lua_gettop(L) == 2); - /* perms rootobj */ - lua_assert(!lua_isnil(L, 2)); - /* perms rootobj */ - lua_newtable(L); - /* perms rootobj reftbl */ - - /* Now we're going to make the table weakly keyed. This prevents the - * GC from visiting it and trying to mark things it doesn't want to - * mark in tables, e.g. upvalues. All objects in the table are - * a priori reachable, so it doesn't matter that we do this. */ - lua_newtable(L); - /* perms rootobj reftbl mt */ - lua_pushstring(L, "__mode"); - /* perms rootobj reftbl mt "__mode" */ - lua_pushstring(L, "k"); - /* perms rootobj reftbl mt "__mode" "k" */ - lua_settable(L, 4); - /* perms rootobj reftbl mt */ - lua_setmetatable(L, 3); - /* perms rootobj reftbl */ - lua_insert(L, 2); - /* perms reftbl rootobj */ - persist(&pi); - /* perms reftbl rootobj */ - lua_remove(L, 2); - /* perms rootobj */ -} - -typedef struct WriterInfo_t { - char* buf; - size_t buflen; -} WriterInfo; - -static int bufwriter (lua_State *L, const void *p, size_t sz, void *ud) { - const char *cp = (const char *)p; - WriterInfo *wi = (WriterInfo *)ud; - - LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char); - while(sz) - { - /* how dearly I love ugly C pointer twiddling */ - wi->buf[wi->buflen++] = *cp++; - sz--; - } - return 0; -} - -int persist_l(lua_State *L) -{ - /* perms? rootobj? ...? */ - WriterInfo wi; - - wi.buf = NULL; - wi.buflen = 0; - - lua_settop(L, 2); - /* perms? rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj */ - - pluto_persist(L, bufwriter, &wi); - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, wi.buf, wi.buflen); - /* str */ - pdep_freearray(L, wi.buf, wi.buflen, char); - return 1; -} - -typedef struct UnpersistInfo_t { - lua_State *L; - ZIO zio; -#ifdef PLUTO_DEBUG - int level; -#endif -} UnpersistInfo; - -static void unpersist(UnpersistInfo *upi); - -/* The object is left on the stack. This is primarily used by unpersist, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerobject(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... obj */ - lua_checkstack(upi->L, 2); - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... obj ref */ - lua_pushvalue(upi->L, -2); - /* perms reftbl ... obj ref obj */ - lua_settable(upi->L, 2); - /* perms reftbl ... obj */ -} - -static void unpersistboolean(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int b; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0); - lua_pushboolean(upi->L, b); - /* perms reftbl ... bool */ -} - -static void unpersistlightuserdata(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - void *p; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0); - lua_pushlightuserdata(upi->L, p); - /* perms reftbl ... ludata */ -} - -static void unpersistnumber(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_Number n; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0); - lua_pushnumber(upi->L, n); - /* perms reftbl ... num */ -} - -static void unpersiststring(UnpersistInfo *upi) -{ - /* perms reftbl sptbl ref */ - /*int length;*/ - size_t length; - char* string; - lua_checkstack(upi->L, 1); - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);*/ - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);*/ - read_size(&upi->zio, &length); - string = pdep_newvector(upi->L, length, char); - verify(LIF(Z,read)(&upi->zio, string, length) == 0); - lua_pushlstring(upi->L, string, length); - /* perms reftbl sptbl ref str */ - pdep_freearray(upi->L, string, length, char); -} - -static void unpersistspecialtable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ - lua_call(upi->L, 0, 1); - /* perms reftbl ... tbl? */ - lua_assert(lua_istable(upi->L, -1)); - /* perms reftbl ... tbl */ -} - -static void unpersistliteraltable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 3); - /* Preregister table for handling of cycles */ - lua_newtable(upi->L); - /* perms reftbl ... tbl */ - registerobject(ref, upi); - /* perms reftbl ... tbl */ - /* Unpersist metatable */ - { - unpersist(upi); - /* perms reftbl ... tbl mt/nil? */ - if(lua_istable(upi->L, -1)) { - /* perms reftbl ... tbl mt */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... tbl */ - } else { - /* perms reftbl ... tbl nil? */ - lua_assert(lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } - - while(1) - { - /* perms reftbl ... tbl */ - unpersist(upi); - /* perms reftbl ... tbl key/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - break; - } - /* perms reftbl ... tbl key */ - unpersist(upi); - /* perms reftbl ... tbl key value? */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl key value */ - lua_rawset(upi->L, -3); - /* perms reftbl ... tbl */ - } -} - -static void unpersisttable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - { - int isspecial; - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersistspecialtable(ref, upi); - /* perms reftbl ... tbl */ - } else { - unpersistliteraltable(ref, upi); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } -} - -static UpVal *makeupval(lua_State *L, int stackpos) -{ - UpVal *uv = pdep_new(L, UpVal); - pdep_link(L, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - setobj(L, uv->v, getobject(L, stackpos)); - return uv; -} - -static Proto *makefakeproto(lua_State *L, lu_byte nups) -{ - Proto *p = pdep_newproto(L); - p->sizelineinfo = 1; - p->lineinfo = pdep_newvector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = pdep_newvector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = pdep_newlstr(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -/* The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. */ -static void boxupval_start(lua_State *L) -{ - LClosure *lcl; - lcl = (LClosure *)pdep_newLclosure(L, 1, hvalue(&L->l_gt)); - pushclosure(L, (Closure *)lcl); - /* ... func */ - lcl->p = makefakeproto(L, 1); - - /* Temporarily initialize the upvalue to nil */ - - lua_pushnil(L); - lcl->upvals[0] = makeupval(L, -1); - lua_pop(L, 1); -} - -static void boxupval_finish(lua_State *L) -{ - /* ... func obj */ - LClosure *lcl = (LClosure *) clvalue(getobject(L, -2)); - - lcl->upvals[0]->u.value = *getobject(L, -1); - lua_pop(L, 1); -} - - -static void unboxupval(lua_State *L) -{ - /* ... func */ - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getobject(L, -1)); - uv = lcl->upvals[0]; - lua_pop(L, 1); - /* ... */ - pushupval(L, uv); - /* ... upval */ -} - -static void unpersistfunction(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - LClosure *lcl; - int i; - lu_byte nupvalues; - lua_checkstack(upi->L, 2); - - verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); - - lcl = (LClosure *)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt)); - pushclosure(upi->L, (Closure *)lcl); - - /* perms reftbl ... func */ - /* Put *some* proto in the closure, before the GC can find it */ - lcl->p = makefakeproto(upi->L, nupvalues); - - /* Also, we need to temporarily fill the upvalues */ - lua_pushnil(upi->L); - /* perms reftbl ... func nil */ - for(i=0; iupvals[i] = makeupval(upi->L, -1); - } - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - /* I can't see offhand how a function would ever get to be self- - * referential, but just in case let's register it early */ - registerobject(ref, upi); - - /* Now that it's safe, we can get the real proto */ - unpersist(upi); - /* perms reftbl ... func proto? */ - lua_assert(lua_type(upi->L, -1) == LUA_TPROTO); - /* perms reftbl ... func proto */ - lcl->p = toproto(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - for(i=0; iL); - /* perms reftbl ... func upval */ - lcl->upvals[i] = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - - /* Finally, the fenv */ - unpersist(upi); - /* perms reftbl ... func fenv/nil? */ - lua_assert(lua_type(upi->L, -1) == LUA_TNIL || - lua_type(upi->L, -1) == LUA_TTABLE); - /* perms reftbl ... func fenv/nil */ - if(!lua_isnil(upi->L, -1)) { - /* perms reftbl ... func fenv */ - lua_setfenv(upi->L, -2); - /* perms reftbl ... func */ - } else { - /* perms reftbl ... func nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ -} - -static void unpersistupval(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - - boxupval_start(upi->L); - /* perms reftbl ... func */ - registerobject(ref, upi); - - unpersist(upi); - /* perms reftbl ... func obj */ - boxupval_finish(upi->L); - /* perms reftbl ... func */ -} - -static void unpersistproto(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - Proto *p; - int i; - int sizep, sizek; - - /* We have to be careful. The GC expects a lot out of protos. In - * particular, we need to give the function a valid string for its - * source, and valid code, even before we actually read in the real - * code. */ - TString *source = pdep_newlstr(upi->L, "", 0); - p = pdep_newproto(upi->L); - p->source = source; - p->sizecode=1; - p->code = pdep_newvector(upi->L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(upi->L, 2); - - pushproto(upi->L, p); - /* perms reftbl ... proto */ - /* We don't need to register early, since protos can never ever be - * involved in cyclic references */ - - /* Read in constant references */ - { - verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue); - for(i=0; iL, &p->k[i], getobject(upi->L, -1)); - p->sizek++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - /* Read in sub-proto references */ - { - verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*); - for(i=0; ip[i] = toproto(upi->L, -1); - p->sizep++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - - /* Read in code */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction); - verify(LIF(Z,read)(&upi->zio, p->code, - sizeof(Instruction) * p->sizecode) == 0); - } - - /* Read in upvalue names */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0); - if (p->sizeupvalues) - { - LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *); - for(i=0; isizeupvalues; i++) - { - unpersist(upi); - p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - } - } - } - - /* Read in local variable infos */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0); - if (p->sizelocvars) - { - LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar); - for(i=0; isizelocvars; i++) - { - unpersist(upi); - p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0); - } - } - } - - /* Read in source string*/ - unpersist(upi); - p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - /* Read in line numbers */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0); - if (p->sizelineinfo) - { - LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int); - verify(LIF(Z,read)(&upi->zio, p->lineinfo, - sizeof(int) * p->sizelineinfo) == 0); - } - } - - /* Read in linedefined and lastlinedefined */ - verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0); - - /* Read in misc values */ - { - verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0); - } -} - - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -static void gcunlink(lua_State *L, GCObject *gco) -{ - GCObject *prevslot; - if(G(L)->rootgc == gco) { - G(L)->rootgc = G(L)->rootgc->gch.next; - return; - } - - prevslot = G(L)->rootgc; - while(prevslot->gch.next != gco) { - lua_assert(prevslot->gch.next != NULL); - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -/* FIXME __ALL__ field ordering */ -static void unpersistthread(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_State *L2; - size_t stacklimit = 0; - L2 = lua_newthread(upi->L); - lua_checkstack(upi->L, 3); - /* L1: perms reftbl ... thr */ - /* L2: (empty) */ - registerobject(ref, upi); - - /* First, deserialize the object stack. */ - { - size_t i, stacksize; - read_size(&upi->zio, &stacksize); - LIF(D,growstack)(L2, (int)stacksize); - /* Make sure that the first stack element (a nil, representing - * the imaginary top-level C function) is written to the very, - * very bottom of the stack */ - L2->top--; - for(i=0; iL, L2, stacksize); - /* L1: perms reftbl ... thr */ - /* L2: obj* */ - } - /* (hereafter, stacks refer to L1) */ - - /* Now, deserialize the CallInfo stack. */ - { - size_t i, numframes; - read_size(&upi->zio, &numframes); - LIF(D,reallocCI)(L2,numframes*2); - for(i=0; ibase_ci + i; - size_t stackbase, stackfunc, stacktop, savedpc; - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stackfunc); - read_size(&upi->zio, &stacktop); - verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0); - read_size(&upi->zio, &savedpc); - - if(stacklimit < stacktop) - stacklimit = stacktop; - - ci->base = L2->stack+stackbase; - ci->func = L2->stack+stackfunc; - ci->top = L2->stack+stacktop; - ci->savedpc = (ci != L2->base_ci) ? - ci_func(ci)->l.p->code+savedpc : - 0; - ci->tailcalls = 0; - /* Update the pointer each time, to keep the GC - * happy*/ - L2->ci = ci; - } - } - /* perms reftbl ... thr */ - /* Deserialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase, stacktop; - L2->savedpc = L2->ci->savedpc; - verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stacktop); - -#ifdef SIZES64 - uint64 value; - verify(LIF(Z,read)(&upi->zio, &value, sizeof(uint64)) == 0); - - L2->errfunc = static_cast(value); -#else - verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); -#endif - - //read_size(&upi->zio, (size_t *)&L2->errfunc); - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - } - /* Finally, "reopen" upvalues (see persistupval() for why) */ - { - UpVal* uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - while(1) { - size_t stackpos; - unpersist(upi); - /* perms reftbl ... thr uv/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... thr nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - break; - } - /* perms reftbl ... thr boxeduv */ - unboxupval(upi->L); - /* perms reftbl ... thr uv */ - uv = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - - read_size(&upi->zio, &stackpos); - uv->v = L2->stack + stackpos; - gcunlink(upi->L, (GCObject *)uv); - uv->marked = luaC_white(g); - *nextslot = (GCObject *)uv; - nextslot = &uv->next; - uv->u.l.prev = &G(L2)->uvhead; - uv->u.l.next = G(L2)->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - G(L2)->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - } - *nextslot = NULL; - } - - /* The stack must be valid at least to the highest value among the CallInfos */ - /* 'top' and the values up to there must be filled with 'nil' */ - { - StkId o; - LIF(D,checkstack)(L2, (int)stacklimit); - for (o = L2->top; o <= L2->top + stacklimit; o++) - setnilvalue(o); - } -} - -static void unpersistuserdata(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int isspecial; - lua_checkstack(upi->L, 2); - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(upi->L, &upi->zio); - lua_call(upi->L, 1, 1); -#else - lua_call(upi->L, 0, 1); -#endif - /* perms reftbl ... udata? */ -/* This assertion might not be necessary; it's conceivable, for - * example, that the SP function might decide to return a table - * with equivalent functionality. For the time being, we'll - * ignore this possibility in favor of stricter and more testable - * requirements. */ - lua_assert(lua_isuserdata(upi->L, -1)); - /* perms reftbl ... udata */ - } else { - size_t length; - read_size(&upi->zio, &length); - - lua_newuserdata(upi->L, length); - /* perms reftbl ... udata */ - registerobject(ref, upi); - verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0); - - unpersist(upi); - /* perms reftbl ... udata mt/nil? */ - lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1)); - /* perms reftbl ... udata mt/nil */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... udata */ - } - /* perms reftbl ... udata */ -} - -static void unpersistpermanent(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - unpersist(upi); - /* perms reftbl permkey */ - lua_gettable(upi->L, 1); - /* perms reftbl perm? */ - /* We assume currently that the substituted permanent value - * shouldn't be nil. This may be a bad assumption. Real-life - * experience is needed to evaluate this. */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl perm */ -} - -#if 0 -/* For debugging only; not called when lua_assert is empty */ -static int inreftable(lua_State *L, int ref) -{ - int res; - lua_checkstack(L, 1); - /* perms reftbl ... */ - lua_pushlightuserdata(L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(L, 2); - /* perms reftbl ... obj? */ - res = !lua_isnil(L, -1); - lua_pop(L, 1); - /* perms reftbl ... */ - return res; -} -#endif - -static void unpersist(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int firstTime; - int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */ - lua_checkstack(upi->L, 2); - LIF(Z,read)(&upi->zio, &firstTime, sizeof(int)); - if(firstTime) { - int ref; - int type; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); - lua_assert(!inreftable(upi->L, ref)); - LIF(Z,read)(&upi->zio, &type, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("1 %d %d\n", ref, type); - upi->level++; -#endif - switch(type) { - case LUA_TBOOLEAN: - unpersistboolean(upi); - break; - case LUA_TLIGHTUSERDATA: - unpersistlightuserdata(upi); - break; - case LUA_TNUMBER: - unpersistnumber(upi); - break; - case LUA_TSTRING: - unpersiststring(upi); - break; - case LUA_TTABLE: - unpersisttable(ref, upi); - break; - case LUA_TFUNCTION: - unpersistfunction(ref, upi); - break; - case LUA_TTHREAD: - unpersistthread(ref, upi); - break; - case LUA_TPROTO: - unpersistproto(ref, upi); - break; - case LUA_TUPVAL: - unpersistupval(ref, upi); - break; - case LUA_TUSERDATA: - unpersistuserdata(ref, upi); - break; - case PLUTO_TPERMANENT: - unpersistpermanent(ref, upi); - break; - default: - lua_assert(0); - } - /* perms reftbl ... obj */ - lua_assert(lua_type(upi->L, -1) == type || - type == PLUTO_TPERMANENT || - /* Remember, upvalues get a special dispensation, as - * described in boxupval */ - (lua_type(upi->L, -1) == LUA_TFUNCTION && - type == LUA_TUPVAL)); - registerobject(ref, upi); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - upi->level--; -#endif - } else { - int ref; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("0 %d\n", ref); -#endif - if(ref == 0) { - lua_pushnil(upi->L); - /* perms reftbl ... nil */ - } else { - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(upi->L, 2); - /* perms reftbl ... obj? */ - lua_assert(!lua_isnil(upi->L, -1)); - } - /* perms reftbl ... obj/nil */ - } - /* perms reftbl ... obj/nil */ - lua_assert(lua_gettop(upi->L) == stacksize + 1); -} - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) -{ - /* We use the graciously provided ZIO (what the heck does the Z stand - * for?) library so that we don't have to deal with the reader directly. - * Letting the reader function decide how much data to return can be - * very unpleasant. - */ - UnpersistInfo upi; - upi.L = L; -#ifdef PLUTO_DEBUG - upi.level = 0; -#endif - - lua_checkstack(L, 3); - LIF(Z,init)(L, &upi.zio, reader, ud); - - /* perms */ - lua_newtable(L); - /* perms reftbl */ - lua_gc(L, LUA_GCSTOP, 0); - unpersist(&upi); - lua_gc(L, LUA_GCRESTART, 0); - /* perms reftbl rootobj */ - lua_replace(L, 2); - /* perms rootobj */ -} - -typedef struct LoadInfo_t { - char *buf; - size_t size; -} LoadInfo; - - -static const char *bufreader(lua_State *L, void *ud, size_t *sz) { - LoadInfo *li = (LoadInfo *)ud; - if(li->size == 0) { - return NULL; - } - *sz = li->size; - li->size = 0; - return li->buf; -} - -int unpersist_l(lua_State *L) -{ - LoadInfo li; - char const *origbuf; - char *tempbuf; - size_t bufsize; - /* perms? str? ...? */ - lua_settop(L, 2); - /* perms? str? */ - origbuf = luaL_checklstring(L, 2, &bufsize); - tempbuf = LIF(M,newvector)(L, bufsize, char); - memcpy(tempbuf, origbuf, bufsize); - - li.buf = tempbuf; - li.size = bufsize; - - /* perms? str */ - lua_pop(L, 1); - /* perms? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms */ - pluto_unpersist(L, bufreader, &li); - /* perms rootobj */ - LIF(M,freearray)(L, tempbuf, bufsize, char); - return 1; -} - -/* Stefan's first C function for Lua! :) - Returns a string describing the Pluto version you're using. */ - -int version_l(lua_State *L) -{ - const char *version = VERSION; - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, version, strlen(version)); - /* str */ - return 1; -} - -/* Set human-readable output on or off. */ -int human_l(lua_State *L) -{ - /* flag? ...? */ - lua_settop(L, 1); - /* flag? */ - /*luaL_checktype(L, 1, LUA_TBOOLEAN);*/ - /* flag */ - - humanReadable = lua_toboolean(L, 1); - - lua_settop(L, 0); - /* (empty) */ - return 0; -} - -static luaL_reg pluto_reg[] = { - { "persist", persist_l }, - { "unpersist", unpersist_l }, - { "version", version_l }, - { "human", human_l }, - { NULL, NULL } -}; - -LUALIB_API int luaopen_pluto(lua_State *L) { - luaL_openlib(L, "pluto", pluto_reg, 0); - return 1; -} diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h deleted file mode 100644 index 3674842d44..0000000000 --- a/engines/sword25/util/pluto/pluto.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $Id$ */ - -/* Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* lua.h must be included before this file */ - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud); - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud); - -LUALIB_API int luaopen_pluto(lua_State *L); diff --git a/engines/sword25/util/pluto/plzio.cpp b/engines/sword25/util/pluto/plzio.cpp deleted file mode 100644 index 21f69a9e8d..0000000000 --- a/engines/sword25/util/pluto/plzio.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -** $Id$ -** a generic input stream interface -** See Copyright Notice in lua.h -*/ - - -#include - -#define lzio_c -#define LUA_CORE - -#include "pdep/pdep.h" - -int pdep_fill (ZIO *z) { - size_t size; - lua_State *L = z->L; - const char *buff; - lua_unlock(L); - buff = z->reader(L, z->data, &size); - lua_lock(L); - if (buff == NULL || size == 0) return EOZ; - z->n = size - 1; - z->p = buff; - return char2int(*(z->p++)); -} - - -int pdep_lookahead (ZIO *z) { - if (z->n == 0) { - if (pdep_fill(z) == EOZ) - return EOZ; - else { - z->n++; /* pdep_fill removed first byte; put back it */ - z->p--; - } - } - return char2int(*z->p); -} - - -void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { - z->L = L; - z->reader = reader; - z->data = data; - z->n = 0; - z->p = NULL; -} - - -/* --------------------------------------------------------------- read --- */ -size_t pdep_read (ZIO *z, void *b, size_t n) { - while (n) { - size_t m; - if (pdep_lookahead(z) == EOZ) - return n; /* return number of missing bytes */ - m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ - memcpy(b, z->p, m); - z->n -= m; - z->p += m; - b = (char *)b + m; - n -= m; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ -char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - pdep_resizebuffer(L, buff, n); - } - return buff->buffer; -} -- cgit v1.2.3 From eaff6a40f6f9cb8a5817c7b90a7e24c8c12da3fe Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 18:21:43 -0600 Subject: SWORD25: Correct include guards to reflect the changes to the file names --- engines/sword25/util/lua_persistence.h | 4 ++-- engines/sword25/util/lua_persistence_util.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h index cf7d7e03ca..88cbb04ad2 100644 --- a/engines/sword25/util/lua_persistence.h +++ b/engines/sword25/util/lua_persistence.h @@ -20,8 +20,8 @@ * */ -#ifndef LUA_SERIALIZATION_H -#define LUA_SERIALIZATION_H +#ifndef LUA_PERSISTENCE_H +#define LUA_PERSISTENCE_H #include "sword25/util/lua/lua.h" diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h index 345996f606..305eea4aaf 100644 --- a/engines/sword25/util/lua_persistence_util.h +++ b/engines/sword25/util/lua_persistence_util.h @@ -20,8 +20,8 @@ * */ -#ifndef LUA_SERIALIZATION_UTIL_H -#define LUA_SERIALIZATION_UTIL_H +#ifndef LUA_PERISTENCE_UTIL_H +#define LUA_PERISTENCE_UTIL_H struct lua_State; -- cgit v1.2.3 From 8ee75e1dc56681337a3ae98c9d207e70e28c5ff5 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 18:22:15 -0600 Subject: SWORD25: Add Pluto copyright message to new persistence code Since the code is based off the Pluto code --- engines/sword25/util/lua_persist.cpp | 24 ++++++++++++++ engines/sword25/util/lua_persistence.h | 23 +++++++++++++ engines/sword25/util/lua_persistence_util.cpp | 47 +++++++++++++++++++++++++++ engines/sword25/util/lua_persistence_util.h | 24 ++++++++++++++ engines/sword25/util/lua_unpersist.cpp | 24 ++++++++++++++ 5 files changed, 142 insertions(+) diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp index 6d758067ad..aea4e70036 100644 --- a/engines/sword25/util/lua_persist.cpp +++ b/engines/sword25/util/lua_persist.cpp @@ -20,6 +20,30 @@ * */ +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + #include "sword25/util/lua_persistence.h" #include "sword25/util/double_serialization.h" diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h index 88cbb04ad2..53e3dee02e 100644 --- a/engines/sword25/util/lua_persistence.h +++ b/engines/sword25/util/lua_persistence.h @@ -20,6 +20,29 @@ * */ +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + #ifndef LUA_PERSISTENCE_H #define LUA_PERSISTENCE_H diff --git a/engines/sword25/util/lua_persistence_util.cpp b/engines/sword25/util/lua_persistence_util.cpp index 8365f5d483..958fb7ac3c 100644 --- a/engines/sword25/util/lua_persistence_util.cpp +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -20,6 +20,53 @@ * */ +/** + * This code is heavily based on the pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + #include "sword25/util/lua_persistence_util.h" #include "common/scummsys.h" diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h index 305eea4aaf..4d0085e242 100644 --- a/engines/sword25/util/lua_persistence_util.h +++ b/engines/sword25/util/lua_persistence_util.h @@ -20,6 +20,30 @@ * */ +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + #ifndef LUA_PERISTENCE_UTIL_H #define LUA_PERISTENCE_UTIL_H diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp index 8d644302f9..6f3275741a 100644 --- a/engines/sword25/util/lua_unpersist.cpp +++ b/engines/sword25/util/lua_unpersist.cpp @@ -20,6 +20,30 @@ * */ +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + #include "sword25/util/lua_persistence.h" #include "sword25/util/double_serialization.h" -- cgit v1.2.3 From 9a4d62e76a29647ed7f2c0b16f009ff143fdf739 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 18:26:59 -0600 Subject: SWORD25: Change function names to use persist instead of serialize Same argument as in 97c35714ce3986b99848a780f6b195a63f8910b7. To match the rest of the SWORD25 code base --- engines/sword25/util/lua_persist.cpp | 98 +++++++++++++++---------------- engines/sword25/util/lua_unpersist.cpp | 102 ++++++++++++++++----------------- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp index aea4e70036..6038111bbc 100644 --- a/engines/sword25/util/lua_persist.cpp +++ b/engines/sword25/util/lua_persist.cpp @@ -66,17 +66,17 @@ struct SerializationInfo { uint counter; }; -static void serialize(SerializationInfo *info); +static void persist(SerializationInfo *info); -static void serializeBoolean(SerializationInfo *info); -static void serializeNumber(SerializationInfo *info); -static void serializeString(SerializationInfo *info); -static void serializeTable(SerializationInfo *info); -static void serializeFunction(SerializationInfo *info); -static void serializeThread(SerializationInfo *info); -static void serializeProto(SerializationInfo *info); -static void serializeUpValue(SerializationInfo *info); -static void serializeUserData(SerializationInfo *info); +static void persistBoolean(SerializationInfo *info); +static void persistNumber(SerializationInfo *info); +static void persistString(SerializationInfo *info); +static void persistTable(SerializationInfo *info); +static void persistFunction(SerializationInfo *info); +static void persistThread(SerializationInfo *info); +static void persistProto(SerializationInfo *info); +static void persistUpValue(SerializationInfo *info); +static void persistUserData(SerializationInfo *info); void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { @@ -127,14 +127,14 @@ void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { // >>>>> permTbl indexTbl rootObj // Serialize the root recursively - serialize(&info); + persist(&info); // Return the stack back to the original state lua_remove(luaState, 2); // >>>>> permTbl rootObj } -static void serialize(SerializationInfo *info) { +static void persist(SerializationInfo *info) { // The stack can potentially have many things on it // The object we want to serialize is the item on the top of the stack // >>>>> permTbl indexTbl rootObj ...... obj @@ -215,7 +215,7 @@ static void serialize(SerializationInfo *info) { info->writeStream->writeSint32LE(PERMANENT_TYPE); // Serialize the key - serialize(info); + persist(info); // Pop the key off the stack lua_pop(info->luaState, 1); @@ -236,7 +236,7 @@ static void serialize(SerializationInfo *info) { switch (objType) { case LUA_TBOOLEAN: - serializeBoolean(info); + persistBoolean(info); break; case LUA_TLIGHTUSERDATA: // You can't serialize a pointer @@ -244,41 +244,41 @@ static void serialize(SerializationInfo *info) { assert(0); break; case LUA_TNUMBER: - serializeNumber(info); + persistNumber(info); break; case LUA_TSTRING: - serializeString(info); + persistString(info); break; case LUA_TTABLE: - serializeTable(info); + persistTable(info); break; case LUA_TFUNCTION: - serializeFunction(info); + persistFunction(info); break; case LUA_TTHREAD: - serializeThread(info); + persistThread(info); break; case LUA_TPROTO: - serializeProto(info); + persistProto(info); break; case LUA_TUPVAL: - serializeUpValue(info); + persistUpValue(info); break; case LUA_TUSERDATA: - serializeUserData(info); + persistUserData(info); break; default: assert(0); } } -static void serializeBoolean(SerializationInfo *info) { +static void persistBoolean(SerializationInfo *info) { int value = lua_toboolean(info->luaState, -1); info->writeStream->writeSint32LE(value); } -static void serializeNumber(SerializationInfo *info) { +static void persistNumber(SerializationInfo *info) { lua_Number value = lua_tonumber(info->luaState, -1); #if 1 @@ -298,7 +298,7 @@ static void serializeNumber(SerializationInfo *info) { } -static void serializeString(SerializationInfo *info) { +static void persistString(SerializationInfo *info) { // Hard cast to a uint32 to force size_t to an explicit size // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems uint32 length = static_cast(lua_strlen(info->luaState, -1)); @@ -395,7 +395,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { info->writeStream->writeSint32LE(1); // Serialize the function - serialize(info); + persist(info); lua_pop(info->luaState, 2); // >>>>> permTbl indexTbl ...... obj @@ -403,7 +403,7 @@ static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { return true; } -static void serializeTable(SerializationInfo *info) { +static void persistTable(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... tbl // Make sure there is enough room on the stack @@ -422,7 +422,7 @@ static void serializeTable(SerializationInfo *info) { } // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl @@ -439,13 +439,13 @@ static void serializeTable(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... tbl k v k */ // Serialize the key - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl k v */ // Serialize the value - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl k */ @@ -457,13 +457,13 @@ static void serializeTable(SerializationInfo *info) { lua_pushnil(info->luaState); // >>>>> permTbl indexTbl ...... tbl - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... tbl } -static void serializeFunction(SerializationInfo *info) { +static void persistFunction(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... func Closure *cl = clvalue(getObject(info->luaState, -1)); lua_checkstack(info->luaState, 2); @@ -484,7 +484,7 @@ static void serializeFunction(SerializationInfo *info) { pushProto(info->luaState, cl->l.p); // >>>>> permTbl indexTbl ...... func proto */ - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func @@ -495,7 +495,7 @@ static void serializeFunction(SerializationInfo *info) { pushUpValue(info->luaState, cl->l.upvals[i]); // >>>>> permTbl indexTbl ...... func upval - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func @@ -519,14 +519,14 @@ static void serializeFunction(SerializationInfo *info) { } // >>>>> permTbl indexTbl ...... func fenv/nil - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... func } } -static void serializeThread(SerializationInfo *info) { +static void persistThread(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... thread lua_State *threadState = lua_tothread(info->luaState, -1); @@ -547,7 +547,7 @@ static void serializeThread(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ for (; stackSize > 0; --stackSize) { - serialize(info); + persist(info); lua_pop(info->luaState, 1); } @@ -609,7 +609,7 @@ static void serializeThread(SerializationInfo *info) { pushUpValue(info->luaState, upVal); // >>>>> permTbl indexTbl ...... thread upVal - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... thread @@ -624,13 +624,13 @@ static void serializeThread(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... thread nil // Use nil as a terminator - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... thread } -static void serializeProto(SerializationInfo *info) { +static void persistProto(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... proto Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); @@ -644,7 +644,7 @@ static void serializeProto(SerializationInfo *info) { pushObject(info->luaState, &proto->k[i]); // >>>>> permTbl indexTbl ...... proto const - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -660,7 +660,7 @@ static void serializeProto(SerializationInfo *info) { pushProto(info->luaState, proto->p[i]); // >>>>> permTbl indexTbl ...... proto subProto */ - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -683,7 +683,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->upvalues[i]); // >>>>> permTbl indexTbl ...... proto str - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -697,7 +697,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->locvars[i].varname); // >>>>> permTbl indexTbl ...... proto str - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -711,7 +711,7 @@ static void serializeProto(SerializationInfo *info) { pushString(info->luaState, proto->source); // >>>>> permTbl indexTbl ...... proto sourceStr - serialize(info); + persist(info); lua_pop(info->luaState, 1); // >>>>> permTbl indexTbl ...... proto @@ -757,7 +757,7 @@ static void serializeProto(SerializationInfo *info) { * (d) When unserializing, "reopen" each of these upvalues as the thread is * unserialized */ -static void serializeUpValue(SerializationInfo *info) { +static void persistUpValue(SerializationInfo *info) { // >>>>> permTbl indexTbl ...... upval assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); @@ -774,11 +774,11 @@ static void serializeUpValue(SerializationInfo *info) { pushObject(info->luaState, upValue->v); // >>>>> permTbl indexTbl ...... obj - serialize(info); + persist(info); // >>>>> permTbl indexTbl ...... obj } -static void serializeUserData(SerializationInfo *info) { +static void persistUserData(SerializationInfo *info) { // >>>>> permTbl rootObj ...... udata // Make sure there is enough room on the stack @@ -804,7 +804,7 @@ static void serializeUserData(SerializationInfo *info) { } // >>>>> permTbl rootObj ...... udata metaTbl/nil - serialize(info); + persist(info); lua_pop(info->luaState, 1); /* perms reftbl ... udata */ diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp index 6f3275741a..ef0ef31041 100644 --- a/engines/sword25/util/lua_unpersist.cpp +++ b/engines/sword25/util/lua_unpersist.cpp @@ -64,18 +64,18 @@ struct UnSerializationInfo { Common::ReadStream *readStream; }; -static void unserialize(UnSerializationInfo *info); +static void unpersist(UnSerializationInfo *info); -static void unserializeBoolean(UnSerializationInfo *info); -static void unserializeNumber(UnSerializationInfo *info); -static void unserializeString(UnSerializationInfo *info); -static void unserializeTable(UnSerializationInfo *info, int index); -static void unserializeFunction(UnSerializationInfo *info, int index); -static void unserializeThread(UnSerializationInfo *info, int index); -static void unserializeProto(UnSerializationInfo *info, int index); -static void unserializeUpValue(UnSerializationInfo *info, int index); -static void unserializeUserData(UnSerializationInfo *info, int index); -static void unserializePermanent(UnSerializationInfo *info, int index); +static void unpersistBoolean(UnSerializationInfo *info); +static void unpersistNumber(UnSerializationInfo *info); +static void unpersistString(UnSerializationInfo *info); +static void unpersistTable(UnSerializationInfo *info, int index); +static void unpersistFunction(UnSerializationInfo *info, int index); +static void unpersistThread(UnSerializationInfo *info, int index); +static void unpersistProto(UnSerializationInfo *info, int index); +static void unpersistUpValue(UnSerializationInfo *info, int index); +static void unpersistUserData(UnSerializationInfo *info, int index); +static void unpersistPermanent(UnSerializationInfo *info, int index); void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { @@ -98,7 +98,7 @@ void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { lua_gc(luaState, LUA_GCSTOP, 0); // Unserialize the root object - unserialize(&info); + unpersist(&info); // >>>>> permTbl indexTbl rootObj // Re-start garbage collection @@ -129,7 +129,7 @@ static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... obj } -static void unserialize(UnSerializationInfo *info) { +static void unpersist(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -142,7 +142,7 @@ static void unserialize(UnSerializationInfo *info) { switch (type) { case LUA_TBOOLEAN: - unserializeBoolean(info); + unpersistBoolean(info); break; case LUA_TLIGHTUSERDATA: // You can't serialize a pointer @@ -150,31 +150,31 @@ static void unserialize(UnSerializationInfo *info) { assert(0); break; case LUA_TNUMBER: - unserializeNumber(info); + unpersistNumber(info); break; case LUA_TSTRING: - unserializeString(info); + unpersistString(info); break; case LUA_TTABLE: - unserializeTable(info, index); + unpersistTable(info, index); break; case LUA_TFUNCTION: - unserializeFunction(info, index); + unpersistFunction(info, index); break; case LUA_TTHREAD: - unserializeThread(info, index); + unpersistThread(info, index); break; case LUA_TPROTO: - unserializeProto(info, index); + unpersistProto(info, index); break; case LUA_TUPVAL: - unserializeUpValue(info, index); + unpersistUpValue(info, index); break; case LUA_TUSERDATA: - unserializeUserData(info, index); + unpersistUserData(info, index); break; case PERMANENT_TYPE: - unserializePermanent(info, index); + unpersistPermanent(info, index); break; default: assert(0); @@ -212,7 +212,7 @@ static void unserialize(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... obj/nil } -static void unserializeBoolean(UnSerializationInfo *info) { +static void unpersistBoolean(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -224,7 +224,7 @@ static void unserializeBoolean(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... bool } -static void unserializeNumber(UnSerializationInfo *info) { +static void unpersistNumber(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -242,7 +242,7 @@ static void unserializeNumber(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... num } -static void unserializeString(UnSerializationInfo *info) { +static void unpersistString(UnSerializationInfo *info) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -265,7 +265,7 @@ static void unserializeSpecialTable(UnSerializationInfo *info, int index) { // Make sure there is enough room on the stack lua_checkstack(info->luaState, 1); - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... spfunc lua_call(info->luaState, 0, 1); @@ -286,7 +286,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... tbl // Unserialize metatable - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? if (lua_istable(info->luaState, -1)) { @@ -303,7 +303,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { while (1) { // >>>>> permTbl indexTbl ...... tbl - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... tbl key/nil // The table serialization is nil terminated @@ -316,7 +316,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { } // >>>>> permTbl indexTbl ...... tbl key - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... tbl value lua_rawset(info->luaState, -3); @@ -324,7 +324,7 @@ static void unserializeLiteralTable(UnSerializationInfo *info, int index) { } } -void unserializeTable(UnSerializationInfo *info, int index) { +void unpersistTable(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -341,7 +341,7 @@ void unserializeTable(UnSerializationInfo *info, int index) { } } -void unserializeFunction(UnSerializationInfo *info, int index) { +void unpersistFunction(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -372,7 +372,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { registerObjectInIndexTable(info, index); // Now that it's safe, we can get the real proto - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... func proto lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); @@ -382,7 +382,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { for (byte i = 0; i < numUpValues; ++i) { // >>>>> permTbl indexTbl ...... func - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... func func2 unboxUpValue(info->luaState); @@ -394,7 +394,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { } // Finally, the fenv - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... func ?fenv/nil? if (!lua_isnil(info->luaState, -1)) { @@ -410,7 +410,7 @@ void unserializeFunction(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... func } -void unserializeThread(UnSerializationInfo *info, int index) { +void unpersistThread(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... lua_State *L2; @@ -432,7 +432,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { // very bottom of the stack L2->top--; for (uint32 i = 0; i < stackSize; ++i) { - unserialize(info); + unpersist(info); // L1: permTbl indexTbl ...... thread obj* } @@ -492,7 +492,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { global_State *g = G(L2); while (true) { - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... thread upVal/nil // The list is terminated by a nil @@ -535,7 +535,7 @@ void unserializeThread(UnSerializationInfo *info, int index) { } } -void unserializeProto(UnSerializationInfo *info, int index) { +void unpersistProto(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... // We have to be careful. The GC expects a lot out of protos. In particular, we need @@ -564,7 +564,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); for (int i = 0; i < sizek; ++i) { // >>>>> permTbl indexTbl ...... proto - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... proto k setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); @@ -581,7 +581,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); for (int i = 0; i < sizep; ++i) { // >>>>> permTbl indexTbl ...... proto - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... proto subproto p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; @@ -605,7 +605,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); for (int i = 0; i < p->sizeupvalues; ++i) { // >>>>> permTbl indexTbl ...... proto - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... proto str p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -621,7 +621,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); for (int i = 0; i < p->sizelocvars; ++i) { // >>>>> permTbl indexTbl ...... proto - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... proto str p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -635,7 +635,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... proto // Read in source string - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... proto sourceStr p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); @@ -661,7 +661,7 @@ void unserializeProto(UnSerializationInfo *info, int index) { p->maxstacksize = info->readStream->readByte(); } -void unserializeUpValue(UnSerializationInfo *info, int index) { +void unpersistUpValue(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... lua_checkstack(info->luaState, 2); @@ -669,14 +669,14 @@ void unserializeUpValue(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... func registerObjectInIndexTable(info, index); - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... func obj boxUpValue_finish(info->luaState); // >>>>> permTbl indexTbl ...... func } -void unserializeUserData(UnSerializationInfo *info, int index) { +void unpersistUserData(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack @@ -684,7 +684,7 @@ void unserializeUserData(UnSerializationInfo *info, int index) { int isspecial = info->readStream->readSint32LE(); if (isspecial) { - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... specialFunc lua_call(info->luaState, 0, 1); @@ -697,7 +697,7 @@ void unserializeUserData(UnSerializationInfo *info, int index) { info->readStream->read(lua_touserdata(info->luaState, -1), length); - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... udata metaTable/nil lua_setmetatable(info->luaState, -2); @@ -706,13 +706,13 @@ void unserializeUserData(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... udata } -void unserializePermanent(UnSerializationInfo *info, int index) { +void unpersistPermanent(UnSerializationInfo *info, int index) { // >>>>> permTbl indexTbl ...... // Make sure there is enough room on the stack lua_checkstack(info->luaState, 2); - unserialize(info); + unpersist(info); // >>>>> permTbl indexTbl ...... permKey lua_gettable(info->luaState, 1); -- cgit v1.2.3 From e4f74b6c346023608841d9f871b16069e5b54194 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 18:28:14 -0600 Subject: SWORD25: Remove the option to persist a double as a string Since the current method *should* be more accurate --- engines/sword25/util/lua_persist.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp index 6038111bbc..6fe88fe9a3 100644 --- a/engines/sword25/util/lua_persist.cpp +++ b/engines/sword25/util/lua_persist.cpp @@ -281,21 +281,11 @@ static void persistBoolean(SerializationInfo *info) { static void persistNumber(SerializationInfo *info) { lua_Number value = lua_tonumber(info->luaState, -1); - #if 1 - Util::SerializedDouble serializedValue(Util::encodeDouble(value)); - - info->writeStream->writeUint32LE(serializedValue.significandOne); - info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); - info->writeStream->writeSint16LE(serializedValue.exponent); - #else - // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness. - // Also, I don't know if we can assume all compilers use IEEE double - // Therefore, I have chosen to store the double as a string. - Common::String buffer = Common::String::format("%f", value); - - info->writeStream->write(buffer.c_str(), buffer.size()); - #endif + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); } static void persistString(SerializationInfo *info) { -- cgit v1.2.3