Merge branch 'rr1-maint'
[lsnes.git] / src / lua / lua.cpp
blob2b1ad343ccbb2c2ca3e2fc7ce6b2cd85bd9126c2
1 #include "core/command.hpp"
2 #include "library/globalwrap.hpp"
3 #include "lua/internal.hpp"
4 #include "lua/lua.hpp"
5 #include "lua/unsaferewind.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/memorymanip.hpp"
8 #include "core/moviedata.hpp"
9 #include "core/misc.hpp"
11 #include <map>
12 #include <cstring>
13 #include <string>
14 #include <iostream>
15 extern "C" {
16 #include <lualib.h>
19 uint64_t lua_idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
20 uint64_t lua_timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
21 bool* lua_veto_flag = NULL;
22 extern const char* lua_sysrc_script;
24 lua_state LS;
26 namespace
28 void pushpair(lua_state& L, std::string key, double value)
30 L.pushstring(key.c_str());
31 L.pushnumber(value);
32 L.settable(-3);
35 void pushpair(lua_state& L, std::string key, std::string value)
37 L.pushstring(key.c_str());
38 L.pushstring(value.c_str());
39 L.settable(-3);
42 std::string calibration_to_type(keyboard_axis_calibration p)
44 if(p.mode == -1) return "disabled";
45 if(p.mode == 1 && p.esign_b == 1) return "axis";
46 if(p.mode == 1 && p.esign_b == -1) return "axis-inverse";
47 if(p.mode == 0 && p.esign_a == -1 && p.esign_b == 0) return "pressure-m0";
48 if(p.mode == 0 && p.esign_a == -1 && p.esign_b == 1) return "pressure-mp";
49 if(p.mode == 0 && p.esign_a == 0 && p.esign_b == -1) return "pressure-0m";
50 if(p.mode == 0 && p.esign_a == 0 && p.esign_b == 1) return "pressure-0p";
51 if(p.mode == 0 && p.esign_a == 1 && p.esign_b == -1) return "pressure-pm";
52 if(p.mode == 0 && p.esign_a == 1 && p.esign_b == 0) return "pressure-p0";
53 return "";
58 void push_keygroup_parameters(lua_state& L, keyboard_key& p)
60 keyboard_mouse_calibration p2;
61 keyboard_axis_calibration p3;
62 L.newtable();
63 switch(p.get_type()) {
64 case KBD_KEYTYPE_KEY:
65 pushpair(L, "value", p.get_state());
66 pushpair(L, "type", "key");
67 break;
68 case KBD_KEYTYPE_HAT:
69 pushpair(L, "value", p.get_state());
70 pushpair(L, "type", "hat");
71 break;
72 case KBD_KEYTYPE_MOUSE:
73 p2 = p.cast_mouse()->get_calibration();
74 pushpair(L, "value", p.get_state());
75 pushpair(L, "type", "mouse");
76 break;
77 case KBD_KEYTYPE_AXIS:
78 p3 = p.cast_axis()->get_calibration();
79 pushpair(L, "value", p.get_state());
80 pushpair(L, "type", calibration_to_type(p3));
81 break;
85 lua_render_context* lua_render_ctx = NULL;
86 controller_frame* lua_input_controllerdata = NULL;
87 bool lua_booted_flag = false;
89 namespace
91 int push_keygroup_parameters2(lua_state& L, keyboard_key* p)
93 push_keygroup_parameters(L, *p);
94 return 1;
97 bool recursive_flag = false;
98 const char* luareader_fragment = NULL;
100 const char* read_lua_fragment(lua_State* LS, void* dummy, size_t* size)
102 if(luareader_fragment) {
103 const char* ret = luareader_fragment;
104 *size = strlen(luareader_fragment);
105 luareader_fragment = NULL;
106 return ret;
107 } else {
108 *size = 0;
109 return NULL;
113 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
115 const char* eval_lua_lua = "loadstring(" TEMPORARY ")();";
116 const char* run_lua_lua = "dofile(" TEMPORARY ");";
118 void run_lua_fragment(lua_state& L) throw(std::bad_alloc)
120 if(recursive_flag)
121 return;
122 #if LUA_VERSION_NUM == 501
123 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment");
124 #endif
125 #if LUA_VERSION_NUM == 502
126 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment", "bt");
127 #endif
128 if(t == LUA_ERRSYNTAX) {
129 messages << "Can't run Lua: Internal syntax error: " << L.tostring(-1)
130 << std::endl;
131 L.pop(1);
132 return;
134 if(t == LUA_ERRMEM) {
135 messages << "Can't run Lua: Out of memory" << std::endl;
136 L.pop(1);
137 return;
139 recursive_flag = true;
140 int r = L.pcall(0, 0, 0);
141 recursive_flag = false;
142 if(r == LUA_ERRRUN) {
143 messages << "Error running Lua hunk: " << L.tostring(-1) << std::endl;
144 L.pop(1);
146 if(r == LUA_ERRMEM) {
147 messages << "Error running Lua hunk: Out of memory" << std::endl;
148 L.pop(1);
150 if(r == LUA_ERRERR) {
151 messages << "Error running Lua hunk: Double Fault???" << std::endl;
152 L.pop(1);
154 lua_render_ctx = NULL;
155 if(lua_requests_repaint) {
156 lua_requests_repaint = false;
157 lsnes_cmd.invoke("repaint");
161 void do_eval_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
163 L.pushlstring(c.c_str(), c.length());
164 L.setglobal(TEMPORARY);
165 luareader_fragment = eval_lua_lua;
166 run_lua_fragment(L);
169 void do_run_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
171 L.pushlstring(c.c_str(), c.length());
172 L.setglobal(TEMPORARY);
173 luareader_fragment = run_lua_lua;
174 run_lua_fragment(L);
177 template<typename... T> bool run_callback(T... args)
179 if(recursive_flag)
180 return true;
181 recursive_flag = true;
182 try {
183 if(!LS.callback(args...)) {
184 recursive_flag = false;
185 return false;
187 } catch(std::exception& e) {
188 messages << e.what() << std::endl;
190 lua_render_ctx = NULL;
191 if(lua_requests_repaint) {
192 lua_requests_repaint = false;
193 lsnes_cmd.invoke("repaint");
195 recursive_flag = false;
196 return true;
199 int system_write_error(lua_State* L)
201 lua_pushstring(L, "_SYSTEM is write-protected");
202 lua_error(L);
203 return 0;
206 void copy_system_tables(lua_state& L)
208 #if LUA_VERSION_NUM == 501
209 L.pushvalue(LUA_GLOBALSINDEX);
210 #endif
211 #if LUA_VERSION_NUM == 502
212 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
213 #endif
214 L.newtable();
215 L.pushnil();
216 while(L.next(-3)) {
217 //Stack: _SYSTEM, KEY, VALUE
218 L.pushvalue(-2);
219 L.pushvalue(-2);
220 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
221 L.rawset(-5);
222 //Stack: _SYSTEM, KEY, VALUE
223 L.pop(1);
224 //Stack: _SYSTEM, KEY
226 L.newtable();
227 L.pushcfunction(system_write_error);
228 L.setfield(-2, "__newindex");
229 L.setmetatable(-2);
230 L.setglobal("_SYSTEM");
233 void run_sysrc_lua(lua_state& L)
235 do_eval_lua(L, lua_sysrc_script);
240 void lua_callback_do_paint(struct lua_render_context* ctx, bool non_synthetic) throw()
242 run_callback("on_paint", lua_state::store_tag(lua_render_ctx, ctx), lua_state::boolean_tag(non_synthetic));
245 void lua_callback_do_video(struct lua_render_context* ctx) throw()
247 run_callback("on_video", lua_state::store_tag(lua_render_ctx, ctx));
250 void lua_callback_do_reset() throw()
252 run_callback("on_reset");
255 void lua_callback_do_frame() throw()
257 run_callback("on_frame");
260 void lua_callback_do_rewind() throw()
262 run_callback("on_rewind");
265 void lua_callback_do_idle() throw()
267 lua_idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
268 run_callback("on_idle");
271 void lua_callback_do_timer() throw()
273 lua_timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
274 run_callback("on_timer");
277 void lua_callback_do_frame_emulated() throw()
279 run_callback("on_frame_emulated");
282 void lua_callback_do_readwrite() throw()
284 run_callback("on_readwrite");
287 void lua_callback_startup() throw()
289 lua_booted_flag = true;
290 run_callback("on_startup");
293 void lua_callback_pre_load(const std::string& name) throw()
295 run_callback("on_pre_load", lua_state::string_tag(name));
298 void lua_callback_err_load(const std::string& name) throw()
300 run_callback("on_err_load", lua_state::string_tag(name));
303 void lua_callback_post_load(const std::string& name, bool was_state) throw()
305 run_callback("on_post_load", lua_state::string_tag(name), lua_state::boolean_tag(was_state));
308 void lua_callback_pre_save(const std::string& name, bool is_state) throw()
310 run_callback("on_pre_save", lua_state::string_tag(name), lua_state::boolean_tag(is_state));
313 void lua_callback_err_save(const std::string& name) throw()
315 run_callback("on_err_save", lua_state::string_tag(name));
318 void lua_callback_post_save(const std::string& name, bool is_state) throw()
320 run_callback("on_post_save", lua_state::string_tag(name), lua_state::boolean_tag(is_state));
323 void lua_callback_do_input(controller_frame& data, bool subframe) throw()
325 run_callback("on_input", lua_state::store_tag(lua_input_controllerdata, &data),
326 lua_state::boolean_tag(subframe));
329 void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
331 if(run_callback("on_snoop2", lua_state::numeric_tag(port), lua_state::numeric_tag(controller),
332 lua_state::numeric_tag(index), lua_state::numeric_tag(value)))
333 return;
334 run_callback("on_snoop", lua_state::numeric_tag(port), lua_state::numeric_tag(controller),
335 lua_state::numeric_tag(index), lua_state::numeric_tag(value));
338 bool lua_callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type)
340 bool flag = false;
341 run_callback("on_button", lua_state::store_tag(lua_veto_flag, &flag), lua_state::numeric_tag(port),
342 lua_state::numeric_tag(controller), lua_state::numeric_tag(index), lua_state::string_tag(type));
343 return flag;
346 namespace
348 function_ptr_command<const std::string&> evaluate_lua(lsnes_cmd, "evaluate-lua", "Evaluate expression in "
349 "Lua VM", "Syntax: evaluate-lua <expression>\nEvaluates <expression> in Lua VM.\n",
350 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
351 if(args == "")
352 throw std::runtime_error("Expected expression to evaluate");
353 do_eval_lua(LS, args);
356 function_ptr_command<arg_filename> run_lua(lsnes_cmd, "run-lua", "Run Lua script in Lua VM",
357 "Syntax: run-lua <file>\nRuns <file> in Lua VM.\n",
358 [](arg_filename args) throw(std::bad_alloc, std::runtime_error)
360 do_run_lua(LS, args);
363 function_ptr_command<> reset_lua(lsnes_cmd, "reset-lua", "Reset the Lua VM",
364 "Syntax: reset-lua\nReset the Lua VM.\n",
365 []() throw(std::bad_alloc, std::runtime_error)
367 LS.reset();
368 luaL_openlibs(LS.handle());
370 run_sysrc_lua(LS);
371 copy_system_tables(LS);
372 messages << "Lua VM reset" << std::endl;
377 void lua_callback_quit() throw()
379 run_callback("on_quit");
382 void lua_callback_keyhook(const std::string& key, keyboard_key& p) throw()
384 run_callback("on_keyhook", lua_state::string_tag(key), lua_state::fnptr_tag(push_keygroup_parameters2, &p));
387 void init_lua(bool soft) throw()
389 LS.set_oom_handler(OOM_panic);
390 try {
391 LS.reset();
392 } catch(std::exception& e) {
393 messages << "Can't initialize Lua." << std::endl;
394 if(soft)
395 return;
396 fatal_error();
398 luaL_openlibs(LS.handle());
399 run_sysrc_lua(LS);
400 copy_system_tables(LS);
403 void quit_lua() throw()
405 LS.deinit();
409 #define LUA_TIMED_HOOK_IDLE 0
410 #define LUA_TIMED_HOOK_TIMER 1
412 uint64_t lua_timed_hook(int timer) throw()
414 switch(timer) {
415 case LUA_TIMED_HOOK_IDLE:
416 return lua_idle_hook_time;
417 case LUA_TIMED_HOOK_TIMER:
418 return lua_timer_hook_time;
420 return 0;
423 void lua_callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void* u)
425 if(u) {
426 lua_unsaferewind* u2 = reinterpret_cast<lua_obj_pin<lua_unsaferewind>*>(u)->object();
427 //Load.
428 try {
429 run_callback("on_pre_rewind");
430 mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
431 mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
432 try { get_host_memory() = u2->hostmemory; } catch(...) {}
433 run_callback("on_post_rewind");
434 } catch(...) {
435 return;
437 } else {
438 //Save
439 run_callback("on_set_rewind", lua_state::fn_tag([save, secs, ssecs, &mov](lua_state& L) -> int {
440 lua_unsaferewind* u2 = lua_class<lua_unsaferewind>::create(LS);
441 u2->state = save;
442 u2->secs = secs,
443 u2->ssecs = ssecs;
444 u2->hostmemory = get_host_memory();
445 mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
446 return 1;
447 }));
451 bool lua_requests_repaint = false;
452 bool lua_requests_subframe_paint = false;
454 DECLARE_LUACLASS(lua_unsaferewind, "UNSAFEREWIND");