Compare commits

8 Commits

Author SHA1 Message Date
201f54ae36 Formatting 2024-04-16 01:28:29 +07:00
3c2609798b Add option to enable/disable hook 2024-04-16 01:27:48 +07:00
c895c275cf CRLF -> LF 2024-04-15 20:34:30 +07:00
1eceba427e re-enable LTO
it has weird build issues on my machine, so I turned it off.
2024-04-15 20:27:45 +07:00
ecfc104f0f Migrate cm/mai2/mu3 to unityhook 2024-04-15 20:27:04 +07:00
49f7189d35 unityhook: managing VFS redirection and loading assemblies 2024-04-15 20:26:25 +07:00
47a65e5e51 fixed aime LED firmware 2024-03-17 14:20:13 +01:00
774a639bb7 cxb: fixed configs 2024-03-14 00:14:51 +01:00
43 changed files with 495 additions and 301 deletions

View File

@ -25,6 +25,13 @@ struct sg_res_header {
uint8_t payload_len; uint8_t payload_len;
}; };
/* struct to save the version string with its length
to fix NUL terminator issues */
struct version_info {
const char *version;
uint8_t length;
};
typedef HRESULT (*sg_dispatch_fn_t)( typedef HRESULT (*sg_dispatch_fn_t)(
void *ctx, void *ctx,
const void *req, const void *req,

View File

@ -27,11 +27,11 @@ static HRESULT sg_led_cmd_set_color(
const struct sg_led *led, const struct sg_led *led,
const struct sg_led_req_set_color *req); const struct sg_led_req_set_color *req);
const char *sg_led_info[] = { static const struct version_info led_version[] = {
"15084\xFF\x10\x00\x12", {"15084\xFF\x10\x00\x12", 9},
"000-00000\xFF\x11\x40", {"000-00000\xFF\x11\x40", 12},
// maybe the same? // maybe the same?
"000-00000\xFF\x11\x40" {"000-00000\xFF\x11\x40", 12}
}; };
void sg_led_init( void sg_led_init(
@ -156,10 +156,10 @@ static HRESULT sg_led_cmd_get_info(
{ {
sg_led_dprintf(led, "Get info\n"); sg_led_dprintf(led, "Get info\n");
unsigned int len = strlen(sg_led_info[led->gen - 1]); const struct version_info *fw = &led_version[led->gen - 1];
sg_res_init(&res->res, req, len); sg_res_init(&res->res, req, fw->length);
memcpy(res->payload, sg_led_info[led->gen - 1], len); memcpy(res->payload, fw->version, fw->length);
return S_OK; return S_OK;
} }

View File

@ -65,16 +65,16 @@ static HRESULT sg_nfc_cmd_dummy(
const struct sg_req_header *req, const struct sg_req_header *req,
struct sg_res_header *res); struct sg_res_header *res);
static const char *hw_version[] = { static const struct version_info hw_version[] = {
"TN32MSEC003S H/W Ver3.0", {"TN32MSEC003S H/W Ver3.0", 23},
"837-15286", {"837-15286", 9},
"837-15396" {"837-15396", 9}
}; };
static const char *fw_version[] = { static const struct version_info fw_version[] = {
"TN32MSEC003S F/W Ver1.2", {"TN32MSEC003S F/W Ver1.2", 23},
"\x94", {"\x94", 1},
"\x94" {"\x94", 1}
}; };
void sg_nfc_init( void sg_nfc_init(
@ -217,11 +217,11 @@ static HRESULT sg_nfc_cmd_get_fw_version(
const struct sg_req_header *req, const struct sg_req_header *req,
struct sg_nfc_res_get_fw_version *res) struct sg_nfc_res_get_fw_version *res)
{ {
unsigned int len = strlen(fw_version[nfc->gen - 1]); const struct version_info *fw = &fw_version[nfc->gen - 1];
/* Dest version is not NUL terminated, this is intentional */ /* Dest version is not NUL terminated, this is intentional */
sg_res_init(&res->res, req, len); sg_res_init(&res->res, req, fw->length);
memcpy(res->version, fw_version[nfc->gen - 1], len); memcpy(res->version, fw->version, fw->length);
return S_OK; return S_OK;
} }
@ -231,11 +231,11 @@ static HRESULT sg_nfc_cmd_get_hw_version(
const struct sg_req_header *req, const struct sg_req_header *req,
struct sg_nfc_res_get_hw_version *res) struct sg_nfc_res_get_hw_version *res)
{ {
unsigned int len = strlen(hw_version[nfc->gen - 1]); const struct version_info *hw = &hw_version[nfc->gen - 1];
/* Dest version is not NUL terminated, this is intentional */ /* Dest version is not NUL terminated, this is intentional */
sg_res_init(&res->res, req, len); sg_res_init(&res->res, req, hw->length);
memcpy(res->version, hw_version[nfc->gen - 1], len); memcpy(res->version, hw->version, hw->length);
return S_OK; return S_OK;
} }

View File

@ -40,4 +40,5 @@ void cm_hook_config_load(
vfd_config_load(&cfg->vfd, filename); vfd_config_load(&cfg->vfd, filename);
touch_screen_config_load(&cfg->touch, filename); touch_screen_config_load(&cfg->touch, filename);
cm_dll_config_load(&cfg->dll, filename); cm_dll_config_load(&cfg->dll, filename);
unity_config_load(&cfg->unity, filename);
} }

View File

@ -11,6 +11,8 @@
#include "platform/config.h" #include "platform/config.h"
#include "unityhook/config.h"
struct cm_hook_config { struct cm_hook_config {
struct platform_config platform; struct platform_config platform;
struct aime_config aime; struct aime_config aime;
@ -19,6 +21,7 @@ struct cm_hook_config {
struct vfd_config vfd; struct vfd_config vfd;
struct cm_dll_config dll; struct cm_dll_config dll;
struct touch_screen_config touch; struct touch_screen_config touch;
struct unity_config unity;
}; };
void cm_dll_config_load( void cm_dll_config_load(

View File

@ -16,10 +16,11 @@
#include "cmhook/config.h" #include "cmhook/config.h"
#include "cmhook/io4.h" #include "cmhook/io4.h"
#include "cmhook/cm-dll.h" #include "cmhook/cm-dll.h"
#include "cmhook/unity.h"
#include "platform/platform.h" #include "platform/platform.h"
#include "unityhook/hook.h"
#include "util/dprintf.h" #include "util/dprintf.h"
static HMODULE cm_hook_mod; static HMODULE cm_hook_mod;
@ -83,7 +84,7 @@ static DWORD CALLBACK cm_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `cmhook` initialization. */ hooked earlier in the `cmhook` initialization. */
unity_hook_init(); unity_hook_init(&cm_hook_cfg.unity, cm_hook_mod);
/* Initialize debug helpers */ /* Initialize debug helpers */

View File

@ -16,6 +16,7 @@ shared_library(
hooklib_lib, hooklib_lib,
cmio_lib, cmio_lib,
platform_lib, platform_lib,
unityhook_lib,
util_lib, util_lib,
], ],
sources : [ sources : [
@ -26,7 +27,5 @@ shared_library(
'io4.h', 'io4.h',
'cm-dll.c', 'cm-dll.c',
'cm-dll.h', 'cm-dll.h',
'unity.h',
'unity.c',
], ],
) )

View File

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

View File

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

View File

@ -23,22 +23,32 @@ void cxb_dll_config_load(
struct cxb_dll_config *cfg, struct cxb_dll_config *cfg,
const wchar_t *filename) const wchar_t *filename)
{ {
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"cxbio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
} }
void revio_config_load(struct revio_config *cfg, const wchar_t *filename) void revio_config_load(struct revio_config *cfg, const wchar_t *filename)
{ {
assert(cfg != NULL);
assert(filename != NULL);
} cfg->enable = GetPrivateProfileIntW(L"revio", L"enable", 1, filename);
void network_config_load(struct network_config *cfg, const wchar_t *filename)
{
} }
void led_config_load(struct led_config *cfg, const wchar_t *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 cxb_hook_config_load( void cxb_hook_config_load(
@ -56,6 +66,5 @@ void cxb_hook_config_load(
gfx_config_load(&cfg->gfx, filename); gfx_config_load(&cfg->gfx, filename);
cxb_dll_config_load(&cfg->dll, filename); cxb_dll_config_load(&cfg->dll, filename);
revio_config_load(&cfg->revio, filename); revio_config_load(&cfg->revio, filename);
network_config_load(&cfg->network, filename);
led_config_load(&cfg->led, filename); led_config_load(&cfg->led, filename);
} }

View File

@ -10,7 +10,6 @@
#include "cxbhook/cxb-dll.h" #include "cxbhook/cxb-dll.h"
#include "cxbhook/revio.h" #include "cxbhook/revio.h"
#include "cxbhook/led.h" #include "cxbhook/led.h"
#include "cxbhook/network.h"
#include "gfxhook/gfx.h" #include "gfxhook/gfx.h"
@ -23,7 +22,6 @@ struct cxb_hook_config {
struct gfx_config gfx; struct gfx_config gfx;
struct cxb_dll_config dll; struct cxb_dll_config dll;
struct revio_config revio; struct revio_config revio;
struct network_config network;
struct led_config led; struct led_config led;
}; };
@ -32,7 +30,6 @@ void cxb_dll_config_load(
const wchar_t *filename); const wchar_t *filename);
void revio_config_load(struct revio_config *cfg, const wchar_t *filename); void revio_config_load(struct revio_config *cfg, const wchar_t *filename);
void network_config_load(struct network_config *cfg, const wchar_t *filename);
void led_config_load(struct led_config *cfg, const wchar_t *filename); void led_config_load(struct led_config *cfg, const wchar_t *filename);
void cxb_hook_config_load( void cxb_hook_config_load(

View File

@ -9,7 +9,6 @@
#include "cxbhook/config.h" #include "cxbhook/config.h"
#include "cxbhook/revio.h" #include "cxbhook/revio.h"
#include "cxbhook/led.h" #include "cxbhook/led.h"
#include "cxbhook/network.h"
#include "cxbio/cxbio.h" #include "cxbio/cxbio.h"
@ -103,12 +102,6 @@ static DWORD CALLBACK cxb_pre_startup(void)
goto fail; goto fail;
} }
hr = network_hook_init(&cxb_hook_cfg.network);
if (FAILED(hr)) {
goto fail;
}
hr = led_hook_init(&cxb_hook_cfg.led); hr = led_hook_init(&cxb_hook_cfg.led);
if (FAILED(hr)) { if (FAILED(hr)) {

View File

@ -49,7 +49,13 @@ static struct hook_symbol lamp_syms[] = {
HRESULT led_hook_init(struct led_config *cfg) HRESULT led_hook_init(struct led_config *cfg)
{ {
dprintf("LED: Init\n"); assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
dprintf("LED: Hook enabled.\n");
return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms)); return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms));
} }

View File

@ -30,7 +30,5 @@ shared_library(
'revio.h', 'revio.h',
'led.c', 'led.c',
'led.h', 'led.h',
'network.c',
'network.h',
], ],
) )

View File

@ -1,13 +0,0 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "cxbhook/network.h"
#include "util/dprintf.h"
HRESULT network_hook_init(struct network_config *cfg)
{
dprintf("Network: Init\n");
return S_OK;
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct network_config {
bool enable;
bool disable_ssl;
char title_server[PATH_MAX];
};
HRESULT network_hook_init(struct network_config *cfg);

View File

@ -82,7 +82,13 @@ static struct hook_symbol revio_syms[] = {
HRESULT revio_hook_init(struct revio_config *cfg) HRESULT revio_hook_init(struct revio_config *cfg)
{ {
dprintf("Revio: Init\n"); assert(cfg != NULL);
if (!cfg->enable) {
return S_FALSE;
}
dprintf("Revio: Hook enabled.\n");
return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms)); return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms));
} }

View File

@ -68,6 +68,11 @@ dipsw1=0
; Enable/Disable WinTouch emulation ; Enable/Disable WinTouch emulation
enable=0 enable=0
[unity]
; Path to a .NET DLL that should run before the game. Useful for loading
; modding frameworks such as BepInEx.
targetAssembly=
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; Custom IO settings ; Custom IO settings
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------

View File

@ -110,7 +110,7 @@ path=
[revio] [revio]
; Enable emulation of the rev IO board ; Enable emulation of the rev IO board
enabe=1 enable=1
; Test button virtual-key code. Default is the F1 key. ; Test button virtual-key code. Default is the F1 key.
test=0x70 test=0x70
; Service button virtual-key code. Default is the F2 key. ; Service button virtual-key code. Default is the F2 key.

View File

@ -83,6 +83,15 @@ path=
; Leave empty if you want to use Segatools built-in keyboard input. ; Leave empty if you want to use Segatools built-in keyboard input.
path= path=
; -----------------------------------------------------------------------------
; Misc. hook settings
; -----------------------------------------------------------------------------
[unity]
; Path to a .NET DLL that should run before the game. Useful for loading
; modding frameworks such as BepInEx.
targetAssembly=
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; Input settings ; Input settings
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------

View File

@ -72,6 +72,11 @@ dipsw1=1
[gfx] [gfx]
enable=1 enable=1
[unity]
; Path to a .NET DLL that should run before the game. Useful for loading
; modding frameworks such as BepInEx.
targetAssembly=
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; Custom IO settings ; Custom IO settings
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------

View File

@ -39,4 +39,5 @@ void mai2_hook_config_load(
io4_config_load(&cfg->io4, filename); io4_config_load(&cfg->io4, filename);
vfd_config_load(&cfg->vfd, filename); vfd_config_load(&cfg->vfd, filename);
mai2_dll_config_load(&cfg->dll, filename); mai2_dll_config_load(&cfg->dll, filename);
unity_config_load(&cfg->unity, filename);
} }

View File

@ -10,6 +10,8 @@
#include "platform/config.h" #include "platform/config.h"
#include "unityhook/config.h"
struct mai2_hook_config { struct mai2_hook_config {
struct platform_config platform; struct platform_config platform;
struct aime_config aime; struct aime_config aime;
@ -17,6 +19,7 @@ struct mai2_hook_config {
struct io4_config io4; struct io4_config io4;
struct vfd_config vfd; struct vfd_config vfd;
struct mai2_dll_config dll; struct mai2_dll_config dll;
struct unity_config unity;
}; };
void mai2_dll_config_load( void mai2_dll_config_load(

View File

@ -12,10 +12,11 @@
#include "mai2hook/config.h" #include "mai2hook/config.h"
#include "mai2hook/io4.h" #include "mai2hook/io4.h"
#include "mai2hook/mai2-dll.h" #include "mai2hook/mai2-dll.h"
#include "mai2hook/unity.h"
#include "platform/platform.h" #include "platform/platform.h"
#include "unityhook/hook.h"
#include "util/dprintf.h" #include "util/dprintf.h"
static HMODULE mai2_hook_mod; static HMODULE mai2_hook_mod;
@ -80,7 +81,7 @@ static DWORD CALLBACK mai2_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mai2hook` initialization. */ hooked earlier in the `mai2hook` initialization. */
unity_hook_init(); unity_hook_init(&mai2_hook_cfg.unity, mai2_hook_mod);
/* Initialize debug helpers */ /* Initialize debug helpers */

View File

@ -15,6 +15,7 @@ shared_library(
hooklib_lib, hooklib_lib,
mai2io_lib, mai2io_lib,
platform_lib, platform_lib,
unityhook_lib,
util_lib, util_lib,
], ],
sources : [ sources : [
@ -25,7 +26,5 @@ shared_library(
'io4.h', 'io4.h',
'mai2-dll.c', 'mai2-dll.c',
'mai2-dll.h', 'mai2-dll.h',
'unity.h',
'unity.c',
], ],
) )

View File

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

View File

@ -42,6 +42,7 @@ shlwapi_lib = cc.find_library('shlwapi')
dinput8_lib = cc.find_library('dinput8') dinput8_lib = cc.find_library('dinput8')
dxguid_lib = cc.find_library('dxguid') dxguid_lib = cc.find_library('dxguid')
xinput_lib = cc.find_library('xinput') xinput_lib = cc.find_library('xinput')
pathcch_lib = cc.find_library('pathcch')
inc = include_directories('.') inc = include_directories('.')
capnhook = subproject('capnhook') capnhook = subproject('capnhook')
@ -55,6 +56,7 @@ subdir('platform')
subdir('util') subdir('util')
subdir('gfxhook') subdir('gfxhook')
subdir('unityhook')
subdir('aimeio') subdir('aimeio')
subdir('chuniio') subdir('chuniio')

View File

@ -42,4 +42,5 @@ void mu3_hook_config_load(
gfx_config_load(&cfg->gfx, filename); gfx_config_load(&cfg->gfx, filename);
vfd_config_load(&cfg->vfd, filename); vfd_config_load(&cfg->vfd, filename);
mu3_dll_config_load(&cfg->dll, filename); mu3_dll_config_load(&cfg->dll, filename);
unity_config_load(&cfg->unity, filename);
} }

View File

@ -13,6 +13,8 @@
#include "platform/config.h" #include "platform/config.h"
#include "unityhook/config.h"
struct mu3_hook_config { struct mu3_hook_config {
struct platform_config platform; struct platform_config platform;
struct aime_config aime; struct aime_config aime;
@ -22,6 +24,7 @@ struct mu3_hook_config {
// struct led15093_config led15093; // struct led15093_config led15093;
struct vfd_config vfd; struct vfd_config vfd;
struct mu3_dll_config dll; struct mu3_dll_config dll;
struct unity_config unity;
}; };
void mu3_dll_config_load( void mu3_dll_config_load(

View File

@ -2,7 +2,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "board/io4.h"
#include "board/sg-reader.h" #include "board/sg-reader.h"
#include "board/vfd.h" #include "board/vfd.h"
@ -20,10 +19,12 @@
#include "mu3hook/config.h" #include "mu3hook/config.h"
#include "mu3hook/io4.h" #include "mu3hook/io4.h"
#include "mu3hook/mu3-dll.h" #include "mu3hook/mu3-dll.h"
#include "mu3hook/unity.h"
#include "platform/platform.h" #include "platform/platform.h"
#include "unityhook/config.h"
#include "unityhook/hook.h"
#include "util/dprintf.h" #include "util/dprintf.h"
static HMODULE mu3_hook_mod; static HMODULE mu3_hook_mod;
@ -99,7 +100,7 @@ static DWORD CALLBACK mu3_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mu3hook` initialization. */ hooked earlier in the `mu3hook` initialization. */
unity_hook_init(); unity_hook_init(&mu3_hook_cfg.unity, mu3_hook_mod);
/* Initialize debug helpers */ /* Initialize debug helpers */

View File

@ -17,6 +17,7 @@ shared_library(
hooklib_lib, hooklib_lib,
mu3io_lib, mu3io_lib,
platform_lib, platform_lib,
unityhook_lib,
util_lib, util_lib,
], ],
sources : [ sources : [
@ -27,7 +28,5 @@ shared_library(
'io4.h', 'io4.h',
'mu3-dll.c', 'mu3-dll.c',
'mu3-dll.h', 'mu3-dll.h',
'unity.h',
'unity.c',
], ],
) )

View File

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

View File

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

View File

@ -82,7 +82,7 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
return hr; return hr;
} }
// croosbeats REV. // crossbeats REV.
hr = dns_hook_push(L"https://rev-ent.ac.capcom.jp:443", cfg->title); hr = dns_hook_push(L"https://rev-ent.ac.capcom.jp:443", cfg->title);
if (FAILED(hr)) { if (FAILED(hr)) {

14
unityhook/config.c Normal file
View File

@ -0,0 +1,14 @@
#include "config.h"
void unity_config_load(struct unity_config *cfg, const wchar_t *filename) {
cfg->enable = GetPrivateProfileIntW(L"unity", L"enable", 1, filename);
GetPrivateProfileStringW(
L"unity",
L"targetAssembly",
L"",
cfg->target_assembly,
_countof(cfg->target_assembly),
filename
);
}

12
unityhook/config.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <stdbool.h>
#include <windows.h>
struct unity_config {
bool enable;
wchar_t target_assembly[MAX_PATH];
};
void unity_config_load(struct unity_config *cfg, const wchar_t *filename);

173
unityhook/doorstop.c Normal file
View File

@ -0,0 +1,173 @@
// A simplified version of NeighTools' UnityDoorstop, allowing mod loaders
// like BepInEx to be loaded into Unity games.
//
// SPDX-License-Identifier: CC0
// https://github.com/NeighTools/UnityDoorstop
#include <stdbool.h>
#include <pathcch.h>
#include <psapi.h>
#include "hooklib/procaddr.h"
#include "util/dprintf.h"
#include "doorstop.h"
#include "mono.h"
#include "util.h"
static void * my_mono_jit_init_version(const char *root_domain_name, const char *runtime_version);
void doorstop_invoke(void *domain);
static char module_name[MAX_PATH];
static bool doorstop_hook_initted;
static struct unity_config unity_config;
static struct hook_symbol unity_mono_syms[] = {
{
.name = "mono_jit_init_version",
.patch = my_mono_jit_init_version,
}
};
void doorstop_mono_hook_init(const struct unity_config *cfg, HINSTANCE module) {
if (doorstop_hook_initted || cfg->target_assembly[0] == 0) {
return;
}
GetModuleBaseNameA(GetCurrentProcess(), module, module_name, MAX_PATH);
memcpy(&unity_config, cfg, sizeof(*cfg));
load_mono_functions(module);
proc_addr_table_push(module_name, unity_mono_syms, _countof(unity_mono_syms));
doorstop_hook_initted = true;
}
static void * my_mono_jit_init_version(const char *root_domain_name, const char *runtime_version) {
dprintf("Unity: Starting Mono domain \"%s\"\n", root_domain_name);
SetEnvironmentVariableW(L"DOORSTOP_DLL_SEARCH_DIRS", widen(mono_assembly_getrootdir()));
void* domain = mono_jit_init_version(root_domain_name, runtime_version);
doorstop_invoke(domain);
return domain;
}
void doorstop_invoke(void* domain) {
if (GetEnvironmentVariableW(L"DOORSTOP_INITIALIZED", NULL, 0) != 0) {
dprintf("Unity: Doorstop is already initialized.\n");
return;
}
SetEnvironmentVariableW(L"DOORSTOP_INITIALIZED", L"TRUE");
mono_thread_set_main(mono_thread_current());
if (mono_domain_set_config) {
#define CONFIG_EXT L".config"
wchar_t exe_path[MAX_PATH];
size_t exe_path_len = GetModuleFileNameW(NULL, exe_path, MAX_PATH);
wchar_t *folder_name = wcsdup(exe_path);
PathCchRemoveFileSpec(folder_name, exe_path_len + 1);
char *exe_path_n = narrow(exe_path);
char *folder_name_n = narrow(folder_name);
dprintf("Unity: Setting config paths: base dir: %s; config path: %s\n", folder_name_n, exe_path_n);
mono_domain_set_config(domain, folder_name_n, exe_path_n);
free(folder_name);
free(exe_path_n);
free(folder_name_n);
#undef CONFIG_EXT
}
SetEnvironmentVariableW(L"DOORSTOP_INVOKE_DLL_PATH", unity_config.target_assembly);
char *assembly_dir = mono_assembly_getrootdir();
dprintf("Unity: Assembly directory: %s\n", assembly_dir);
SetEnvironmentVariableA("DOORSTOP_MANAGED_FOLDER_DIR", assembly_dir);
wchar_t app_path[MAX_PATH];
GetModuleFileNameW(NULL, app_path, MAX_PATH);
SetEnvironmentVariableW(L"DOORSTOP_PROCESS_PATH", app_path);
char* dll_path = narrow(unity_config.target_assembly);
dprintf("Unity: Loading assembly: %s\n", dll_path);
void* assembly = mono_domain_assembly_open(domain, dll_path);
if (!assembly) {
dprintf("Unity: Failed to load assembly\n");
free(dll_path);
return;
}
void *image = mono_assembly_get_image(assembly);
if (!image) {
dprintf("Unity: Assembly image doesn't exist\n");
free(dll_path);
return;
}
void *desc = mono_method_desc_new("*:Main", FALSE);
void *method = mono_method_desc_search_in_image(desc, image);
if (!method) {
dprintf("Unity: Assembly does not have a valid entrypoint.\n");
free(dll_path);
return;
}
void *signature = mono_method_signature(method);
UINT32 params = mono_signature_get_param_count(signature);
void **args = NULL;
if (params == 1) {
// If there is a parameter, it's most likely a string[].
void *args_array = mono_array_new(domain, mono_get_string_class(), 0);
args = malloc(sizeof(void*) * 1);
args[0] = args_array;
}
dprintf("Unity: Invoking method %p\n", method);
void *exc = NULL;
mono_runtime_invoke(method, NULL, args, &exc);
if (exc) {
dprintf("Unity: Error invoking method!\n");
void *ex_class = mono_get_exception_class();
void *to_string_desc = mono_method_desc_new("*:ToString()", FALSE);
void* to_string_method = mono_method_desc_search_in_class(to_string_desc, ex_class);
mono_method_desc_free(to_string_desc);
if (to_string_method) {
void* real_to_string_method = mono_object_get_virtual_method(exc, to_string_method);
void* exc2 = NULL;
void* str = mono_runtime_invoke(real_to_string_method, exc, NULL, &exc2);
if (!exc2) {
char* exc_str = mono_string_to_utf8(str);
dprintf("Unity: Error message: %s\n", exc_str);
}
}
}
mono_method_desc_free(desc);
free(dll_path);
if (args) {
free(args);
args = NULL;
}
}

5
unityhook/doorstop.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "config.h"
void doorstop_mono_hook_init(const struct unity_config *cfg, HINSTANCE module);

View File

@ -1,14 +1,23 @@
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <windows.h>
#include "hook/table.h" #include "hook/table.h"
#include "hooklib/dll.h"
#include "hooklib/path.h" #include "hooklib/path.h"
#include "util/dprintf.h" #include "util/dprintf.h"
#include "doorstop.h"
#include "hook.h"
static bool unity_hook_initted;
static struct unity_config unity_config;
static const wchar_t *target_modules[] = {
L"mono.dll",
L"mono-2.0-bdwgc.dll",
L"cri_ware_unity.dll",
};
static const size_t target_modules_len = _countof(target_modules);
static void dll_hook_insert_hooks(HMODULE target); static void dll_hook_insert_hooks(HMODULE target);
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name); static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name);
@ -22,28 +31,33 @@ static const struct hook_symbol unity_kernel32_syms[] = {
}, },
}; };
static const wchar_t *target_modules[] = { void unity_hook_init(const struct unity_config *cfg, HINSTANCE self) {
L"mono-2.0-bdwgc.dll", assert(cfg != NULL);
L"cri_ware_unity.dll",
};
static const size_t target_modules_len = _countof(target_modules);
void unity_hook_init(void) if (!cfg->enable) {
{ return;
}
if (unity_hook_initted) {
return;
}
memcpy(&unity_config, cfg, sizeof(*cfg));
dll_hook_insert_hooks(NULL); dll_hook_insert_hooks(NULL);
unity_hook_initted = true;
dprintf("Unity: Hook enabled.\n");
} }
static void dll_hook_insert_hooks(HMODULE target) static void dll_hook_insert_hooks(HMODULE target) {
{
hook_table_apply( hook_table_apply(
target, target,
"kernel32.dll", "kernel32.dll",
unity_kernel32_syms, unity_kernel32_syms,
_countof(unity_kernel32_syms)); _countof(unity_kernel32_syms));
} }
static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) {
{
const wchar_t *name_end; const wchar_t *name_end;
const wchar_t *target_module; const wchar_t *target_module;
bool already_loaded; bool already_loaded;
@ -66,6 +80,11 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name)
if (!already_loaded && result != NULL) { if (!already_loaded && result != NULL) {
name_len = wcslen(name); name_len = wcslen(name);
// mono entrypoint for injecting target_assembly
if (GetProcAddress(result, "mono_jit_init_version")) {
doorstop_mono_hook_init(&unity_config, result);
}
for (size_t i = 0; i < target_modules_len; i++) { for (size_t i = 0; i < target_modules_len; i++) {
target_module = target_modules[i]; target_module = target_modules[i];
target_module_len = wcslen(target_module); target_module_len = wcslen(target_module);

7
unityhook/hook.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "config.h"
void unity_hook_init(const struct unity_config *cfg, HINSTANCE self);

20
unityhook/meson.build Normal file
View File

@ -0,0 +1,20 @@
unityhook_lib = static_library(
'unityhook',
include_directories: inc,
implicit_include_directories: false,
c_pch: '../precompiled.h',
dependencies: [
capnhook.get_variable('hook_dep'),
pathcch_lib
],
sources: [
'mono.h',
'config.c',
'config.h',
'doorstop.c',
'doorstop.h',
'hook.c',
'hook.h',
'util.h'
],
)

100
unityhook/mono.h Normal file
View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: CC0
// https://github.com/NeighTools/UnityDoorstop
#pragma once
#include <windows.h>
// Here we define the pointers to some functions within mono.dll
// Note to C learners: these are not signature definitions, but rather "variable"
// definitions with the function pointer type.
// Note: we use void* instead of the real intented structs defined in mono API
// This way we don't need to include or define any of Mono's structs, which saves space
// This, obviously, comes with a drawback of not being able to easily access the contents of the structs
void * (*mono_thread_current)();
void (*mono_thread_set_main)(void *);
void *(*mono_jit_init_version)(const char *root_domain_name, const char *runtime_version);
void *(*mono_domain_assembly_open)(void *domain, const char *name);
void *(*mono_assembly_get_image)(void *assembly);
void *(*mono_runtime_invoke)(void *method, void *obj, void **params, void **exc);
void *(*mono_method_desc_new)(const char *name, int include_namespace);
void* (*mono_method_desc_search_in_image)(void* desc, void* image);
void *(*mono_method_desc_search_in_class)(void *desc, void *klass);
void (*mono_method_desc_free)(void *desc);
void *(*mono_method_signature)(void *method);
UINT32 (*mono_signature_get_param_count)(void *sig);
void (*mono_domain_set_config)(void *domain, char *base_dir, char *config_file_name);
void *(*mono_array_new)(void *domain, void *eclass, uintptr_t n);
void *(*mono_get_string_class)();
char *(*mono_assembly_getrootdir)();
// Additional funcs to bootstrap custom MONO
void (*mono_set_dirs)(const char* assembly_dir, const char* config_dir);
void (*mono_config_parse)(const char* filename);
void (*mono_set_assemblies_path)(const char* path);
void *(*mono_object_to_string)(void* obj, void** exc);
char *(*mono_string_to_utf8)(void* s);
void *(*mono_image_open_from_data_with_name)(void *data, DWORD data_len, int need_copy, void *status, int refonly,
const char *name);
void* (*mono_get_exception_class)();
void* (*mono_object_get_virtual_method)(void* obj_raw, void* method);
void* (*mono_jit_parse_options)(int argc, const char** argv);
typedef enum {
MONO_DEBUG_FORMAT_NONE,
MONO_DEBUG_FORMAT_MONO,
/* Deprecated, the mdb debugger is not longer supported. */
MONO_DEBUG_FORMAT_DEBUGGER
} MonoDebugFormat;
void* (*mono_debug_init)(MonoDebugFormat format);
void* (*mono_debug_domain_create)(void* domain);
/**
* \brief Loads Mono C API function pointers so that the above definitions can be called.
* \param mono_lib Mono.dll module.
*/
void load_mono_functions(HMODULE mono_lib) {
// Enjoy the fact that C allows such sloppy casting
// In C++ you would have to cast to the precise function pointer type
#define GET_MONO_PROC(name) name = (void*)GetProcAddress(mono_lib, #name)
// Find and assign all our functions that we are going to use
GET_MONO_PROC(mono_domain_assembly_open);
GET_MONO_PROC(mono_assembly_get_image);
GET_MONO_PROC(mono_runtime_invoke);
GET_MONO_PROC(mono_jit_init_version);
GET_MONO_PROC(mono_method_desc_new);
GET_MONO_PROC(mono_method_desc_search_in_class);
GET_MONO_PROC(mono_method_desc_search_in_image);
GET_MONO_PROC(mono_method_desc_free);
GET_MONO_PROC(mono_method_signature);
GET_MONO_PROC(mono_signature_get_param_count);
GET_MONO_PROC(mono_array_new);
GET_MONO_PROC(mono_get_string_class);
GET_MONO_PROC(mono_assembly_getrootdir);
GET_MONO_PROC(mono_thread_current);
GET_MONO_PROC(mono_thread_set_main);
GET_MONO_PROC(mono_domain_set_config);
GET_MONO_PROC(mono_set_dirs);
GET_MONO_PROC(mono_config_parse);
GET_MONO_PROC(mono_set_assemblies_path);
GET_MONO_PROC(mono_object_to_string);
GET_MONO_PROC(mono_string_to_utf8);
GET_MONO_PROC(mono_image_open_from_data_with_name);
GET_MONO_PROC(mono_get_exception_class);
GET_MONO_PROC(mono_object_get_virtual_method);
GET_MONO_PROC(mono_jit_parse_options);
GET_MONO_PROC(mono_debug_init);
GET_MONO_PROC(mono_debug_domain_create);
#undef GET_MONO_PROC
}

20
unityhook/util.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
wchar_t *widen(const char *str) {
const int reqsz = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
wchar_t *result = malloc(reqsz * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, str, -1, result, reqsz);
return result;
}
char *narrow(const wchar_t *str) {
const int reqsz = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
char *result = malloc(reqsz * sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, str, -1, result, reqsz, NULL, NULL);
return result;
}