/* 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 . * */ #include "common/debug.h" #include "common/textconsole.h" #include "common/lua/lua.h" #include "common/lua/lualib.h" #include "common/lua/lauxlib.h" #include "tetraedge/te/te_lua_context.h" namespace Tetraedge { //#define TETRAEDGE_LUA_DEBUG 1 //static lua_State *globalState = nullptr; static int luaPanicFunction(lua_State *state) { const char *msg = lua_tolstring(state, -1, nullptr); warning("Lua: %s", msg); lua_settop(state, -2); return 1; } #ifdef TETRAEDGE_LUA_DEBUG static void luaDebugHook(lua_State *L, lua_Debug *ar) { if (!lua_getinfo(L, "Sn", ar)) return; debug("LUA: %s %d", ar->source, ar->currentline); } #endif TeLuaContext::TeLuaContext() : _luaState(nullptr) { _luaState = lua_open(); luaL_openlibs(_luaState); lua_atpanic(_luaState, luaPanicFunction); } TeLuaContext::~TeLuaContext() { destroy(); } void TeLuaContext::addBindings(void(*fn)(lua_State *)) { fn(_luaState); } void TeLuaContext::create() { _luaState = lua_open(); luaL_openlibs(_luaState); lua_atpanic(_luaState, luaPanicFunction); #ifdef TETRAEDGE_LUA_DEBUG lua_sethook(_luaState, luaDebugHook, LUA_MASKCALL | LUA_MASKLINE, 0); #endif } void TeLuaContext::destroy() { if (_luaState) lua_close(_luaState); _luaState = nullptr; } TeVariant TeLuaContext::global(const Common::String &name) { lua_getglobal(_luaState, name.c_str()); TeVariant retval; int type = lua_type(_luaState, -1); if (type == LUA_TBOOLEAN) { int result = lua_toboolean(_luaState, -1); lua_settop(_luaState, -2); return TeVariant(result > 0); } else if (type == LUA_TNUMBER) { double result = lua_tonumber(_luaState, -1); lua_settop(_luaState, -2); return TeVariant(result); } else if (type == LUA_TSTRING) { const char *str = lua_tolstring(_luaState, -1, nullptr); lua_settop(_luaState, -2); return TeVariant(str); } if (type != LUA_TNIL) warning("TeLuaContext::global: Unexpected type %d for global %s", type, name.c_str()); else debug("TeLuaContext::global: Request for nil global %s", name.c_str()); return TeVariant(); } void TeLuaContext::setGlobal(const Common::String &name, int val) { lua_pushinteger(_luaState, val); lua_setglobal(_luaState, name.c_str()); } void TeLuaContext::setGlobal(const Common::String &name, bool val) { lua_pushboolean(_luaState, val); lua_setglobal(_luaState, name.c_str()); } void TeLuaContext::setGlobal(const Common::String &name, const Common::String &val) { lua_pushstring(_luaState, val.c_str()); lua_setglobal(_luaState, name.c_str()); } void TeLuaContext::removeGlobal(const Common::String &name) { lua_pushnil(_luaState); lua_setglobal(_luaState, name.c_str()); } void TeLuaContext::registerCFunction(const Common::String &name, int(*fn)(lua_State *)) { lua_pushcclosure(_luaState, fn, 0); lua_setglobal(_luaState, name.c_str()); } void TeLuaContext::setInRegistry(const Common::String &name, TeLuaGUI *gui) { lua_pushstring(_luaState, name.c_str()); lua_pushlightuserdata(_luaState, gui); lua_settable(_luaState, LUA_REGISTRYINDEX); } // Types for save file. Aligned with the Lua types at type of // writing, but don't save them just in case they could change. enum TeLuaSaveVarType { None = 0, Boolean = 1, Number = 3, String = 4 }; //#define TETRAEDGE_LUA_DEBUG_SAVELOAD Common::Error TeLuaContext::syncState(Common::Serializer &s) { // Save/Load globals. The format of saving is: // [type][name][val] [type][name][val]... // Vals can be string, number (uint32), or boolean (byte) // The type of "None" (0) is the end of the list (and has no name/val). if (s.isSaving()) { #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: --- Saving globals: ---"); #endif lua_pushvalue(_luaState, LUA_GLOBALSINDEX); lua_pushnil(_luaState); int nextresult = lua_next(_luaState, -2); while (true) { if (nextresult == 0) { TeLuaSaveVarType stype = None; s.syncAsUint32LE(stype); lua_settop(_luaState, -2); break; } uint vtype = lua_type(_luaState, -1); Common::String name = lua_tolstring(_luaState, -2, nullptr); if (vtype == LUA_TBOOLEAN) { TeLuaSaveVarType stype = Boolean; s.syncAsUint32LE(stype); s.syncString(name); bool val = lua_toboolean(_luaState, -1); s.syncAsByte(val); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: bool %s = %s", name.c_str(), val ? "true" : "false"); #endif } else if (vtype == LUA_TNUMBER) { TeLuaSaveVarType stype = Number; s.syncAsUint32LE(stype); s.syncString(name); double val = lua_tonumber(_luaState, -1); s.syncAsDoubleLE(val); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: num %s = %f", name.c_str(), val); #endif } else if (vtype == LUA_TSTRING) { TeLuaSaveVarType stype = String; s.syncAsUint32LE(stype); s.syncString(name); Common::String val = lua_tostring(_luaState, -1); s.syncString(val); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: str %s = '%s'", name.c_str(), val.c_str()); #endif } lua_settop(_luaState, -2); nextresult = lua_next(_luaState, -2); } } else { #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: --- Loading globals: --- "); #endif // loading TeLuaSaveVarType vtype = None; s.syncAsUint32LE(vtype); while (vtype != None) { Common::String name; s.syncString(name); switch (vtype) { case Boolean: { byte b = 0; s.syncAsByte(b); lua_pushboolean(_luaState, b); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: bool %s = %s", name.c_str(), b ? "true" : "false"); #endif break; } case Number: { float d = 0; s.syncAsDoubleLE(d); lua_pushnumber(_luaState, d); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: num %s = %f", name.c_str(), d); #endif break; } case String: { Common::String str; s.syncString(str); lua_pushstring(_luaState, str.c_str()); #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: str %s = '%s'", name.c_str(), str.c_str()); #endif break; } default: error("Unexpected lua type on load %d", (int)vtype); } lua_setglobal(_luaState, name.c_str()); s.syncAsUint32LE(vtype); } } #ifdef TETRAEDGE_LUA_DEBUG_SAVELOAD debug("TeLuaContext::syncState: -------- end --------"); #endif return Common::kNoError; } } // end namespace Tetraedge