From 897cc01f938a8532e89f8726d9b7db4bcac63c77 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 3 Oct 2021 18:34:27 +0200 Subject: [PATCH] some lua bindings --- .github/workflows/c-cpp.yml | 2 +- .gitmodules | 3 + CMakeLists.txt | 12 +- core/deps/luabridge | 1 + core/emulator.cpp | 2 + core/lua/lua.cpp | 560 ++++++++++++++++++++++++++++++++++++ core/lua/lua.h | 39 +++ core/nullDC.cpp | 3 + 8 files changed, 619 insertions(+), 3 deletions(-) create mode 160000 core/deps/luabridge create mode 100644 core/lua/lua.cpp create mode 100644 core/lua/lua.h diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 81dfe93e0..fdd5c2e2e 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -68,7 +68,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: MINGW64 - install: git base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-breakpad-git + install: git base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-breakpad-git mingw-w64-x86_64-lua if: matrix.config.os == 'windows-latest' - uses: actions/checkout@v2 diff --git a/.gitmodules b/.gitmodules index 5e5038215..74ce284de 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "core/deps/libchdr"] path = core/deps/libchdr url = https://github.com/rtissera/libchdr.git +[submodule "core/deps/luabridge"] + path = core/deps/luabridge + url = https://github.com/vinniefalco/LuaBridge.git diff --git a/CMakeLists.txt b/CMakeLists.txt index cc5a80a93..e943b86d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,6 @@ if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /GR- /GS-) else() target_compile_options(${PROJECT_NAME} PRIVATE - $<$:-fno-rtti> $<$:-fno-strict-aliasing> $<$:-Wall>) endif() @@ -262,6 +261,13 @@ if(NOT LIBRETRO) set(WITH_SYSTEM_ZLIB ON CACHE BOOL "Use system provided zlib library") target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB) endif() + + find_package(Lua) + if(LUA_FOUND) + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LUA) + target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIR} core/deps/luabridge/Source) + target_link_libraries(${PROJECT_NAME} PRIVATE ${LUA_LIBRARIES}) + endif() endif() execute_process(COMMAND git apply -p1 ${CMAKE_CURRENT_SOURCE_DIR}/core/deps/patches/libchdr.patch @@ -817,7 +823,9 @@ target_sources(${PROJECT_NAME} PRIVATE core/oslib/audiostream.h core/oslib/directory.h core/oslib/host_context.h - core/oslib/oslib.h) + core/oslib/oslib.h + core/lua/lua.cpp + core/lua/lua.h) target_sources(${PROJECT_NAME} PRIVATE core/profiler/profiler.cpp diff --git a/core/deps/luabridge b/core/deps/luabridge new file mode 160000 index 000000000..fab7b33b8 --- /dev/null +++ b/core/deps/luabridge @@ -0,0 +1 @@ +Subproject commit fab7b33b896a42dcc865ba5ecdbacd9f409137f8 diff --git a/core/emulator.cpp b/core/emulator.cpp index 1cf948f9e..3600b924f 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -38,6 +38,7 @@ #include "hw/mem/mem_watch.h" #include "network/net_handshake.h" #include "rend/gui.h" +#include "lua/lua.h" #include settings_t settings; @@ -778,6 +779,7 @@ bool Emulator::render() void Emulator::vblank() { + lua::vblank(); // Time out if a frame hasn't been rendered for 50 ms if (sh4_sched_now64() - startTime <= 10000000) return; diff --git a/core/lua/lua.cpp b/core/lua/lua.cpp new file mode 100644 index 000000000..6788f2786 --- /dev/null +++ b/core/lua/lua.cpp @@ -0,0 +1,560 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "lua.h" + +#ifdef USE_LUA +#include +#include +#include "rend/gui.h" +#include "hw/mem/_vmem.h" +#include "cfg/option.h" +#include "emulator.h" +#include "input/gamepad_device.h" +#include "hw/maple/maple_devs.h" +#include "hw/maple/maple_if.h" +#include "stdclass.h" + +namespace lua +{ +const char *CallbackTable = "flycast_callbacks"; +static lua_State *L; +using namespace luabridge; + +static void emuEventCallback(Event event) +{ + if (L == nullptr) + return; + try { + LuaRef v = LuaRef::getGlobal(L, CallbackTable); + if (!v.isTable()) + return; + const char *key = nullptr; + switch (event) + { + case Event::Start: + key = "start"; + break; + case Event::Resume: + key = "resume"; + break; + case Event::Pause: + key = "pause"; + break; + case Event::Terminate: + key = "terminate"; + break; + case Event::LoadState: + key = "loadState"; + break; + } + if (v[key].isFunction()) + v[key](); + } catch (const LuaException& e) { + WARN_LOG(COMMON, "Lua exception: %s", e.what()); + } +} + +void vblank() +{ + if (L == nullptr) + return; + try { + LuaRef v = LuaRef::getGlobal(L, CallbackTable); + if (v.isTable() && v["vblank"].isFunction()) + v["vblank"](); + } catch (const LuaException& e) { + WARN_LOG(COMMON, "Lua exception: %s", e.what()); + } +} + +template +static LuaRef readMemoryTable(u32 address, int count, lua_State* L) +{ + LuaRef t(L); + t = newTable(L); + while (count > 0) + { + t[address] = _vmem_readt(address); + address += sizeof(T); + count--; + } + + return t; +} + +#define CONFIG_ACCESSORS(Config) \ +template \ +static T get ## Config() { \ + return config::Config.get(); \ +} \ +template \ +static void set ## Config(T v) \ +{ \ + config::Config.set(v); \ +} + +// General +CONFIG_ACCESSORS(Cable); +CONFIG_ACCESSORS(Region); +CONFIG_ACCESSORS(Broadcast); +CONFIG_ACCESSORS(Language); +CONFIG_ACCESSORS(FullMMU); +CONFIG_ACCESSORS(ForceWindowsCE); +CONFIG_ACCESSORS(AutoLoadState); +CONFIG_ACCESSORS(AutoSaveState); +CONFIG_ACCESSORS(SavestateSlot); +// TODO Option, false> ContentPath; +CONFIG_ACCESSORS(HideLegacyNaomiRoms) + +// Video +CONFIG_ACCESSORS(RendererType) +CONFIG_ACCESSORS(Widescreen) +CONFIG_ACCESSORS(UseMipmaps) +CONFIG_ACCESSORS(SuperWidescreen) +CONFIG_ACCESSORS(ShowFPS) +CONFIG_ACCESSORS(RenderToTextureBuffer) +CONFIG_ACCESSORS(TranslucentPolygonDepthMask) +CONFIG_ACCESSORS(ModifierVolumes) +CONFIG_ACCESSORS(TextureUpscale) +CONFIG_ACCESSORS(MaxFilteredTextureSize) +CONFIG_ACCESSORS(ExtraDepthScale) +CONFIG_ACCESSORS(CustomTextures) +CONFIG_ACCESSORS(DumpTextures) +CONFIG_ACCESSORS(ScreenStretching) +CONFIG_ACCESSORS(Fog) +CONFIG_ACCESSORS(FloatVMUs) +CONFIG_ACCESSORS(Rotate90) +CONFIG_ACCESSORS(PerStripSorting) +CONFIG_ACCESSORS(DelayFrameSwapping) +CONFIG_ACCESSORS(WidescreenGameHacks) +//TODO CrosshairColor; +CONFIG_ACCESSORS(SkipFrame) +CONFIG_ACCESSORS(MaxThreads) +CONFIG_ACCESSORS(AutoSkipFrame) +CONFIG_ACCESSORS(RenderResolution) +CONFIG_ACCESSORS(VSync) +CONFIG_ACCESSORS(PixelBufferSize) +CONFIG_ACCESSORS(AnisotropicFiltering) +CONFIG_ACCESSORS(ThreadedRendering) + +// Audio +CONFIG_ACCESSORS(DSPEnabled) +CONFIG_ACCESSORS(AudioBufferSize) +CONFIG_ACCESSORS(AutoLatency) +CONFIG_ACCESSORS(AudioBackend) +CONFIG_ACCESSORS(AudioVolume) + +// Advanced +CONFIG_ACCESSORS(DynarecEnabled) +CONFIG_ACCESSORS(DynarecIdleSkip) +CONFIG_ACCESSORS(SerialConsole) +CONFIG_ACCESSORS(SerialPTY) +CONFIG_ACCESSORS(UseReios) +CONFIG_ACCESSORS(FastGDRomLoad) +CONFIG_ACCESSORS(OpenGlChecks) + +// Network +CONFIG_ACCESSORS(NetworkEnable) +CONFIG_ACCESSORS(ActAsServer) +CONFIG_ACCESSORS(DNS) +CONFIG_ACCESSORS(NetworkServer) +CONFIG_ACCESSORS(EmulateBBA) +CONFIG_ACCESSORS(GGPOEnable) +CONFIG_ACCESSORS(GGPODelay) +CONFIG_ACCESSORS(NetworkStats) +CONFIG_ACCESSORS(GGPOAnalogAxes) +CONFIG_ACCESSORS(GGPOPlayerNum) + +// Maple devices + +static int getMapleType(int bus, lua_State *L) +{ + luaL_argcheck(L, bus >= 1 && bus <= 4, 1, "bus must be between 1 and 4"); + if (MapleDevices[bus - 1][5] == nullptr) + return MDT_None; + return MapleDevices[bus - 1][5]->get_device_type(); +} + +static int getMapleSubType(int bus, int port, lua_State *L) +{ + luaL_argcheck(L, bus >= 1 && bus <= 4, 1, "bus must be between 1 and 4"); + luaL_argcheck(L, port >= 1 && port <= 2, 2, "port must be between 1 and 2"); + if (MapleDevices[bus - 1][port - 1] == nullptr) + return MDT_None; + return MapleDevices[bus - 1][port - 1]->get_device_type(); +} + +static void setMapleType(int bus, int type, lua_State *L) +{ + luaL_argcheck(L, bus >= 1 && bus <= 4, 1, "bus must be between 1 and 4"); + switch ((MapleDeviceType)type) { + case MDT_SegaController: + case MDT_AsciiStick: + case MDT_Keyboard: + case MDT_Mouse: + case MDT_LightGun: + case MDT_TwinStick: + case MDT_None: + config::MapleMainDevices[bus - 1] = (MapleDeviceType)type; + maple_ReconnectDevices(); + break; + default: + luaL_argerror(L, 2, "Invalid device type"); + break; + } +} + +static void setMapleSubType(int bus, int port, int type, lua_State *L) +{ + luaL_argcheck(L, bus >= 1 && bus <= 4, 1, "bus must be between 1 and 4"); + luaL_argcheck(L, port >= 1 && port <= 2, 2, "port must be between 1 and 2"); + switch ((MapleDeviceType)type) { + case MDT_SegaVMU: + case MDT_PurupuruPack: + case MDT_Microphone: + case MDT_None: + config::MapleExpansionDevices[bus - 1][port - 1] = (MapleDeviceType)type; + maple_ReconnectDevices(); + break; + default: + luaL_argerror(L, 3, "Invalid device type"); + break; + } +} + +// Inputs + +static void checkPlayerNum(lua_State *L, int player) { + luaL_argcheck(L, player >= 1 && player <= 4, 1, "player must be between 1 and 4"); +} + +static u32 getButtons(int player, lua_State *L) +{ + checkPlayerNum(L, player); + return kcode[player - 1]; +} + +static void pressButtons(int player, u32 buttons, lua_State *L) +{ + checkPlayerNum(L, player); + kcode[player - 1] &= ~buttons; +} + +static void releaseButtons(int player, u32 buttons, lua_State *L) +{ + checkPlayerNum(L, player); + kcode[player - 1] |= buttons; +} + +static int getAxis(int player, int axis, lua_State *L) +{ + checkPlayerNum(L, player); + luaL_argcheck(L, axis >= 1 && axis <= 6, 2, "axis must be between 1 and 6"); + switch (axis - 1) + { + case 0: + return joyx[player - 1]; + case 1: + return joyy[player - 1]; + case 2: + return joyrx[player - 1]; + case 3: + return joyry[player - 1]; + case 4: + return lt[player - 1]; + case 5: + return rt[player - 1]; + default: + return 0; + } +} + +static void setAxis(int player, int axis, int value, lua_State *L) +{ + checkPlayerNum(L, player); + luaL_argcheck(L, axis >= 1 && axis <= 6, 2, "axis must be between 1 and 6"); + switch (axis - 1) + { + case 0: + joyx[player - 1] = value; + break; + case 1: + joyy[player - 1] = value; + break; + case 2: + joyrx[player - 1] = value; + break; + case 3: + joyry[player - 1] = value; + break; + case 4: + lt[player - 1] = value; + break; + case 5: + rt[player - 1] = value; + break; + default: + break; + } +} + +static int getAbsCoordinates(lua_State *L) +{ + int player = luaL_checkinteger(L, 1); + checkPlayerNum(L, player); + lua_pushnumber(L, mo_x_abs[player - 1]); + lua_pushnumber(L, mo_y_abs[player - 1]); + return 2; +} + +static void setAbsCoordinates(int player, int x, int y, lua_State *L) +{ + checkPlayerNum(L, player); + SetMousePosition(x, y, settings.display.width, settings.display.height, player - 1); +} + +static int getRelCoordinates(lua_State *L) +{ + int player = luaL_checkinteger(L, 1); + checkPlayerNum(L, player); + lua_pushnumber(L, mo_x_delta[player - 1]); + lua_pushnumber(L, mo_y_delta[player - 1]); + return 2; +} + +static void setRelCoordinates(int player, float x, float y, lua_State *L) +{ + checkPlayerNum(L, player); + SetRelativeMousePosition(x, y, player - 1); +} + +static void luaRegister(lua_State *L) +{ + getGlobalNamespace(L) + .beginNamespace ("flycast") + .beginNamespace("emulator") + .addFunction("startGame", gui_start_game) // FIXME threading! + .addFunction("stopGame", std::function([]() { gui_stop_game(""); })) + .addFunction("pause", std::function([]() { + if (gui_state == GuiState::Closed) + gui_open_settings(); + })) + .addFunction("resume", std::function([]() { + if (gui_state == GuiState::Commands) + gui_open_settings(); + })) + .addFunction("saveState", std::function([](int index) { + bool restart = false; + if (gui_state == GuiState::Closed) { + gui_open_settings(); + restart = true; + } + dc_savestate(index); + if (restart) + gui_open_settings(); + })) + .addFunction("loadState", std::function([](int index) { + bool restart = false; + if (gui_state == GuiState::Closed) { + gui_open_settings(); + restart = true; + } + dc_loadstate(index); + if (restart) + gui_open_settings(); + })) + .addFunction("exit", dc_exit) + .addFunction("displayNotification", gui_display_notification) + .endNamespace() + + .beginNamespace("config") +#define CONFIG_PROPERTY(Config, type) .addProperty(#Config, get ## Config, set ## Config) + .beginNamespace("general") + CONFIG_PROPERTY(Cable, int) + CONFIG_PROPERTY(Region, int) + CONFIG_PROPERTY(Broadcast, int) + CONFIG_PROPERTY(Language, int) + CONFIG_PROPERTY(FullMMU, bool) + CONFIG_PROPERTY(ForceWindowsCE, bool) + CONFIG_PROPERTY(AutoLoadState, bool) + CONFIG_PROPERTY(AutoSaveState, bool) + CONFIG_PROPERTY(SavestateSlot, int) + CONFIG_PROPERTY(HideLegacyNaomiRoms, bool) + .endNamespace() + + .beginNamespace("video") +// FIXME .addProperty("RendererType", getRendererType, setRendererType) + CONFIG_PROPERTY(Widescreen, bool) + CONFIG_PROPERTY(SuperWidescreen, bool) + CONFIG_PROPERTY(UseMipmaps, bool) + CONFIG_PROPERTY(ShowFPS, bool) + CONFIG_PROPERTY(RenderToTextureBuffer, bool) + CONFIG_PROPERTY(TranslucentPolygonDepthMask, bool) + CONFIG_PROPERTY(ModifierVolumes, bool) + CONFIG_PROPERTY(TextureUpscale, int) + CONFIG_PROPERTY(MaxFilteredTextureSize, int) + CONFIG_PROPERTY(ExtraDepthScale, float) + CONFIG_PROPERTY(CustomTextures, bool) + CONFIG_PROPERTY(DumpTextures, bool) + CONFIG_PROPERTY(ScreenStretching, int) + CONFIG_PROPERTY(Fog, bool) + CONFIG_PROPERTY(FloatVMUs, bool) + CONFIG_PROPERTY(Rotate90, bool) + CONFIG_PROPERTY(PerStripSorting, bool) + CONFIG_PROPERTY(DelayFrameSwapping, bool) + CONFIG_PROPERTY(WidescreenGameHacks, bool) + // TODO CrosshairColor; + CONFIG_PROPERTY(SkipFrame, int) + CONFIG_PROPERTY(MaxThreads, int) + CONFIG_PROPERTY(AutoSkipFrame, int) + CONFIG_PROPERTY(RenderResolution, int) + CONFIG_PROPERTY(VSync, bool) + CONFIG_PROPERTY(PixelBufferSize, u64) + CONFIG_PROPERTY(AnisotropicFiltering, int) + CONFIG_PROPERTY(ThreadedRendering, bool) + .endNamespace() + + .beginNamespace("audio") + CONFIG_PROPERTY(DSPEnabled, bool) + CONFIG_PROPERTY(AudioBufferSize, int) + CONFIG_PROPERTY(AutoLatency, bool) + CONFIG_PROPERTY(AudioBackend, std::string) + CONFIG_PROPERTY(AudioVolume, int) + .endNamespace() + + .beginNamespace("advanced") + CONFIG_PROPERTY(DynarecEnabled, bool) + CONFIG_PROPERTY(DynarecIdleSkip, bool) + CONFIG_PROPERTY(SerialConsole, bool) + CONFIG_PROPERTY(SerialPTY, bool) + CONFIG_PROPERTY(UseReios, bool) + CONFIG_PROPERTY(FastGDRomLoad, bool) + CONFIG_PROPERTY(OpenGlChecks, bool) + .endNamespace() + + .beginNamespace("network") + CONFIG_PROPERTY(NetworkEnable, bool) + CONFIG_PROPERTY(ActAsServer, bool) + CONFIG_PROPERTY(DNS, std::string) + CONFIG_PROPERTY(NetworkServer, std::string) + CONFIG_PROPERTY(EmulateBBA, bool) + CONFIG_PROPERTY(GGPOEnable, bool) + CONFIG_PROPERTY(GGPODelay, int) + CONFIG_PROPERTY(NetworkStats, bool) + CONFIG_PROPERTY(GGPOAnalogAxes, int) + CONFIG_PROPERTY(GGPOPlayerNum, int) + .endNamespace() + + .beginNamespace("maple") + .addFunction("getDeviceType", getMapleType) + .addFunction("getSubDeviceType", getMapleSubType) + .addFunction("setDeviceType", setMapleType) + .addFunction("setSubDeviceType", setMapleSubType) + .endNamespace() + .endNamespace() + + .beginNamespace("memory") + .addFunction("read8", _vmem_readt) + .addFunction("read16", _vmem_readt) + .addFunction("read32", _vmem_readt) + .addFunction("read64", _vmem_readt) + .addFunction("readTable8", readMemoryTable) + .addFunction("readTable16", readMemoryTable) + .addFunction("readTable32", readMemoryTable) + .addFunction("readTable64", readMemoryTable) + .addFunction("write8", _vmem_writet) + .addFunction("write16", _vmem_writet) + .addFunction("write32", _vmem_writet) + .addFunction("write64", _vmem_writet) + .endNamespace() + + .beginNamespace("input") + .addFunction("getButtons", getButtons) + .addFunction("pressButtons", pressButtons) + .addFunction("releaseButtons", releaseButtons) + .addFunction("getAxis", getAxis) + .addFunction("setAxis", setAxis) + .addFunction("getAbsCoordinates", getAbsCoordinates) + .addFunction("setAbsCoordinates", setAbsCoordinates) + .addFunction("getRelCoordinates", getRelCoordinates) + .addFunction("setRelCoordinates", setRelCoordinates) + .endNamespace() + + .beginNamespace("state") + .addProperty("system", &settings.platform.system, false) + .addProperty("media", &settings.content.path, false) + .addProperty("gameId", &settings.content.gameId, false) + .beginNamespace("display") + .addProperty("width", &settings.display.width, false) + .addProperty("height", &settings.display.height, false) + .endNamespace() + .endNamespace() + .endNamespace(); +} + +static void doExec(const std::string& path) +{ + if (L == nullptr) + return; + DEBUG_LOG(COMMON, "Executing script: %s", path.c_str()); + int err = luaL_dofile(L, path.c_str()); + if (err != 0) + WARN_LOG(COMMON, "Lua error: %s", lua_tostring(L, -1)); +} + +void exec(const std::string& path) +{ + std::string file = get_readonly_config_path(path); + if (!file_exists(file)) + return; + doExec(file); +} + +void init() +{ + std::string initFile = get_readonly_config_path("flycast.lua"); + if (!file_exists(initFile)) + return; + L = luaL_newstate(); + luaL_openlibs(L); + luaRegister(L); + EventManager::listen(Event::Start, emuEventCallback); + EventManager::listen(Event::Resume, emuEventCallback); + EventManager::listen(Event::Pause, emuEventCallback); + EventManager::listen(Event::Terminate, emuEventCallback); + EventManager::listen(Event::LoadState, emuEventCallback); + + doExec(initFile); +} + +void term() +{ + if (L == nullptr) + return; + EventManager::unlisten(Event::Start, emuEventCallback); + EventManager::unlisten(Event::Resume, emuEventCallback); + EventManager::unlisten(Event::Pause, emuEventCallback); + EventManager::unlisten(Event::Terminate, emuEventCallback); + EventManager::unlisten(Event::LoadState, emuEventCallback); + lua_close(L); + L = nullptr; +} + +} +#endif diff --git a/core/lua/lua.h b/core/lua/lua.h new file mode 100644 index 000000000..c1fd243d1 --- /dev/null +++ b/core/lua/lua.h @@ -0,0 +1,39 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "build.h" +#include + +namespace lua +{ +#ifdef USE_LUA + +void init(); +void term(); +void exec(const std::string& path); +void vblank(); + +#else + +inline static void init() {} +inline static void term() {} +inline static void exec(const std::string& path) {} +inline static void vblank() {} + +#endif +} diff --git a/core/nullDC.cpp b/core/nullDC.cpp index d476212df..7cbf56270 100644 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -11,6 +11,7 @@ #include "archive/rzip.h" #include "rend/mainui.h" #include "input/gamepad_device.h" +#include "lua/lua.h" int flycast_init(int argc, char* argv[]) { @@ -47,6 +48,7 @@ int flycast_init(int argc, char* argv[]) os_SetupInput(); debugger::init(); + lua::init(); return 0; } @@ -71,6 +73,7 @@ void SaveSettings() void flycast_term() { gui_cancel_load(); + lua::term(); emu.term(); }