diff options
author | Eugene Sandulenko | 2010-07-29 19:53:02 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2010-10-12 21:38:20 +0000 |
commit | a683a420a9e43705c972b5e74d55e319729e1a81 (patch) | |
tree | bde6e4abd417bdfaec120aa951da9a19be36b654 /engines/sword25/script | |
parent | 7723d91c957d07205c51be32498d45cd0a78568f (diff) | |
download | scummvm-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-x | engines/sword25/script/lua_extensions.cpp | 68 | ||||
-rwxr-xr-x | engines/sword25/script/luabindhelper.cpp | 419 | ||||
-rwxr-xr-x | engines/sword25/script/luabindhelper.h | 107 | ||||
-rwxr-xr-x | engines/sword25/script/luacallback.cpp | 203 | ||||
-rwxr-xr-x | engines/sword25/script/luacallback.h | 64 | ||||
-rwxr-xr-x | engines/sword25/script/luascript.cpp | 607 | ||||
-rwxr-xr-x | engines/sword25/script/luascript.h | 102 | ||||
-rwxr-xr-x | engines/sword25/script/script.h | 99 |
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 |