Lua: Add table _SYSTEM
[lsnes.git] / src / core / lua.cpp
blob9d28f4282799bff606b3985bcdc052a1abf0a41d
1 #include "core/lua.hpp"
3 #ifdef NO_LUA
4 struct lua_State { int x; };
5 lua_function::lua_function(const std::string& name) throw(std::bad_alloc) {}
6 lua_function::~lua_function() throw() {}
7 void lua_callback_do_paint(struct lua_render_context* ctx) throw() {}
8 void lua_callback_do_video(struct lua_render_context* ctx) throw() {}
9 void lua_callback_do_input(controls_t& data, bool subframe) throw() {}
10 void lua_callback_do_reset() throw() {}
11 void lua_callback_do_frame() throw() {}
12 void lua_callback_do_readwrite() throw() {}
13 void lua_callback_startup() throw() {}
14 void lua_callback_pre_load(const std::string& name) throw() {}
15 void lua_callback_err_load(const std::string& name) throw() {}
16 void lua_callback_post_load(const std::string& name, bool was_state) throw() {}
17 void lua_callback_pre_save(const std::string& name, bool is_state) throw() {}
18 void lua_callback_err_save(const std::string& name) throw() {}
19 void lua_callback_post_save(const std::string& name, bool is_state) throw() {}
20 void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw() {}
21 void lua_callback_quit() throw() {}
22 void init_lua() throw() {}
23 bool lua_requests_repaint = false;
24 bool lua_requests_subframe_paint = false;
25 bool lua_supported = false;
26 #else
28 #include "core/command.hpp"
29 #include "core/globalwrap.hpp"
30 #include "core/lua-int.hpp"
31 #include "core/mainloop.hpp"
32 #include "core/memorymanip.hpp"
33 #include "core/misc.hpp"
35 #include <map>
36 #include <cstring>
37 #include <string>
38 #include <iostream>
39 extern "C" {
40 #include <lua.h>
41 #include <lualib.h>
44 namespace
46 globalwrap<std::map<std::string, lua_function*>> functions;
47 lua_State* lua_initialized;
48 int lua_trampoline_function(lua_State* L)
50 void* ptr = lua_touserdata(L, lua_upvalueindex(1));
51 lua_function* f = reinterpret_cast<lua_function*>(ptr);
52 return f->invoke(L);
55 //Pushes given table to top of stack, creating if needed.
56 void recursive_lookup_table(lua_State* L, const std::string& tab)
58 if(tab == "") {
59 lua_pushvalue(L, LUA_GLOBALSINDEX);
60 return;
62 std::string u = tab;
63 size_t split = u.find_last_of(".");
64 std::string u1;
65 std::string u2 = u;
66 if(split < u.length()) {
67 u1 = u.substr(0, split);
68 u2 = u.substr(split + 1);
70 recursive_lookup_table(L, u1);
71 lua_getfield(L, -1, u2.c_str());
72 if(lua_type(L, -1) != LUA_TTABLE) {
73 //Not a table, create a table.
74 lua_pop(L, 1);
75 lua_newtable(L);
76 lua_setfield(L, -2, u2.c_str());
77 lua_getfield(L, -1, u2.c_str());
79 //Get rid of previous table.
80 lua_insert(L, -2);
81 lua_pop(L, 1);
84 void register_lua_function(lua_State* L, const std::string& fun)
86 std::string u = fun;
87 size_t split = u.find_last_of(".");
88 std::string u1;
89 std::string u2 = u;
90 if(split < u.length()) {
91 u1 = u.substr(0, split);
92 u2 = u.substr(split + 1);
94 recursive_lookup_table(L, u1);
95 void* ptr = reinterpret_cast<void*>(functions()[fun]);
96 lua_pushlightuserdata(L, ptr);
97 lua_pushcclosure(L, lua_trampoline_function, 1);
98 lua_setfield(L, -2, u2.c_str());
99 lua_pop(L, 1);
102 void register_lua_functions(lua_State* L)
104 for(auto i : functions())
105 register_lua_function(L, i.first);
106 lua_initialized = L;
110 lua_function::lua_function(const std::string& name) throw(std::bad_alloc)
112 functions()[fname = name] = this;
113 if(lua_initialized)
114 register_lua_function(lua_initialized, fname);
117 lua_function::~lua_function() throw()
119 functions().erase(fname);
122 std::string get_string_argument(lua_State* LS, unsigned argindex, const char* fname)
124 if(lua_isnone(LS, argindex)) {
125 char buffer[1024];
126 sprintf(buffer, "argument #%i to %s must be string", argindex, fname);
127 lua_pushstring(LS, buffer);
128 lua_error(LS);
130 size_t len;
131 const char* f = lua_tolstring(LS, argindex, &len);
132 if(!f) {
133 char buffer[1024];
134 sprintf(buffer, "argument #%i to %s must be string", argindex, fname);
135 lua_pushstring(LS, buffer);
136 lua_error(LS);
138 return std::string(f, f + len);
141 bool get_boolean_argument(lua_State* LS, unsigned argindex, const char* fname)
143 if(lua_isnone(LS, argindex) || !lua_isboolean(LS, argindex)) {
144 char buffer[1024];
145 sprintf(buffer, "argument #%i to %s must be boolean", argindex, fname);
146 lua_pushstring(LS, buffer);
147 lua_error(LS);
149 return (lua_toboolean(LS, argindex) != 0);
152 lua_render_context* lua_render_ctx = NULL;
153 controls_t* lua_input_controllerdata = NULL;
155 namespace
157 lua_State* L;
158 bool recursive_flag = false;
159 const char* luareader_fragment = NULL;
161 const char* read_lua_fragment(lua_State* LS, void* dummy, size_t* size)
163 if(luareader_fragment) {
164 const char* ret = luareader_fragment;
165 *size = strlen(luareader_fragment);
166 luareader_fragment = NULL;
167 return ret;
168 } else {
169 *size = 0;
170 return NULL;
174 void* alloc(void* user, void* old, size_t olds, size_t news)
176 if(news) {
177 void* m = realloc(old, news);
178 if(!m)
179 OOM_panic();
180 return m;
181 } else
182 free(old);
183 return NULL;
186 bool callback_exists(const char* name)
188 if(recursive_flag)
189 return false;
190 lua_getglobal(L, name);
191 int t = lua_type(L, -1);
192 if(t != LUA_TFUNCTION)
193 lua_pop(L, 1);
194 return (t == LUA_TFUNCTION);
197 void push_string(const std::string& s)
199 lua_pushlstring(L, s.c_str(), s.length());
202 void push_boolean(bool b)
204 lua_pushboolean(L, b ? 1 : 0);
207 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
209 const char* eval_lua_lua = "loadstring(" TEMPORARY ")();";
210 const char* run_lua_lua = "dofile(" TEMPORARY ");";
212 void run_lua_fragment() throw(std::bad_alloc)
214 if(recursive_flag)
215 return;
216 int t = lua_load(L, read_lua_fragment, NULL, "run_lua_fragment");
217 if(t == LUA_ERRSYNTAX) {
218 messages << "Can't run Lua: Internal syntax error: " << lua_tostring(L, -1) << std::endl;
219 lua_pop(L, 1);
220 return;
222 if(t == LUA_ERRMEM) {
223 messages << "Can't run Lua: Out of memory" << std::endl;
224 lua_pop(L, 1);
225 return;
227 recursive_flag = true;
228 int r = lua_pcall(L, 0, 0, 0);
229 recursive_flag = false;
230 if(r == LUA_ERRRUN) {
231 messages << "Error running Lua hunk: " << lua_tostring(L, -1) << std::endl;
232 lua_pop(L, 1);
234 if(r == LUA_ERRMEM) {
235 messages << "Error running Lua hunk: Out of memory" << std::endl;
236 lua_pop(L, 1);
238 if(r == LUA_ERRERR) {
239 messages << "Error running Lua hunk: Double Fault???" << std::endl;
240 lua_pop(L, 1);
242 if(lua_requests_repaint) {
243 lua_requests_repaint = false;
244 command::invokeC("repaint");
248 void do_eval_lua(const std::string& c) throw(std::bad_alloc)
250 push_string(c);
251 lua_setglobal(L, TEMPORARY);
252 luareader_fragment = eval_lua_lua;
253 run_lua_fragment();
256 void do_run_lua(const std::string& c) throw(std::bad_alloc)
258 push_string(c);
259 lua_setglobal(L, TEMPORARY);
260 luareader_fragment = run_lua_lua;
261 run_lua_fragment();
264 void run_lua_cb(int args) throw()
266 recursive_flag = true;
267 int r = lua_pcall(L, args, 0, 0);
268 recursive_flag = false;
269 if(r == LUA_ERRRUN) {
270 messages << "Error running Lua callback: " << lua_tostring(L, -1) << std::endl;
271 lua_pop(L, 1);
273 if(r == LUA_ERRMEM) {
274 messages << "Error running Lua callback: Out of memory" << std::endl;
275 lua_pop(L, 1);
277 if(r == LUA_ERRERR) {
278 messages << "Error running Lua callback: Double Fault???" << std::endl;
279 lua_pop(L, 1);
281 if(lua_requests_repaint) {
282 lua_requests_repaint = false;
283 command::invokeC("repaint");
287 int system_write_error(lua_State* L)
289 lua_pushstring(L, "_SYSTEM is write-protected");
290 lua_error(L);
291 return 0;
294 void copy_system_tables(lua_State* L)
296 lua_newtable(L);
297 lua_pushnil(L);
298 while(lua_next(L, LUA_GLOBALSINDEX)) {
299 //Stack: _SYSTEM, KEY, VALUE
300 lua_pushvalue(L, -2);
301 lua_pushvalue(L, -2);
302 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
303 lua_rawset(L, -5);
304 //Stack: _SYSTEM, KEY, VALUE
305 lua_pop(L, 1);
306 //Stack: _SYSTEM, KEY
308 lua_newtable(L);
309 lua_pushcfunction(L, system_write_error);
310 lua_setfield(L, -2, "__newindex");
311 lua_setmetatable(L, -2);
312 lua_setglobal(L, "_SYSTEM");
316 void lua_callback_do_paint(struct lua_render_context* ctx) throw()
318 if(!callback_exists("on_paint"))
319 return;
320 lua_render_ctx = ctx;
321 run_lua_cb(0);
322 lua_render_ctx = NULL;
325 void lua_callback_do_video(struct lua_render_context* ctx) throw()
327 if(!callback_exists("on_video"))
328 return;
329 lua_render_ctx = ctx;
330 run_lua_cb(0);
331 lua_render_ctx = NULL;
334 void lua_callback_do_reset() throw()
336 if(!callback_exists("on_reset"))
337 return;
338 run_lua_cb(0);
341 void lua_callback_do_frame() throw()
343 if(!callback_exists("on_frame"))
344 return;
345 run_lua_cb(0);
348 void lua_callback_do_readwrite() throw()
350 if(!callback_exists("on_readwrite"))
351 return;
352 run_lua_cb(0);
355 void lua_callback_startup() throw()
357 if(!callback_exists("on_startup"))
358 return;
359 run_lua_cb(0);
362 void lua_callback_pre_load(const std::string& name) throw()
364 if(!callback_exists("on_pre_load"))
365 return;
366 push_string(name);
367 run_lua_cb(1);
370 void lua_callback_err_load(const std::string& name) throw()
372 if(!callback_exists("on_err_load"))
373 return;
374 push_string(name);
375 run_lua_cb(1);
378 void lua_callback_post_load(const std::string& name, bool was_state) throw()
380 if(!callback_exists("on_post_load"))
381 return;
382 push_string(name);
383 push_boolean(was_state);
384 run_lua_cb(2);
387 void lua_callback_pre_save(const std::string& name, bool is_state) throw()
389 if(!callback_exists("on_pre_save"))
390 return;
391 push_string(name);
392 push_boolean(is_state);
393 run_lua_cb(2);
396 void lua_callback_err_save(const std::string& name) throw()
398 if(!callback_exists("on_err_save"))
399 return;
400 push_string(name);
401 run_lua_cb(1);
404 void lua_callback_post_save(const std::string& name, bool is_state) throw()
406 if(!callback_exists("on_post_save"))
407 return;
408 push_string(name);
409 push_boolean(is_state);
410 run_lua_cb(2);
413 void lua_callback_do_input(controls_t& data, bool subframe) throw()
415 if(!callback_exists("on_input"))
416 return;
417 lua_input_controllerdata = &data;
418 push_boolean(subframe);
419 run_lua_cb(1);
420 lua_input_controllerdata = NULL;
423 void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
425 if(!callback_exists("on_snoop"))
426 return;
427 lua_pushnumber(L, port);
428 lua_pushnumber(L, controller);
429 lua_pushnumber(L, index);
430 lua_pushnumber(L, value);
431 run_lua_cb(4);
434 namespace
436 function_ptr_command<const std::string&> evaluate_lua("evaluate-lua", "Evaluate expression in Lua VM",
437 "Syntax: evaluate-lua <expression>\nEvaluates <expression> in Lua VM.\n",
438 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
439 if(args == "")
440 throw std::runtime_error("Expected expression to evaluate");
441 do_eval_lua(args);
444 function_ptr_command<arg_filename> run_lua("run-lua", "Run Lua script in Lua VM",
445 "Syntax: run-lua <file>\nRuns <file> in Lua VM.\n",
446 [](arg_filename args) throw(std::bad_alloc, std::runtime_error)
448 do_run_lua(args);
452 void lua_callback_quit() throw()
454 if(!callback_exists("on_quit"))
455 return;
456 run_lua_cb(0);
459 void init_lua() throw()
461 L = lua_newstate(alloc, NULL);
462 if(!L) {
463 messages << "Can't initialize Lua." << std::endl;
464 fatal_error();
466 luaL_openlibs(L);
468 register_lua_functions(L);
469 copy_system_tables(L);
472 bool lua_requests_repaint = false;
473 bool lua_requests_subframe_paint = false;
474 bool lua_supported = true;
475 #endif