Make HKB hook public, with unity option fixes

This commit is contained in:
Hay1tsme 2024-06-08 00:43:06 -04:00
parent 94f9357382
commit 7051b849fa
23 changed files with 1054 additions and 0 deletions

View File

@ -104,6 +104,21 @@ $(BUILD_DIR_ZIP)/mu3.zip:
$(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll} $(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip * $(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip *
$(BUILD_DIR_ZIP)/hkb.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/hkb
$(V)mkdir -p $(BUILD_DIR_ZIP)/hkb/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/hkbhook/hkbhook.dll \
$(DIST_DIR)/hkb/segatools.ini \
$(DIST_DIR)/hkb/start.bat \
$(BUILD_DIR_ZIP)/hkb
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/hkb/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/hkb/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/hkb ; zip -r ../hkb.zip *
$(BUILD_DIR_ZIP)/mai2.zip: $(BUILD_DIR_ZIP)/mai2.zip:
$(V)echo ... $@ $(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/mai2 $(V)mkdir -p $(BUILD_DIR_ZIP)/mai2
@ -136,6 +151,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/idz.zip \ $(BUILD_DIR_ZIP)/idz.zip \
$(BUILD_DIR_ZIP)/mercury.zip \ $(BUILD_DIR_ZIP)/mercury.zip \
$(BUILD_DIR_ZIP)/mu3.zip \ $(BUILD_DIR_ZIP)/mu3.zip \
$(BUILD_DIR_ZIP)/hkb.zip \
$(BUILD_DIR_ZIP)/mai2.zip \ $(BUILD_DIR_ZIP)/mai2.zip \
CHANGELOG.md \ CHANGELOG.md \
README.md \ README.md \

41
dist/hkb/segatools.ini vendored Normal file
View File

@ -0,0 +1,41 @@
[vfs]
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
amfs=amfs
; Create an empty directory somewhere and insert the path here.
; This directory may be shared between multiple SEGA games.
; NOTE: This has nothing to do with Windows %APPDATA%.
appdata=appdata
option=Option
[dns]
; Insert the hostname or IP address of the server you wish to use here.
; Note that 127.0.0.1, localhost etc are specifically rejected.
default=127.0.0.1
[ds]
; Region code on the emulated AMEX board DS EEPROM.
; 1: Japan
; 4: Export (some UI elements in English)
;
; NOTE: Changing this setting causes a factory reset.
region=1
[netenv]
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
; SEGA games are somewhat picky about their LAN environment, so leaving this
; setting enabled is recommended.
enable=1
[keychip]
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
; If you disable netenv then you must set this to your LAN's IP subnet, and
; that subnet must start with 192.168.
subnet=192.168.155.0
[gfx]
enable=1
[io4]
test=0x31
service=0x32
coin=0x33

11
dist/hkb/start.bat vendored Normal file
View File

@ -0,0 +1,11 @@
@echo off
pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
start inject.exe -d -k hkbhook.dll amdaemon.exe -f -c common.json terminal.json satellite.json
inject.exe -d -k hkbhook.dll hkb.exe -screen-fullscreen 0 -screen-quality Fantastic -silent-crashes -logFile gameLog.txt
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

52
hkbhook/config.c Normal file
View File

@ -0,0 +1,52 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "hkbhook/config.h"
#include "platform/config.h"
void hkb_dll_config_load(
struct hkb_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"hkbio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void hkb_led_config_load(
struct hkb_led_config *cfg,
const wchar_t *filename)
{
cfg->enable = GetPrivateProfileIntW(L"led", L"enable", 1, filename);
}
void hkb_hook_config_load(
struct hkb_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
gfx_config_load(&cfg->gfx, filename);
hkb_dll_config_load(&cfg->dll, filename);
hkb_led_config_load(&cfg->led, filename);
}

33
hkbhook/config.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/gfx.h"
#include "hooklib/dvd.h"
#include "hkbhook/hkb-dll.h"
#include "platform/config.h"
#include "hkbhook/led.h"
struct hkb_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct hkb_dll_config dll;
struct hkb_led_config led;
};
void hkb_dll_config_load(
struct hkb_dll_config *cfg,
const wchar_t *filename);
void hkb_hook_config_load(
struct hkb_hook_config *cfg,
const wchar_t *filename);

154
hkbhook/dllmain.c Normal file
View File

@ -0,0 +1,154 @@
#include <windows.h>
#include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "gfxhook/d3d9.h"
#include "gfxhook/d3d11.h"
#include "gfxhook/dxgi.h"
#include "gfxhook/gfx.h"
#include "hook/process.h"
#include "hooklib/dvd.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "hooklib/cursor.h"
#include "hkbhook/config.h"
#include "hkbhook/io4.h"
#include "hkbhook/led.h"
#include "hkbhook/hkb-dll.h"
#include "hkbhook/unity.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE hkb_hook_mod;
static process_entry_t hkb_startup;
static struct hkb_hook_config hkb_hook_cfg;
static DWORD CALLBACK hkb_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin hkb_pre_startup ---\n");
/* Load config */
hkb_hook_config_load(&hkb_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&hkb_hook_cfg.dvd, hkb_hook_mod);
gfx_hook_init(&hkb_hook_cfg.gfx);
gfx_d3d9_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
gfx_d3d11_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
gfx_dxgi_hook_init(&hkb_hook_cfg.gfx, hkb_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&hkb_hook_cfg.platform,
"SDEC",
"ACA2",
hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
cursor_hook_init();
// COM1: Touch
// COM2: LED (Satalite)
// COM3: Reader (Satalite)
// COM3: LED (Terminal)
if (FAILED(hr)) {
goto fail;
}
if (hkb_hook_cfg.platform.nusec.platform_id[0] != '\0' && hkb_hook_cfg.platform.nusec.platform_id[3] == '4') {
hr = led_hook_init(&hkb_hook_cfg.led, 3);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&hkb_hook_cfg.aime, 1, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(4);
}
else {
hr = led_hook_init(&hkb_hook_cfg.led, 2);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&hkb_hook_cfg.aime, 3, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(1);
}
if (FAILED(hr)) {
goto fail;
}
hr = hkb_dll_init(&hkb_hook_cfg.dll, hkb_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = hkb_io4_hook_init(&hkb_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `hkbhook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End hkb_pre_startup ---\n");
/* Jump to EXE start address */
return hkb_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
hkb_hook_mod = mod;
hr = process_hijack_startup(hkb_pre_startup, &hkb_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

103
hkbhook/hkb-dll.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "hkbhook/hkb-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym hkb_dll_syms[] = {
{
.sym = "hkb_io_init",
.off = offsetof(struct hkb_dll, init),
}, {
.sym = "hkb_io_poll",
.off = offsetof(struct hkb_dll, poll),
},
};
struct hkb_dll hkb_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT hkb_dll_init(const struct hkb_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("Ongeki IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "hkb_io_get_api_version");
if (get_api_version != NULL) {
hkb_dll.api_version = get_api_version();
} else {
hkb_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose hkb_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (hkb_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Ongeki IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
hkb_dll.api_version);
goto end;
}
sym = hkb_dll_syms;
hr = dll_bind(&hkb_dll, src, &sym, _countof(hkb_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Ongeki IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

19
hkbhook/hkb-dll.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <windows.h>
#include "hkbio/hkbio.h"
struct hkb_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(uint8_t *opbtn, uint8_t *gamebtn);
};
struct hkb_dll_config {
wchar_t path[MAX_PATH];
};
extern struct hkb_dll hkb_dll;
HRESULT hkb_dll_init(const struct hkb_dll_config *cfg, HINSTANCE self);

22
hkbhook/hkbhook.def Normal file
View File

@ -0,0 +1,22 @@
LIBRARY hkbhook
EXPORTS
CreateDXGIFactory
CreateDXGIFactory1
CreateDXGIFactory2
D3D11CreateDevice
D3D11CreateDeviceAndSwapChain
Direct3DCreate9
aime_io_get_api_version
aime_io_init
aime_io_led_set_color
aime_io_nfc_get_aime_id
aime_io_nfc_get_felica_id
aime_io_nfc_poll
amDllVideoClose @2
amDllVideoGetVBiosVersion @4
amDllVideoOpen @1
amDllVideoSetResolution @3
hkb_io_get_api_version
hkb_io_init
hkb_io_poll

109
hkbhook/io4.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/io4.h"
#include "hkbhook/hkb-dll.h"
#include "util/dprintf.h"
bool hkb_io_coin = false;
uint16_t hkb_io_coins = 0;
static HRESULT hkb_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops hkb_io4_ops = {
.poll = hkb_io4_poll,
};
HRESULT hkb_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(hkb_dll.init != NULL);
hr = io4_hook_init(cfg, &hkb_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return hkb_dll.init();
}
static HRESULT hkb_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn;
uint8_t gamebtn;
HRESULT hr;
assert(hkb_dll.poll != NULL);
memset(state, 0, sizeof(*state));
hr = hkb_dll.poll(&opbtn, &gamebtn);
if (FAILED(hr)) {
return hr;
}
if (opbtn & HKB_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & HKB_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & HKB_IO_OPBTN_COIN) {
if (!hkb_io_coin) {
hkb_io_coin = true;
hkb_io_coins++;
state->buttons[0] |= 1 << 25;
}
}
else {
hkb_io_coin = false;
}
state->chutes[0] = 128 + 256 * hkb_io_coins;
if (gamebtn & HKB_IO_GAMEBTN_RIGHT) {
state->buttons[1] |= 1 << 1;
}
if (gamebtn & HKB_IO_GAMEBTN_LEFT) {
state->buttons[1] |= 1 << 0;
}
if (gamebtn & HKB_IO_GAMEBTN_UP) {
state->buttons[1] |= 1 << 15;
}
if (gamebtn & HKB_IO_GAMEBTN_DOWN) {
state->buttons[1] |= 1 << 14;
}
if (gamebtn & HKB_IO_GAMEBTN_ENTER) {
state->buttons[1] |= 1 << 13;
}
if (gamebtn & HKB_IO_GAMEBTN_CANCEL) {
state->buttons[1] |= 1 << 12;
}
if (gamebtn & HKB_IO_GAMEBTN_ARR_RIGHT) {
state->buttons[1] |= 1 << 2;
}
if (gamebtn & HKB_IO_GAMEBTN_ARR_LEFT) {
state->buttons[1] |= 1 << 3;
}
return S_OK;
}

7
hkbhook/io4.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/io4.h"
HRESULT hkb_io4_hook_init(const struct io4_config *cfg);

98
hkbhook/led.c Normal file
View File

@ -0,0 +1,98 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
#include "hkbhook/led.h"
static HRESULT hkb_led_handle_irp(struct irp *irp);
static HRESULT hkb_led_handle_irp_locked(struct irp *irp);
static CRITICAL_SECTION hkb_led_lock;
static bool hkb_led_started;
static struct uart hkb_led_uart;
static uint8_t hkb_led_written_bytes[520];
static uint8_t hkb_led_readable_bytes[520];
HRESULT led_hook_init(const struct hkb_led_config *cfg, uint8_t port)
{
if (!cfg->enable) {
return S_OK;
}
InitializeCriticalSection(&hkb_led_lock);
uart_init(&hkb_led_uart, port);
hkb_led_uart.written.bytes = hkb_led_written_bytes;
hkb_led_uart.written.nbytes = sizeof(hkb_led_written_bytes);
hkb_led_uart.readable.bytes = hkb_led_readable_bytes;
hkb_led_uart.readable.nbytes = sizeof(hkb_led_readable_bytes);
return iohook_push_handler(hkb_led_handle_irp);
}
static HRESULT hkb_led_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&hkb_led_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&hkb_led_lock);
hr = hkb_led_handle_irp_locked(irp);
LeaveCriticalSection(&hkb_led_lock);
return hr;
}
static HRESULT hkb_led_handle_irp_locked(struct irp *irp)
{
HRESULT hr;
#if 1
if (irp->op == IRP_OP_WRITE) {
dprintf("WRITE:\n");
dump_const_iobuf(&irp->write);
}
#endif
#if 0
if (irp->op == IRP_OP_READ) {
dprintf("READ:\n");
dump_iobuf(&hkb_led_uart.readable);
}
#endif
if (irp->op == IRP_OP_OPEN) {
/* Unfortunately the card reader UART gets opened and closed
repeatedly */
if (!hkb_led_started) {
dprintf("Led: Open\n");
hkb_led_started = true;
} else {
return S_OK;
}
}
hr = uart_handle_irp(&hkb_led_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
hkb_led_uart.written.pos = 0;
return hr;
}

39
hkbhook/led.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push, 1)
struct hkb_led_config {
bool enable;
};
struct hbk_led_packet_head {
uint8_t sync;
uint8_t dst;
uint8_t src;
uint8_t len;
};
struct hbk_led_req_header {
struct hbk_led_packet_head head;
uint8_t cmd;
};
struct hbk_led_resp_header {
struct hbk_led_packet_head head;
uint8_t status;
uint8_t cmd;
uint8_t report;
};
struct hbk_led_packet_tail {
uint8_t checksum;
};
struct hbk_led_req_any {
struct hbk_led_req_header header;
struct hbk_led_packet_tail tail;
};
struct hbk_led_resp_any {
struct hbk_led_resp_header header;
struct hbk_led_packet_tail tail;
};
#pragma pack(pop)
HRESULT led_hook_init(const struct hkb_led_config *cfg, uint8_t port);

35
hkbhook/meson.build Normal file
View File

@ -0,0 +1,35 @@
shared_library(
'hkbhook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'hkbhook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
xinput_lib,
],
link_with : [
aimeio_lib,
board_lib,
gfxhook_lib,
hooklib_lib,
hkbio_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'io4.c',
'io4.h',
'led.c',
'led.h',
'hkb-dll.c',
'hkb-dll.h',
'unity.h',
'unity.c',
],
)

105
hkbhook/unity.c Normal file
View File

@ -0,0 +1,105 @@
#include <stdbool.h>
#include <windows.h>
#include "hook/table.h"
#include "hook/iohook.h"
#include "hooklib/dll.h"
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hooklib/serial.h"
#include "util/dprintf.h"
static void dll_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name);
static const struct hook_symbol unity_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = my_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
},
};
static const wchar_t *target_modules[] = {
L"mono.dll",
L"cri_ware_unity.dll",
L"HKBSys_api.dll",
L"amdaemon_api.dll",
};
static const size_t target_modules_len = _countof(target_modules);
void unity_hook_init(void)
{
dll_hook_insert_hooks(NULL);
}
static void dll_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
target,
"kernel32.dll",
unity_kernel32_syms,
_countof(unity_kernel32_syms));
}
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
{
const wchar_t *name_end;
const wchar_t *target_module;
bool already_loaded;
HMODULE result;
size_t name_len;
size_t target_module_len;
if (name == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// Check if the module is already loaded
already_loaded = GetModuleHandleW(name) != NULL;
// Must call the next handler so the DLL reference count is incremented
result = next_LoadLibraryW(name);
if (!already_loaded && result != NULL) {
name_len = wcslen(name);
for (size_t i = 0; i < target_modules_len; i++) {
target_module = target_modules[i];
target_module_len = wcslen(target_module);
// Check if the newly loaded library is at least the length of
// the name of the target module
if (name_len < target_module_len) {
continue;
}
name_end = &name[name_len - target_module_len];
// Check if the name of the newly loaded library is one of the
// modules the path hooks should be injected into
if (_wcsicmp(name_end, target_module) != 0) {
continue;
}
dprintf("Unity: Loaded %S\n", target_module);
dll_hook_insert_hooks(result);
path_hook_insert_hooks(result);
reg_hook_insert_hooks(result);
proc_addr_insert_hooks(result);
serial_hook_apply_hooks(result);
iohook_apply_hooks(result);
}
}
return result;
}

3
hkbhook/unity.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void unity_hook_init(void);

31
hkbio/config.c Normal file
View File

@ -0,0 +1,31 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "hkbio/config.h"
void hkb_io_config_load(
struct hkb_io_config *cfg,
const wchar_t *filename)
{
wchar_t key[240];
int i;
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
cfg->vk_up = GetPrivateProfileIntW(L"io4", L"up", VK_UP, filename);
cfg->vk_down = GetPrivateProfileIntW(L"io4", L"down", VK_DOWN, filename);
cfg->vk_left = GetPrivateProfileIntW(L"io4", L"left", VK_LEFT, filename);
cfg->vk_right = GetPrivateProfileIntW(L"io4", L"right", VK_RIGHT, filename);
cfg->vk_enter = GetPrivateProfileIntW(L"io4", L"enter", VK_SPACE, filename);
cfg->vk_cancel = GetPrivateProfileIntW(L"io4", L"cancel", VK_LSHIFT, filename);
cfg->vk_arr_right = GetPrivateProfileIntW(L"io4", L"right_arrow", 'A', filename);
cfg->vk_arr_left = GetPrivateProfileIntW(L"io4", L"left_arrow", 'D', filename);
}

24
hkbio/config.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct hkb_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_up;
uint8_t vk_down;
uint8_t vk_left;
uint8_t vk_right;
uint8_t vk_enter;
uint8_t vk_cancel;
uint8_t vk_arr_right;
uint8_t vk_arr_left;
};
void hkb_io_config_load(
struct hkb_io_config *cfg,
const wchar_t *filename);

74
hkbio/hkbio.c Normal file
View File

@ -0,0 +1,74 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include "hkbio/hkbio.h"
#include "hkbio/config.h"
static struct hkb_io_config cfg;
uint16_t hkb_io_get_api_version(void)
{
return 0x0100;
}
HRESULT hkb_io_init(void)
{
hkb_io_config_load(&cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT hkb_io_poll(uint8_t *opbtn, uint8_t *gamebtn)
{
*opbtn = 0;
*gamebtn = 0;
if (GetAsyncKeyState(cfg.vk_test) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(cfg.vk_service) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_SERVICE;
}
if (GetAsyncKeyState(cfg.vk_coin) & 0x8000) {
*opbtn |= HKB_IO_OPBTN_COIN;
}
if (GetAsyncKeyState(cfg.vk_right) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(cfg.vk_left) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(cfg.vk_up) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(cfg.vk_down) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(cfg.vk_enter) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ENTER;
}
if (GetAsyncKeyState(cfg.vk_cancel) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_CANCEL;
}
if (GetAsyncKeyState(cfg.vk_arr_right) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ARR_RIGHT;
}
if (GetAsyncKeyState(cfg.vk_arr_left) & 0x8000) {
*gamebtn |= HKB_IO_GAMEBTN_ARR_LEFT;
}
return S_OK;
}

47
hkbio/hkbio.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
HKB_IO_OPBTN_TEST = 0x01,
HKB_IO_OPBTN_SERVICE = 0x02,
HKB_IO_OPBTN_COIN = 0x04,
};
enum {
HKB_IO_GAMEBTN_RIGHT = 0x01,
HKB_IO_GAMEBTN_LEFT = 0x02,
HKB_IO_GAMEBTN_UP = 0x04,
HKB_IO_GAMEBTN_DOWN = 0x08,
HKB_IO_GAMEBTN_ENTER = 0x10,
HKB_IO_GAMEBTN_CANCEL = 0x20,
HKB_IO_GAMEBTN_ARR_RIGHT = 0x40,
HKB_IO_GAMEBTN_ARR_LEFT = 0x80,
};
/* Get the version of the Ongeki IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is
the major version and the low byte is the minor version (as defined by the
Semantic Versioning standard).
The latest API version as of this writing is 0x0100. */
uint16_t hkb_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after hkb_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT hkb_io_init(void);
/* Send any queued outputs (of which there are currently none, though this may
change in subsequent API versions) and retrieve any new inputs.
Minimum API version: 0x0100 */
HRESULT hkb_io_poll(uint8_t *opbtn, uint8_t *gamebtn);

13
hkbio/meson.build Normal file
View File

@ -0,0 +1,13 @@
hkbio_lib = static_library(
'hkbio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'hkbio.c',
'hkbio.h',
'config.c',
'config.h',
],
)

View File

@ -62,6 +62,7 @@ subdir('mu3io')
subdir('mercuryio') subdir('mercuryio')
subdir('cxbio') subdir('cxbio')
subdir('mai2io') subdir('mai2io')
subdir('hkbio')
subdir('chunihook') subdir('chunihook')
subdir('divahook') subdir('divahook')
@ -72,3 +73,4 @@ subdir('mu3hook')
subdir('mercuryhook') subdir('mercuryhook')
subdir('cxbhook') subdir('cxbhook')
subdir('mai2hook') subdir('mai2hook')
subdir('hkbhook')

View File

@ -36,12 +36,20 @@ static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static wchar_t* my_System_getAppRootPath(); static wchar_t* my_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)(); static wchar_t* (*next_System_getAppRootPath)();
static wchar_t* my_AppImage_getOptionMountRootPath();
static wchar_t* (*next_AppImage_getOptionMountRootPath)();
static const struct hook_symbol amdaemon_syms[] = { static const struct hook_symbol amdaemon_syms[] = {
{ {
.name = "System_getAppRootPath", .name = "System_getAppRootPath",
.patch = my_System_getAppRootPath, .patch = my_System_getAppRootPath,
.link = (void **) &next_System_getAppRootPath, .link = (void **) &next_System_getAppRootPath,
}, },
{
.name = "AppImage_getOptionMountRootPath",
.patch = my_AppImage_getOptionMountRootPath,
.link = (void **) &next_AppImage_getOptionMountRootPath,
},
}; };
static wchar_t game[5] = {0}; static wchar_t game[5] = {0};
@ -505,3 +513,11 @@ static wchar_t* my_System_getAppRootPath()
wcscat_s(path, MAX_PATH, L"\\"); wcscat_s(path, MAX_PATH, L"\\");
return path; return path;
} }
static wchar_t* my_AppImage_getOptionMountRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.option);
wcscat_s(path, MAX_PATH, L"\\");
return path;
}