Tweak format of command help files and do some further command cleanup
[lsnes.git] / src / lua / lua.cpp
blob59e86518e8b2197e0d81f145264d96d1d04e6edd
1 #include "cmdhelp/lua.hpp"
2 #include "core/command.hpp"
3 #include "library/globalwrap.hpp"
4 #include "library/keyboard.hpp"
5 #include "lua/internal.hpp"
6 #include "lua/lua.hpp"
7 #include "lua/unsaferewind.hpp"
8 #include "core/instance.hpp"
9 #include "core/mainloop.hpp"
10 #include "core/messages.hpp"
11 #include "core/memorymanip.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/misc.hpp"
15 #include <map>
16 #include <cstring>
17 #include <string>
18 #include <iostream>
19 extern "C" {
20 #include <lualib.h>
23 extern const char* lua_sysrc_script;
25 lua::function_group lua_func_bit;
26 lua::function_group lua_func_misc;
27 lua::function_group lua_func_load;
28 lua::function_group lua_func_zip;
30 lua::class_group lua_class_callback;
31 lua::class_group lua_class_gui;
32 lua::class_group lua_class_bind;
33 lua::class_group lua_class_pure;
34 lua::class_group lua_class_movie;
35 lua::class_group lua_class_memory;
36 lua::class_group lua_class_fileio;
38 namespace
40 void pushpair(lua::state& L, std::string key, double value)
42 L.pushstring(key.c_str());
43 L.pushnumber(value);
44 L.settable(-3);
47 void pushpair(lua::state& L, std::string key, std::string value)
49 L.pushstring(key.c_str());
50 L.pushstring(value.c_str());
51 L.settable(-3);
54 std::string get_mode_str(int mode)
56 if(mode < 0)
57 return "disabled";
58 else if(mode > 0)
59 return "axis";
60 return "pressure0+";
64 void push_keygroup_parameters(lua::state& L, keyboard::key& p)
66 keyboard::mouse_calibration p2;
67 int mode;
68 L.newtable();
69 switch(p.get_type()) {
70 case keyboard::KBD_KEYTYPE_KEY:
71 pushpair(L, "value", p.get_state());
72 pushpair(L, "type", "key");
73 break;
74 case keyboard::KBD_KEYTYPE_HAT:
75 pushpair(L, "value", p.get_state());
76 pushpair(L, "type", "hat");
77 break;
78 case keyboard::KBD_KEYTYPE_MOUSE:
79 p2 = p.cast_mouse()->get_calibration();
80 pushpair(L, "value", p.get_state());
81 pushpair(L, "type", "mouse");
82 break;
83 case keyboard::KBD_KEYTYPE_AXIS:
84 mode = p.cast_axis()->get_mode();
85 pushpair(L, "value", p.get_state());
86 pushpair(L, "type", get_mode_str(mode));
87 break;
92 namespace
94 int push_keygroup_parameters2(lua::state& L, keyboard::key* p)
96 push_keygroup_parameters(L, *p);
97 return 1;
101 const char* read_lua_fragment(lua_State* L, void* fragment, size_t* size)
103 const char*& luareader_fragment = *reinterpret_cast<const char**>(fragment);
104 if(luareader_fragment) {
105 const char* ret = luareader_fragment;
106 *size = strlen(luareader_fragment);
107 luareader_fragment = NULL;
108 return ret;
109 } else {
110 *size = 0;
111 return NULL;
115 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
117 const char* CONST_eval_sysrc_lua = "local fn = loadstring(" TEMPORARY ", \"<built-in>\"); "
118 "if fn then fn(); else print2(\"Parse error in sysrc.lua script\"); end;";
119 const char* CONST_eval_lua_lua = "local fn = loadstring(" TEMPORARY "); if fn then fn(); else print("
120 "\"Parse error in Lua statement\"); end;";
121 const char* CONST_run_lua_lua = "dofile(" TEMPORARY ");";
123 int system_write_error(lua_State* L)
125 lua_pushstring(L, "_SYSTEM is write-protected");
126 lua_error(L);
127 return 0;
130 void copy_system_tables(lua::state& L)
132 #if LUA_VERSION_NUM == 501
133 L.pushvalue(LUA_GLOBALSINDEX);
134 #endif
135 #if LUA_VERSION_NUM == 502
136 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
137 #endif
138 L.newtable();
139 L.pushnil();
140 while(L.next(-3)) {
141 //Stack: _SYSTEM, KEY, VALUE
142 L.pushvalue(-2);
143 L.pushvalue(-2);
144 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
145 L.rawset(-5);
146 //Stack: _SYSTEM, KEY, VALUE
147 L.pop(1);
148 //Stack: _SYSTEM, KEY
150 L.newtable();
151 L.pushcfunction(system_write_error);
152 L.setfield(-2, "__newindex");
153 L.setmetatable(-2);
154 L.setglobal("_SYSTEM");
158 lua_state::lua_state(lua::state& _L, command::group& _command)
159 : L(_L), command(_command),
160 resetcmd(command, CLUA::reset, [this]() { this->do_reset(); }),
161 evalcmd(command, CLUA::eval, [this](const std::string& a) { this->do_eval_lua(a); }),
162 evalcmd2(command, CLUA::eval2, [this](const std::string& a) { this->do_eval_lua(a); }),
163 runcmd(command, CLUA::run, [this](command::arg_filename a) { this->do_run_lua(a); })
165 requests_repaint = false;
166 requests_subframe_paint = false;
167 render_ctx = NULL;
168 input_controllerdata = NULL;
170 idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
171 timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
172 veto_flag = NULL;
173 kill_frame = NULL;
174 hscl = NULL;
175 vscl = NULL;
176 synchronous_paint_ctx = NULL;
177 recursive_flag = false;
178 luareader_fragment = NULL;
180 renderq_saved = NULL;
181 renderq_last = NULL;
182 renderq_redirect = false;
184 on_paint = new lua::state::callback_list(L, "paint", "on_paint");
185 on_video = new lua::state::callback_list(L, "video", "on_video");
186 on_reset = new lua::state::callback_list(L, "reset", "on_reset");
187 on_frame = new lua::state::callback_list(L, "frame", "on_frame");
188 on_rewind = new lua::state::callback_list(L, "rewind", "on_rewind");
189 on_idle = new lua::state::callback_list(L, "idle", "on_idle");
190 on_timer = new lua::state::callback_list(L, "timer", "on_timer");
191 on_frame_emulated = new lua::state::callback_list(L, "frame_emulated", "on_frame_emulated");
192 on_readwrite = new lua::state::callback_list(L, "readwrite", "on_readwrite");
193 on_startup = new lua::state::callback_list(L, "startup", "on_startup");
194 on_pre_load = new lua::state::callback_list(L, "pre_load", "on_pre_load");
195 on_post_load = new lua::state::callback_list(L, "post_load", "on_post_load");
196 on_err_load = new lua::state::callback_list(L, "err_load", "on_err_load");
197 on_pre_save = new lua::state::callback_list(L, "pre_save", "on_pre_save");
198 on_post_save = new lua::state::callback_list(L, "post_save", "on_post_save");
199 on_err_save = new lua::state::callback_list(L, "err_save", "on_err_save");
200 on_input = new lua::state::callback_list(L, "input", "on_input");
201 on_snoop = new lua::state::callback_list(L, "snoop", "on_snoop");
202 on_snoop2 = new lua::state::callback_list(L, "snoop2", "on_snoop2");
203 on_button = new lua::state::callback_list(L, "button", "on_button");
204 on_quit = new lua::state::callback_list(L, "quit", "on_quit");
205 on_keyhook = new lua::state::callback_list(L, "keyhook", "on_keyhook");
206 on_movie_lost = new lua::state::callback_list(L, "movie_lost", "on_movie_lost");
207 on_pre_rewind = new lua::state::callback_list(L, "pre_rewind", "on_pre_rewind");
208 on_post_rewind = new lua::state::callback_list(L, "post_rewind", "on_post_rewind");
209 on_set_rewind = new lua::state::callback_list(L, "set_rewind", "on_set_rewind");
210 on_latch = new lua::state::callback_list(L, "latch", "on_latch");
213 lua_state::~lua_state()
215 delete on_paint;
216 delete on_video;
217 delete on_reset;
218 delete on_frame;
219 delete on_rewind;
220 delete on_idle;
221 delete on_timer;
222 delete on_frame_emulated;
223 delete on_readwrite;
224 delete on_startup;
225 delete on_pre_load;
226 delete on_post_load;
227 delete on_err_load;
228 delete on_pre_save;
229 delete on_post_save;
230 delete on_err_save;
231 delete on_input;
232 delete on_snoop;
233 delete on_snoop2;
234 delete on_button;
235 delete on_quit;
236 delete on_keyhook;
237 delete on_movie_lost;
238 delete on_pre_rewind;
239 delete on_post_rewind;
240 delete on_set_rewind;
241 delete on_latch;
244 void lua_state::callback_do_paint(struct lua::render_context* ctx, bool non_synthetic) throw()
246 run_synchronous_paint(ctx);
247 run_callback(*on_paint, lua::state::store_tag(render_ctx, ctx), lua::state::boolean_tag(non_synthetic));
250 void lua_state::callback_do_video(struct lua::render_context* ctx, bool& _kill_frame, uint32_t& _hscl,
251 uint32_t& _vscl) throw()
253 run_callback(*on_video, lua::state::store_tag(render_ctx, ctx), lua::state::store_tag(kill_frame,
254 &_kill_frame), lua::state::store_tag(hscl, &_hscl), lua::state::store_tag(vscl, &_vscl));
257 void lua_state::callback_do_reset() throw()
259 run_callback(*on_reset);
262 void lua_state::callback_do_frame() throw()
264 run_callback(*on_frame);
267 void lua_state::callback_do_rewind() throw()
269 run_callback(*on_rewind);
272 void lua_state::callback_do_idle() throw()
274 idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
275 run_callback(*on_idle);
278 void lua_state::callback_do_timer() throw()
280 timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
281 run_callback(*on_timer);
284 void lua_state::callback_do_frame_emulated() throw()
286 run_callback(*on_frame_emulated);
289 void lua_state::callback_do_readwrite() throw()
291 run_callback(*on_readwrite);
294 void lua_state::callback_pre_load(const std::string& name) throw()
296 run_callback(*on_pre_load, lua::state::string_tag(name));
299 void lua_state::callback_err_load(const std::string& name) throw()
301 run_callback(*on_err_load, lua::state::string_tag(name));
304 void lua_state::callback_post_load(const std::string& name, bool was_state) throw()
306 run_callback(*on_post_load, lua::state::string_tag(name), lua::state::boolean_tag(was_state));
309 void lua_state::callback_pre_save(const std::string& name, bool is_state) throw()
311 run_callback(*on_pre_save, lua::state::string_tag(name), lua::state::boolean_tag(is_state));
314 void lua_state::callback_err_save(const std::string& name) throw()
316 run_callback(*on_err_save, lua::state::string_tag(name));
319 void lua_state::callback_post_save(const std::string& name, bool is_state) throw()
321 run_callback(*on_post_save, lua::state::string_tag(name), lua::state::boolean_tag(is_state));
324 void lua_state::callback_do_input(portctrl::frame& data, bool subframe) throw()
326 run_callback(*on_input, lua::state::store_tag(input_controllerdata, &data),
327 lua::state::boolean_tag(subframe));
330 void lua_state::callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
332 if(run_callback(*on_snoop2, lua::state::numeric_tag(port), lua::state::numeric_tag(controller),
333 lua::state::numeric_tag(index), lua::state::numeric_tag(value)))
334 return;
335 run_callback(*on_snoop, lua::state::numeric_tag(port), lua::state::numeric_tag(controller),
336 lua::state::numeric_tag(index), lua::state::numeric_tag(value));
339 bool lua_state::callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type)
341 bool flag = false;
342 run_callback(*on_button, lua::state::store_tag(veto_flag, &flag), lua::state::numeric_tag(port),
343 lua::state::numeric_tag(controller), lua::state::numeric_tag(index), lua::state::string_tag(type));
344 return flag;
347 namespace
349 lua::_class<lua_unsaferewind> LUA_class_unsaferewind(lua_class_movie, "UNSAFEREWIND", {}, {
350 }, &lua_unsaferewind::print);
353 void lua_state::do_reset()
355 L.reset();
356 luaL_openlibs(L.handle());
358 run_sysrc_lua(true);
359 copy_system_tables(L);
360 messages << "Lua VM reset" << std::endl;
363 void lua_state::do_evaluate(const std::string& a)
365 if(a == "")
366 throw std::runtime_error("Expected expression to evaluate");
367 do_eval_lua(a);
370 void lua_state::callback_quit() throw()
372 run_callback(*on_quit);
375 void lua_state::callback_keyhook(const std::string& key, keyboard::key& p) throw()
377 run_callback(*on_keyhook, lua::state::string_tag(key), lua::state::fnptr_tag(push_keygroup_parameters2, &p));
380 void init_lua() throw()
382 auto& core = lsnes_instance;
383 core.lua->set_oom_handler(OOM_panic);
384 try {
385 core.lua->reset();
386 core.lua->add_function_group(lua_func_bit);
387 core.lua->add_function_group(lua_func_load);
388 core.lua->add_function_group(lua_func_misc);
389 core.lua->add_function_group(lua_func_zip);
390 core.lua->add_class_group(lua_class_callback);
391 core.lua->add_class_group(lua_class_gui);
392 core.lua->add_class_group(lua_class_bind);
393 core.lua->add_class_group(lua_class_pure);
394 core.lua->add_class_group(lua_class_movie);
395 core.lua->add_class_group(lua_class_memory);
396 core.lua->add_class_group(lua_class_fileio);
397 } catch(std::exception& e) {
398 messages << "Can't initialize Lua." << std::endl;
399 fatal_error();
401 luaL_openlibs(core.lua->handle());
402 core.lua2->run_sysrc_lua(false);
403 copy_system_tables(*core.lua);
406 void quit_lua() throw()
408 lsnes_instance.lua->deinit();
412 #define LUA_TIMED_HOOK_IDLE 0
413 #define LUA_TIMED_HOOK_TIMER 1
415 uint64_t lua_state::timed_hook(int timer) throw()
417 switch(timer) {
418 case LUA_TIMED_HOOK_IDLE:
419 return idle_hook_time;
420 case LUA_TIMED_HOOK_TIMER:
421 return timer_hook_time;
423 return 0;
426 void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov,
427 void* u)
429 auto& core = CORE();
430 if(u) {
431 lua_unsaferewind* u2 = reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u)->object();
432 //Load.
433 try {
434 run_callback(*on_pre_rewind);
435 run_callback(*on_movie_lost, "unsaferewind");
436 mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
437 mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
438 try { core.mlogic->get_mfile().host_memory = u2->hostmemory; } catch(...) {}
439 run_callback(*on_post_rewind);
440 delete reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u);
441 } catch(...) {
442 return;
444 } else {
445 //Save
446 run_callback(*on_set_rewind, lua::state::fn_tag([&core, save, secs, ssecs, &mov](lua::state& L) ->
447 int {
448 lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(*core.lua);
449 u2->state = save;
450 u2->secs = secs,
451 u2->ssecs = ssecs;
452 u2->hostmemory = core.mlogic->get_mfile().host_memory;
453 mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
454 return 1;
455 }));
459 void lua_state::callback_movie_lost(const char* what)
461 run_callback(*on_movie_lost, std::string(what));
464 void lua_state::callback_do_latch(std::list<std::string>& args)
466 run_callback(*on_latch, lua::state::vararg_tag(args));
469 lua_unsaferewind::lua_unsaferewind(lua::state& L)
473 void lua_state::run_startup_scripts()
475 for(auto i : startup_scripts) {
476 messages << "Trying to run Lua script: " << i << std::endl;
477 do_run_lua(i);
481 void lua_state::add_startup_script(const std::string& file)
483 startup_scripts.push_back(file);
486 const std::map<std::string, std::u32string>& lua_state::get_watch_vars()
488 return watch_vars;
491 bool lua_state::run_lua_fragment() throw(std::bad_alloc)
493 bool result = true;
494 if(recursive_flag)
495 return false;
496 #if LUA_VERSION_NUM == 501
497 int t = L.load(read_lua_fragment, &luareader_fragment, "run_lua_fragment");
498 #endif
499 #if LUA_VERSION_NUM == 502
500 int t = L.load(read_lua_fragment, &luareader_fragment, "run_lua_fragment", "t");
501 #endif
502 if(t == LUA_ERRSYNTAX) {
503 messages << "Can't run Lua: Internal syntax error: " << L.tostring(-1)
504 << std::endl;
505 L.pop(1);
506 return false;
508 if(t == LUA_ERRMEM) {
509 messages << "Can't run Lua: Out of memory" << std::endl;
510 L.pop(1);
511 return false;
513 recursive_flag = true;
514 int r = L.pcall(0, 0, 0);
515 recursive_flag = false;
516 if(r == LUA_ERRRUN) {
517 messages << "Error running Lua hunk: " << L.tostring(-1) << std::endl;
518 L.pop(1);
519 result = false;
521 if(r == LUA_ERRMEM) {
522 messages << "Error running Lua hunk: Out of memory" << std::endl;
523 L.pop(1);
524 result = false;
526 if(r == LUA_ERRERR) {
527 messages << "Error running Lua hunk: Double Fault???" << std::endl;
528 L.pop(1);
529 result = false;
531 render_ctx = NULL;
532 if(requests_repaint) {
533 requests_repaint = false;
534 command.invoke("repaint");
536 return result;
539 void lua_state::do_eval_lua(const std::string& c) throw(std::bad_alloc)
541 L.pushlstring(c.c_str(), c.length());
542 L.setglobal(TEMPORARY);
543 luareader_fragment = CONST_eval_lua_lua;
544 run_lua_fragment();
547 void lua_state::do_run_lua(const std::string& c) throw(std::bad_alloc)
549 L.pushlstring(c.c_str(), c.length());
550 L.setglobal(TEMPORARY);
551 luareader_fragment = CONST_run_lua_lua;
552 run_lua_fragment();
555 template<typename... T> bool lua_state::run_callback(lua::state::callback_list& list, T... args)
557 if(recursive_flag)
558 return true;
559 recursive_flag = true;
560 try {
561 if(!list.callback(args...)) {
562 recursive_flag = false;
563 return false;
565 } catch(std::exception& e) {
566 messages << e.what() << std::endl;
568 recursive_flag = false;
569 render_ctx = NULL;
570 if(requests_repaint) {
571 requests_repaint = false;
572 command.invoke("repaint");
574 return true;
577 void lua_state::run_sysrc_lua(bool rerun)
579 L.pushstring(lua_sysrc_script);
580 L.setglobal(TEMPORARY);
581 luareader_fragment = CONST_eval_sysrc_lua;
582 if(!run_lua_fragment() && !rerun) {
583 //run_lua_fragment shows error.
584 //messages << "Failed to run sysrc lua script" << std::endl;
585 fatal_error();
589 void lua_state::run_synchronous_paint(struct lua::render_context* ctx)
591 if(!synchronous_paint_ctx)
592 return;
593 lua_renderq_run(ctx, synchronous_paint_ctx);