Skip to content
Snippets Groups Projects
script-lua.cpp 2.46 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vicki Pfau's avatar
    Vicki Pfau committed
    #include "script-lua.h"
    
    #include "data.h"
    
    using namespace Retro;
    using namespace std;
    
    shared_ptr<ScriptContext> ScriptLua::create() {
    	return make_shared<ScriptLua>();
    }
    
    ScriptLua::~ScriptLua() {
    	if (m_L) {
    		lua_close(m_L);
    	}
    }
    
    static int _getData(lua_State* L) {
    	const GameData* data = static_cast<const GameData*>(lua_touserdata(L, 1));
    	int64_t datum;
    	if (lua_isnumber(L, 2)) {
    		int64_t address = lua_tonumber(L, 2);
    		const AddressSpace& as = data->addressSpace();
    		if (address < 0 || !as.hasBlock(address)) {
    			lua_pushstring(L, "Out of bounds access");
    			lua_error(L);
    		}
    		datum = as[address];
    	} else {
    		const char* name = lua_tostring(L, 2);
    		datum = data->lookupValue(name);
    	}
    
    	lua_pushnumber(L, datum);
    	return 1;
    }
    
    static int _noop(lua_State*) {
    	return 0;
    }
    
    void ScriptLua::setData(const GameData* data) {
    	ScriptContext::setData(data);
    
    	// Make "table"
    	lua_pushlightuserdata(m_L, const_cast<void*>(static_cast<const void*>(data)));
    
    	// Make metatable
    	lua_createtable(m_L, 0, 2);
    	lua_pushcfunction(m_L, _getData);
    	lua_setfield(m_L, -2, "__index");
    
    	lua_pushcfunction(m_L, _noop);
    	lua_setfield(m_L, -2, "__newindex");
    
    	lua_setmetatable(m_L, -2);
    	lua_setglobal(m_L, "data");
    };
    
    bool ScriptLua::init() {
    	m_L = luaL_newstate();
    	if (!m_L) {
    		return false;
    	}
    	luaopen_base(m_L);
    	luaopen_table(m_L);
    	luaopen_string(m_L);
    	luaopen_math(m_L);
    
    	vector<string> functions = listFunctions();
    	m_blacklist = { functions.begin(), functions.end() };
    	return true;
    }
    
    bool ScriptLua::load(const string& filename) {
    	return luaL_dofile(m_L, filename.c_str()) == 0;
    }
    
    Variant ScriptLua::callFunction(const string& funcName) {
    	lua_getglobal(m_L, funcName.c_str());
    	int status = lua_pcall(m_L, 0, 1, 0);
    	if (status != 0) {
    		string error = string("Lua call failed: ") + lua_tostring(m_L, -1);
    		lua_pop(m_L, 1);
    		throw runtime_error(error);
    	}
    
    	Variant v;
    	switch (lua_type(m_L, -1)) {
    	case LUA_TNUMBER:
    		v = lua_tonumber(m_L, -1);
    		break;
    	case LUA_TBOOLEAN:
    		v = static_cast<bool>(lua_toboolean(m_L, -1));
    		break;
    	default:
    		break;
    	}
    	lua_pop(m_L, 1);
    	return v;
    }
    
    vector<string> ScriptLua::listFunctions() {
    	vector<string> funcs;
    	lua_pushvalue(m_L, LUA_GLOBALSINDEX);
    	lua_pushnil(m_L);
    	while (lua_next(m_L, -2) != 0) {
    		if (lua_isfunction(m_L, -1)) {
    			lua_pushvalue(m_L, -2);
    			const char* str = lua_tostring(m_L, -1);
    			if (!m_blacklist.count(str)) {
    				funcs.emplace_back(str);
    			}
    			lua_pop(m_L, 1);
    		}
    		lua_pop(m_L, 1);
    	}
    	lua_pop(m_L, 1);
    	return funcs;
    }