Initial commit
This commit is contained in:
70
engines/sword25/script/lua_extensions.cpp
Normal file
70
engines/sword25/script/lua_extensions.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/script/luascript.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
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);
|
||||
lua_pop(L, 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_reg GLOBAL_FUNCTIONS[] = {
|
||||
{"warning", warning},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
bool LuaScriptEngine::registerStandardLibExtensions() {
|
||||
lua_State *L = _state;
|
||||
assert(_state);
|
||||
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, "", GLOBAL_FUNCTIONS))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
442
engines/sword25/script/luabindhelper.cpp
Normal file
442
engines/sword25/script/luabindhelper.cpp
Normal file
@@ -0,0 +1,442 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
#include "sword25/script/luascript.h"
|
||||
|
||||
namespace {
|
||||
const char *METATABLES_TABLE_NAME = "__METATABLES";
|
||||
const char *PERMANENTS_TABLE_NAME = "Permanents";
|
||||
|
||||
bool registerPermanent(lua_State *L, const Common::String &name) {
|
||||
// A C function has to be on the stack
|
||||
if (!lua_iscfunction(L, -1))
|
||||
return false;
|
||||
|
||||
// Make sure that the Permanents-Table is on top of the stack
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
|
||||
if (lua_isnil(L, -1)) {
|
||||
// Permanents-Table does not yet exist, so it has to be created
|
||||
|
||||
// Pop nil from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Create Permanents-Table and insert a second reference to it on the stack
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
|
||||
// Store the Permanents-Table in the registry. The second reference is left
|
||||
// on the stack to be used in the connection
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
|
||||
}
|
||||
|
||||
// C function with the name of an index in the Permanents-Table
|
||||
lua_insert(L, -2);
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
|
||||
// Remove the Permanents-Table from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
* Registers a set of functions into a Lua library.
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param LibName The name of the library.
|
||||
* If this is an empty string, the functions will be added to the global namespace.
|
||||
* @param Functions An array of function pointers along with their names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool LuaBindhelper::addFunctionsToLib(lua_State *L, const Common::String &libName, const luaL_reg *functions) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// If the table name is empty, the functions are to be added to the global namespace
|
||||
if (libName.size() == 0) {
|
||||
for (; functions->name; ++functions) {
|
||||
lua_pushstring(L, functions->name);
|
||||
lua_pushcclosure(L, functions->func, 0);
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
|
||||
// Function is being permanently registed, so persistence can be ignored
|
||||
lua_pushstring(L, functions->name);
|
||||
lua_gettable(L, LUA_GLOBALSINDEX);
|
||||
registerPermanent(L, functions->name);
|
||||
}
|
||||
} else { // If the table name is not empty, the functions are added to the given table
|
||||
// Ensure that the library table exists
|
||||
if (!createTable(L, libName)) return false;
|
||||
|
||||
// Register each function into the table
|
||||
for (; functions->name; ++functions) {
|
||||
// Function registration
|
||||
lua_pushstring(L, functions->name);
|
||||
lua_pushcclosure(L, functions->func, 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Function is being permanently registed, so persistence can be ignored
|
||||
lua_pushstring(L, functions->name);
|
||||
lua_gettable(L, -2);
|
||||
registerPermanent(L, libName + "." + functions->name);
|
||||
}
|
||||
|
||||
// Remove the library table from the Lua stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of constants to the Lua library
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param LibName The name of the library.
|
||||
* If this is an empty string, the functions will be added to the global namespace.
|
||||
* @param Constants An array of the constant values along with their names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool LuaBindhelper::addConstantsToLib(lua_State *L, const Common::String &libName, const lua_constant_reg *constants) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// If the table is empty, the constants are added to the global namespace
|
||||
if (libName.size() == 0) {
|
||||
for (; constants->Name; ++constants) {
|
||||
lua_pushstring(L, constants->Name);
|
||||
lua_pushnumber(L, constants->Value);
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
}
|
||||
}
|
||||
// If the table name is not empty, the constants are added to that table
|
||||
else {
|
||||
// Ensure that the library table exists
|
||||
if (!createTable(L, libName)) return false;
|
||||
|
||||
// Register each constant in the table
|
||||
for (; constants->Name; ++constants) {
|
||||
lua_pushstring(L, constants->Name);
|
||||
lua_pushnumber(L, constants->Value);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
// Remove the library table from the Lua stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of methods to a Lua class
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param ClassName The name of the class
|
||||
* When the class name specified does not exist, it is created.
|
||||
* @param Methods An array of function pointers along with their method names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool LuaBindhelper::addMethodsToClass(lua_State *L, const Common::String &className, const luaL_reg *methods) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Load the metatable onto the Lua stack
|
||||
if (!getMetatable(L, className)) return false;
|
||||
|
||||
// Register each method in the Metatable
|
||||
for (; methods->name; ++methods) {
|
||||
lua_pushstring(L, methods->name);
|
||||
lua_pushcclosure(L, methods->func, 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Function is being permanently registed, so persistence can be ignored
|
||||
lua_pushstring(L, methods->name);
|
||||
lua_gettable(L, -2);
|
||||
registerPermanent(L, className + "." + methods->name);
|
||||
}
|
||||
|
||||
// Remove the metatable from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the garbage collector callback method when items of a particular class are deleted
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param ClassName The name of the class
|
||||
* When the class name specified does not exist, it is created.
|
||||
* @param GCHandler A function pointer
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool LuaBindhelper::setClassGCHandler(lua_State *L, const Common::String &className, lua_CFunction GCHandler) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Load the metatable onto the Lua stack
|
||||
if (!getMetatable(L, className)) return false;
|
||||
|
||||
// Add the GC handler to the Metatable
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_pushcclosure(L, GCHandler, 0);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Function is being permanently registed, so persistence can be ignored
|
||||
lua_pushstring(L, "__gc");
|
||||
lua_gettable(L, -2);
|
||||
registerPermanent(L, className + ".__gc");
|
||||
|
||||
// Remove the metatable from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
namespace {
|
||||
void pushMetatableTable(lua_State *L) {
|
||||
// Push the Metatable table onto the stack
|
||||
lua_getglobal(L, METATABLES_TABLE_NAME);
|
||||
|
||||
// If the table doesn't yet exist, it must be created
|
||||
if (lua_isnil(L, -1)) {
|
||||
// Pop nil from stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// New table has been created, so add it to the global table and leave reference on stack
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, METATABLES_TABLE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool LuaBindhelper::getMetatable(lua_State *L, const Common::String &tableName) {
|
||||
// Push the Metatable table onto the stack
|
||||
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)) {
|
||||
// Pop nil from stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Create new table
|
||||
lua_newtable(L);
|
||||
|
||||
// Set the __index field in the table
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
// Flag the table as persisted. This ensures that objects within this table get stored
|
||||
lua_pushbooleancpp(L, true);
|
||||
lua_setfield(L, -2, "__persist");
|
||||
|
||||
// Set the table name and push it onto the stack
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, tableName.c_str());
|
||||
}
|
||||
|
||||
// Remove the Metatable table from the stack
|
||||
lua_remove(L, -2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Like luaL_checkudata, only without that no error is generated.
|
||||
void *LuaBindhelper::my_checkudata(lua_State *L, int ud, const char *tname) {
|
||||
int top = lua_gettop(L);
|
||||
|
||||
void *p = lua_touserdata(L, ud);
|
||||
if (p != NULL) { /* value is a userdata? */
|
||||
if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
|
||||
// lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
|
||||
LuaBindhelper::getMetatable(L, tname);
|
||||
if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
|
||||
lua_settop(L, top);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, top);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool LuaBindhelper::createTable(lua_State *L, const Common::String &tableName) {
|
||||
const char *partBegin = tableName.c_str();
|
||||
|
||||
while (partBegin - tableName.c_str() < (int)tableName.size()) {
|
||||
const char *partEnd = strchr(partBegin, '.');
|
||||
if (!partEnd)
|
||||
partEnd = partBegin + strlen(partBegin);
|
||||
Common::String subTableName(partBegin, partEnd);
|
||||
|
||||
// Tables with an empty string as the name are not allowed
|
||||
if (subTableName.size() == 0)
|
||||
return false;
|
||||
|
||||
// Verify that the table with the name already exists
|
||||
// The first round will be searched in the global namespace, with later passages
|
||||
// in the corresponding parent table in the stack
|
||||
if (partBegin == tableName.c_str()) {
|
||||
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);
|
||||
}
|
||||
|
||||
// If it doesn't exist, create table
|
||||
if (lua_isnil(L, -1)) {
|
||||
// Pop nil from stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Create new table
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, subTableName.c_str());
|
||||
lua_pushvalue(L, -2);
|
||||
if (partBegin == tableName.c_str())
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
else {
|
||||
lua_settable(L, -4);
|
||||
lua_remove(L, -2);
|
||||
}
|
||||
}
|
||||
|
||||
partBegin = partEnd + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
namespace {
|
||||
Common::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;
|
||||
}
|
||||
|
||||
Common::String result(lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Common::String LuaBindhelper::stackDump(lua_State *L) {
|
||||
Common::String oss;
|
||||
|
||||
int i = lua_gettop(L);
|
||||
oss += "------------------- Stack Dump -------------------\n";
|
||||
|
||||
while (i) {
|
||||
oss += Common::String::format("%d: ", i) + getLuaValueInfo(L, i) + "\n";
|
||||
i--;
|
||||
}
|
||||
|
||||
oss += "-------------- Stack Dump Finished ---------------\n";
|
||||
|
||||
return oss;
|
||||
}
|
||||
|
||||
Common::String LuaBindhelper::tableDump(lua_State *L) {
|
||||
Common::String oss;
|
||||
|
||||
oss += "------------------- Table Dump -------------------\n";
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
// Get the value of the current element on top of the stack, including the index
|
||||
oss += getLuaValueInfo(L, -2) + " : " + getLuaValueInfo(L, -1) + "\n";
|
||||
|
||||
// Pop value from the stack. The index is then ready for the next call to lua_next()
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
oss += "-------------- Table Dump Finished ---------------\n";
|
||||
|
||||
return oss;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
117
engines/sword25/script/luabindhelper.h
Normal file
117
engines/sword25/script/luabindhelper.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_LUABINDHELPER_H
|
||||
#define SWORD25_LUABINDHELPER_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
#include "common/lua/lua.h"
|
||||
#include "common/lua/lauxlib.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#define lua_pushbooleancpp(L, b) (lua_pushboolean(L, b ? 1 : 0))
|
||||
#define lua_tobooleancpp(L, i) (lua_toboolean(L, i) != 0)
|
||||
|
||||
struct lua_constant_reg {
|
||||
const char *Name;
|
||||
lua_Number Value;
|
||||
};
|
||||
|
||||
class LuaBindhelper {
|
||||
public:
|
||||
/**
|
||||
* Registers a set of functions into a Lua library.
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param LibName The name of the library.
|
||||
* If this is an empty string, the functions will be added to the global namespace.
|
||||
* @param Functions An array of function pointers along with their names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
static bool addFunctionsToLib(lua_State *L, const Common::String &libName, const luaL_reg *functions);
|
||||
|
||||
/**
|
||||
* Adds a set of constants to the Lua library
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param LibName The name of the library.
|
||||
* If this is an empty string, the functions will be added to the global namespace.
|
||||
* @param Constants An array of the constant values along with their names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
static bool addConstantsToLib(lua_State *L, const Common::String &libName, const lua_constant_reg *constants);
|
||||
|
||||
/**
|
||||
* Adds a set of methods to a Lua class
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param ClassName The name of the class
|
||||
* When the class name specified does not exist, it is created.
|
||||
* @param Methods An array of function pointers along with their method names.
|
||||
* The array must be terminated with the enry (0, 0)
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
static bool addMethodsToClass(lua_State *L, const Common::String &className, const luaL_reg *methods);
|
||||
|
||||
/**
|
||||
* Sets the garbage collector callback method when items of a particular class are deleted
|
||||
* @param L A pointer to the Lua VM
|
||||
* @param ClassName The name of the class
|
||||
* When the class name specified does not exist, it is created.
|
||||
* @param GCHandler A function pointer
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
static bool setClassGCHandler(lua_State *L, const Common::String &className, lua_CFunction GCHandler);
|
||||
|
||||
/**
|
||||
* Returns a string containing a stack dump of the Lua stack
|
||||
* @param L A pointer to the Lua VM
|
||||
*/
|
||||
static Common::String stackDump(lua_State *L);
|
||||
|
||||
/**
|
||||
* Returns a string that describes the contents of a table
|
||||
* @param L A pointer to the Lua VM
|
||||
* @remark The table must be on the Lua stack to be read out.
|
||||
*/
|
||||
static Common::String tableDump(lua_State *L);
|
||||
|
||||
static bool getMetatable(lua_State *L, const Common::String &tableName);
|
||||
|
||||
static void *my_checkudata(lua_State *L, int ud, const char *tname);
|
||||
|
||||
private:
|
||||
static bool createTable(lua_State *L, const Common::String &tableName);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
169
engines/sword25/script/luacallback.cpp
Normal file
169
engines/sword25/script/luacallback.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "sword25/script/luacallback.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
#include "common/lua/lua.h"
|
||||
#include "common/lua/lauxlib.h"
|
||||
|
||||
const char *CALLBACKTABLE_NAME = "__CALLBACKS";
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
LuaCallback::LuaCallback(lua_State *L) {
|
||||
// Create callback table
|
||||
lua_newtable(L);
|
||||
lua_setglobal(L, CALLBACKTABLE_NAME);
|
||||
}
|
||||
|
||||
LuaCallback::~LuaCallback() {
|
||||
}
|
||||
|
||||
void LuaCallback::registerCallbackFunction(lua_State *L, uint objectHandle) {
|
||||
assert(lua_isfunction(L, -1));
|
||||
ensureObjectCallbackTableExists(L, objectHandle);
|
||||
|
||||
// Store function in the callback object table store
|
||||
lua_pushvalue(L, -2);
|
||||
luaL_ref(L, -2);
|
||||
|
||||
// Pop the function and object callback table from the stack
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
void LuaCallback::unregisterCallbackFunction(lua_State *L, uint objectHandle) {
|
||||
assert(lua_isfunction(L, -1));
|
||||
ensureObjectCallbackTableExists(L, objectHandle);
|
||||
|
||||
// Iterate over all elements of the object callback table and remove the function from it
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
// The value of the current element is the top of the stack, including the index
|
||||
|
||||
// If the value is identical to the function parameters, it is removed from the table
|
||||
if (lua_equal(L, -1, -4)) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushnil(L);
|
||||
lua_settable(L, -5);
|
||||
|
||||
// The function was found, iteration can be stopped
|
||||
lua_pop(L, 2);
|
||||
break;
|
||||
} else {
|
||||
// Pop value from the stack. The index is then ready for the next call to lua_next()
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Function and object table are popped from the stack
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
void LuaCallback::removeAllObjectCallbacks(lua_State *L, uint objectHandle) {
|
||||
pushCallbackTable(L);
|
||||
|
||||
// Remove the object callback from the callback table
|
||||
lua_pushnumber(L, objectHandle);
|
||||
lua_pushnil(L);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void LuaCallback::invokeCallbackFunctions(lua_State *L, uint objectHandle) {
|
||||
ensureObjectCallbackTableExists(L, objectHandle);
|
||||
|
||||
// Iterate through the table and perform all the callbacks
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
// The value of the current element is at the top of the stack, including the index
|
||||
|
||||
// If the value is a function, execute it
|
||||
if (lua_type(L, -1) == LUA_TFUNCTION) {
|
||||
// Pre-Function Call
|
||||
// Derived classes can function in this parameter onto the stack.
|
||||
// The return value indicates the number of parameters
|
||||
int argumentCount = preFunctionInvocation(L);
|
||||
|
||||
// Lua_pcall the function and the parameters pop themselves from the stack
|
||||
if (lua_pcall(L, argumentCount, 0, 0) != 0) {
|
||||
// An error has occurred
|
||||
error("An error occurred executing a callback function: %s", lua_tostring(L, -1));
|
||||
|
||||
// Pop error message from the stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
} else {
|
||||
// Pop value from the stack. The index is then ready for the next call to lua_next()
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LuaCallback::ensureObjectCallbackTableExists(lua_State *L, uint objectHandle) {
|
||||
pushObjectCallbackTable(L, objectHandle);
|
||||
|
||||
// If the table is nil, it must first be created
|
||||
if (lua_isnil(L, -1)) {
|
||||
// Pop nil from stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
pushCallbackTable(L);
|
||||
|
||||
// Create the table, and put the objectHandle into it
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, objectHandle);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -4);
|
||||
|
||||
// Pop the callback table from the stack
|
||||
lua_remove(L, -2);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaCallback::pushCallbackTable(lua_State *L) {
|
||||
lua_getglobal(L, CALLBACKTABLE_NAME);
|
||||
}
|
||||
|
||||
void LuaCallback::pushObjectCallbackTable(lua_State *L, uint objectHandle) {
|
||||
pushCallbackTable(L);
|
||||
|
||||
// Push Object Callback table onto the stack
|
||||
lua_pushnumber(L, objectHandle);
|
||||
lua_gettable(L, -2);
|
||||
|
||||
// Pop the callback table from the stack
|
||||
lua_remove(L, -2);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
68
engines/sword25/script/luacallback.h
Normal file
68
engines/sword25/script/luacallback.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_LUACALLBACK_H
|
||||
#define SWORD25_LUACALLBACK_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class LuaCallback {
|
||||
public:
|
||||
LuaCallback(lua_State *L);
|
||||
virtual ~LuaCallback();
|
||||
|
||||
// Funktion muss auf dem Lua-Stack liegen.
|
||||
void registerCallbackFunction(lua_State *L, uint objectHandle);
|
||||
|
||||
// Funktion muss auf dem Lua-Stack liegen.
|
||||
void unregisterCallbackFunction(lua_State *L, uint objectHandle);
|
||||
|
||||
void removeAllObjectCallbacks(lua_State *L, uint objectHandle);
|
||||
|
||||
void invokeCallbackFunctions(lua_State *L, uint objectHandle);
|
||||
|
||||
protected:
|
||||
virtual int preFunctionInvocation(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void ensureObjectCallbackTableExists(lua_State *L, uint objectHandle);
|
||||
void pushCallbackTable(lua_State *L);
|
||||
void pushObjectCallbackTable(lua_State *L, uint objectHandle);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
514
engines/sword25/script/luascript.cpp
Normal file
514
engines/sword25/script/luascript.cpp
Normal file
@@ -0,0 +1,514 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/debug-channels.h"
|
||||
|
||||
#include "sword25/sword25.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/script/luascript.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
#include "common/lua/lua.h"
|
||||
#include "common/lua/lualib.h"
|
||||
#include "common/lua/lauxlib.h"
|
||||
#include "common/lua/lua_persistence.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
LuaScriptEngine::LuaScriptEngine(Kernel *KernelPtr) :
|
||||
ScriptEngine(KernelPtr),
|
||||
_state(0),
|
||||
_pcallErrorhandlerRegistryIndex(0) {
|
||||
}
|
||||
|
||||
LuaScriptEngine::~LuaScriptEngine() {
|
||||
// Lua de-initialisation
|
||||
if (_state)
|
||||
lua_close(_state);
|
||||
}
|
||||
|
||||
namespace {
|
||||
int panicCB(lua_State *L) {
|
||||
error("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void debugHook(lua_State *L, lua_Debug *ar) {
|
||||
if (!lua_getinfo(L, "Sn", ar))
|
||||
return;
|
||||
|
||||
debug("LUA: %s %s: %s %d", ar->namewhat, ar->name, ar->short_src, ar->currentline);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaScriptEngine::init() {
|
||||
// Lua-State initialisation, as well as standard libaries initialisation
|
||||
_state = luaL_newstate();
|
||||
if (!_state || ! registerStandardLibs() || !registerStandardLibExtensions()) {
|
||||
error("Lua could not be initialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register panic callback function
|
||||
lua_atpanic(_state, panicCB);
|
||||
|
||||
// Error handler for lua_pcall calls
|
||||
// The code below contains a local error handler function
|
||||
const char errorHandlerCode[] =
|
||||
"local function ErrorHandler(message) "
|
||||
" return message .. '\\n' .. debug.traceback('', 2) "
|
||||
"end "
|
||||
"return ErrorHandler";
|
||||
|
||||
// Compile the code
|
||||
if (luaL_loadbuffer(_state, errorHandlerCode, strlen(errorHandlerCode), "PCALL ERRORHANDLER") != 0) {
|
||||
// An error occurred, so dislay the reason and exit
|
||||
error("Couldn't compile luaL_pcall errorhandler:\n%s", lua_tostring(_state, -1));
|
||||
lua_pop(_state, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
// Running the code, the error handler function sets the top of the stack
|
||||
if (lua_pcall(_state, 0, 1, 0) != 0) {
|
||||
// An error occurred, so dislay the reason and exit
|
||||
error("Couldn't prepare luaL_pcall errorhandler:\n%s", lua_tostring(_state, -1));
|
||||
lua_pop(_state, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Place the error handler function in the Lua registry, and remember the index
|
||||
_pcallErrorhandlerRegistryIndex = luaL_ref(_state, LUA_REGISTRYINDEX);
|
||||
|
||||
// Initialize debugging callback
|
||||
if (DebugMan.isDebugChannelEnabled(kDebugScript)) {
|
||||
int mask = 0;
|
||||
if ((gDebugLevel & 1) != 0)
|
||||
mask |= LUA_MASKCALL;
|
||||
if ((gDebugLevel & 2) != 0)
|
||||
mask |= LUA_MASKRET;
|
||||
if ((gDebugLevel & 4) != 0)
|
||||
mask |= LUA_MASKLINE;
|
||||
|
||||
if (mask != 0)
|
||||
lua_sethook(_state, debugHook, mask, 0);
|
||||
}
|
||||
|
||||
debugC(kDebugScript, "Lua initialized.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LuaScriptEngine::executeFile(const Common::String &fileName) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(_state);
|
||||
#endif
|
||||
debug(2, "LuaScriptEngine::executeFile(%s)", fileName.c_str());
|
||||
|
||||
// Get a pointer to the package manager
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// File read
|
||||
uint fileSize;
|
||||
byte *fileData = pPackage->getFile(fileName, &fileSize);
|
||||
if (!fileData) {
|
||||
error("Couldn't read \"%s\".", fileName.c_str());
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(_state));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the file content
|
||||
if (!executeBuffer(fileData, fileSize, "@" + pPackage->getAbsolutePath(fileName))) {
|
||||
// Release file buffer
|
||||
delete[] fileData;
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(_state));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Release file buffer
|
||||
delete[] fileData;
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(_state));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LuaScriptEngine::executeString(const Common::String &code) {
|
||||
return executeBuffer((const byte *)code.c_str(), code.size(), "???");
|
||||
}
|
||||
|
||||
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 LuaScriptEngine::registerStandardLibs() {
|
||||
luaL_openlibs(_state);
|
||||
removeForbiddenFunctions(_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LuaScriptEngine::executeBuffer(const byte *data, uint size, const Common::String &name) const {
|
||||
// Compile buffer
|
||||
if (luaL_loadbuffer(_state, (const char *)data, size, name.c_str()) != 0) {
|
||||
error("Couldn't compile \"%s\":\n%s", name.c_str(), lua_tostring(_state, -1));
|
||||
lua_pop(_state, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Error handling function to be executed after the function is put on the stack
|
||||
lua_rawgeti(_state, LUA_REGISTRYINDEX, _pcallErrorhandlerRegistryIndex);
|
||||
lua_insert(_state, -2);
|
||||
|
||||
// Run buffer contents
|
||||
if (lua_pcall(_state, 0, 0, -2) != 0) {
|
||||
error("An error occurred while executing \"%s\":\n%s.",
|
||||
name.c_str(),
|
||||
lua_tostring(_state, -1));
|
||||
lua_pop(_state, 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the error handler function from the stack
|
||||
lua_pop(_state, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaScriptEngine::setCommandLine(const Common::StringArray &commandLineParameters) {
|
||||
lua_newtable(_state);
|
||||
|
||||
for (size_t i = 0; i < commandLineParameters.size(); ++i) {
|
||||
lua_pushnumber(_state, i + 1);
|
||||
lua_pushstring(_state, commandLineParameters[i].c_str());
|
||||
lua_settable(_state, -3);
|
||||
}
|
||||
|
||||
lua_setglobal(_state, "CommandLine");
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char *PERMANENTS_TABLE_NAME = "Permanents";
|
||||
|
||||
// This array contains the name of global Lua objects that should not be persisted
|
||||
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-Table
|
||||
lua_newtable(L);
|
||||
|
||||
// All standard permanents are inserted into this table
|
||||
uint Index = 0;
|
||||
while (STANDARD_PERMANENTS[Index]) {
|
||||
// Permanents are placed onto the stack; if it does not exist, it is simply ignored
|
||||
lua_getglobal(L, STANDARD_PERMANENTS[Index]);
|
||||
if (!lua_isnil(L, -1)) {
|
||||
// Name of the element as a unique value on the stack
|
||||
lua_pushstring(L, STANDARD_PERMANENTS[Index]);
|
||||
|
||||
// If it is loaded, then it can be used
|
||||
// In this case, the position of name and object are reversed on the stack
|
||||
if (tableType == PTT_UNPERSIST)
|
||||
lua_insert(L, -2);
|
||||
|
||||
// Make an entry in the table
|
||||
lua_settable(L, -3);
|
||||
} else {
|
||||
// Pop nil value from stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
++Index;
|
||||
}
|
||||
|
||||
// All registered C functions to be inserted into the table
|
||||
// BS_LuaBindhelper places in the register a table in which all registered C functions
|
||||
// are stored
|
||||
|
||||
// Table is put on the stack
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
|
||||
|
||||
if (!lua_isnil(L, -1)) {
|
||||
// Iterate over all elements of the table
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
// Value and index duplicated on the stack and changed in the sequence
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushvalue(L, -3);
|
||||
|
||||
// If it is loaded, then it can be used
|
||||
// In this case, the position of name and object are reversed on the stack
|
||||
if (tableType == PTT_UNPERSIST)
|
||||
lua_insert(L, -2);
|
||||
|
||||
// Make an entry in the results table
|
||||
lua_settable(L, -6);
|
||||
|
||||
// Pop value from the stack. The index is then ready for the next call to lua_next()
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Pop the C-Permanents table from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// coroutine.yield must be registered in the extra-Permanents table because they
|
||||
// are inactive coroutine C functions on the stack
|
||||
|
||||
// Function coroutine.yield placed on the stack
|
||||
lua_getglobal(L, "coroutine");
|
||||
lua_pushstring(L, "yield");
|
||||
lua_gettable(L, -2);
|
||||
|
||||
// Store coroutine.yield with it's own unique value in the Permanents table
|
||||
lua_pushstring(L, "coroutine.yield");
|
||||
|
||||
if (tableType == PTT_UNPERSIST)
|
||||
lua_insert(L, -2);
|
||||
|
||||
lua_settable(L, -4);
|
||||
|
||||
// Coroutine table is popped from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) {
|
||||
// Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters
|
||||
lua_settop(_state, 0);
|
||||
|
||||
// Garbage Collection erzwingen.
|
||||
lua_gc(_state, LUA_GCCOLLECT, 0);
|
||||
|
||||
// Permanents-Table is set on the stack
|
||||
// pluto_persist expects these two items on the Lua stack
|
||||
pushPermanentsTable(_state, PTT_PERSIST);
|
||||
lua_getglobal(_state, "_G");
|
||||
|
||||
// Lua persists and stores the data in a WriteStream
|
||||
Common::MemoryWriteStreamDynamic writeStream(DisposeAfterUse::YES);
|
||||
Lua::persistLua(_state, &writeStream);
|
||||
|
||||
// Persistenzdaten in den Writer schreiben.
|
||||
writer.write(writeStream.getData(), writeStream.size());
|
||||
|
||||
// Die beiden Tabellen vom Stack nehmen.
|
||||
lua_pop(_state, 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void clearGlobalTable(lua_State *L, const char **exceptions) {
|
||||
// Iterate over all elements of the global table
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
// Now the value and the index of the current element is on the stack
|
||||
// This value does not interest us, so it is popped from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Determine whether the item is set to nil, so you want to remove from the global table.
|
||||
// For this will determine whether the element name is a string and is present in
|
||||
// the list of exceptions
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// If the above test showed that the item should be removed, it is removed by setting the value to nil.
|
||||
if (setElementToNil) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushnil(L);
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
}
|
||||
}
|
||||
|
||||
// Pop the Global table from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Perform garbage collection, so that all removed elements are deleted
|
||||
lua_gc(L, LUA_GCCOLLECT, 0);
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) {
|
||||
// Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters
|
||||
lua_settop(_state, 0);
|
||||
|
||||
// Permanents table is placed on the stack. This has already happened at this point, because
|
||||
// to create the table all permanents must be accessible. This is the case only for the
|
||||
// beginning of the function, because the global table is emptied below
|
||||
pushPermanentsTable(_state, PTT_UNPERSIST);
|
||||
|
||||
// All items from global table of _G and __METATABLES are removed.
|
||||
// After a garbage collection is performed, and thus all managed objects deleted
|
||||
|
||||
// __METATABLES is not immediately removed becausen the Metatables are needed
|
||||
// for the finalisers of objects.
|
||||
static const char *clearExceptionsFirstPass[] = {
|
||||
"_G",
|
||||
"__METATABLES",
|
||||
0
|
||||
};
|
||||
clearGlobalTable(_state, clearExceptionsFirstPass);
|
||||
|
||||
// In the second pass, the Metatables are removed
|
||||
static const char *clearExceptionsSecondPass[] = {
|
||||
"_G",
|
||||
0
|
||||
};
|
||||
clearGlobalTable(_state, clearExceptionsSecondPass);
|
||||
|
||||
// Persisted Lua data
|
||||
Common::Array<byte> chunkData;
|
||||
reader.readByteArray(chunkData);
|
||||
Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO);
|
||||
|
||||
Lua::unpersistLua(_state, &readStream);
|
||||
|
||||
// Permanents-Table is removed from stack
|
||||
lua_remove(_state, -2);
|
||||
|
||||
// The read elements in the global table about
|
||||
lua_pushnil(_state);
|
||||
while (lua_next(_state, -2) != 0) {
|
||||
// The reference to the global table (_G) must not be overwritten, or ticks from Lua total
|
||||
bool isGlobalReference = lua_isstring(_state, -2) && strcmp(lua_tostring(_state, -2), "_G") == 0;
|
||||
if (!isGlobalReference) {
|
||||
lua_pushvalue(_state, -2);
|
||||
lua_pushvalue(_state, -2);
|
||||
|
||||
lua_settable(_state, LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
// Pop value from the stack. The index is then ready for the next call to lua_next()
|
||||
lua_pop(_state, 1);
|
||||
}
|
||||
|
||||
// The table with the loaded data is popped from the stack
|
||||
lua_pop(_state, 1);
|
||||
|
||||
// Force garbage collection
|
||||
lua_gc(_state, LUA_GCCOLLECT, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
106
engines/sword25/script/luascript.h
Normal file
106
engines/sword25/script/luascript.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_LUASCRIPT_H
|
||||
#define SWORD25_LUASCRIPT_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/str-array.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/script/script.h"
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
|
||||
class LuaScriptEngine : public ScriptEngine {
|
||||
public:
|
||||
LuaScriptEngine(Kernel *KernelPtr);
|
||||
~LuaScriptEngine() override;
|
||||
|
||||
/**
|
||||
* Initializes the scripting engine
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool init() override;
|
||||
|
||||
/**
|
||||
* Loads a script file and executes it
|
||||
* @param FileName The filename of the script
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool executeFile(const Common::String &fileName) override;
|
||||
|
||||
/**
|
||||
* Execute a string of script code
|
||||
* @param Code A string of script code
|
||||
* @return Returns true if successful, otherwise false.
|
||||
*/
|
||||
bool executeString(const Common::String &code) override;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the main object of the scripting language
|
||||
* @remark Using this method breaks the encapsulation of the language
|
||||
*/
|
||||
void *getScriptObject() override {
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the command line parameters for the scripting environment available
|
||||
* @param CommandLineParameters An array containing all the command line parameters
|
||||
* @remark How the command line parameters will be used by scripts is
|
||||
* dependant on the particular implementation.
|
||||
*/
|
||||
void setCommandLine(const Common::StringArray &commandLineParameters) override;
|
||||
|
||||
/**
|
||||
* @remark The Lua stack is cleared by this method
|
||||
*/
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
/**
|
||||
* @remark The Lua stack is cleared by this method
|
||||
*/
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
private:
|
||||
lua_State *_state;
|
||||
int _pcallErrorhandlerRegistryIndex;
|
||||
|
||||
bool registerStandardLibs();
|
||||
bool registerStandardLibExtensions();
|
||||
bool executeBuffer(const byte *data, uint size, const Common::String &name) const;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
92
engines/sword25/script/script.h
Normal file
92
engines/sword25/script/script.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_SCRIPT_H
|
||||
#define SWORD25_SCRIPT_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/str.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/service.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class OutputPersistenceBlock;
|
||||
class BS_InputPersistenceBlock;
|
||||
|
||||
class ScriptEngine : public Service, public Persistable {
|
||||
public:
|
||||
ScriptEngine(Kernel *KernelPtr) : Service(KernelPtr) {}
|
||||
~ScriptEngine() override {}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// This method must be implemented by the script engine
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initializes the scrip tengine. Returns true if successful, false otherwise.
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* Loads a script file and executes it.
|
||||
* @param FileName The script filename
|
||||
*/
|
||||
virtual bool executeFile(const Common::String &fileName) = 0;
|
||||
|
||||
/**
|
||||
* Executes a specified script fragment
|
||||
* @param Code String of script code
|
||||
*/
|
||||
virtual bool executeString(const Common::String &code) = 0;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the main object of the script engine
|
||||
* Note: Using this method breaks the encapsulation of the language from the rest of the engine.
|
||||
*/
|
||||
virtual void *getScriptObject() = 0;
|
||||
|
||||
/**
|
||||
* Makes the command line parameters for the script environment available
|
||||
* Note: How the command line parameters will be used by scripts is dependant on the
|
||||
* particular implementation.
|
||||
* @param CommandLineParameters List containing the command line parameters
|
||||
*/
|
||||
virtual void setCommandLine(const Common::Array<Common::String> &commandLineParameters) = 0;
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override = 0;
|
||||
bool unpersist(InputPersistenceBlock &reader) override = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user