aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/script
diff options
context:
space:
mode:
authorEugene Sandulenko2010-07-29 19:53:02 +0000
committerEugene Sandulenko2010-10-12 21:38:20 +0000
commita683a420a9e43705c972b5e74d55e319729e1a81 (patch)
treebde6e4abd417bdfaec120aa951da9a19be36b654 /engines/sword25/script
parent7723d91c957d07205c51be32498d45cd0a78568f (diff)
downloadscummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.gz
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.bz2
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.zip
SWORD25: Importing original sources
svn-id: r53171
Diffstat (limited to 'engines/sword25/script')
-rwxr-xr-xengines/sword25/script/lua_extensions.cpp68
-rwxr-xr-xengines/sword25/script/luabindhelper.cpp419
-rwxr-xr-xengines/sword25/script/luabindhelper.h107
-rwxr-xr-xengines/sword25/script/luacallback.cpp203
-rwxr-xr-xengines/sword25/script/luacallback.h64
-rwxr-xr-xengines/sword25/script/luascript.cpp607
-rwxr-xr-xengines/sword25/script/luascript.h102
-rwxr-xr-xengines/sword25/script/script.h99
8 files changed, 1669 insertions, 0 deletions
diff --git a/engines/sword25/script/lua_extensions.cpp b/engines/sword25/script/lua_extensions.cpp
new file mode 100755
index 0000000000..05f9c5db79
--- /dev/null
+++ b/engines/sword25/script/lua_extensions.cpp
@@ -0,0 +1,68 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "luascript.h"
+#include "luabindhelper.h"
+
+// -----------------------------------------------------------------------------
+
+static int Warning(lua_State * L)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ luaL_checkstring(L, 1);
+ luaL_where(L, 1);
+ lua_pushstring(L, "WARNING - ");
+ lua_pushvalue(L, 1);
+ lua_concat(L, 3);
+ BS_Log::Log("%s\n", luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg GLOBAL_FUNCTIONS[] =
+{
+ "warning", Warning,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::RegisterStandardLibExtensions()
+{
+ lua_State * L = m_State;
+ BS_ASSERT(m_State);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, "", GLOBAL_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/script/luabindhelper.cpp b/engines/sword25/script/luabindhelper.cpp
new file mode 100755
index 0000000000..eccc568989
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.cpp
@@ -0,0 +1,419 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "luabindhelper.h"
+#include "luascript.h"
+#include <sstream>
+
+#define BS_LOG_PREFIX "LUABINDHELPER"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * METATABLES_TABLE_NAME = "__METATABLES";
+ const char * PERMANENTS_TABLE_NAME = "Permanents";
+
+ bool RegisterPermanent(lua_State * L, const std::string & Name)
+ {
+ // Eine C-Funktion muss auf dem Stack liegen.
+ if (!lua_iscfunction(L, -1)) return false;
+
+ // Sicherstellen, dass die Permanents-Tabelle oben auf dem Stack liegt.
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ if (lua_isnil(L, -1))
+ {
+ // Permanents-Tabelle existiert noch nicht, sie muss erstellt werden.
+
+ // Nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Permanents-Tabelle erstellen und eine zweite Referenz darauf auf den Stack legen.
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+
+ // Permanents-Tabelle in der Registry speichern. Die zweite Referenz verbleibt auf dem Stack um im Anschluss benutzt zu werden.
+ lua_setfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ }
+
+ // C-Funktion mit dem Namen als Index in der Permanents-Tabelle ablegen.
+ lua_insert(L, -2);
+ lua_setfield(L, -2, Name.c_str());
+
+ // Permanents-Tabelle vom Stack nehmen.
+ lua_pop(L, 1);
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddFunctionsToLib(lua_State * L, const std::string & LibName, const luaL_reg * Functions)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Wenn der Tabellenname leer ist, werden die Funktionen zum globalen Namensraum hinzugefügt.
+ if (LibName.size() == 0)
+ {
+ for (; Functions->name; ++Functions)
+ {
+ lua_pushstring(L, Functions->name);
+ lua_pushcclosure(L, Functions->func, 0);
+ lua_settable(L, LUA_GLOBALSINDEX);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Functions->name);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ RegisterPermanent(L, Functions->name);
+ }
+ }
+ // Wenn der Tabellenname nicht leer ist, werden die Funktionen zu dieser Tabelle hinzugefügt.
+ else
+ {
+ // Sicherstellen, dass die Library-Table existiert.
+ if (!_CreateTable(L, LibName)) return false;
+
+ // Die einzelnen Funktionen in der Table registrieren.
+ for (; Functions->name; ++Functions)
+ {
+ // Funktion registrieren.
+ lua_pushstring(L, Functions->name);
+ lua_pushcclosure(L, Functions->func, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Functions->name);
+ lua_gettable(L, -2);
+ RegisterPermanent(L, LibName + "." + Functions->name);
+ }
+
+ // Library-Table vom Lua-Stack nehmen.
+ lua_pop(L, 1);
+ }
+
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddConstantsToLib(lua_State * L, const std::string & LibName, const lua_constant_reg * Constants)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Wenn der Tabellenname leer ist, werden die Konstanten zum globalen Namensraum hinzugefügt.
+ if (LibName.size() == 0)
+ {
+ for (; Constants->Name; ++Constants)
+ {
+ lua_pushstring(L, Constants->Name);
+ lua_pushnumber(L, Constants->Value);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+ // Wenn der Tabellenname nicht leer ist, werden die Konstanten zu dieser Tabelle hinzugefügt.
+ else
+ {
+ // Sicherstellen, dass die Library-Table existiert.
+ if (!_CreateTable(L, LibName)) return false;
+
+ // Die einzelnen Konstanten in der Table registrieren
+ for (; Constants->Name; ++Constants)
+ {
+ lua_pushstring(L, Constants->Name);
+ lua_pushnumber(L, Constants->Value);
+ lua_settable(L, -3);
+ }
+
+ // Library-Tabelle vom Lua-Stack nehmen
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddMethodsToClass(lua_State * L, const std::string & ClassName, const luaL_reg * Methods)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Metatable auf den Lua-Stack laden
+ if (!GetMetatable(L, ClassName)) return false;
+
+ // Die einzelnen Methoden in der Metatable registrieren
+ for (; Methods->name; ++Methods)
+ {
+ lua_pushstring(L, Methods->name);
+ lua_pushcclosure(L, Methods->func, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Methods->name);
+ lua_gettable(L, -2);
+ RegisterPermanent(L, ClassName + "." + Methods->name);
+ }
+
+ // Metatable vom Lua-Stack nehmen
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::SetClassGCHandler(lua_State * L, const std::string & ClassName, lua_CFunction GCHandler)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Metatable auf den Lua-Stack laden
+ if (!GetMetatable(L, ClassName)) return false;
+
+ // Den GC-Handler in die Metatable schreiben
+ lua_pushstring(L, "__gc");
+ lua_pushcclosure(L, GCHandler, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, "__gc");
+ lua_gettable(L, -2);
+ RegisterPermanent(L, ClassName + ".__gc");
+
+ // Metatable vom Lua-Stack nehmen
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ void PushMetatableTable(lua_State * L)
+ {
+ // Tabelle mit den Metatabellen auf den Stack legen.
+ lua_getglobal(L, METATABLES_TABLE_NAME);
+
+ // Wenn die Tabelle noch nicht existiert, muss sie erstellt werden.
+ if (lua_isnil(L, -1))
+ {
+ // nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen, in die globale Table eintragen und eine Referenz auf dem Stack lassen.
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, METATABLES_TABLE_NAME);
+ }
+ }
+}
+
+
+bool BS_LuaBindhelper::GetMetatable(lua_State * L, const std::string & TableName)
+{
+ // Tabelle mit den Metatabellen auf den Stack legen.
+ PushMetatableTable(L);
+
+ // Versuchen, die gewünschte Metatabelle auf den Stack zu legen. Wenn sie noch nicht existiert, muss sie erstellt werden.
+ lua_getfield(L, -1, TableName.c_str());
+ if (lua_isnil(L, -1))
+ {
+ // nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen.
+ lua_newtable(L);
+
+ // Das __index Feld der Metatabele zeigt auf die Metatabelle selbst.
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+
+ // Persistfeld auf true setzen. Dies sorgt dafür, dass Objekte mit dieser Metatabelle direkt gespeichert werden.
+ lua_pushbooleancpp(L, true);
+ lua_setfield(L, -2, "__persist");
+
+ // Metatabelle in die Tabelle für Metatabellen eintragen und eine Referenz auf dem Stack lassen.
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, TableName.c_str());
+ }
+
+ // Tabelle mit den Metatabellen vom Stack nehmen.
+ lua_remove(L, -2);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::_CreateTable(lua_State * L, const std::string & TableName)
+{
+ // Der Tabellenname wird an den Punkten auseinandergetrennt und jeweils die Untertabellen erstellt.
+ // Auf diese Weise können auch Tabellen mit Untertabellen erstellt werden (z.B. Foo.Bar).
+ std::string::size_type PartBegin = 0;
+ while (PartBegin <= TableName.size())
+ {
+ std::string::size_type PartEnd;
+ PartEnd = TableName.find(".", PartBegin);
+ if (PartEnd == std::string::npos) PartEnd = TableName.size();
+ std::string SubTableName = TableName.substr(PartBegin, PartEnd - PartBegin);
+
+ // Tabellen mit einen leeren String als Namen sind nicht zulässig.
+ if (SubTableName.size() == 0) return false;
+
+ // Überprüfen, ob die Tabelle mit dem Namen bereits existiert.
+ // Beim ersten Durchgang wird im globalen Namensbereich gesucht, bei späteren Durchgängen in der entsprechenden Elterntabelle auf dem Stack.
+ if (PartBegin == 0)
+ {
+ lua_pushstring(L, SubTableName.c_str());
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ }
+ else
+ {
+ lua_pushstring(L, SubTableName.c_str());
+ lua_gettable(L, -2);
+ if (!lua_isnil(L, -1)) lua_remove(L, -2);
+ }
+
+ // Wenn nicht, Table erstellen
+ if (lua_isnil(L, -1))
+ {
+ // nil-Wert vom Stack holen
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen
+ lua_newtable(L);
+ lua_pushstring(L, SubTableName.c_str());
+ lua_pushvalue(L, -2);
+ if (PartBegin == 0)
+ lua_settable(L, LUA_GLOBALSINDEX);
+ else
+ {
+ lua_settable(L, -4);
+ lua_remove(L, -2);
+ }
+ }
+
+ PartBegin = PartEnd + 1;
+ }
+
+ return true;
+}
+
+namespace
+{
+ std::string GetLuaValueInfo(lua_State * L, int StackIndex)
+ {
+ switch (lua_type(L, StackIndex))
+ {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, StackIndex));
+ break;
+
+ case LUA_TSTRING:
+ lua_pushfstring(L, "\"%s\"", lua_tostring(L, StackIndex));
+ break;
+
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, StackIndex) ? "true" : "false"));
+ break;
+
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+
+ default:
+ lua_pushfstring(L, "%s: %p", luaL_typename(L, StackIndex), lua_topointer(L, StackIndex));
+ break;
+ }
+
+ std::string Result(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ return Result;
+ }
+}
+
+std::string BS_LuaBindhelper::StackDump(lua_State *L)
+{
+ std::ostringstream oss;
+
+ int i = lua_gettop(L);
+ oss << "------------------- Stack Dump -------------------\n";
+
+ while(i)
+ {
+ oss << i << ": " << GetLuaValueInfo(L, i) << "\n";
+ i--;
+ }
+
+ oss << "-------------- Stack Dump Finished ---------------\n";
+
+ return oss.str();
+}
+
+std::string BS_LuaBindhelper::TableDump(lua_State * L)
+{
+ std::ostringstream oss;
+
+ oss << "------------------- Table Dump -------------------\n";
+
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+ oss << GetLuaValueInfo(L, -2) << " : " << GetLuaValueInfo(L, -1) << "\n";
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+
+ oss << "-------------- Table Dump Finished ---------------\n";
+
+ return oss.str();
+}
diff --git a/engines/sword25/script/luabindhelper.h b/engines/sword25/script/luabindhelper.h
new file mode 100755
index 0000000000..c6a8b8e626
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.h
@@ -0,0 +1,107 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef LUABINDHELPER_H
+#define LUABINDHELPER_H
+
+#include "kernel/common.h"
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lauxlib.h>
+}
+
+#define lua_pushbooleancpp(L, b) (lua_pushboolean(L, b ? 1 : 0))
+#define lua_tobooleancpp(L, i) (lua_toboolean(L, i) == 0 ? false : true)
+
+struct lua_constant_reg
+{
+ const char * Name;
+ lua_Number Value;
+};
+
+class BS_LuaBindhelper
+{
+public:
+ /**
+ @brief Registriert eine Menge von Funktionen und fügt dieser einer Lua-Library hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Funktionen registriert werden sollen
+ @param LibName der Name der Library.<br>
+ Wenn dies ein Leerer String ist, werden die Funktionen zum globalen Namensraum hinzugefügt.
+ @param Functions ein Array das die Funktionspointer mit ihren Namen enthält.<br>
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddFunctionsToLib(lua_State * L, const std::string & LibName, const luaL_reg * Functions);
+
+ /**
+ @brief Fügt eine Menge von Konstanten einer Lua-Library hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Konstanten registriert werden sollen
+ @param LibName der Name der Library.<br>
+ Wenn dies ein Leerer String ist, werden die Konstanten zum globalen Namensraum hinzugefügt.
+ @param Constants ein Array das die Werte der Konstanten mit ihren Namen enthält.<br
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddConstantsToLib(lua_State * L, const std::string & LibName, const lua_constant_reg * Constants);
+
+ /**
+ @brief Fügt eine Menge von Methoden zu einer Lua-Klasse hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Methoden registriert werden sollen
+ @param ClassName der Name der Metatable der Klasse.<br>
+ Wenn die Metatable noch nicht existiert, wird sie erstellt.
+ @param Methods ein Array das die Funktionspointer der Methoden mit ihren Namen enthält.<br>
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddMethodsToClass(lua_State * L, const std::string & ClassName, const luaL_reg * Methods);
+
+ /**
+ @brief Legt eine Funktion fest, die aufgerufen wird, wenn Exemplare einer bestimmten Lua-Klasse vom Garbage-Collecter gelöscht werden.
+ @param L ein Pointer auf die Lua-VM
+ @param ClassName der Name der Metatable der Klasse.<br>
+ Wenn die Metatable noch nicht existiert, wird sie erstellt.
+ @param GCHandler ein Funktionspointer auf die Funktion.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool SetClassGCHandler(lua_State * L, const std::string & ClassName, lua_CFunction GCHandler);
+
+ /**
+ @brief Gibt einen String zurück, der einen Stackdump des Lua-Stacks enthält.
+
+ @param L ein Pointer auf die Lua-VM.
+ */
+ static std::string StackDump(lua_State * L);
+
+ /**
+ @brief Gibt einen String zurück, den den Inhalt einer Tabelle beschreibt.
+
+ @param L ein Pointer auf die Lua-VM.
+ @remark Auf dem Lua-Stack muss die Tabelle liegen, die ausgelesen werden soll.
+ */
+ static std::string TableDump(lua_State * L);
+
+ static bool GetMetatable(lua_State * L, const std::string & TableName);
+
+private:
+ static bool _CreateTable(lua_State * L, const std::string & TableName);
+};
+
+#endif
diff --git a/engines/sword25/script/luacallback.cpp b/engines/sword25/script/luacallback.cpp
new file mode 100755
index 0000000000..09103d5467
--- /dev/null
+++ b/engines/sword25/script/luacallback.cpp
@@ -0,0 +1,203 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "luacallback.h"
+#include "luabindhelper.h"
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lauxlib.h>
+}
+
+#define BS_LOG_PREFIX "LUA"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * CALLBACKTABLE_NAME = "__CALLBACKS";
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaCallback::BS_LuaCallback(lua_State * L)
+{
+ // Callbacktabelle erstellen.
+ lua_newtable(L);
+ lua_setglobal(L, CALLBACKTABLE_NAME);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaCallback::~BS_LuaCallback()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::RegisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
+{
+ BS_ASSERT(lua_isfunction(L, -1));
+ EnsureObjectCallbackTableExists(L, ObjectHandle);
+
+ // Funktion in der Objekt-Callbacktabelle speichern.
+ lua_pushvalue(L, -2);
+ luaL_ref(L, -2);
+
+ // Funktion und Objekt-Callbacktabelle vom Stack poppen.
+ lua_pop(L, 2);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::UnregisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
+{
+ BS_ASSERT(lua_isfunction(L, -1));
+ EnsureObjectCallbackTableExists(L,ObjectHandle);
+
+ // Über alle Elemente der Objekt-Callbacktabelle iterieren und die Funktion daraus entfernen.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+
+ // Falls der Wert identisch mit dem Funktionsparameter ist, wird sie aus der Tabelle entfernt.
+ if (lua_equal(L, -1, -4))
+ {
+ lua_pushvalue(L, -2);
+ lua_pushnil(L);
+ lua_settable(L, -5);
+
+ // Die Funktion wurde gefunden, die Iteration kann abgebrochen werden.
+ lua_pop(L, 2);
+ break;
+ }
+ else
+ {
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+
+ // Funktion und Objekt-Callbacktabelle vom Stack poppen.
+ lua_pop(L, 2);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::RemoveAllObjectCallbacks(lua_State * L, unsigned int ObjectHandle)
+{
+ PushCallbackTable(L);
+
+ // Objekt-Callbacktabelle aus der Callbacktabelle entfernen.
+ lua_pushnumber(L, ObjectHandle);
+ lua_pushnil(L);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::InvokeCallbackFunctions(lua_State * L, unsigned int ObjectHandle)
+{
+ EnsureObjectCallbackTableExists(L, ObjectHandle);
+
+ // Über die Tabelle iterieren und alle Callbacks ausführen.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+
+ // Falls der Wert eine Funktion ist, wird sie ausgeführt.
+ if (lua_type(L, -1) == LUA_TFUNCTION)
+ {
+ // Pre-Funktion aufrufen.
+ // Abgeleitete Klassen könnten in dieser Funktion Parameter auf den Stack schieben.
+ // Der Rückgabewert gibt die Anzahl der Parameter zurück.
+ int ArgumentCount = PreFunctionInvokation(L);
+
+ // lua_pcall poppt die Funktion und die Parameter selber vom Stack.
+ if (lua_pcall(L, ArgumentCount, 0, 0) != 0)
+ {
+ // Ein Fehler ist aufgetreten.
+ BS_LOG_ERRORLN("An error occured executing a callback function: %s", lua_tostring(L, -1));
+
+ // Fehlernachricht vom Stack poppen.
+ lua_pop(L, 1);
+ }
+ }
+ else
+ {
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::EnsureObjectCallbackTableExists(lua_State * L, unsigned int ObjectHandle)
+{
+ PushObjectCallbackTable(L, ObjectHandle);
+
+ // Falls die Tabelle nil ist, muss sie zunächst erstellt werden.
+ if (lua_isnil(L, -1))
+ {
+ // Nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ PushCallbackTable(L);
+
+ // Neue Tabelle unter dem Index ObjectHandle in der Callbacktabelle ablegen.
+ lua_newtable(L);
+ lua_pushnumber(L, ObjectHandle);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4);
+
+ // Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
+ lua_remove(L, -2);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::PushCallbackTable(lua_State * L)
+{
+ lua_getglobal(L, CALLBACKTABLE_NAME);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::PushObjectCallbackTable(lua_State * L, unsigned int ObjectHandle)
+{
+ PushCallbackTable(L);
+
+ // Objekt-Callbacktabelle auf den Stack legen.
+ lua_pushnumber(L, ObjectHandle);
+ lua_gettable(L, -2);
+
+ // Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
+ lua_remove(L, -2);
+}
diff --git a/engines/sword25/script/luacallback.h b/engines/sword25/script/luacallback.h
new file mode 100755
index 0000000000..dbf201b4ad
--- /dev/null
+++ b/engines/sword25/script/luacallback.h
@@ -0,0 +1,64 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_LUACALLBACK_H
+#define BS_LUACALLBACK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+struct lua_State;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_LuaCallback
+{
+public:
+ BS_LuaCallback(lua_State * L);
+ virtual ~BS_LuaCallback();
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void RegisterCallbackFunction(lua_State * L, unsigned int ObjectHandle);
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void UnregisterCallbackFunction(lua_State * L, unsigned int ObjectHandle);
+
+ void RemoveAllObjectCallbacks(lua_State * L, unsigned int ObjectHandle);
+
+ void InvokeCallbackFunctions(lua_State * L, unsigned int ObjectHandle);
+
+protected:
+ virtual int PreFunctionInvokation(lua_State * L) { return 0; }
+
+private:
+ void EnsureObjectCallbackTableExists(lua_State * L,unsigned int ObjectHandle);
+ void PushCallbackTable(lua_State * L);
+ void PushObjectCallbackTable(lua_State * L, unsigned int ObjectHandle);
+};
+
+#endif
diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp
new file mode 100755
index 0000000000..dbefc338ce
--- /dev/null
+++ b/engines/sword25/script/luascript.cpp
@@ -0,0 +1,607 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "LUA"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lualib.h>
+ #include <lauxlib.h>
+ #include "util/pluto/pluto.h"
+}
+
+#include "package/packagemanager.h"
+#include "luascript.h"
+#include "luabindhelper.h"
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_LuaScriptEngine::BS_LuaScriptEngine(BS_Kernel * KernelPtr) :
+ BS_ScriptEngine(KernelPtr),
+ m_State(0),
+ m_PcallErrorhandlerRegistryIndex(0)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaScriptEngine::~BS_LuaScriptEngine()
+{
+ // Lua deinitialisieren
+ if (m_State) lua_close(m_State);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_LuaScriptEngine_CreateObject(BS_Kernel * KernelPtr) { return new BS_LuaScriptEngine(KernelPtr); }
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ int PanicCB(lua_State * L)
+ {
+ BS_LOG_ERRORLN("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::Init()
+{
+ // Lua-State intialisieren und Standardbibliotheken initialisieren
+ m_State = luaL_newstate();
+ if (!m_State || ! RegisterStandardLibs() || !RegisterStandardLibExtensions())
+ {
+ BS_LOG_ERRORLN("Lua could not be initialized.");
+ return false;
+ }
+
+ // Panic-Callbackfunktion registrieren.
+ lua_atpanic(m_State, PanicCB);
+
+ // Errorhandlerfunktion für lua_pcall-Aufrufe.
+ // Der untenstehende Code enthält eine lokale ErrorHandler-Funktion und gibt diese zurück.
+ const char ErrorHandlerCode[] =
+ "local function ErrorHandler(message) "
+ " return message .. '\\n' .. debug.traceback('', 2) "
+ "end "
+ "return ErrorHandler";
+
+ // Den Code compilieren.
+ if (luaL_loadbuffer(m_State, ErrorHandlerCode, strlen(ErrorHandlerCode), "PCALL ERRORHANDLER") != 0)
+ {
+ // Fehlernachricht ausgeben und Methode beenden.
+ BS_LOG_ERRORLN("Couldn't compile luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+ // Den Code ausführen, dies legt die Errorhandler-Funktion oben auf den Stack.
+ if (lua_pcall(m_State, 0, 1, 0) != 0)
+ {
+ // Fehlernachricht ausgeben und Methode beenden.
+ BS_LOG_ERRORLN("Couldn't prepare luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+
+ // Die Errorhandler-Funktion in der Lua-Registry ablegen und den Index merken.
+ m_PcallErrorhandlerRegistryIndex = luaL_ref(m_State, LUA_REGISTRYINDEX);
+
+ // Die Pluto Persistenz-Bibliothek initialisieren.
+ luaopen_pluto(m_State);
+ lua_pop(m_State, 1);
+
+ BS_LOGLN("Lua initialized.");
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteFile(const std::string & FileName)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(m_State);
+#endif
+
+ // Pointer auf den Packagemanager holen
+ BS_PackageManager * pPackage = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Datei einlesen
+ unsigned int FileSize;
+ char * FileData = static_cast<char *>(pPackage->GetFile(FileName, &FileSize));
+ if (!FileData)
+ {
+ BS_LOG_ERRORLN("Couldn't read \"%s\".", FileName.c_str());
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+ return false;
+ }
+
+ // Dateiinhalt ausführen
+ if (!ExecuteBuffer(FileData, FileSize, "@" + pPackage->GetAbsolutePath(FileName)))
+ {
+ // Dateipuffer freigeben
+ delete[] FileData;
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+ return false;
+ }
+
+ // Dateipuffer freigeben
+ delete[] FileData;
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteString(const std::string & Code)
+{
+ return ExecuteBuffer(Code.c_str(), Code.length(), "???");
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ void RemoveForbiddenFunctions(lua_State * L)
+ {
+ static const char * FORBIDDEN_FUNCTIONS[] =
+ {
+ "dofile",
+ 0
+ };
+
+ const char ** Iterator = FORBIDDEN_FUNCTIONS;
+ while (*Iterator)
+ {
+ lua_pushnil(L);
+ lua_setfield(L, LUA_GLOBALSINDEX, *Iterator);
+ ++Iterator;
+ }
+ }
+}
+
+bool BS_LuaScriptEngine::RegisterStandardLibs()
+{
+ luaL_openlibs(m_State);
+ RemoveForbiddenFunctions(m_State);
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteBuffer(const char * Data, unsigned int Size, const std::string & Name) const
+{
+ // Puffer kompilieren
+ if (luaL_loadbuffer(m_State, Data, Size, Name.c_str()) != 0)
+ {
+ BS_LOG_ERRORLN("Couldn't compile \"%s\":\n%s", Name.c_str(), lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+
+ // Error-Handler Funktion hinter der auszuführenden Funktion auf den Stack legen.
+ lua_rawgeti(m_State, LUA_REGISTRYINDEX, m_PcallErrorhandlerRegistryIndex);
+ lua_insert(m_State, -2);
+
+ // Pufferinhalt ausführen
+ if (lua_pcall(m_State, 0, 0, -2) != 0)
+ {
+ BS_LOG_ERRORLN("An error occured while executing \"%s\":\n%s.",
+ Name.c_str(),
+ lua_tostring(m_State, -1));
+ lua_pop(m_State, 2);
+
+ return false;
+ }
+
+ // Error-Handler Funktion vom Stack nehmen.
+ lua_pop(m_State, 1);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaScriptEngine::SetCommandLine(const vector<string> & CommandLineParameters)
+{
+ lua_newtable(m_State);
+
+ for (size_t i = 0; i < CommandLineParameters.size(); ++i)
+ {
+ lua_pushnumber(m_State, i + 1);
+ lua_pushstring(m_State, CommandLineParameters[i].c_str());
+ lua_settable(m_State, -3);
+ }
+
+ lua_setglobal(m_State, "CommandLine");
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * PERMANENTS_TABLE_NAME = "Permanents";
+
+ // -------------------------------------------------------------------------
+
+ // Diese Array enthält die Namen der globalen Lua-Objekte, die nicht persistiert werden sollen.
+ const char * STANDARD_PERMANENTS[] =
+ {
+ "string",
+ "xpcall",
+ "package",
+ "tostring",
+ "print",
+ "os",
+ "unpack",
+ "require",
+ "getfenv",
+ "setmetatable",
+ "next",
+ "assert",
+ "tonumber",
+ "io",
+ "rawequal",
+ "collectgarbage",
+ "getmetatable",
+ "module",
+ "rawset",
+ "warning",
+ "math",
+ "debug",
+ "pcall",
+ "table",
+ "newproxy",
+ "type",
+ "coroutine",
+ "select",
+ "gcinfo",
+ "pairs",
+ "rawget",
+ "loadstring",
+ "ipairs",
+ "_VERSION",
+ "setfenv",
+ "load",
+ "error",
+ "loadfile",
+
+ "pairs_next",
+ "ipairs_next",
+ "pluto",
+ "Cfg",
+ "Translator",
+ "Persistence",
+ "CommandLine",
+ 0
+ };
+
+ // -------------------------------------------------------------------------
+
+ enum PERMANENT_TABLE_TYPE
+ {
+ PTT_PERSIST,
+ PTT_UNPERSIST,
+ };
+
+ // -------------------------------------------------------------------------
+
+ bool PushPermanentsTable(lua_State * L, PERMANENT_TABLE_TYPE TableType)
+ {
+ // Permanents-Tabelle erstellen.
+ lua_newtable(L);
+
+ // Alle Standard-Permanents in die Tabelle einfügen.
+ unsigned int Index = 0;
+ while (STANDARD_PERMANENTS[Index])
+ {
+ // Permanent auf den Stack legen, falls es nicht existiert, wird es einfach ignoriert.
+ lua_getglobal(L, STANDARD_PERMANENTS[Index]);
+ if (!lua_isnil(L, -1))
+ {
+ // Namen des Elementes als einzigartigen Wert auf den Stack legen.
+ lua_pushstring(L, STANDARD_PERMANENTS[Index]);
+
+ // Falls geladen wird, ist der Name der Index und das Objekt der Wert.
+ // In diesem Fall müssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ // Eintrag in der Tabelle vornehmen.
+ lua_settable(L, -3);
+ }
+ else
+ {
+ // nil von Stack poppen.
+ lua_pop(L, 1);
+ }
+
+ ++Index;
+ }
+
+ // Alle Registrierten C-Funktionen in die Tabelle einfügen.
+ // BS_LuaBindhelper legt in der Registry eine Tabelle an, in der alle registrierten C-Funktionen gespeichert werden.
+
+ // Tabelle mit den C-Permanents auf den Stack legen.
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+
+ if (!lua_isnil(L, -1))
+ {
+ // Über alle Elemente der Tabelle iterieren.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Wert und Index auf dem Stack duplizieren und in der Reihenfolge vertauschen.
+ lua_pushvalue(L, -1);
+ lua_pushvalue(L, -3);
+
+ // Falls geladen wird, ist der Name der Index und das Objekt der Wert.
+ // In diesem Fall müssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ // Eintrag in der Ergebnistabelle vornehmen.
+ lua_settable(L, -6);
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+
+ // Tabelle mit den C-Permanents vom Stack poppen.
+ lua_pop(L, 1);
+
+ // coroutine.yield muss extra in die Permanents-Tabelle eingetragen werden, da inaktive Coroutinen diese C-Funktion auf dem Stack liegen
+ // haben.
+
+ // Funktion coroutine.yield auf den Stack legen.
+ lua_getglobal(L, "coroutine");
+ lua_pushstring(L, "yield");
+ lua_gettable(L, -2);
+
+ // coroutine.yield mit eigenem eindeutigen Wert in der Permanents-Tabelle ablegen.
+ lua_pushstring(L, "coroutine.yield");
+
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ lua_settable(L, -4);
+
+ // Tabelle coroutine vom Stack poppen.
+ lua_pop(L, 1);
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ int Chunkwriter(lua_State *L, const void* p, size_t sz, void* ud)
+ {
+ vector<unsigned char> & chunkData = *reinterpret_cast<vector<unsigned char> * >(ud);
+ const unsigned char * buffer = reinterpret_cast<const unsigned char *>(p);
+
+ while (sz--) chunkData.push_back(*buffer++);
+
+ return 1;
+ }
+}
+
+bool BS_LuaScriptEngine::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ // Den Lua-Stack leeren. pluto_persist() erwartet, dass der Stack bis aus seine Parameter leer ist.
+ lua_settop(m_State, 0);
+
+ // Garbage Collection erzwingen.
+ lua_gc(m_State, LUA_GCCOLLECT, 0);
+
+ // Permanents-Tabelle und die zu persistierende Tabelle auf den Stack legen.
+ // pluto_persist erwartet diese beiden Objekte auf dem Lua-Stack.
+ PushPermanentsTable(m_State, PTT_PERSIST);
+ lua_getglobal(m_State, "_G");
+
+ // Lua persistieren und die Daten in einem vector ablegen.
+ vector<unsigned char> chunkData;
+ pluto_persist(m_State, Chunkwriter, &chunkData);
+
+ // Persistenzdaten in den Writer schreiben.
+ Writer.Write(&chunkData[0], chunkData.size());
+
+ // Die beiden Tabellen vom Stack nehmen.
+ lua_pop(m_State, 2);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+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)
+ {
+ // Über alle Elemente der globalen Tabelle iterieren.
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Jetzt liegen der Wert und der Index des aktuellen Elementes auf dem Stack.
+ // Der Wert interessiert uns nicht, daher wird er vom Stack gepoppt.
+ lua_pop(L, 1);
+
+ // Feststellen, ob das Element auf nil gesetzt , also aus der globalen Tabelle entfernt werden soll.
+ // Hierfür wird geprüft, ob das Elementname ein String ist und in der Liste der Ausnahmen vorkommt.
+ bool SetElementToNil = true;
+ if (lua_isstring(L, -1))
+ {
+ const char * IndexString = lua_tostring(L, -1);
+ const char ** ExceptionsWalker = Exceptions;
+ while (*ExceptionsWalker)
+ {
+ if (strcmp(IndexString, *ExceptionsWalker) == 0) SetElementToNil = false;
+ ++ExceptionsWalker;
+ }
+ }
+
+ // Wenn der obige Test ergeben hat, dass das Element entfernt werden soll, wird es entfernt indem der Wert auf nil gesetzt wird.
+ if (SetElementToNil)
+ {
+ lua_pushvalue(L, -1);
+ lua_pushnil(L);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+
+ // Globale Tabelle vom Stack nehmen.
+ lua_pop(L, 1);
+
+ // Garbage-Collection vornehmen, damit die entfernten Elemente alle gelöscht werden.
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ // Den Lua-Stack leeren. pluto_unpersist() erwartet, dass der Stack bis aus seine Parameter leer ist.
+ lua_settop(m_State, 0);
+
+ // Permanents Tabelle auf den Stack legen. Dies passiert schon an dieser Stelle, da zum Erstellen der Tabelle alle Permanents zugreifbar sein
+ // müssen. Dies ist nur am Anfang dieser Funktion der Fall, im Folgenden wird die globale Tabelle geleert.
+ PushPermanentsTable(m_State, PTT_UNPERSIST);
+
+ // Alle Elemente aus der globalen Tabelle mit Ausnhame von _G und __METATABLES entfernen.
+ // Danach wird eine Garbage Collection durchgeführt und somit alle von Lua verwalteten Objekte gelöscht.
+ // __METATABLES wird zunächst nicht entfernt, da die Metatables für die Finalizer der Objekte benötigt werden.
+ static const char * ClearExceptionsFirstPass[] =
+ {
+ "_G",
+ "__METATABLES",
+ 0
+ };
+ ClearGlobalTable(m_State, ClearExceptionsFirstPass);
+
+ // Im zweiten Durchgang werden auch die Metatables entfernt.
+ static const char * ClearExceptionsSecondPass[] =
+ {
+ "_G",
+ 0
+ };
+ ClearGlobalTable(m_State, ClearExceptionsSecondPass);
+
+ // Persistierte Lua-Daten einlesen.
+ vector<unsigned char> chunkData;
+ Reader.Read(chunkData);
+
+ // Chunk-Reader initialisieren. Er wird von pluto_unpersist benutzt um die benötigten Daten einzulesen.
+ ChunkreaderData cd;
+ cd.BufferPtr = &chunkData[0];
+ cd.Size = chunkData.size();
+ cd.BufferReturned = false;
+
+ pluto_unpersist(m_State, Chunkreader, &cd);
+
+ // Permanents-Tabelle vom Stack nehmen.
+ lua_remove(m_State, -2);
+
+ // Die eingelesenen Elemente in die globale Tabelle eintragen.
+ lua_pushnil(m_State);
+ while (lua_next(m_State, -2) != 0)
+ {
+ // Die Referenz auf die globale Tabelle (_G) darf nicht überschrieben werden, sonst tickt Lua total aus.
+ bool IsGlobalReference = lua_isstring(m_State, -2) && strcmp(lua_tostring(m_State, -2), "_G") == 0;
+ if (!IsGlobalReference)
+ {
+ lua_pushvalue(m_State, -2);
+ lua_pushvalue(m_State, -2);
+
+ lua_settable(m_State, LUA_GLOBALSINDEX);
+ }
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(m_State, 1);
+ }
+
+ // Tabelle mit den geladenen Daten vom Stack poppen.
+ lua_pop(m_State, 1);
+
+ // Garbage Collection erzwingen.
+ lua_gc(m_State, LUA_GCCOLLECT, 0);
+
+ return true;
+}
diff --git a/engines/sword25/script/luascript.h b/engines/sword25/script/luascript.h
new file mode 100755
index 0000000000..dd0500a710
--- /dev/null
+++ b/engines/sword25/script/luascript.h
@@ -0,0 +1,102 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef LUASCRIPT_H
+#define LUASCRIPT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "script.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+struct lua_State;
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_LuaScriptEngine : public BS_ScriptEngine
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_LuaScriptEngine(BS_Kernel * KernelPtr);
+ virtual ~BS_LuaScriptEngine();
+
+ /**
+ @brief Initialisiert die Scriptengine.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool Init();
+
+ /**
+ @brief Lädt eine Skriptdatei und führt diese aus.
+ @param FileName der Dateiname der Skriptdatei
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteFile(const std::string & FileName);
+
+ /**
+ @brief Führt einen String mit Skriptcode aus.
+ @param Code ein String der Skriptcode enthält.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteString(const std::string & Code);
+
+ /**
+ @brief Gibt einen Pointer auf das Hauptobjekt der Skriptsprache zurück.
+ @remark Durch die Benutzung dieser Methode wird die Kapselung der Sprache aufgehoben.
+ */
+ virtual void * GetScriptObject() { return m_State; }
+
+ /**
+ @brief Macht die Kommandozeilen-Parameter für die Skriptumgebung zugänglich.
+ @param CommandLineParameters ein string vector der alle Kommandozeilenparameter enthält.
+ @remark Auf welche Weise die Kommandozeilen-Parameter durch Skripte zugegriffen werden können hängt von der jeweiligen Implementierung ab.
+ */
+ virtual void SetCommandLine(const std::vector<std::string> & CommandLineParameters);
+
+ /**
+ @remark Der Lua-Stack wird durch diese Methode geleert.
+ */
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ /**
+ @remark Der Lua-Stack wird durch diese Methode geleert.
+ */
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ lua_State * m_State;
+ int m_PcallErrorhandlerRegistryIndex;
+
+ bool RegisterStandardLibs();
+ bool RegisterStandardLibExtensions();
+ bool ExecuteBuffer(const char * Data, unsigned int Size, const std::string & Name) const;
+};
+
+#endif
diff --git a/engines/sword25/script/script.h b/engines/sword25/script/script.h
new file mode 100755
index 0000000000..7fafac0f96
--- /dev/null
+++ b/engines/sword25/script/script.h
@@ -0,0 +1,99 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 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.
+//
+// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/service.h"
+#include "kernel/persistable.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include <string>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_OutputPersistenceBlock;
+class BS_InputPersistenceBlock;
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_ScriptEngine : public BS_Service, public BS_Persistable
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_ScriptEngine(BS_Kernel * KernelPtr) : BS_Service(KernelPtr) {};
+ virtual ~BS_ScriptEngine() {};
+
+ // -----------------------------------------------------------------------------
+ // DIESE METHODEN MÜSSEN VON DER SCRIPTENGINE IMPLEMENTIERT WERDEN
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Initialisiert die Scriptengine.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool Init() = 0;
+
+ /**
+ @brief Lädt eine Skriptdatei und führt diese aus.
+ @param FileName der Dateiname der Skriptdatei
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteFile(const std::string & FileName) = 0;
+
+ /**
+ @brief Führt einen String mit Skriptcode aus.
+ @param Code ein String der Skriptcode enthält.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteString(const std::string & Code) = 0;
+
+ /**
+ @brief Gibt einen Pointer auf das Hauptobjekt der Skriptsprache zurück.
+ @remark Durch die Benutzung dieser Methode wird die Kapselung der Sprache vom Rest der Engine aufgehoben.
+ */
+ virtual void * GetScriptObject() = 0;
+
+ /**
+ @brief Macht die Kommandozeilen-Parameter für die Skriptumgebung zugänglich.
+ @param CommandLineParameters ein string vector der alle Kommandozeilenparameter enthält.
+ @remark Auf welche Weise die Kommandozeilen-Parameter durch Skripte zugegriffen werden können hängt von der jeweiligen Implementierung ab.
+ */
+ virtual void SetCommandLine(const std::vector<std::string> & CommandLineParameters) = 0;
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer) = 0;
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader) = 0;
+};
+
+#endif