chore: CRLF to LF

This commit is contained in:
smpn2
2022-12-16 23:18:35 +09:00
parent b7a60638a4
commit 6c914266d9
725 changed files with 131020 additions and 131020 deletions
+294 -294
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,5 +1,5 @@
#pragma once
void spicedevice_attach();
void spicedevice_detach();
#pragma once
void spicedevice_attach();
void spicedevice_detach();
+75 -75
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,4 +1,4 @@
#pragma once
void extdev_attach();
void extdev_detach();
#pragma once
void extdev_attach();
void extdev_detach();
+307 -307
View File
@@ -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
View File
@@ -1,4 +1,4 @@
#pragma once
void sciunit_attach();
void sciunit_detach();
#pragma once
void sciunit_attach();
void sciunit_detach();
+28 -28
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}