aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2015-01-04 19:47:17 +0100
committerEugene Sandulenko2015-01-04 19:47:17 +0100
commit2587852c88e2fbc52bf702f71738e80628eb43fe (patch)
treed2efd31596411669890a12ad43771a500fd6d7a1
parent7da48233ce9a0154364bf26c1e419d24e70f2a26 (diff)
parente4f74b6c346023608841d9f871b16069e5b54194 (diff)
downloadscummvm-rg350-2587852c88e2fbc52bf702f71738e80628eb43fe.tar.gz
scummvm-rg350-2587852c88e2fbc52bf702f71738e80628eb43fe.tar.bz2
scummvm-rg350-2587852c88e2fbc52bf702f71738e80628eb43fe.zip
Merge pull request #557 from RichieSams/replace_pluto
SWORD25: Replace 'Pluto' lua persistance code with re-written, platform-agnostic code
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.cpp7
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.h1
-rw-r--r--engines/sword25/module.mk7
-rw-r--r--engines/sword25/script/luascript.cpp61
-rw-r--r--engines/sword25/util/double_serialization.cpp138
-rw-r--r--engines/sword25/util/double_serialization.h95
-rw-r--r--engines/sword25/util/lua_persist.cpp804
-rw-r--r--engines/sword25/util/lua_persistence.h67
-rw-r--r--engines/sword25/util/lua_persistence_util.cpp393
-rw-r--r--engines/sword25/util/lua_persistence_util.h122
-rw-r--r--engines/sword25/util/lua_unpersist.cpp722
-rw-r--r--engines/sword25/util/pluto/CHANGELOG37
-rw-r--r--engines/sword25/util/pluto/FILEFORMAT168
-rw-r--r--engines/sword25/util/pluto/README133
-rw-r--r--engines/sword25/util/pluto/THANKS9
-rw-r--r--engines/sword25/util/pluto/pdep.cpp112
-rw-r--r--engines/sword25/util/pluto/pdep/README5
-rw-r--r--engines/sword25/util/pluto/pdep/lzio.h65
-rw-r--r--engines/sword25/util/pluto/pdep/pdep.h42
-rw-r--r--engines/sword25/util/pluto/pluto.cpp2083
-rw-r--r--engines/sword25/util/pluto/pluto.h25
-rw-r--r--engines/sword25/util/pluto/plzio.cpp74
22 files changed, 2365 insertions, 2805 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/module.mk b/engines/sword25/module.mk
index 234baec165..0842eb9aa8 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_serialization.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 f62a08005b..e93289596b 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_persistence.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<byte> & chunkData = *reinterpret_cast<Common::Array<byte> * >(ud);
- const byte *buffer = reinterpret_cast<const byte *>(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<byte> chunkData;
- pluto_persist(_state, chunkwriter, &chunkData);
+ // Lua persists and stores the data in a WriteStream
+ Common::MemoryWriteStreamDynamic writeStream;
+ Lua::persistLua(_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<ChunkreaderData *>(ud);
-
- if (!cd.BufferReturned) {
- cd.BufferReturned = true;
- *sz = cd.Size;
- return reinterpret_cast<const char *>(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<byte> 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::unpersistLua(_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);
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/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp
new file mode 100644
index 0000000000..6fe88fe9a3
--- /dev/null
+++ b/engines/sword25/util/lua_persist.cpp
@@ -0,0 +1,804 @@
+/* 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.
+ *
+ */
+
+/**
+ * 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"
+#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 persist(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) {
+ SerializationInfo info;
+ info.luaState = luaState;
+ info.writeStream = writeStream;
+ info.counter = 1u;
+
+ // 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
+ persist(&info);
+
+ // Return the stack back to the original state
+ lua_remove(luaState, 2);
+ // >>>>> permTbl rootObj
+}
+
+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
+
+ // 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 index/nil off the stack
+ lua_pop(info->luaState, 1);
+
+ // 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);
+ // >>>>> 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
+ persist(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:
+ persistBoolean(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:
+ persistNumber(info);
+ break;
+ case LUA_TSTRING:
+ persistString(info);
+ break;
+ case LUA_TTABLE:
+ persistTable(info);
+ break;
+ case LUA_TFUNCTION:
+ persistFunction(info);
+ break;
+ case LUA_TTHREAD:
+ persistThread(info);
+ break;
+ case LUA_TPROTO:
+ persistProto(info);
+ break;
+ case LUA_TUPVAL:
+ persistUpValue(info);
+ break;
+ case LUA_TUSERDATA:
+ persistUserData(info);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void persistBoolean(SerializationInfo *info) {
+ int value = lua_toboolean(info->luaState, -1);
+
+ info->writeStream->writeSint32LE(value);
+}
+
+static void persistNumber(SerializationInfo *info) {
+ lua_Number value = lua_tonumber(info->luaState, -1);
+
+ 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) {
+ // 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<uint32>(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
+ persist(info);
+
+ lua_pop(info->luaState, 2);
+ // >>>>> permTbl indexTbl ...... obj
+
+ return true;
+}
+
+static void persistTable(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 */
+ persist(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
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl k v */
+
+ // Serialize the value
+ persist(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
+
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl
+}
+
+static void persistFunction(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 */
+
+ persist(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
+
+ persist(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
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... func
+ }
+}
+
+static void persistThread(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<uint32>(appendStackToStack_reverse(threadState, info->luaState));
+ info->writeStream->writeUint32LE(stackSize);
+
+ // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */
+ for (; stackSize > 0; --stackSize) {
+ persist(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<uint32>((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<uint32>(ci->base - threadState->stack);
+ uint32 stackFunc = static_cast<uint32>(ci->func - threadState->stack);
+ uint32 stackTop = static_cast<uint32>(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<uint32>(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<uint32>(threadState->base - threadState->stack);
+ uint32 stackFunc = static_cast<uint32>(threadState->top - threadState->stack);
+ info->writeStream->writeUint32LE(stackBase);
+ info->writeStream->writeUint32LE(stackFunc);
+
+ // Same argument as above about truncation
+ uint32 stackOffset = static_cast<uint32>(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
+
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... thread
+
+ // Same argument as above about truncation
+ uint32 stackpos = static_cast<uint32>(upVal->v - threadState->stack);
+ info->writeStream->writeUint32LE(stackpos);
+ }
+
+ // >>>>> permTbl indexTbl ...... thread
+ lua_pushnil(info->luaState);
+ // >>>>> permTbl indexTbl ...... thread nil
+
+ // Use nil as a terminator
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... thread
+}
+
+static void persistProto(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
+
+ persist(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 */
+
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+ }
+
+ // >>>>> permTbl indexTbl ...... proto
+
+ // Serialize the code
+ info->writeStream->writeSint32LE(proto->sizecode);
+
+ uint32 len = static_cast<uint32>(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
+
+ persist(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
+
+ persist(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
+
+ persist(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+
+ // Serialize line numbers
+ info->writeStream->writeSint32LE(proto->sizelineinfo);
+
+ if (proto->sizelineinfo) {
+ uint32 len = static_cast<uint32>(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 persistUpValue(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
+
+ persist(info);
+ // >>>>> permTbl indexTbl ...... obj
+}
+
+static void persistUserData(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<uint32>(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
+ persist(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..53e3dee02e
--- /dev/null
+++ b/engines/sword25/util/lua_persistence.h
@@ -0,0 +1,67 @@
+/* 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.
+ *
+ */
+
+/**
+ * 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
+
+#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..958fb7ac3c
--- /dev/null
+++ b/engines/sword25/util/lua_persistence_util.cpp
@@ -0,0 +1,393 @@
+/* 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.
+ *
+ */
+
+/**
+ * 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"
+
+#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..4d0085e242
--- /dev/null
+++ b/engines/sword25/util/lua_persistence_util.h
@@ -0,0 +1,122 @@
+/* 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.
+ *
+ */
+
+/**
+ * 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
+
+
+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_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp
new file mode 100644
index 0000000000..ef0ef31041
--- /dev/null
+++ b/engines/sword25/util/lua_unpersist.cpp
@@ -0,0 +1,722 @@
+/* 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.
+ *
+ */
+
+/**
+ * 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"
+#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 unpersist(UnSerializationInfo *info);
+
+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) {
+ 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
+ unpersist(&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 unpersist(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:
+ unpersistBoolean(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:
+ unpersistNumber(info);
+ break;
+ case LUA_TSTRING:
+ unpersistString(info);
+ break;
+ case LUA_TTABLE:
+ unpersistTable(info, index);
+ break;
+ case LUA_TFUNCTION:
+ unpersistFunction(info, index);
+ break;
+ case LUA_TTHREAD:
+ unpersistThread(info, index);
+ break;
+ case LUA_TPROTO:
+ unpersistProto(info, index);
+ break;
+ case LUA_TUPVAL:
+ unpersistUpValue(info, index);
+ break;
+ case LUA_TUSERDATA:
+ unpersistUserData(info, index);
+ break;
+ case PERMANENT_TYPE:
+ unpersistPermanent(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 unpersistBoolean(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 unpersistNumber(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 unpersistString(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);
+
+ unpersist(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
+ unpersist(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
+ unpersist(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
+ unpersist(info);
+ // >>>>> permTbl indexTbl ...... tbl value
+
+ lua_rawset(info->luaState, -3);
+ // >>>>> permTbl indexTbl ...... tbl
+ }
+}
+
+void unpersistTable(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 unpersistFunction(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
+ unpersist(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
+ unpersist(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
+ unpersist(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 unpersistThread(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) {
+ unpersist(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) {
+ unpersist(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 unpersistProto(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
+ unpersist(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
+ unpersist(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
+ unpersist(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
+ 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)));
+ 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
+ unpersist(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 unpersistUpValue(UnSerializationInfo *info, int index) {
+ // >>>>> permTbl indexTbl ......
+ lua_checkstack(info->luaState, 2);
+
+ boxUpValue_start(info->luaState);
+ // >>>>> permTbl indexTbl ...... func
+ registerObjectInIndexTable(info, index);
+
+ unpersist(info);
+ // >>>>> permTbl indexTbl ...... func obj
+
+ boxUpValue_finish(info->luaState);
+ // >>>>> permTbl indexTbl ...... func
+}
+
+void unpersistUserData(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) {
+ unpersist(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);
+
+ unpersist(info);
+ // >>>>> permTbl indexTbl ...... udata metaTable/nil
+
+ lua_setmetatable(info->luaState, -2);
+ // >>>>> permTbl indexTbl ...... udata
+ }
+ // >>>>> permTbl indexTbl ...... udata
+}
+
+void unpersistPermanent(UnSerializationInfo *info, int index) {
+ // >>>>> permTbl indexTbl ......
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 2);
+
+ unpersist(info);
+ // >>>>> permTbl indexTbl ...... permKey
+
+ lua_gettable(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... perm
+}
+
+} // End of namespace Lua
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 <string.h>
-
-
-/* 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 <stdio.h>
-#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; il<indent; il++) {
- printf(" ");
- }
-}
-#endif
-
-/* lua_Chunkwriter signature: (lua_State *L, const void *p, size_t sz, void *ud).
- ud is a pointer to the WriterInfo struct (holds the buffer pointer)
-*/
-
-static void pi_write(PersistInfo *pi, const void *p, size_t size, void *ud) {
- if (humanReadable) {
- uint i;
- snprintf(hrBuf, hrBufSize, " pi_write %d ", (int) size);
- pi->writer(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; i<cl->l.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; i<p->sizek; 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; i<p->sizep; 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; i<p->sizeupvalues; 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; i<p->sizelocvars; 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; i<numframes; i++) {
- CallInfo *ci = L2->base_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<uint64>(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; i<nupvalues; i++) {
- lcl->upvals[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; i<nupvalues; i++) {
- /* perms reftbl ... func */
- unpersist(upi);
- /* perms reftbl ... func func2 */
- unboxupval(upi->L);
- /* 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; i<sizek; i++) {
- /* perms reftbl ... proto */
- unpersist(upi);
- /* perms reftbl ... proto k */
- setobj2s(upi->L, &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; i<sizep; i++) {
- /* perms reftbl ... proto */
- unpersist(upi);
- /* perms reftbl ... proto subproto */
- p->p[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; i<p->sizeupvalues; 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; i<p->sizelocvars; 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; i<stacksize; i++) {
- unpersist(upi);
- /* L1: perms reftbl ... thr obj* */
- }
- lua_xmove(upi->L, 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; i<numframes; i++) {
- CallInfo *ci = L2->base_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<ptrdiff_t>(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 <string.h>
-
-#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;
-}