forked from Hay1tsme/segatools
		
	Compare commits
	
		
			10 Commits
		
	
	
		
			2024-02-22
			...
			fix/unity/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 65173e1fa6 | |||
| 4041844ea9 | |||
| 47a65e5e51 | |||
| 774a639bb7 | |||
| 097b74d849 | |||
| 2590e749ca | |||
| 9c66488906 | |||
| f570869946 | |||
| 629ded4018 | |||
| ca36a879cb | 
| @ -1,6 +1,6 @@ | ||||
| # Segatools | ||||
|  | ||||
| Version: `2024-02-22` | ||||
| Version: `2024-03-13` | ||||
|  | ||||
| Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms. | ||||
|  | ||||
| @ -8,7 +8,9 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo | ||||
|  | ||||
| * CHUNITHM | ||||
|   * up to [CHUNITHM PARADISE LOST](doc/chunihook.md) | ||||
|   * starting from CHUNITHM NEW!!  | ||||
|   * starting from CHUNITHM NEW!! | ||||
| * crossbeats REV. | ||||
|   * up to crossbeats REV. SUNRISE | ||||
| * Initial D | ||||
|   * [Initial D Arcade Stage Zero](doc/idzhook.md) | ||||
|   * Initial D THE ARCADE | ||||
|  | ||||
| @ -68,7 +68,6 @@ static void aime_io_config_read( | ||||
|             cfg->felica_path, | ||||
|             _countof(cfg->felica_path), | ||||
|             filename); | ||||
|     // dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError()); | ||||
|  | ||||
|     cfg->felica_gen = GetPrivateProfileIntW( | ||||
|             L"aime", | ||||
|  | ||||
| @ -25,6 +25,13 @@ struct sg_res_header { | ||||
|     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)( | ||||
|         void *ctx, | ||||
|         const void *req, | ||||
|  | ||||
| @ -27,11 +27,11 @@ static HRESULT sg_led_cmd_set_color( | ||||
|         const struct sg_led *led, | ||||
|         const struct sg_led_req_set_color *req); | ||||
|  | ||||
| const char *sg_led_info[] = { | ||||
|     "15084\xFF\x10\x00\x12", | ||||
|     "000-00000\xFF\x11\x40", | ||||
| static const struct version_info led_version[] = { | ||||
|     {"15084\xFF\x10\x00\x12", 9}, | ||||
|     {"000-00000\xFF\x11\x40", 12}, | ||||
|     // maybe the same? | ||||
|     "000-00000\xFF\x11\x40" | ||||
|     {"000-00000\xFF\x11\x40", 12} | ||||
| }; | ||||
|  | ||||
| void sg_led_init( | ||||
| @ -156,10 +156,10 @@ static HRESULT sg_led_cmd_get_info( | ||||
| { | ||||
|     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); | ||||
|     memcpy(res->payload, sg_led_info[led->gen - 1], len); | ||||
|     sg_res_init(&res->res, req, fw->length); | ||||
|     memcpy(res->payload, fw->version, fw->length); | ||||
|  | ||||
|     return S_OK; | ||||
| } | ||||
|  | ||||
| @ -65,16 +65,16 @@ static HRESULT sg_nfc_cmd_dummy( | ||||
|         const struct sg_req_header *req, | ||||
|         struct sg_res_header *res); | ||||
|  | ||||
| static const char *hw_version[] = { | ||||
|     "TN32MSEC003S H/W Ver3.0", | ||||
|     "837-15286", | ||||
|     "837-15396" | ||||
| static const struct version_info hw_version[] = { | ||||
|     {"TN32MSEC003S H/W Ver3.0", 23}, | ||||
|     {"837-15286", 9}, | ||||
|     {"837-15396", 9} | ||||
| }; | ||||
|  | ||||
| static const char *fw_version[] = { | ||||
|     "TN32MSEC003S F/W Ver1.2", | ||||
|     "\x94", | ||||
|     "\x94" | ||||
| static const struct version_info fw_version[] = { | ||||
|     {"TN32MSEC003S F/W Ver1.2", 23}, | ||||
|     {"\x94", 1}, | ||||
|     {"\x94", 1} | ||||
| }; | ||||
|  | ||||
| void sg_nfc_init( | ||||
| @ -217,11 +217,11 @@ static HRESULT sg_nfc_cmd_get_fw_version( | ||||
|         const struct sg_req_header *req, | ||||
|         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 */ | ||||
|     sg_res_init(&res->res, req, len); | ||||
|     memcpy(res->version, fw_version[nfc->gen - 1], len); | ||||
|     sg_res_init(&res->res, req, fw->length); | ||||
|     memcpy(res->version, fw->version, fw->length); | ||||
|  | ||||
|     return S_OK; | ||||
| } | ||||
| @ -231,11 +231,11 @@ static HRESULT sg_nfc_cmd_get_hw_version( | ||||
|         const struct sg_req_header *req, | ||||
|         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 */ | ||||
|     sg_res_init(&res->res, req, len); | ||||
|     memcpy(res->version, hw_version[nfc->gen - 1], len); | ||||
|     sg_res_init(&res->res, req, hw->length); | ||||
|     memcpy(res->version, hw->version, hw->length); | ||||
|  | ||||
|     return S_OK; | ||||
| } | ||||
|  | ||||
| @ -40,4 +40,5 @@ void cm_hook_config_load( | ||||
|     vfd_config_load(&cfg->vfd, filename); | ||||
|     touch_screen_config_load(&cfg->touch, filename); | ||||
|     cm_dll_config_load(&cfg->dll, filename); | ||||
|     unity_config_load(&cfg->unity, filename); | ||||
| } | ||||
|  | ||||
| @ -11,6 +11,8 @@ | ||||
|  | ||||
| #include "platform/config.h" | ||||
|  | ||||
| #include "unityhook/config.h" | ||||
|  | ||||
| struct cm_hook_config { | ||||
|     struct platform_config platform; | ||||
|     struct aime_config aime; | ||||
| @ -19,6 +21,7 @@ struct cm_hook_config { | ||||
|     struct vfd_config vfd; | ||||
|     struct cm_dll_config dll; | ||||
|     struct touch_screen_config touch; | ||||
|     struct unity_config unity; | ||||
| }; | ||||
|  | ||||
| void cm_dll_config_load( | ||||
|  | ||||
| @ -16,10 +16,11 @@ | ||||
| #include "cmhook/config.h" | ||||
| #include "cmhook/io4.h" | ||||
| #include "cmhook/cm-dll.h" | ||||
| #include "cmhook/unity.h" | ||||
|  | ||||
| #include "platform/platform.h" | ||||
|  | ||||
| #include "unityhook/hook.h" | ||||
|  | ||||
| #include "util/dprintf.h" | ||||
|  | ||||
| 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 | ||||
|        hooked earlier in the `cmhook` initialization. */ | ||||
|  | ||||
|     unity_hook_init(); | ||||
|     unity_hook_init(&cm_hook_cfg.unity, cm_hook_mod); | ||||
|  | ||||
|     /* Initialize debug helpers */ | ||||
|  | ||||
|  | ||||
| @ -16,6 +16,7 @@ shared_library( | ||||
|         hooklib_lib, | ||||
|         cmio_lib, | ||||
|         platform_lib, | ||||
|         unityhook_lib, | ||||
|         util_lib, | ||||
|     ], | ||||
|     sources : [ | ||||
| @ -26,7 +27,5 @@ shared_library( | ||||
|         'io4.h', | ||||
|         'cm-dll.c', | ||||
|         'cm-dll.h', | ||||
|         'unity.h', | ||||
|         'unity.c', | ||||
|     ], | ||||
| ) | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -1,3 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| void unity_hook_init(void); | ||||
| @ -23,22 +23,32 @@ void cxb_dll_config_load( | ||||
|         struct cxb_dll_config *cfg, | ||||
|         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) | ||||
| { | ||||
|     assert(cfg != NULL); | ||||
|     assert(filename != NULL); | ||||
|  | ||||
| } | ||||
|  | ||||
| void network_config_load(struct network_config *cfg, const wchar_t *filename) | ||||
| { | ||||
|  | ||||
|     cfg->enable = GetPrivateProfileIntW(L"revio", 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 cxb_hook_config_load( | ||||
| @ -56,6 +66,5 @@ void cxb_hook_config_load( | ||||
|     gfx_config_load(&cfg->gfx, filename); | ||||
|     cxb_dll_config_load(&cfg->dll, filename); | ||||
|     revio_config_load(&cfg->revio, filename); | ||||
|     network_config_load(&cfg->network, filename); | ||||
|     led_config_load(&cfg->led, filename); | ||||
| } | ||||
| @ -10,7 +10,6 @@ | ||||
| #include "cxbhook/cxb-dll.h" | ||||
| #include "cxbhook/revio.h" | ||||
| #include "cxbhook/led.h" | ||||
| #include "cxbhook/network.h" | ||||
|  | ||||
| #include "gfxhook/gfx.h" | ||||
|  | ||||
| @ -23,7 +22,6 @@ struct cxb_hook_config { | ||||
|     struct gfx_config gfx; | ||||
|     struct cxb_dll_config dll; | ||||
|     struct revio_config revio; | ||||
|     struct network_config network; | ||||
|     struct led_config led; | ||||
| }; | ||||
|  | ||||
| @ -32,7 +30,6 @@ void cxb_dll_config_load( | ||||
|         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 cxb_hook_config_load( | ||||
|  | ||||
| @ -9,7 +9,6 @@ | ||||
| #include "cxbhook/config.h" | ||||
| #include "cxbhook/revio.h" | ||||
| #include "cxbhook/led.h" | ||||
| #include "cxbhook/network.h" | ||||
|  | ||||
| #include "cxbio/cxbio.h" | ||||
|  | ||||
| @ -103,12 +102,6 @@ static DWORD CALLBACK cxb_pre_startup(void) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     hr = network_hook_init(&cxb_hook_cfg.network); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     hr = led_hook_init(&cxb_hook_cfg.led); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|  | ||||
| @ -49,7 +49,13 @@ static struct hook_symbol lamp_syms[] = { | ||||
|  | ||||
| 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)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -30,7 +30,5 @@ shared_library( | ||||
|         'revio.h', | ||||
|         'led.c', | ||||
|         'led.h', | ||||
|         'network.c', | ||||
|         'network.h', | ||||
|     ], | ||||
| ) | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -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); | ||||
| @ -82,7 +82,13 @@ static struct hook_symbol revio_syms[] = { | ||||
|  | ||||
| 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)); | ||||
| } | ||||
|  | ||||
| @ -154,7 +160,7 @@ static int my_cCommIo_GetTrigger() | ||||
|      | ||||
|     out &= ~last_triggers; | ||||
|  | ||||
|     dprintf("Revio: GetTrigger %X\n", out); | ||||
|     // dprintf("Revio: GetTrigger %X\n", out); | ||||
|     last_triggers = out; | ||||
|     return out; | ||||
| } | ||||
| @ -188,7 +194,7 @@ static int my_cCommIo_GetRelease() | ||||
|  | ||||
|     out &= ~btns; | ||||
|      | ||||
|     dprintf("Revio: GetRelease %X\n", out); | ||||
|     // dprintf("Revio: GetRelease %X\n", out); | ||||
|     last_triggers = btns; | ||||
|     return out; | ||||
| } | ||||
|  | ||||
							
								
								
									
										5
									
								
								dist/cm/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								dist/cm/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -68,6 +68,11 @@ dipsw1=0 | ||||
| ; Enable/Disable WinTouch emulation | ||||
| 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 | ||||
| ; ----------------------------------------------------------------------------- | ||||
|  | ||||
							
								
								
									
										11
									
								
								dist/cxb/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								dist/cxb/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -19,10 +19,10 @@ appdata= | ||||
|  | ||||
| [aime] | ||||
| ; Aime reader emulation | ||||
| ; CXB is stupid, so we have to make the paths go back one | ||||
| enable=1 | ||||
| ; CXB is stupid, so we have to make the paths go back two directories. This  | ||||
| ; will load the file from "resource\DEVICE\aime.txt".  | ||||
| aimePath=../DEVICE/aime.txt | ||||
| felicaPath=../DEVICE/felica.txt | ||||
|  | ||||
| [led] | ||||
| ; Emulation for the LED board. Currently it's just dummy responses, | ||||
| @ -39,6 +39,10 @@ enable=1 | ||||
| ; Note that 127.0.0.1, localhost etc are specifically rejected. | ||||
| default=127.0.0.1 | ||||
|  | ||||
| ; Set the title server hostname or IP address here, as the title server | ||||
| ; is hardcoded in the game. | ||||
| title=https://127.0.0.1:9002 | ||||
|  | ||||
| [netenv] | ||||
| ; Simulate an ideal LAN environment. This may interfere with head-to-head play. | ||||
| ; Crossbeats is extremely picky about its LAN environment, so leaving this | ||||
| @ -106,13 +110,14 @@ path= | ||||
|  | ||||
| [revio] | ||||
| ; Enable emulation of the rev IO board | ||||
| enabe=1 | ||||
| enable=1 | ||||
| ; Test button virtual-key code. Default is the F1 key. | ||||
| test=0x70 | ||||
| ; Service button virtual-key code. Default is the F2 key. | ||||
| service=0x71 | ||||
| ; Keyboard button to increment coin counter. Default is the F3 key. | ||||
| coin=0x72 | ||||
|  | ||||
| ; Menu up key. Default is up arrow. | ||||
| up=0x26 | ||||
| ; Menu down key. Default is down arrow. | ||||
|  | ||||
							
								
								
									
										16
									
								
								dist/fgo/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								dist/fgo/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -129,12 +129,12 @@ path= | ||||
|  | ||||
| [io4] | ||||
| ; Input API selection for JVS input emulator. | ||||
| ; Test button virtual-key code. Default is the 1 key. | ||||
| test=0x31 | ||||
| ; Service button virtual-key code. Default is the 2 key. | ||||
| service=0x32 | ||||
| ; Keyboard button to increment coin counter. Default is the 3 key. | ||||
| coin=0x33 | ||||
| ; Test button virtual-key code. Default is the F1 key. | ||||
| test=0x70 | ||||
| ; Service button virtual-key code. Default is the F2 key. | ||||
| service=0x71 | ||||
| ; Keyboard button to increment coin counter. Default is the F3 key. | ||||
| coin=0x72 | ||||
|  | ||||
| ;   .·:'''''''''''''''''''''''''''''''''''''''''''''':·. | ||||
| ;   : :  ______                            / \   []  : : | ||||
| @ -151,8 +151,8 @@ coin=0x33 | ||||
| ; | ||||
| ; Only XInput is currently supported. | ||||
|  | ||||
| ; Controller            Button | ||||
| ; ------------------------------------------------------- | ||||
| ; XInput bindings  | ||||
| ; | ||||
| ; Left Stick           Joystick | ||||
| ; Left Stick Click     Reset Camera | ||||
| ; Left Trigger         Dash | ||||
|  | ||||
							
								
								
									
										9
									
								
								dist/mai2/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								dist/mai2/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -83,6 +83,15 @@ path= | ||||
| ; Leave empty if you want to use Segatools built-in keyboard input. | ||||
| 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 | ||||
| ; ----------------------------------------------------------------------------- | ||||
|  | ||||
							
								
								
									
										3
									
								
								dist/mai2/start.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								dist/mai2/start.bat
									
									
									
									
										vendored
									
									
								
							| @ -3,7 +3,8 @@ | ||||
| pushd %~dp0 | ||||
|  | ||||
| start "AM Daemon" /min inject -d -k mai2hook.dll amdaemon.exe -f -c config_common.json config_server.json config_client.json | ||||
| inject -d -k mai2hook.dll sinmai -screen-fullscreen 0 | ||||
| inject -d -k mai2hook.dll sinmai -screen-fullscreen 0 -popupwindow -screen-width 2160 -screen-height 1920  -silent-crashes | ||||
|  | ||||
| taskkill /f /im amdaemon.exe > nul 2>&1 | ||||
|  | ||||
| echo. | ||||
|  | ||||
							
								
								
									
										7
									
								
								dist/mu3/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								dist/mu3/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -72,6 +72,11 @@ dipsw1=1 | ||||
| [gfx] | ||||
| 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 | ||||
| ; ----------------------------------------------------------------------------- | ||||
| @ -81,7 +86,7 @@ enable=1 | ||||
| ; Leave empty if you want to use Segatools built-in keyboard input. | ||||
| path= | ||||
|  | ||||
| [fgoio] | ||||
| [mu3io] | ||||
| ; To use a custom O.N.G.E.K.I. IO DLL enter its path here. | ||||
| ; Leave empty if you want to use Segatools built-in keyboard/gamepad input. | ||||
| path= | ||||
|  | ||||
							
								
								
									
										2
									
								
								dist/swdc/segatools.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/swdc/segatools.ini
									
									
									
									
										vendored
									
									
								
							| @ -63,7 +63,7 @@ enable=1 | ||||
| ; Enable freeplay mode. This will disable the coin slot and set the game to | ||||
| ; freeplay. Keep in mind that some game modes (e.g. Freedom/Time Modes) will not | ||||
| ; allow you to start a game in freeplay mode. | ||||
| freeplay=0´ | ||||
| freeplay=0 | ||||
|  | ||||
| ; ----------------------------------------------------------------------------- | ||||
| ; Custom IO settings | ||||
|  | ||||
| @ -153,6 +153,13 @@ setting. Also, loopback addresses are specifically checked for and rejected by | ||||
| the games themselves; this needs to be a LAN or WAN IP (or a hostname that | ||||
| resolves to one). | ||||
|  | ||||
| ### `title` | ||||
|  | ||||
| Default: `title` | ||||
|  | ||||
| Leave it as `title` to use the title server returned by ALL.Net. Rewrites  | ||||
| the title server hostname for certain games, such as crossbeats REV. | ||||
|  | ||||
| ### `router` | ||||
|  | ||||
| Default: Empty string (i.e. use value from `default` setting) | ||||
| @ -388,13 +395,29 @@ Bit values are: | ||||
| - 3: EXP: Export (for Asian markets) | ||||
| - 4: CHS: China (Simplified Chinese?) | ||||
|  | ||||
| ### `billingCa` | ||||
|  | ||||
| Default: `DEVICE\\ca.crt` | ||||
|  | ||||
| Set the billing certificate path. This has to match the one used for the  | ||||
| SSL billing server. The DER certificate must fit in 1024 bytes so it must be | ||||
| small. | ||||
|  | ||||
| ### `billingPub` | ||||
|  | ||||
| Default: `DEVICE\\billing.pub` | ||||
|  | ||||
| Set the actual keychip RSA public key path. This public key has to match the | ||||
| private key `billing.key` of the billing server in order to decrypt/encrypt | ||||
| the billing transactions. | ||||
|  | ||||
| ### `billingType` | ||||
|  | ||||
| Default: `1` | ||||
|  | ||||
| Set the billing "type" for the keychip. The type determins what kind of revenue share, | ||||
| if any, the game maker has with SEGA. Some games may be picky and require types other  | ||||
| then 1 (ex. Crossbeats requires billing type 2), so this option is provided if this | ||||
| then 1 (ex. crossbeats REV. requires billing type 2), so this option is provided if this | ||||
| is an issue. Billing types are: | ||||
|  | ||||
| - 0: No billing? | ||||
|  | ||||
							
								
								
									
										127
									
								
								hooklib/dns.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								hooklib/dns.c
									
									
									
									
									
								
							| @ -3,6 +3,7 @@ | ||||
| #include <windows.h> | ||||
| #include <windns.h> | ||||
| #include <ws2tcpip.h> | ||||
| #include <winhttp.h> | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdbool.h> | ||||
| @ -12,6 +13,8 @@ | ||||
| #include "hook/hr.h" | ||||
| #include "hook/table.h" | ||||
|  | ||||
| #include "util/dprintf.h" | ||||
|  | ||||
| #include "hooklib/dns.h" | ||||
|  | ||||
| /* Latest w32headers does not include DnsQueryEx, so we'll have to "polyfill" | ||||
| @ -66,6 +69,18 @@ static int WSAAPI hook_getaddrinfo( | ||||
|         const ADDRINFOA *pHints, | ||||
|         ADDRINFOA **ppResult); | ||||
|  | ||||
| static HINTERNET WINAPI hook_WinHttpConnect( | ||||
|         HINTERNET hSession, | ||||
|         const wchar_t *pwszServerName, | ||||
|         INTERNET_PORT nServerPort, | ||||
|         DWORD dwReserved); | ||||
|  | ||||
| static bool WINAPI hook_WinHttpCrackUrl( | ||||
|         const wchar_t *pwszUrl, | ||||
|         DWORD dwUrlLength, | ||||
|         DWORD dwFlags, | ||||
|         LPURL_COMPONENTS lpUrlComponents); | ||||
|  | ||||
| /* Link pointers */ | ||||
|  | ||||
| static DNS_STATUS (WINAPI *next_DnsQuery_A)( | ||||
| @ -95,6 +110,18 @@ static int (WSAAPI *next_getaddrinfo)( | ||||
|         const ADDRINFOA *pHints, | ||||
|         ADDRINFOA **ppResult); | ||||
|  | ||||
| static HINTERNET (WINAPI *next_WinHttpConnect)( | ||||
|         HINTERNET hSession, | ||||
|         const wchar_t *pwszServerName, | ||||
|         INTERNET_PORT nServerPort, | ||||
|         DWORD dwReserved); | ||||
|  | ||||
| static bool (WINAPI *next_WinHttpCrackUrl)( | ||||
|         const wchar_t *pwszUrl, | ||||
|         DWORD dwUrlLength, | ||||
|         DWORD dwFlags, | ||||
|         LPURL_COMPONENTS lpUrlComponents); | ||||
|  | ||||
| static const struct hook_symbol dns_hook_syms_dnsapi[] = { | ||||
|     { | ||||
|         .name       = "DnsQuery_A", | ||||
| @ -120,10 +147,24 @@ static const struct hook_symbol dns_hook_syms_ws2[] = { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static const struct hook_symbol dns_hook_syms_winhttp[] = { | ||||
|     { | ||||
|         .name       = "WinHttpConnect", | ||||
|         .patch      = hook_WinHttpConnect, | ||||
|         .link       = (void **) &next_WinHttpConnect, | ||||
|     }, { | ||||
|         .name       = "WinHttpCrackUrl", | ||||
|         .patch      = hook_WinHttpCrackUrl, | ||||
|         .link       = (void **) &next_WinHttpCrackUrl, | ||||
|     } | ||||
|  | ||||
| }; | ||||
|  | ||||
| static bool dns_hook_initted; | ||||
| static CRITICAL_SECTION dns_hook_lock; | ||||
| static struct dns_hook_entry *dns_hook_entries; | ||||
| static size_t dns_hook_nentries; | ||||
| static char received_title_url[255]; | ||||
|  | ||||
| static void dns_hook_init(void) | ||||
| { | ||||
| @ -145,6 +186,12 @@ static void dns_hook_init(void) | ||||
|             "ws2_32.dll", | ||||
|             dns_hook_syms_ws2, | ||||
|             _countof(dns_hook_syms_ws2)); | ||||
|      | ||||
|     hook_table_apply( | ||||
|             NULL, | ||||
|             "winhttp.dll", | ||||
|             dns_hook_syms_winhttp, | ||||
|             _countof(dns_hook_syms_winhttp)); | ||||
| } | ||||
|  | ||||
| HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src) | ||||
| @ -460,3 +507,83 @@ end: | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| static HINTERNET WINAPI hook_WinHttpConnect( | ||||
|         HINTERNET hSession, | ||||
|         const wchar_t *pwszServerName, | ||||
|         INTERNET_PORT nServerPort, | ||||
|         DWORD dwReserved) | ||||
| { | ||||
|     const struct dns_hook_entry *pos; | ||||
|     size_t i; | ||||
|  | ||||
|     if (pwszServerName == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     EnterCriticalSection(&dns_hook_lock); | ||||
|  | ||||
|     for (i = 0 ; i < dns_hook_nentries ; i++) { | ||||
|         pos = &dns_hook_entries[i]; | ||||
|  | ||||
|         if (_wcsicmp(pwszServerName, pos->from) == 0) { | ||||
|             if(pos->to == NULL) { | ||||
|                 LeaveCriticalSection(&dns_hook_lock); | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             pwszServerName = pos->to; | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LeaveCriticalSection(&dns_hook_lock); | ||||
|  | ||||
|     return next_WinHttpConnect(hSession, pwszServerName, nServerPort, dwReserved); | ||||
| } | ||||
|  | ||||
| // Hook to replace CXB title url | ||||
| static bool WINAPI hook_WinHttpCrackUrl( | ||||
|         const wchar_t *pwszUrl, | ||||
|         DWORD dwUrlLength, | ||||
|         DWORD dwFlags, | ||||
|         LPURL_COMPONENTS lpUrlComponents) | ||||
| { | ||||
|     const struct dns_hook_entry *pos; | ||||
|     size_t i; | ||||
|  | ||||
|     EnterCriticalSection(&dns_hook_lock); | ||||
|  | ||||
|     for (i = 0 ; i < dns_hook_nentries ; i++) { | ||||
|         pos = &dns_hook_entries[i]; | ||||
|  | ||||
|         if (_wcsicmp(pwszUrl, pos->from) == 0) { | ||||
|             wchar_t* toAddr = pos->to; | ||||
|             wchar_t titleBuffer[255]; | ||||
|              | ||||
|             if(wcscmp(toAddr, L"title") == 0) { | ||||
|                 size_t wstr_c; | ||||
|                 mbstowcs_s(&wstr_c, titleBuffer, 255, received_title_url, strlen(received_title_url)); | ||||
|                 toAddr = titleBuffer; | ||||
|             } | ||||
|  | ||||
|             bool result = next_WinHttpCrackUrl( | ||||
|                 toAddr, | ||||
|                 wcslen(toAddr), | ||||
|                 dwFlags, | ||||
|                 lpUrlComponents | ||||
|             ); | ||||
|             LeaveCriticalSection(&dns_hook_lock); | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     LeaveCriticalSection(&dns_hook_lock); | ||||
|     return next_WinHttpCrackUrl( | ||||
|         pwszUrl, | ||||
|         dwUrlLength, | ||||
|         dwFlags, | ||||
|         lpUrlComponents | ||||
|     ); | ||||
| } | ||||
|  | ||||
							
								
								
									
										305
									
								
								hooklib/path.c
									
									
									
									
									
								
							
							
						
						
									
										305
									
								
								hooklib/path.c
									
									
									
									
									
								
							| @ -101,6 +101,40 @@ static BOOL WINAPI hook_PathFileExistsA(LPCSTR pszPath); | ||||
|  | ||||
| static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath); | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileA( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName); | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileW( | ||||
|         const wchar_t *lpExistingFileName, | ||||
|         const wchar_t *lpNewFileName); | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileExA( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName, | ||||
|         uint32_t dwFlags); | ||||
|  | ||||
|  | ||||
| static BOOL WINAPI hook_ReplaceFileA( | ||||
|         const char *lpReplacedFileName, | ||||
|         const char *lpReplacementFileName, | ||||
|         const char *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved); | ||||
|  | ||||
| static BOOL WINAPI hook_ReplaceFileW( | ||||
|         const wchar_t *lpReplacedFileName, | ||||
|         const wchar_t *lpReplacementFileName, | ||||
|         const wchar_t *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved); | ||||
|  | ||||
| static BOOL WINAPI hook_DeleteFileA(const char *lpFileName); | ||||
|  | ||||
| static BOOL WINAPI hook_DeleteFileW(const wchar_t *lpFileName); | ||||
|  | ||||
| /* Link pointers */ | ||||
|  | ||||
| static BOOL (WINAPI *next_CreateDirectoryA)( | ||||
| @ -185,6 +219,39 @@ static BOOL (WINAPI *next_PathFileExistsA)(LPCSTR pszPath); | ||||
|  | ||||
| static BOOL (WINAPI *next_PathFileExistsW)(LPCWSTR pszPath); | ||||
|  | ||||
| static BOOL (WINAPI *next_MoveFileA)( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName); | ||||
|  | ||||
| static BOOL (WINAPI *next_MoveFileW)( | ||||
|         const wchar_t *lpExistingFileName, | ||||
|         const wchar_t *lpNewFileName); | ||||
|  | ||||
| static BOOL (WINAPI *next_MoveFileExA)( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName, | ||||
|         uint32_t dwFlags); | ||||
|  | ||||
| static BOOL (WINAPI *next_ReplaceFileA)( | ||||
|         const char *lpReplacedFileName, | ||||
|         const char *lpReplacementFileName, | ||||
|         const char *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved); | ||||
|  | ||||
| static BOOL (WINAPI *next_ReplaceFileW)( | ||||
|         const wchar_t *lpReplacedFileName, | ||||
|         const wchar_t *lpReplacementFileName, | ||||
|         const wchar_t *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved); | ||||
|  | ||||
| static BOOL (WINAPI *next_DeleteFileA)(const char *lpFileName); | ||||
|  | ||||
| static BOOL (WINAPI *next_DeleteFileW)(const wchar_t *lpFileName); | ||||
|  | ||||
| /* Hook table */ | ||||
|  | ||||
| static const struct hook_symbol path_hook_syms[] = { | ||||
| @ -260,6 +327,34 @@ static const struct hook_symbol path_hook_syms[] = { | ||||
|         .name   = "PathFileExistsW", | ||||
|         .patch  = hook_PathFileExistsW, | ||||
|         .link   = (void **) &next_PathFileExistsW, | ||||
|     }, { | ||||
|         .name   = "MoveFileA", | ||||
|         .patch  = hook_MoveFileA, | ||||
|         .link   = (void **) &next_MoveFileA, | ||||
|     }, { | ||||
|         .name   = "MoveFileW", | ||||
|         .patch  = hook_MoveFileW, | ||||
|         .link   = (void **) &next_MoveFileW, | ||||
|     }, { | ||||
|         .name   = "MoveFileExA", | ||||
|         .patch  = hook_MoveFileExA, | ||||
|         .link   = (void **) &next_MoveFileExA, | ||||
|     }, { | ||||
|         .name   = "ReplaceFileA", | ||||
|         .patch  = hook_ReplaceFileA, | ||||
|         .link   = (void **) &next_ReplaceFileA, | ||||
|     }, { | ||||
|         .name   = "ReplaceFileW", | ||||
|         .patch  = hook_ReplaceFileW, | ||||
|         .link   = (void **) &next_ReplaceFileW, | ||||
|     }, { | ||||
|         .name   = "DeleteFileA", | ||||
|         .patch  = hook_DeleteFileA, | ||||
|         .link   = (void **) &next_DeleteFileA, | ||||
|     }, { | ||||
|         .name   = "DeleteFileW", | ||||
|         .patch  = hook_DeleteFileW, | ||||
|         .link   = (void **) &next_DeleteFileW, | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @ -906,3 +1001,213 @@ static BOOL WINAPI hook_PathFileExistsW(LPCWSTR pszPath) | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileA( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName) | ||||
| { | ||||
|     char *oldTrans; | ||||
|     char *newTrans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_a(&oldTrans, lpExistingFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = path_transform_a(&newTrans, lpNewFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         free(oldTrans); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_MoveFileA( | ||||
|         oldTrans ? oldTrans : lpExistingFileName, | ||||
|         newTrans ? newTrans : lpNewFileName); | ||||
|      | ||||
|     free(oldTrans); | ||||
|     free(newTrans); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileW( | ||||
|         const wchar_t *lpExistingFileName, | ||||
|         const wchar_t *lpNewFileName) | ||||
| { | ||||
|     wchar_t *oldTrans; | ||||
|     wchar_t *newTrans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_w(&oldTrans, lpExistingFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = path_transform_w(&newTrans, lpNewFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         free(oldTrans); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_MoveFileW( | ||||
|         oldTrans ? oldTrans : lpExistingFileName, | ||||
|         newTrans ? newTrans : lpNewFileName); | ||||
|      | ||||
|     free(oldTrans); | ||||
|     free(newTrans); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_MoveFileExA( | ||||
|         const char *lpExistingFileName, | ||||
|         const char *lpNewFileName, | ||||
|         uint32_t dwFlags) | ||||
| { | ||||
|     char *oldTrans; | ||||
|     char *newTrans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_a(&oldTrans, lpExistingFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = path_transform_a(&newTrans, lpNewFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         free(oldTrans); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_MoveFileExA( | ||||
|         oldTrans ? oldTrans : lpExistingFileName, | ||||
|         newTrans ? newTrans : lpNewFileName, | ||||
|         dwFlags); | ||||
|      | ||||
|     free(oldTrans); | ||||
|     free(newTrans); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_ReplaceFileA( | ||||
|         const char *lpReplacedFileName, | ||||
|         const char *lpReplacementFileName, | ||||
|         const char *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved) | ||||
| { | ||||
|     char *oldTrans; | ||||
|     char *newTrans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_a(&oldTrans, lpReplacedFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = path_transform_a(&newTrans, lpReplacementFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         free(oldTrans); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_ReplaceFileA( | ||||
|         oldTrans ? oldTrans : lpReplacedFileName, | ||||
|         newTrans ? newTrans : lpReplacementFileName, | ||||
|         lpBackupFileName, | ||||
|         dwReplaceFlags, | ||||
|         lpExclude, | ||||
|         lpReserved); | ||||
|  | ||||
|     free(oldTrans); | ||||
|     free(newTrans); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_ReplaceFileW( | ||||
|         const wchar_t *lpReplacedFileName, | ||||
|         const wchar_t *lpReplacementFileName, | ||||
|         const wchar_t *lpBackupFileName, | ||||
|         uint32_t dwReplaceFlags, | ||||
|         void *lpExclude, | ||||
|         void *lpReserved) | ||||
| { | ||||
|     wchar_t *oldTrans; | ||||
|     wchar_t *newTrans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_w(&oldTrans, lpReplacedFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = path_transform_w(&newTrans, lpReplacementFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         free(oldTrans); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_ReplaceFileW( | ||||
|         oldTrans ? oldTrans : lpReplacedFileName, | ||||
|         newTrans ? newTrans : lpReplacementFileName, | ||||
|         lpBackupFileName, | ||||
|         dwReplaceFlags, | ||||
|         lpExclude, | ||||
|         lpReserved); | ||||
|  | ||||
|     free(oldTrans); | ||||
|     free(newTrans); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_DeleteFileA(const char *lpFileName)  | ||||
| { | ||||
|     char *trans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_a(&trans, lpFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_DeleteFileA(trans ? trans: lpFileName); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| static BOOL WINAPI hook_DeleteFileW(const wchar_t *lpFileName) | ||||
| { | ||||
|     wchar_t *trans; | ||||
|     BOOL ok; | ||||
|  | ||||
|     ok = path_transform_w(&trans, lpFileName); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     ok = next_DeleteFileW(trans ? trans: lpFileName); | ||||
|  | ||||
|     return ok; | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,6 @@ EXPORTS | ||||
|     amDllVideoSetResolution @3 | ||||
|     idac_io_get_api_version | ||||
|     idac_io_init | ||||
|     idac_io_poll | ||||
|     idac_io_get_opbtns | ||||
|     idac_io_get_gamebtns | ||||
|     idac_io_get_shifter | ||||
|  | ||||
| @ -52,10 +52,6 @@ HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_back | ||||
|     return S_OK; | ||||
| } | ||||
|  | ||||
| HRESULT idac_io_poll(void) { | ||||
|     return S_OK; | ||||
| } | ||||
|  | ||||
| static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg) { | ||||
|     /* Deadzones check */ | ||||
|     if (cfg->left_stick_deadzone > 32767 || cfg->left_stick_deadzone < 0) { | ||||
|  | ||||
| @ -39,4 +39,5 @@ void mai2_hook_config_load( | ||||
|     io4_config_load(&cfg->io4, filename); | ||||
|     vfd_config_load(&cfg->vfd, filename); | ||||
|     mai2_dll_config_load(&cfg->dll, filename); | ||||
|     unity_config_load(&cfg->unity, filename); | ||||
| } | ||||
|  | ||||
| @ -10,6 +10,8 @@ | ||||
|  | ||||
| #include "platform/config.h" | ||||
|  | ||||
| #include "unityhook/config.h" | ||||
|  | ||||
| struct mai2_hook_config { | ||||
|     struct platform_config platform; | ||||
|     struct aime_config aime; | ||||
| @ -17,6 +19,7 @@ struct mai2_hook_config { | ||||
|     struct io4_config io4; | ||||
|     struct vfd_config vfd; | ||||
|     struct mai2_dll_config dll; | ||||
|     struct unity_config unity; | ||||
| }; | ||||
|  | ||||
| void mai2_dll_config_load( | ||||
|  | ||||
| @ -12,10 +12,11 @@ | ||||
| #include "mai2hook/config.h" | ||||
| #include "mai2hook/io4.h" | ||||
| #include "mai2hook/mai2-dll.h" | ||||
| #include "mai2hook/unity.h" | ||||
|  | ||||
| #include "platform/platform.h" | ||||
|  | ||||
| #include "unityhook/hook.h" | ||||
|  | ||||
| #include "util/dprintf.h" | ||||
|  | ||||
| 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 | ||||
|        hooked earlier in the `mai2hook` initialization. */ | ||||
|  | ||||
|     unity_hook_init(); | ||||
|     unity_hook_init(&mai2_hook_cfg.unity, mai2_hook_mod); | ||||
|  | ||||
|     /* Initialize debug helpers */ | ||||
|  | ||||
|  | ||||
| @ -15,6 +15,7 @@ shared_library( | ||||
|         hooklib_lib, | ||||
|         mai2io_lib, | ||||
|         platform_lib, | ||||
|         unityhook_lib, | ||||
|         util_lib, | ||||
|     ], | ||||
|     sources : [ | ||||
| @ -25,7 +26,5 @@ shared_library( | ||||
|         'io4.h', | ||||
|         'mai2-dll.c', | ||||
|         'mai2-dll.h', | ||||
|         'unity.h', | ||||
|         'unity.c', | ||||
|     ], | ||||
| ) | ||||
|  | ||||
| @ -1,3 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| void unity_hook_init(void); | ||||
| @ -42,6 +42,7 @@ shlwapi_lib = cc.find_library('shlwapi') | ||||
| dinput8_lib = cc.find_library('dinput8') | ||||
| dxguid_lib = cc.find_library('dxguid') | ||||
| xinput_lib = cc.find_library('xinput') | ||||
| pathcch_lib = cc.find_library('pathcch') | ||||
|  | ||||
| inc = include_directories('.') | ||||
| capnhook = subproject('capnhook') | ||||
| @ -55,6 +56,7 @@ subdir('platform') | ||||
| subdir('util') | ||||
|  | ||||
| subdir('gfxhook') | ||||
| subdir('unityhook') | ||||
|  | ||||
| subdir('aimeio') | ||||
| subdir('chuniio') | ||||
|  | ||||
| @ -42,4 +42,5 @@ void mu3_hook_config_load( | ||||
|     gfx_config_load(&cfg->gfx, filename); | ||||
|     vfd_config_load(&cfg->vfd, filename); | ||||
|     mu3_dll_config_load(&cfg->dll, filename); | ||||
|     unity_config_load(&cfg->unity, filename); | ||||
| } | ||||
|  | ||||
| @ -13,6 +13,8 @@ | ||||
|  | ||||
| #include "platform/config.h" | ||||
|  | ||||
| #include "unityhook/config.h" | ||||
|  | ||||
| struct mu3_hook_config { | ||||
|     struct platform_config platform; | ||||
|     struct aime_config aime; | ||||
| @ -22,6 +24,7 @@ struct mu3_hook_config { | ||||
|     // struct led15093_config led15093; | ||||
|     struct vfd_config vfd; | ||||
|     struct mu3_dll_config dll; | ||||
|     struct unity_config unity; | ||||
| }; | ||||
|  | ||||
| void mu3_dll_config_load( | ||||
|  | ||||
| @ -2,7 +2,6 @@ | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "board/io4.h" | ||||
| #include "board/sg-reader.h" | ||||
| #include "board/vfd.h" | ||||
|  | ||||
| @ -20,10 +19,12 @@ | ||||
| #include "mu3hook/config.h" | ||||
| #include "mu3hook/io4.h" | ||||
| #include "mu3hook/mu3-dll.h" | ||||
| #include "mu3hook/unity.h" | ||||
|  | ||||
| #include "platform/platform.h" | ||||
|  | ||||
| #include "unityhook/config.h" | ||||
| #include "unityhook/hook.h" | ||||
|  | ||||
| #include "util/dprintf.h" | ||||
|  | ||||
| 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 | ||||
|        hooked earlier in the `mu3hook` initialization. */ | ||||
|  | ||||
|     unity_hook_init(); | ||||
|     unity_hook_init(&mu3_hook_cfg.unity, mu3_hook_mod); | ||||
|  | ||||
|     /* Initialize debug helpers */ | ||||
|  | ||||
|  | ||||
| @ -17,6 +17,7 @@ shared_library( | ||||
|         hooklib_lib, | ||||
|         mu3io_lib, | ||||
|         platform_lib, | ||||
|         unityhook_lib, | ||||
|         util_lib, | ||||
|     ], | ||||
|     sources : [ | ||||
| @ -27,7 +28,5 @@ shared_library( | ||||
|         'io4.h', | ||||
|         'mu3-dll.c', | ||||
|         'mu3-dll.h', | ||||
|         'unity.h', | ||||
|         'unity.c', | ||||
|     ], | ||||
| ) | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -1,3 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| void unity_hook_init(void); | ||||
| @ -113,6 +113,14 @@ void dns_config_load(struct dns_config *cfg, const wchar_t *filename) | ||||
|             cfg->aimedb, | ||||
|             _countof(cfg->aimedb), | ||||
|             filename); | ||||
|  | ||||
|     GetPrivateProfileStringW( | ||||
|             L"dns", | ||||
|             L"title", | ||||
|             L"title", | ||||
|             cfg->title, | ||||
|             _countof(cfg->title), | ||||
|             filename); | ||||
| } | ||||
|  | ||||
| void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename) | ||||
|  | ||||
| @ -82,6 +82,33 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg) | ||||
|         return hr; | ||||
|     } | ||||
|  | ||||
|     // crossbeats REV. | ||||
|     hr = dns_hook_push(L"https://rev-ent.ac.capcom.jp:443", cfg->title); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|         return hr; | ||||
|     } | ||||
|  | ||||
|     // AimePay | ||||
|     hr = dns_hook_push(L"api-aime.am-all.net", cfg->startup); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|         return hr; | ||||
|     } | ||||
|  | ||||
|     // E-MONEY | ||||
|     hr = dns_hook_push(L"tasms-api-basis.thincacloud.com", cfg->startup); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|         return hr; | ||||
|     } | ||||
|  | ||||
|     hr = dns_hook_push(L"shop.tfps.thincacloud.com", cfg->startup); | ||||
|  | ||||
|     if (FAILED(hr)) { | ||||
|         return hr; | ||||
|     } | ||||
|  | ||||
|     // if your ISP resolves bad domains, it will kill the network. These 2 | ||||
|     // *cannot* resolve | ||||
|  | ||||
|  | ||||
| @ -11,6 +11,7 @@ struct dns_config { | ||||
|     wchar_t startup[128]; | ||||
|     wchar_t billing[128]; | ||||
|     wchar_t aimedb[128]; | ||||
|     wchar_t title[128]; | ||||
| }; | ||||
|  | ||||
| HRESULT dns_platform_hook_init(const struct dns_config *cfg); | ||||
|  | ||||
| @ -14,22 +14,22 @@ | ||||
| #include "util/str.h" | ||||
|  | ||||
| enum { | ||||
|     NUSEC_IOCTL_PING                    = 0x22A114, | ||||
|     NUSEC_IOCTL_ERASE_TRACE_LOG         = 0x22E188, | ||||
|     NUSEC_IOCTL_TD_ERASE_USED           = 0x22E18C, | ||||
|     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 { | ||||
|  | ||||
| @ -277,11 +277,12 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) | ||||
|     const wchar_t *redir; | ||||
|     size_t required; | ||||
|     size_t redir_len; | ||||
|     size_t src_len; | ||||
|  | ||||
|     assert(src != NULL); | ||||
|     assert(count != NULL); | ||||
|  | ||||
|     if (src[0] == L'\0' || src[1] != L':' || !path_is_separator_w(src[2])) { | ||||
|     if (src[0] == L'\0' || src[1] != L':') { | ||||
|         return S_FALSE; | ||||
|     } | ||||
|  | ||||
| @ -304,10 +305,15 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) | ||||
|         return S_FALSE; | ||||
|     } | ||||
|  | ||||
|     /* GetFileAttributesW would request the src "E:", so fix the src_len in | ||||
|        in order to redirect the drive letter successfully */ | ||||
|  | ||||
|     src_len = path_is_separator_w(src[2]) ? 3 : 2; | ||||
|  | ||||
|     /* Cut off <prefix>\, replace with redir path, count NUL terminator */ | ||||
|  | ||||
|     redir_len = wcslen(redir); | ||||
|     required = wcslen(src) - 3 + redir_len + 1; | ||||
|     required = wcslen(src) - src_len + redir_len + 1; | ||||
|  | ||||
|     if (dest != NULL) { | ||||
|         if (required > *count) { | ||||
| @ -315,7 +321,7 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) | ||||
|         } | ||||
|  | ||||
|         wcscpy_s(dest, *count, redir); | ||||
|         wcscpy_s(dest + redir_len, *count - redir_len, src + 3); | ||||
|         wcscpy_s(dest + redir_len, *count - redir_len, src + src_len); | ||||
|     } | ||||
|  | ||||
|     *count = required; | ||||
|  | ||||
							
								
								
									
										14
									
								
								unityhook/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								unityhook/config.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										12
									
								
								unityhook/config.h
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										174
									
								
								unityhook/doorstop.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								unityhook/doorstop.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | ||||
| // 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 config_path[MAX_PATH]; | ||||
|         size_t config_path_len = GetModuleFileNameW(NULL, config_path, MAX_PATH); | ||||
|         wchar_t *folder_name = wcsdup(config_path); | ||||
|  | ||||
|         PathCchRemoveFileSpec(folder_name, config_path_len + 1); | ||||
|         wmemcpy(config_path + config_path_len, CONFIG_EXT, sizeof(CONFIG_EXT) / sizeof(CONFIG_EXT[0])); | ||||
|  | ||||
|         char *config_path_n = narrow(config_path); | ||||
|         char *folder_name_n = narrow(folder_name); | ||||
|  | ||||
|         dprintf("Unity: Setting config paths: base dir: %s; config path: %s\n", folder_name_n, config_path_n); | ||||
|  | ||||
|         mono_domain_set_config(domain, folder_name_n, config_path_n); | ||||
|  | ||||
|         free(folder_name); | ||||
|         free(config_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
									
								
							
							
						
						
									
										5
									
								
								unityhook/doorstop.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| void doorstop_mono_hook_init(const struct unity_config *cfg, HINSTANCE module); | ||||
| @ -1,14 +1,23 @@ | ||||
| #include <assert.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include <windows.h> | ||||
| 
 | ||||
| #include "hook/table.h" | ||||
| 
 | ||||
| #include "hooklib/dll.h" | ||||
| #include "hooklib/path.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 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[] = { | ||||
|     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(const struct unity_config *cfg, HINSTANCE self) { | ||||
|     assert(cfg != NULL); | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|     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( | ||||
|             target, | ||||
|             "kernel32.dll", | ||||
|             unity_kernel32_syms, | ||||
|             _countof(unity_kernel32_syms)); | ||||
|         target, | ||||
|         "kernel32.dll", | ||||
|         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 *target_module; | ||||
|     bool already_loaded; | ||||
| @ -66,6 +80,11 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) | ||||
|     if (!already_loaded && result != NULL) { | ||||
|         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++) { | ||||
|             target_module = target_modules[i]; | ||||
|             target_module_len = wcslen(target_module); | ||||
							
								
								
									
										7
									
								
								unityhook/hook.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								unityhook/hook.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										20
									
								
								unityhook/meson.build
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										100
									
								
								unityhook/mono.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										20
									
								
								unityhook/util.h
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	