/* 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 "tetraedge/te/te_lua_thread.h" #include "tetraedge/te/te_lua_context.h" #include "tetraedge/te/te_variant.h" #include "common/str.h" #include "common/debug.h" #include "common/file.h" #include "common/lua/lua.h" #include "common/lua/lauxlib.h" #include "common/lua/lualib.h" namespace Tetraedge { /*static*/ Common::Array *TeLuaThread::_threadList = nullptr; TeLuaThread::TeLuaThread(TeLuaContext *context) : _resumeCount(0), _lastResumeResult(0), _released(false) { _luaThread = lua_newthread(context->luaState()); _bottomRef = luaL_ref(context->luaState(), LUA_REGISTRYINDEX); threadList()->push_back(this); } TeLuaThread::~TeLuaThread() { luaL_unref(_luaThread, LUA_REGISTRYINDEX, _bottomRef); uint i; Common::Array *threads = threadList(); for (i = 0; i < threads->size(); i++) if ((*threads)[i] == this) break; if (i < threads->size()) threads->remove_at(i); } /*static*/ TeLuaThread *TeLuaThread::create(TeLuaContext *context) { return new TeLuaThread(context); } void TeLuaThread::_resume(int nargs) { _resumeCount++; _lastResumeResult = lua_resume(_luaThread, nargs); if (_lastResumeResult > 1) { const char *msg = lua_tolstring(_luaThread, -1, nullptr); warning("TeLuaThread::_resume: %s", msg); } if (_lastResumeResult != 1 && _released) { debug("TeLuaThread:: deleting this?"); delete this; } } void TeLuaThread::execute(const Common::String &fname) { if (!_luaThread) return; lua_getglobal(_luaThread, fname.c_str()); if (lua_type(_luaThread, -1) == LUA_TFUNCTION) { _resume(0); } else { if (!fname.contains("Update")) debug("[TeLuaThread::Execute0] La fonction : \"%s\" n'existe pas.", fname.c_str()); lua_settop(_luaThread, -2); } } void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1) { if (!_luaThread) return; lua_getglobal(_luaThread, fname.c_str()); if (lua_type(_luaThread, -1) == LUA_TFUNCTION) { pushValue(p1); _resume(1); } else { if (!fname.contains("Update")) debug("[TeLuaThread::Execute1] La fonction : \"%s\" n'existe pas.", fname.c_str()); lua_settop(_luaThread, -2); } } void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, const TeVariant &p2) { if (!_luaThread) return; lua_getglobal(_luaThread, fname.c_str()); if (lua_type(_luaThread, -1) == LUA_TFUNCTION) { pushValue(p1); pushValue(p2); _resume(2); } else { if (!fname.contains("Update")) debug("[TeLuaThread::Execute2] La fonction : \"%s\" n'existe pas.", fname.c_str()); lua_settop(_luaThread, -2); } } void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, const TeVariant &p2, const TeVariant &p3) { if (!_luaThread) return; lua_getglobal(_luaThread, fname.c_str()); if (lua_type(_luaThread, -1) == LUA_TFUNCTION) { pushValue(p1); pushValue(p2); pushValue(p3); _resume(3); } else { if (!fname.contains("Update")) debug("[TeLuaThread::Execute3] La fonction : \"%s\" n'existe pas.", fname.c_str()); lua_settop(_luaThread, -4); } } void TeLuaThread::executeFile(const Common::Path &path) { Common::File scriptFile; if (!scriptFile.open(path)) { warning("TeLuaThread::executeFile: File %s can't be opened", path.toString().c_str()); return; } int64 fileLen = scriptFile.size(); char *buf = new char[fileLen + 1]; scriptFile.read(buf, fileLen); buf[fileLen] = 0; scriptFile.close(); // WORKAROUND: Some script files have rogue ";" lines in them with nothing else, clean those up. char *fixline = strstr(buf, "\n\t;"); if (fixline) fixline[2] = '\t'; _lastResumeResult = luaL_loadbuffer(_luaThread, buf, fileLen, path.toString().c_str()); if (_lastResumeResult) { const char *msg = lua_tostring(_luaThread, -1); warning("TeLuaThread::executeFile: %s", msg); } delete [] buf; _resume(0); } void TeLuaThread::pushValue(const TeVariant &val) { TeVariant::VariantType valType = val.type(); switch(valType) { case TeVariant::TypeBoolean: lua_pushboolean(_luaThread, val.toBoolean()); break; case TeVariant::TypeInt32: lua_pushinteger(_luaThread, val.toSigned32()); break; case TeVariant::TypeUInt32: lua_pushinteger(_luaThread, val.toUnsigned32()); break; case TeVariant::TypeInt64: lua_pushinteger(_luaThread, val.toSigned64()); break; case TeVariant::TypeUInt64: lua_pushinteger(_luaThread, val.toUnsigned64()); break; case TeVariant::TypeFloat32: lua_pushnumber(_luaThread, val.toFloat32()); break; case TeVariant::TypeFloat64: lua_pushnumber(_luaThread, val.toFloat64()); break; case TeVariant::TypeString: lua_pushstring(_luaThread, val.toString().c_str()); break; default: warning("TeLuaThread::pushValue: Unknown type"); return; } } void TeLuaThread::release() { _released = true; if (_lastResumeResult != 1) { //debug("TeLuaThread:: deleting this?"); delete this; } } void TeLuaThread::resume() { if (_luaThread) _resume(0); } void TeLuaThread::resume(const TeVariant &p1) { if (_luaThread) { pushValue(p1); _resume(1); } } void TeLuaThread::resume(const TeVariant &p1, const TeVariant &p2) { if (_luaThread) { pushValue(p1); pushValue(p2); _resume(2); } } void TeLuaThread::resume(const TeVariant &p1, const TeVariant &p2, const TeVariant &p3) { if (_luaThread) { pushValue(p1); pushValue(p2); pushValue(p3); _resume(3); } } /*static*/ TeLuaThread *TeLuaThread::threadFromState(lua_State *state) { Common::Array *threads = threadList(); for (auto &thread : *threads) { if (thread->_luaThread == state) return thread; } return nullptr; } /*static*/ Common::Array *TeLuaThread::threadList() { if (!_threadList) _threadList = new Common::Array(); return _threadList; } /*static*/ void TeLuaThread::cleanup() { delete _threadList; _threadList = nullptr; } int TeLuaThread::yield() { return lua_yield(_luaThread, 0); } } // end namespace Tetraedge