Initial re-upload of spice2x-24-08-24
This commit is contained in:
73
script/api/analogs.cpp
Normal file
73
script/api/analogs.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "analogs.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::analogs {
|
||||
typedef std::unordered_map<std::string, std::unordered_map<std::string, std::string>> analog_data_t;
|
||||
|
||||
static std::vector<Analog> *get_analogs() {
|
||||
static thread_local std::vector<Analog> *analogs = nullptr;
|
||||
if (!analogs) {
|
||||
if (!(analogs = games::get_analogs(eamuse_get_game()))) {
|
||||
static auto empty = std::vector<Analog>();
|
||||
return ∅
|
||||
}
|
||||
}
|
||||
return analogs;
|
||||
}
|
||||
|
||||
auto read() {
|
||||
analog_data_t ret;
|
||||
for (auto &analog : *get_analogs()) {
|
||||
auto &map = ret[analog.getName()];
|
||||
map["state"] = std::to_string(GameAPI::Analogs::getState(RI_MGR, analog));
|
||||
map["enabled"] = std::to_string(analog.override_enabled);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void write(analog_data_t data) {
|
||||
for (auto &[name, map] : data) {
|
||||
|
||||
// get state
|
||||
auto state_it = map.find("state");
|
||||
if (state_it == map.end()) continue;
|
||||
try {
|
||||
float state = std::stof(state_it->second);
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *get_analogs()) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||
analog.override_enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_reset(const std::string &name) {
|
||||
for (auto &analog : *get_analogs()) {
|
||||
if (name.empty() || analog.getName() == name) {
|
||||
analog.override_enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("analogs")
|
||||
.addFunction("read", read)
|
||||
.addFunction("write", write)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/analogs.h
Normal file
7
script/api/analogs.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::analogs {
|
||||
void init(lua_State *L);
|
||||
}
|
||||
82
script/api/buttons.cpp
Normal file
82
script/api/buttons.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "buttons.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::buttons {
|
||||
typedef std::unordered_map<std::string, std::unordered_map<std::string, std::string>> button_data_t;
|
||||
|
||||
static std::vector<Button> *get_buttons() {
|
||||
static thread_local std::vector<Button> *buttons = nullptr;
|
||||
if (!buttons) {
|
||||
if (!(buttons = games::get_buttons(eamuse_get_game()))) {
|
||||
static auto empty = std::vector<Button>();
|
||||
return ∅
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
auto read() {
|
||||
button_data_t ret;
|
||||
for (auto &button : *get_buttons()) {
|
||||
auto &map = ret[button.getName()];
|
||||
map["state"] = std::to_string(GameAPI::Buttons::getState(RI_MGR, button));
|
||||
map["enabled"] = std::to_string(button.override_enabled);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void write(button_data_t data) {
|
||||
for (auto &[name, map] : data) {
|
||||
|
||||
// get state
|
||||
auto state_it = map.find("state");
|
||||
if (state_it == map.end()) continue;
|
||||
try {
|
||||
float state;
|
||||
if (state_it->second == "true") {
|
||||
state = 1.f;
|
||||
} else if (state_it->second == "false") {
|
||||
state = 0.f;
|
||||
} else {
|
||||
state = std::stof(state_it->second);
|
||||
}
|
||||
|
||||
// find button
|
||||
for (auto &button : *get_buttons()) {
|
||||
if (button.getName() == name) {
|
||||
button.override_state = state > 0.f ?
|
||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||
button.override_enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_reset(const std::string &name) {
|
||||
for (auto &button : *get_buttons()) {
|
||||
if (name.empty() || button.getName() == name) {
|
||||
button.override_enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("buttons")
|
||||
.addFunction("read", read)
|
||||
.addFunction("write", write)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/buttons.h
Normal file
7
script/api/buttons.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::buttons {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
53
script/api/capture.cpp
Normal file
53
script/api/capture.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "capture.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/crypt.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::capture {
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
std::vector<int> get_screens() {
|
||||
|
||||
// get screen IDs
|
||||
std::vector<int> screens;
|
||||
graphics_screens_get(screens);
|
||||
return screens;
|
||||
}
|
||||
|
||||
std::string get_jpg(int screen, int quality, int divide) {
|
||||
CAPTURE_BUFFER.reserve(1024 * 128);
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
graphics_capture_trigger(screen);
|
||||
bool success = graphics_capture_receive_jpeg(screen, [] (uint8_t byte) {
|
||||
CAPTURE_BUFFER.push_back(byte);
|
||||
}, true, quality, true, divide, ×tamp, &width, &height);
|
||||
if (!success) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// encode to base64
|
||||
auto encoded = crypt::base64_encode(
|
||||
CAPTURE_BUFFER.data(),
|
||||
CAPTURE_BUFFER.size());
|
||||
|
||||
// clear buffer
|
||||
CAPTURE_BUFFER.clear();
|
||||
|
||||
// return base64
|
||||
return encoded;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("capture")
|
||||
.addFunction("get_screens", get_screens)
|
||||
.addFunction("get_jpg", get_jpg)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/capture.h
Normal file
7
script/api/capture.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::capture {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
28
script/api/card.cpp
Normal file
28
script/api/card.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "card.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::card {
|
||||
|
||||
void insert(int index, const std::string &card_hex) {
|
||||
|
||||
// convert to binary
|
||||
uint8_t card_bin[8] {};
|
||||
if (!hex2bin(card_hex.c_str(), card_bin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// insert card
|
||||
eamuse_card_insert(index & 1, card_bin);
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("card")
|
||||
.addFunction("insert", insert)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/card.h
Normal file
7
script/api/card.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::card {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
38
script/api/coin.cpp
Normal file
38
script/api/coin.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "coin.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::coin {
|
||||
|
||||
int get() {
|
||||
return eamuse_coin_get_stock();
|
||||
}
|
||||
|
||||
void set(int amount) {
|
||||
eamuse_coin_set_stock(amount);
|
||||
}
|
||||
|
||||
void insert(int amount) {
|
||||
if (amount == 1) {
|
||||
eamuse_coin_add();
|
||||
} else if (amount > 1) {
|
||||
eamuse_coin_set_stock(eamuse_coin_get_stock() + amount);
|
||||
}
|
||||
}
|
||||
|
||||
bool blocker_get() {
|
||||
return eamuse_coin_get_block();
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("coin")
|
||||
.addFunction("get", get)
|
||||
.addFunction("set", set)
|
||||
.addFunction("insert", insert)
|
||||
.addFunction("blocker_get", blocker_get)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/coin.h
Normal file
7
script/api/coin.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::coin {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
121
script/api/control.cpp
Normal file
121
script/api/control.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "control.h"
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include "external/LuaBridge.h"
|
||||
#include "launcher/shutdown.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::control {
|
||||
|
||||
struct SignalMapping {
|
||||
int signum;
|
||||
const char* name;
|
||||
};
|
||||
static SignalMapping SIGNAL_MAPPINGS[] = {
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
};
|
||||
|
||||
static inline bool acquire_shutdown_privs() {
|
||||
|
||||
// check if already acquired
|
||||
static bool acquired = false;
|
||||
if (acquired)
|
||||
return true;
|
||||
|
||||
// get process token
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// get the LUID for the shutdown privilege
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// get the shutdown privilege for this process
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
||||
|
||||
// check for error
|
||||
bool success = GetLastError() == ERROR_SUCCESS;
|
||||
if (success)
|
||||
acquired = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
void raise(const std::string &signal) {
|
||||
|
||||
// get signal
|
||||
int signal_val = -1;
|
||||
for (auto mapping : SIGNAL_MAPPINGS) {
|
||||
if (_stricmp(mapping.name, signal.c_str()) == 0) {
|
||||
signal_val = mapping.signum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// raise if found
|
||||
if (signal_val >= 0) {
|
||||
::raise(signal_val);
|
||||
}
|
||||
}
|
||||
|
||||
void exit(int code) {
|
||||
launcher::shutdown(code);
|
||||
}
|
||||
|
||||
void restart() {
|
||||
launcher::restart();
|
||||
}
|
||||
|
||||
bool shutdown() {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return false;
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_POWEROFF | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return false;
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reboot() {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return false;
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return false;
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("control")
|
||||
.addFunction("raise", raise)
|
||||
.addFunction("exit", exit)
|
||||
.addFunction("restart", restart)
|
||||
.addFunction("shutdown", shutdown)
|
||||
.addFunction("reboot", reboot)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/control.h
Normal file
7
script/api/control.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::control {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
21
script/api/drs.cpp
Normal file
21
script/api/drs.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "drs.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/drs/drs.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::drs {
|
||||
|
||||
std::string tapeled_get() {
|
||||
const size_t len = sizeof(games::drs::DRS_TAPELED);
|
||||
return bin2hex((uint8_t*) games::drs::DRS_TAPELED, len);
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("drs")
|
||||
.addFunction("tapeled_get", tapeled_get)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/drs.h
Normal file
7
script/api/drs.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::drs {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
46
script/api/iidx.cpp
Normal file
46
script/api/iidx.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "iidx.h"
|
||||
#include <cstring>
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::iidx {
|
||||
|
||||
// settings
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
|
||||
std::string ticker_get() {
|
||||
std::lock_guard<std::mutex> lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
return std::string(games::iidx::IIDXIO_LED_TICKER);
|
||||
}
|
||||
|
||||
void ticker_set(const std::string &text) {
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
|
||||
// set to read only
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = true;
|
||||
|
||||
// set led ticker
|
||||
memset(games::iidx::IIDXIO_LED_TICKER, ' ', TICKER_SIZE);
|
||||
for (size_t i = 0; i < TICKER_SIZE && i < text.size(); i++) {
|
||||
games::iidx::IIDXIO_LED_TICKER[i] = text[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ticker_reset() {
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("iidx")
|
||||
.addFunction("ticker_get", ticker_get)
|
||||
.addFunction("ticker_set", ticker_set)
|
||||
.addFunction("ticker_reset", ticker_reset)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/iidx.h
Normal file
7
script/api/iidx.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::iidx {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
68
script/api/info.cpp
Normal file
68
script/api/info.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "coin.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "avs/game.h"
|
||||
#include "avs/ea3.h"
|
||||
#include "build/defs.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::info {
|
||||
|
||||
auto avs() {
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
map["model"] = std::string(avs::game::MODEL);
|
||||
map["dest"] = std::string(avs::game::DEST);
|
||||
map["spec"] = std::string(avs::game::SPEC);
|
||||
map["rev"] = std::string(avs::game::REV);
|
||||
map["ext"] = std::string(avs::game::EXT);
|
||||
map["services"] = avs::ea3::EA3_BOOT_URL;
|
||||
return map;
|
||||
}
|
||||
|
||||
auto launcher() {
|
||||
|
||||
// build args
|
||||
std::string args;
|
||||
for (int count = 0; count < LAUNCHER_ARGC; count++) {
|
||||
auto arg = LAUNCHER_ARGV[count];
|
||||
if (count > 0) args += " ";
|
||||
args += arg;
|
||||
}
|
||||
|
||||
// get system time
|
||||
auto t_now = std::time(nullptr);
|
||||
auto tm_now = *std::gmtime(&t_now);
|
||||
auto tm_str = to_string(std::put_time(&tm_now, "%Y-%m-%dT%H:%M:%SZ"));
|
||||
|
||||
// build info
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
map["version"] = VERSION_STRING;
|
||||
map["compile_date"] = __DATE__;
|
||||
map["compile_time"] = __TIME__;
|
||||
map["system_time"] = tm_str.c_str();
|
||||
map["args"] = args;
|
||||
return map;
|
||||
}
|
||||
|
||||
auto memory() {
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
map["mem_total"] = memutils::mem_total();
|
||||
map["mem_total_used"] = memutils::mem_total_used();
|
||||
map["mem_used"] = memutils::mem_used();
|
||||
map["vmem_total"] = memutils::vmem_total();
|
||||
map["vmem_total_used"] = memutils::vmem_total_used();
|
||||
map["vmem_used"] = memutils::vmem_used();
|
||||
return map;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("info")
|
||||
.addFunction("avs", avs)
|
||||
.addFunction("launcher", launcher)
|
||||
.addFunction("memory", memory)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/info.h
Normal file
7
script/api/info.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::info {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
114
script/api/keypads.cpp
Normal file
114
script/api/keypads.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "keypads.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::keypads {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
void write(uint32_t keypad, const std::string &input) {
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
void set(uint32_t keypad, const std::string &keys) {
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
for (auto key : keys) {
|
||||
|
||||
// find mapping
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
std::string get(uint32_t keypad) {
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys
|
||||
std::string res = "";
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
res += std::to_string(mapping.character);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("keypads")
|
||||
.addFunction("write", write)
|
||||
.addFunction("set", set)
|
||||
.addFunction("get", get)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/keypads.h
Normal file
7
script/api/keypads.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::keypads {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
28
script/api/lcd.cpp
Normal file
28
script/api/lcd.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "lcd.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::lcd {
|
||||
|
||||
auto info() {
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
map["enabled"] = std::to_string(games::shared::LCD_ENABLED);
|
||||
map["csm"] = games::shared::LCD_CSM;
|
||||
map["bri"] = std::to_string(games::shared::LCD_BRI);
|
||||
map["con"] = std::to_string(games::shared::LCD_CON);
|
||||
map["bl"] = std::to_string(games::shared::LCD_BL);
|
||||
map["red"] = std::to_string(games::shared::LCD_RED);
|
||||
map["green"] = std::to_string(games::shared::LCD_GREEN);
|
||||
map["blue"] = std::to_string(games::shared::LCD_BLUE);
|
||||
return map;
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("lcd")
|
||||
.addFunction("info", info)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/lcd.h
Normal file
7
script/api/lcd.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::lcd {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
104
script/api/lights.cpp
Normal file
104
script/api/lights.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "lights.h"
|
||||
|
||||
#include <cfg/configurator.h>
|
||||
|
||||
#include "external/LuaBridge.h"
|
||||
#include "games/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "util/utils.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::lights {
|
||||
typedef std::unordered_map<std::string, std::unordered_map<std::string, std::string>> light_data_t;
|
||||
|
||||
static std::vector<Light> *get_lights() {
|
||||
static thread_local std::vector<Light> *lights = nullptr;
|
||||
if (!lights) {
|
||||
if (!(lights = games::get_lights(eamuse_get_game()))) {
|
||||
static auto empty = std::vector<Light>();
|
||||
return ∅
|
||||
}
|
||||
}
|
||||
return lights;
|
||||
}
|
||||
|
||||
auto read() {
|
||||
light_data_t ret;
|
||||
for (auto &light : *get_lights()) {
|
||||
auto &map = ret[light.getName()];
|
||||
map["state"] = std::to_string(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
map["enabled"] = std::to_string(light.override_enabled);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto read_original() {
|
||||
light_data_t ret;
|
||||
for (auto &light : *get_lights()) {
|
||||
auto &map = ret[light.getName()];
|
||||
map["state"] = std::to_string(light.last_state);
|
||||
map["enabled"] = std::to_string(light.override_enabled);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void write(light_data_t data) {
|
||||
for (auto &[name, map] : data) {
|
||||
|
||||
// get state
|
||||
auto state_it = map.find("state");
|
||||
if (state_it == map.end()) continue;
|
||||
try {
|
||||
float state = std::stof(state_it->second);
|
||||
|
||||
// find light
|
||||
for (auto &light : *get_lights()) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_reset(const std::string &name) {
|
||||
for (auto &light : *get_lights()) {
|
||||
if (name.empty() || light.getName() == name) {
|
||||
if (light.override_enabled) {
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, light.last_state);
|
||||
}
|
||||
light.override_enabled = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update() {
|
||||
RI_MGR->devices_flush_output();
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("lights")
|
||||
.addFunction("read", read)
|
||||
.addFunction("read_original", read_original)
|
||||
.addFunction("write", write)
|
||||
.addFunction("write_reset", write_reset)
|
||||
.addFunction("update", update)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/lights.h
Normal file
7
script/api/lights.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::lights {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
143
script/api/memory.cpp
Normal file
143
script/api/memory.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "memory.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::memory {
|
||||
|
||||
std::string read(const std::string &dll_name, intptr_t offset, uint64_t size) {
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
return bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
}
|
||||
|
||||
bool write(const std::string &dll_name, const std::string &data, intptr_t offset) {
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = data.length() / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data.c_str(), data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return false;
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t signature(const std::string &dll_name, const std::string &signature,
|
||||
const std::string &replacement, uint64_t offset, uint64_t usage) {
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// success
|
||||
return result;
|
||||
}
|
||||
|
||||
void init(lua_State *L, bool sandbox) {
|
||||
auto n = getGlobalNamespace(L).beginNamespace("memory")
|
||||
.addFunction("read", read);
|
||||
if (!sandbox) {
|
||||
n.addFunction("write", write)
|
||||
.addFunction("signature", signature)
|
||||
.endNamespace();
|
||||
} else {
|
||||
n.endNamespace();
|
||||
}
|
||||
}
|
||||
}
|
||||
7
script/api/memory.h
Normal file
7
script/api/memory.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::memory {
|
||||
void init(lua_State *n, bool sandbox);
|
||||
}
|
||||
67
script/api/touch.cpp
Normal file
67
script/api/touch.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "touch.h"
|
||||
#include "external/LuaBridge.h"
|
||||
#include "touch/touch.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
namespace script::api::touch {
|
||||
typedef std::vector<std::unordered_map<std::string, std::string>> touch_data_t;
|
||||
|
||||
touch_data_t read() {
|
||||
|
||||
// get touch points
|
||||
std::vector<TouchPoint> touch_points;
|
||||
touch_get_points(touch_points);
|
||||
|
||||
// add state for each touch point
|
||||
touch_data_t ret;
|
||||
for (auto &touch : touch_points) {
|
||||
auto &state = ret.emplace_back();
|
||||
state["id"] = std::to_string(touch.id);
|
||||
state["x"] = std::to_string(touch.x);
|
||||
state["y"] = std::to_string(touch.y);
|
||||
state["mouse"] = std::to_string(touch.mouse);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void write(touch_data_t data) {
|
||||
|
||||
// get all touch points
|
||||
std::vector<TouchPoint> touch_points;
|
||||
for (auto &state : data) {
|
||||
try {
|
||||
uint32_t touch_id = std::stoul(state["id"]);
|
||||
int32_t touch_x = std::stol(state["x"]);
|
||||
int32_t touch_y = std::stol(state["y"]);
|
||||
TouchPoint tp {
|
||||
.id = touch_id,
|
||||
.x = touch_x,
|
||||
.y = touch_y,
|
||||
.mouse = false,
|
||||
};
|
||||
touch_points.emplace_back(tp);
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
touch_write_points(&touch_points);
|
||||
}
|
||||
|
||||
void write_reset(uint32_t id) {
|
||||
std::vector<DWORD> touch_point_ids;
|
||||
touch_point_ids.push_back(id);
|
||||
touch_remove_points(&touch_point_ids);
|
||||
}
|
||||
|
||||
void init(lua_State *L) {
|
||||
getGlobalNamespace(L)
|
||||
.beginNamespace("touch")
|
||||
.addFunction("read", read)
|
||||
.addFunction("write", write)
|
||||
.addFunction("write_reset", write_reset)
|
||||
.endNamespace();
|
||||
}
|
||||
}
|
||||
7
script/api/touch.h
Normal file
7
script/api/touch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script::api::touch {
|
||||
void init(lua_State *n);
|
||||
}
|
||||
133
script/instance.cpp
Normal file
133
script/instance.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "instance.h"
|
||||
#include <lua.hpp>
|
||||
#include "util/logging.h"
|
||||
#include "lib.h"
|
||||
|
||||
namespace script {
|
||||
thread_local Instance *INSTANCE;
|
||||
|
||||
struct LuaData {
|
||||
lua_State *L = nullptr;
|
||||
|
||||
~LuaData() {
|
||||
if (L) {
|
||||
lua_close(L);
|
||||
L = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void lua_hook(lua_State *L, lua_Debug *data) {
|
||||
auto instance = INSTANCE;
|
||||
do {
|
||||
|
||||
// check if running
|
||||
if (!instance->is_running()) {
|
||||
luaL_error(L, "_stop_");
|
||||
return;
|
||||
}
|
||||
|
||||
// check if paused
|
||||
if (instance->is_paused()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
} while (instance->is_paused());
|
||||
}
|
||||
|
||||
Instance::Instance(const char *str, bool is_path, bool sandbox) {
|
||||
if (is_path) {
|
||||
log_misc("script", "loading {}", str);
|
||||
}
|
||||
|
||||
// create state
|
||||
lua = new LuaData();
|
||||
lua->L = luaL_newstate();
|
||||
if (!lua->L) {
|
||||
error = true;
|
||||
log_warning("script", "unable to create state");
|
||||
return;
|
||||
}
|
||||
|
||||
// load libraries
|
||||
load_libs(lua->L, sandbox);
|
||||
|
||||
// load script
|
||||
int err;
|
||||
if (is_path) {
|
||||
err = luaL_loadfile(lua->L, str);
|
||||
} else {
|
||||
err = luaL_loadstring(lua->L, str);
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (err != 0) {
|
||||
error = true;
|
||||
auto err_str = std::string(lua_tostring(lua->L, -1));
|
||||
if (is_path) {
|
||||
log_warning("script", "error loading '{}':\n{}", str, err_str);
|
||||
} else {
|
||||
log_warning("script", "error parsing:\n{}", err_str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// start script thread
|
||||
running = true;
|
||||
thread = new std::thread([this, str, is_path] {
|
||||
|
||||
// insert hooks
|
||||
INSTANCE = this;
|
||||
lua_sethook(lua->L, &lua_hook, LUA_MASKCOUNT, 1024);
|
||||
lua_sethook(lua->L, &lua_hook, LUA_MASKCALL, 1);
|
||||
lua_sethook(lua->L, &lua_hook, LUA_MASKRET, 1);
|
||||
|
||||
// call script
|
||||
auto err = lua_pcall(lua->L, 0, LUA_MULTRET, 0);
|
||||
|
||||
// display error
|
||||
if (err != 0) {
|
||||
auto err_str = std::string(lua_tostring(lua->L, -1));
|
||||
if (!err_str.ends_with("_stop_")) {
|
||||
error = true;
|
||||
if (is_path) {
|
||||
log_warning("script", "runtime error in '{}':\n{}", str, err_str);
|
||||
} else {
|
||||
log_warning("script", "runtime error:\n{}", err_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
running = false;
|
||||
});
|
||||
}
|
||||
|
||||
Instance::~Instance() {
|
||||
stop();
|
||||
delete lua;
|
||||
}
|
||||
|
||||
void Instance::join() {
|
||||
if (thread) {
|
||||
if (thread->joinable()) {
|
||||
thread->join();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(thread_m);
|
||||
delete thread;
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::stop() {
|
||||
running = false;
|
||||
join();
|
||||
}
|
||||
|
||||
void Instance::pause() {
|
||||
paused = true;
|
||||
}
|
||||
|
||||
void Instance::resume() {
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
39
script/instance.h
Normal file
39
script/instance.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
|
||||
namespace script {
|
||||
struct LuaData;
|
||||
|
||||
class Instance {
|
||||
private:
|
||||
LuaData *lua = nullptr;
|
||||
std::thread *thread = nullptr;
|
||||
std::mutex thread_m;
|
||||
bool running = false;
|
||||
bool paused = false;
|
||||
bool error = false;
|
||||
|
||||
public:
|
||||
|
||||
Instance(const char *str, bool is_path = true, bool sandbox = false);
|
||||
~Instance();
|
||||
|
||||
void join();
|
||||
void stop();
|
||||
void pause();
|
||||
void resume();
|
||||
|
||||
inline bool is_running() {
|
||||
return running;
|
||||
}
|
||||
|
||||
inline bool is_paused() {
|
||||
return paused;
|
||||
}
|
||||
};
|
||||
|
||||
extern thread_local Instance *INSTANCE;
|
||||
}
|
||||
155
script/lib.cpp
Normal file
155
script/lib.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "lib.h"
|
||||
#include <thread>
|
||||
#include <external/LuaBridge.h>
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "instance.h"
|
||||
#include "api/analogs.h"
|
||||
#include "api/buttons.h"
|
||||
#include "api/capture.h"
|
||||
#include "api/card.h"
|
||||
#include "api/coin.h"
|
||||
#include "api/control.h"
|
||||
#include "api/drs.h"
|
||||
#include "api/iidx.h"
|
||||
#include "api/info.h"
|
||||
#include "api/keypads.h"
|
||||
#include "api/lcd.h"
|
||||
#include "api/lights.h"
|
||||
#include "api/memory.h"
|
||||
#include "api/touch.h"
|
||||
|
||||
using namespace luabridge;
|
||||
|
||||
extern "C" const luaL_Reg *base_funcs_sandbox;
|
||||
namespace script {
|
||||
|
||||
LUAMOD_API int luaopen_base_sandbox(lua_State *L) {
|
||||
lua_pushglobaltable(L);
|
||||
luaL_setfuncs(L, base_funcs_sandbox, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, LUA_GNAME);
|
||||
lua_pushliteral(L, LUA_VERSION);
|
||||
lua_setfield(L, -2, "_VERSION");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg LUA_LIBRARIES[] = {
|
||||
{"_G", luaopen_base},
|
||||
{LUA_LOADLIBNAME, luaopen_package},
|
||||
{LUA_COLIBNAME, luaopen_coroutine},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_IOLIBNAME, luaopen_io},
|
||||
{LUA_OSLIBNAME, luaopen_os},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
#if defined(LUA_COMPAT_BITLIB)
|
||||
{LUA_BITLIBNAME, luaopen_bit32},
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const luaL_Reg LUA_SANDBOX[] = {
|
||||
{"_G", luaopen_base_sandbox},
|
||||
{LUA_COLIBNAME, luaopen_coroutine},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
#if defined(LUA_COMPAT_BITLIB)
|
||||
{LUA_BITLIBNAME, luaopen_bit32},
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
LuaRef require_sandbox(const std::string &path, lua_State *L) {
|
||||
luaL_error(L, "require not available in sandbox");
|
||||
return LuaRef(L);
|
||||
}
|
||||
|
||||
void sleep(double duration) {
|
||||
auto instance = script::INSTANCE;
|
||||
double cur_slept = 0.0;
|
||||
while (instance->is_running() && cur_slept < duration) {
|
||||
auto diff = std::min(0.02, duration);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(
|
||||
(uint64_t) (diff * 1000000ULL)));
|
||||
cur_slept += diff;
|
||||
}
|
||||
}
|
||||
|
||||
double time() {
|
||||
return get_performance_seconds();
|
||||
}
|
||||
|
||||
void yield() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
void msgbox(const std::string &s) {
|
||||
MessageBoxA(nullptr, s.c_str(), "Script", 0);
|
||||
}
|
||||
|
||||
void log_fatal_(const std::string &s) {
|
||||
log_fatal("script", "{}", s);
|
||||
}
|
||||
|
||||
void log_warning_(const std::string &s) {
|
||||
log_warning("script", "{}", s);
|
||||
}
|
||||
|
||||
void log_info_(const std::string &s) {
|
||||
log_info("script", "{}", s);
|
||||
}
|
||||
|
||||
void log_misc_(const std::string &s) {
|
||||
log_misc("script", "{}", s);
|
||||
}
|
||||
|
||||
uint16_t GetAsyncKeyState_(const int vKey) {
|
||||
return GetAsyncKeyState(vKey);
|
||||
}
|
||||
|
||||
void load_libs(lua_State *L, bool sandbox) {
|
||||
|
||||
// load STD libs
|
||||
for (const luaL_Reg *lib = sandbox ? LUA_SANDBOX : LUA_LIBRARIES; lib->func; ++lib) {
|
||||
luaL_requiref(L, lib->name, lib->func, 1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// add base functions
|
||||
auto G = getGlobalNamespace(L);
|
||||
G.addFunction("sleep", sleep);
|
||||
G.addFunction("time", time);
|
||||
G.addFunction("yield", yield);
|
||||
G.addFunction("msgbox", msgbox);
|
||||
G.addFunction("log_fatal", log_fatal_);
|
||||
G.addFunction("log_warning", log_warning_);
|
||||
G.addFunction("log_info", log_info_);
|
||||
G.addFunction("log_misc", log_misc_);
|
||||
if (sandbox) {
|
||||
G.addFunction("require", require_sandbox);
|
||||
} else {
|
||||
G.addFunction("GetAsyncKeyState", GetAsyncKeyState_);
|
||||
}
|
||||
|
||||
// add API modules
|
||||
api::analogs::init(L);
|
||||
api::buttons::init(L);
|
||||
api::capture::init(L);
|
||||
api::card::init(L);
|
||||
api::coin::init(L);
|
||||
api::control::init(L);
|
||||
api::drs::init(L);
|
||||
api::iidx::init(L);
|
||||
api::info::init(L);
|
||||
api::keypads::init(L);
|
||||
api::lcd::init(L);
|
||||
api::lights::init(L);
|
||||
api::memory::init(L, sandbox);
|
||||
api::touch::init(L);
|
||||
}
|
||||
}
|
||||
7
script/lib.h
Normal file
7
script/lib.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace script {
|
||||
|
||||
void load_libs(lua_State *L, bool sandbox = false);
|
||||
}
|
||||
122
script/manager.cpp
Normal file
122
script/manager.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "manager.h"
|
||||
#include <thread>
|
||||
#include "util/fileutils.h"
|
||||
#include "script/instance.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "launcher/options.h"
|
||||
#include "games/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace script {
|
||||
|
||||
// globals
|
||||
static std::string DIRECTORY = "script";
|
||||
static std::vector<std::string> BOOT_LIST;
|
||||
static std::vector<std::string> CONFIG_LIST;
|
||||
static std::vector<std::string> SHUTDOWN_LIST;
|
||||
static std::vector<std::shared_ptr<Instance>> INSTANCES;
|
||||
static std::mutex INSTANCES_M;
|
||||
static bool BOOT = false;
|
||||
static bool CONFIG = false;
|
||||
static bool SHUTDOWN = false;
|
||||
|
||||
void manager_scan() {
|
||||
|
||||
// clear lists
|
||||
BOOT_LIST.clear();
|
||||
|
||||
// scan script directory
|
||||
std::vector<std::string> files;
|
||||
fileutils::dir_scan(DIRECTORY, files, true);
|
||||
for (auto &file : files) {
|
||||
if (file.ends_with(".lua")) {
|
||||
auto name = file.substr(file.find_last_of("/\\") + 1);
|
||||
if (name.starts_with("boot_")) {
|
||||
BOOT_LIST.push_back(file);
|
||||
}
|
||||
if (name.starts_with("shutdown_")) {
|
||||
SHUTDOWN_LIST.push_back(file);
|
||||
}
|
||||
if (name.starts_with("config_")) {
|
||||
CONFIG_LIST.push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void manager_add(std::shared_ptr<Instance> instance) {
|
||||
std::lock_guard<std::mutex> lock(INSTANCES_M);
|
||||
auto it = INSTANCES.begin();
|
||||
while (it != INSTANCES.end()) {
|
||||
if (!(*it)->is_running()) {
|
||||
it = INSTANCES.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
INSTANCES.emplace_back(instance);
|
||||
}
|
||||
|
||||
void manager_boot() {
|
||||
if (BOOT) return;
|
||||
BOOT = true;
|
||||
|
||||
// game options
|
||||
auto &options = *games::get_options(eamuse_get_game());
|
||||
for (auto &value : options[launcher::Options::ExecuteScript].values()) {
|
||||
if (fileutils::file_exists(value)) {
|
||||
manager_add(std::make_shared<Instance>(value.c_str(), true, false));
|
||||
} else {
|
||||
log_warning("script", "unable to locate {}", value);
|
||||
}
|
||||
}
|
||||
|
||||
// run boot scripts
|
||||
for (auto &file : BOOT_LIST) {
|
||||
manager_add(std::make_shared<Instance>(file.c_str(), true, false));
|
||||
}
|
||||
}
|
||||
|
||||
void manager_config() {
|
||||
if (CONFIG) return;
|
||||
CONFIG = true;
|
||||
|
||||
// launcher options
|
||||
for (auto &value : LAUNCHER_OPTIONS->at(launcher::Options::ExecuteScript).values()) {
|
||||
if (fileutils::file_exists(value)) {
|
||||
manager_add(std::make_shared<Instance>(value.c_str(), true, false));
|
||||
} else {
|
||||
log_warning("script", "unable to locate {}", value);
|
||||
}
|
||||
}
|
||||
|
||||
// run config scripts
|
||||
for (auto &file : CONFIG_LIST) {
|
||||
manager_add(std::make_shared<Instance>(file.c_str(), true, false));
|
||||
}
|
||||
}
|
||||
|
||||
void manager_shutdown() {
|
||||
if (SHUTDOWN) return;
|
||||
SHUTDOWN = true;
|
||||
|
||||
// stop running scripts
|
||||
INSTANCES_M.lock();
|
||||
INSTANCES.clear();
|
||||
INSTANCES_M.unlock();
|
||||
|
||||
// run shutdown scripts
|
||||
std::vector<std::shared_ptr<Instance>> join_list;
|
||||
for (auto &file : SHUTDOWN_LIST) {
|
||||
auto instance = std::make_shared<Instance>(file.c_str(), true, false);
|
||||
join_list.emplace_back(instance);
|
||||
manager_add(instance);
|
||||
}
|
||||
|
||||
// wait for completion
|
||||
for (auto &instance : join_list) {
|
||||
instance->join();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
script/manager.h
Normal file
13
script/manager.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace script {
|
||||
class Instance;
|
||||
|
||||
void manager_scan();
|
||||
void manager_add(std::shared_ptr<Instance> instance);
|
||||
void manager_boot();
|
||||
void manager_config();
|
||||
void manager_shutdown();
|
||||
}
|
||||
Reference in New Issue
Block a user