Compare commits

...

20 Commits

Author SHA1 Message Date
Hay1tsme 94f9357382 rehook ShowCursor 2024-04-07 17:55:55 -04:00
Hay1tsme 434382ce4b vfs: hook System_getAppRootPath for unity games 2024-04-07 17:55:38 -04:00
Hay1tsme 16f7272751 epay: fix print statement to work with other compilers, fixes #4 2024-03-14 11:12:48 -04:00
Hay1tsme 67ffde7289 nusec: add NUSEC_IOCTL_TD_ERASE_USED 2024-03-12 13:55:57 -04:00
Hay1tsme eb93c6cf75 add vsprintf hook 2024-02-08 01:05:13 -05:00
Hay1tsme ba9fb5d0b9 catch epay URLs 2023-12-14 10:36:52 -05:00
Hay1tsme 2ec0ee4794 update procaddr hook 2023-12-10 20:47:43 -05:00
Hay1tsme ccdd07e262 mai2: move serial hook inserters 2023-12-04 03:28:38 -05:00
Hay1tsme 9d8a38bbf7 mai2: wip hook 2023-11-30 02:29:27 -05:00
Hay1tsme 4dcf01f643 carol: somewhat-working touch board?? 2023-11-27 23:23:00 -05:00
Hay1tsme 962e14dc9b ongeki: fix start.bat and segatools.ini 2023-11-05 21:51:36 -05:00
Hay1tsme 5d04685c73 update gitignore 2023-09-19 10:33:30 -04:00
Hay1tsme 528ec4379c createprocess: add replace_all flag 2023-09-15 19:57:11 -04:00
Hay1tsme 5a4e947354 carol: use createprocess hook 2023-09-15 19:52:26 -04:00
Hay1tsme 157f52da4c platform: add epay hook 2023-09-15 01:35:33 -04:00
Hay1tsme 0d83977073 hooklib: fill out my_CreateProcessA 2023-09-13 20:23:40 -04:00
Hay1tsme dca84e08d0 hooklib: fix createprocess imports 2023-09-13 19:57:10 -04:00
Hay1tsme 2dbb4aec8c hooklib: add createprocess to meson 2023-09-13 19:54:22 -04:00
Hay1tsme 3d7d9fcaa5 hooklib: add createprocess hook skeleton 2023-09-13 17:54:40 -04:00
Hay1tsme 98d2ea1390 vfs: add hook for C:\Users\AppUser 2023-09-13 11:25:29 -04:00
58 changed files with 2702 additions and 307 deletions

13
.gitignore vendored
View File

@ -1,6 +1,17 @@
.*.swp
.vscode/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Suggested names for build dirs
build/

16
.vscode/settings.json vendored
View File

@ -1,3 +1,19 @@
{
"editor.formatOnSave": false,
"mesonbuild.configureOnOpen": false,
"files.associations": {
"string.h": "c",
"stdbool.h": "c",
"windows.h": "c",
"dprintf.h": "c",
"touch.h": "c",
"mai2-dll.h": "c",
"led.h": "c",
"path.h": "c",
"reg.h": "c",
"platform.h": "c",
"procaddr.h": "c",
"table.h": "c",
"serial.h": "c"
},
}

View File

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

View File

@ -8,8 +8,6 @@
#include "hook/iobuf.h"
#include "hook/iohook.h"
#include "hook/table.h"
#include "carolhook/carol-dll.h"
#include "carolhook/controlbd.h"
@ -38,39 +36,6 @@ static struct uart controlbd_uart;
static uint8_t controlbd_written_bytes[520];
static uint8_t controlbd_readable_bytes[520];
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static BOOL (WINAPI *next_CreateProcessA)(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static const struct hook_symbol win32_hooks[] = {
{
.name = "CreateProcessA",
.patch = my_CreateProcessA,
.link = (void **) &next_CreateProcessA
}
};
HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
{
if (!cfg->enable) {
@ -85,12 +50,6 @@ HRESULT controlbd_hook_init(const struct controlbd_config *cfg)
controlbd_uart.readable.bytes = controlbd_readable_bytes;
controlbd_uart.readable.nbytes = sizeof(controlbd_readable_bytes);
hook_table_apply(
NULL,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
dprintf("Control Board: Init\n");
return iohook_push_handler(controlbd_handle_irp);
@ -378,50 +337,3 @@ static HRESULT controlbd_req_ack_any(uint8_t cmd)
return iobuf_write(&controlbd_uart.readable, &resp, sizeof(resp));
}
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
dprintf("Control Board: my_CreateProcessA Hit! %s\n", lpCommandLine);
if (strncmp(".\\15312firm\\firmupdate_1113.exe", lpCommandLine, 31)) {
return next_CreateProcessA(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}
dprintf("Control Board: Hooking child process\n");
char new_cmd[MAX_PATH] = "inject -d -k carolhook.dll ";
strcat_s(new_cmd, MAX_PATH, lpCommandLine);
return next_CreateProcessA(
lpApplicationName,
new_cmd,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}

View File

@ -19,6 +19,8 @@
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "hooklib/createprocess.h"
#include "hooklib/cursor.h"
#include "platform/platform.h"
@ -43,6 +45,8 @@ static DWORD CALLBACK carol_pre_startup(void)
HMODULE dbghelp;
dprintf("--- Begin carol_pre_startup ---\n");
if ( !SetProcessDPIAware() )
dprintf("Failed to set process DPI awareness level!\n");
/* Pin the D3D shader compiler. This makes startup much faster. */
@ -64,6 +68,8 @@ static DWORD CALLBACK carol_pre_startup(void)
dprintf("Failed to load debug helper library!\n");
}
cursor_hook_init();
/* Config load */
carol_hook_config_load(&carol_hook_cfg, L".\\segatools.ini");
@ -122,7 +128,12 @@ static DWORD CALLBACK carol_pre_startup(void)
if (FAILED(hr)) {
goto fail;
}
hr = createprocess_push_hook_a(".\\15312firm\\firmupdate_1113.exe", "inject -d -k carolhook.dll ", NULL, false);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");

View File

@ -8,13 +8,15 @@
#include "carolhook/carol-dll.h"
#include "carolhook/touch.h"
#include "hook/table.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
/**
* CMDS for M3 EX series
* CMDS for touch thing
* CX -> Calibrate Extend, preform callibration
* MS -> Mode Stream, enters stream mode
* R -> Reset, resets the device
@ -32,7 +34,8 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf);
static HRESULT handle_touch_ack_cmd(const struct touch_req *req);
static HRESULT handle_touch_name_cmd(const struct touch_req *req);
static HRESULT handle_touch_id_cmd(const struct touch_req *req);
static void touch_scan_auto(const bool is_pressed, const uint32_t mouse_x, const uint32_t mouse_y);
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req);
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
static CRITICAL_SECTION touch_lock;
static struct uart touch_uart;
@ -40,8 +43,10 @@ static uint8_t touch_written_bytes[528];
static uint8_t touch_readable_bytes[528];
static bool should_stream = false;
static bool last_pressed;
static uint16_t last_x;
static uint16_t last_y;
static uint8_t last_x1;
static uint8_t last_x2;
static uint8_t last_y1;
static uint8_t last_y2;
HRESULT touch_hook_init(const struct touch_config *cfg)
@ -91,6 +96,7 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
if (irp->op == IRP_OP_OPEN) {
dprintf("Touchscreen: Starting backend DLL\n");
hr = carol_dll.touch_init();
carol_dll.touch_start(touch_scan_auto);
if (FAILED(hr)) {
dprintf("Touchscreen: Backend DLL error: %X\n", (int) hr);
@ -123,7 +129,9 @@ static HRESULT touch_handle_irp_locked(struct irp *irp)
}
else if (!strcmp("OI", (char *)req.cmd)) {
hr = handle_touch_id_cmd(&req);
//carol_dll.touch_start(touch_scan_auto);
}
else if (!strcmp("UT", (char *)req.cmd)) {
hr = handle_touch_unit_type_cmd(&req);
}
else if (!strcmp("NM", (char *)req.cmd)) {
hr = handle_touch_name_cmd(&req);
@ -150,16 +158,22 @@ static HRESULT handle_touch_ack_cmd(const struct touch_req *req)
static HRESULT handle_touch_name_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Name\n");
return iobuf_write(&touch_uart.readable, "\001EX1234 EX1234\015", 15);
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 15);
}
static HRESULT handle_touch_id_cmd(const struct touch_req *req)
{
dprintf("Touch: Get ID\n");
return iobuf_write(&touch_uart.readable, "\001EX1234\015", 8);
return iobuf_write(&touch_uart.readable, "\001AD1000\015", 8);
}
static void touch_scan_auto(const bool is_pressed, const uint32_t mouse_x, const uint32_t mouse_y)
static HRESULT handle_touch_unit_type_cmd(const struct touch_req *req)
{
dprintf("Touch: Get Unit Type\n");
return iobuf_write(&touch_uart.readable, "\001AD****00\015", 8);
}
static void touch_scan_auto(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y)
{
struct touch_auto_resp resp;
uint16_t tmp_x;
@ -167,34 +181,37 @@ static void touch_scan_auto(const bool is_pressed, const uint32_t mouse_x, const
bool flg = false;
memset(&resp, 0, sizeof(resp));
resp.rep_id = 0x17;
resp.touches[0].status = 0x04;
resp.count = 1;
resp.touches[0].status |= 1 << 7;
if (is_pressed) {
resp.touches[0].status = 0x07;
resp.touches[0].status |= (1 << 7) | (1 << 6);
resp.touches[0].touch_id = 1;
tmp_x = mouse_x & 0x7FFF;
tmp_y = mouse_y & 0x7FFF;
// flip
resp.touches[0].x = (tmp_x << 8) | (tmp_x >> 8);
resp.touches[0].y = (tmp_y << 8) | (tmp_y >> 8);
resp.touches[0].x1 = tmp_x & 0x7F;
resp.touches[0].x2 = (tmp_x >> 7) & 0x7F;
resp.touches[0].y1 = tmp_y & 0x7F;
resp.touches[0].y2 = (tmp_y >> 7) & 0x7F;
flg = resp.touches[0].x != last_x || resp.touches[0].y != last_y;
flg = resp.touches[0].x1 != last_x1 || resp.touches[0].x2 != last_x2 || resp.touches[0].y1 != last_y1 || resp.touches[0].y2 != last_y2;
#if 1
if (flg)
dprintf("Touch: Mouse down! x %04X y: %04X\n", resp.touches[0].x, resp.touches[0].y);
dprintf("Touch: Mouse down! x %02X %02X y: %02X %02X\n", resp.touches[0].x1, resp.touches[0].x2, resp.touches[0].y1, resp.touches[0].y2);
#endif
last_x = resp.touches[0].x;
last_y = resp.touches[0].y;
last_x1 = resp.touches[0].x1;
last_x2 = resp.touches[0].x2;
last_y1 = resp.touches[0].y1;
last_y2 = resp.touches[0].y2;
} else if (last_pressed) {
resp.touches[0].x = last_x;
resp.touches[0].y = last_y;
resp.touches[0].x1 = last_x1;
resp.touches[0].x2 = last_x2;
resp.touches[0].y1 = last_y1;
resp.touches[0].y2 = last_y2;
}
last_pressed = is_pressed;
@ -203,11 +220,9 @@ static void touch_scan_auto(const bool is_pressed, const uint32_t mouse_x, const
iobuf_write(&touch_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&touch_lock);
#if 1
//if (flg) {
dprintf("Touch: RX Buffer: (pos %08x)\n", (uint32_t)touch_uart.readable.pos);
dump_iobuf(&touch_uart.readable);
//}
#if 0
dprintf("Touch: RX Buffer: (pos %08x)\n", (uint32_t)touch_uart.readable.pos);
dump_iobuf(&touch_uart.readable);
#endif
}
@ -235,4 +250,4 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf)
}
return S_OK;
}
}

View File

@ -8,6 +8,9 @@
struct touch_config {
bool enable;
unsigned int port_no;
char board_id[7];
char unit_type[9];
};
// Always starts with 0x01, always ends with 0x0D
@ -20,17 +23,21 @@ struct touch_req {
struct touch_report {
uint8_t status;
uint8_t x1;
uint8_t x2;
uint8_t y1;
uint8_t y2;
uint8_t touch_id;
uint16_t x;
uint16_t y;
};
struct touch_auto_resp {
uint8_t rep_id;
struct touch_report touches[10];
uint8_t count;
uint16_t scan_time;
//uint8_t padding[456];
};
enum {
TOUCH_MODE_STREAM = 0x01,
TOUCH_MODE_DOWN_UP = 0x02,
TOUCH_MODE_INACTIVE = 0x03,
};
#pragma pack(pop)

View File

@ -7,6 +7,7 @@
#include "carolio/carolio.h"
#include "carolio/config.h"
#include "util/dprintf.h"
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx);
@ -15,6 +16,7 @@ static uint16_t carol_io_coins;
static struct carol_io_config carol_io_cfg;
static bool carol_io_touch_stop_flag;
static HANDLE carol_io_touch_thread;
static bool carol_io_window_focus = false;
uint16_t carol_io_get_api_version(void)
{
@ -110,22 +112,62 @@ void carol_io_touch_stop()
carol_io_touch_stop_flag = true;
}
void check_fg_wind(void)
{
HWND hwnd = GetForegroundWindow();
wchar_t window_class[MAX_PATH];
/* Unlike every other game, we can't use GetWindowText here. Why?
From MSDN:
"If the window does not have a caption, the return value is a null
string. This behavior is by design. It allows applications to call
GetWindowText without becoming unresponsive if the process that owns
the target window is not responding. However, if the target window
is not responding and it belongs to the calling application,
GetWindowText will cause the calling application to become
unresponsive."
Great, thanks Microsoft, very cool. Luckily Carol sets its class
name to the window title too so we can use that. */
GetClassNameW(hwnd, window_class, MAX_PATH);
if (wcscmp(window_class, L"WONDER Master")) {
if (carol_io_window_focus) {
dprintf("Carol IO: Window focus lost\n");
carol_io_window_focus = false;
}
} else if (!carol_io_window_focus) {
dprintf("Carol IO: Window focus regained\n");
carol_io_window_focus = true;
}
}
static unsigned int __stdcall carol_io_touch_thread_proc(void *ctx)
{
carol_io_touch_callback_t callback;
bool mouse_is_down = false;
uint32_t mX = 0;
uint32_t mY = 0;
uint16_t mX = 0;
uint16_t mY = 0;
POINT lpPoint;
HWND hwnd;
callback = ctx;
while (!carol_io_touch_stop_flag) {
check_fg_wind();
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
mouse_is_down = true;
if (GetCursorPos(&lpPoint)) {
mX = lpPoint.x;
mY = lpPoint.y;
if (GetCursorPos(&lpPoint)) {
hwnd = GetForegroundWindow();
if (ScreenToClient(hwnd, &lpPoint)) {
if (lpPoint.x < 0) lpPoint.x = 0;
if (lpPoint.y < 0) lpPoint.y = 0;
mX = (uint16_t)lpPoint.x;
mY = (uint16_t)lpPoint.y;
}
}
} else {
mouse_is_down = false;

View File

@ -5,7 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
typedef void (*carol_io_touch_callback_t)(const bool is_pressed, const uint32_t mouse_x, const uint32_t mouse_y);
typedef void (*carol_io_touch_callback_t)(const bool is_pressed, const uint16_t mouse_x, const uint16_t mouse_y);
/* Get the version of the Project carol IO API that this DLL supports. This
function should return a positive 16-bit integer, where the high byte is

View File

@ -5,7 +5,7 @@
#include "cxbhook/led.h"
#include "cxbhook/cxb-dll.h"
#include "hooklib/procaddr.h"
#include "hook/procaddr.h"
#include "hook/table.h"
@ -50,7 +50,7 @@ static struct hook_symbol lamp_syms[] = {
HRESULT led_hook_init(struct led_config *cfg)
{
dprintf("LED: Init\n");
return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms));
return proc_addr_table_push(NULL, "CommLamp.dll", lamp_syms, _countof(lamp_syms));
}
static int my_cCommLamp_Open(char *port)

View File

@ -6,7 +6,7 @@
#include "cxbhook/revio.h"
#include "cxbhook/cxb-dll.h"
#include "hooklib/procaddr.h"
#include "hook/procaddr.h"
#include "hook/table.h"
@ -83,7 +83,7 @@ static struct hook_symbol revio_syms[] = {
HRESULT revio_hook_init(struct revio_config *cfg)
{
dprintf("Revio: Init\n");
return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms));
return proc_addr_table_push(NULL, "CommIo.dll", revio_syms, _countof(revio_syms));
}
static int my_cCommIo_Open(char *port)

44
dist/mai2/segatools.ini vendored Normal file
View File

@ -0,0 +1,44 @@
[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.172.0
[gfx]
enable=1
[io4]
; Delete
test=0x2E
; End
service=0x23
; Insert
coin=0x2D

11
dist/mai2/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 -d -k mai2hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject.exe -d -k mai2hook.dll Sinmai.exe -screen-fullscreen 0 -screen-width 2160 -screen-height 1920
taskkill /f /im amdaemon.exe > nul 2>&1
echo Game processes have terminated

View File

@ -30,7 +30,7 @@ enable=1
; 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.250.0
subnet=192.168.162.0
[gfx]
enable=1
@ -57,4 +57,4 @@ RIGHT_SIDE=0x55
SLIDER_LEFT=0x54
SLIDER_RIGHT=0x59
;Change move speed of slider when use dinput
SLIDER_SPEED=1000
SLIDER_SPEED=1000

8
dist/mu3/start.bat vendored
View File

@ -3,12 +3,8 @@ pushd %~dp0
taskkill /f /im amdaemon.exe > nul 2>&1
REM USA
REM start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json
REM JP
start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json
inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe
start inject -d -k mu3hook.dll amdaemon.exe -f -c config_client.json config_common.json config_server.json
inject -d -k mu3hook.dll mu3.exe
taskkill /f /im amdaemon.exe > nul 2>&1

257
hooklib/createprocess.c Normal file
View File

@ -0,0 +1,257 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/createprocess.h"
#include "util/dprintf.h"
void createprocess_hook_init();
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
BOOL my_CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static BOOL (WINAPI *next_CreateProcessA)(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static BOOL (WINAPI *next_CreateProcessW)(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
static const struct hook_symbol win32_hooks[] = {
{
.name = "CreateProcessA",
.patch = my_CreateProcessA,
.link = (void **) &next_CreateProcessA
},
{
.name = "CreateProcessW",
.patch = my_CreateProcessW,
.link = (void **) &next_CreateProcessW
},
};
static bool did_init = false;
static struct process_hook_sym_w *process_syms_w;
static struct process_hook_sym_a *process_syms_a;
static size_t process_nsyms_a = 0;
static size_t process_nsyms_w = 0;
static CRITICAL_SECTION createproc_lock;
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all) {
struct process_hook_sym_w *new_mem;
struct process_hook_sym_w *new_proc;
HRESULT hr;
assert(name != NULL);
assert(head != NULL);
createprocess_hook_init();
EnterCriticalSection(&createproc_lock);
new_mem = realloc(
process_syms_w,
(process_nsyms_w + 1) * sizeof(struct process_hook_sym_w));
if (new_mem == NULL) {
LeaveCriticalSection(&createproc_lock);
return E_OUTOFMEMORY;
}
new_proc = &new_mem[process_nsyms_w];
memset(new_proc, 0, sizeof(*new_proc));
new_proc->name = name;
new_proc->head = head;
new_proc->tail = tail;
new_proc->replace_all = replace_all;
process_syms_w = new_mem;
process_nsyms_w++;
LeaveCriticalSection(&createproc_lock);
return S_OK;
}
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all) {
struct process_hook_sym_a *new_mem;
struct process_hook_sym_a *new_proc;
assert(name != NULL);
assert(head != NULL);
createprocess_hook_init();
EnterCriticalSection(&createproc_lock);
new_mem = realloc(
process_syms_a,
(process_nsyms_a + 1) * sizeof(struct process_hook_sym_a));
if (new_mem == NULL) {
LeaveCriticalSection(&createproc_lock);
return E_OUTOFMEMORY;
}
new_proc = &new_mem[process_nsyms_a];
memset(new_proc, 0, sizeof(*new_proc));
new_proc->name = name;
new_proc->head = head;
new_proc->tail = tail;
new_proc->replace_all = replace_all;
process_syms_a = new_mem;
process_nsyms_a++;
LeaveCriticalSection(&createproc_lock);
return S_OK;
}
void createprocess_hook_init() {
if (did_init) {
return;
}
did_init = true;
hook_table_apply(
NULL,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
InitializeCriticalSection(&createproc_lock);
dprintf("CreateProcess: Init\n");
}
static BOOL WINAPI my_CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
for (int i = 0; i < process_nsyms_a; i++) {
if (strncmp(process_syms_a[i].name, lpCommandLine, strlen(process_syms_a[i].name))) {
continue;
}
dprintf("CreateProcess: Hooking child process %s %s\n", lpApplicationName, lpCommandLine);
char new_cmd[MAX_PATH] = {0};
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].head);
if (!process_syms_a[i].replace_all) {
strcat_s(new_cmd, MAX_PATH, lpCommandLine);
}
if (process_syms_a[i].tail != NULL) {
strcat_s(new_cmd, MAX_PATH, process_syms_a[i].tail);
}
dprintf("CreateProcess: Replaced CreateProcessA %s\n", new_cmd);
return next_CreateProcessA(
lpApplicationName,
new_cmd,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}
return next_CreateProcessA(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}
BOOL my_CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
return next_CreateProcessW(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
}

21
hooklib/createprocess.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
HRESULT createprocess_push_hook_w(const wchar_t *name, const wchar_t *head, const wchar_t *tail, bool replace_all);
HRESULT createprocess_push_hook_a(const char *name, const char *head, const char *tail, bool replace_all);
struct process_hook_sym_w {
const wchar_t *name;
const wchar_t *head;
const wchar_t *tail;
bool replace_all;
};
struct process_hook_sym_a {
const char *name;
const char *head;
const char *tail;
bool replace_all;
};

71
hooklib/cursor.c Normal file
View File

@ -0,0 +1,71 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "hook/table.h"
#include "util/dprintf.h"
static HCURSOR my_SetCursor(HCURSOR hCursor);
static HCURSOR (*next_SetCursor)(HCURSOR hCursor);
static BOOL my_SetCursorPos(int x, int y);
static BOOL my_SetPhysicalCursorPos(int x, int y);
static int my_ShowCursor(BOOL bShow);
static const struct hook_symbol cursor_syms[] = {
{
.name = "SetCursor",
.patch = my_SetCursor,
.link = (void **) &next_SetCursor
},/*{
.name = "SetCursorPos",
.patch = my_SetCursorPos,
},*/ {
.name = "SetPhysicalCursorPos",
.patch = my_SetPhysicalCursorPos
}, {
.name = "ShowCursor",
.patch = my_ShowCursor
}
};
void cursor_hook_init()
{
hook_table_apply(
NULL,
"user32.dll",
cursor_syms,
_countof(cursor_syms));
dprintf("Cursor: Init\n");
}
static BOOL my_SetCursorPos(int x, int y)
{
dprintf("my_SetCursorPos Hit! x %d y %d\n", x, y);
return true;
}
static BOOL my_SetPhysicalCursorPos(int x, int y)
{
dprintf("my_SetPhysicalCursorPos Hit! x %d y %d\n", x, y);
return true;
}
static int my_ShowCursor(BOOL bShow)
{
return 0;
}
static HCURSOR my_SetCursor(HCURSOR hCursor)
{
HCURSOR fake_cursor;
if ( hCursor )
return next_SetCursor(hCursor);
fake_cursor = LoadCursorA(0, (LPCSTR)0x7F00);
next_SetCursor(fake_cursor);
return 0;
}

3
hooklib/cursor.h Normal file
View File

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

View File

@ -7,8 +7,12 @@ hooklib_lib = static_library(
capnhook.get_variable('hook_dep'),
],
sources : [
'cursor.c',
'cursor.h',
'config.c',
'config.h',
'createprocess.c',
'createprocess.h',
'dll.c',
'dll.h',
'dns.c',
@ -19,8 +23,6 @@ hooklib_lib = static_library(
'fdshark.h',
'path.c',
'path.h',
'procaddr.c',
'procaddr.h',
'reg.c',
'reg.h',
'setupapi.c',

View File

@ -1,125 +0,0 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <libgen.h>
#include "hooklib/procaddr.h"
#include "hook/table.h"
#include "util/dprintf.h"
static struct proc_addr_table *proc_addr_hook_list;
static size_t proc_addr_hook_count;
static CRITICAL_SECTION proc_addr_hook_lock;
static bool proc_addr_hook_initted;
static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name);
static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name);
static void proc_addr_hook_init(void);
static const struct hook_symbol win32_hooks[] = {
{
.name = "GetProcAddress",
.patch = my_GetProcAddress,
.link = (void **) &next_GetProcAddress
}
};
HRESULT proc_addr_table_push(
const char *target,
struct hook_symbol *syms,
size_t nsyms
)
{
HRESULT hr;
struct proc_addr_table *new_item;
struct proc_addr_table *new_mem;
proc_addr_hook_init();
EnterCriticalSection(&proc_addr_hook_lock);
new_mem = realloc(
proc_addr_hook_list,
(proc_addr_hook_count + 1) * sizeof(struct proc_addr_table));
if (new_mem == NULL) {
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&proc_addr_hook_lock);
return hr;
}
new_item = &new_mem[proc_addr_hook_count];
new_item->name = target;
new_item->nsyms = nsyms;
new_item->syms = syms;
proc_addr_hook_list = new_mem;
proc_addr_hook_count++;
hr = S_OK;
LeaveCriticalSection(&proc_addr_hook_lock);
return hr;
}
static void proc_addr_hook_init(void)
{
if (proc_addr_hook_initted) {
return;
}
dprintf("ProcAddr: Hook init\n");
proc_addr_hook_initted = true;
InitializeCriticalSection(&proc_addr_hook_lock);
hook_table_apply(
NULL,
"kernel32.dll",
win32_hooks,
_countof(win32_hooks));
}
FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name)
{
uintptr_t ordinal = (uintptr_t) name;
char mod_path[PATH_MAX];
char *mod_name;
const struct hook_symbol *sym;
FARPROC result = next_GetProcAddress(hModule, name);
GetModuleFileNameA(hModule, mod_path, PATH_MAX);
mod_name = basename(mod_path);
for (int i = 0; i < proc_addr_hook_count; i++) {
if (strcmp(proc_addr_hook_list[i].name, mod_name) == 0) {
for (int j = 0; j < proc_addr_hook_list[i].nsyms; j++) {
sym = &proc_addr_hook_list[i].syms[j];
if (ordinal > 0xFFFF) {
if (strcmp(sym->name, name) == 0) {
dprintf("ProcAddr: Hooking %s from %s\n", name, mod_name);
result = (FARPROC) sym->patch;
}
}
else {
if (sym->ordinal == ordinal) {
dprintf("ProcAddr: Hooking Ord %p from %s\n", (void *)ordinal, mod_name);
result = (FARPROC) sym->patch;
}
}
}
}
}
return result;
}

View File

@ -1,18 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "hook/table.h"
struct proc_addr_table {
const char *name;
size_t nsyms;
struct hook_symbol *syms;
};
HRESULT proc_addr_table_push(
const char *target,
struct hook_symbol *syms,
size_t nsyms
);

View File

@ -7,6 +7,7 @@
#include "hook/table.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "util/dprintf.h"
#include "util/str.h"
@ -99,6 +100,29 @@ static LSTATUS WINAPI hook_RegGetValueW(
uint32_t *numData
);
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime);
static LSTATUS WINAPI hook_RegEnumValueW(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
/* Link pointers */
static LSTATUS (WINAPI *next_RegOpenKeyExW)(
@ -155,6 +179,30 @@ static LSTATUS (WINAPI *next_RegGetValueW)(
uint32_t *numData
);
static LSTATUS (WINAPI *next_RegQueryInfoKeyW)(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime);
static LSTATUS (WINAPI *next_RegEnumValueW)(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData);
static const struct hook_symbol reg_hook_syms[] = {
{
.name = "RegOpenKeyExW",
@ -184,6 +232,14 @@ static const struct hook_symbol reg_hook_syms[] = {
.name = "RegGetValueW",
.patch = hook_RegGetValueW,
.link = (void **) &next_RegGetValueW,
}, {
.name = "RegQueryInfoKeyW",
.patch = hook_RegQueryInfoKeyW,
.link = (void **) &next_RegQueryInfoKeyW,
}, {
.name = "RegEnumValueW",
.patch = hook_RegEnumValueW,
.link = (void **) &next_RegEnumValueW,
}
};
@ -254,11 +310,24 @@ static void reg_hook_init(void)
InitializeCriticalSection(&reg_hook_lock);
dprintf("Reg hook init\n");
reg_hook_insert_hooks(NULL);
proc_addr_table_push(
NULL,
"ADVAPI32.dll",
(struct hook_symbol *) reg_hook_syms,
_countof(reg_hook_syms));
}
void reg_hook_insert_hooks(HMODULE target)
{
hook_table_apply(
NULL,
target,
"advapi32.dll",
reg_hook_syms,
_countof(reg_hook_syms));
}
static LRESULT reg_hook_propagate_hr(HRESULT hr)
@ -331,6 +400,7 @@ static LSTATUS reg_hook_open_locked(
/* 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, name)) {
break;
@ -821,6 +891,99 @@ static LSTATUS WINAPI hook_RegGetValueW(
return err;
}
static LSTATUS WINAPI hook_RegQueryInfoKeyW(
HKEY hKey,
LPWSTR lpClass,
LPDWORD lpcchClass,
LPDWORD lpReserved,
LPDWORD lpcSubKeys,
LPDWORD lpcbMaxSubKeyLen,
LPDWORD lpcbMaxClassLen,
LPDWORD lpcValues,
LPDWORD lpcbMaxValueNameLen,
LPDWORD lpcbMaxValueLen,
LPDWORD lpcbSecurityDescriptor,
PFILETIME lpftLastWriteTime)
{
struct reg_hook_key *key;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(hKey);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegQueryInfoKeyW(
hKey,
lpClass,
lpcchClass,
lpReserved,
lpcSubKeys,
lpcbMaxSubKeyLen,
lpcbMaxClassLen,
lpcValues,
lpcbMaxValueNameLen,
lpcbMaxValueLen,
lpcbSecurityDescriptor,
lpftLastWriteTime);
}
// This is the only one I've seen even be changed, so it's all I'm doing
// until I see otherwise.
*lpcValues = key->nvals;
LeaveCriticalSection(&reg_hook_lock);
return ERROR_SUCCESS;
}
static LSTATUS WINAPI hook_RegEnumValueW(
HKEY hkey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcchValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData)
{
struct reg_hook_key *key;
HRESULT hr;
LSTATUS err;
EnterCriticalSection(&reg_hook_lock);
key = reg_hook_match_key_locked(hkey);
/* Check if this is a virtualized registry key */
if (key == NULL) {
LeaveCriticalSection(&reg_hook_lock);
return next_RegEnumValueW(
hkey,
dwIndex,
lpValueName,
lpcchValueName,
lpReserved,
lpType,
lpData,
lpcbData);
}
if (dwIndex >= key->nvals) {
LeaveCriticalSection(&reg_hook_lock);
return ERROR_NO_MORE_ITEMS; // Pretty sure this is what it actually returns here?
}
wcscpy_s(lpValueName, *lpcchValueName, key->vals[dwIndex].name);
*lpcchValueName = wcslen(key->vals[dwIndex].name);
LeaveCriticalSection(&reg_hook_lock);
return ERROR_SUCCESS;
}
HRESULT reg_hook_read_bin(
void *bytes,
uint32_t *nbytes,

View File

@ -12,6 +12,8 @@ struct reg_hook_val {
uint32_t type;
};
void reg_hook_insert_hooks(HMODULE target);
HRESULT reg_hook_push_key(
HKEY root,
const wchar_t *name,

66
mai2hook/config.c Normal file
View File

@ -0,0 +1,66 @@
#include <assert.h>
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "mai2hook/config.h"
#include "platform/config.h"
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"mai2io",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void touch_config_load(
struct touch_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename);
}
void led_config_load(
struct led_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"led", L"enable", 1, filename);
}
void mai2_hook_config_load(
struct mai2_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);
mai2_dll_config_load(&cfg->dll, filename);
touch_config_load(&cfg->touch, filename);
led_config_load(&cfg->led, filename);
}

34
mai2hook/config.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <stddef.h>
#include "board/config.h"
#include "gfxhook/gfx.h"
#include "hooklib/dvd.h"
#include "mai2hook/mai2-dll.h"
#include "mai2hook/touch.h"
#include "mai2hook/led.h"
#include "platform/config.h"
struct mai2_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct gfx_config gfx;
struct mai2_dll_config dll;
struct touch_config touch;
struct led_config led;
};
void mai2_dll_config_load(
struct mai2_dll_config *cfg,
const wchar_t *filename);
void mai2_hook_config_load(
struct mai2_hook_config *cfg,
const wchar_t *filename);

148
mai2hook/dllmain.c Normal file
View File

@ -0,0 +1,148 @@
#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 "hook/table.h"
#include "hooklib/dvd.h"
#include "hooklib/spike.h"
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hooklib/serial.h"
#include "mai2hook/config.h"
#include "mai2hook/io4.h"
#include "mai2hook/mai2-dll.h"
#include "mai2hook/unity.h"
#include "mai2hook/touch.h"
#include "mai2hook/led.h"
#include "platform/platform.h"
#include "util/dprintf.h"
static HMODULE mai2_hook_mod;
static process_entry_t mai2_startup;
static struct mai2_hook_config mai2_hook_cfg;
static DWORD CALLBACK mai2_pre_startup(void)
{
HRESULT hr;
dprintf("--- Begin mai2_pre_startup ---\n");
/* Load config */
mai2_hook_config_load(&mai2_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&mai2_hook_cfg.dvd, mai2_hook_mod);
gfx_hook_init(&mai2_hook_cfg.gfx);
gfx_d3d9_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
gfx_d3d11_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
gfx_dxgi_hook_init(&mai2_hook_cfg.gfx, mai2_hook_mod);
serial_hook_init();
/* Initialize emulation hooks */
hr = platform_hook_init(
&mai2_hook_cfg.platform,
"SDEZ",
"ACA1",
mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&mai2_hook_cfg.aime, 1, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = vfd_hook_init(2);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_dll_init(&mai2_hook_cfg.dll, mai2_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = mai2_io4_hook_init(&mai2_hook_cfg.io4);
if (FAILED(hr)) {
goto fail;
}
// TODO: The handling of the fake registry COM values is extraordinarly lazy
// and I need to make a better method for tacking fake values onto common keys
// that multiple modules may try to use.
hr = led_hook_init(&mai2_hook_cfg.led);
if (FAILED(hr)) {
goto fail;
}
hr = touch_hook_init(&mai2_hook_cfg.touch);
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 `mai2hook` initialization. */
unity_hook_init();
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End mai2_pre_startup ---\n");
/* Jump to EXE start address */
return mai2_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
{
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
mai2_hook_mod = mod;
hr = process_hijack_startup(mai2_pre_startup, &mai2_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

147
mai2hook/io4.c Normal file
View File

@ -0,0 +1,147 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "board/io4.h"
#include "mai2hook/mai2-dll.h"
#include "util/dprintf.h"
bool mai2_io_coin = false;
uint16_t mai2_io_coins = 0;
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state);
static const struct io4_ops mai2_io4_ops = {
.poll = mai2_io4_poll,
};
HRESULT mai2_io4_hook_init(const struct io4_config *cfg)
{
HRESULT hr;
assert(mai2_dll.init != NULL);
hr = io4_hook_init(cfg, &mai2_io4_ops, NULL);
if (FAILED(hr)) {
return hr;
}
return mai2_dll.init();
}
static HRESULT mai2_io4_poll(void *ctx, struct io4_state *state)
{
uint8_t opbtn = 0;
uint8_t player1 = 0;
uint8_t player2 = 0;
HRESULT hr;
assert(mai2_dll.poll != NULL);
hr = mai2_dll.poll(&opbtn, &player1, &player2);
if (FAILED(hr)) {
return hr;
}
if (opbtn & MAI2_IO_OPBTN_TEST) {
state->buttons[0] |= IO4_BUTTON_TEST;
}
if (opbtn & MAI2_IO_OPBTN_SERVICE) {
state->buttons[0] |= IO4_BUTTON_SERVICE;
}
if (opbtn & MAI2_IO_P1_START) {
state->buttons[0] |= 1 << 1;
}
if (opbtn & MAI2_IO_P2_START) {
state->buttons[1] |= 1 << 4;
}
if (!(player1 & MAI2_IO_GAMEBTN_1)) {
state->buttons[0] |= 1 << 2;
}
if (!(player1 & MAI2_IO_GAMEBTN_2)) {
state->buttons[0] |= 1 << 3;
}
if (!(player1 & MAI2_IO_GAMEBTN_3)) {
state->buttons[0] |= 1 << 0;
}
if (!(player1 & MAI2_IO_GAMEBTN_4)) {
state->buttons[0] |= 1 << 15;
}
if (!(player1 & MAI2_IO_GAMEBTN_5)) {
state->buttons[0] |= 1 << 14;
}
if (!(player1 & MAI2_IO_GAMEBTN_6)) {
state->buttons[0] |= 1 << 13;
}
if (!(player1 & MAI2_IO_GAMEBTN_7)) {
state->buttons[0] |= 1 << 12;
}
if (!(player1 & MAI2_IO_GAMEBTN_8)) {
state->buttons[0] |= 1 << 11;
}
if (!(player2 & MAI2_IO_GAMEBTN_1)) {
state->buttons[1] |= 1 << 2;
}
if (!(player2 & MAI2_IO_GAMEBTN_2)) {
state->buttons[1] |= 1 << 3;
}
if (!(player2 & MAI2_IO_GAMEBTN_3)) {
state->buttons[1] |= 1 << 0;
}
if (!(player2 & MAI2_IO_GAMEBTN_4)) {
state->buttons[1] |= 1 << 15;
}
if (!(player2 & MAI2_IO_GAMEBTN_5)) {
state->buttons[1] |= 1 << 14;
}
if (!(player2 & MAI2_IO_GAMEBTN_6)) {
state->buttons[1] |= 1 << 13;
}
if (!(player2 & MAI2_IO_GAMEBTN_7)) {
state->buttons[1] |= 1 << 12;
}
if (!(player2 & MAI2_IO_GAMEBTN_8)) {
state->buttons[1] |= 1 << 11;
}
if (opbtn & MAI2_IO_OPBTN_COIN) {
if (!mai2_io_coin) {
mai2_io_coin = true;
mai2_io_coins++;
}
}
else {
mai2_io_coin = false;
}
state->chutes[0] = 128 + 256 * mai2_io_coins;
return S_OK;
}

7
mai2hook/io4.h Normal file
View File

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

210
mai2hook/led.c Normal file
View File

@ -0,0 +1,210 @@
#include <windows.h>
#include <stdbool.h>
#include "hooklib/reg.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "mai2hook/led.h"
#include "util/dprintf.h"
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes);
static HRESULT led_handle_irp(struct irp *irp);
static HRESULT led0_handle_irp_locked(struct irp *irp);
static HRESULT led1_handle_irp_locked(struct irp *irp);
static CRITICAL_SECTION led0_lock;
static struct uart led0_uart;
static uint8_t led0_written_bytes[520];
static uint8_t led0_readable_bytes[520];
static CRITICAL_SECTION led1_lock;
static struct uart led1_uart;
static uint8_t led1_written_bytes[520];
static uint8_t led1_readable_bytes[520];
static const struct reg_hook_val fake_com_keys[] = {
{
.name = L"\\Device\\RealTouchBoard0",
.read = read_fake_com0,
.type = REG_SZ,
},{
.name = L"\\Device\\RealTouchBoard1",
.read = read_fake_com1,
.type = REG_SZ,
},{
.name = L"\\Device\\RealLedBoard0",
.read = read_fake_com2,
.type = REG_SZ,
},
};
HRESULT led_hook_init(const struct led_config *cfg)
{
HRESULT hr;
if (!cfg->enable) {
return S_FALSE;
}
dprintf("Mai2 LED: Init\n");
InitializeCriticalSection(&led0_lock);
InitializeCriticalSection(&led1_lock);
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"HARDWARE\\DEVICEMAP\\SERIALCOMM",
fake_com_keys,
_countof(fake_com_keys));
if (FAILED(hr)) {
return hr;
}
uart_init(&led0_uart, 21);
led0_uart.written.bytes = led0_written_bytes;
led0_uart.written.nbytes = sizeof(led0_written_bytes);
led0_uart.readable.bytes = led0_readable_bytes;
led0_uart.readable.nbytes = sizeof(led0_readable_bytes);
uart_init(&led1_uart, 23);
led1_uart.written.bytes = led1_written_bytes;
led1_uart.written.nbytes = sizeof(led1_written_bytes);
led1_uart.readable.bytes = led1_readable_bytes;
led1_uart.readable.nbytes = sizeof(led1_readable_bytes);
return iohook_push_handler(led_handle_irp);
}
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 Touch: Read COM3 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM3");
}
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 Touch: Read COM4 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM4");
}
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes)
{
//dprintf("Mai2 LED: Read COM20 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM20");
}
static HRESULT led_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (uart_match_irp(&led0_uart, irp)) {
EnterCriticalSection(&led0_lock);
hr = led0_handle_irp_locked(irp);
LeaveCriticalSection(&led0_lock);
}
else if (uart_match_irp(&led1_uart, irp)) {
EnterCriticalSection(&led1_lock);
hr = led1_handle_irp_locked(irp);
LeaveCriticalSection(&led1_lock);
}
else {
return iohook_invoke_next(irp);
}
return hr;
}
static HRESULT led0_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 led0: Starting backend\n");
//hr = mai2_dll.led_init();
if (FAILED(hr)) {
dprintf("Mai2 led: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&led0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX0 Buffer:\n");
dump_iobuf(&led0_uart.written);
#endif
//hr = led_frame_decode(&req, &led0_uart.written, 0);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Mai2 led: Deframe error: %x\n", (int) hr);
}
return hr;
}
//hr = led_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Mai2 led: Processing error: %x\n", (int) hr);
}
return hr;
}
}
static HRESULT led1_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 led1: Starting backend\n");
//hr = mai2_dll.led_init();
if (FAILED(hr)) {
dprintf("Mai2 led: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&led0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("TX0 Buffer:\n");
dump_iobuf(&led0_uart.written);
#endif
//hr = led_frame_decode(&req, &led0_uart.written, 0);
if (hr != S_OK) {
if (FAILED(hr)) {
dprintf("Mai2 led: Deframe error: %x\n", (int) hr);
}
return hr;
}
//hr = led_req_dispatch(&req);
if (FAILED(hr)) {
dprintf("Mai2 led: Processing error: %x\n", (int) hr);
}
return hr;
}
}

9
mai2hook/led.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct led_config {
bool enable;
};
HRESULT led_hook_init(const struct led_config *cfg);

103
mai2hook/mai2-dll.c Normal file
View File

@ -0,0 +1,103 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "mai2hook/mai2-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym mai2_dll_syms[] = {
{
.sym = "mai2_io_init",
.off = offsetof(struct mai2_dll, init),
}, {
.sym = "mai2_io_poll",
.off = offsetof(struct mai2_dll, poll),
},
};
struct mai2_dll mai2_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 mai2_dll_init(const struct mai2_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("Mai2 IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Mai2 IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "mai2_io_get_api_version");
if (get_api_version != NULL) {
mai2_dll.api_version = get_api_version();
} else {
mai2_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose mai2_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (mai2_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Mai2 IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
mai2_dll.api_version);
goto end;
}
sym = mai2_dll_syms;
hr = dll_bind(&mai2_dll, src, &sym, _countof(mai2_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Mai2 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
mai2hook/mai2-dll.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <windows.h>
#include "mai2io/mai2io.h"
struct mai2_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(uint8_t *opbtn, uint8_t *p1, uint8_t *p2);
};
struct mai2_dll_config {
wchar_t path[MAX_PATH];
};
extern struct mai2_dll mai2_dll;
HRESULT mai2_dll_init(const struct mai2_dll_config *cfg, HINSTANCE self);

22
mai2hook/mai2hook.def Normal file
View File

@ -0,0 +1,22 @@
LIBRARY mai2hook
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
mai2_io_get_api_version
mai2_io_init
mai2_io_poll

37
mai2hook/meson.build Normal file
View File

@ -0,0 +1,37 @@
shared_library(
'mai2hook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'mai2hook.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,
mai2io_lib,
platform_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'led.c',
'led.h',
'io4.c',
'io4.h',
'mai2-dll.c',
'mai2-dll.h',
'touch.c',
'touch.h',
'unity.h',
'unity.c',
],
)

237
mai2hook/touch.c Normal file
View File

@ -0,0 +1,237 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "hooklib/reg.h"
#include "hooklib/uart.h"
#include "hooklib/fdshark.h"
#include "mai2hook/touch.h"
#include "mai2hook/mai2-dll.h"
#include "util/dprintf.h"
#include "util/dump.h"
const char CMD_START = '{';
const char CMD_END = '}';
const char RESP_START = '(';
const char RESP_END = ')';
const char BLANK = '@';
// Commands with parameters
const char CMD_RATIO[1] = "r";
const char CMD_SENS[1] = "k";
const char CMD_SENS_CHECK[2] = "th";
// Commands that never change
const char CMD_RESET[7] = "{RSET}"; // Reset board to default state
const char CMD_STAT[7] = "{STAT}"; // Start sending touch state
const char CMD_HALT[7] = "{HALT}"; // Stop sending touch state
const char RSP_ANY[6] = "(0000)"; // Stop sending touch state
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes);
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes);
static HRESULT touch_handle_irp(struct irp *irp);
static HRESULT touch0_handle_irp_locked(struct irp *irp);
static HRESULT touch1_handle_irp_locked(struct irp *irp);
static HRESULT touch_cmd_dispatch(char* cmd, struct iobuf *dest, uint8_t side);
bool touch0_auto = false;
bool touch1_auto = false;
static CRITICAL_SECTION touch0_lock;
static struct uart touch0_uart;
static uint8_t touch0_written_bytes[520];
static uint8_t touch0_readable_bytes[520];
static CRITICAL_SECTION touch1_lock;
static struct uart touch1_uart;
static uint8_t touch1_written_bytes[520];
static uint8_t touch1_readable_bytes[520];
static const struct reg_hook_val fake_com_keys[] = {
{
.name = L"\\Device\\RealTouchBoard0",
.read = read_fake_com0,
.type = REG_SZ,
},{
.name = L"\\Device\\RealTouchBoard1",
.read = read_fake_com1,
.type = REG_SZ,
},{
.name = L"\\Device\\RealLedBoard0",
.read = read_fake_com2,
.type = REG_SZ,
},
};
HRESULT touch_hook_init(const struct touch_config *cfg)
{
HRESULT hr;
if (!cfg->enable) {
return S_FALSE;
}
dprintf("Mai2 touch: Init\n");
InitializeCriticalSection(&touch0_lock);
InitializeCriticalSection(&touch1_lock);
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"HARDWARE\\DEVICEMAP\\SERIALCOMM",
fake_com_keys,
_countof(fake_com_keys));
if (FAILED(hr)) {
return hr;
}
uart_init(&touch0_uart, 3);
touch0_uart.written.bytes = touch0_written_bytes;
touch0_uart.written.nbytes = sizeof(touch0_written_bytes);
touch0_uart.readable.bytes = touch0_readable_bytes;
touch0_uart.readable.nbytes = sizeof(touch0_readable_bytes);
uart_init(&touch1_uart, 4);
touch1_uart.written.bytes = touch1_written_bytes;
touch1_uart.written.nbytes = sizeof(touch1_written_bytes);
touch1_uart.readable.bytes = touch1_readable_bytes;
touch1_uart.readable.nbytes = sizeof(touch1_readable_bytes);
return iohook_push_handler(touch_handle_irp);
}
static HRESULT read_fake_com0(void *bytes, uint32_t *nbytes)
{
// dprintf("Mai2 Touch: Read COM3 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM3");
}
static HRESULT read_fake_com1(void *bytes, uint32_t *nbytes)
{
// dprintf("Mai2 Touch: Read COM4 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM4");
}
static HRESULT read_fake_com2(void *bytes, uint32_t *nbytes)
{
// dprintf("Mai2 LED: Read COM20 reg val\n");
return reg_hook_read_wstr(bytes, nbytes, L"COM20");
}
static HRESULT touch_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (uart_match_irp(&touch0_uart, irp)) {
EnterCriticalSection(&touch0_lock);
hr = touch0_handle_irp_locked(irp);
LeaveCriticalSection(&touch0_lock);
}
else if (uart_match_irp(&touch1_uart, irp)) {
EnterCriticalSection(&touch1_lock);
hr = touch1_handle_irp_locked(irp);
LeaveCriticalSection(&touch1_lock);
}
else {
return iohook_invoke_next(irp);
}
return hr;
}
static HRESULT touch0_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 touch0: Starting backend\n");
//hr = mai2_dll.touch_init();
if (FAILED(hr)) {
dprintf("Mai2 touch0: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch0_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("touch0 Buffer:\n");
dump_iobuf(&touch0_uart.written);
#endif
hr = touch_cmd_dispatch((char*)touch0_uart.written.bytes, &touch0_uart.readable, 0);
if (FAILED(hr)) {
dprintf("Mai2 touch0: Dispatch failed %08lX\n", hr);
return hr;
}
touch0_uart.written.pos = 0;
return hr;
}
}
static HRESULT touch1_handle_irp_locked(struct irp *irp)
{
HRESULT hr = S_OK;
if (irp->op == IRP_OP_OPEN) {
dprintf("Mai2 touch1: Starting backend\n");
//hr = mai2_dll.touch_init();
if (FAILED(hr)) {
dprintf("Mai2 touch1: Backend error: %x\n", (int) hr);
return hr;
}
}
hr = uart_handle_irp(&touch1_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
for (;;) {
#if 0
dprintf("touch1 Buffer:\n");
dump_iobuf(&touch0_uart.written);
#endif
hr = touch_cmd_dispatch((char*)touch1_uart.written.bytes, &touch1_uart.readable, 1);
if (FAILED(hr)) {
dprintf("Mai2 touch1: Dispatch failed %08lX\n", hr);
return hr;
}
touch1_uart.written.pos = 0;
return hr;
}
}
static HRESULT touch_cmd_dispatch(char* cmd, struct iobuf *dest, uint8_t side)
{
if (!strcmp(cmd, CMD_RESET)) {
dprintf("Mai2 touch%d: Reset\n", side);
return S_OK;
}
else if (!strcmp(cmd, CMD_HALT)) {
dprintf("Mai2 touch%d: Halt\n", side);
return S_OK;
}
dprintf("Mai2 touch%d: Unknow %s\n", side, cmd);
return S_OK;
}

9
mai2hook/touch.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct touch_config {
bool enable;
};
HRESULT touch_hook_init(const struct touch_config *cfg);

116
mai2hook/unity.c Normal file
View File

@ -0,0 +1,116 @@
#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 HMODULE WINAPI my_LoadLibraryExW(const wchar_t *name, HANDLE hFile, DWORD dwFlags);
static HMODULE (WINAPI *next_LoadLibraryExW)(const wchar_t *name, HANDLE hFile, DWORD dwFlags);
static const struct hook_symbol unity_kernel32_syms[] = {
{
.name = "LoadLibraryW",
.patch = my_LoadLibraryW,
.link = (void **) &next_LoadLibraryW,
},{
.name = "LoadLibraryExW",
.patch = my_LoadLibraryExW,
.link = (void **) &next_LoadLibraryExW,
}
};
static const wchar_t *target_modules[] = {
L"mono-2.0-bdwgc.dll",
L"cri_ware_unity.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_LoadLibraryExW(const wchar_t *name, HANDLE hFile, DWORD dwFlags)
{
//dprintf("Unity: LoadLibraryExW %ls\n", name);
return my_LoadLibraryW(name);
}
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
mai2hook/unity.h Normal file
View File

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

46
mai2io/config.c Normal file
View File

@ -0,0 +1,46 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "mai2io/config.h"
static const int mai2_io_1p_default[] = {'W', 'E', 'D', 'C', 'X', 'Z', 'A', 'Q'};
static const int mai2_io_2p_default[] = {0x68, 0x69, 0x66, 0x63, 0x62, 0x61, 0x64, 0x67};
void mai2_io_config_load(
struct mai2_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", VK_DELETE, filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_END, filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_INSERT, filename);
cfg->vk_p1_start = GetPrivateProfileIntW(L"io4", L"p1_start", '1', filename);
cfg->vk_p2_start = GetPrivateProfileIntW(L"io4", L"p2_start", '2', filename);
for (i = 0 ; i < 8 ; i++) {
swprintf_s(key, _countof(key), L"1p_btn%i", i + 1);
cfg->vk_p1_btn[i] = GetPrivateProfileIntW(
L"button",
key,
mai2_io_1p_default[i],
filename);
}
for (i = 0 ; i < 8 ; i++) {
swprintf_s(key, _countof(key), L"2p_btn%i", i + 1);
cfg->vk_p2_btn[i] = GetPrivateProfileIntW(
L"button",
key,
mai2_io_2p_default[i],
filename);
}
}

20
mai2io/config.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct mai2_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_p1_start;
uint8_t vk_p2_start;
uint8_t vk_p1_btn[8];
uint8_t vk_p2_btn[8];
};
void mai2_io_config_load(
struct mai2_io_config *cfg,
const wchar_t *filename);

83
mai2io/mai2io.c Normal file
View File

@ -0,0 +1,83 @@
#include <windows.h>
#include <xinput.h>
#include <limits.h>
#include <stdint.h>
#include "mai2io/mai2io.h"
#include "mai2io/config.h"
static struct mai2_io_config mai2_io_cfg;
uint16_t mai2_io_get_api_version(void)
{
return 0x0100;
}
HRESULT mai2_io_init(void)
{
mai2_io_config_load(&mai2_io_cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT mai2_io_poll(uint8_t *opbtn, uint8_t *player1, uint8_t *player2)
{
uint8_t opts = 0;
uint8_t p1 = 0;
uint8_t p2 = 0;
if (GetAsyncKeyState(mai2_io_cfg.vk_test) & 0x8000) {
opts |= MAI2_IO_OPBTN_TEST;
}if (GetAsyncKeyState(mai2_io_cfg.vk_service) & 0x8000) {
opts |= MAI2_IO_OPBTN_SERVICE;
}if (GetAsyncKeyState(mai2_io_cfg.vk_coin) & 0x8000) {
opts |= MAI2_IO_OPBTN_COIN;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_start) & 0x8000) {
opts |= MAI2_IO_P1_START;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_start) & 0x8000) {
opts |= MAI2_IO_P2_START;
}
*opbtn = opts;
if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[0]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_1;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[1]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_2;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[2]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_3;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[3]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_4;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[4]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_5;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[5]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_6;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[6]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_7;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p1_btn[7]) & 0x8000) {
p1 |= MAI2_IO_GAMEBTN_8;
}
*player1 = p1;
if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[0]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_1;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[1]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_2;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[2]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_3;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[3]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_4;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[4]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_5;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[5]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_6;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[6]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_7;
}if (GetAsyncKeyState(mai2_io_cfg.vk_p2_btn[7]) & 0x8000) {
p2 |= MAI2_IO_GAMEBTN_8;
}
*player2 = p2;
return S_OK;
}

49
mai2io/mai2io.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
MAI2_IO_OPBTN_TEST = 0x01,
MAI2_IO_OPBTN_SERVICE = 0x02,
MAI2_IO_OPBTN_COIN = 0x04,
MAI2_IO_P1_START = 0x08,
MAI2_IO_P2_START = 0x10,
};
enum {
MAI2_IO_GAMEBTN_1 = 0x01,
MAI2_IO_GAMEBTN_2 = 0x02,
MAI2_IO_GAMEBTN_3 = 0x04,
MAI2_IO_GAMEBTN_4 = 0x08,
MAI2_IO_GAMEBTN_5 = 0x10,
MAI2_IO_GAMEBTN_6 = 0x20,
MAI2_IO_GAMEBTN_7 = 0x40,
MAI2_IO_GAMEBTN_8 = 0x80,
};
/* Get the version of the Mai2 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 mai2_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after mai2_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT mai2_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 mai2_io_poll(uint8_t *opbtn, uint8_t *player1, uint8_t *player2);

16
mai2io/meson.build Normal file
View File

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

View File

@ -61,6 +61,7 @@ subdir('idzio')
subdir('mu3io')
subdir('mercuryio')
subdir('cxbio')
subdir('mai2io')
subdir('chunihook')
subdir('divahook')
@ -70,3 +71,4 @@ subdir('minihook')
subdir('mu3hook')
subdir('mercuryhook')
subdir('cxbhook')
subdir('mai2hook')

View File

@ -13,6 +13,7 @@
#include "platform/clock.h"
#include "platform/config.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
@ -30,6 +31,7 @@ void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
amvideo_config_load(&cfg->amvideo, filename);
clock_config_load(&cfg->clock, filename);
dns_config_load(&cfg->dns, filename);
epay_config_load(&cfg->epay, filename);
hwmon_config_load(&cfg->hwmon, filename);
hwreset_config_load(&cfg->hwreset, filename);
misc_config_load(&cfg->misc, filename);
@ -317,3 +319,10 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
filename);
}
void epay_config_load(struct epay_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"epay", L"enable", 1, filename);
}

View File

@ -9,6 +9,7 @@
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
@ -25,6 +26,7 @@ void platform_config_load(
void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename);
void clock_config_load(struct clock_config *cfg, const wchar_t *filename);
void dns_config_load(struct dns_config *cfg, const wchar_t *filename);
void epay_config_load(struct epay_config *cfg, const wchar_t *filename);
void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename);
void hwreset_config_load(struct hwreset_config *cfg, const wchar_t *filename);
void misc_config_load(struct misc_config *cfg, const wchar_t *filename);

View File

@ -64,6 +64,19 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
return hr;
}
// Epay urls
hr = dns_hook_push(L"api-aime.am-all.net", cfg->startup);
if (FAILED(hr)) {
return hr;
}
hr = dns_hook_push(L"tasms-api-basis.thincacloud.com", cfg->startup);
if (FAILED(hr)) {
return hr;
}
// if your ISP resolves bad domains, it will kill the network. These 2
// *cannot* resolve

335
platform/epay.c Normal file
View File

@ -0,0 +1,335 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "hook/table.h"
#include "hooklib/reg.h"
#include "platform/epay.h"
#include "util/dprintf.h"
static HRESULT misc_read_thinca_adapter(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_ca_loc(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_ca_client_loc(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_pattern0(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout0(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_pattern1(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_network_timeout1(void *bytes, uint32_t *nbytes);
static uint64_t thinca_initialize(struct thinca_impl * self, uint64_t val);
static uint64_t thinca_dispose(struct thinca_impl * self);
static uint64_t thinca_set_resource(struct thinca_impl * self, char * res);
static uint64_t thinca_set_pay_log(struct thinca_impl * self, uint64_t val, char * log, uint64_t val2, const char * size_lim);
static uint64_t thinca_set_client_log(struct thinca_impl * self, uint64_t val, char * log);
static uint64_t thinca_set_client_cfg(struct thinca_impl * self, char * log, uint64_t val);
static uint64_t thinca_set_goods_code(struct thinca_impl * self, char * code);
static uint64_t thinca_set_evt_handler(struct thinca_impl * self, void* handler);
static uint64_t thinca_set_cert(struct thinca_impl * self, char * cert, uint64_t val);
static uint64_t thinca_set_serial(struct thinca_impl * self, char * cert);
static uint64_t thinca_check_deal(struct thinca_impl * self, void* deal);
static uint64_t thinca_cancel(struct thinca_impl * self);
static uint64_t thinca_select(struct thinca_impl * self);
static uint64_t thinca_unk(struct thinca_impl * self, uint64_t val);
static void thinca_unk8(struct thinca_impl * self);
static uint64_t my_ThincaPaymentGetVersion();
static uint64_t (*next_ThincaPaymentGetVersion)();
static struct thinca_main* my_ThincaPaymentGetInstance(uint64_t ver);
static struct thinca_main* (*next_ThincaPaymentGetInstance)(uint64_t ver);
static struct thinca_main* thinca_stub;
static const struct reg_hook_val epay_adapter_keys[] = {
{
.name = L"TfpsAimeRwAdapter",
.read = misc_read_thinca_adapter,
.type = REG_SZ,
}
};
static const struct reg_hook_val epay_tcap_keys[] = {
{
.name = L"CaLocation",
.read = misc_read_ca_loc,
.type = REG_SZ,
},
{
.name = L"ThincaTcapClientPath",
.read = misc_read_ca_client_loc,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout,
.type = REG_DWORD,
}
};
static const struct reg_hook_val epay_tcap_url0_keys[] = {
{
.name = L"Pattern",
.read = misc_read_pattern0,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout0,
.type = REG_DWORD,
}
};
static const struct reg_hook_val epay_tcap_url1_keys[] = {
{
.name = L"Pattern",
.read = misc_read_pattern1,
.type = REG_SZ,
},
{
.name = L"ClientNetworkTimeout",
.read = misc_read_network_timeout1,
.type = REG_DWORD,
}
};
static const struct hook_symbol epay_syms[] = {
{
.name = "ThincaPaymentGetVersion",
.patch = my_ThincaPaymentGetVersion,
.link = (void **) &next_ThincaPaymentGetVersion,
.ordinal = 1,
},
{
.name = "__imp_ThincaPaymentGetInstance",
.patch = my_ThincaPaymentGetInstance,
.link = (void **) &next_ThincaPaymentGetInstance,
.ordinal = 2,
},
{
.name = "ThincaPaymentGetInstance",
.patch = my_ThincaPaymentGetInstance,
.link = (void **) &next_ThincaPaymentGetInstance,
.ordinal = 2,
}
};
HRESULT epay_hook_init(const struct epay_config *cfg) {
HRESULT hr;
assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaRwAdapter",
epay_adapter_keys,
_countof(epay_adapter_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient",
epay_tcap_keys,
_countof(epay_tcap_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient\\URL0",
epay_tcap_url0_keys,
_countof(epay_tcap_url0_keys));
if (FAILED(hr)) {
return hr;
}
hr = reg_hook_push_key(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\TFPaymentService\\ThincaTcapClient\\URL1",
epay_tcap_url1_keys,
_countof(epay_tcap_url1_keys));
hook_table_apply(
NULL,
"ThincaPayment.dll",
epay_syms,
_countof(epay_syms));
thinca_stub = (struct thinca_main *)malloc(sizeof(struct thinca_main));
thinca_stub->impl1 = (struct thinca_impl *)malloc(sizeof(struct thinca_impl));
thinca_stub->impl1->unk8 = thinca_unk8;
thinca_stub->impl1->initialize = thinca_initialize;
thinca_stub->impl1->dispose = thinca_dispose;
thinca_stub->impl1->setResource = thinca_set_resource;
thinca_stub->impl1->setThincaPaymentLog = thinca_set_pay_log;
thinca_stub->impl1->setThincaEventInterface = thinca_set_evt_handler;
thinca_stub->impl1->setIcasClientLog = thinca_set_client_log;
thinca_stub->impl1->setIcasClientConfig = thinca_set_client_cfg;
thinca_stub->impl1->setGoodsCode = thinca_set_goods_code;
thinca_stub->impl1->setTerminalSerial = thinca_set_serial;
thinca_stub->impl1->setClientCertificate = thinca_set_cert;
thinca_stub->impl1->checkDeal = thinca_check_deal;
thinca_stub->impl1->cancelRequest = thinca_cancel;
thinca_stub->impl1->selectButton = thinca_select;
thinca_stub->impl1->unk220 = thinca_unk;
thinca_stub->impl1->unk228 = thinca_unk;
dprintf("Epay: Init\n");
return hr;
}
static HRESULT misc_read_thinca_adapter(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"aime_rw_adapterMD.dll");
}
static HRESULT misc_read_ca_loc(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"ca.pem");
}
static HRESULT misc_read_ca_client_loc(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"thincatcapclient.dll");
}
static HRESULT misc_read_network_timeout(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 20000);
}
static HRESULT misc_read_pattern0(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L".*\\.jsp");
}
static HRESULT misc_read_network_timeout0(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 5000);
}
static HRESULT misc_read_pattern1(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L".*(closing|remove).*");
}
static HRESULT misc_read_network_timeout1(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_u32(bytes, nbytes, 60000);
}
static uint64_t thinca_initialize(struct thinca_impl * self, uint64_t val)
{
dprintf("Epay: Thinca Initialize v%I64X\n", val);
return 0;
}
static uint64_t thinca_dispose(struct thinca_impl * self)
{
dprintf("Epay: Thinca Dispose\n");
return 0;
}
static uint64_t thinca_set_resource(struct thinca_impl * self, char * res)
{
dprintf("Epay: Thinca Set Resource %s\n", res);
return 0;
}
static uint64_t thinca_set_pay_log(struct thinca_impl * self, uint64_t val, char * log, uint64_t val2, const char * size_lim)
{
dprintf("Epay: Thinca Set Paylog %lld | %s | %lld | %s\n", val, log, val2, size_lim);
return 0;
}
static uint64_t thinca_set_client_log(struct thinca_impl * self, uint64_t val, char * log)
{
dprintf("Epay: Thinca Set ICAS Client log %lld | %s\n", val, log);
return 0;
}
static uint64_t thinca_set_client_cfg(struct thinca_impl * self, char * log, uint64_t val)
{
dprintf("Epay: Thinca Set ICAS Client Config %s | %lld\n", log, val);
return 0;
}
static uint64_t thinca_set_goods_code(struct thinca_impl * self, char * code)
{
dprintf("Epay: Thinca Set Goods Code %s\n", code);
return 0;
}
static uint64_t thinca_set_evt_handler(struct thinca_impl * self, void* handler)
{
dprintf("Epay: Thinca Set Event Handler %p\n", handler);
return 0;
}
static uint64_t thinca_set_cert(struct thinca_impl * self, char * cert, uint64_t val)
{
dprintf("Epay: Thinca Set Client Cert %s | %lld\n", cert, val);
return 0;
}
static uint64_t thinca_set_serial(struct thinca_impl * self, char * cert)
{
dprintf("Epay: Thinca Set Terminal Serial %s\n", cert);
return 0;
}
static uint64_t thinca_check_deal(struct thinca_impl * self, void* deal)
{
dprintf("Epay: Thinca Check Deal %p\n", deal);
return 0;
}
static uint64_t thinca_cancel(struct thinca_impl * self)
{
dprintf("Epay: Thinca Cancel\n");
return 0;
}
static uint64_t thinca_select(struct thinca_impl * self)
{
dprintf("Epay: Thinca Select\n");
return 0;
}
static uint64_t thinca_unk(struct thinca_impl * self, uint64_t val)
{
dprintf("Epay: Thinca Unknown 220/228 %lld\n", val);
return 0;
}
static void thinca_unk8(struct thinca_impl * self)
{
dprintf("Epay: Thinca Unknown 8\n");
}
static uint64_t my_ThincaPaymentGetVersion()
{
return 0x1040B00;
}
static struct thinca_main* my_ThincaPaymentGetInstance(uint64_t ver)
{
dprintf("Epay: my_ThincaPaymentGetInstance hit!\n");
return thinca_stub;
}

63
platform/epay.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#pragma pack(push,1)
struct epay_config {
bool enable;
};
/* The functions in these structs are how clients like amdaemon interface
* with thinca. We can simply replace these functions with our own stubs
* to bypass errors and such. Currently this DOES NOT allow for epay, and
* trying to do so will most likely just lead to misery. My goal isn't to
* reimplement epay, just to give amdaemon SOMETHING so we can boot properly.
*/
struct thinca_impl {
uint64_t* unk0;
void (*unk8)(struct thinca_impl *);
uint64_t (*initialize)(struct thinca_impl *, uint64_t);
uint64_t (*dispose)(struct thinca_impl *);
uint64_t (*setResource)(struct thinca_impl *, char *);
uint64_t (*setThincaPaymentLog)(struct thinca_impl *, uint64_t, char *, uint64_t, const char *);
uint64_t (*setIcasClientLog)(struct thinca_impl *, uint64_t, char *);
uint64_t (*setIcasClientConfig)(struct thinca_impl *, char *, uint64_t);
uint64_t* unk40;
uint64_t* unk48;
uint64_t (*setClientCertificate)(struct thinca_impl *, char *, uint64_t);
uint64_t (*setTerminalSerial)(struct thinca_impl *, char *);
uint64_t (*setGoodsCode)(struct thinca_impl *, char *);
uint64_t unk68;
uint64_t (*setThincaEventInterface)(struct thinca_impl *, void*); // probably a struct
uint64_t unkGap78[7];
uint64_t (*checkDeal)(struct thinca_impl *, void *); // probably a struct
uint64_t unkGapB8[41];
uint64_t (*cancelRequest)(struct thinca_impl *);
uint64_t (*selectButton)(struct thinca_impl *);
uint64_t unkGap210[2];
uint64_t (*unk220)(struct thinca_impl *, uint64_t);
uint64_t (*unk228)(struct thinca_impl *, uint64_t);
};
/* I believe the actual struct is 0x310 bytes, so for now I'm just
* implementing what I need and hoping the rest don't cause issues
* later. AMDaemon seems to only care about impl1 and deal_thing,
* at least from what I can tell
*/
struct thinca_main {
struct thinca_impl* impl1;
struct thinca_impl* impl2;
HANDLE* mutex1;
HANDLE* mutex2;
HANDLE* mutex3;
uint64_t* unk28;
uint64_t* unk30;
uint64_t* unk38;
uint64_t* unk40;
uint64_t* deal_thing;
uint64_t filler[88];
};
#pragma pack(pop)
HRESULT epay_hook_init(const struct epay_config *cfg);

View File

@ -16,6 +16,8 @@ platform_lib = static_library(
'config.h',
'dns.c',
'dns.h',
'epay.c',
'epay.h',
'hwmon.c',
'hwmon.h',
'hwreset.c',

View File

@ -14,6 +14,8 @@
#include "util/dprintf.h"
static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason);
static int my_vsprintf(char *const buffer, const char *const format, va_list args);
static int (*next_vsprintf)(char *const buffer, const char *const format, va_list args);
static HRESULT misc_read_os_version(void *bytes, uint32_t *nbytes);
static HRESULT misc_read_app_loader_count(void *bytes, uint32_t *nbytes);
@ -29,6 +31,14 @@ static const struct hook_symbol misc_syms[] = {
}
};
static const struct hook_symbol logging_sym[] = {
{
.name = "vsprintf",
.patch = my_vsprintf,
.link = (void **) &next_vsprintf,
}
};
static const struct reg_hook_val misc_root_keys[] = {
{
.name = L"OSVersion",
@ -130,6 +140,7 @@ HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id)
/* Apply function hooks */
hook_table_apply(NULL, "user32.dll", misc_syms, _countof(misc_syms));
hook_table_apply(NULL, "msvcr110.dll", logging_sym, _countof(logging_sym));
return S_OK;
}
@ -170,3 +181,10 @@ static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, L"ALLS MX2.1"); // TODO: Dynamic
}
static int my_vsprintf(char *const buffer, const char *const format, va_list args)
{
int ret = next_vsprintf(buffer, format, args);
dprintf("%s\n", buffer);
return ret;
}

View File

@ -14,21 +14,22 @@
#include "util/str.h"
enum {
NUSEC_IOCTL_PING = 0x22A114,
NUSEC_IOCTL_ERASE_TRACE_LOG = 0x22E188,
NUSEC_IOCTL_ADD_PLAY_COUNT = 0x22E154,
NUSEC_IOCTL_GET_BILLING_CA_CERT = 0x22E1C4,
NUSEC_IOCTL_GET_BILLING_PUBKEY = 0x22E1C8,
NUSEC_IOCTL_GET_NEARFULL = 0x22E20C,
NUSEC_IOCTL_GET_NVRAM_AVAILABLE = 0x22E19C,
NUSEC_IOCTL_GET_NVRAM_GEOMETRY = 0x22E24C,
NUSEC_IOCTL_GET_PLAY_COUNT = 0x22E150,
NUSEC_IOCTL_GET_PLAY_LIMIT = 0x22E204,
NUSEC_IOCTL_GET_TRACE_LOG_DATA = 0x22E194,
NUSEC_IOCTL_GET_TRACE_LOG_STATE = 0x22E198,
NUSEC_IOCTL_PUT_NEARFULL = 0x22E210,
NUSEC_IOCTL_PUT_PLAY_LIMIT = 0x22E208,
NUSEC_IOCTL_PUT_TRACE_LOG_DATA = 0x22E190,
NUSEC_IOCTL_PING = CTL_CODE(0x22, 0x845, METHOD_BUFFERED, FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_PLAY_COUNT = CTL_CODE(0x22, 0x854, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_ADD_PLAY_COUNT = CTL_CODE(0x22, 0x855, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_ERASE_TRACE_LOG = CTL_CODE(0x22, 0x862, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_TD_ERASE_USED = CTL_CODE(0x22, 0x863, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_PUT_TRACE_LOG_DATA = CTL_CODE(0x22, 0x864, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_TRACE_LOG_DATA = CTL_CODE(0x22, 0x865, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_TRACE_LOG_STATE = CTL_CODE(0x22, 0x866, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_NVRAM_AVAILABLE = CTL_CODE(0x22, 0x867, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_BILLING_CA_CERT = CTL_CODE(0x22, 0x871, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_BILLING_PUBKEY = CTL_CODE(0x22, 0x872, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_PLAY_LIMIT = CTL_CODE(0x22, 0x881, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_PUT_PLAY_LIMIT = CTL_CODE(0x22, 0x882, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_NEARFULL = CTL_CODE(0x22, 0x883, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_PUT_NEARFULL = CTL_CODE(0x22, 0x884, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
NUSEC_IOCTL_GET_NVRAM_GEOMETRY = CTL_CODE(0x22, 0x893, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
};
struct nusec_log_record {
@ -42,6 +43,7 @@ static HRESULT nusec_handle_ioctl(struct irp *irp);
static HRESULT nusec_ioctl_ping(struct irp *irp);
static HRESULT nusec_ioctl_erase_trace_log(struct irp *irp);
static HRESULT nusec_ioctl_td_erase_used(struct irp *irp);
static HRESULT nusec_ioctl_add_play_count(struct irp *irp);
static HRESULT nusec_ioctl_get_billing_ca_cert(struct irp *irp);
static HRESULT nusec_ioctl_get_billing_pubkey(struct irp *irp);
@ -209,6 +211,9 @@ static HRESULT nusec_handle_ioctl(struct irp *irp)
case NUSEC_IOCTL_ERASE_TRACE_LOG:
return nusec_ioctl_erase_trace_log(irp);
case NUSEC_IOCTL_TD_ERASE_USED:
return nusec_ioctl_td_erase_used(irp);
case NUSEC_IOCTL_ADD_PLAY_COUNT:
return nusec_ioctl_add_play_count(irp);
@ -288,6 +293,13 @@ static HRESULT nusec_ioctl_erase_trace_log(struct irp *irp)
return S_OK;
}
static HRESULT nusec_ioctl_td_erase_used(struct irp *irp)
{
dprintf("Security: %s\n", __func__);
return S_OK;
}
static HRESULT nusec_ioctl_add_play_count(struct irp *irp)
{
uint32_t delta;

View File

@ -5,6 +5,7 @@
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/misc.h"
#include "platform/netenv.h"
@ -74,7 +75,13 @@ HRESULT platform_hook_init(
return hr;
}
hr = vfs_hook_init(&cfg->vfs);
hr = vfs_hook_init(&cfg->vfs, game_id);
if (FAILED(hr)) {
return hr;
}
hr = epay_hook_init(&cfg->epay);
if (FAILED(hr)) {
return hr;

View File

@ -5,6 +5,7 @@
#include "platform/amvideo.h"
#include "platform/clock.h"
#include "platform/dns.h"
#include "platform/epay.h"
#include "platform/hwmon.h"
#include "platform/hwreset.h"
#include "platform/misc.h"
@ -17,6 +18,7 @@ struct platform_config {
struct amvideo_config amvideo;
struct clock_config clock;
struct dns_config dns;
struct epay_config epay;
struct hwmon_config hwmon;
struct hwreset_config hwreset;
struct misc_config misc;

View File

@ -8,6 +8,8 @@
#include "hooklib/path.h"
#include "hooklib/reg.h"
#include "hook/procaddr.h"
#include "hook/table.h"
#include "platform/vfs.h"
@ -20,6 +22,10 @@ static HRESULT vfs_path_hook_nthome(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_path_hook_w10home(
const wchar_t *src,
wchar_t *dest,
size_t *count);
static HRESULT vfs_path_hook_option(
const wchar_t *src,
wchar_t *dest,
@ -27,10 +33,25 @@ static HRESULT vfs_path_hook_option(
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static wchar_t* my_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)();
static const struct hook_symbol amdaemon_syms[] = {
{
.name = "System_getAppRootPath",
.patch = my_System_getAppRootPath,
.link = (void **) &next_System_getAppRootPath,
},
};
static wchar_t game[5] = {0};
static wchar_t vfs_nthome_real[MAX_PATH];
static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser";
static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1;
static const wchar_t vfs_w10home[] = L"C:\\Users\\AppUser";
static const size_t vfs_w10home_len = _countof(vfs_w10home) - 1;
static const wchar_t vfs_option[] = L"C:\\Mount\\Option";
static const size_t vfs_option_len = _countof(vfs_option) - 1;
@ -48,7 +69,7 @@ static const struct reg_hook_val vfs_reg_vals[] = {
static struct vfs_config vfs_config;
HRESULT vfs_hook_init(const struct vfs_config *config)
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id)
{
wchar_t temp[MAX_PATH];
size_t nthome_len;
@ -61,6 +82,8 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
return S_FALSE;
}
mbstowcs(game, game_id, 4);
if (config->amfs[0] == L'\0') {
dprintf("Vfs: FATAL: AMFS path not specified in INI file\n");
@ -144,6 +167,12 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
return hr;
}
hr = path_hook_push(vfs_path_hook_w10home);
if (FAILED(hr)) {
return hr;
}
if (vfs_config.option[0] != L'\0') {
hr = path_hook_push(vfs_path_hook_option);
@ -162,6 +191,13 @@ HRESULT vfs_hook_init(const struct vfs_config *config)
return hr;
}
proc_addr_table_push(
NULL,
"amdaemon_api.dll",
amdaemon_syms,
_countof(amdaemon_syms)
);
return S_OK;
}
@ -357,6 +393,53 @@ static HRESULT vfs_path_hook_nthome(
return S_OK;
}
static HRESULT vfs_path_hook_w10home(
const wchar_t *src,
wchar_t *dest,
size_t *count)
{
size_t required;
size_t redir_len;
size_t shift;
assert(src != NULL);
assert(count != NULL);
/* Case-insensitive check to see if src starts with vfs_w10home */
if (path_compare_w(src, vfs_w10home, vfs_w10home_len) != 0) {
return S_FALSE;
}
/* Check if the character after vfs_w10home is a separator or the end of
the string */
if (!path_is_separator_w(src[vfs_w10home_len]) &&
src[vfs_w10home_len] != L'\0')
{
return S_FALSE;
}
/* Cut off the matched <prefix>\, add the replaced prefix, count NUL */
shift = path_is_separator_w(src[vfs_w10home_len]) ? 1 : 0;
redir_len = wcslen(vfs_nthome_real);
required = wcslen(src) - vfs_w10home_len - shift + redir_len + 1;
if (dest != NULL) {
if (required > *count) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
wcscpy_s(dest, *count, vfs_nthome_real);
wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_w10home_len + shift);
}
*count = required;
return S_OK;
}
static HRESULT vfs_path_hook_option(
const wchar_t *src,
wchar_t *dest,
@ -413,3 +496,12 @@ static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes)
{
return reg_hook_read_wstr(bytes, nbytes, vfs_config.appdata);
}
static wchar_t* my_System_getAppRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.appdata);
wcscat_s(path, MAX_PATH, game);
wcscat_s(path, MAX_PATH, L"\\");
return path;
}

View File

@ -12,4 +12,4 @@ struct vfs_config {
wchar_t option[MAX_PATH];
};
HRESULT vfs_hook_init(const struct vfs_config *config);
HRESULT vfs_hook_init(const struct vfs_config *config, const char* game_id);

View File

@ -1,4 +1,4 @@
[wrap-git]
directory = capnhook
url = https://github.com/decafcode/capnhook
revision = 69f7e3b48c2e0ff5be1d7a83cdcc2597a458357b
url = https://github.com/Hay1tsme/capnhook
revision = dbdcd61b3a3043b08f86f959bd45df4967503a77