Update to spice2x-25-04-25 (pre-apply)
> broken commit
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
namespace clipboard {
|
||||
namespace imports {
|
||||
@@ -140,4 +141,45 @@ namespace clipboard {
|
||||
|
||||
handle.detach();
|
||||
}
|
||||
|
||||
void copy_text(const std::string& str) {
|
||||
if (!OpenClipboard(nullptr)) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, str.length() + 1);
|
||||
memcpy(GlobalLock(mem), str.c_str(), str.length() + 1);
|
||||
GlobalUnlock(mem);
|
||||
|
||||
EmptyClipboard();
|
||||
SetClipboardData(CF_TEXT, mem);
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
const std::string paste_text() {
|
||||
HGLOBAL hglb;
|
||||
LPSTR str;
|
||||
std::string text;
|
||||
|
||||
// check if clipboard content is text
|
||||
if (!IsClipboardFormatAvailable(CF_TEXT)) {
|
||||
return text;
|
||||
}
|
||||
if (!OpenClipboard(nullptr)) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return text;
|
||||
}
|
||||
|
||||
hglb = GetClipboardData(CF_TEXT);
|
||||
if (hglb != NULL) {
|
||||
str = reinterpret_cast<LPSTR>(GlobalLock(hglb));
|
||||
if (str != NULL) {
|
||||
text = str;
|
||||
GlobalUnlock(hglb);
|
||||
}
|
||||
}
|
||||
CloseClipboard();
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
|
||||
namespace clipboard {
|
||||
void copy_image(const std::filesystem::path path);
|
||||
void copy_text(const std::string& str);
|
||||
const std::string paste_text();
|
||||
}
|
||||
|
||||
+24
-6
@@ -32,8 +32,9 @@ static int DEVICE_INPUT_STATE;
|
||||
static int __cdecl device_check_secplug(int a1) {
|
||||
|
||||
// check for invalid index
|
||||
if (a1 > 1)
|
||||
if (a1 > 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// J44/K44 has it flipped
|
||||
if (avs::game::is_model({ "J44", "K44" })) {
|
||||
@@ -295,6 +296,9 @@ static int __cdecl device_set_coincounter_work() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cdecl device_set_coincounter_controllable() {
|
||||
}
|
||||
|
||||
static int __cdecl device_set_panel_mode(int mode) {
|
||||
return 0;
|
||||
}
|
||||
@@ -348,6 +352,10 @@ static int __cdecl device_set_watchdog_timer(int a1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cdecl device_set_watchdog() {
|
||||
return;
|
||||
}
|
||||
|
||||
static void __cdecl device_update() {
|
||||
|
||||
// flush device output
|
||||
@@ -609,7 +617,9 @@ void spicedevice_attach() {
|
||||
detour::inline_hook((void *) device_get_coinstock, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_coinstock", "?device_get_coinstock@@YAXPEAG0@Z"}));
|
||||
detour::inline_hook((void *) device_get_coinstock_all, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_coinstock_all", "?device_get_coinstock_all@@YAXPEAG0@Z"}));
|
||||
DEVICE_INSTANCE, {"device_get_coinstock_all",
|
||||
"?device_get_coinstock_all@@YAXPEAG0@Z",
|
||||
"?device_get_coinstock_all@@YAXPAG0@Z"}));
|
||||
detour::inline_hook((void *) device_get_dispw, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_dispw", "?device_get_dipsw@@YAEH@Z"}));
|
||||
detour::inline_hook((void *) device_get_input, libutils::try_proc_list(
|
||||
@@ -620,7 +630,8 @@ void spicedevice_attach() {
|
||||
DEVICE_INSTANCE, {"device_get_jamma"}));
|
||||
detour::inline_hook((void *) device_get_jamma_history, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_jamma_history",
|
||||
"?device_get_jamma_history@@YAHPEAUT_JAMMA_HISTORY_INFO@@H@Z"}));
|
||||
"?device_get_jamma_history@@YAHPEAUT_JAMMA_HISTORY_INFO@@H@Z",
|
||||
"?device_get_jamma_history@@YAHPAUT_JAMMA_HISTORY_INFO@@H@Z"}));
|
||||
detour::inline_hook((void *) device_get_panel_trg_off, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_panel_trg_off"}));
|
||||
detour::inline_hook((void *) device_get_panel_trg_on, libutils::try_proc_list(
|
||||
@@ -640,11 +651,15 @@ void spicedevice_attach() {
|
||||
detour::inline_hook((void *) device_get_status, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_status", "?device_get_status@@YAHXZ"}));
|
||||
detour::inline_hook((void *) device_get_subboard_version, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_subboard_version", "?device_get_subboard_version@@YAHPEADH@Z"}));
|
||||
DEVICE_INSTANCE, {"device_get_subboard_version",
|
||||
"?device_get_subboard_version@@YAHPEADH@Z",
|
||||
"?device_get_subboard_version@@YAHPADH@Z"}));
|
||||
detour::inline_hook((void *) device_get_sys_version, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_get_sys_version", "?device_get_sys_version@@YAPEBDXZ"}));
|
||||
detour::inline_hook((void *) device_initialize, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_initialize", "?device_initialize@@YAHH@Z"}));
|
||||
DEVICE_INSTANCE, {"device_initialize",
|
||||
"?device_initialize@@YAHH@Z",
|
||||
"?device_initialize@@YAHXZ"}));
|
||||
detour::inline_hook((void *) device_is_initialized, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_is_initialized"}));
|
||||
detour::inline_hook((void *) device_poweroff, libutils::try_proc_list(
|
||||
@@ -657,6 +672,8 @@ void spicedevice_attach() {
|
||||
DEVICE_INSTANCE, {"device_set_coincounter_merge", "?device_set_coincounter_merge@@YAXE@Z"}));
|
||||
detour::inline_hook((void *) device_set_coincounter_work, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_set_coincounter_work", "?device_set_coincounter_work@@YAXEE@Z"}));
|
||||
detour::inline_hook((void *) device_set_coincounter_controllable, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_set_coincounter_controllable", "?device_set_coincounter_controllable@@YAXEE@Z"}));
|
||||
detour::inline_hook((void *) device_set_panel_mode, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_set_panel_mode"}));
|
||||
detour::inline_hook((void *) device_set_jamma_asyncmode, libutils::try_proc_list(
|
||||
@@ -671,6 +688,8 @@ void spicedevice_attach() {
|
||||
DEVICE_INSTANCE, {"device_set_portoutbit", "?device_set_portoutbit@@YAXHH@Z"}));
|
||||
detour::inline_hook((void *) device_set_watchdog_timer, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_set_watchdog_timer"}));
|
||||
detour::inline_hook((void *) device_set_watchdog, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"?device_set_watchdog@@YAXH@Z"}));
|
||||
detour::inline_hook((void *) device_update, libutils::try_proc_list(
|
||||
DEVICE_INSTANCE, {"device_update", "?device_update@@YAXXZ"}));
|
||||
detour::inline_hook((void *) device_update_secplug, libutils::try_proc_list(
|
||||
@@ -711,7 +730,6 @@ void spicedevice_attach() {
|
||||
// device_hwinfo_etc_update
|
||||
// device_hwinfo_get
|
||||
// device_hwinfo_update
|
||||
// device_set_coincounter_controllable
|
||||
// device_set_jamma_interrupt_status
|
||||
// sci_mng_get_debug_info
|
||||
// test_device
|
||||
|
||||
+182
-20
@@ -11,6 +11,7 @@
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
#include "overlay/overlay.h"
|
||||
|
||||
#include "bt5api.h"
|
||||
|
||||
@@ -32,16 +33,40 @@ static uint16_t KEYPAD_STATE_OVERRIDES_OVERLAY[] = {0, 0};
|
||||
static std::string EAMUSE_GAME_NAME;
|
||||
static ConfigKeypadBindings KEYPAD_BINDINGS {};
|
||||
|
||||
// auto card
|
||||
bool AUTO_INSERT_CARD[2] = {false, false};
|
||||
float AUTO_INSERT_CARD_COOLDOWN = 8.f; // seconds
|
||||
static std::optional<double> AUTO_INSERT_CARD_FIRST_CONSUME_TIME;
|
||||
static bool AUTO_INSERT_CARD_CACHED[2];
|
||||
static uint8_t AUTO_INSERT_CARD_CACHED_DATA[2][8];
|
||||
|
||||
// pin macro
|
||||
bool PIN_MACRO_ENABLED = false;
|
||||
std::string PIN_MACRO_VALUES[2] = {"", ""};
|
||||
static std::thread *PIN_MACRO_THREAD = nullptr;
|
||||
static bool PIN_MACRO_THREAD_ACTIVE = false;
|
||||
static uint16_t PIN_MACRO_TRIGGER_KEYS[2] = {
|
||||
games::OverlayButtons::TriggerPinMacroP1,
|
||||
games::OverlayButtons::TriggerPinMacroP2
|
||||
};
|
||||
|
||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||
|
||||
// get unit index
|
||||
int index = unit_id > 0 && active_count > 1 ? 1 : 0;
|
||||
|
||||
// check cards cached for auto-insert
|
||||
// explicitly not logging anything in this path to avoid log spam
|
||||
if (AUTO_INSERT_CARD[index] && AUTO_INSERT_CARD_CACHED[index]) {
|
||||
memcpy(card, AUTO_INSERT_CARD_CACHED_DATA[index], 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
// reader card input
|
||||
if (CARD_INSERT_UID_ENABLE[index]) {
|
||||
CARD_INSERT_UID_ENABLE[index] = false;
|
||||
memcpy(card, CARD_INSERT_UID[index], 8);
|
||||
log_info("eamuse", "Inserted card from reader {}: {}", index, bin2hex(card, 8));
|
||||
log_info("eamuse", "[P{}] Inserted card from reader: {}", index+1, bin2hex(card, 8));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,38 +83,50 @@ bool eamuse_get_card(int active_count, int unit_id, uint8_t *card) {
|
||||
}
|
||||
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int index) {
|
||||
|
||||
// do a quick copy under lock
|
||||
std::unique_lock<std::mutex> lock(CARD_OVERRIDES_LOCK);
|
||||
const auto card_override = CARD_OVERRIDES[index];
|
||||
lock.unlock();
|
||||
|
||||
// Check if card overrides are present
|
||||
if (!CARD_OVERRIDES[index].empty()) {
|
||||
if (!card_override.empty()) {
|
||||
|
||||
// Override is present
|
||||
for (int n = 0; n < 16; n++) {
|
||||
char c = CARD_OVERRIDES[index].c_str()[n];
|
||||
char c = card_override.c_str()[n];
|
||||
bool digit = c >= '0' && c <= '9';
|
||||
bool character_big = c >= 'A' && c <= 'F';
|
||||
bool character_small = c >= 'a' && c <= 'f';
|
||||
if (!digit && !character_big && !character_small) {
|
||||
log_warning("eamuse",
|
||||
"{} card override contains an invalid character sequence at byte {} (16 characters, 0-9/A-F only)",
|
||||
CARD_OVERRIDES[index], n);
|
||||
card_override, n);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Log info
|
||||
log_info("eamuse", "Inserted card override: {}", CARD_OVERRIDES[index]);
|
||||
log_info("eamuse", "[P{}] Inserted card override: {}", index+1, card_override);
|
||||
|
||||
// Card is valid, convert and set it.
|
||||
hex2bin(CARD_OVERRIDES[index].c_str(), card);
|
||||
hex2bin(card_override.c_str(), card);
|
||||
|
||||
// cache it for auto-insert
|
||||
// always overwrite from overrides since override may have changed by user in card manager
|
||||
if (AUTO_INSERT_CARD[index]) {
|
||||
memcpy(AUTO_INSERT_CARD_CACHED_DATA[index], card, 8);
|
||||
AUTO_INSERT_CARD_CACHED[index] = true;
|
||||
log_info("eamuse", "[P{}] Auto card insert - caching this card override in memory: {}", index+1, card_override);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
// Overrides are not present. Use the standard file reading method.
|
||||
return eamuse_get_card(path, card);
|
||||
return eamuse_get_card_from_file(path, card, index);
|
||||
}
|
||||
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card) {
|
||||
bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card, int index) {
|
||||
|
||||
// open file
|
||||
std::ifstream f(path);
|
||||
@@ -130,11 +167,18 @@ bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card) {
|
||||
}
|
||||
|
||||
// info
|
||||
log_info("eamuse", "Inserted {}: {}", path.string(), buffer);
|
||||
log_info("eamuse", "[P{}] Inserted {}: {}", index+1, path.string(), buffer);
|
||||
|
||||
// convert hex to bytes
|
||||
hex2bin(buffer, card);
|
||||
|
||||
// cache it for auto-insert
|
||||
if (AUTO_INSERT_CARD[index] && !AUTO_INSERT_CARD_CACHED[index]) {
|
||||
memcpy(AUTO_INSERT_CARD_CACHED_DATA[index], card, 8);
|
||||
AUTO_INSERT_CARD_CACHED[index] = true;
|
||||
log_info("eamuse", "[P{}] Auto card insert - caching this card from file in memory: {}", index+1, buffer);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
@@ -161,12 +205,29 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
bt5api_poll_reader_card((uint8_t) index);
|
||||
}
|
||||
|
||||
// auto insert card
|
||||
if (AUTO_INSERT_CARD[index]) {
|
||||
// reset timer if enough time has passed since last insert
|
||||
if (CARD_INSERT[index] &&
|
||||
AUTO_INSERT_CARD_COOLDOWN < (get_performance_seconds() - CARD_INSERT_TIME[index])) {
|
||||
CARD_INSERT[index] = false;
|
||||
}
|
||||
|
||||
if (!CARD_INSERT[index]) {
|
||||
eamuse_card_insert(index);
|
||||
// not logging anything here to prevent spam
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check for card insert
|
||||
auto keypad_buttons = games::get_buttons_keypads(eamuse_get_game());
|
||||
auto offset = unit_id * games::KeypadButtons::Size;
|
||||
if ((CARD_INSERT[index] && fabs(get_performance_seconds() - CARD_INSERT_TIME[index]) < CARD_INSERT_TIMEOUT)
|
||||
|| GameAPI::Buttons::getState(RI_MGR, keypad_buttons->at(games::KeypadButtons::InsertCard + offset))) {
|
||||
log_info("eamuse", "Card insert on {}/{}", unit_id + 1, active_count);
|
||||
|| GameAPI::Buttons::getState(RI_MGR, keypad_buttons->at(games::KeypadButtons::InsertCard + offset))) {
|
||||
log_info("eamuse", "[P{}] Card insert on reader (total active count: {})", unit_id+1, active_count);
|
||||
CARD_INSERT[index] = false;
|
||||
return true;
|
||||
}
|
||||
@@ -249,6 +310,93 @@ void eamuse_coin_stop_thread() {
|
||||
COIN_INPUT_THREAD = nullptr;
|
||||
}
|
||||
|
||||
void eamuse_pin_macro_start_thread() {
|
||||
|
||||
// set active
|
||||
PIN_MACRO_THREAD_ACTIVE = true;
|
||||
|
||||
// create thread
|
||||
PIN_MACRO_THREAD = new std::thread([]() {
|
||||
uint16_t keypad_overrides[] = {
|
||||
1 << EAM_IO_KEYPAD_0,
|
||||
1 << EAM_IO_KEYPAD_1,
|
||||
1 << EAM_IO_KEYPAD_2,
|
||||
1 << EAM_IO_KEYPAD_3,
|
||||
1 << EAM_IO_KEYPAD_4,
|
||||
1 << EAM_IO_KEYPAD_5,
|
||||
1 << EAM_IO_KEYPAD_6,
|
||||
1 << EAM_IO_KEYPAD_7,
|
||||
1 << EAM_IO_KEYPAD_8,
|
||||
1 << EAM_IO_KEYPAD_9,
|
||||
};
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
size_t pin_index[2] = {PIN_MACRO_VALUES[0].length(), PIN_MACRO_VALUES[1].length()};
|
||||
|
||||
std::optional<uint8_t> active_unit = std::nullopt;
|
||||
|
||||
while (PIN_MACRO_THREAD_ACTIVE) {
|
||||
// wait for key press
|
||||
if (!active_unit.has_value()) {
|
||||
for (int unit = 0; unit < 2; unit++) {
|
||||
if (PIN_MACRO_VALUES[unit].empty()) {
|
||||
continue;
|
||||
}
|
||||
if (overlay_buttons &&
|
||||
(!overlay::OVERLAY || overlay::OVERLAY->hotkeys_triggered()) &&
|
||||
GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(PIN_MACRO_TRIGGER_KEYS[unit]))) {
|
||||
active_unit = unit;
|
||||
// Reset key index
|
||||
pin_index[unit] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!active_unit.has_value()) {
|
||||
Sleep(20);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const auto unit = active_unit.value();
|
||||
// get character from config
|
||||
if (pin_index[unit] < PIN_MACRO_VALUES[unit].length()) {
|
||||
|
||||
// insert character
|
||||
char pin_char = PIN_MACRO_VALUES[unit].at(pin_index[unit]);
|
||||
if (pin_char >= '0' && pin_char <= '9') {
|
||||
int char_index = pin_char - '0';
|
||||
eamuse_set_keypad_overrides(unit, keypad_overrides[char_index]);
|
||||
}
|
||||
pin_index[unit]++;
|
||||
Sleep(100);
|
||||
|
||||
// clear
|
||||
eamuse_set_keypad_overrides(unit, 0);
|
||||
Sleep(50);
|
||||
|
||||
// end of PIN
|
||||
if (pin_index[unit] == PIN_MACRO_VALUES[unit].length()) {
|
||||
active_unit = std::nullopt;
|
||||
Sleep(120);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Sleep(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void eamuse_pin_macro_stop_thread() {
|
||||
PIN_MACRO_THREAD_ACTIVE = false;
|
||||
if (PIN_MACRO_THREAD != nullptr) {
|
||||
PIN_MACRO_THREAD->join();
|
||||
delete PIN_MACRO_THREAD;
|
||||
PIN_MACRO_THREAD = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state) {
|
||||
|
||||
// check unit
|
||||
@@ -300,13 +448,6 @@ uint16_t eamuse_get_keypad_state(size_t unit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// keypad disabled for KFC in BI2X mode, since the game won't handle it correctly
|
||||
// this check for IO is necessary (as opposed to just doing a check for Valkyrie cab mode)
|
||||
// because there are hex edits that allow you to use legacy (KFC/BIO2) IO while in Valk mode.
|
||||
if (avs::game::is_model("KFC") && games::sdvx::BI2X_INITIALIZED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reset
|
||||
KEYPAD_STATE[unit] = KEYPAD_STATE_OVERRIDES[unit];
|
||||
KEYPAD_STATE[unit] |= KEYPAD_STATE_OVERRIDES_BT5[unit];
|
||||
@@ -512,6 +653,12 @@ void eamuse_autodetect_game() {
|
||||
eamuse_set_game("Ongaku Paradise");
|
||||
else if (avs::game::is_model("TBS"))
|
||||
eamuse_set_game("Busou Shinki: Armored Princess Battle Conductor");
|
||||
else if (avs::game::is_model("UJK"))
|
||||
eamuse_set_game("Chase Chase Jokers");
|
||||
else if (avs::game::is_model("UKS"))
|
||||
eamuse_set_game("QuizKnock STADIUM");
|
||||
else if (avs::game::is_model("VFG"))
|
||||
eamuse_set_game("Mahjong Fight Girl");
|
||||
else {
|
||||
log_warning("eamuse", "unknown game model: {}", avs::game::MODEL);
|
||||
eamuse_set_game("unknown");
|
||||
@@ -519,7 +666,22 @@ void eamuse_autodetect_game() {
|
||||
}
|
||||
|
||||
void eamuse_scard_callback(uint8_t slot_no, card_info_t *cardinfo) {
|
||||
log_misc("scard", "eamuse_scard_callback: slot_no={}, card_type={}", slot_no, cardinfo->card_type);
|
||||
if (cardinfo->card_type > 0) {
|
||||
eamuse_card_insert(slot_no, cardinfo->uid);
|
||||
eamuse_card_insert(slot_no & 1, cardinfo->uid);
|
||||
}
|
||||
}
|
||||
|
||||
bool eamuse_scanned_card_peek_noninvasive(size_t unit, char card_uid[8]) {
|
||||
const int unit_index = unit > 0 ? 1 : 0;
|
||||
if (!CARD_INSERT_UID_ENABLE[unit_index]) {
|
||||
return false;
|
||||
}
|
||||
memcpy(card_uid, CARD_INSERT_UID[unit_index], 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
void eamuse_scanned_card_clear(size_t unit) {
|
||||
const int unit_index = unit > 0 ? 1 : 0;
|
||||
CARD_INSERT_UID_ENABLE[unit_index] = false;
|
||||
}
|
||||
|
||||
+15
-1
@@ -3,6 +3,8 @@
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/scard/scard.h"
|
||||
|
||||
@@ -29,11 +31,17 @@ enum eam_io_keypad_scan_code {
|
||||
EAM_IO_INSERT = 13, /* SpiceTools Extension */
|
||||
};
|
||||
|
||||
extern std::mutex CARD_OVERRIDES_LOCK;
|
||||
extern std::string CARD_OVERRIDES[2];
|
||||
extern bool AUTO_INSERT_CARD[2];
|
||||
extern float AUTO_INSERT_CARD_COOLDOWN;
|
||||
|
||||
extern bool PIN_MACRO_ENABLED;
|
||||
extern std::string PIN_MACRO_VALUES[2];
|
||||
|
||||
bool eamuse_get_card(int active_count, int unit_id, uint8_t *card);
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card, int unit_id);
|
||||
bool eamuse_get_card(const std::filesystem::path &path, uint8_t *card);
|
||||
bool eamuse_get_card_from_file(const std::filesystem::path &path, uint8_t *card, int index);
|
||||
|
||||
void eamuse_card_insert(int unit);
|
||||
void eamuse_card_insert(int unit, const uint8_t *card);
|
||||
@@ -53,6 +61,9 @@ int eamuse_coin_add();
|
||||
void eamuse_coin_start_thread();
|
||||
void eamuse_coin_stop_thread();
|
||||
|
||||
void eamuse_pin_macro_start_thread();
|
||||
void eamuse_pin_macro_stop_thread();
|
||||
|
||||
void eamuse_set_keypad_overrides(size_t unit, uint16_t keypad_state);
|
||||
void eamuse_set_keypad_overrides_bt5(size_t unit, uint16_t keypad_state);
|
||||
void eamuse_set_keypad_overrides_reader(size_t unit, uint16_t keypad_state);
|
||||
@@ -73,3 +84,6 @@ int eamuse_get_game_keypads_name();
|
||||
void eamuse_autodetect_game();
|
||||
|
||||
void eamuse_scard_callback(uint8_t slot_no, card_info_t *cardinfo);
|
||||
|
||||
bool eamuse_scanned_card_peek_noninvasive(size_t unit, char card_uid[8]);
|
||||
void eamuse_scanned_card_clear(size_t unit);
|
||||
|
||||
+277
-20
@@ -1,6 +1,7 @@
|
||||
#include "extdev.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <windows.h>
|
||||
#include "util/utils.h"
|
||||
#include "util/detour.h"
|
||||
@@ -9,11 +10,18 @@
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/api.h"
|
||||
#include "acio/icca/icca.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/gitadora/io.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
#if SPICE64
|
||||
typedef uint64_t LED_INDEX;
|
||||
#else
|
||||
typedef uint32_t LED_INDEX;
|
||||
#endif
|
||||
|
||||
// card unit
|
||||
static size_t EXTDEV_CARDUNIT_COUNT = 0;
|
||||
static circular_buffer<int> EXTDEV_CARDUNIT_KEY[2] = {
|
||||
@@ -65,8 +73,14 @@ static std::string EXTDEV_INSTANCE_NAMES[] = {
|
||||
"libcardunit.dll",
|
||||
"libledunit.dll",
|
||||
"libgfdm_unit2.dll",
|
||||
"libshare-pj.dll",
|
||||
};
|
||||
|
||||
static void set_led_state(games::gitadora::Lights::gitadora_lights_t light, bool led_on);
|
||||
static void set_led_value(games::gitadora::Lights::gitadora_lights_t light, uint8_t value);
|
||||
|
||||
static long __cdecl gitadora_led_common(long index, uint8_t *data);
|
||||
|
||||
static void __cdecl cardunit_boot2(int a1, int a2, int a3) {
|
||||
|
||||
// default reader count to 1
|
||||
@@ -326,9 +340,9 @@ static void *__cdecl gfdm_unit_get_button_p(void *a1, int a2, size_t player) {
|
||||
memset(a1, 0, 64);
|
||||
|
||||
// irrelevant for drummania
|
||||
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B'))
|
||||
if (games::gitadora::is_drum()) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::gitadora::get_buttons();
|
||||
@@ -414,8 +428,7 @@ static long __cdecl gfdm_unit_get_input_p(int device, size_t player) {
|
||||
auto &analogs = games::gitadora::get_analogs();
|
||||
|
||||
// drum mania
|
||||
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B')) {
|
||||
if (games::gitadora::is_drum()) {
|
||||
|
||||
// we don't want input for this
|
||||
if (device == 1)
|
||||
@@ -471,9 +484,7 @@ static long __cdecl gfdm_unit_get_input_p(int device, size_t player) {
|
||||
}
|
||||
|
||||
// guitar freaks
|
||||
if (avs::game::is_model({ "J33", "K33", "L33" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'A'))
|
||||
{
|
||||
if (games::gitadora::is_guitar()) {
|
||||
auto offset = player * 11;
|
||||
|
||||
// pick up
|
||||
@@ -577,8 +588,7 @@ static long __cdecl gfdm_unit2_get_input(int device) {
|
||||
static long __cdecl gfdm_unit_get_sensor_gf_p(int a1, int a2, size_t player) {
|
||||
|
||||
// return if it's actually drum mania
|
||||
if (avs::game::is_model({ "J32", "K32", "L32" }) ||
|
||||
(avs::game::is_model("M32") && avs::game::SPEC[0] == 'B'))
|
||||
if (games::gitadora::is_drum())
|
||||
return 0;
|
||||
|
||||
// get buttons and analogs
|
||||
@@ -752,15 +762,166 @@ static int __cdecl led_unit_reset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __cdecl led_unit_send_custom1(int a1, unsigned char *a2) {
|
||||
static void set_led_state(games::gitadora::Lights::gitadora_lights_t light, bool led_on) {
|
||||
auto &lights = games::gitadora::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(light), (led_on ? 1.f : 0.f));
|
||||
}
|
||||
|
||||
static void set_led_value(games::gitadora::Lights::gitadora_lights_t light, uint8_t value) {
|
||||
auto &lights = games::gitadora::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(light), value / 128.f);
|
||||
}
|
||||
|
||||
static unsigned int __cdecl led_unit_send_custom1(int index, uint8_t *data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __cdecl led_unit_send_custom2(int a1, unsigned char *a2) {
|
||||
struct ExtDevLight {
|
||||
int data_index;
|
||||
games::gitadora::Lights::gitadora_lights_t light;
|
||||
ExtDevLight(
|
||||
int data_index, games::gitadora::Lights::gitadora_lights_t light) :
|
||||
data_index(data_index), light(light) {}
|
||||
};
|
||||
|
||||
// GITADORA series
|
||||
static unsigned int __cdecl led_unit_send_custom2(int index, uint8_t *data) {
|
||||
#ifdef SPICE64
|
||||
return gitadora_led_common(index, data);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static long __cdecl led_unit_send_direct(long a1, uint8_t *a2) {
|
||||
// XG series
|
||||
static long __cdecl led_unit_send_direct(long index, uint8_t *data) {
|
||||
#ifdef SPICE64
|
||||
return 0;
|
||||
#else
|
||||
return gitadora_led_common(index, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
static long __cdecl gitadora_led_common(long index, uint8_t *data) {
|
||||
// log_info("extdev::led", "gitadora_led_common {}", index);
|
||||
// for (size_t i = 0; i < 24; i += 3) {
|
||||
// log_info("extdev::led", "gitadora_led_common ... {} {} {}", data[i], data[i+1], data[i+2]);
|
||||
// }
|
||||
|
||||
if (games::gitadora::is_drum()) {
|
||||
// Drum: index is always 0, data array has 24 elements, each value ranging [0, 128]
|
||||
static ExtDevLight mapping_dm[] = {
|
||||
{0, games::gitadora::Lights::DrumWooferR},
|
||||
{1, games::gitadora::Lights::DrumWooferG},
|
||||
{2, games::gitadora::Lights::DrumWooferB},
|
||||
|
||||
{3, games::gitadora::Lights::DrumSpotRearLeft},
|
||||
{7, games::gitadora::Lights::DrumSpotRearRight},
|
||||
{11, games::gitadora::Lights::SpotFrontLeft},
|
||||
|
||||
{12, games::gitadora::Lights::DrumStageR},
|
||||
{13, games::gitadora::Lights::DrumStageG},
|
||||
{14, games::gitadora::Lights::DrumStageB},
|
||||
|
||||
{15, games::gitadora::Lights::SpotCenterLeft},
|
||||
{19, games::gitadora::Lights::SpotCenterRight},
|
||||
{23, games::gitadora::Lights::SpotFrontRight}
|
||||
};
|
||||
if (index == 0) {
|
||||
for (const auto &map : mapping_dm) {
|
||||
set_led_value(map.light, data[map.data_index]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Guitar: index is {0,1,2}, data array has 24 elements, each value ranging [0, 128]
|
||||
static ExtDevLight mapping0[] = {
|
||||
{0, games::gitadora::Lights::GuitarLowerLeftR},
|
||||
{1, games::gitadora::Lights::GuitarLowerLeftG},
|
||||
{2, games::gitadora::Lights::GuitarLowerLeftB},
|
||||
|
||||
{3, games::gitadora::Lights::SpotFrontLeft},
|
||||
{7, games::gitadora::Lights::SpotCenterLeft},
|
||||
|
||||
{8, games::gitadora::Lights::GuitarLowerRightR},
|
||||
{9, games::gitadora::Lights::GuitarLowerRightG},
|
||||
{10, games::gitadora::Lights::GuitarLowerRightB},
|
||||
|
||||
{11, games::gitadora::Lights::SpotCenterRight},
|
||||
{15, games::gitadora::Lights::SpotFrontRight}
|
||||
};
|
||||
|
||||
static ExtDevLight mapping1[] = {
|
||||
{0, games::gitadora::Lights::GuitarLeftSpeakerUpperR},
|
||||
{1, games::gitadora::Lights::GuitarLeftSpeakerUpperG},
|
||||
{2, games::gitadora::Lights::GuitarLeftSpeakerUpperB},
|
||||
|
||||
{4, games::gitadora::Lights::GuitarLeftSpeakerMidUpLeftR},
|
||||
{5, games::gitadora::Lights::GuitarLeftSpeakerMidUpLeftG},
|
||||
{6, games::gitadora::Lights::GuitarLeftSpeakerMidUpLeftB},
|
||||
|
||||
{8, games::gitadora::Lights::GuitarLeftSpeakerMidUpRightR},
|
||||
{9, games::gitadora::Lights::GuitarLeftSpeakerMidUpRightG},
|
||||
{10, games::gitadora::Lights::GuitarLeftSpeakerMidUpRightB},
|
||||
|
||||
{12, games::gitadora::Lights::GuitarLeftSpeakerMidLowLeftR},
|
||||
{13, games::gitadora::Lights::GuitarLeftSpeakerMidLowLeftG},
|
||||
{14, games::gitadora::Lights::GuitarLeftSpeakerMidLowLeftB},
|
||||
|
||||
{16, games::gitadora::Lights::GuitarLeftSpeakerMidLowRightR},
|
||||
{17, games::gitadora::Lights::GuitarLeftSpeakerMidLowRightG},
|
||||
{18, games::gitadora::Lights::GuitarLeftSpeakerMidLowRightB},
|
||||
|
||||
{20, games::gitadora::Lights::GuitarLeftSpeakerLowerR},
|
||||
{21, games::gitadora::Lights::GuitarLeftSpeakerLowerG},
|
||||
{22, games::gitadora::Lights::GuitarLeftSpeakerLowerB}
|
||||
};
|
||||
|
||||
static ExtDevLight mapping2[] = {
|
||||
{0, games::gitadora::Lights::GuitarRightSpeakerUpperR},
|
||||
{1, games::gitadora::Lights::GuitarRightSpeakerUpperG},
|
||||
{2, games::gitadora::Lights::GuitarRightSpeakerUpperB},
|
||||
|
||||
{4, games::gitadora::Lights::GuitarRightSpeakerMidUpLeftR},
|
||||
{5, games::gitadora::Lights::GuitarRightSpeakerMidUpLeftG},
|
||||
{6, games::gitadora::Lights::GuitarRightSpeakerMidUpLeftB},
|
||||
|
||||
{8, games::gitadora::Lights::GuitarRightSpeakerMidUpRightR},
|
||||
{9, games::gitadora::Lights::GuitarRightSpeakerMidUpRightG},
|
||||
{10, games::gitadora::Lights::GuitarRightSpeakerMidUpRightB},
|
||||
|
||||
{12, games::gitadora::Lights::GuitarRightSpeakerMidLowLeftR},
|
||||
{13, games::gitadora::Lights::GuitarRightSpeakerMidLowLeftG},
|
||||
{14, games::gitadora::Lights::GuitarRightSpeakerMidLowLeftB},
|
||||
|
||||
{16, games::gitadora::Lights::GuitarRightSpeakerMidLowRightR},
|
||||
{17, games::gitadora::Lights::GuitarRightSpeakerMidLowRightG},
|
||||
{18, games::gitadora::Lights::GuitarRightSpeakerMidLowRightB},
|
||||
|
||||
{20, games::gitadora::Lights::GuitarRightSpeakerLowerR},
|
||||
{21, games::gitadora::Lights::GuitarRightSpeakerLowerG},
|
||||
{22, games::gitadora::Lights::GuitarRightSpeakerLowerB}
|
||||
};
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
for (const auto &map : mapping0) {
|
||||
set_led_value(map.light, data[map.data_index]);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (const auto &map : mapping1) {
|
||||
set_led_value(map.light, data[map.data_index]);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (const auto &map : mapping2) {
|
||||
set_led_value(map.light, data[map.data_index]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -814,6 +975,72 @@ static int __cdecl sci_setparam(int a1, int a2, int a3, char a4) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __cdecl dm_output_set_padled(LED_INDEX index, bool led_on) {
|
||||
games::gitadora::Lights::gitadora_lights_t lights[] = {
|
||||
games::gitadora::Lights::DrumHighTom,
|
||||
games::gitadora::Lights::DrumLowTom,
|
||||
games::gitadora::Lights::DrumSnare,
|
||||
games::gitadora::Lights::DrumFloorTom,
|
||||
games::gitadora::Lights::DrumLeftCymbal,
|
||||
games::gitadora::Lights::DrumRightCymbal,
|
||||
games::gitadora::Lights::DrumHiHat
|
||||
};
|
||||
|
||||
// log_info("gitadora", "dm_output_set_padled {} {}", index, led_on);
|
||||
|
||||
if (index < std::size(lights)) {
|
||||
set_led_state(lights[index], led_on);
|
||||
}
|
||||
}
|
||||
|
||||
static void __cdecl dm_output_set_startled(bool led_on) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuStart, led_on);
|
||||
}
|
||||
|
||||
static void __cdecl dm_output_set_up_down(bool led_on) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuUpDown, led_on);
|
||||
}
|
||||
|
||||
static void __cdecl dm_output_set_left_right(bool led_on) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuLeftRight, led_on);
|
||||
}
|
||||
|
||||
static void __cdecl dm_output_set_help(bool led_on) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuHelp, led_on);
|
||||
}
|
||||
|
||||
static void __cdecl gf_output_set_startlamp(LED_INDEX index, bool led_on) {
|
||||
if (index == 0) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuStart, led_on);
|
||||
} else if (index == 1) {
|
||||
set_led_state(games::gitadora::Lights::P2MenuStart, led_on);
|
||||
}
|
||||
}
|
||||
|
||||
static void __cdecl gf_output_set_up_down(LED_INDEX index, bool led_on) {
|
||||
if (index == 0) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuUpDown, led_on);
|
||||
} else if (index == 1) {
|
||||
set_led_state(games::gitadora::Lights::P2MenuUpDown, led_on);
|
||||
}
|
||||
}
|
||||
|
||||
static void __cdecl gf_output_set_left_right(LED_INDEX index, bool led_on) {
|
||||
if (index == 0) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuLeftRight, led_on);
|
||||
} else if (index == 1) {
|
||||
set_led_state(games::gitadora::Lights::P2MenuLeftRight, led_on);
|
||||
}
|
||||
}
|
||||
|
||||
static void __cdecl gf_output_set_help(LED_INDEX index, bool led_on) {
|
||||
if (index == 0) {
|
||||
set_led_state(games::gitadora::Lights::P1MenuHelp, led_on);
|
||||
} else if (index == 1) {
|
||||
set_led_state(games::gitadora::Lights::P2MenuHelp, led_on);
|
||||
}
|
||||
}
|
||||
|
||||
void extdev_attach() {
|
||||
|
||||
// get instance
|
||||
@@ -902,7 +1129,8 @@ void extdev_attach() {
|
||||
"?gfdm_unit_get_button_gf@@YAHW4UNIT_NO@@W4GF_BUTTON_DEFINITION@@@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_get_dm_attack_border_value, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_get_dm_attack_border_value",
|
||||
"?gfdm_unit_get_dm_attack_border_value@@YAHHPEAH@Z"}));
|
||||
"?gfdm_unit_get_dm_attack_border_value@@YAHHPEAH@Z",
|
||||
"?gfdm_unit_get_dm_attack_border_value@@YAHHPAH@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_get_errorcount, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_get_errorcount",
|
||||
"?gfdm_unit_get_errorcount@@YAHH@Z"}));
|
||||
@@ -920,7 +1148,8 @@ void extdev_attach() {
|
||||
"?gfdm_unit_get_stream_errorcount@@YAHH@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_get_version, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_get_version",
|
||||
"?gfdm_unit_get_version@@YAPEBUfirm_version@@H@Z"}));
|
||||
"?gfdm_unit_get_version@@YAPEBUfirm_version@@H@Z",
|
||||
"?gfdm_unit_get_version@@YAPBUfirm_version@@H@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_reset, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_reset",
|
||||
"?gfdm_unit_reset@@YAHXZ"}));
|
||||
@@ -932,7 +1161,8 @@ void extdev_attach() {
|
||||
"?gfdm_unit_send_motor_value@@YAIHE@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_set_dm_attack_border_value, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_set_dm_attack_border_value",
|
||||
"?gfdm_unit_set_dm_attack_border_value@@YAXQEAH@Z"}));
|
||||
"?gfdm_unit_set_dm_attack_border_value@@YAXQEAH@Z",
|
||||
"?gfdm_unit_set_dm_attack_border_value@@YAXQAH@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit_shutdown, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit_shutdown",
|
||||
"?gfdm_unit_shutdown@@YAXXZ"}));
|
||||
@@ -976,7 +1206,8 @@ void extdev_attach() {
|
||||
"?gfdm_unit2_get_stream_errorcount@@YAHH@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit2_get_version, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit2_get_version",
|
||||
"?gfdm_unit2_get_version@@YAPEBUfirm_version@@H@Z"}));
|
||||
"?gfdm_unit2_get_version@@YAPEBUfirm_version@@H@Z",
|
||||
"?gfdm_unit2_get_version@@YAPBUfirm_version@@H@Z"}));
|
||||
detour::inline_hook((void *) gfdm_unit2_reset, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"gfdm_unit2_reset",
|
||||
"?gfdm_unit2_reset@@YAHXZ"}));
|
||||
@@ -999,7 +1230,8 @@ void extdev_attach() {
|
||||
// led unit
|
||||
detour::inline_hook((void *) led_get_version, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_get_version",
|
||||
"?led_get_version@@YAPEBUfirm_version@@H@Z"}));
|
||||
"?led_get_version@@YAPEBUfirm_version@@H@Z",
|
||||
"?led_get_version@@YAPBUfirm_version@@H@Z"}));
|
||||
detour::inline_hook((void *) led_unit_boot, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_unit_boot",
|
||||
"?led_unit_boot@@YAXHH@Z"}));
|
||||
@@ -1017,13 +1249,16 @@ void extdev_attach() {
|
||||
"?led_unit_reset@@YAHXZ"}));
|
||||
detour::inline_hook((void *) led_unit_send_custom1, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_unit_send_custom1",
|
||||
"?led_unit_send_custom1@@YAIHQEAE@Z"}));
|
||||
"?led_unit_send_custom1@@YAIHQEAE@Z",
|
||||
"?led_unit_send_custom1@@YAIHQAE@Z"}));
|
||||
detour::inline_hook((void *) led_unit_send_custom2, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_unit_send_custom2",
|
||||
"?led_unit_send_custom2@@YAIHQEAE@Z"}));
|
||||
"?led_unit_send_custom2@@YAIHQEAE@Z",
|
||||
"?led_unit_send_custom2@@YAIHQAE@Z"}));
|
||||
detour::inline_hook((void *) led_unit_send_direct, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_unit_send_direct",
|
||||
"?led_unit_send_direct@@YAIHQEAE@Z"}));
|
||||
"?led_unit_send_direct@@YAIHQEAE@Z",
|
||||
"?led_unit_send_direct@@YAIHQAE@Z"}));
|
||||
detour::inline_hook((void *) led_unit_shutdown, libutils::try_proc_list(
|
||||
EXTDEV_INSTANCE, {"led_unit_shutdown",
|
||||
"?led_unit_shutdown@@YAXXZ"}));
|
||||
@@ -1054,6 +1289,28 @@ void extdev_attach() {
|
||||
EXTDEV_INSTANCE, "sci_set_linebreak"));
|
||||
detour::inline_hook((void *) sci_setparam, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "sci_setparam"));
|
||||
|
||||
// libshare-pj.dll - dm_output
|
||||
detour::inline_hook((void *) dm_output_set_padled, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "dm_output_set_padled"));
|
||||
detour::inline_hook((void *) dm_output_set_startled, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "dm_output_set_startled"));
|
||||
detour::inline_hook((void *) dm_output_set_up_down, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "dm_output_set_up_down"));
|
||||
detour::inline_hook((void *) dm_output_set_left_right, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "dm_output_set_left_right"));
|
||||
detour::inline_hook((void *) dm_output_set_help, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "dm_output_set_help"));
|
||||
|
||||
// libshare-pj.dll - gf_output
|
||||
detour::inline_hook((void *) gf_output_set_startlamp, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "gf_output_set_startlamp"));
|
||||
detour::inline_hook((void *) gf_output_set_up_down, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "gf_output_set_up_down"));
|
||||
detour::inline_hook((void *) gf_output_set_left_right, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "gf_output_set_left_right"));
|
||||
detour::inline_hook((void *) gf_output_set_help, libutils::try_proc(
|
||||
EXTDEV_INSTANCE, "gf_output_set_help"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
#include "vrutil.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace vrutil {
|
||||
static bool INITIALIZED = false;
|
||||
|
||||
VRStatus STATUS = VRStatus::Disabled;
|
||||
vr::IVRSystem *SYSTEM = nullptr;
|
||||
vr::TrackedDeviceIndex_t INDEX_LEFT = ~0u;
|
||||
vr::TrackedDeviceIndex_t INDEX_RIGHT = ~0u;
|
||||
|
||||
static std::string device_string(vr::TrackedDeviceIndex_t dev,
|
||||
vr::TrackedDeviceProperty prop) {
|
||||
uint32_t len = vr::VRSystem()->GetStringTrackedDeviceProperty(
|
||||
dev, prop, nullptr, 0, nullptr);
|
||||
if (len == 0) return "";
|
||||
char *buf = new char[len];
|
||||
vr::VRSystem()->GetStringTrackedDeviceProperty(
|
||||
dev, prop, buf, len, nullptr);
|
||||
std::string res = buf;
|
||||
delete[] buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (INITIALIZED) return;
|
||||
|
||||
// initialize OpenVR
|
||||
vr::EVRInitError error = vr::VRInitError_None;
|
||||
SYSTEM = vr::VR_Init(&error, vr::VRApplication_Other);
|
||||
if (error != vr::VRInitError_None || !SYSTEM) {
|
||||
SYSTEM = nullptr;
|
||||
STATUS = VRStatus::Error;
|
||||
log_warning("vrutil", "unable to initialize: {}",
|
||||
vr::VR_GetVRInitErrorAsEnglishDescription(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// get information
|
||||
auto driver = device_string(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String);
|
||||
auto serial = device_string(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String);
|
||||
log_info("vrutil", "initialized: {} - {}", driver, serial);
|
||||
|
||||
// success
|
||||
INITIALIZED = true;
|
||||
STATUS = VRStatus::Running;
|
||||
scan(true);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (STATUS == VRStatus::Running) {
|
||||
vr::VR_Shutdown();
|
||||
}
|
||||
SYSTEM = nullptr;
|
||||
STATUS = VRStatus::Disabled;
|
||||
}
|
||||
|
||||
void scan(bool log) {
|
||||
if (STATUS != VRStatus::Running) return;
|
||||
for (vr::TrackedDeviceIndex_t index = 0; index < vr::k_unMaxTrackedDeviceCount; ++index) {
|
||||
auto dclass = SYSTEM->GetTrackedDeviceClass(index);
|
||||
switch (dclass) {
|
||||
case vr::TrackedDeviceClass_Controller: {
|
||||
auto role = SYSTEM->GetControllerRoleForTrackedDeviceIndex(index);
|
||||
switch (role) {
|
||||
case vr::TrackedControllerRole_Invalid:
|
||||
if (log) log_warning("vrutil", "invalid controller on index {}", index);
|
||||
break;
|
||||
case vr::TrackedControllerRole_LeftHand:
|
||||
if (log) log_info("vrutil", "detected left controller on index {}", index);
|
||||
INDEX_LEFT = index;
|
||||
break;
|
||||
case vr::TrackedControllerRole_RightHand:
|
||||
if (log) log_info("vrutil", "detected right controller on index {}", index);
|
||||
INDEX_RIGHT = index;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case vr::TrackedDeviceClass_GenericTracker: {
|
||||
if (log) log_info("vrutil", "detected tracker on index {}", index);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool get_hmd_pose(vr::TrackedDevicePose_t *pose) {
|
||||
if (STATUS != VRStatus::Running) {
|
||||
*pose = vr::TrackedDevicePose_t {};
|
||||
pose->bPoseIsValid = false;
|
||||
return false;
|
||||
}
|
||||
SYSTEM->GetDeviceToAbsoluteTrackingPose(
|
||||
vr::TrackingUniverseStanding,
|
||||
0,
|
||||
pose, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_con_pose(vr::TrackedDeviceIndex_t index,
|
||||
vr::TrackedDevicePose_t *pose,
|
||||
vr::VRControllerState_t *state) {
|
||||
if (STATUS != VRStatus::Running || index == ~0u) {
|
||||
*state = vr::VRControllerState_t {};
|
||||
*pose = vr::TrackedDevicePose_t {};
|
||||
pose->bPoseIsValid = false;
|
||||
return false;
|
||||
}
|
||||
SYSTEM->GetControllerStateWithPose(
|
||||
vr::TrackingUniverseStanding,
|
||||
index, state, sizeof(vr::VRControllerState_t), pose);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
#include <cmath>
|
||||
#include "external/openvr/headers/openvr.h"
|
||||
#include "external/linalg.h"
|
||||
|
||||
namespace vrutil {
|
||||
|
||||
enum class VRStatus {
|
||||
Disabled, Error, Running,
|
||||
};
|
||||
|
||||
extern VRStatus STATUS;
|
||||
extern vr::IVRSystem *SYSTEM;
|
||||
extern vr::TrackedDeviceIndex_t INDEX_LEFT;
|
||||
extern vr::TrackedDeviceIndex_t INDEX_RIGHT;
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
void scan(bool log = false);
|
||||
|
||||
bool get_hmd_pose(vr::TrackedDevicePose_t *pose);
|
||||
bool get_con_pose(vr::TrackedDeviceIndex_t index,
|
||||
vr::TrackedDevicePose_t *pose,
|
||||
vr::VRControllerState_t *state);
|
||||
|
||||
/*
|
||||
* Util
|
||||
*/
|
||||
|
||||
inline linalg::aliases::float3 get_translation(vr::HmdMatrix34_t &m) {
|
||||
return linalg::aliases::float3 {
|
||||
m.m[0][3],
|
||||
m.m[1][3],
|
||||
m.m[2][3],
|
||||
};
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_back(vr::HmdMatrix34_t &m) {
|
||||
return linalg::normalize(linalg::aliases::float3 {
|
||||
m.m[0][2],
|
||||
m.m[1][2],
|
||||
m.m[2][2],
|
||||
});
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_front(vr::HmdMatrix34_t &m) {
|
||||
return -get_direction_back(m);
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_up(vr::HmdMatrix34_t &m) {
|
||||
return linalg::normalize(linalg::aliases::float3 {
|
||||
m.m[0][1],
|
||||
m.m[1][1],
|
||||
m.m[2][1],
|
||||
});
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_down(vr::HmdMatrix34_t &m) {
|
||||
return -get_direction_up(m);
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_right(vr::HmdMatrix34_t &m) {
|
||||
return linalg::normalize(linalg::aliases::float3 {
|
||||
m.m[0][0],
|
||||
m.m[1][0],
|
||||
m.m[2][0],
|
||||
});
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 get_direction_left(vr::HmdMatrix34_t &m) {
|
||||
return -get_direction_right(m);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline linalg::aliases::float4 get_rotation(T &m) {
|
||||
linalg::aliases::float4 q;
|
||||
q.w = sqrtf(fmaxf(0, 1 + m[0][0] + m[1][1] + m[2][2])) * 0.5f;
|
||||
q.x = sqrtf(fmaxf(0, 1 + m[0][0] - m[1][1] - m[2][2])) * 0.5f;
|
||||
q.y = sqrtf(fmaxf(0, 1 - m[0][0] + m[1][1] - m[2][2])) * 0.5f;
|
||||
q.z = sqrtf(fmaxf(0, 1 - m[0][0] - m[1][1] + m[2][2])) * 0.5f;
|
||||
q.x = copysignf(q.x, m[2][1] - m[1][2]);
|
||||
q.y = copysignf(q.y, m[0][2] - m[2][0]);
|
||||
q.z = copysignf(q.z, m[1][0] - m[0][1]);
|
||||
return q;
|
||||
}
|
||||
|
||||
inline linalg::aliases::float4 get_rotation(
|
||||
float pitch, float yaw, float roll) {
|
||||
return linalg::aliases::float4 {
|
||||
sinf(roll/2) * cosf(pitch/2) * cosf(yaw/2) - cosf(roll/2) * sinf(pitch/2) * sinf(yaw/2),
|
||||
cosf(roll/2) * sinf(pitch/2) * cosf(yaw/2) + sinf(roll/2) * cosf(pitch/2) * sinf(yaw/2),
|
||||
cosf(roll/2) * cosf(pitch/2) * sinf(yaw/2) - sinf(roll/2) * sinf(pitch/2) * cosf(yaw/2),
|
||||
cosf(roll/2) * cosf(pitch/2) * cosf(yaw/2) + sinf(roll/2) * sinf(pitch/2) * sinf(yaw/2),
|
||||
};
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 orthogonal(
|
||||
linalg::aliases::float3 v) {
|
||||
auto x = std::fabs(v.x);
|
||||
auto y = std::fabs(v.y);
|
||||
auto z = std::fabs(v.z);
|
||||
linalg::aliases::float3 o;
|
||||
if (x < y) {
|
||||
if (x < z) o = linalg::aliases::float3(1, 0, 0);
|
||||
else o = linalg::aliases::float3(0, 0, 1);
|
||||
} else {
|
||||
if (y < z) o = linalg::aliases::float3(0, 1, 0);
|
||||
else linalg::aliases::float3(0, 0, 1);
|
||||
}
|
||||
return linalg::cross(v, o);
|
||||
}
|
||||
|
||||
inline linalg::aliases::float4 get_rotation(
|
||||
linalg::aliases::float3 v1,
|
||||
linalg::aliases::float3 v2) {
|
||||
auto dot = linalg::dot(v1, v2);
|
||||
auto scl = sqrtf(linalg::length2(v1) * linalg::length2(v2));
|
||||
if (dot / scl == -1) {
|
||||
auto v = linalg::normalize(orthogonal(v1));
|
||||
return linalg::aliases::float4 {
|
||||
v.x, v.y, v.z, 0,
|
||||
};
|
||||
}
|
||||
auto cross = linalg::cross(v1, v2);
|
||||
return linalg::normalize(linalg::aliases::float4 {
|
||||
cross.x, cross.y, cross.z, dot + scl,
|
||||
});
|
||||
}
|
||||
|
||||
inline linalg::aliases::float3 intersect_point(
|
||||
linalg::aliases::float3 ray_dir,
|
||||
linalg::aliases::float3 ray_point,
|
||||
linalg::aliases::float3 plane_normal,
|
||||
linalg::aliases::float3 plane_point) {
|
||||
auto delta = ray_point - plane_point;
|
||||
auto delta_dot = linalg::dot(delta, plane_normal);
|
||||
auto ray_dot = linalg::dot(ray_dir, plane_normal);
|
||||
return ray_point - ray_dir * (delta_dot / ray_dot);
|
||||
}
|
||||
}
|
||||
+60
-32
@@ -4,11 +4,16 @@
|
||||
|
||||
#include "wintouchemu.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "cfg/screen_resize.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "overlay/windows/generic_sub.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
@@ -31,7 +36,7 @@ namespace wintouchemu {
|
||||
bool LOG_FPS = false;
|
||||
|
||||
static inline bool is_emu_enabled() {
|
||||
return FORCE || !is_touch_available() || GRAPHICS_SHOW_CURSOR;
|
||||
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
|
||||
}
|
||||
|
||||
static decltype(GetSystemMetrics) *GetSystemMetrics_orig = nullptr;
|
||||
@@ -75,10 +80,10 @@ namespace wintouchemu {
|
||||
HMODULE HOOKED_MODULE = nullptr;
|
||||
std::string WINDOW_TITLE_START = "";
|
||||
std::optional<std::string> WINDOW_TITLE_END = std::nullopt;
|
||||
bool INITIALIZED = false;
|
||||
volatile bool INITIALIZED = false;
|
||||
mouse_state_t mouse_state;
|
||||
|
||||
void hook(const char *window_title, HMODULE module) {
|
||||
void hook(const char *window_title, HMODULE module, int delay_in_s) {
|
||||
|
||||
// hooks
|
||||
auto system_metrics_hook = detour::iat_try(
|
||||
@@ -97,7 +102,20 @@ namespace wintouchemu {
|
||||
// set module and title
|
||||
HOOKED_MODULE = module;
|
||||
WINDOW_TITLE_START = window_title;
|
||||
INITIALIZED = true;
|
||||
|
||||
if (0 < delay_in_s) {
|
||||
// some games crash when touch events are injected too early during boot
|
||||
std::thread t([&]() {
|
||||
log_misc("wintouchemu", "defer initialization until later (with delay of {}s)", delay_in_s);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(delay_in_s));
|
||||
log_misc("wintouchemu", "initializing", delay_in_s);
|
||||
INITIALIZED = true;
|
||||
});
|
||||
t.detach();
|
||||
} else {
|
||||
log_misc("wintouchemu", "initializing");
|
||||
INITIALIZED = true;
|
||||
}
|
||||
}
|
||||
|
||||
void hook_title_ends(const char *window_title_start, const char *window_title_end, HMODULE module) {
|
||||
@@ -145,8 +163,20 @@ namespace wintouchemu {
|
||||
auto x = touch_event->x;
|
||||
auto y = touch_event->y;
|
||||
auto valid = true;
|
||||
if (overlay::OVERLAY) {
|
||||
|
||||
// log_misc("wintouchemu", "touch event ({}, {})", to_string(x), to_string(y));
|
||||
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
// touch was received on subscreen window.
|
||||
RECT clientRect {};
|
||||
GetClientRect(TDJ_SUBSCREEN_WINDOW, &clientRect);
|
||||
x = (float) x / clientRect.right * SPICETOUCH_TOUCH_WIDTH + SPICETOUCH_TOUCH_X;
|
||||
y = (float) y / clientRect.bottom * SPICETOUCH_TOUCH_HEIGHT + SPICETOUCH_TOUCH_Y;
|
||||
} else if (overlay::OVERLAY) {
|
||||
// touch was received on global coords
|
||||
valid = overlay::OVERLAY->transform_touch_point(&x, &y);
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
touch_input->x = x * 100;
|
||||
@@ -185,6 +215,14 @@ namespace wintouchemu {
|
||||
result = true;
|
||||
touch_input->x = mouse_state.pos.x;
|
||||
touch_input->y = mouse_state.pos.y;
|
||||
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
touch_input->x -= SPICETOUCH_TOUCH_X;
|
||||
touch_input->y -= SPICETOUCH_TOUCH_Y;
|
||||
}
|
||||
|
||||
// log_misc("wintouchemu", "mouse state ({}, {})", to_string(touch_input->x), to_string(touch_input->y));
|
||||
|
||||
auto valid = true;
|
||||
if (overlay::OVERLAY) {
|
||||
valid = overlay::OVERLAY->transform_touch_point(
|
||||
@@ -222,7 +260,7 @@ namespace wintouchemu {
|
||||
// reset it since the event was consumed & propagated as touch
|
||||
mouse_state.touch_event = 0;
|
||||
}
|
||||
} else {
|
||||
} else if (!GRAPHICS_IIDX_WSUB) {
|
||||
|
||||
/*
|
||||
* For some reason, Nostalgia won't show an active touch point unless a move event
|
||||
@@ -319,39 +357,29 @@ namespace wintouchemu {
|
||||
|
||||
// check if windowed
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
log_info("wintouchemu", "activating window hooks");
|
||||
|
||||
// get client size
|
||||
RECT rect {};
|
||||
GetClientRect(hWnd, &rect);
|
||||
|
||||
// remove style borders
|
||||
LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
|
||||
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
|
||||
SetWindowLongPtr(hWnd, GWL_STYLE, lStyle);
|
||||
|
||||
// remove ex style borders
|
||||
LONG lExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
|
||||
SetWindowLongPtr(hWnd, GWL_EXSTYLE, lExStyle);
|
||||
|
||||
// update window
|
||||
AdjustWindowRect(&rect, lStyle, FALSE);
|
||||
SetWindowPos(hWnd, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
|
||||
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
||||
|
||||
// create touch window - create overlay if not yet existing at this point
|
||||
touch_create_wnd(hWnd, overlay::ENABLED && !overlay::OVERLAY);
|
||||
USE_MOUSE = false;
|
||||
if (GRAPHICS_IIDX_WSUB) {
|
||||
// no handling is needed here
|
||||
// graphics::MoveWindow_hook will attach hook to windowed subscreen
|
||||
log_info("wintouchemu", "attach touch hook to windowed subscreen for TDJ");
|
||||
USE_MOUSE = false;
|
||||
} else if (avs::game::is_model("LDJ") && !GENERIC_SUB_WINDOW_FULLSIZE) {
|
||||
// overlay subscreen in IIDX
|
||||
// use mouse position as ImGui overlay will block the touch window
|
||||
log_info("wintouchemu", "use mouse cursor API for overlay subscreen");
|
||||
USE_MOUSE = true;
|
||||
} else {
|
||||
// create touch window - create overlay if not yet existing at this point
|
||||
log_info("wintouchemu", "create touch window relative to main game window");
|
||||
touch_create_wnd(hWnd, overlay::ENABLED && !overlay::OVERLAY);
|
||||
USE_MOUSE = false;
|
||||
}
|
||||
} else if (INJECT_MOUSE_AS_WM_TOUCH) {
|
||||
log_info(
|
||||
"wintouchemu",
|
||||
"using raw mouse cursor API in full screen and injecting them as WM_TOUCH events");
|
||||
USE_MOUSE = true;
|
||||
|
||||
} else {
|
||||
log_info("wintouchemu", "activating DirectX hooks");
|
||||
|
||||
// mouse position based input only
|
||||
touch_attach_dx_hook();
|
||||
USE_MOUSE = false;
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ namespace wintouchemu {
|
||||
extern bool INJECT_MOUSE_AS_WM_TOUCH;
|
||||
extern bool LOG_FPS;
|
||||
|
||||
void hook(const char *window_title, HMODULE module = nullptr);
|
||||
void hook(const char *window_title, HMODULE module = nullptr, int delay_in_s=0);
|
||||
void hook_title_ends(
|
||||
const char *window_title_start,
|
||||
const char *window_title_ends,
|
||||
|
||||
Reference in New Issue
Block a user