Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#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;
}