add ll3 hook

This commit is contained in:
Hay1tsme 2024-02-17 16:46:23 -05:00
parent 05d337c499
commit 068e3f3963
18 changed files with 546 additions and 27 deletions

View File

@ -19,6 +19,21 @@ $(BUILD_DIR_ZIP)/siva.zip:
$(BUILD_DIR_ZIP)/siva/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/siva/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/siva ; zip -r ../siva.zip *
$(BUILD_DIR_ZIP)/ll3.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/ll3
$(V)mkdir -p $(BUILD_DIR_ZIP)/ll3/DEVICE
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_64)/ll3hook/ll3hook.dll \
$(DIST_DIR)/ll3/taitools.ini \
$(DIST_DIR)/ll3/start.bat \
$(BUILD_DIR_ZIP)/ll3
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/ll3/DEVICE
$(V)strip $(BUILD_DIR_ZIP)/ll3/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/ll3 ; zip -r ../ll3.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
@ -28,6 +43,7 @@ $(BUILD_DIR_ZIP)/doc.zip: \
$(BUILD_DIR_ZIP)/taitools.zip: \
$(BUILD_DIR_ZIP)/siva.zip \
$(BUILD_DIR_ZIP)/ll3.zip \
CHANGELOG.md \
README.md \

13
dist/ll3/start.bat vendored Normal file
View File

@ -0,0 +1,13 @@
@echo off
pushd %~dp0
REM chcp 932
start inject.exe -d -k ll3hook.dll d_drive\Service\NesysService.exe -app
inject.exe -d -k ll3hook.dll ll3.exe -singlemonitor -windowmode -highquality
REM Comment above and uncomment below for terminal mode
REM inject.exe -d -k ll3hook.dll ll3.exe -singlemonitor -windowmode -highquality -centralmode
echo.
echo The game process has terminated
pause

5
dist/ll3/taitools.ini vendored Normal file
View File

@ -0,0 +1,5 @@
[vfs]
d_drive=d_drive
[netenv]
enable=1

View File

@ -520,8 +520,9 @@ static WINHTTPAPI BOOL hook_WinHttpCrackUrl(
} else {
wcscat_s(bfr, _countof(bfr), pos->to);
wcscat_s(bfr, _countof(bfr), path);
//dprintf("DNS: Replaced cracked url %S (%d) -> %S\n", lpUrlComponents->lpszHostName, (int)lpUrlComponents->dwHostNameLength, bfr);
lpUrlComponents->lpszHostName = bfr;
lpUrlComponents->dwHostNameLength = wcslen(bfr);
lpUrlComponents->dwHostNameLength = wcslen(pos->to);
}
break;
}

View File

@ -37,6 +37,11 @@ static LSTATUS reg_hook_open_locked(
const wchar_t *name,
HKEY *out);
static LSTATUS reg_hook_open_locked_a(
HKEY parent,
const char *name,
HKEY *out);
static LSTATUS reg_hook_query_val_locked(
struct reg_hook_key *key,
const wchar_t *name,
@ -53,6 +58,13 @@ static LSTATUS WINAPI hook_RegOpenKeyExW(
uint32_t access,
HKEY *out);
static LSTATUS WINAPI hook_RegOpenKeyExA(
HKEY parent,
const char *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,
@ -132,6 +144,13 @@ static LSTATUS (WINAPI *next_RegOpenKeyExW)(
uint32_t access,
HKEY *out);
static LSTATUS (WINAPI *next_RegOpenKeyExA)(
HKEY parent,
const char *name,
uint32_t flags,
uint32_t access,
HKEY *out);
static LSTATUS (WINAPI *next_RegCreateKeyExW)(
HKEY parent,
const wchar_t *name,
@ -208,6 +227,10 @@ static const struct hook_symbol reg_hook_syms[] = {
.name = "RegOpenKeyExW",
.patch = hook_RegOpenKeyExW,
.link = (void **) &next_RegOpenKeyExW,
}, {
.name = "RegOpenKeyExA",
.patch = hook_RegOpenKeyExA,
.link = (void **) &next_RegOpenKeyExA,
}, {
.name = "RegCreateKeyExW",
.patch = hook_RegCreateKeyExW,
@ -403,6 +426,7 @@ static LSTATUS reg_hook_open_locked(
//dprintf("Reg: %ls vs %ls\n", name, key->name);
if (key->root == parent && wstr_ieq(key->name, name)) {
dprintf("Reg: Replace key %S\n", name);
break;
}
}
@ -417,6 +441,7 @@ static LSTATUS reg_hook_open_locked(
/* Assume only one handle will be open at a time */
if (key->handle != NULL) {
dprintf("Reg: Fake handle already open: %p\n", key->handle);
return ERROR_SHARING_VIOLATION;
}
@ -431,6 +456,67 @@ static LSTATUS reg_hook_open_locked(
0,
KEY_READ,
out);
if (err == ERROR_SUCCESS) {
key->handle = *out;
}
return err;
}
static LSTATUS reg_hook_open_locked_a(
HKEY parent,
const char *name,
HKEY *out)
{
struct reg_hook_key *key;
LSTATUS err;
size_t i;
wchar_t wide_str[MAX_PATH];
int cvt_num = mbstowcs(wide_str, name, MAX_PATH);
*out = NULL;
for (i = 0 ; i < reg_hook_nkeys ; i++) {
/* Assume reg keys are referenced from a root key and not from some
intermediary key */
key = &reg_hook_keys[i];
//dprintf("Reg: %ls vs %ls\n", name, key->name);
if (key->root == parent && wstr_ieq(key->name, wide_str)) {
dprintf("Reg: Replace key %S\n", wide_str);
break;
}
}
/* (Bail out if we didn't find anything; this causes the open/create call
to be passed onward down the hook chain) */
if (i >= reg_hook_nkeys) {
return ERROR_SUCCESS;
}
/* Assume only one handle will be open at a time */
if (key->handle != NULL) {
dprintf("Reg: Fake handle already open: %p\n", key->handle);
return ERROR_SHARING_VIOLATION;
}
/* Open a unique HKEY handle that we can use to identify accesses to
this virtual registry key. We open a read-only handle to an arbitrary
registry key that we can reliably assume exists and isn't one of the
hardcoded root handles. HKLM\SOFTWARE will suffice for this purpose. */
err = next_RegOpenKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE",
0,
KEY_READ,
out);
if (err == ERROR_SUCCESS) {
key->handle = *out;
@ -467,6 +553,35 @@ static LSTATUS WINAPI hook_RegOpenKeyExW(
return err;
}
static LSTATUS WINAPI hook_RegOpenKeyExA(
HKEY parent,
const char *name,
uint32_t flags,
uint32_t access,
HKEY *out)
{
LSTATUS err;
if (out == NULL) {
return ERROR_INVALID_PARAMETER;
}
EnterCriticalSection(&reg_hook_lock);
err = reg_hook_open_locked_a(parent, name, out);
LeaveCriticalSection(&reg_hook_lock);
if (err == ERROR_SUCCESS) {
if (*out != NULL) {
//dprintf("Registry: Opened virtual key %s\n", name);
} else {
err = next_RegOpenKeyExA(parent, name, flags, access, out);
}
}
return err;
}
static LSTATUS WINAPI hook_RegCreateKeyExW(
HKEY parent,
const wchar_t *name,

34
ll3hook/config.c Normal file
View File

@ -0,0 +1,34 @@
#include <assert.h>
#include <stddef.h>
#include "ll3hook/config.h"
#include "platform/config.h"
void ll3_dll_config_load(
struct ll3_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"ll3io",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void ll3_hook_config_load(
struct ll3_hook_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
ll3_dll_config_load(&cfg->dll, filename);
gfx_config_load(&cfg->gfx, filename);
}

19
ll3hook/config.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <stddef.h>
#include "ll3hook/ll3-dll.h"
#include "platform/config.h"
#include "gfxhook/config.h"
#include "board/config.h"
struct ll3_hook_config {
struct platform_config platform;
struct ll3_dll_config dll;
struct gfx_config gfx;
};
void ll3_hook_config_load(
struct ll3_hook_config *cfg,
const wchar_t *filename);

View File

@ -50,35 +50,12 @@ static DWORD CALLBACK ll3_pre_startup(void)
goto fail;
}
hr = sg_reader_hook_init(&ll3_hook_cfg.aime, 1, ll3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = ll3_dll_init(&ll3_hook_cfg.dll, ll3_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = ll3_io4_hook_init(&ll3_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
/* Start elisabeth Hooks for the LED and IO Board DLLs */
elisabeth_hook_init(&ll3_hook_cfg.elisabeth);
touch_hook_init(&ll3_hook_cfg.touch);
/* Initialize debug helpers */
spike_hook_init(L".\\taitools.ini");
@ -97,6 +74,10 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause == DLL_THREAD_ATTACH && sizeof(uint64_t) == 4) {
dprintf("Connected\n");
}
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}

100
ll3hook/ll3-dll.c Normal file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "ll3hook/ll3-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym ll3_dll_syms[] = {
{
.sym = "ll3_io_init",
.off = offsetof(struct ll3_dll, init),
}, {
.sym = "ll3_io_read_coin_counter",
.off = offsetof(struct ll3_dll, read_coin_counter),
}, {
.sym = "ll3_io_get_btns",
.off = offsetof(struct ll3_dll, get_btns),
},
};
struct ll3_dll ll3_dll;
HRESULT ll3_dll_init(const struct ll3_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("ll3 IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("ll3 IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "ll3_io_get_api_version");
if (get_api_version != NULL) {
ll3_dll.api_version = get_api_version();
} else {
ll3_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose ll3_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (ll3_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("ll3 IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
ll3_dll.api_version);
goto end;
}
sym = ll3_dll_syms;
hr = dll_bind(&ll3_dll, src, &sym, _countof(ll3_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("ll3 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;
}

20
ll3hook/ll3-dll.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include "ll3io/ll3io.h"
struct ll3_dll {
uint16_t api_version;
HRESULT (*init)(void);
void (*read_coin_counter)(uint16_t *coins, uint16_t *services);
void (*get_btns)(uint8_t *btn, uint8_t *stick);
};
struct ll3_dll_config {
wchar_t path[MAX_PATH];
};
extern struct ll3_dll ll3_dll;
HRESULT ll3_dll_init(const struct ll3_dll_config *cfg, HINSTANCE self);

7
ll3hook/ll3hook.def Normal file
View File

@ -0,0 +1,7 @@
LIBRARY ll3hook
EXPORTS
ll3_io_get_api_version
ll3_io_init
ll3_io_read_coin_counter
ll3_io_get_btns

30
ll3io/config.c Normal file
View File

@ -0,0 +1,30 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "ll3io/config.h"
void ll3_io_config_load(struct ll3_input_config *cfg, const wchar_t *filename)
{
cfg->test = GetPrivateProfileIntW(L"jvs", L"test", VK_HOME, filename);
cfg->service = GetPrivateProfileIntW(L"jvs", L"service", VK_DELETE, filename);
cfg->coin = GetPrivateProfileIntW(L"jvs", L"coin", VK_INSERT, filename);
cfg->is_xinput = GetPrivateProfileIntW(L"deck", L"deck", 0, filename);
cfg->xinput_player = GetPrivateProfileIntW(L"deck", L"controller_num", 0, filename);
cfg->btn_l = GetPrivateProfileIntW(L"deck", L"left_button", 'C', filename);
cfg->btn_r = GetPrivateProfileIntW(L"deck", L"right_button", 'N', filename);
cfg->stick_l_up = GetPrivateProfileIntW(L"deck", L"left_stick_up", 'W', filename);
cfg->stick_l_right = GetPrivateProfileIntW(L"deck", L"left_stick_right", 'D', filename);
cfg->stick_l_down = GetPrivateProfileIntW(L"deck", L"left_stick_down", 'S', filename);
cfg->stick_l_left = GetPrivateProfileIntW(L"deck", L"left_stick_left", 'A', filename);
cfg->stick_r_up = GetPrivateProfileIntW(L"deck", L"right_stick_up", 'I', filename);
cfg->stick_r_right = GetPrivateProfileIntW(L"deck", L"right_stick_right", 'L', filename);
cfg->stick_r_down = GetPrivateProfileIntW(L"deck", L"right_stick_down", 'K', filename);
cfg->stick_r_left = GetPrivateProfileIntW(L"deck", L"right_stick_left", 'J', filename);
}

28
ll3io/config.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#pragma pack(push, 1)
struct ll3_input_config {
uint8_t is_xinput;
uint8_t xinput_player;
uint8_t test;
uint8_t service;
uint8_t coin;
uint8_t btn_l;
uint8_t btn_r;
uint8_t stick_l_up;
uint8_t stick_l_right;
uint8_t stick_l_down;
uint8_t stick_l_left;
uint8_t stick_r_up;
uint8_t stick_r_right;
uint8_t stick_r_down;
uint8_t stick_r_left;
};
#pragma pack(pop)
void ll3_io_config_load(struct ll3_input_config *cfg, const wchar_t *filename);

64
ll3io/ll3io.c Normal file
View File

@ -0,0 +1,64 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include <stdbool.h>
#include "ll3io/ll3io.h"
#include "ll3io/config.h"
#include "util/dprintf.h"
static bool ll3_io_coin = false;
static bool ll3_io_service = false;
static uint16_t ll3_coin_ct = 0;
static uint16_t ll3_service_ct = 0;
static struct ll3_input_config cfg;
uint16_t ll3_io_get_api_version(void)
{
return 0x0100;
}
HRESULT ll3_io_init(void)
{
dprintf("ll3 IO: Init\n");
ll3_io_config_load(&cfg, L".\\bananatools.ini");
return S_OK;
}
void ll3_io_get_btns(uint8_t *btn, uint8_t *stick)
{
if (GetAsyncKeyState(cfg.test) & 0x8000) {
*btn |= ll3_BTN_TEST;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
*btn |= ll3_BTN_SERVICE;
}
}
void ll3_io_read_coin_counter(uint16_t *coins, uint16_t *services)
{
if (GetAsyncKeyState(cfg.coin) & 0x8000) {
if (!ll3_io_coin) {
ll3_io_coin = true;
ll3_coin_ct++;
}
} else {
ll3_io_coin = false;
}
if (GetAsyncKeyState(cfg.service) & 0x8000) {
if (!ll3_io_service) {
ll3_io_service = true;
ll3_service_ct++;
}
} else {
ll3_io_service = false;
}
*coins = ll3_coin_ct;
*services = ll3_service_ct;
}

57
ll3io/ll3io.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
ll3_BTN_TEST = 0x01,
ll3_BTN_SERVICE = 0x02,
ll3_BTN_COIN = 0x03,
ll3_BTN_LEFT = 0x04,
ll3_BTN_RIGHT = 0x05,
};
enum {
ll3_STICK_L_UP = 0x01,
ll3_STICK_L_RIGHT = 0x02,
ll3_STICK_L_DOWN = 0x03,
ll3_STICK_L_LEFT = 0x04,
ll3_STICK_R_UP = 0x05,
ll3_STICK_R_RIGHT = 0x06,
ll3_STICK_R_DOWN = 0x07,
ll3_STICK_R_LEFT = 0x08,
};
/* Get the version of the Theatrhythm 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 ll3_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after ll3_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT ll3_io_init(void);
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
ll3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
a left hand side set of inputs and a right hand side set of inputs: the bit
mappings are the same in both cases.
All buttons are active-high, even though some buttons' electrical signals
on a real cabinet are active-low.
Minimum API version: 0x0100 */
void ll3_io_get_btns(uint8_t *btn, uint8_t *stick);
void ll3_io_read_coin_counter(uint16_t *coins, uint16_t *services);

16
ll3io/meson.build Normal file
View File

@ -0,0 +1,16 @@
ll3io_lib = static_library(
'll3io',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
xinput_lib,
],
sources : [
'll3io.c',
'll3io.h',
'config.c',
'config.h',
],
)

View File

@ -55,6 +55,8 @@ subdir('util')
subdir('gfxhook')
subdir('sivaio')
subdir('ll3io')
subdir('minihook')
subdir('sivahook')
subdir('ll3hook')

View File

@ -67,6 +67,8 @@ static uint32_t WINAPI hook_IcmpSendEcho2(
uint32_t ReplySize,
uint32_t Timeout);
static uint32_t WINAPI hook_FlushIpNetTable(DWORD dwIfIndex);
/* Link pointers */
static uint32_t (WINAPI *next_GetAdaptersAddresses)(
@ -124,6 +126,9 @@ static const struct hook_symbol netenv_hook_syms[] = {
.name = "IcmpSendEcho2",
.patch = hook_IcmpSendEcho2,
.link = (void **) &next_IcmpSendEcho2,
}, {
.name = "FlushIpNetTable",
.patch = hook_FlushIpNetTable,
}
};
@ -294,7 +299,8 @@ static uint32_t WINAPI hook_GetAdaptersInfo(
return ERROR_BUFFER_OVERFLOW;
}
dprintf("Netenv: GetAdaptersInfo: Virtualized LAN configuration:\n");
// This spams console on NesysService.exe, comment out for now
/*dprintf("Netenv: GetAdaptersInfo: Virtualized LAN configuration:\n");
dprintf("Netenv: Interface IP : %3i.%3i.%3i.%3i\n",
(uint8_t) (netenv_ip_iface >> 24),
(uint8_t) (netenv_ip_iface >> 16),
@ -311,7 +317,7 @@ static uint32_t WINAPI hook_GetAdaptersInfo(
netenv_mac_addr[2],
netenv_mac_addr[3],
netenv_mac_addr[4],
netenv_mac_addr[5]);
netenv_mac_addr[5]);*/
memset(&iface, 0, sizeof(iface));
memset(&router, 0, sizeof(router));
@ -410,7 +416,7 @@ static uint32_t WINAPI hook_GetIfTable(
*pdwSize = sizeof(*row) + sizeof(DWORD);
if (pIfTable == NULL || nbytes < sizeof(*row) + sizeof(DWORD)) {
return ERROR_BUFFER_OVERFLOW;
return ERROR_INSUFFICIENT_BUFFER; // was ERROR_BUFFER_OVERFLOW
}
pIfTable->dwNumEntries = 1;
@ -499,3 +505,8 @@ static uint32_t WINAPI hook_IcmpSendEcho2(
return 1;
}
static uint32_t WINAPI hook_FlushIpNetTable(DWORD dwIfIndex)
{
return 0;
}