chore: CRLF to LF
This commit is contained in:
+294
-294
@@ -1,294 +1,294 @@
|
||||
#include "bt5api.h"
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include <external/robin_hood.h>
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/libutils.h"
|
||||
#include "avs/game.h"
|
||||
#include "eamuse.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Bemanitools is compiled with GCC (MinGW, specifically) as of version 5 */
|
||||
#define LOG_CHECK_FMT __attribute__(( format(printf, 2, 3) ))
|
||||
#else
|
||||
/* Compile it out for MSVC plebs */
|
||||
#define LOG_CHECK_FMT
|
||||
#endif
|
||||
|
||||
/* An AVS-style logger function. Comes in four flavors: misc, info, warning,
|
||||
and fatal, with increasing severity. Fatal loggers do not return, they
|
||||
abort the running process after writing their message to the log.
|
||||
|
||||
"module" is an arbitrary short string identifying the source of the log
|
||||
message. The name of the calling DLL is a good default choice for this
|
||||
string, although you might want to identify a module within your DLL here
|
||||
instead.
|
||||
|
||||
"fmt" is a printf-style format string. Depending on the context in which
|
||||
your DLL is running you might end up calling a logger function exported
|
||||
from libavs, which has its own printf implementation (including a number of
|
||||
proprietary extensions), so don't use any overly exotic formats. */
|
||||
|
||||
typedef void (*log_formatter_t)(const char *module, const char *fmt, ...)
|
||||
LOG_CHECK_FMT;
|
||||
|
||||
/* An API for spawning threads. This API is defined by libavs, although
|
||||
Bemanitools itself may supply compatible implementations of these functions
|
||||
to your DLL, depending on the context in which it runs.
|
||||
|
||||
NOTE: You may only use the logging functions from a thread where Bemanitools
|
||||
calls you, or a thread that you create using this API. Failure to observe
|
||||
this restriction will cause the process to crash. This is a limitation of
|
||||
libavs itself, not Bemanitools. */
|
||||
|
||||
typedef int (*thread_create_t)(int (*proc)(void *), void *ctx,
|
||||
uint32_t stack_sz, unsigned int priority);
|
||||
typedef void (*thread_join_t)(int thread_id, int *result);
|
||||
typedef void (*thread_destroy_t)(int thread_id);
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
typedef void (__cdecl *eam_io_set_loggers_t)(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
static eam_io_set_loggers_t eam_io_set_loggers = nullptr;
|
||||
|
||||
|
||||
/* Initialize your card reader emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
typedef bool (__cdecl *eam_io_init_t)(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
static eam_io_init_t eam_io_init = nullptr;
|
||||
|
||||
/* Shut down your card reader emulation DLL. */
|
||||
typedef void (__cdecl *eam_io_fini_t)(void);
|
||||
static eam_io_fini_t eam_io_fini = nullptr;
|
||||
|
||||
/* Return the state of the number pad on your reader. This function will be
|
||||
called frequently. See enum eam_io_keypad_scan_code above for the meaning of
|
||||
each bit within the return value.
|
||||
|
||||
This function will be called even if the running game does not actually have
|
||||
a number pad on the real cabinet (e.g. Jubeat).
|
||||
|
||||
unit_no is either 0 or 1. Games with only a single reader (jubeat, popn,
|
||||
drummania) will only use unit_no 0. */
|
||||
typedef uint16_t (__cdecl *eam_io_get_keypad_state_t)(uint8_t unit_no);
|
||||
static eam_io_get_keypad_state_t eam_io_get_keypad_state = nullptr;
|
||||
|
||||
/* Indicate which sensors (front and back) are triggered for a slotted reader
|
||||
(refer to enum). To emulate non-slotted readers, just set both sensors
|
||||
to on to indicate the card is in range of the reader. This function
|
||||
will be called frequently. */
|
||||
typedef uint8_t (__cdecl *eam_io_get_sensor_state_t)(uint8_t unit_no);
|
||||
static eam_io_get_sensor_state_t eam_io_get_sensor_state = nullptr;
|
||||
|
||||
/* Read a card ID. This function is only called when the return value of
|
||||
eam_io_get_sensor_state() changes from false to true, so you may take your
|
||||
time and perform file I/O etc, within reason. You must return exactly eight
|
||||
bytes into the buffer pointed to by card_id. */
|
||||
typedef bool (__cdecl *eam_io_read_card_t)(uint8_t unit_no, uint8_t *card_id, uint8_t nbytes);
|
||||
static eam_io_read_card_t eam_io_read_card = nullptr;
|
||||
|
||||
/* Send a command to the card slot. This is called by the game to execute
|
||||
certain actions on a slotted reader (refer to enum). When emulating
|
||||
wave pass readers, this is function is never called. */
|
||||
typedef bool (__cdecl *eam_io_card_slot_cmd_t)(uint8_t unit_no, uint8_t cmd);
|
||||
static eam_io_card_slot_cmd_t eam_io_card_slot_cmd = nullptr;
|
||||
|
||||
/* This function is called frequently. Update your device and states in here */
|
||||
typedef bool (__cdecl *eam_io_poll_t)(uint8_t unit_no);
|
||||
static eam_io_poll_t eam_io_poll = nullptr;
|
||||
|
||||
/* Return a pointer to an internal configuration API for use by config.exe.
|
||||
Custom implementations should return NULL. */
|
||||
typedef const struct eam_io_config_api* (__cdecl *eam_io_get_config_api_t)(void);
|
||||
static eam_io_get_config_api_t eam_io_get_config_api = nullptr;
|
||||
|
||||
bool BT5API_ENABLED = false;
|
||||
static HMODULE EAMIO_DLL;
|
||||
static std::string EAMIO_DLL_NAME = "eamio.dll";
|
||||
static robin_hood::unordered_map<int, std::thread *> BT5API_THREADS;
|
||||
static robin_hood::unordered_map<int, int> BT5API_THREAD_RESULTS;
|
||||
static uint8_t BT5API_CARD_STATES[] = {0, 0};
|
||||
|
||||
static void bt5api_log(const char *bt5_module, const char *fmt, ...) {
|
||||
|
||||
// pre-compute module
|
||||
std::string module = fmt::format("bt5api:{}", bt5_module != nullptr ? bt5_module : "(null)");
|
||||
|
||||
// string format
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const auto result = std::vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// check if format failed, fallback to logging the format string
|
||||
if (result < 0) {
|
||||
log_info(module.c_str(), "{}", fmt);
|
||||
return;
|
||||
}
|
||||
|
||||
// log it if the buffer was enough
|
||||
const size_t len = (size_t) result;
|
||||
if (len < sizeof(buf)) {
|
||||
log_info(module.c_str(), "{}", buf);
|
||||
} else {
|
||||
|
||||
// allocate a new string and format again
|
||||
std::string new_buf(len, '\0');
|
||||
va_start(args, fmt);
|
||||
std::vsnprintf(new_buf.data(), len + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// log the result
|
||||
log_info(module.c_str(), "{}", new_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int bt5api_thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority) {
|
||||
std::thread *thread = new std::thread([proc, ctx]() {
|
||||
int thread_id = (int) std::hash<std::thread::id>{}(std::this_thread::get_id());
|
||||
BT5API_THREAD_RESULTS[thread_id] = proc(ctx);
|
||||
});
|
||||
|
||||
int thread_id = static_cast<int>(std::hash<std::thread::id>{}(thread->get_id()));
|
||||
BT5API_THREADS[thread_id] = thread;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
static void bt5api_thread_join(int thread_id, int *result) {
|
||||
BT5API_THREADS[thread_id]->join();
|
||||
|
||||
if (result != nullptr) {
|
||||
*result = BT5API_THREAD_RESULTS[thread_id];
|
||||
}
|
||||
}
|
||||
|
||||
static void bt5api_thread_destroy(int thread_id) {
|
||||
auto thread_handle = BT5API_THREADS.find(thread_id);
|
||||
if (thread_handle != BT5API_THREADS.end()) {
|
||||
if (thread_handle->second->joinable()) {
|
||||
thread_handle->second->detach();
|
||||
}
|
||||
delete thread_handle->second;
|
||||
BT5API_THREADS.erase(thread_handle);
|
||||
}
|
||||
BT5API_THREAD_RESULTS.erase(thread_id);
|
||||
}
|
||||
|
||||
void bt5api_init() {
|
||||
|
||||
// check if already initialized
|
||||
if (EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("bt5api", "initializing");
|
||||
|
||||
// load DLL instances
|
||||
EAMIO_DLL = libutils::try_library(EAMIO_DLL_NAME);
|
||||
if (!EAMIO_DLL) {
|
||||
EAMIO_DLL = libutils::try_library("..\\" + EAMIO_DLL_NAME);
|
||||
}
|
||||
if (!EAMIO_DLL) {
|
||||
log_warning("bt5api", "unable to load '{}': 0x{:x}", EAMIO_DLL_NAME, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// load eamio funcs
|
||||
eam_io_set_loggers = libutils::try_proc<eam_io_set_loggers_t>(EAMIO_DLL, "eam_io_set_loggers");
|
||||
eam_io_init = libutils::try_proc<eam_io_init_t>(EAMIO_DLL, "eam_io_init");
|
||||
eam_io_fini = libutils::try_proc<eam_io_fini_t>(EAMIO_DLL, "eam_io_fini");
|
||||
eam_io_get_keypad_state = libutils::try_proc<eam_io_get_keypad_state_t>(EAMIO_DLL, "eam_io_get_keypad_state");
|
||||
eam_io_get_sensor_state = libutils::try_proc<eam_io_get_sensor_state_t>(EAMIO_DLL, "eam_io_get_sensor_state");
|
||||
eam_io_read_card = libutils::try_proc<eam_io_read_card_t>(EAMIO_DLL, "eam_io_read_card");
|
||||
eam_io_card_slot_cmd = libutils::try_proc<eam_io_card_slot_cmd_t>(EAMIO_DLL, "eam_io_card_slot_cmd");
|
||||
eam_io_poll = libutils::try_proc<eam_io_poll_t>(EAMIO_DLL, "eam_io_poll");
|
||||
eam_io_get_config_api = libutils::try_proc<eam_io_get_config_api_t>(EAMIO_DLL, "eam_io_get_config_api");
|
||||
|
||||
// initialize
|
||||
eam_io_set_loggers(&bt5api_log, &bt5api_log, &bt5api_log, &bt5api_log);
|
||||
eam_io_init(&bt5api_thread_create, &bt5api_thread_join, &bt5api_thread_destroy);
|
||||
|
||||
// bt5api workaround for games with 2 card readers (NFCeAmuse cares about this)
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
bt5api_poll_reader_card(1);
|
||||
bt5api_poll_reader_card(0);
|
||||
}
|
||||
|
||||
// done
|
||||
log_info("bt5api", "done initializing");
|
||||
}
|
||||
|
||||
void bt5api_hook(HINSTANCE module) {
|
||||
libraryhook_enable(module);
|
||||
|
||||
// toastertools
|
||||
libraryhook_hook_proc("iidx_io_ext_get_16seg", games::iidx::get_16seg);
|
||||
}
|
||||
|
||||
void bt5api_poll_reader_card(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
}
|
||||
|
||||
// get sensor state
|
||||
uint8_t sensor_state = eam_io_get_sensor_state(unit_no);
|
||||
|
||||
// check for card in
|
||||
if (sensor_state > BT5API_CARD_STATES[unit_no]) {
|
||||
uint8_t card_id[8];
|
||||
eam_io_read_card(unit_no, card_id, 8);
|
||||
eamuse_card_insert(unit_no, card_id);
|
||||
}
|
||||
|
||||
// save state
|
||||
BT5API_CARD_STATES[unit_no] = sensor_state;
|
||||
}
|
||||
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
}
|
||||
|
||||
// get keypad
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no));
|
||||
}
|
||||
|
||||
void bt5api_dispose() {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// finish
|
||||
eam_io_fini();
|
||||
}
|
||||
#include "bt5api.h"
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include <external/robin_hood.h>
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/libutils.h"
|
||||
#include "avs/game.h"
|
||||
#include "eamuse.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Bemanitools is compiled with GCC (MinGW, specifically) as of version 5 */
|
||||
#define LOG_CHECK_FMT __attribute__(( format(printf, 2, 3) ))
|
||||
#else
|
||||
/* Compile it out for MSVC plebs */
|
||||
#define LOG_CHECK_FMT
|
||||
#endif
|
||||
|
||||
/* An AVS-style logger function. Comes in four flavors: misc, info, warning,
|
||||
and fatal, with increasing severity. Fatal loggers do not return, they
|
||||
abort the running process after writing their message to the log.
|
||||
|
||||
"module" is an arbitrary short string identifying the source of the log
|
||||
message. The name of the calling DLL is a good default choice for this
|
||||
string, although you might want to identify a module within your DLL here
|
||||
instead.
|
||||
|
||||
"fmt" is a printf-style format string. Depending on the context in which
|
||||
your DLL is running you might end up calling a logger function exported
|
||||
from libavs, which has its own printf implementation (including a number of
|
||||
proprietary extensions), so don't use any overly exotic formats. */
|
||||
|
||||
typedef void (*log_formatter_t)(const char *module, const char *fmt, ...)
|
||||
LOG_CHECK_FMT;
|
||||
|
||||
/* An API for spawning threads. This API is defined by libavs, although
|
||||
Bemanitools itself may supply compatible implementations of these functions
|
||||
to your DLL, depending on the context in which it runs.
|
||||
|
||||
NOTE: You may only use the logging functions from a thread where Bemanitools
|
||||
calls you, or a thread that you create using this API. Failure to observe
|
||||
this restriction will cause the process to crash. This is a limitation of
|
||||
libavs itself, not Bemanitools. */
|
||||
|
||||
typedef int (*thread_create_t)(int (*proc)(void *), void *ctx,
|
||||
uint32_t stack_sz, unsigned int priority);
|
||||
typedef void (*thread_join_t)(int thread_id, int *result);
|
||||
typedef void (*thread_destroy_t)(int thread_id);
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
typedef void (__cdecl *eam_io_set_loggers_t)(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
static eam_io_set_loggers_t eam_io_set_loggers = nullptr;
|
||||
|
||||
|
||||
/* Initialize your card reader emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
typedef bool (__cdecl *eam_io_init_t)(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
static eam_io_init_t eam_io_init = nullptr;
|
||||
|
||||
/* Shut down your card reader emulation DLL. */
|
||||
typedef void (__cdecl *eam_io_fini_t)(void);
|
||||
static eam_io_fini_t eam_io_fini = nullptr;
|
||||
|
||||
/* Return the state of the number pad on your reader. This function will be
|
||||
called frequently. See enum eam_io_keypad_scan_code above for the meaning of
|
||||
each bit within the return value.
|
||||
|
||||
This function will be called even if the running game does not actually have
|
||||
a number pad on the real cabinet (e.g. Jubeat).
|
||||
|
||||
unit_no is either 0 or 1. Games with only a single reader (jubeat, popn,
|
||||
drummania) will only use unit_no 0. */
|
||||
typedef uint16_t (__cdecl *eam_io_get_keypad_state_t)(uint8_t unit_no);
|
||||
static eam_io_get_keypad_state_t eam_io_get_keypad_state = nullptr;
|
||||
|
||||
/* Indicate which sensors (front and back) are triggered for a slotted reader
|
||||
(refer to enum). To emulate non-slotted readers, just set both sensors
|
||||
to on to indicate the card is in range of the reader. This function
|
||||
will be called frequently. */
|
||||
typedef uint8_t (__cdecl *eam_io_get_sensor_state_t)(uint8_t unit_no);
|
||||
static eam_io_get_sensor_state_t eam_io_get_sensor_state = nullptr;
|
||||
|
||||
/* Read a card ID. This function is only called when the return value of
|
||||
eam_io_get_sensor_state() changes from false to true, so you may take your
|
||||
time and perform file I/O etc, within reason. You must return exactly eight
|
||||
bytes into the buffer pointed to by card_id. */
|
||||
typedef bool (__cdecl *eam_io_read_card_t)(uint8_t unit_no, uint8_t *card_id, uint8_t nbytes);
|
||||
static eam_io_read_card_t eam_io_read_card = nullptr;
|
||||
|
||||
/* Send a command to the card slot. This is called by the game to execute
|
||||
certain actions on a slotted reader (refer to enum). When emulating
|
||||
wave pass readers, this is function is never called. */
|
||||
typedef bool (__cdecl *eam_io_card_slot_cmd_t)(uint8_t unit_no, uint8_t cmd);
|
||||
static eam_io_card_slot_cmd_t eam_io_card_slot_cmd = nullptr;
|
||||
|
||||
/* This function is called frequently. Update your device and states in here */
|
||||
typedef bool (__cdecl *eam_io_poll_t)(uint8_t unit_no);
|
||||
static eam_io_poll_t eam_io_poll = nullptr;
|
||||
|
||||
/* Return a pointer to an internal configuration API for use by config.exe.
|
||||
Custom implementations should return NULL. */
|
||||
typedef const struct eam_io_config_api* (__cdecl *eam_io_get_config_api_t)(void);
|
||||
static eam_io_get_config_api_t eam_io_get_config_api = nullptr;
|
||||
|
||||
bool BT5API_ENABLED = false;
|
||||
static HMODULE EAMIO_DLL;
|
||||
static std::string EAMIO_DLL_NAME = "eamio.dll";
|
||||
static robin_hood::unordered_map<int, std::thread *> BT5API_THREADS;
|
||||
static robin_hood::unordered_map<int, int> BT5API_THREAD_RESULTS;
|
||||
static uint8_t BT5API_CARD_STATES[] = {0, 0};
|
||||
|
||||
static void bt5api_log(const char *bt5_module, const char *fmt, ...) {
|
||||
|
||||
// pre-compute module
|
||||
std::string module = fmt::format("bt5api:{}", bt5_module != nullptr ? bt5_module : "(null)");
|
||||
|
||||
// string format
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const auto result = std::vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// check if format failed, fallback to logging the format string
|
||||
if (result < 0) {
|
||||
log_info(module.c_str(), "{}", fmt);
|
||||
return;
|
||||
}
|
||||
|
||||
// log it if the buffer was enough
|
||||
const size_t len = (size_t) result;
|
||||
if (len < sizeof(buf)) {
|
||||
log_info(module.c_str(), "{}", buf);
|
||||
} else {
|
||||
|
||||
// allocate a new string and format again
|
||||
std::string new_buf(len, '\0');
|
||||
va_start(args, fmt);
|
||||
std::vsnprintf(new_buf.data(), len + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// log the result
|
||||
log_info(module.c_str(), "{}", new_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int bt5api_thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz, unsigned int priority) {
|
||||
std::thread *thread = new std::thread([proc, ctx]() {
|
||||
int thread_id = (int) std::hash<std::thread::id>{}(std::this_thread::get_id());
|
||||
BT5API_THREAD_RESULTS[thread_id] = proc(ctx);
|
||||
});
|
||||
|
||||
int thread_id = static_cast<int>(std::hash<std::thread::id>{}(thread->get_id()));
|
||||
BT5API_THREADS[thread_id] = thread;
|
||||
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
static void bt5api_thread_join(int thread_id, int *result) {
|
||||
BT5API_THREADS[thread_id]->join();
|
||||
|
||||
if (result != nullptr) {
|
||||
*result = BT5API_THREAD_RESULTS[thread_id];
|
||||
}
|
||||
}
|
||||
|
||||
static void bt5api_thread_destroy(int thread_id) {
|
||||
auto thread_handle = BT5API_THREADS.find(thread_id);
|
||||
if (thread_handle != BT5API_THREADS.end()) {
|
||||
if (thread_handle->second->joinable()) {
|
||||
thread_handle->second->detach();
|
||||
}
|
||||
delete thread_handle->second;
|
||||
BT5API_THREADS.erase(thread_handle);
|
||||
}
|
||||
BT5API_THREAD_RESULTS.erase(thread_id);
|
||||
}
|
||||
|
||||
void bt5api_init() {
|
||||
|
||||
// check if already initialized
|
||||
if (EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("bt5api", "initializing");
|
||||
|
||||
// load DLL instances
|
||||
EAMIO_DLL = libutils::try_library(EAMIO_DLL_NAME);
|
||||
if (!EAMIO_DLL) {
|
||||
EAMIO_DLL = libutils::try_library("..\\" + EAMIO_DLL_NAME);
|
||||
}
|
||||
if (!EAMIO_DLL) {
|
||||
log_warning("bt5api", "unable to load '{}': 0x{:x}", EAMIO_DLL_NAME, GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// load eamio funcs
|
||||
eam_io_set_loggers = libutils::try_proc<eam_io_set_loggers_t>(EAMIO_DLL, "eam_io_set_loggers");
|
||||
eam_io_init = libutils::try_proc<eam_io_init_t>(EAMIO_DLL, "eam_io_init");
|
||||
eam_io_fini = libutils::try_proc<eam_io_fini_t>(EAMIO_DLL, "eam_io_fini");
|
||||
eam_io_get_keypad_state = libutils::try_proc<eam_io_get_keypad_state_t>(EAMIO_DLL, "eam_io_get_keypad_state");
|
||||
eam_io_get_sensor_state = libutils::try_proc<eam_io_get_sensor_state_t>(EAMIO_DLL, "eam_io_get_sensor_state");
|
||||
eam_io_read_card = libutils::try_proc<eam_io_read_card_t>(EAMIO_DLL, "eam_io_read_card");
|
||||
eam_io_card_slot_cmd = libutils::try_proc<eam_io_card_slot_cmd_t>(EAMIO_DLL, "eam_io_card_slot_cmd");
|
||||
eam_io_poll = libutils::try_proc<eam_io_poll_t>(EAMIO_DLL, "eam_io_poll");
|
||||
eam_io_get_config_api = libutils::try_proc<eam_io_get_config_api_t>(EAMIO_DLL, "eam_io_get_config_api");
|
||||
|
||||
// initialize
|
||||
eam_io_set_loggers(&bt5api_log, &bt5api_log, &bt5api_log, &bt5api_log);
|
||||
eam_io_init(&bt5api_thread_create, &bt5api_thread_join, &bt5api_thread_destroy);
|
||||
|
||||
// bt5api workaround for games with 2 card readers (NFCeAmuse cares about this)
|
||||
if (eamuse_get_game_keypads() > 1) {
|
||||
bt5api_poll_reader_card(1);
|
||||
bt5api_poll_reader_card(0);
|
||||
}
|
||||
|
||||
// done
|
||||
log_info("bt5api", "done initializing");
|
||||
}
|
||||
|
||||
void bt5api_hook(HINSTANCE module) {
|
||||
libraryhook_enable(module);
|
||||
|
||||
// toastertools
|
||||
libraryhook_hook_proc("iidx_io_ext_get_16seg", games::iidx::get_16seg);
|
||||
}
|
||||
|
||||
void bt5api_poll_reader_card(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
}
|
||||
|
||||
// get sensor state
|
||||
uint8_t sensor_state = eam_io_get_sensor_state(unit_no);
|
||||
|
||||
// check for card in
|
||||
if (sensor_state > BT5API_CARD_STATES[unit_no]) {
|
||||
uint8_t card_id[8];
|
||||
eam_io_read_card(unit_no, card_id, 8);
|
||||
eamuse_card_insert(unit_no, card_id);
|
||||
}
|
||||
|
||||
// save state
|
||||
BT5API_CARD_STATES[unit_no] = sensor_state;
|
||||
}
|
||||
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no) {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// poll
|
||||
if (!eam_io_poll(unit_no)) {
|
||||
log_warning("bt5api", "polling bt5api reader {} returned failure",
|
||||
static_cast<int>(unit_no));
|
||||
}
|
||||
|
||||
// get keypad
|
||||
eamuse_set_keypad_overrides_bt5(unit_no, eam_io_get_keypad_state(unit_no));
|
||||
}
|
||||
|
||||
void bt5api_dispose() {
|
||||
|
||||
// check if initialized
|
||||
if (!EAMIO_DLL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// finish
|
||||
eam_io_fini();
|
||||
}
|
||||
|
||||
+36
-36
@@ -1,36 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <windows.h>
|
||||
|
||||
/* Emulating the sensors of a slotted card reader. The reader has one
|
||||
sensor at the front that detects if a card is getting inserted or
|
||||
if the card is not fully removed. When the back sensor is triggered
|
||||
the card is locked in the slot and its data is read. */
|
||||
enum eam_io_sensor_state {
|
||||
EAM_IO_SENSOR_FRONT = 0,
|
||||
EAM_IO_SENSOR_BACK = 1,
|
||||
};
|
||||
|
||||
/* Different commands for the (slotted) reader. The game triggers one
|
||||
of these actions and the card slot as to execute it. When non-slotted
|
||||
readers are emulated, these states are not used/set. */
|
||||
enum eam_io_card_slot_cmd {
|
||||
EAM_IO_CARD_SLOT_CMD_CLOSE = 0,
|
||||
EAM_IO_CARD_SLOT_CMD_OPEN = 1,
|
||||
EAM_IO_CARD_SLOT_CMD_EJECT = 2,
|
||||
EAM_IO_CARD_SLOT_CMD_READ = 3,
|
||||
};
|
||||
|
||||
/* A private function pointer table returned by the stock EAMIO.DLL
|
||||
implementation and consumed by config.exe. The contents of this table are
|
||||
undocumented and subject to change without notice. */
|
||||
struct eam_io_config_api;
|
||||
|
||||
extern bool BT5API_ENABLED;
|
||||
|
||||
void bt5api_init();
|
||||
void bt5api_hook(HINSTANCE module);
|
||||
void bt5api_poll_reader_card(uint8_t unit_no);
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no);
|
||||
void bt5api_dispose();
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <windows.h>
|
||||
|
||||
/* Emulating the sensors of a slotted card reader. The reader has one
|
||||
sensor at the front that detects if a card is getting inserted or
|
||||
if the card is not fully removed. When the back sensor is triggered
|
||||
the card is locked in the slot and its data is read. */
|
||||
enum eam_io_sensor_state {
|
||||
EAM_IO_SENSOR_FRONT = 0,
|
||||
EAM_IO_SENSOR_BACK = 1,
|
||||
};
|
||||
|
||||
/* Different commands for the (slotted) reader. The game triggers one
|
||||
of these actions and the card slot as to execute it. When non-slotted
|
||||
readers are emulated, these states are not used/set. */
|
||||
enum eam_io_card_slot_cmd {
|
||||
EAM_IO_CARD_SLOT_CMD_CLOSE = 0,
|
||||
EAM_IO_CARD_SLOT_CMD_OPEN = 1,
|
||||
EAM_IO_CARD_SLOT_CMD_EJECT = 2,
|
||||
EAM_IO_CARD_SLOT_CMD_READ = 3,
|
||||
};
|
||||
|
||||
/* A private function pointer table returned by the stock EAMIO.DLL
|
||||
implementation and consumed by config.exe. The contents of this table are
|
||||
undocumented and subject to change without notice. */
|
||||
struct eam_io_config_api;
|
||||
|
||||
extern bool BT5API_ENABLED;
|
||||
|
||||
void bt5api_init();
|
||||
void bt5api_hook(HINSTANCE module);
|
||||
void bt5api_poll_reader_card(uint8_t unit_no);
|
||||
void bt5api_poll_reader_keypad(uint8_t unit_no);
|
||||
void bt5api_dispose();
|
||||
|
||||
+143
-143
@@ -1,143 +1,143 @@
|
||||
#include "clipboard.h"
|
||||
|
||||
// GDI+ Headers
|
||||
// WARNING: Must stay in this order to compile
|
||||
#include <windows.h>
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
#ifdef __GNUC__
|
||||
#include <gdiplus/gdiplusflat.h>
|
||||
#else
|
||||
#include <gdiplusflat.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace clipboard {
|
||||
namespace imports {
|
||||
static bool ATTEMPTED_LOAD_LIBRARY = false;
|
||||
|
||||
static decltype(Gdiplus::GdiplusShutdown) *GdiplusShutdown = nullptr;
|
||||
static decltype(Gdiplus::GdiplusStartup) *GdiplusStartup = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateBitmapFromFile) *GdipCreateBitmapFromFile = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateHBITMAPFromBitmap) *GdipCreateHBITMAPFromBitmap = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipDisposeImage) *GdipDisposeImage = nullptr;
|
||||
}
|
||||
|
||||
void copy_image_handler(const std::filesystem::path &path) {
|
||||
if (!imports::ATTEMPTED_LOAD_LIBRARY) {
|
||||
imports::ATTEMPTED_LOAD_LIBRARY = true;
|
||||
|
||||
auto gdiplus = libutils::try_library("gdiplus.dll");
|
||||
|
||||
if (gdiplus) {
|
||||
imports::GdiplusShutdown = (decltype(imports::GdiplusShutdown)) libutils::try_proc(
|
||||
gdiplus, "GdiplusShutdown");
|
||||
imports::GdiplusStartup = (decltype(imports::GdiplusStartup)) libutils::try_proc(
|
||||
gdiplus, "GdiplusStartup");
|
||||
imports::GdipCreateBitmapFromFile = (decltype(imports::GdipCreateBitmapFromFile)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateBitmapFromFile");
|
||||
imports::GdipCreateHBITMAPFromBitmap = (decltype(imports::GdipCreateHBITMAPFromBitmap)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateHBITMAPFromBitmap");
|
||||
imports::GdipDisposeImage = (decltype(imports::GdipDisposeImage)) libutils::try_proc(
|
||||
gdiplus, "GdipDisposeImage");
|
||||
} else {
|
||||
log_warning("clipboard", "GDI+ library not found, disabling clipboard functionality");
|
||||
}
|
||||
}
|
||||
if (!imports::GdiplusShutdown ||
|
||||
!imports::GdiplusStartup ||
|
||||
!imports::GdipCreateBitmapFromFile ||
|
||||
!imports::GdipCreateHBITMAPFromBitmap ||
|
||||
!imports::GdipDisposeImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the screenshot button is print screen, the OpenClipboard call seems to fail often if we only
|
||||
// call it once, probably due to a race condition. So, we can try calling a lot until we can open it.
|
||||
bool clipboard_open = false;
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
clipboard_open = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipboard_open) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start gdiplus
|
||||
Gdiplus::GdiplusStartupInput input {};
|
||||
ULONG_PTR token;
|
||||
imports::GdiplusStartup(&token, &input, nullptr);
|
||||
|
||||
// Convert the file path to a wstring and open the screenshot file
|
||||
Gdiplus::GpBitmap *bitmap = nullptr;
|
||||
auto status = imports::GdipCreateBitmapFromFile(path.c_str(), &bitmap);
|
||||
if (status != Gdiplus::Ok) {
|
||||
log_warning("clipboard", "failed to create GDI+ bitmap: {}", status);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
CloseClipboard();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the HBITMAP from the Bitmap object
|
||||
HBITMAP hbitmap {};
|
||||
status = imports::GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
|
||||
if (status == Gdiplus::Ok) {
|
||||
|
||||
// Convert the HBITMAP to a DIB to copy to the clipboard
|
||||
BITMAP bm;
|
||||
GetObject(hbitmap, sizeof(bm), &bm);
|
||||
|
||||
BITMAPINFOHEADER info;
|
||||
info.biSize = sizeof(info);
|
||||
info.biWidth = bm.bmWidth;
|
||||
info.biHeight = bm.bmHeight;
|
||||
info.biPlanes = 1;
|
||||
info.biBitCount = bm.bmBitsPixel;
|
||||
info.biCompression = BI_RGB;
|
||||
|
||||
std::vector<BYTE> dimensions(bm.bmWidthBytes * bm.bmHeight);
|
||||
auto hdc = GetDC(nullptr);
|
||||
GetDIBits(hdc, hbitmap, 0, info.biHeight, dimensions.data(), reinterpret_cast<BITMAPINFO *>(&info), 0);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
|
||||
auto hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof(info) + dimensions.size());
|
||||
auto buffer = reinterpret_cast<BYTE *>(GlobalLock(hmem));
|
||||
memcpy(buffer, &info, sizeof(info));
|
||||
memcpy(&buffer[sizeof(info)], dimensions.data(), dimensions.size());
|
||||
GlobalUnlock(hmem);
|
||||
|
||||
if (SetClipboardData(CF_DIB, hmem)) {
|
||||
log_info("clipboard", "saved image to clipboard");
|
||||
} else {
|
||||
log_warning("clipboard", "failed to save image to clipboard");
|
||||
}
|
||||
} else {
|
||||
log_warning("clipboard", "failed to retrieve HBITMAP from image bitmap: {}", status);
|
||||
}
|
||||
|
||||
// Clean up after ourselves. hmem can't be deleted because it is owned by the clipboard now.
|
||||
imports::GdipDisposeImage(bitmap);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void copy_image(const std::filesystem::path path) {
|
||||
|
||||
// Create a new thread since we loop to open the clipboard
|
||||
std::thread handle(copy_image_handler, std::move(path));
|
||||
|
||||
handle.detach();
|
||||
}
|
||||
}
|
||||
#include "clipboard.h"
|
||||
|
||||
// GDI+ Headers
|
||||
// WARNING: Must stay in this order to compile
|
||||
#include <windows.h>
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
#ifdef __GNUC__
|
||||
#include <gdiplus/gdiplusflat.h>
|
||||
#else
|
||||
#include <gdiplusflat.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace clipboard {
|
||||
namespace imports {
|
||||
static bool ATTEMPTED_LOAD_LIBRARY = false;
|
||||
|
||||
static decltype(Gdiplus::GdiplusShutdown) *GdiplusShutdown = nullptr;
|
||||
static decltype(Gdiplus::GdiplusStartup) *GdiplusStartup = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateBitmapFromFile) *GdipCreateBitmapFromFile = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipCreateHBITMAPFromBitmap) *GdipCreateHBITMAPFromBitmap = nullptr;
|
||||
static decltype(Gdiplus::DllExports::GdipDisposeImage) *GdipDisposeImage = nullptr;
|
||||
}
|
||||
|
||||
void copy_image_handler(const std::filesystem::path &path) {
|
||||
if (!imports::ATTEMPTED_LOAD_LIBRARY) {
|
||||
imports::ATTEMPTED_LOAD_LIBRARY = true;
|
||||
|
||||
auto gdiplus = libutils::try_library("gdiplus.dll");
|
||||
|
||||
if (gdiplus) {
|
||||
imports::GdiplusShutdown = (decltype(imports::GdiplusShutdown)) libutils::try_proc(
|
||||
gdiplus, "GdiplusShutdown");
|
||||
imports::GdiplusStartup = (decltype(imports::GdiplusStartup)) libutils::try_proc(
|
||||
gdiplus, "GdiplusStartup");
|
||||
imports::GdipCreateBitmapFromFile = (decltype(imports::GdipCreateBitmapFromFile)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateBitmapFromFile");
|
||||
imports::GdipCreateHBITMAPFromBitmap = (decltype(imports::GdipCreateHBITMAPFromBitmap)) libutils::try_proc(
|
||||
gdiplus, "GdipCreateHBITMAPFromBitmap");
|
||||
imports::GdipDisposeImage = (decltype(imports::GdipDisposeImage)) libutils::try_proc(
|
||||
gdiplus, "GdipDisposeImage");
|
||||
} else {
|
||||
log_warning("clipboard", "GDI+ library not found, disabling clipboard functionality");
|
||||
}
|
||||
}
|
||||
if (!imports::GdiplusShutdown ||
|
||||
!imports::GdiplusStartup ||
|
||||
!imports::GdipCreateBitmapFromFile ||
|
||||
!imports::GdipCreateHBITMAPFromBitmap ||
|
||||
!imports::GdipDisposeImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the screenshot button is print screen, the OpenClipboard call seems to fail often if we only
|
||||
// call it once, probably due to a race condition. So, we can try calling a lot until we can open it.
|
||||
bool clipboard_open = false;
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
clipboard_open = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipboard_open) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start gdiplus
|
||||
Gdiplus::GdiplusStartupInput input {};
|
||||
ULONG_PTR token;
|
||||
imports::GdiplusStartup(&token, &input, nullptr);
|
||||
|
||||
// Convert the file path to a wstring and open the screenshot file
|
||||
Gdiplus::GpBitmap *bitmap = nullptr;
|
||||
auto status = imports::GdipCreateBitmapFromFile(path.c_str(), &bitmap);
|
||||
if (status != Gdiplus::Ok) {
|
||||
log_warning("clipboard", "failed to create GDI+ bitmap: {}", status);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
CloseClipboard();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the HBITMAP from the Bitmap object
|
||||
HBITMAP hbitmap {};
|
||||
status = imports::GdipCreateHBITMAPFromBitmap(bitmap, &hbitmap, 0);
|
||||
if (status == Gdiplus::Ok) {
|
||||
|
||||
// Convert the HBITMAP to a DIB to copy to the clipboard
|
||||
BITMAP bm;
|
||||
GetObject(hbitmap, sizeof(bm), &bm);
|
||||
|
||||
BITMAPINFOHEADER info;
|
||||
info.biSize = sizeof(info);
|
||||
info.biWidth = bm.bmWidth;
|
||||
info.biHeight = bm.bmHeight;
|
||||
info.biPlanes = 1;
|
||||
info.biBitCount = bm.bmBitsPixel;
|
||||
info.biCompression = BI_RGB;
|
||||
|
||||
std::vector<BYTE> dimensions(bm.bmWidthBytes * bm.bmHeight);
|
||||
auto hdc = GetDC(nullptr);
|
||||
GetDIBits(hdc, hbitmap, 0, info.biHeight, dimensions.data(), reinterpret_cast<BITMAPINFO *>(&info), 0);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
|
||||
auto hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof(info) + dimensions.size());
|
||||
auto buffer = reinterpret_cast<BYTE *>(GlobalLock(hmem));
|
||||
memcpy(buffer, &info, sizeof(info));
|
||||
memcpy(&buffer[sizeof(info)], dimensions.data(), dimensions.size());
|
||||
GlobalUnlock(hmem);
|
||||
|
||||
if (SetClipboardData(CF_DIB, hmem)) {
|
||||
log_info("clipboard", "saved image to clipboard");
|
||||
} else {
|
||||
log_warning("clipboard", "failed to save image to clipboard");
|
||||
}
|
||||
} else {
|
||||
log_warning("clipboard", "failed to retrieve HBITMAP from image bitmap: {}", status);
|
||||
}
|
||||
|
||||
// Clean up after ourselves. hmem can't be deleted because it is owned by the clipboard now.
|
||||
imports::GdipDisposeImage(bitmap);
|
||||
|
||||
imports::GdiplusShutdown(token);
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void copy_image(const std::filesystem::path path) {
|
||||
|
||||
// Create a new thread since we loop to open the clipboard
|
||||
std::thread handle(copy_image_handler, std::move(path));
|
||||
|
||||
handle.detach();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace clipboard {
|
||||
void copy_image(const std::filesystem::path path);
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace clipboard {
|
||||
void copy_image(const std::filesystem::path path);
|
||||
}
|
||||
|
||||
+722
-722
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void spicedevice_attach();
|
||||
|
||||
void spicedevice_detach();
|
||||
#pragma once
|
||||
|
||||
void spicedevice_attach();
|
||||
|
||||
void spicedevice_detach();
|
||||
|
||||
+75
-75
@@ -1,75 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "external/scard/scard.h"
|
||||
|
||||
/* From BT5 API for compatibility reasons.
|
||||
Scan codes for the so-called "10 key" button panel on each card reader. Each
|
||||
scan code corresponds to a bit position within the 16-bit bitfield that you
|
||||
return from eam_io_get_keypad_state(). */
|
||||
enum eam_io_keypad_scan_code {
|
||||
EAM_IO_KEYPAD_0 = 0,
|
||||
EAM_IO_KEYPAD_1 = 1,
|
||||
EAM_IO_KEYPAD_4 = 2,
|
||||
EAM_IO_KEYPAD_7 = 3,
|
||||
EAM_IO_KEYPAD_00 = 4,
|
||||
EAM_IO_KEYPAD_2 = 5,
|
||||
EAM_IO_KEYPAD_5 = 6,
|
||||
EAM_IO_KEYPAD_8 = 7,
|
||||
EAM_IO_KEYPAD_DECIMAL = 8,
|
||||
EAM_IO_KEYPAD_3 = 9,
|
||||
EAM_IO_KEYPAD_6 = 10,
|
||||
EAM_IO_KEYPAD_9 = 11,
|
||||
|
||||
EAM_IO_KEYPAD_COUNT = 12, /* Not an actual scan code */
|
||||
|
||||
EAM_IO_INSERT = 13, /* SpiceTools Extension */
|
||||
};
|
||||
|
||||
extern std::string CARD_OVERRIDES[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);
|
||||
|
||||
void eamuse_card_insert(int unit);
|
||||
void eamuse_card_insert(int unit, const uint8_t *card);
|
||||
|
||||
bool eamuse_card_insert_consume(int active_count, int unit_id);
|
||||
|
||||
bool eamuse_coin_get_block();
|
||||
void eamuse_coin_set_block(bool block);
|
||||
|
||||
int eamuse_coin_get_stock();
|
||||
void eamuse_coin_set_stock(int amount);
|
||||
bool eamuse_coin_consume(int amount);
|
||||
int eamuse_coin_consume_stock();
|
||||
|
||||
int eamuse_coin_add();
|
||||
|
||||
void eamuse_coin_start_thread();
|
||||
void eamuse_coin_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);
|
||||
void eamuse_set_keypad_overrides_overlay(size_t unit, uint16_t keypad_state);
|
||||
|
||||
uint16_t eamuse_get_keypad_state(size_t unit);
|
||||
std::string eamuse_get_keypad_state_str(size_t unit);
|
||||
void eamuse_update_keypad_bindings();
|
||||
|
||||
bool eamuse_keypad_state_naive();
|
||||
|
||||
void eamuse_set_game(std::string game);
|
||||
|
||||
std::string eamuse_get_game();
|
||||
int eamuse_get_game_keypads();
|
||||
int eamuse_get_game_keypads_name();
|
||||
|
||||
void eamuse_autodetect_game();
|
||||
|
||||
void eamuse_scard_callback(uint8_t slot_no, card_info_t *cardinfo);
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "external/scard/scard.h"
|
||||
|
||||
/* From BT5 API for compatibility reasons.
|
||||
Scan codes for the so-called "10 key" button panel on each card reader. Each
|
||||
scan code corresponds to a bit position within the 16-bit bitfield that you
|
||||
return from eam_io_get_keypad_state(). */
|
||||
enum eam_io_keypad_scan_code {
|
||||
EAM_IO_KEYPAD_0 = 0,
|
||||
EAM_IO_KEYPAD_1 = 1,
|
||||
EAM_IO_KEYPAD_4 = 2,
|
||||
EAM_IO_KEYPAD_7 = 3,
|
||||
EAM_IO_KEYPAD_00 = 4,
|
||||
EAM_IO_KEYPAD_2 = 5,
|
||||
EAM_IO_KEYPAD_5 = 6,
|
||||
EAM_IO_KEYPAD_8 = 7,
|
||||
EAM_IO_KEYPAD_DECIMAL = 8,
|
||||
EAM_IO_KEYPAD_3 = 9,
|
||||
EAM_IO_KEYPAD_6 = 10,
|
||||
EAM_IO_KEYPAD_9 = 11,
|
||||
|
||||
EAM_IO_KEYPAD_COUNT = 12, /* Not an actual scan code */
|
||||
|
||||
EAM_IO_INSERT = 13, /* SpiceTools Extension */
|
||||
};
|
||||
|
||||
extern std::string CARD_OVERRIDES[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);
|
||||
|
||||
void eamuse_card_insert(int unit);
|
||||
void eamuse_card_insert(int unit, const uint8_t *card);
|
||||
|
||||
bool eamuse_card_insert_consume(int active_count, int unit_id);
|
||||
|
||||
bool eamuse_coin_get_block();
|
||||
void eamuse_coin_set_block(bool block);
|
||||
|
||||
int eamuse_coin_get_stock();
|
||||
void eamuse_coin_set_stock(int amount);
|
||||
bool eamuse_coin_consume(int amount);
|
||||
int eamuse_coin_consume_stock();
|
||||
|
||||
int eamuse_coin_add();
|
||||
|
||||
void eamuse_coin_start_thread();
|
||||
void eamuse_coin_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);
|
||||
void eamuse_set_keypad_overrides_overlay(size_t unit, uint16_t keypad_state);
|
||||
|
||||
uint16_t eamuse_get_keypad_state(size_t unit);
|
||||
std::string eamuse_get_keypad_state_str(size_t unit);
|
||||
void eamuse_update_keypad_bindings();
|
||||
|
||||
bool eamuse_keypad_state_naive();
|
||||
|
||||
void eamuse_set_game(std::string game);
|
||||
|
||||
std::string eamuse_get_game();
|
||||
int eamuse_get_game_keypads();
|
||||
int eamuse_get_game_keypads_name();
|
||||
|
||||
void eamuse_autodetect_game();
|
||||
|
||||
void eamuse_scard_callback(uint8_t slot_no, card_info_t *cardinfo);
|
||||
|
||||
+1062
-1062
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void extdev_attach();
|
||||
void extdev_detach();
|
||||
#pragma once
|
||||
|
||||
void extdev_attach();
|
||||
void extdev_detach();
|
||||
|
||||
+307
-307
@@ -1,307 +1,307 @@
|
||||
#include "sciunit.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "eamuse.h"
|
||||
#include "games/jb/jb.h"
|
||||
#include "games/jb/io.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// function definitions
|
||||
typedef int (__cdecl *cardunit_card_cardnumber_t)(const uint8_t *const, int, char *, int);
|
||||
|
||||
static cardunit_card_cardnumber_t cardunit_card_cardnumber = nullptr;
|
||||
|
||||
// state
|
||||
static HINSTANCE SCIUNIT_INSTANCE;
|
||||
static std::string SCIUNIT_INSTANCE_NAME = "sciunit.dll";
|
||||
static bool SCIUNIT_INITIALIZED = false;
|
||||
static bool CARD_IN = false;
|
||||
static bool CARD_PRESSED = false;
|
||||
static uint8_t CARD_UID[8];
|
||||
static int CARD_TYPE = 1;
|
||||
|
||||
static char CARD_DISPLAY_ID[17];
|
||||
static bool CARD_WAS_CONVERTED = false;
|
||||
|
||||
static inline void update_card() {
|
||||
if (eamuse_card_insert_consume(1, 0)) {
|
||||
if (!CARD_PRESSED) {
|
||||
CARD_PRESSED = true;
|
||||
|
||||
// get and check if valid card
|
||||
if (!eamuse_get_card(1, 0, CARD_UID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CARD_IN = true;
|
||||
CARD_WAS_CONVERTED = false;
|
||||
|
||||
CARD_TYPE = is_card_uid_felica(CARD_UID) ? 2 : 1;
|
||||
|
||||
// convert the card UID into the Konami ID if the conversion method was found
|
||||
if (cardunit_card_cardnumber != nullptr) {
|
||||
auto ret = cardunit_card_cardnumber(CARD_UID, CARD_TYPE, CARD_DISPLAY_ID, sizeof(CARD_DISPLAY_ID));
|
||||
|
||||
CARD_DISPLAY_ID[16] = '\0';
|
||||
CARD_WAS_CONVERTED = ret == 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CARD_PRESSED = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_check_removed(int /* a1 */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_eject_init(int /* a1 */) {
|
||||
CARD_IN = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_cardnumber(int a1, uint8_t *out) {
|
||||
if (CARD_WAS_CONVERTED) {
|
||||
|
||||
// copy the display id to the output buffer
|
||||
memcpy(out, CARD_DISPLAY_ID, 16);
|
||||
} else {
|
||||
|
||||
// fallback to displaying the card UID if the conversion method was not
|
||||
// found or the conversion to the Konami ID failed
|
||||
std::string konami_id = bin2hex(CARD_UID, sizeof(CARD_UID));
|
||||
memcpy(out, konami_id.c_str(), 16);
|
||||
}
|
||||
|
||||
// ensure the output is null-terminated
|
||||
out[16] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_cardtype(int /* a1 */) {
|
||||
return CARD_TYPE;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_stat(int /* a1 */) {
|
||||
return CARD_IN ? 2 : 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_uid(int /* a1 */, uint8_t *uid) {
|
||||
memcpy(uid, CARD_UID, 8);
|
||||
|
||||
return CARD_IN ? 0 : 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_detected(int /* a1 */) {
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_ejected(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_inserted(int /* a1 */) {
|
||||
update_card();
|
||||
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static bool __cdecl mng_card_is_invalid_unit(int /* a1 */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_read_done(int /* a1 */) {
|
||||
update_card();
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_ready(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_removed(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_valid_card(int /* a1 */) {
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_read_init(int /* a1 */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_sensor_raw(int /* a1 */) {
|
||||
update_card();
|
||||
return CARD_IN ? 48 : 0;
|
||||
}
|
||||
|
||||
static void __cdecl mng_card_sleep(int a1) {
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_finalize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_errorunit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_stat() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_version(int a1, int a2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_initialize() {
|
||||
SCIUNIT_INITIALIZED = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_is_initialized() {
|
||||
return SCIUNIT_INITIALIZED ? 1 : 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_is_usable() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_led_set_color(uint8_t *led_field) {
|
||||
|
||||
// Jubeat
|
||||
if (avs::game::is_model({ "J44", "K44", "L44" })) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::jb::get_lights();
|
||||
|
||||
// control mapping
|
||||
//
|
||||
// The list in `Lamp Check` is not in the same order as the LED array passed to this
|
||||
// function.
|
||||
static size_t mapping[] = {
|
||||
games::jb::Lights::PANEL_FRONT_R,
|
||||
games::jb::Lights::PANEL_FRONT_G,
|
||||
games::jb::Lights::PANEL_FRONT_B,
|
||||
games::jb::Lights::PANEL_TOP_R,
|
||||
games::jb::Lights::PANEL_TOP_G,
|
||||
games::jb::Lights::PANEL_TOP_B,
|
||||
games::jb::Lights::PANEL_LEFT_R,
|
||||
games::jb::Lights::PANEL_LEFT_G,
|
||||
games::jb::Lights::PANEL_LEFT_B,
|
||||
games::jb::Lights::PANEL_RIGHT_R,
|
||||
games::jb::Lights::PANEL_RIGHT_G,
|
||||
games::jb::Lights::PANEL_RIGHT_B,
|
||||
games::jb::Lights::PANEL_TITLE_R,
|
||||
games::jb::Lights::PANEL_TITLE_G,
|
||||
games::jb::Lights::PANEL_TITLE_B,
|
||||
games::jb::Lights::PANEL_WOOFER_R,
|
||||
games::jb::Lights::PANEL_WOOFER_G,
|
||||
games::jb::Lights::PANEL_WOOFER_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i]), led_field[i] / 255.f);
|
||||
}
|
||||
|
||||
RI_MGR->devices_flush_output();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_reset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_update() {
|
||||
if (SCIUNIT_INITIALIZED)
|
||||
update_card();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sciunit_attach() {
|
||||
|
||||
// get instance
|
||||
SCIUNIT_INSTANCE = libutils::try_module(SCIUNIT_INSTANCE_NAME);
|
||||
if (!SCIUNIT_INSTANCE) {
|
||||
log_info("sciunit", "skipping sciunit hooks");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("sciunit", "SpiceTools SCIUNIT");
|
||||
|
||||
// get functions
|
||||
if (cardunit_card_cardnumber == nullptr) {
|
||||
cardunit_card_cardnumber = libutils::try_proc_list<cardunit_card_cardnumber_t>(
|
||||
SCIUNIT_INSTANCE, {"cardunit_card_cardnumber", "?cardunit_card_cardnumber@@YAHQBEHPADH@Z"});
|
||||
}
|
||||
|
||||
// patch
|
||||
detour::inline_hook((void *) mng_card_check_removed, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_check_removed"));
|
||||
detour::inline_hook((void *) mng_card_eject_init, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_eject_init"));
|
||||
detour::inline_hook((void *) mng_card_get_cardnumber, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_cardnumber"));
|
||||
detour::inline_hook((void *) mng_card_get_cardtype, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_cardtype"));
|
||||
detour::inline_hook((void *) mng_card_get_stat, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_stat"));
|
||||
detour::inline_hook((void *) mng_card_get_uid, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_uid"));
|
||||
detour::inline_hook((void *) mng_card_is_detected, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_detected"));
|
||||
detour::inline_hook((void *) mng_card_is_ejected, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_ejected"));
|
||||
detour::inline_hook((void *) mng_card_is_inserted, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_inserted"));
|
||||
detour::inline_hook((void *) mng_card_is_invalid_unit, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_invalid_unit"));
|
||||
detour::inline_hook((void *) mng_card_is_read_done, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_read_done"));
|
||||
detour::inline_hook((void *) mng_card_is_ready, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_ready"));
|
||||
detour::inline_hook((void *) mng_card_is_removed, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_removed"));
|
||||
detour::inline_hook((void *) mng_card_is_valid_card, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_valid_card"));
|
||||
detour::inline_hook((void *) mng_card_read_init, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_read_init"));
|
||||
detour::inline_hook((void *) mng_card_sensor_raw, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_sensor_raw"));
|
||||
detour::inline_hook((void *) mng_card_sleep, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_sleep"));
|
||||
detour::inline_hook((void *) sciunit_finalize, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_finalize"));
|
||||
detour::inline_hook((void *) sciunit_get_errorunit, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_errorunit"));
|
||||
detour::inline_hook((void *) sciunit_get_stat, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_stat"));
|
||||
detour::inline_hook((void *) sciunit_get_version, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_version"));
|
||||
detour::inline_hook((void *) sciunit_initialize, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_initialize"));
|
||||
detour::inline_hook((void *) sciunit_is_initialized, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_is_initialized"));
|
||||
detour::inline_hook((void *) sciunit_is_usable, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_is_usable"));
|
||||
detour::inline_hook((void *) sciunit_led_set_color, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_led_set_color"));
|
||||
detour::inline_hook((void *) sciunit_reset, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_reset"));
|
||||
detour::inline_hook((void *) sciunit_update, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_update"));
|
||||
}
|
||||
|
||||
void sciunit_detach() {
|
||||
// TODO
|
||||
}
|
||||
#include "sciunit.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "eamuse.h"
|
||||
#include "games/jb/jb.h"
|
||||
#include "games/jb/io.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// function definitions
|
||||
typedef int (__cdecl *cardunit_card_cardnumber_t)(const uint8_t *const, int, char *, int);
|
||||
|
||||
static cardunit_card_cardnumber_t cardunit_card_cardnumber = nullptr;
|
||||
|
||||
// state
|
||||
static HINSTANCE SCIUNIT_INSTANCE;
|
||||
static std::string SCIUNIT_INSTANCE_NAME = "sciunit.dll";
|
||||
static bool SCIUNIT_INITIALIZED = false;
|
||||
static bool CARD_IN = false;
|
||||
static bool CARD_PRESSED = false;
|
||||
static uint8_t CARD_UID[8];
|
||||
static int CARD_TYPE = 1;
|
||||
|
||||
static char CARD_DISPLAY_ID[17];
|
||||
static bool CARD_WAS_CONVERTED = false;
|
||||
|
||||
static inline void update_card() {
|
||||
if (eamuse_card_insert_consume(1, 0)) {
|
||||
if (!CARD_PRESSED) {
|
||||
CARD_PRESSED = true;
|
||||
|
||||
// get and check if valid card
|
||||
if (!eamuse_get_card(1, 0, CARD_UID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CARD_IN = true;
|
||||
CARD_WAS_CONVERTED = false;
|
||||
|
||||
CARD_TYPE = is_card_uid_felica(CARD_UID) ? 2 : 1;
|
||||
|
||||
// convert the card UID into the Konami ID if the conversion method was found
|
||||
if (cardunit_card_cardnumber != nullptr) {
|
||||
auto ret = cardunit_card_cardnumber(CARD_UID, CARD_TYPE, CARD_DISPLAY_ID, sizeof(CARD_DISPLAY_ID));
|
||||
|
||||
CARD_DISPLAY_ID[16] = '\0';
|
||||
CARD_WAS_CONVERTED = ret == 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CARD_PRESSED = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_check_removed(int /* a1 */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_eject_init(int /* a1 */) {
|
||||
CARD_IN = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_cardnumber(int a1, uint8_t *out) {
|
||||
if (CARD_WAS_CONVERTED) {
|
||||
|
||||
// copy the display id to the output buffer
|
||||
memcpy(out, CARD_DISPLAY_ID, 16);
|
||||
} else {
|
||||
|
||||
// fallback to displaying the card UID if the conversion method was not
|
||||
// found or the conversion to the Konami ID failed
|
||||
std::string konami_id = bin2hex(CARD_UID, sizeof(CARD_UID));
|
||||
memcpy(out, konami_id.c_str(), 16);
|
||||
}
|
||||
|
||||
// ensure the output is null-terminated
|
||||
out[16] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_cardtype(int /* a1 */) {
|
||||
return CARD_TYPE;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_stat(int /* a1 */) {
|
||||
return CARD_IN ? 2 : 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_get_uid(int /* a1 */, uint8_t *uid) {
|
||||
memcpy(uid, CARD_UID, 8);
|
||||
|
||||
return CARD_IN ? 0 : 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_detected(int /* a1 */) {
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_ejected(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_inserted(int /* a1 */) {
|
||||
update_card();
|
||||
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static bool __cdecl mng_card_is_invalid_unit(int /* a1 */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_read_done(int /* a1 */) {
|
||||
update_card();
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_ready(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_removed(int /* a1 */) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_is_valid_card(int /* a1 */) {
|
||||
return static_cast<int>(CARD_IN);
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_read_init(int /* a1 */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl mng_card_sensor_raw(int /* a1 */) {
|
||||
update_card();
|
||||
return CARD_IN ? 48 : 0;
|
||||
}
|
||||
|
||||
static void __cdecl mng_card_sleep(int a1) {
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_finalize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_errorunit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_stat() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_get_version(int a1, int a2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_initialize() {
|
||||
SCIUNIT_INITIALIZED = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_is_initialized() {
|
||||
return SCIUNIT_INITIALIZED ? 1 : 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_is_usable() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_led_set_color(uint8_t *led_field) {
|
||||
|
||||
// Jubeat
|
||||
if (avs::game::is_model({ "J44", "K44", "L44" })) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::jb::get_lights();
|
||||
|
||||
// control mapping
|
||||
//
|
||||
// The list in `Lamp Check` is not in the same order as the LED array passed to this
|
||||
// function.
|
||||
static size_t mapping[] = {
|
||||
games::jb::Lights::PANEL_FRONT_R,
|
||||
games::jb::Lights::PANEL_FRONT_G,
|
||||
games::jb::Lights::PANEL_FRONT_B,
|
||||
games::jb::Lights::PANEL_TOP_R,
|
||||
games::jb::Lights::PANEL_TOP_G,
|
||||
games::jb::Lights::PANEL_TOP_B,
|
||||
games::jb::Lights::PANEL_LEFT_R,
|
||||
games::jb::Lights::PANEL_LEFT_G,
|
||||
games::jb::Lights::PANEL_LEFT_B,
|
||||
games::jb::Lights::PANEL_RIGHT_R,
|
||||
games::jb::Lights::PANEL_RIGHT_G,
|
||||
games::jb::Lights::PANEL_RIGHT_B,
|
||||
games::jb::Lights::PANEL_TITLE_R,
|
||||
games::jb::Lights::PANEL_TITLE_G,
|
||||
games::jb::Lights::PANEL_TITLE_B,
|
||||
games::jb::Lights::PANEL_WOOFER_R,
|
||||
games::jb::Lights::PANEL_WOOFER_G,
|
||||
games::jb::Lights::PANEL_WOOFER_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i]), led_field[i] / 255.f);
|
||||
}
|
||||
|
||||
RI_MGR->devices_flush_output();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_reset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl sciunit_update() {
|
||||
if (SCIUNIT_INITIALIZED)
|
||||
update_card();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sciunit_attach() {
|
||||
|
||||
// get instance
|
||||
SCIUNIT_INSTANCE = libutils::try_module(SCIUNIT_INSTANCE_NAME);
|
||||
if (!SCIUNIT_INSTANCE) {
|
||||
log_info("sciunit", "skipping sciunit hooks");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("sciunit", "SpiceTools SCIUNIT");
|
||||
|
||||
// get functions
|
||||
if (cardunit_card_cardnumber == nullptr) {
|
||||
cardunit_card_cardnumber = libutils::try_proc_list<cardunit_card_cardnumber_t>(
|
||||
SCIUNIT_INSTANCE, {"cardunit_card_cardnumber", "?cardunit_card_cardnumber@@YAHQBEHPADH@Z"});
|
||||
}
|
||||
|
||||
// patch
|
||||
detour::inline_hook((void *) mng_card_check_removed, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_check_removed"));
|
||||
detour::inline_hook((void *) mng_card_eject_init, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_eject_init"));
|
||||
detour::inline_hook((void *) mng_card_get_cardnumber, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_cardnumber"));
|
||||
detour::inline_hook((void *) mng_card_get_cardtype, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_cardtype"));
|
||||
detour::inline_hook((void *) mng_card_get_stat, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_stat"));
|
||||
detour::inline_hook((void *) mng_card_get_uid, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_get_uid"));
|
||||
detour::inline_hook((void *) mng_card_is_detected, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_detected"));
|
||||
detour::inline_hook((void *) mng_card_is_ejected, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_ejected"));
|
||||
detour::inline_hook((void *) mng_card_is_inserted, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_inserted"));
|
||||
detour::inline_hook((void *) mng_card_is_invalid_unit, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_invalid_unit"));
|
||||
detour::inline_hook((void *) mng_card_is_read_done, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_read_done"));
|
||||
detour::inline_hook((void *) mng_card_is_ready, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_ready"));
|
||||
detour::inline_hook((void *) mng_card_is_removed, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_removed"));
|
||||
detour::inline_hook((void *) mng_card_is_valid_card, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_is_valid_card"));
|
||||
detour::inline_hook((void *) mng_card_read_init, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_read_init"));
|
||||
detour::inline_hook((void *) mng_card_sensor_raw, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_sensor_raw"));
|
||||
detour::inline_hook((void *) mng_card_sleep, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "mng_card_sleep"));
|
||||
detour::inline_hook((void *) sciunit_finalize, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_finalize"));
|
||||
detour::inline_hook((void *) sciunit_get_errorunit, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_errorunit"));
|
||||
detour::inline_hook((void *) sciunit_get_stat, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_stat"));
|
||||
detour::inline_hook((void *) sciunit_get_version, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_get_version"));
|
||||
detour::inline_hook((void *) sciunit_initialize, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_initialize"));
|
||||
detour::inline_hook((void *) sciunit_is_initialized, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_is_initialized"));
|
||||
detour::inline_hook((void *) sciunit_is_usable, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_is_usable"));
|
||||
detour::inline_hook((void *) sciunit_led_set_color, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_led_set_color"));
|
||||
detour::inline_hook((void *) sciunit_reset, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_reset"));
|
||||
detour::inline_hook((void *) sciunit_update, libutils::try_proc(
|
||||
SCIUNIT_INSTANCE, "sciunit_update"));
|
||||
}
|
||||
|
||||
void sciunit_detach() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void sciunit_attach();
|
||||
void sciunit_detach();
|
||||
#pragma once
|
||||
|
||||
void sciunit_attach();
|
||||
void sciunit_detach();
|
||||
|
||||
+28
-28
@@ -1,28 +1,28 @@
|
||||
#include "sde.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "hooks/sleephook.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
void sde_init(std::string sde_path) {
|
||||
|
||||
// check trailing slash
|
||||
if (sde_path.length() > 0 && sde_path.back() == '\\') {
|
||||
sde_path.pop_back();
|
||||
}
|
||||
|
||||
// get PID
|
||||
auto pid = GetCurrentProcessId();
|
||||
|
||||
// try attach
|
||||
log_info("sde", "Attaching SDE ({}) to PID {}", sde_path, pid);
|
||||
std::string cmd = "sde.exe -attach-pid " + to_string(pid);
|
||||
std::thread t([sde_path, cmd]() {
|
||||
system((sde_path + "\\" + cmd).c_str());
|
||||
});
|
||||
t.detach();
|
||||
|
||||
// wait to make sure it went through
|
||||
Sleep(500);
|
||||
}
|
||||
#include "sde.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "hooks/sleephook.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
void sde_init(std::string sde_path) {
|
||||
|
||||
// check trailing slash
|
||||
if (sde_path.length() > 0 && sde_path.back() == '\\') {
|
||||
sde_path.pop_back();
|
||||
}
|
||||
|
||||
// get PID
|
||||
auto pid = GetCurrentProcessId();
|
||||
|
||||
// try attach
|
||||
log_info("sde", "Attaching SDE ({}) to PID {}", sde_path, pid);
|
||||
std::string cmd = "sde.exe -attach-pid " + to_string(pid);
|
||||
std::thread t([sde_path, cmd]() {
|
||||
system((sde_path + "\\" + cmd).c_str());
|
||||
});
|
||||
t.detach();
|
||||
|
||||
// wait to make sure it went through
|
||||
Sleep(500);
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void sde_init(std::string sde_path);
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void sde_init(std::string sde_path);
|
||||
|
||||
+120
-120
@@ -1,120 +1,120 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
#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
-139
@@ -1,139 +1,139 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
+376
-376
@@ -1,376 +1,376 @@
|
||||
// enable touch functions - set version to windows 7
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "wintouchemu.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
namespace wintouchemu {
|
||||
|
||||
// settings
|
||||
bool FORCE = false;
|
||||
bool LOG_FPS = false;
|
||||
|
||||
static inline bool is_emu_enabled() {
|
||||
return FORCE || !is_touch_available() || GRAPHICS_SHOW_CURSOR;
|
||||
}
|
||||
|
||||
static decltype(GetSystemMetrics) *GetSystemMetrics_orig = nullptr;
|
||||
static decltype(RegisterTouchWindow) *RegisterTouchWindow_orig = nullptr;
|
||||
|
||||
static int WINAPI GetSystemMetrics_hook(int nIndex) {
|
||||
|
||||
/*
|
||||
* fake touch screen
|
||||
* the game requires 0x01 and 0x02 flags to be set
|
||||
* 0x40 and 0x80 are set for completeness
|
||||
*/
|
||||
if (nIndex == 94)
|
||||
return 0x01 | 0x02 | 0x40 | 0x80;
|
||||
|
||||
// call original
|
||||
if (GetSystemMetrics_orig != nullptr) {
|
||||
return GetSystemMetrics_orig(nIndex);
|
||||
}
|
||||
|
||||
// return error
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL WINAPI RegisterTouchWindow_hook(HWND hwnd, ULONG ulFlags) {
|
||||
|
||||
// don't register it if the emu is enabled
|
||||
if (is_emu_enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// call default
|
||||
return RegisterTouchWindow_orig(hwnd, ulFlags);
|
||||
}
|
||||
|
||||
// state
|
||||
BOOL (WINAPI *GetTouchInputInfo_orig)(HANDLE, UINT, PTOUCHINPUT, int);
|
||||
bool USE_MOUSE = false;
|
||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
std::vector<TouchPoint> TOUCH_POINTS;
|
||||
HMODULE HOOKED_MODULE = nullptr;
|
||||
std::string WINDOW_TITLE_START = "";
|
||||
std::optional<std::string> WINDOW_TITLE_END = std::nullopt;
|
||||
bool INITIALIZED = false;
|
||||
|
||||
void hook(const char *window_title, HMODULE module) {
|
||||
|
||||
// hooks
|
||||
auto system_metrics_hook = detour::iat_try(
|
||||
"GetSystemMetrics", GetSystemMetrics_hook, module);
|
||||
auto register_touch_window_hook = detour::iat_try(
|
||||
"RegisterTouchWindow", RegisterTouchWindow_hook, module);
|
||||
|
||||
// don't hook twice
|
||||
if (GetSystemMetrics_orig == nullptr) {
|
||||
GetSystemMetrics_orig = system_metrics_hook;
|
||||
}
|
||||
if (RegisterTouchWindow_orig == nullptr) {
|
||||
RegisterTouchWindow_orig = register_touch_window_hook;
|
||||
}
|
||||
|
||||
// set module and title
|
||||
HOOKED_MODULE = module;
|
||||
WINDOW_TITLE_START = window_title;
|
||||
INITIALIZED = true;
|
||||
}
|
||||
|
||||
void hook_title_ends(const char *window_title_start, const char *window_title_end, HMODULE module) {
|
||||
hook(window_title_start, module);
|
||||
|
||||
WINDOW_TITLE_END = window_title_end;
|
||||
}
|
||||
|
||||
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||
|
||||
// check if original should be called
|
||||
if (hTouchInput != GetTouchInputInfoHook) {
|
||||
return GetTouchInputInfo_orig(hTouchInput, cInputs, pInputs, cbSize);
|
||||
}
|
||||
|
||||
// set touch inputs
|
||||
bool result = false;
|
||||
bool mouse_used = false;
|
||||
static bool mouse_state_old = false;
|
||||
for (UINT input = 0; input < cInputs; input++) {
|
||||
auto *touch_input = &pInputs[input];
|
||||
|
||||
// clear touch input
|
||||
touch_input->x = 0;
|
||||
touch_input->y = 0;
|
||||
touch_input->hSource = nullptr;
|
||||
touch_input->dwID = 0;
|
||||
touch_input->dwFlags = 0;
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
|
||||
// get touch event
|
||||
TouchEvent *touch_event = nullptr;
|
||||
if (TOUCH_EVENTS.size() > input) {
|
||||
touch_event = &TOUCH_EVENTS.at(input);
|
||||
}
|
||||
|
||||
// check touch point
|
||||
if (touch_event) {
|
||||
|
||||
// set touch point
|
||||
result = true;
|
||||
touch_input->x = touch_event->x * 100;
|
||||
touch_input->y = touch_event->y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = touch_event->id;
|
||||
touch_input->dwFlags = 0;
|
||||
switch (touch_event->type) {
|
||||
case TOUCH_DOWN:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
break;
|
||||
case TOUCH_MOVE:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
break;
|
||||
case TOUCH_UP:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
break;
|
||||
}
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
|
||||
} else if (USE_MOUSE && !mouse_used) {
|
||||
|
||||
// disable further mouse inputs this call
|
||||
mouse_used = true;
|
||||
|
||||
// get mouse state
|
||||
bool mouse_state = (GetKeyState(VK_LBUTTON) & 0x100) != 0;
|
||||
|
||||
// check event needs to be generated
|
||||
if (mouse_state || mouse_state_old) {
|
||||
|
||||
// get current cursor position
|
||||
POINT cursorPos {};
|
||||
GetCursorPos(&cursorPos);
|
||||
|
||||
// set touch input to mouse
|
||||
result = true;
|
||||
touch_input->x = cursorPos.x * 100;
|
||||
touch_input->y = cursorPos.y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = 0;
|
||||
touch_input->dwFlags = 0;
|
||||
if (mouse_state && !mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
} else if (mouse_state && mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
} else if (!mouse_state && mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
}
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
}
|
||||
|
||||
// save old state
|
||||
mouse_state_old = mouse_state;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* For some reason, Nostalgia won't show an active touch point unless a move event
|
||||
* triggers in the same frame. To work around this, we just supply a fake move
|
||||
* event if we didn't update the same pointer ID in the same call.
|
||||
*/
|
||||
|
||||
// find touch point which has no associated input event
|
||||
TouchPoint *touch_point = nullptr;
|
||||
for (auto &tp : TOUCH_POINTS) {
|
||||
bool found = false;
|
||||
for (UINT i = 0; i < cInputs; i++) {
|
||||
if (input > 0 && pInputs[i].dwID == tp.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
touch_point = &tp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if unused touch point was found
|
||||
if (touch_point) {
|
||||
|
||||
// set touch point
|
||||
result = true;
|
||||
touch_input->x = touch_point->x * 100;
|
||||
touch_input->y = touch_point->y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = touch_point->id;
|
||||
touch_input->dwFlags = 0;
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return result;
|
||||
}
|
||||
|
||||
void update() {
|
||||
|
||||
// check if initialized
|
||||
if (!INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need for hooks if touch is available
|
||||
if (!is_emu_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get window handle
|
||||
static HWND hWnd = nullptr;
|
||||
if (hWnd == nullptr) {
|
||||
|
||||
// start with the active foreground window
|
||||
hWnd = GetForegroundWindow();
|
||||
auto title = get_window_title(hWnd);
|
||||
|
||||
// if the foreground window does not match the window title start, find a window
|
||||
// that does
|
||||
if (!string_begins_with(title, WINDOW_TITLE_START)) {
|
||||
hWnd = FindWindowBeginsWith(WINDOW_TITLE_START);
|
||||
title = get_window_title(hWnd);
|
||||
}
|
||||
|
||||
// if a window title end is set, check to see if it matches
|
||||
if (WINDOW_TITLE_END.has_value() && !string_ends_with(title.c_str(), WINDOW_TITLE_END.value().c_str())) {
|
||||
hWnd = nullptr;
|
||||
title = "";
|
||||
|
||||
for (auto &window : find_windows_beginning_with(WINDOW_TITLE_START)) {
|
||||
auto check_title = get_window_title(window);
|
||||
if (string_ends_with(check_title.c_str(), WINDOW_TITLE_END.value().c_str())) {
|
||||
hWnd = std::move(window);
|
||||
title = std::move(check_title);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check window
|
||||
if (hWnd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
log_info("wintouchemu", "activating DirectX hooks");
|
||||
|
||||
// mouse position based input only
|
||||
touch_attach_dx_hook();
|
||||
USE_MOUSE = true;
|
||||
}
|
||||
|
||||
// hooks
|
||||
auto GetTouchInputInfo_orig_new = detour::iat_try(
|
||||
"GetTouchInputInfo", GetTouchInputInfoHook, HOOKED_MODULE);
|
||||
if (GetTouchInputInfo_orig == nullptr) {
|
||||
GetTouchInputInfo_orig = GetTouchInputInfo_orig_new;
|
||||
}
|
||||
}
|
||||
|
||||
// update touch events
|
||||
if (hWnd != nullptr) {
|
||||
|
||||
// get touch events
|
||||
TOUCH_EVENTS.clear();
|
||||
touch_get_events(TOUCH_EVENTS);
|
||||
|
||||
// get touch points
|
||||
TOUCH_POINTS.clear();
|
||||
touch_get_points(TOUCH_POINTS);
|
||||
|
||||
// get event count
|
||||
auto event_count = TOUCH_EVENTS.size();
|
||||
if (USE_MOUSE) {
|
||||
event_count++;
|
||||
}
|
||||
|
||||
// for the fake move events
|
||||
event_count += MAX(0, (int) (TOUCH_POINTS.size() - TOUCH_EVENTS.size()));
|
||||
|
||||
// check if new events are available
|
||||
if (event_count > 0) {
|
||||
|
||||
// send fake event to make the game update it's touch inputs
|
||||
auto wndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
||||
wndProc(hWnd, WM_TOUCH, MAKEWORD(event_count, 0), (LPARAM) GetTouchInputInfoHook);
|
||||
}
|
||||
|
||||
// update frame logging
|
||||
if (LOG_FPS) {
|
||||
static int log_frames = 0;
|
||||
static uint64_t log_time = 0;
|
||||
log_frames++;
|
||||
if (log_time < get_system_seconds()) {
|
||||
if (log_time > 0) {
|
||||
log_info("wintouchemu", "polling at {} touch frames per second", log_frames);
|
||||
}
|
||||
log_frames = 0;
|
||||
log_time = get_system_seconds();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// enable touch functions - set version to windows 7
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "wintouchemu.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/time.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
namespace wintouchemu {
|
||||
|
||||
// settings
|
||||
bool FORCE = false;
|
||||
bool LOG_FPS = false;
|
||||
|
||||
static inline bool is_emu_enabled() {
|
||||
return FORCE || !is_touch_available() || GRAPHICS_SHOW_CURSOR;
|
||||
}
|
||||
|
||||
static decltype(GetSystemMetrics) *GetSystemMetrics_orig = nullptr;
|
||||
static decltype(RegisterTouchWindow) *RegisterTouchWindow_orig = nullptr;
|
||||
|
||||
static int WINAPI GetSystemMetrics_hook(int nIndex) {
|
||||
|
||||
/*
|
||||
* fake touch screen
|
||||
* the game requires 0x01 and 0x02 flags to be set
|
||||
* 0x40 and 0x80 are set for completeness
|
||||
*/
|
||||
if (nIndex == 94)
|
||||
return 0x01 | 0x02 | 0x40 | 0x80;
|
||||
|
||||
// call original
|
||||
if (GetSystemMetrics_orig != nullptr) {
|
||||
return GetSystemMetrics_orig(nIndex);
|
||||
}
|
||||
|
||||
// return error
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL WINAPI RegisterTouchWindow_hook(HWND hwnd, ULONG ulFlags) {
|
||||
|
||||
// don't register it if the emu is enabled
|
||||
if (is_emu_enabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// call default
|
||||
return RegisterTouchWindow_orig(hwnd, ulFlags);
|
||||
}
|
||||
|
||||
// state
|
||||
BOOL (WINAPI *GetTouchInputInfo_orig)(HANDLE, UINT, PTOUCHINPUT, int);
|
||||
bool USE_MOUSE = false;
|
||||
std::vector<TouchEvent> TOUCH_EVENTS;
|
||||
std::vector<TouchPoint> TOUCH_POINTS;
|
||||
HMODULE HOOKED_MODULE = nullptr;
|
||||
std::string WINDOW_TITLE_START = "";
|
||||
std::optional<std::string> WINDOW_TITLE_END = std::nullopt;
|
||||
bool INITIALIZED = false;
|
||||
|
||||
void hook(const char *window_title, HMODULE module) {
|
||||
|
||||
// hooks
|
||||
auto system_metrics_hook = detour::iat_try(
|
||||
"GetSystemMetrics", GetSystemMetrics_hook, module);
|
||||
auto register_touch_window_hook = detour::iat_try(
|
||||
"RegisterTouchWindow", RegisterTouchWindow_hook, module);
|
||||
|
||||
// don't hook twice
|
||||
if (GetSystemMetrics_orig == nullptr) {
|
||||
GetSystemMetrics_orig = system_metrics_hook;
|
||||
}
|
||||
if (RegisterTouchWindow_orig == nullptr) {
|
||||
RegisterTouchWindow_orig = register_touch_window_hook;
|
||||
}
|
||||
|
||||
// set module and title
|
||||
HOOKED_MODULE = module;
|
||||
WINDOW_TITLE_START = window_title;
|
||||
INITIALIZED = true;
|
||||
}
|
||||
|
||||
void hook_title_ends(const char *window_title_start, const char *window_title_end, HMODULE module) {
|
||||
hook(window_title_start, module);
|
||||
|
||||
WINDOW_TITLE_END = window_title_end;
|
||||
}
|
||||
|
||||
static BOOL WINAPI GetTouchInputInfoHook(HANDLE hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) {
|
||||
|
||||
// check if original should be called
|
||||
if (hTouchInput != GetTouchInputInfoHook) {
|
||||
return GetTouchInputInfo_orig(hTouchInput, cInputs, pInputs, cbSize);
|
||||
}
|
||||
|
||||
// set touch inputs
|
||||
bool result = false;
|
||||
bool mouse_used = false;
|
||||
static bool mouse_state_old = false;
|
||||
for (UINT input = 0; input < cInputs; input++) {
|
||||
auto *touch_input = &pInputs[input];
|
||||
|
||||
// clear touch input
|
||||
touch_input->x = 0;
|
||||
touch_input->y = 0;
|
||||
touch_input->hSource = nullptr;
|
||||
touch_input->dwID = 0;
|
||||
touch_input->dwFlags = 0;
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
|
||||
// get touch event
|
||||
TouchEvent *touch_event = nullptr;
|
||||
if (TOUCH_EVENTS.size() > input) {
|
||||
touch_event = &TOUCH_EVENTS.at(input);
|
||||
}
|
||||
|
||||
// check touch point
|
||||
if (touch_event) {
|
||||
|
||||
// set touch point
|
||||
result = true;
|
||||
touch_input->x = touch_event->x * 100;
|
||||
touch_input->y = touch_event->y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = touch_event->id;
|
||||
touch_input->dwFlags = 0;
|
||||
switch (touch_event->type) {
|
||||
case TOUCH_DOWN:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
break;
|
||||
case TOUCH_MOVE:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
break;
|
||||
case TOUCH_UP:
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
break;
|
||||
}
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
|
||||
} else if (USE_MOUSE && !mouse_used) {
|
||||
|
||||
// disable further mouse inputs this call
|
||||
mouse_used = true;
|
||||
|
||||
// get mouse state
|
||||
bool mouse_state = (GetKeyState(VK_LBUTTON) & 0x100) != 0;
|
||||
|
||||
// check event needs to be generated
|
||||
if (mouse_state || mouse_state_old) {
|
||||
|
||||
// get current cursor position
|
||||
POINT cursorPos {};
|
||||
GetCursorPos(&cursorPos);
|
||||
|
||||
// set touch input to mouse
|
||||
result = true;
|
||||
touch_input->x = cursorPos.x * 100;
|
||||
touch_input->y = cursorPos.y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = 0;
|
||||
touch_input->dwFlags = 0;
|
||||
if (mouse_state && !mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_DOWN;
|
||||
} else if (mouse_state && mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
} else if (!mouse_state && mouse_state_old) {
|
||||
touch_input->dwFlags |= TOUCHEVENTF_UP;
|
||||
}
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
}
|
||||
|
||||
// save old state
|
||||
mouse_state_old = mouse_state;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* For some reason, Nostalgia won't show an active touch point unless a move event
|
||||
* triggers in the same frame. To work around this, we just supply a fake move
|
||||
* event if we didn't update the same pointer ID in the same call.
|
||||
*/
|
||||
|
||||
// find touch point which has no associated input event
|
||||
TouchPoint *touch_point = nullptr;
|
||||
for (auto &tp : TOUCH_POINTS) {
|
||||
bool found = false;
|
||||
for (UINT i = 0; i < cInputs; i++) {
|
||||
if (input > 0 && pInputs[i].dwID == tp.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
touch_point = &tp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if unused touch point was found
|
||||
if (touch_point) {
|
||||
|
||||
// set touch point
|
||||
result = true;
|
||||
touch_input->x = touch_point->x * 100;
|
||||
touch_input->y = touch_point->y * 100;
|
||||
touch_input->hSource = hTouchInput;
|
||||
touch_input->dwID = touch_point->id;
|
||||
touch_input->dwFlags = 0;
|
||||
touch_input->dwFlags |= TOUCHEVENTF_MOVE;
|
||||
touch_input->dwMask = 0;
|
||||
touch_input->dwTime = 0;
|
||||
touch_input->dwExtraInfo = 0;
|
||||
touch_input->cxContact = 0;
|
||||
touch_input->cyContact = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return result;
|
||||
}
|
||||
|
||||
void update() {
|
||||
|
||||
// check if initialized
|
||||
if (!INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need for hooks if touch is available
|
||||
if (!is_emu_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get window handle
|
||||
static HWND hWnd = nullptr;
|
||||
if (hWnd == nullptr) {
|
||||
|
||||
// start with the active foreground window
|
||||
hWnd = GetForegroundWindow();
|
||||
auto title = get_window_title(hWnd);
|
||||
|
||||
// if the foreground window does not match the window title start, find a window
|
||||
// that does
|
||||
if (!string_begins_with(title, WINDOW_TITLE_START)) {
|
||||
hWnd = FindWindowBeginsWith(WINDOW_TITLE_START);
|
||||
title = get_window_title(hWnd);
|
||||
}
|
||||
|
||||
// if a window title end is set, check to see if it matches
|
||||
if (WINDOW_TITLE_END.has_value() && !string_ends_with(title.c_str(), WINDOW_TITLE_END.value().c_str())) {
|
||||
hWnd = nullptr;
|
||||
title = "";
|
||||
|
||||
for (auto &window : find_windows_beginning_with(WINDOW_TITLE_START)) {
|
||||
auto check_title = get_window_title(window);
|
||||
if (string_ends_with(check_title.c_str(), WINDOW_TITLE_END.value().c_str())) {
|
||||
hWnd = std::move(window);
|
||||
title = std::move(check_title);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check window
|
||||
if (hWnd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
log_info("wintouchemu", "activating DirectX hooks");
|
||||
|
||||
// mouse position based input only
|
||||
touch_attach_dx_hook();
|
||||
USE_MOUSE = true;
|
||||
}
|
||||
|
||||
// hooks
|
||||
auto GetTouchInputInfo_orig_new = detour::iat_try(
|
||||
"GetTouchInputInfo", GetTouchInputInfoHook, HOOKED_MODULE);
|
||||
if (GetTouchInputInfo_orig == nullptr) {
|
||||
GetTouchInputInfo_orig = GetTouchInputInfo_orig_new;
|
||||
}
|
||||
}
|
||||
|
||||
// update touch events
|
||||
if (hWnd != nullptr) {
|
||||
|
||||
// get touch events
|
||||
TOUCH_EVENTS.clear();
|
||||
touch_get_events(TOUCH_EVENTS);
|
||||
|
||||
// get touch points
|
||||
TOUCH_POINTS.clear();
|
||||
touch_get_points(TOUCH_POINTS);
|
||||
|
||||
// get event count
|
||||
auto event_count = TOUCH_EVENTS.size();
|
||||
if (USE_MOUSE) {
|
||||
event_count++;
|
||||
}
|
||||
|
||||
// for the fake move events
|
||||
event_count += MAX(0, (int) (TOUCH_POINTS.size() - TOUCH_EVENTS.size()));
|
||||
|
||||
// check if new events are available
|
||||
if (event_count > 0) {
|
||||
|
||||
// send fake event to make the game update it's touch inputs
|
||||
auto wndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
||||
wndProc(hWnd, WM_TOUCH, MAKEWORD(event_count, 0), (LPARAM) GetTouchInputInfoHook);
|
||||
}
|
||||
|
||||
// update frame logging
|
||||
if (LOG_FPS) {
|
||||
static int log_frames = 0;
|
||||
static uint64_t log_time = 0;
|
||||
log_frames++;
|
||||
if (log_time < get_system_seconds()) {
|
||||
if (log_time > 0) {
|
||||
log_info("wintouchemu", "polling at {} touch frames per second", log_frames);
|
||||
}
|
||||
log_frames = 0;
|
||||
log_time = get_system_seconds();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace wintouchemu {
|
||||
|
||||
// settings
|
||||
extern bool FORCE;
|
||||
extern bool LOG_FPS;
|
||||
|
||||
void hook(const char *window_title, HMODULE module = nullptr);
|
||||
void hook_title_ends(
|
||||
const char *window_title_start,
|
||||
const char *window_title_ends,
|
||||
HMODULE module = nullptr);
|
||||
void update();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace wintouchemu {
|
||||
|
||||
// settings
|
||||
extern bool FORCE;
|
||||
extern bool LOG_FPS;
|
||||
|
||||
void hook(const char *window_title, HMODULE module = nullptr);
|
||||
void hook_title_ends(
|
||||
const char *window_title_start,
|
||||
const char *window_title_ends,
|
||||
HMODULE module = nullptr);
|
||||
void update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user