From 40e2b037662636e09ae79e4515c9aad9ff6eb835 Mon Sep 17 00:00:00 2001 From: James Liu Date: Fri, 8 Nov 2019 10:54:59 +0800 Subject: [PATCH 001/175] Update start.bat make it convenient for everyone to run it as Administrator --- dist/chuni/start.bat | 2 ++ dist/idz/start.bat | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dist/chuni/start.bat b/dist/chuni/start.bat index 7f99eb8..0c942e9 100644 --- a/dist/chuni/start.bat +++ b/dist/chuni/start.bat @@ -1,5 +1,7 @@ @echo off +pushd %~dp0 + start /min inject -d -k chunihook.dll aimeReaderHost.exe -p 12 inject -d -k chunihook.dll chuniApp.exe taskkill /f /im aimeReaderHost.exe > nul 2>&1 diff --git a/dist/idz/start.bat b/dist/idz/start.bat index b62983d..b8d6b18 100644 --- a/dist/idz/start.bat +++ b/dist/idz/start.bat @@ -1,5 +1,7 @@ @echo off +pushd %~dp0 + .\inject.exe -k .\idzhook.dll .\InitialD0_DX11_Nu.exe .\inject.exe -d -k .\idzhook.dll .\amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json From 1cf9e1a1b0c56080d31149727276453fe185f482 Mon Sep 17 00:00:00 2001 From: Shiz Date: Fri, 8 Nov 2019 02:03:55 +0100 Subject: [PATCH 002/175] build: don't hardcode cross-toolchain path It doesn't seem to be needed, and this makes the build work for environments where MinGW-w64 cross-toolchains get installed someplace else (e.g. macOS with Homebrew to /usr/local). --- cross-mingw-32.txt | 6 +++--- cross-mingw-64.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cross-mingw-32.txt b/cross-mingw-32.txt index c2ae6d4..21ed951 100644 --- a/cross-mingw-32.txt +++ b/cross-mingw-32.txt @@ -1,7 +1,7 @@ [binaries] -c = '/usr/bin/i686-w64-mingw32-gcc' -ar = '/usr/bin/i686-w64-mingw32-ar' -strip = '/usr/bin/i686-w64-mingw32-strip' +c = 'i686-w64-mingw32-gcc' +ar = 'i686-w64-mingw32-ar' +strip = 'i686-w64-mingw32-strip' [host_machine] system = 'windows' diff --git a/cross-mingw-64.txt b/cross-mingw-64.txt index a385ae9..6b15798 100644 --- a/cross-mingw-64.txt +++ b/cross-mingw-64.txt @@ -1,7 +1,7 @@ [binaries] -c = '/usr/bin/x86_64-w64-mingw32-gcc' -ar = '/usr/bin/x86_64-w64-mingw32-ar' -strip = '/usr/bin/x86_64-w64-mingw32-strip' +c = 'x86_64-w64-mingw32-gcc' +ar = 'x86_64-w64-mingw32-ar' +strip = 'x86_64-w64-mingw32-strip' [host_machine] system = 'windows' From 33621dd1e80a05f29f61b205c82de3342c6d486c Mon Sep 17 00:00:00 2001 From: Sam Sun Date: Sat, 9 Nov 2019 00:18:28 -0700 Subject: [PATCH 003/175] Fix build on msbuild --- hooklib/setupapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hooklib/setupapi.c b/hooklib/setupapi.c index dac66ba..eb50323 100644 --- a/hooklib/setupapi.c +++ b/hooklib/setupapi.c @@ -126,6 +126,7 @@ HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path) class_ = &setupapi_classes[setupapi_nclasses++]; class_->guid = iface_class; class_->path = path; + hr = S_OK; end: LeaveCriticalSection(&setupapi_lock); From 5ab159c972eeef67fa42c1f8a647463c1b48324a Mon Sep 17 00:00:00 2001 From: Will Toohey Date: Sun, 17 Nov 2019 14:30:17 +1000 Subject: [PATCH 004/175] Fix compile due to undefined CERT_NAME_BLOB mingw-w64 bug 523 means that compile fails with missing type CERT_NAME_BLOB Pulling in wincrypt.h solves this --- platform/netenv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/netenv.c b/platform/netenv.c index 6126aa9..37a9eae 100644 --- a/platform/netenv.c +++ b/platform/netenv.c @@ -1,4 +1,5 @@ #include +#include #include #include From e15a61a7d04524243c92454f96b785e849d3d49b Mon Sep 17 00:00:00 2001 From: Will Toohey Date: Sun, 17 Nov 2019 15:08:25 +1000 Subject: [PATCH 005/175] Allow dns hook to fail a resolve and add domains This lets people with crap ISPs that resolve domains that they shouldn't to use the game without changing their DNS server --- hooklib/dns.c | 35 ++++++++++++++++++++++++++++++----- hooklib/dns.h | 1 + platform/dns.c | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/hooklib/dns.c b/hooklib/dns.c index 04699d3..aac90ce 100644 --- a/hooklib/dns.c +++ b/hooklib/dns.c @@ -156,7 +156,6 @@ HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src) wchar_t *to; assert(from_src != NULL); - assert(to_src != NULL); to = NULL; from = NULL; @@ -172,12 +171,14 @@ HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src) goto end; } - to = _wcsdup(to_src); + if(to_src != NULL) { + to = _wcsdup(to_src); - if (to == NULL) { - hr = E_OUTOFMEMORY; + if (to == NULL) { + hr = E_OUTOFMEMORY; - goto end; + goto end; + } } newmem = realloc( @@ -250,6 +251,13 @@ static DNS_STATUS WINAPI hook_DnsQuery_A( pos = &dns_hook_entries[i]; if (_wcsicmp(wstr, pos->from) == 0) { + if(pos->to == NULL) { + LeaveCriticalSection(&dns_hook_lock); + hr = HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR); + + goto end; + } + wcstombs_s(&str_c, NULL, 0, pos->to, 0); str = malloc(str_c * sizeof(char)); @@ -307,6 +315,11 @@ static DNS_STATUS WINAPI hook_DnsQuery_W( pos = &dns_hook_entries[i]; if (_wcsicmp(pszName, pos->from) == 0) { + if(pos->to == NULL) { + LeaveCriticalSection(&dns_hook_lock); + return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR); + } + pszName = pos->to; break; @@ -346,6 +359,11 @@ static DNS_STATUS WINAPI hook_DnsQueryEx( pos = &dns_hook_entries[i]; if (_wcsicmp(pRequest->QueryName, pos->from) == 0) { + if(pos->to == NULL) { + LeaveCriticalSection(&dns_hook_lock); + return HRESULT_FROM_WIN32(DNS_ERROR_RCODE_NAME_ERROR); + } + pRequest->QueryName = pos->to; break; @@ -408,6 +426,13 @@ static int WSAAPI hook_getaddrinfo( pos = &dns_hook_entries[i]; if (_wcsicmp(wstr, pos->from) == 0) { + if(pos->to == NULL) { + LeaveCriticalSection(&dns_hook_lock); + result = EAI_NONAME; + + goto end; + } + wcstombs_s(&str_c, NULL, 0, pos->to, 0); str = malloc(str_c * sizeof(char)); diff --git a/hooklib/dns.h b/hooklib/dns.h index 062d481..1f93b0f 100644 --- a/hooklib/dns.h +++ b/hooklib/dns.h @@ -4,5 +4,6 @@ #include +// if to_src is NULL, all lookups for from_src will fail HRESULT dns_hook_push(const wchar_t *from_src, const wchar_t *to_src); diff --git a/platform/dns.c b/platform/dns.c index 51f64f8..5486d42 100644 --- a/platform/dns.c +++ b/platform/dns.c @@ -64,5 +64,20 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg) return hr; } + // if your ISP resolves bad domains, it will kill the network. These 2 + // *cannot* resolve + + hr = dns_hook_push(L"mobirouter.loc", NULL); + + if (FAILED(hr)) { + return hr; + } + + hr = dns_hook_push(L"dslrouter.loc", NULL); + + if (FAILED(hr)) { + return hr; + } + return S_OK; } From 27db92c0bfffabbcb1f6c3ec380320e26ad6e2bb Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Fri, 22 Nov 2019 00:59:08 +0000 Subject: [PATCH 006/175] Added more controller options to idzio Add axis reversal for dinput and single stick steering for xinput. --- dist/idz/segatools.ini | 7 +++++++ idzio/config.c | 24 ++++++++++++++++++++++++ idzio/config.h | 8 ++++++++ idzio/di.c | 20 ++++++++++++++++++-- idzio/dllmain.c | 2 +- idzio/xi.c | 33 +++++++++++++++++++++++++++++++-- idzio/xi.h | 3 ++- 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 5acb230..65ee0cb 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -45,6 +45,9 @@ mode=xinput ; Automatically reset the simulated shifter to Neutral when XInput Start is ; pressed (e.g. when navigating menus between races). autoNeutral=1 +; Use the left thumbstick for steering instead of both on XInput Controllers. +; Not recommended as it will not give you the precision needed for this game +singleStickSteering=0 [dinput] ; Name of the DirectInput wheel to use (or any text that occurs in its name) @@ -83,3 +86,7 @@ gear3=3 gear4=4 gear5=5 gear6=6 +; Invert the accelerator and or brake axis +; (Needed when using DirectInput for the Dualshock 4 for example) +reverseAccelAxis=0 +reverseBrakeAxis=0 \ No newline at end of file diff --git a/idzio/config.c b/idzio/config.c index 2422d1a..566c0e3 100644 --- a/idzio/config.c +++ b/idzio/config.c @@ -53,6 +53,17 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename) cfg->shift_dn = GetPrivateProfileIntW(L"dinput", L"shiftDn", 0, filename); cfg->shift_up = GetPrivateProfileIntW(L"dinput", L"shiftUp", 0, filename); + cfg->reverse_brake_axis = GetPrivateProfileIntW( + L"dinput", + L"reverseBrakeAxis", + 0, + filename); + cfg->reverse_accel_axis = GetPrivateProfileIntW( + L"dinput", + L"reverseAccelAxis", + 0, + filename); + for (i = 0 ; i < 6 ; i++) { swprintf_s(key, _countof(key), L"gear%i", i + 1); cfg->gear[i] = GetPrivateProfileIntW(L"dinput", key, i + 1, filename); @@ -60,6 +71,18 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename) } +void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->single_stick_steering = GetPrivateProfileIntW( + L"io3", + L"singleStickSteering", + 0, + filename); +} + void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename) { assert(cfg != NULL); @@ -80,6 +103,7 @@ void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename) idz_shifter_config_load(&cfg->shifter, filename); idz_di_config_load(&cfg->di, filename); + idz_xi_config_load(&cfg->xi, filename); } void idz_shifter_config_load( diff --git a/idzio/config.h b/idzio/config.h index 0c2b9f3..19b837a 100644 --- a/idzio/config.h +++ b/idzio/config.h @@ -18,6 +18,12 @@ struct idz_di_config { uint8_t shift_dn; uint8_t shift_up; uint8_t gear[6]; + bool reverse_brake_axis; + bool reverse_accel_axis; +}; + +struct idz_xi_config { + bool single_stick_steering; }; struct idz_io_config { @@ -28,9 +34,11 @@ struct idz_io_config { int restrict_; struct idz_shifter_config shifter; struct idz_di_config di; + struct idz_xi_config xi; }; void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename); +void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename); void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename); void idz_shifter_config_load( struct idz_shifter_config *cfg, diff --git a/idzio/di.c b/idzio/di.c index 63aeba0..d8405da 100644 --- a/idzio/di.c +++ b/idzio/di.c @@ -66,6 +66,8 @@ static uint8_t idz_di_shift_up; static uint8_t idz_di_view_chg; static uint8_t idz_di_start; static uint8_t idz_di_gear[6]; +static bool idz_di_reverse_brake_axis; +static bool idz_di_reverse_accel_axis; HRESULT idz_di_init( const struct idz_di_config *cfg, @@ -265,6 +267,8 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) dprintf("Wheel: View Change button : %i\n", cfg->view_chg); dprintf("Wheel: Shift Down button . : %i\n", cfg->shift_dn); dprintf("Wheel: Shift Up button . . : %i\n", cfg->shift_up); + dprintf("Wheel: Reverse Brake Axis : %i\n", cfg->reverse_brake_axis); + dprintf("Wheel: Reverse Accel Axis : %i\n", cfg->reverse_accel_axis); dprintf("Wheel: --- End configuration ---\n"); if (cfg->shifter_name[0] != L'\0') { @@ -287,6 +291,8 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg) idz_di_view_chg = cfg->view_chg; idz_di_shift_dn = cfg->shift_dn; idz_di_shift_up = cfg->shift_up; + idz_di_reverse_brake_axis = cfg->reverse_brake_axis; + idz_di_reverse_accel_axis = cfg->reverse_accel_axis; for (i = 0 ; i < 6 ; i++) { idz_di_gear[i] = cfg->gear[i]; @@ -501,6 +507,16 @@ static void idz_di_jvs_read_analogs(struct idz_io_analog_state *out) accel = (LONG *) &state.bytes[idz_di_off_accel]; out->wheel = state.st.lX - 32768; - out->brake = 65535 - *brake; - out->accel = 65535 - *accel; + + if (idz_di_reverse_brake_axis) { + out->brake = *brake; + } else { + out->brake = 65535 - *brake; + } + + if (idz_di_reverse_accel_axis) { + out->accel = *accel; + } else { + out->accel = 65535 - *accel; + } } diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 75c7e95..79bc385 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -30,7 +30,7 @@ HRESULT idz_io_jvs_init(void) if (wstr_ieq(idz_io_cfg.mode, L"dinput")) { hr = idz_di_init(&idz_io_cfg.di, idz_io_hmodule, &idz_io_backend); } else if (wstr_ieq(idz_io_cfg.mode, L"xinput")) { - hr = idz_xi_init(&idz_io_backend); + hr = idz_xi_init(&idz_io_cfg.xi, &idz_io_backend); } else { hr = E_INVALIDARG; dprintf("IDZ IO: Invalid IO mode \"%S\", use dinput or xinput\n", diff --git a/idzio/xi.c b/idzio/xi.c index 526b6c5..c3d33d6 100644 --- a/idzio/xi.c +++ b/idzio/xi.c @@ -6,6 +6,7 @@ #include #include "idzio/backend.h" +#include "idzio/config.h" #include "idzio/idzio.h" #include "idzio/shifter.h" #include "idzio/xi.h" @@ -16,22 +17,45 @@ static void idz_xi_jvs_read_buttons(uint8_t *gamebtn_out); static void idz_xi_jvs_read_shifter(uint8_t *gear); static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out); +static HRESULT idz_xi_config_apply(const struct idz_xi_config *cfg); + static const struct idz_io_backend idz_xi_backend = { .jvs_read_buttons = idz_xi_jvs_read_buttons, .jvs_read_shifter = idz_xi_jvs_read_shifter, .jvs_read_analogs = idz_xi_jvs_read_analogs, }; -HRESULT idz_xi_init(const struct idz_io_backend **backend) +static bool idz_xi_single_stick_steering; + +HRESULT idz_xi_init(const struct idz_xi_config *cfg, const struct idz_io_backend **backend) { + HRESULT hr; + assert(cfg != NULL); assert(backend != NULL); + hr = idz_xi_config_apply(cfg); + + if (FAILED(hr)) { + return hr; + } + dprintf("XInput: Using XInput controller\n"); *backend = &idz_xi_backend; return S_OK; } +static HRESULT idz_xi_config_apply(const struct idz_xi_config *cfg) +{ + dprintf("XInput: --- Begin configuration ---\n"); + dprintf("XInput: Single Stick Steering : %i\n", cfg->single_stick_steering); + dprintf("XInput: --- End configuration ---\n"); + + idz_xi_single_stick_steering = cfg->single_stick_steering; + + return S_OK; +} + static void idz_xi_jvs_read_buttons(uint8_t *gamebtn_out) { uint8_t gamebtn; @@ -130,7 +154,12 @@ static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out) right = 0; } - out->wheel = (left + right) / 2; + if(idz_xi_single_stick_steering) { + out->wheel = left; + } else { + out->wheel = (left + right) / 2; + } + out->accel = xi.Gamepad.bRightTrigger << 8; out->brake = xi.Gamepad.bLeftTrigger << 8; } diff --git a/idzio/xi.h b/idzio/xi.h index 8dcb55b..5df6d67 100644 --- a/idzio/xi.h +++ b/idzio/xi.h @@ -5,5 +5,6 @@ #include #include "idzio/backend.h" +#include "idzio/config.h" -HRESULT idz_xi_init(const struct idz_io_backend **backend); +HRESULT idz_xi_init(const struct idz_xi_config *cfg, const struct idz_io_backend **backend); From eb58c0ef232e0d4935acbd3b38768d9e368b88a0 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 24 Nov 2019 12:46:40 -0500 Subject: [PATCH 007/175] Uncomment default subnet INI setting The default value being commented out is no doubt a great source of confusion whenever somebody attempts to edit it without realizing that it is a comment. --- dist/chuni/segatools.ini | 2 +- dist/idz/segatools.ini | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index 51d78c7..d91984a 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -21,7 +21,7 @@ enable=1 ; The /24 LAN subnet that the emulated keychip will tell the game to expect. ; If you disable netenv then you must set this to your LAN's IP subnet, and ; that subnet must start with 192.168. -;subnet=192.168.100.0 +subnet=192.168.100.0 [gfx] ; Force the game to run windowed. diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 65ee0cb..dbcaf42 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -29,7 +29,7 @@ enable=1 ; The /24 LAN subnet that the emulated keychip will tell the game to expect. ; If you disable netenv then you must set this to your LAN's IP subnet, and ; that subnet must start with 192.168. -;subnet=192.168.100.0 +subnet=192.168.100.0 [gpio] ; Emulated Nu DIP switch for Distribution Server setting. @@ -89,4 +89,4 @@ gear6=6 ; Invert the accelerator and or brake axis ; (Needed when using DirectInput for the Dualshock 4 for example) reverseAccelAxis=0 -reverseBrakeAxis=0 \ No newline at end of file +reverseBrakeAxis=0 From 2402e6cb888b73f540d0640d18eda54ae46a6090 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 24 Nov 2019 12:59:02 -0500 Subject: [PATCH 008/175] idzio: Adjust and document [io3] restrict= setting --- dist/idz/segatools.ini | 12 ++++++++++++ idzio/config.c | 2 +- idzio/dllmain.c | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index dbcaf42..19af346 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -48,6 +48,18 @@ autoNeutral=1 ; Use the left thumbstick for steering instead of both on XInput Controllers. ; Not recommended as it will not give you the precision needed for this game singleStickSteering=0 +; Adjust scaling for steering wheel input. +; +; This setting scales the steering wheel input so that the maximum positive +; and minimum negative steering inputs reported in the operator menu's input +; test screen do not exceed the value below. The maximum possible value is 128, +; and the value that matches the input range of a real cabinet is 97. +; +; NOTE: This is not the same thing as DirectInput steering wheel movement +; range! Segatools cannot control the maximum angle of your physical steering +; wheel controller, this setting is vendor-specific and can only be adjusted +; in the Control Panel. +restrict=97 [dinput] ; Name of the DirectInput wheel to use (or any text that occurs in its name) diff --git a/idzio/config.c b/idzio/config.c index 566c0e3..d06476b 100644 --- a/idzio/config.c +++ b/idzio/config.c @@ -91,7 +91,7 @@ void idz_io_config_load(struct idz_io_config *cfg, const wchar_t *filename) cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); - cfg->restrict_ = GetPrivateProfileIntW(L"io3", L"restrict", 196, filename); + cfg->restrict_ = GetPrivateProfileIntW(L"io3", L"restrict", 97, filename); GetPrivateProfileStringW( L"io3", diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 79bc385..8ebea53 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -85,10 +85,10 @@ void idz_io_jvs_read_analogs(struct idz_io_analog_state *out) /* Apply steering wheel restriction. Real cabs only report about 77% of the IO-3's max ADC output value when the wheel is turned to either of its maximum positions. To match this behavior we set the default value - for the wheel restriction config parameter to 196 (out of 256). This + for the wheel restriction config parameter to 97 (out of 128). This scaling factor is applied using fixed-point arithmetic below. */ - out->wheel = (tmp.wheel * idz_io_cfg.restrict_) / 256; + out->wheel = (tmp.wheel * idz_io_cfg.restrict_) / 128; out->accel = tmp.accel; out->brake = tmp.brake; } From e6c792787b956839a040e11f8116eba4e30540cf Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 17 Nov 2019 12:45:56 -0500 Subject: [PATCH 009/175] chuniio: Increase touch pressure The touch pressure threshold is configurable, probably best not to set this to the bare minimum. --- chuniio/chuniio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index ed02ace..7823669 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -123,7 +123,7 @@ static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) while (!chuni_io_slider_stop_flag) { for (i = 0 ; i < 8 ; i++) { if (GetAsyncKeyState(chuni_io_slider_keys[i]) & 0x8000) { - pressure_val = 20; + pressure_val = 128; } else { pressure_val = 0; } From 388ba0b4a42398e6439d88f925e64343e1daa5df Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 17 Nov 2019 13:11:49 -0500 Subject: [PATCH 010/175] chuniio: Add input config --- chuniio/chuniio.c | 27 ++++++++++++--------------- chuniio/config.c | 43 +++++++++++++++++++++++++++++++++++++++++++ chuniio/config.h | 16 ++++++++++++++++ chuniio/meson.build | 2 ++ 4 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 chuniio/config.c create mode 100644 chuniio/config.h diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index 7823669..be81d64 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -5,21 +5,21 @@ #include #include "chuniio/chuniio.h" +#include "chuniio/config.h" static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); -static const int chuni_io_slider_keys[] = { - 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', -}; - static bool chuni_io_coin; static uint16_t chuni_io_coins; static uint8_t chuni_io_hand_pos; static HANDLE chuni_io_slider_thread; static bool chuni_io_slider_stop_flag; +static struct chuni_io_config chuni_io_cfg; HRESULT chuni_io_jvs_init(void) { + chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); + return S_OK; } @@ -29,7 +29,7 @@ void chuni_io_jvs_read_coin_counter(uint16_t *out) return; } - if (GetAsyncKeyState('3')) { + if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) { if (!chuni_io_coin) { chuni_io_coin = true; chuni_io_coins++; @@ -45,15 +45,15 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) { size_t i; - if (GetAsyncKeyState('1')) { + if (GetAsyncKeyState(chuni_io_cfg.vk_test)) { *opbtn |= 0x01; /* Test */ } - if (GetAsyncKeyState('2')) { + if (GetAsyncKeyState(chuni_io_cfg.vk_service)) { *opbtn |= 0x02; /* Service */ } - if (GetAsyncKeyState(VK_SPACE)) { + if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) { if (chuni_io_hand_pos < 6) { chuni_io_hand_pos++; } @@ -114,21 +114,18 @@ void chuni_io_slider_set_leds(const uint8_t *rgb) static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) { chuni_io_slider_callback_t callback; - uint8_t pressure_val; uint8_t pressure[32]; size_t i; callback = ctx; while (!chuni_io_slider_stop_flag) { - for (i = 0 ; i < 8 ; i++) { - if (GetAsyncKeyState(chuni_io_slider_keys[i]) & 0x8000) { - pressure_val = 128; + for (i = 0 ; i < _countof(pressure) ; i++) { + if (GetAsyncKeyState(chuni_io_cfg.vk_cell[i]) & 0x8000) { + pressure[i] = 128; } else { - pressure_val = 0; + pressure[i] = 0; } - - memset(&pressure[28 - 4 * i], pressure_val, 4); } callback(pressure); diff --git a/chuniio/config.c b/chuniio/config.c new file mode 100644 index 0000000..7365f2c --- /dev/null +++ b/chuniio/config.c @@ -0,0 +1,43 @@ +#include + +#include +#include +#include + +#include "chuniio/config.h" + +static const int chuni_io_default_cells[] = { + 'L', 'L', 'L', 'L', + 'K', 'K', 'K', 'K', + 'J', 'J', 'J', 'J', + 'H', 'H', 'H', 'H', + 'G', 'G', 'G', 'G', + 'F', 'F', 'F', 'F', + 'D', 'D', 'D', 'D', + 'S', 'S', 'S', 'S', +}; + +void chuni_io_config_load( + struct chuni_io_config *cfg, + const wchar_t *filename) +{ + wchar_t key[16]; + int i; + + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); + cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename); + + for (i = 0 ; i < 32 ; i++) { + swprintf_s(key, _countof(key), L"cell%i", i + 1); + cfg->vk_cell[i] = GetPrivateProfileIntW( + L"slider", + key, + chuni_io_default_cells[i], + filename); + } +} diff --git a/chuniio/config.h b/chuniio/config.h new file mode 100644 index 0000000..fa2e2b0 --- /dev/null +++ b/chuniio/config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct chuni_io_config { + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_coin; + uint8_t vk_ir; + uint8_t vk_cell[32]; +}; + +void chuni_io_config_load( + struct chuni_io_config *cfg, + const wchar_t *filename); diff --git a/chuniio/meson.build b/chuniio/meson.build index 78a974b..2e15e4d 100644 --- a/chuniio/meson.build +++ b/chuniio/meson.build @@ -8,5 +8,7 @@ chuniio_dll = shared_library( sources : [ 'chuniio.c', 'chuniio.h', + 'config.c', + 'config.h', ], ) From 97cfa2289ef83a73e2de45d3936e61c324ac77d3 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 17 Nov 2019 13:16:50 -0500 Subject: [PATCH 011/175] Simplify configurable GetAsyncKeyState Setting the VK code to zero effectively disables these anyway. --- amex/gpio.c | 4 ++-- idzio/dllmain.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/amex/gpio.c b/amex/gpio.c index ffd1fc0..4e18630 100644 --- a/amex/gpio.c +++ b/amex/gpio.c @@ -196,11 +196,11 @@ static HRESULT gpio_ioctl_get_psw(struct irp *irp) /* Bit 0 == SW1 == Alt. Test */ /* Bit 1 == SW2 == Alt. Service */ - if (gpio_config.vk_sw1 && (GetAsyncKeyState(gpio_config.vk_sw1) & 0x8000)) { + if (GetAsyncKeyState(gpio_config.vk_sw1) & 0x8000) { result |= 1 << 0; } - if (gpio_config.vk_sw2 && (GetAsyncKeyState(gpio_config.vk_sw2) & 0x8000)) { + if (GetAsyncKeyState(gpio_config.vk_sw2) & 0x8000) { result |= 1 << 1; } diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 8ebea53..5edf67c 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -50,13 +50,11 @@ void idz_io_jvs_read_buttons(uint8_t *opbtn_out, uint8_t *gamebtn_out) opbtn = 0; - if ( idz_io_cfg.vk_test && - (GetAsyncKeyState(idz_io_cfg.vk_test) & 0x8000)) { + if (GetAsyncKeyState(idz_io_cfg.vk_test) & 0x8000) { opbtn |= IDZ_IO_OPBTN_TEST; } - if ( idz_io_cfg.vk_service && - (GetAsyncKeyState(idz_io_cfg.vk_service) & 0x8000)) { + if (GetAsyncKeyState(idz_io_cfg.vk_service) & 0x8000) { opbtn |= IDZ_IO_OPBTN_SERVICE; } From 83afa0a423cd010206f24df7ca8e566a3c3f2c5a Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 24 Nov 2019 18:43:52 -0500 Subject: [PATCH 012/175] Add INI comments for Chunithm IO config --- dist/chuni/segatools.ini | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index d91984a..d474de5 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -28,3 +28,39 @@ subnet=192.168.100.0 windowed=1 ; Add a frame to the game window if running windowed. framed=1 + +; ----------------------------------------------------------------------------- +; Input settings +; ----------------------------------------------------------------------------- + +; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal +; (not prefixed with 0x) virtual-key codes, a list of which can be found here: +; +; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +; +; This is, admittedly, not the most user-friendly configuration method in the +; world. An improved solution will be provided later. + +[io3] +; 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 + +; Key bindings for each of the 32 touch cells. The default key map, depicted +; in left-to-right order, is as follows: +; +; SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL +; +; Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in +; order to match the numbering used in the operator menu and service manual. +; +; Uncomment and complete the following sequence of settings to configure a +; custom high-precision touch strip controller if you have one. +[slider] +;cell32=0x53 +;cell31=0x53 +;cell30=0x53 +; ... etc ... From 28e9594b9816905ee1284cdec21922c36b682d0b Mon Sep 17 00:00:00 2001 From: Shiz Date: Wed, 18 Dec 2019 20:23:35 +0100 Subject: [PATCH 013/175] hooklib/path.c: add support for FindFirstFile{A,W} --- hooklib/path.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/hooklib/path.c b/hooklib/path.c index 6f41a53..887a9e9 100644 --- a/hooklib/path.c +++ b/hooklib/path.c @@ -45,6 +45,14 @@ static HANDLE WINAPI hook_CreateFileW( uint32_t dwFlagsAndAttributes, HANDLE hTemplateFile); +static HANDLE WINAPI hook_FindFirstFileA( + const char *lpFileName, + LPWIN32_FIND_DATAA lpFindFileData); + +static HANDLE WINAPI hook_FindFirstFileW( + const wchar_t *lpFileName, + LPWIN32_FIND_DATAW lpFindFileData); + static HANDLE WINAPI hook_FindFirstFileExA( const char *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, @@ -103,6 +111,14 @@ static HANDLE (WINAPI *next_CreateFileW)( uint32_t dwFlagsAndAttributes, HANDLE hTemplateFile); +static HANDLE (WINAPI *next_FindFirstFileA)( + const char *lpFileName, + LPWIN32_FIND_DATAA lpFindFileData); + +static HANDLE (WINAPI *next_FindFirstFileW)( + const wchar_t *lpFileName, + LPWIN32_FIND_DATAW lpFindFileData); + static HANDLE (WINAPI *next_FindFirstFileExA)( const char *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, @@ -152,6 +168,14 @@ static const struct hook_symbol path_hook_syms[] = { .name = "CreateFileW", .patch = hook_CreateFileW, .link = (void **) &next_CreateFileW, + }, { + .name = "FindFirstFileA", + .patch = hook_FindFirstFileA, + .link = (void **) &next_FindFirstFileA, + }, { + .name = "FindFirstFileW", + .patch = hook_FindFirstFileW, + .link = (void **) &next_FindFirstFileW, }, { .name = "FindFirstFileExA", .patch = hook_FindFirstFileExA, @@ -480,6 +504,48 @@ static HANDLE WINAPI hook_CreateFileW( return result; } +static HANDLE WINAPI hook_FindFirstFileA( + const char *lpFileName, + LPWIN32_FIND_DATAA lpFindFileData) +{ + char *trans; + HANDLE result; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return INVALID_HANDLE_VALUE; + } + + result = next_FindFirstFileA(trans ? trans : lpFileName, lpFindFileData); + + free(trans); + + return result; +} + +static HANDLE WINAPI hook_FindFirstFileW( + const wchar_t *lpFileName, + LPWIN32_FIND_DATAW lpFindFileData) +{ + wchar_t *trans; + HANDLE result; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return INVALID_HANDLE_VALUE; + } + + result = next_FindFirstFileW(trans ? trans : lpFileName, lpFindFileData); + + free(trans); + + return result; +} + static HANDLE WINAPI hook_FindFirstFileExA( const char *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, From a31557c5cd1824d846ef8182ddad66949e8672f8 Mon Sep 17 00:00:00 2001 From: Shiz Date: Wed, 18 Dec 2019 21:30:35 +0100 Subject: [PATCH 014/175] hooklib/path.c: add slash-insensitive path comparison API --- hooklib/path.c | 29 +++++++++++++++++++++++++++++ hooklib/path.h | 1 + platform/vfs.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hooklib/path.c b/hooklib/path.c index 887a9e9..864d2e2 100644 --- a/hooklib/path.c +++ b/hooklib/path.c @@ -388,6 +388,35 @@ end: return ok; } +int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count) +{ + size_t i; + wchar_t c1, c2; + + assert(string1 != NULL); + assert(string2 != NULL); + + for (i = 0; i < count && string1[i] && string2[i]; i++) { + c1 = towlower(string1[i]); + + if (c1 == '/') { + c1 = '\\'; + } + + c2 = towlower(string2[i]); + + if (c2 == '/') { + c2 = '\\'; + } + + if (c1 != c2) { + break; + } + } + + return i == count ? 0 : string2[i] - string1[i]; +} + /* Dumping ground for kernel32 file system ops whose path parameters we have to hook into and translate. This list will grow over time as we go back and fix up older games that don't pay attention to the mount point registry. */ diff --git a/hooklib/path.h b/hooklib/path.h index 9e7b408..779e013 100644 --- a/hooklib/path.h +++ b/hooklib/path.h @@ -10,3 +10,4 @@ typedef HRESULT (*path_hook_t)( size_t *count); HRESULT path_hook_push(path_hook_t hook); +int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count); diff --git a/platform/vfs.c b/platform/vfs.c index 689cd44..d78e6b5 100644 --- a/platform/vfs.c +++ b/platform/vfs.c @@ -273,7 +273,7 @@ static HRESULT vfs_path_hook_nthome( /* Case-insensitive check to see if src starts with vfs_nthome */ - if (_wcsnicmp(src, vfs_nthome, vfs_nthome_len) != 0) { + if (path_compare_w(src, vfs_nthome, vfs_nthome_len) != 0) { return S_FALSE; } From 0d8dd05cebd70fc2dbc7d301b6669b6241bf7d6f Mon Sep 17 00:00:00 2001 From: Shiz Date: Wed, 18 Dec 2019 20:51:09 +0100 Subject: [PATCH 015/175] platform/vfs.c: add option path redirection Optional for now, since not all games or configurations may need it. It may be better eventually to refactor hooklib/path.c to allow for static path remapping, to minimize boilerplate code around the functions. --- dist/chuni/segatools.ini | 2 ++ dist/idz/segatools.ini | 2 ++ platform/config.c | 8 +++++ platform/vfs.c | 65 ++++++++++++++++++++++++++++++++++++++-- platform/vfs.h | 1 + 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index d474de5..7fc0d82 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -1,6 +1,8 @@ [vfs] ; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= ; Create an empty directory somewhere and insert the path here. ; This directory may be shared between multiple SEGA games. ; NOTE: This has nothing to do with Windows %APPDATA%. diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 19af346..4c4f196 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -1,6 +1,8 @@ [vfs] ; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) amfs= +; Insert the path to the game Option directory here (contains OPxx directories) +option= ; Create an empty directory somewhere and insert the path here. ; This directory may be shared between multiple SEGA games. ; NOTE: This has nothing to do with Windows %APPDATA%. diff --git a/platform/config.c b/platform/config.c index 5a34d5f..94b9e40 100644 --- a/platform/config.c +++ b/platform/config.c @@ -295,5 +295,13 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename) cfg->appdata, _countof(cfg->appdata), filename); + + GetPrivateProfileStringW( + L"vfs", + L"option", + L"", + cfg->option, + _countof(cfg->option), + filename); } diff --git a/platform/vfs.c b/platform/vfs.c index d78e6b5..1aecc9d 100644 --- a/platform/vfs.c +++ b/platform/vfs.c @@ -19,6 +19,10 @@ static HRESULT vfs_path_hook_nthome( const wchar_t *src, wchar_t *dest, size_t *count); +static HRESULT vfs_path_hook_option( + const wchar_t *src, + wchar_t *dest, + size_t *count); static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes); static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes); @@ -26,6 +30,9 @@ static wchar_t vfs_nthome_real[MAX_PATH]; static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser\\"; static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1; +static const wchar_t vfs_option[] = L"C:\\Mount\\Option\\"; +static const size_t vfs_option_len = _countof(vfs_option) - 1; + static const struct reg_hook_val vfs_reg_vals[] = { { .name = L"AMFS", @@ -65,6 +72,10 @@ HRESULT vfs_hook_init(const struct vfs_config *config) return E_FAIL; } + if (config->option[0] == L'\0') { + dprintf("Vfs: WARNING: OPTION path not specified in INI file\n"); + } + home_ok = GetEnvironmentVariableW( L"USERPROFILE", vfs_nthome_real, @@ -80,9 +91,13 @@ HRESULT vfs_hook_init(const struct vfs_config *config) memcpy(&vfs_config, config, sizeof(*config)); + vfs_slashify(vfs_nthome_real, _countof(vfs_nthome_real)); vfs_slashify(vfs_config.amfs, _countof(vfs_config.amfs)); vfs_slashify(vfs_config.appdata, _countof(vfs_config.appdata)); - vfs_slashify(vfs_nthome_real, _countof(vfs_nthome_real)); + + if (vfs_config.option[0] != L'\0') { + vfs_slashify(vfs_config.option, _countof(vfs_config.option)); + } hr = vfs_mkdir_rec(vfs_config.amfs); @@ -114,6 +129,8 @@ HRESULT vfs_hook_init(const struct vfs_config *config) dprintf("Vfs: Failed to create %S: %x\n", temp, (int) hr); } + /* Not auto-creating option directory as it is normally a read-only mount */ + hr = path_hook_push(vfs_path_hook); if (FAILED(hr)) { @@ -126,6 +143,14 @@ HRESULT vfs_hook_init(const struct vfs_config *config) return hr; } + if (vfs_config.option[0] != L'\0') { + hr = path_hook_push(vfs_path_hook_option); + + if (FAILED(hr)) { + return hr; + } + } + hr = reg_hook_push_key( HKEY_LOCAL_MACHINE, L"SYSTEM\\SEGA\\SystemProperty\\mount", @@ -241,7 +266,7 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) return S_FALSE; } - /* Cut off E:\ prefix, replace with redir path, count NUL terminator */ + /* Cut off \, replace with redir path, count NUL terminator */ redir_len = wcslen(redir); required = wcslen(src) - 3 + redir_len + 1; @@ -296,6 +321,42 @@ static HRESULT vfs_path_hook_nthome( return S_OK; } +static HRESULT vfs_path_hook_option( + const wchar_t *src, + wchar_t *dest, + size_t *count) +{ + size_t required; + size_t redir_len; + + assert(src != NULL); + assert(count != NULL); + + /* Case-insensitive check to see if src starts with vfs_option */ + + if (path_compare_w(src, vfs_option, vfs_option_len) != 0) { + return S_FALSE; + } + + /* Cut off the matched prefix, add the replaced prefix, count NUL */ + + redir_len = wcslen(vfs_config.option); + required = wcslen(src) - vfs_option_len + redir_len + 1; + + if (dest != NULL) { + if (required > *count) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + wcscpy_s(dest, *count, vfs_config.option); + wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_option_len); + } + + *count = required; + + return S_OK; +} + static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes) { return reg_hook_read_wstr(bytes, nbytes, vfs_config.amfs); diff --git a/platform/vfs.h b/platform/vfs.h index acdd99a..8767305 100644 --- a/platform/vfs.h +++ b/platform/vfs.h @@ -9,6 +9,7 @@ struct vfs_config { bool enable; wchar_t amfs[MAX_PATH]; wchar_t appdata[MAX_PATH]; + wchar_t option[MAX_PATH]; }; HRESULT vfs_hook_init(const struct vfs_config *config); From ad24fe10bb5222dc42454ba212d47efe70f0b497 Mon Sep 17 00:00:00 2001 From: Shiz Date: Sun, 22 Dec 2019 21:42:04 +0100 Subject: [PATCH 016/175] build: enable "secure" CRT headers on MinGW Some distributions of MinGW, recent builds included, require `MINGW_HAS_SECURE_API` to be defined in order to expose `_s` variants of functions, as defined by Annex K of the C standard, in the headers. Omitting this definition leads to a bunch of compiler warnings clobbering the build output, as well as possibly incorrect inference of argument and return types by the compiler. It needs to be set to `1` as opposed to simply defined as per https://sourceforge.net/p/mingw-w64/bugs/714/. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 00ff0be..dcc94bc 100644 --- a/meson.build +++ b/meson.build @@ -5,6 +5,7 @@ add_project_arguments( '-DDIRECTINPUT_VERSION=0x0800', '-DWIN32_LEAN_AND_MEAN', '-D_WIN32_WINNT=_WIN32_WINNT_WIN7', + '-DMINGW_HAS_SECURE_API=1', language: 'c', ) From 9137a36a53a6253ae47d4f7748da039e16563fb2 Mon Sep 17 00:00:00 2001 From: Shiz Date: Sun, 22 Dec 2019 22:38:37 +0100 Subject: [PATCH 017/175] platform/vfs.c: allow relative paths in configuration --- meson.build | 1 + platform/meson.build | 1 + platform/vfs.c | 39 +++++++++++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index dcc94bc..05a2bce 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ if meson.get_compiler('c').get_id() != 'msvc' endif cc = meson.get_compiler('c') +shlwapi_lib = cc.find_library('shlwapi') dinput8_lib = cc.find_library('dinput8') dxguid_lib = cc.find_library('dxguid') xinput_lib = cc.find_library('xinput') diff --git a/platform/meson.build b/platform/meson.build index d45f498..7e5c544 100644 --- a/platform/meson.build +++ b/platform/meson.build @@ -5,6 +5,7 @@ platform_lib = static_library( c_pch : '../precompiled.h', dependencies : [ capnhook.get_variable('hook_dep'), + shlwapi_lib, ], sources : [ 'amvideo.c', diff --git a/platform/vfs.c b/platform/vfs.c index 1aecc9d..0813a6d 100644 --- a/platform/vfs.c +++ b/platform/vfs.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -12,7 +13,7 @@ #include "util/dprintf.h" -static void vfs_slashify(wchar_t *path, size_t max_count); +static void vfs_fixup_path(wchar_t *path, size_t max_count); static HRESULT vfs_mkdir_rec(const wchar_t *path); static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count); static HRESULT vfs_path_hook_nthome( @@ -91,12 +92,12 @@ HRESULT vfs_hook_init(const struct vfs_config *config) memcpy(&vfs_config, config, sizeof(*config)); - vfs_slashify(vfs_nthome_real, _countof(vfs_nthome_real)); - vfs_slashify(vfs_config.amfs, _countof(vfs_config.amfs)); - vfs_slashify(vfs_config.appdata, _countof(vfs_config.appdata)); + vfs_fixup_path(vfs_nthome_real, _countof(vfs_nthome_real)); + vfs_fixup_path(vfs_config.amfs, _countof(vfs_config.amfs)); + vfs_fixup_path(vfs_config.appdata, _countof(vfs_config.appdata)); if (vfs_config.option[0] != L'\0') { - vfs_slashify(vfs_config.option, _countof(vfs_config.option)); + vfs_fixup_path(vfs_config.option, _countof(vfs_config.option)); } hr = vfs_mkdir_rec(vfs_config.amfs); @@ -164,24 +165,46 @@ HRESULT vfs_hook_init(const struct vfs_config *config) return S_OK; } -static void vfs_slashify(wchar_t *path, size_t max_count) +static void vfs_fixup_path(wchar_t *path, size_t max_count) { size_t count; + wchar_t abspath[MAX_PATH]; assert(path != NULL); + /* Requirement for PathIsRelativeW */ + assert(max_count <= MAX_PATH); - count = wcslen(path); + if (PathIsRelativeW(path)) { + count = GetFullPathNameW(path, _countof(abspath), abspath, NULL); + + /* GetFullPathName's length return value is tricky, because it includes + the NUL terminator on failure, but doesn't on success. + Check if it fits the temp buf (else it's a failure and includes NUL + anyway), then if it fits the target buf, NUL included. */ + if (count == 0 || count > _countof(abspath) || count >= max_count) { + goto fail; + } + + wcscpy_s(path, max_count, abspath); + } else { + count = wcslen(path); + } if (path[count - 1] == L'\\' || path[count - 1] == L'/') { return; } if (count + 2 > max_count) { - abort(); + goto fail; } path[count + 0] = L'\\'; path[count + 1] = L'\0'; + return; + +fail: + dprintf("Vfs: FATAL: Path too long: %S\n", path); + abort(); } static HRESULT vfs_mkdir_rec(const wchar_t *path) From 34ad466df29025cb3e3959681ffa95a908555057 Mon Sep 17 00:00:00 2001 From: Tau Date: Wed, 25 Dec 2019 16:06:22 -0500 Subject: [PATCH 018/175] aimeio: Fix INI loading Got bitten by the win32 INI API's handling of relative paths again. --- aimeio/aimeio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c index 0c26b13..747205d 100644 --- a/aimeio/aimeio.c +++ b/aimeio/aimeio.c @@ -164,7 +164,7 @@ static HRESULT aime_io_generate_felica( HRESULT aime_io_init(void) { - aime_io_config_read(&aime_io_cfg, L"segatools.ini"); + aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); return S_OK; } From f732eb05a895feb592d05f360657aa722d7e952b Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 28 Dec 2019 11:16:06 -0500 Subject: [PATCH 019/175] Add CHANGELOG --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ee4bd9d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# v004 + +* Add initial support for mounting DLC package dumps (contributed by Shiz) +* Fix configuration loading in aimeio.dll +* Build system fixes (contributed by Shiz) + +# v003 + +* Add countermeasures for DNS-spamming ISPs (contributed by mon) +* Add option for single-stick steering in IDZ (contributed by BemaniWitch) +* Fix MSVC build (contributed by mariodon) +* Make all 32 Chunithm touch slider cells' keyboard bindings configurable (see + INI) + +# v002 + +* Ship correct inject.exe for IDZ +* Fix IDZ main EXE crash in GetIfTable() hook in platform/netenv.c + +# v001 + +* Initial release From 0c74d64d5cfbb77d426df33cf9c2aa250c685eea Mon Sep 17 00:00:00 2001 From: dogtopus Date: Sun, 29 Dec 2019 13:28:32 -0400 Subject: [PATCH 020/175] divahook: Add an option to disable slider emulation --- divahook/config.c | 8 ++++++++ divahook/config.h | 4 ++++ divahook/dllmain.c | 2 +- divahook/slider.c | 7 ++++++- divahook/slider.h | 8 +++++++- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/divahook/config.c b/divahook/config.c index 1b52c3e..e639874 100644 --- a/divahook/config.c +++ b/divahook/config.c @@ -12,6 +12,13 @@ #include "platform/config.h" #include "platform/platform.h" +void slider_config_load(struct slider_config *cfg, const wchar_t *filename) { + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"slider", L"enable", 1, filename); +} + void diva_hook_config_load( struct diva_hook_config *cfg, const wchar_t *filename) @@ -22,4 +29,5 @@ void diva_hook_config_load( platform_config_load(&cfg->platform, filename); amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); + slider_config_load(&cfg->slider, filename); } diff --git a/divahook/config.h b/divahook/config.h index c7ca22f..3e484d5 100644 --- a/divahook/config.h +++ b/divahook/config.h @@ -6,14 +6,18 @@ #include "board/sg-reader.h" +#include "divahook/slider.h" + #include "platform/platform.h" struct diva_hook_config { struct platform_config platform; struct amex_config amex; struct aime_config aime; + struct slider_config slider; }; +void slider_config_load(struct slider_config *cfg, const wchar_t *filename); void diva_hook_config_load( struct diva_hook_config *cfg, const wchar_t *filename); diff --git a/divahook/dllmain.c b/divahook/dllmain.c index a194f84..1a6449a 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -63,7 +63,7 @@ static DWORD CALLBACK diva_pre_startup(void) return EXIT_FAILURE; } - hr = slider_hook_init(); + hr = slider_hook_init(&diva_hook_cfg.slider); if (FAILED(hr)) { return EXIT_FAILURE; diff --git a/divahook/slider.c b/divahook/slider.c index fe114cb..cc1273d 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -37,8 +37,13 @@ static struct uart slider_uart; static uint8_t slider_written_bytes[520]; static uint8_t slider_readable_bytes[520]; -HRESULT slider_hook_init(void) +HRESULT slider_hook_init(const struct slider_config *cfg) { + assert(cfg != NULL); + if (!cfg->enable) { + return S_FALSE; + } + InitializeCriticalSection(&slider_lock); uart_init(&slider_uart, 11); diff --git a/divahook/slider.h b/divahook/slider.h index 08fe430..7053e04 100644 --- a/divahook/slider.h +++ b/divahook/slider.h @@ -2,4 +2,10 @@ #include -HRESULT slider_hook_init(void); +#include + +struct slider_config { + bool enable; +}; + +HRESULT slider_hook_init(const struct slider_config *cfg); From 01e1e1a7b808327e999c48e0ae73e6d2714a379d Mon Sep 17 00:00:00 2001 From: dogtopus Date: Sun, 29 Dec 2019 20:59:35 -0400 Subject: [PATCH 021/175] Diva: Fix slider detection & sensor order --- board/slider-cmd.h | 4 +++- divahook/slider.c | 20 +++++++++++++++++++- divaio/divaio.c | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/board/slider-cmd.h b/board/slider-cmd.h index 5423a9f..e904967 100644 --- a/board/slider-cmd.h +++ b/board/slider-cmd.h @@ -5,9 +5,11 @@ enum { SLIDER_CMD_AUTO_SCAN = 0x01, SLIDER_CMD_SET_LED_CHUNI = 0x02, + SLIDER_CMD_SET_LED_DIVA = 0x02, SLIDER_CMD_AUTO_SCAN_START = 0x03, SLIDER_CMD_AUTO_SCAN_STOP = 0x04, - SLIDER_CMD_SET_LED_DIVA = 0x9, + SLIDER_CMD_DIVA_UNK_09 = 0x09, + SLIDER_CMD_DIVA_UNK_0A = 0x0A, SLIDER_CMD_RESET = 0x10, SLIDER_CMD_GET_BOARD_INFO = 0xF0, }; diff --git a/divahook/slider.c b/divahook/slider.c index cc1273d..04d3874 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -24,6 +24,7 @@ static HRESULT slider_handle_irp(struct irp *irp); static HRESULT slider_handle_irp_locked(struct irp *irp); static HRESULT slider_req_dispatch(const union slider_req_any *req); +static HRESULT slider_req_nop(uint8_t cmd); static HRESULT slider_req_reset(void); static HRESULT slider_req_get_board_info(void); static HRESULT slider_req_auto_scan_start(void); @@ -146,6 +147,10 @@ static HRESULT slider_req_dispatch(const union slider_req_any *req) case SLIDER_CMD_SET_LED_DIVA: return slider_req_set_led(&req->set_led); + case SLIDER_CMD_DIVA_UNK_09: + case SLIDER_CMD_DIVA_UNK_0A: + return slider_req_nop(req->hdr.cmd); + default: dprintf("Unhandled command %02x\n", req->hdr.cmd); @@ -153,6 +158,19 @@ static HRESULT slider_req_dispatch(const union slider_req_any *req) } } +static HRESULT slider_req_nop(uint8_t cmd) +{ + struct slider_hdr resp; + + dprintf("Diva slider: No-op cmd 0x%02x\n", cmd); + + resp.sync = SLIDER_FRAME_SYNC; + resp.cmd = cmd; + resp.nbytes = 0; + + return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); +} + static HRESULT slider_req_reset(void) { struct slider_hdr resp; @@ -181,7 +199,7 @@ static HRESULT slider_req_get_board_info(void) strcpy_s( resp.version, sizeof(resp.version), - "15275 \xA0" "06712\xFF" "\x90"); + "15275 \xA0" "06687\xFF" "\x90"); return slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp)); } diff --git a/divaio/divaio.c b/divaio/divaio.c index 6817e86..e1dc7f9 100644 --- a/divaio/divaio.c +++ b/divaio/divaio.c @@ -122,7 +122,7 @@ static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx) pressure_val = 0; } - memset(&pressure[28 - 4 * i], pressure_val, 4); + memset(&pressure[4 * i], pressure_val, 4); } callback(pressure); From 300338220a2b57bc36c695d6165c68dcd9c67584 Mon Sep 17 00:00:00 2001 From: dogtopus Date: Sun, 29 Dec 2019 22:20:54 -0400 Subject: [PATCH 022/175] Style fixes --- divahook/config.c | 3 ++- divahook/slider.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/divahook/config.c b/divahook/config.c index e639874..7c21e46 100644 --- a/divahook/config.c +++ b/divahook/config.c @@ -12,7 +12,8 @@ #include "platform/config.h" #include "platform/platform.h" -void slider_config_load(struct slider_config *cfg, const wchar_t *filename) { +void slider_config_load(struct slider_config *cfg, const wchar_t *filename) +{ assert(cfg != NULL); assert(filename != NULL); diff --git a/divahook/slider.c b/divahook/slider.c index 04d3874..539fed7 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -41,6 +41,7 @@ static uint8_t slider_readable_bytes[520]; HRESULT slider_hook_init(const struct slider_config *cfg) { assert(cfg != NULL); + if (!cfg->enable) { return S_FALSE; } @@ -162,7 +163,7 @@ static HRESULT slider_req_nop(uint8_t cmd) { struct slider_hdr resp; - dprintf("Diva slider: No-op cmd 0x%02x\n", cmd); + dprintf("Diva slider: No-op cmd 0x%#02x\n", cmd); resp.sync = SLIDER_FRAME_SYNC; resp.cmd = cmd; From 356e8ae20cdc6008ba820390bcfed4f7eda2b1b7 Mon Sep 17 00:00:00 2001 From: dogtopus Date: Sun, 29 Dec 2019 22:48:35 -0400 Subject: [PATCH 023/175] Unify LED command name for Diva/Chunithm slider --- board/slider-cmd.h | 3 +-- chunihook/slider.c | 2 +- divahook/slider.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/board/slider-cmd.h b/board/slider-cmd.h index e904967..acc2608 100644 --- a/board/slider-cmd.h +++ b/board/slider-cmd.h @@ -4,8 +4,7 @@ enum { SLIDER_CMD_AUTO_SCAN = 0x01, - SLIDER_CMD_SET_LED_CHUNI = 0x02, - SLIDER_CMD_SET_LED_DIVA = 0x02, + SLIDER_CMD_SET_LED = 0x02, SLIDER_CMD_AUTO_SCAN_START = 0x03, SLIDER_CMD_AUTO_SCAN_STOP = 0x04, SLIDER_CMD_DIVA_UNK_09 = 0x09, diff --git a/chunihook/slider.c b/chunihook/slider.c index 13b9850..f4a2bfd 100644 --- a/chunihook/slider.c +++ b/chunihook/slider.c @@ -139,7 +139,7 @@ static HRESULT slider_req_dispatch(const union slider_req_any *req) case SLIDER_CMD_GET_BOARD_INFO: return slider_req_get_board_info(); - case SLIDER_CMD_SET_LED_CHUNI: + case SLIDER_CMD_SET_LED: return slider_req_set_led(&req->set_led); case SLIDER_CMD_AUTO_SCAN_START: diff --git a/divahook/slider.c b/divahook/slider.c index 539fed7..c9bd1dc 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -145,7 +145,7 @@ static HRESULT slider_req_dispatch(const union slider_req_any *req) case SLIDER_CMD_AUTO_SCAN_STOP: return slider_req_auto_scan_stop(); - case SLIDER_CMD_SET_LED_DIVA: + case SLIDER_CMD_SET_LED: return slider_req_set_led(&req->set_led); case SLIDER_CMD_DIVA_UNK_09: From 00819a2721847c2fa3d907fd2391181863fac491 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Thu, 13 Feb 2020 03:14:59 +0000 Subject: [PATCH 024/175] platform/vfs.c: allow nthome and option path hooks without ending back-slash - This actually fixed option loading. - Ongeki uses forward-slashes for Y:/SDDT/log --- hooklib/path.h | 6 ++++++ platform/vfs.c | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/hooklib/path.h b/hooklib/path.h index 779e013..ccbd77a 100644 --- a/hooklib/path.h +++ b/hooklib/path.h @@ -2,6 +2,7 @@ #include +#include #include typedef HRESULT (*path_hook_t)( @@ -11,3 +12,8 @@ typedef HRESULT (*path_hook_t)( HRESULT path_hook_push(path_hook_t hook); int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count); + +static inline bool path_is_separator_w(wchar_t c) +{ + return c == L'\\' || c == L'/'; +} diff --git a/platform/vfs.c b/platform/vfs.c index 0813a6d..d356d7f 100644 --- a/platform/vfs.c +++ b/platform/vfs.c @@ -28,10 +28,10 @@ static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes); static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes); static wchar_t vfs_nthome_real[MAX_PATH]; -static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser\\"; +static const wchar_t vfs_nthome[] = L"C:\\Documents and Settings\\AppUser"; static const size_t vfs_nthome_len = _countof(vfs_nthome) - 1; -static const wchar_t vfs_option[] = L"C:\\Mount\\Option\\"; +static const wchar_t vfs_option[] = L"C:\\Mount\\Option"; static const size_t vfs_option_len = _countof(vfs_option) - 1; static const struct reg_hook_val vfs_reg_vals[] = { @@ -190,7 +190,7 @@ static void vfs_fixup_path(wchar_t *path, size_t max_count) count = wcslen(path); } - if (path[count - 1] == L'\\' || path[count - 1] == L'/') { + if (path_is_separator_w(path[count - 1])) { return; } @@ -268,7 +268,7 @@ static HRESULT vfs_path_hook(const wchar_t *src, wchar_t *dest, size_t *count) assert(src != NULL); assert(count != NULL); - if (src[0] == L'\0' || src[1] != L':' || src[2] != L'\\') { + if (src[0] == L'\0' || src[1] != L':' || !path_is_separator_w(src[2])) { return S_FALSE; } @@ -315,6 +315,7 @@ static HRESULT vfs_path_hook_nthome( { size_t required; size_t redir_len; + size_t shift; assert(src != NULL); assert(count != NULL); @@ -325,10 +326,20 @@ static HRESULT vfs_path_hook_nthome( return S_FALSE; } - /* Cut off the matched prefix, add the replaced prefix, count NUL */ + /* Check if the character after vfs_nthome is a separator or the end of + the string */ + if (!path_is_separator_w(src[vfs_nthome_len]) && + src[vfs_nthome_len] != L'\0') + { + return S_FALSE; + } + + /* Cut off the matched \, add the replaced prefix, count NUL */ + + shift = path_is_separator_w(src[vfs_nthome_len]) ? 1 : 0; redir_len = wcslen(vfs_nthome_real); - required = wcslen(src) - vfs_nthome_len + redir_len + 1; + required = wcslen(src) - vfs_nthome_len - shift + redir_len + 1; if (dest != NULL) { if (required > *count) { @@ -336,7 +347,7 @@ static HRESULT vfs_path_hook_nthome( } wcscpy_s(dest, *count, vfs_nthome_real); - wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_nthome_len); + wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_nthome_len + shift); } *count = required; @@ -351,6 +362,7 @@ static HRESULT vfs_path_hook_option( { size_t required; size_t redir_len; + size_t shift; assert(src != NULL); assert(count != NULL); @@ -361,10 +373,20 @@ static HRESULT vfs_path_hook_option( return S_FALSE; } - /* Cut off the matched prefix, add the replaced prefix, count NUL */ + /* Check if the character after vfs_nthome is a separator or the end of + the string */ + if (!path_is_separator_w(src[vfs_option_len]) && + src[vfs_option_len] != L'\0') + { + return S_FALSE; + } + + /* Cut off the matched \, add the replaced prefix, count NUL */ + + shift = path_is_separator_w(src[vfs_option_len]) ? 1 : 0; redir_len = wcslen(vfs_config.option); - required = wcslen(src) - vfs_option_len + redir_len + 1; + required = wcslen(src) - vfs_option_len - shift + redir_len + 1; if (dest != NULL) { if (required > *count) { @@ -372,7 +394,7 @@ static HRESULT vfs_path_hook_option( } wcscpy_s(dest, *count, vfs_config.option); - wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_option_len); + wcscpy_s(dest + redir_len, *count - redir_len, src + vfs_option_len + shift); } *count = required; From 533683e595c072c41a9a1633bf569c710e978974 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Fri, 14 Feb 2020 07:25:46 +0000 Subject: [PATCH 025/175] hooklib/path.c: add function to hook apply path hooks to another module --- hooklib/path.c | 8 +++++++- hooklib/path.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hooklib/path.c b/hooklib/path.c index 864d2e2..0d86183 100644 --- a/hooklib/path.c +++ b/hooklib/path.c @@ -252,8 +252,13 @@ static void path_hook_init(void) path_hook_initted = true; InitializeCriticalSection(&path_hook_lock); + path_hook_insert_hooks(NULL); +} + +void path_hook_insert_hooks(HMODULE target) +{ hook_table_apply( - NULL, + target, "kernel32.dll", path_hook_syms, _countof(path_hook_syms)); @@ -668,6 +673,7 @@ static DWORD WINAPI hook_GetFileAttributesW(const wchar_t *lpFileName) } result = next_GetFileAttributesW(trans ? trans : lpFileName); + free(trans); return result; diff --git a/hooklib/path.h b/hooklib/path.h index ccbd77a..3a58bb7 100644 --- a/hooklib/path.h +++ b/hooklib/path.h @@ -11,6 +11,7 @@ typedef HRESULT (*path_hook_t)( size_t *count); HRESULT path_hook_push(path_hook_t hook); +void path_hook_insert_hooks(HMODULE target); int path_compare_w(const wchar_t *string1, const wchar_t *string2, size_t count); static inline bool path_is_separator_w(wchar_t c) From a1f093a31765fcd996f400aa6cc07f4da23c9114 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Fri, 13 Mar 2020 05:30:18 +0000 Subject: [PATCH 026/175] mu3hook/unity.c: hook Unity plugin DLLs - Unity loads plugin DLLs and Mono dynamically. - Ongeki uses Criware for videos so hook the Criware plugin DLL so the path hooks load the videos from the hooked paths. --- mu3hook/dllmain.c | 2 + mu3hook/meson.build | 2 + mu3hook/unity.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ mu3hook/unity.h | 3 ++ 4 files changed, 102 insertions(+) create mode 100644 mu3hook/unity.c create mode 100644 mu3hook/unity.h diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index c34bc51..cc6a980 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -11,6 +11,7 @@ #include "mu3hook/config.h" #include "mu3hook/io4.h" +#include "mu3hook/unity.h" #include "platform/platform.h" @@ -33,6 +34,7 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Hook Win32 APIs */ serial_hook_init(); + unity_hook_init(); /* Initialize emulation hooks */ diff --git a/mu3hook/meson.build b/mu3hook/meson.build index 66293dc..4f3ba71 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -24,5 +24,7 @@ shared_library( 'dllmain.c', 'io4.c', 'io4.h', + 'unity.h', + 'unity.c', ], ) diff --git a/mu3hook/unity.c b/mu3hook/unity.c new file mode 100644 index 0000000..efefc32 --- /dev/null +++ b/mu3hook/unity.c @@ -0,0 +1,95 @@ +#include + +#include + +#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; +} diff --git a/mu3hook/unity.h b/mu3hook/unity.h new file mode 100644 index 0000000..99c3bd9 --- /dev/null +++ b/mu3hook/unity.h @@ -0,0 +1,3 @@ +#pragma once + +void unity_hook_init(void); From 86f271731687250bfe82c894e89acc6ae805ca8f Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 23 Mar 2020 06:53:48 +0000 Subject: [PATCH 027/175] mu3hook/dllmain.c: initialize graphics hook --- mu3hook/config.c | 4 ++++ mu3hook/config.h | 3 +++ mu3hook/dllmain.c | 1 + 3 files changed, 8 insertions(+) diff --git a/mu3hook/config.c b/mu3hook/config.c index c2391c8..c611131 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -3,6 +3,9 @@ #include "board/config.h" +#include "hooklib/config.h" +#include "hooklib/gfx.h" + #include "mu3hook/config.h" #include "platform/config.h" @@ -16,4 +19,5 @@ void mu3_hook_config_load( platform_config_load(&cfg->platform, filename); aime_config_load(&cfg->aime, filename); + gfx_config_load(&cfg->gfx, filename); } diff --git a/mu3hook/config.h b/mu3hook/config.h index cd1def4..e6ef4df 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -4,11 +4,14 @@ #include "board/config.h" +#include "hooklib/gfx.h" + #include "platform/config.h" struct mu3_hook_config { struct platform_config platform; struct aime_config aime; + struct gfx_config gfx; }; void mu3_hook_config_load( diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index cc6a980..bf24bfd 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -33,6 +33,7 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Hook Win32 APIs */ + gfx_hook_init(&mu3_hook_cfg.gfx); serial_hook_init(); unity_hook_init(); From c037dd73e4635b119fce5e3edb015c98f3963886 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 23 Mar 2020 06:59:05 +0000 Subject: [PATCH 028/175] hooklib/gfx.c: add dynamic DLL hook on d3d9.dll - Somehow doing `dll_hook_push` on `d3d9.dll` fixes the path hooks for Ongeki's `mu3.exe` --- hooklib/gfx.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/hooklib/gfx.c b/hooklib/gfx.c index 531a101..f442de9 100644 --- a/hooklib/gfx.c +++ b/hooklib/gfx.c @@ -9,10 +9,13 @@ #include "hook/table.h" #include "hooklib/config.h" +#include "hooklib/dll.h" #include "hooklib/gfx.h" #include "util/dprintf.h" +typedef IDirect3D9 * (WINAPI *Direct3DCreate9_t)(UINT sdk_ver); + static HRESULT STDMETHODCALLTYPE my_CreateDevice( IDirect3D9 *self, UINT adapter, @@ -22,7 +25,7 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev); static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver); -static IDirect3D9 * (WINAPI *next_Direct3DCreate9)(UINT sdk_ver); +static Direct3DCreate9_t next_Direct3DCreate9; static HRESULT gfx_frame_window(HWND hwnd); static struct gfx_config gfx_config; @@ -37,6 +40,8 @@ static const struct hook_symbol gfx_hooks[] = { void gfx_hook_init(const struct gfx_config *cfg) { + HMODULE d3d9; + assert(cfg != NULL); if (!cfg->enable) { @@ -45,6 +50,31 @@ void gfx_hook_init(const struct gfx_config *cfg) memcpy(&gfx_config, cfg, sizeof(*cfg)); hook_table_apply(NULL, "d3d9.dll", gfx_hooks, _countof(gfx_hooks)); + + if (next_Direct3DCreate9 == NULL) { + d3d9 = LoadLibraryW(L"d3d9.dll"); + + if (d3d9 == NULL) { + dprintf("Gfx: d3d9.dll not found or failed initialization\n"); + + goto fail; + } + + next_Direct3DCreate9 = (Direct3DCreate9_t) GetProcAddress(d3d9, "Direct3DCreate9"); + + if (next_Direct3DCreate9 == NULL) { + dprintf("Gfx: Direct3DCreate9 not found in loaded d3d9.dll\n"); + + FreeLibrary(d3d9); + + goto fail; + } + } + + dll_hook_push(NULL, L"d3d9.dll", gfx_hooks, _countof(gfx_hooks)); + +fail: + return; } static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver) @@ -56,6 +86,12 @@ static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver) dprintf("Gfx: Direct3DCreate9 hook hit\n"); + if (next_Direct3DCreate9 == NULL) { + dprintf("Gfx: next_Direct3DCreate9 == NULL\n"); + + goto fail; + } + api = next_Direct3DCreate9(sdk_ver); if (api == NULL) { From 776cf50bd0213f9d35b352540bf9db9c636584eb Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 23 Mar 2020 07:31:58 +0000 Subject: [PATCH 029/175] mu3hook/dllmain.c: fix hook initialization order to fix path hook - If the Unity `LoadLibraryW` hook is done too early, then it somehow interferes with the other DLL and hook table IAT hooks. Workaround this by moving the Unity `LoadLibraryW` hook initialization after all other hooks are done. - Also, correct the platform ID to the ALLS HX --- mu3hook/dllmain.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index bf24bfd..7161261 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -35,14 +35,13 @@ static DWORD CALLBACK mu3_pre_startup(void) gfx_hook_init(&mu3_hook_cfg.gfx); serial_hook_init(); - unity_hook_init(); /* Initialize emulation hooks */ hr = platform_hook_init( &mu3_hook_cfg.platform, "SDDT", - "AAV2", + "ACA1", mu3_hook_mod); if (FAILED(hr)) { @@ -67,6 +66,13 @@ static DWORD CALLBACK mu3_pre_startup(void) return hr; } + /* Initialize Unity native plugin DLL hooks + + There seems to be an issue with other DLL hooks if `LoadLibraryW` is + hooked earlier in the `mu3hook` initialization. */ + + unity_hook_init(); + /* Initialize debug helpers */ spike_hook_init(L".\\segatools.ini"); From e5b06037221cdde00cb98bdb811a525cfa52931b Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 18 May 2020 23:01:29 +0000 Subject: [PATCH 030/175] board/io4.c: add system status fix for Ongeki --- board/io4.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/board/io4.c b/board/io4.c index 9140ee4..c956700 100644 --- a/board/io4.c +++ b/board/io4.c @@ -193,11 +193,17 @@ static HRESULT io4_handle_write(struct irp *irp) case IO4_CMD_SET_COMM_TIMEOUT: dprintf("USB I/O: Set comm timeout\n"); + // Ongeki Summer expects the system status to be 0x30 at this point + io4_system_status = 0x30; + return S_OK; case IO4_CMD_SET_SAMPLING_COUNT: dprintf("USB I/O: Set sampling count\n"); + // Ongeki Summer expects the system status to be 0x30 at this point + io4_system_status = 0x30; + return S_OK; case IO4_CMD_CLEAR_BOARD_STATUS: From 4343177e42f61ed5212889a43a466301e3bfc0dc Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 18 May 2020 23:02:11 +0000 Subject: [PATCH 031/175] platform/amvideo.c: remove left-over brace from debug messages --- platform/amvideo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/amvideo.c b/platform/amvideo.c index ef4b213..e06931e 100644 --- a/platform/amvideo.c +++ b/platform/amvideo.c @@ -153,14 +153,14 @@ HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod) static int amDllVideoOpen(void *ctx) { - dprintf("AmVideo: %s)\n", __func__); + dprintf("AmVideo: %s\n", __func__); return 0; } static int amDllVideoClose(void *ctx) { - dprintf("AmVideo: %s)\n", __func__); + dprintf("AmVideo: %s\n", __func__); return 0; } From 008d53dd458996e77630e483ae4c22ba0add4598 Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Mon, 18 May 2020 23:03:25 +0000 Subject: [PATCH 032/175] hooklib/path.c: hook Ex variants of CreateDirectory --- hooklib/path.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/hooklib/path.c b/hooklib/path.c index 0d86183..6d27efc 100644 --- a/hooklib/path.c +++ b/hooklib/path.c @@ -27,6 +27,16 @@ static BOOL WINAPI hook_CreateDirectoryW( const wchar_t *lpFileName, SECURITY_ATTRIBUTES *lpSecurityAttributes); +static BOOL WINAPI hook_CreateDirectoryExA( + const char *lpTemplateDirectory, + const char *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static BOOL WINAPI hook_CreateDirectoryExW( + const wchar_t *lpTemplateDirectory, + const wchar_t *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + static HANDLE WINAPI hook_CreateFileA( const char *lpFileName, uint32_t dwDesiredAccess, @@ -93,6 +103,16 @@ static BOOL (WINAPI *next_CreateDirectoryW)( const wchar_t *lpFileName, SECURITY_ATTRIBUTES *lpSecurityAttributes); +static BOOL (WINAPI *next_CreateDirectoryExA)( + const char *lpTemplateDirectory, + const char *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + +static BOOL (WINAPI *next_CreateDirectoryExW)( + const wchar_t *lpTemplateDirectory, + const wchar_t *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes); + static HANDLE (WINAPI *next_CreateFileA)( const char *lpFileName, uint32_t dwDesiredAccess, @@ -160,6 +180,14 @@ static const struct hook_symbol path_hook_syms[] = { .name = "CreateDirectoryW", .patch = hook_CreateDirectoryW, .link = (void **) &next_CreateDirectoryW, + }, { + .name = "CreateDirectoryExA", + .patch = hook_CreateDirectoryExA, + .link = (void **) &next_CreateDirectoryExA, + }, { + .name = "CreateDirectoryExW", + .patch = hook_CreateDirectoryExW, + .link = (void **) &next_CreateDirectoryExW, }, { .name = "CreateFileA", .patch = hook_CreateFileA, @@ -470,6 +498,54 @@ static BOOL WINAPI hook_CreateDirectoryW( return ok; } +static BOOL WINAPI hook_CreateDirectoryExA( + const char *lpTemplateDirectory, + const char *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes) +{ + char *trans; + BOOL ok; + + ok = path_transform_a(&trans, lpNewDirectory); + + if (!ok) { + return FALSE; + } + + ok = next_CreateDirectoryExA( + lpTemplateDirectory, + trans ? trans : lpNewDirectory, + lpSecurityAttributes); + + free(trans); + + return ok; +} + +static BOOL WINAPI hook_CreateDirectoryExW( + const wchar_t *lpTemplateDirectory, + const wchar_t *lpNewDirectory, + SECURITY_ATTRIBUTES *lpSecurityAttributes) +{ + wchar_t *trans; + BOOL ok; + + ok = path_transform_w(&trans, lpNewDirectory); + + if (!ok) { + return FALSE; + } + + ok = next_CreateDirectoryExW( + lpTemplateDirectory, + trans ? trans : lpNewDirectory, + lpSecurityAttributes); + + free(trans); + + return ok; +} + /* Don't pull in the entire iohook framework just for CreateFileA/CreateFileW */ static HANDLE WINAPI hook_CreateFileA( From 187eabe22597a3c21c1d6ac14824a48385840900 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 14 Dec 2019 11:15:09 -0500 Subject: [PATCH 033/175] Document common INI settings --- doc/config/common.md | 440 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 doc/config/common.md diff --git a/doc/config/common.md b/doc/config/common.md new file mode 100644 index 0000000..6506a59 --- /dev/null +++ b/doc/config/common.md @@ -0,0 +1,440 @@ +# Introduction + +This file describes configuration settings for Segatools that are common to +all games. + +Keyboard binding settings use [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). + +# `[aime]` + +Controls emulation of the Aime card reader assembly. + +## `enable` + +Default: `1` + +Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime +reader (COM port number varies by game). + +## `aimePath` + +Default: `DEVICE\aime.txt` + +Path to a text file containing a classic Aime IC card ID. **This does not +currently work**. + +## `felicaPath` + +Default: `DEVICE\felica.txt` + +Path to a text file containing a FeliCa e-cash card IDm serial number. + +## `felicaGen` + +Default: `1` + +Whether to generate a random FeliCa ID if the file at `felicaPath` does not +exist. + +## `scan` + +Default: `0x0D` (`VK_RETURN`) + +Virtual-key code. If this button is **held** then the emulated IC card reader +emulates an IC card in its proximity. A variety of different IC cards can be +emulated; the exact choice of card that is emulated depends on the presence or +absence of the configured card ID files. + +# `[amvideo]` + +Controls the `amvideo.dll` stub built into Segatools. This is a DLL that is +normally present on the SEGA operating system image which is responsible for +changing screen resolution and orientation. + +## `enable` + +Default: `1` + +Enable stub `amvideo.dll`. Disable to use a real `amvideo.dll` build. Note that +you must have the correct registry settings installed and you must use the +version of `amvideo.dll` that matches your GPU vendor (since these DLLs make +use of vendor-specific APIs). + +# `[clock]` + +Controls hooks for Windows time-of-day APIs. + +## `timezone` + +Default: `1` + +Make the system time zone appear to be JST. SEGA games malfunction in strange +ways if the system time zone is not JST. There should not be any reason to +disable this hook other than possible implementation bugs, but the option is +provided if you need it. + +## `timewarp` + +Default: `0` + +Experimental time-of-day warping hook that skips over the hardcoded server +maintenance period. Causes an incorrect in-game time-of-day to be reported. +Better solutions for this problem exist and this feature will probably be +removed soon. + +## `writeable` + +Default: `0` + +Allow game to adjust system clock and time zone settings. This should normally +be left at `0`, but the option is provided if you need it. + +# `[dns]` + +Controls redirection of network server hostname lookups + +## `default` + +Default: `localhost` + +Controls hostname of all of the common network services servers, unless +overriden by a specific setting below. Most users will only need to change this +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). + +## `router` + +Default: Empty string (i.e. use value from `default` setting) + +Overrides the target of the `tenporouter.loc` and `bbrouter.loc` hostname +lookups. + +## `startup` + +Default: Empty string (i.e. use value from `default` setting) + +Overrides the target of the `naominet.jp` host lookup. + +## `billing` + +Default: Empty string (i.e. use value from `default` setting) + +Overrides the target of the `ib.naominet.jp` host lookup. + +## `aimedb` + +Default: Empty string (i.e. use value from `default` setting) + +Overrides the target of the `aime.naominet.jp` host lookup. + +# `[ds]` + +Controls emulation of the "DS (Dallas Semiconductor) EEPROM" chip on the AMEX +PCIe board. This is a small (32 byte) EEPROM that contains serial number and +region code information. It is not normally written to outside of inital +factory provisioning of a Sega Nu. + +## `enable` + +Default: `1` + +Enable DS EEPROM emulation. Disable to use the DS EEPROM chip on a real AMEX. + +## `region` + +Default: `1` + +AMEX Board region code. This appears to be a bit mask? + +- `1`: Japan +- `2`: USA? (Dead code, not used) +- `4`: Export +- `8`: China + +## `serialNo` + +Default `AAVE-01A99999999` + +"MAIN ID" serial number. First three characters are hardware series: + +- `AAV`: Nu-series +- `AAW`: NuSX-series +- `ACA`: ALLS-series + +# `[eeprom]` + +Controls emulation of the bulk EEPROM on the AMEX PCIe board. This chip stores +status and configuration information. + +## `enable` + +Default: `1` + +Enable bulk EEPROM emulation. Disable to use the bulk EEPROM chip on a real +AMEX. + +## `path` + +Default: `DEVICE\eeprom.bin` + +Path to the storage file for EEPROM emulation. This file is automatically +created and initialized with a suitable number of zero bytes if it does not +already exist. + +# `[gpio]` + +Configure emulation of the AMEX PCIe GPIO (General Purpose Input Output) +controller. + +## `enable` + +Default: `1` + +Enable GPIO emulation. Disable to use the GPIO controller on a real AMEX. + +## `sw1` + +Default `0x70` (`VK_F1`) + +Keyboard binding for Nu chassis SW1 button (alternative Test) + +## `sw2` + +Default `0x71` (`VK_F2`) + +Keyboard binding for Nu chassis SW2 button (alternative Service) + +## `dipsw1` .. `dipsw8` + +Defaults: `1`, `0`, `0`, `0`, `0`, `0`, `0`, `0` + +Nu chassis DIP switch settings: + +- Switch 1: Game-specific, but usually controls the "distribution server" + setting. Exactly one arcade machine on a cabinet router must be set to the + Server setting. + - `0`: Client + - `1`: Server +- Switch 2,3: Game-specific. + - Used by Mario&Sonic to configure cabinet ID, possibly other games. +- Switch 4: Screen orientation. Only used by the Nu system startup program. + - `0`: YOKO/Horizontal + - `1`: TATE/Vertical +- Switch 5,6,7: Screen resolution. Only used by the Nu system startup program. + - `000`: No change + - `100`: 640x480 + - `010`: 1024x600 + - `110`: 1024x768 + - `001`: 1280x720 + - `101`: 1280x1024 + - `110`: 1360x768 + - `111`: 1920x1080 +- Switch 8: Game-specific. Not used in any shipping game. + +# `[hwmon]` + +Configure stub implementation of the platform hardware monitor driver. The +real implementation of this driver monitors CPU temperatures by reading from +Intel Model Specific Registers, which is an action that is only permitted from +kernel mode. + +## `enable` + +Default `1` + +Enable hwmon emulation. Disable to use the real hwmon driver. + +# `[jvs]` + +Configure emulation of the AMEX PCIe JVS *controller* (not IO board!) + +## `enable` + +Default `1` + +Enable JVS port emulation. Disable to use the JVS port on a real AMEX. + +# `[keychip]` + +Configure keychip emulation. + +## `enable` + +Enable keychip emulation. Disable to use a real keychip. + +## `id` + +Default: `A69E-01A88888888` + +Keychip serial number. Keychip serials observed in the wild follow this +pattern: `A6xE-01Ayyyyyyyy`. + +## `gameId` + +Default: (Varies depending on game) + +Override the game's four-character model code. Changing this from the game's +expected value will probably just cause a system error. + +## `platformId` + +Default: (Varies depending on game) + +Override the game's four-character platform code (e.g. `AAV2` for Nu 2). This +is actually supposed to be a separate three-character `platformId` and +integer `modelType` setting, but they are combined here for convenience. Valid +values include: + +- `AAV0`: Nu 1 (Project DIVA) +- `AAV1`: Nu 1.1 (Chunithm) +- `AAV2`: Nu 2 (Initial D Zero) +- `AAW0`: NuSX 1 +- `AAW1`: NuSX 1.1 +- `ACA0`: ALLS UX +- `ACA1`: ALLS HX +- `ACA2`: ALLS HX2 + +## `region` + +Default: `1` + +Override the keychip's region code. Most games seem to pay attention to the +DS EEPROM region code and not the keychip region code, and this seems to be +a bit mask that controls which Nu PCB region codes this keychip is authorized +for. So it probably only affects the system software and not the game software. +Bit values are: + +- 1: JPN: Japan +- 2: USA (unused) +- 3: EXP: Export (for Asian markets) +- 4: CHS: China (Simplified Chinese?) + +## `systemFlag` + +Default: `0x64` + +An 8-bit bitfield of unclear meaning. The least significant bit indicates a +developer dongle, I think? Changing this doesn't seem to have any effect on +anything other than Project DIVA. + +Other values observed in the wild: + +- `0x04`: SDCH, SDCA +- `0x20`: SDCA + +## `subnet` + +Default `192.168.100.0` + +The LAN IP range that the game will expect. The prefix length is hardcoded into +the game program: for some games this is `/24`, for others it is `/20`. + +# `[netenv]` + +Configure network environment virtualization. This module helps bypass various +restrictions placed upon the game's LAN environment. + +## `enable` + +Default `1` + +Enable network environment virtualization. You may need to disable this if +you want to do any head-to-head play on your LAN. + +Note: The virtualized LAN IP range is taken from the emulated keychip's +`subnet` setting. + +## `addrSuffix` + +Default: `11` + +The final octet of the local host's IP address on the virtualized subnet (so, +if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the +local host's virtualized LAN IP is `192.168.32.11`). + +## `routerSuffix` + +Default: `1` + +The final octet of the default gateway's IP address on the virtualized subnet. + +## `macAddr` + +Default: `01:02:03:04:05:06` + +The MAC address of the virtualized Ethernet adapter. The exact value shouldn't +ever matter. + +# `[pcbid]` + +Configure Windows host name virtualization. The ALLS-series platform no longer +has an AMEX board, so the MAIN ID serial number is stored in the Windows +hostname. + +## `enable` + +Default: `1` + +Enable Windows host name virtualization. This is only needed for ALLS-platform +games (since the ALLS lacks an AMEX and therefore has no DS EEPROM, so it needs +another way to store the PCB serial), but it does no harm on games that run on +earlier hardware. + +## `serialNo` + +Default: `ACAE01A99999999` + +Set the Windows host name. This should be an ALLS MAIN ID, without the +hyphen (which is not a valid character in a Windows host name). + +# `[sram]` + +Configure emulation of the AMEX PCIe battery-backed SRAM. This stores +bookkeeping state and settings. This file is automatically created and +initialized with a suitable number of zero bytes if it does not already exist. + +## `enable` + +Default `1` + +Enable SRAM emulation. Disable to use the SRAM on a real AMEX. + +## `path` + +Default `DEVICE\sram.bin` + +Path to the storage file for SRAM emulation. + +# `[vfs]` + +Configure Windows path redirection hooks. + +## `enable` + +Default: `1` + +Enable path redirection. + +## `amfs` + +Default: Empty string (causes a startup error) + +Configure the location of the SEGA AMFS volume. Stored on the `E` partition on +real hardware. + +## `appdata` + +Default: Empty string (causes a startup error) + +Configure the location of the SEGA "APPDATA" volume (nothing to do with the +Windows user's `%APPDATA%` directory). Stored on the `Y` partition on real +hardware. + +## `option` + +Default: Empty string + +Configure the location of the "Option" data mount point. This mount point is +optional (hence the name, probably) and contains directories which contain +minor over-the-air content updates. From 7949f54b309b80586755a6c708751b636fa771d6 Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 6 Jun 2020 14:49:04 -0400 Subject: [PATCH 034/175] Fix instructions in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad4fed3..bdbbf34 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Quick start on a Linux build host: ``` # Install Meson and a recent build of MinGW-w64, then: -$ meson --cross cross-build-32.txt _build32 +$ meson --cross cross-mingw-32.txt _build32 $ ninja -C _build32 -$ meson --cross cross-build-64.txt +$ meson --cross cross-mingw-64.txt _build64 $ ninja -C _build64 ``` From 164c74f16044ce1aa0296acdac480e8164ff1f1f Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 6 Jun 2020 15:08:46 -0400 Subject: [PATCH 035/175] divaio: Add config --- divaio/config.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ divaio/config.h | 16 +++++++++++++++ divaio/meson.build | 2 ++ 3 files changed, 69 insertions(+) create mode 100644 divaio/config.c create mode 100644 divaio/config.h diff --git a/divaio/config.c b/divaio/config.c new file mode 100644 index 0000000..886277e --- /dev/null +++ b/divaio/config.c @@ -0,0 +1,51 @@ +#include + +#include +#include +#include +#include + +#include "divaio/config.h" + +static const int diva_io_default_buttons[] = { + VK_RIGHT, VK_DOWN, VK_LEFT, VK_UP, VK_SPACE +}; + +static const int diva_io_default_slider[] = { + 'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P' +}; + +void diva_io_config_load( + struct diva_io_config *cfg, + const wchar_t *filename) +{ + wchar_t key[5]; + wchar_t cell[8]; + int i; + int c; + + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); + + for (i = 0 ; i < _countof(cfg->vk_buttons) ; i++) { + swprintf_s(key, _countof(key), L"key%i", i + 1); + cfg->vk_buttons[i] = GetPrivateProfileIntW( + L"buttons", + key, + diva_io_default_buttons[i], + filename); + } + + for (c = 0 ; c < _countof(cfg->vk_slider) ; c++) { + swprintf_s(cell, _countof(cell), L"cell%i", c + 1); + cfg->vk_slider[c] = GetPrivateProfileIntW( + L"slider", + cell, + diva_io_default_slider[c], + filename); + } +} diff --git a/divaio/config.h b/divaio/config.h new file mode 100644 index 0000000..86654c8 --- /dev/null +++ b/divaio/config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct diva_io_config { + uint8_t vk_buttons[5]; + uint8_t vk_slider[8]; + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_coin; +}; + +void diva_io_config_load( + struct diva_io_config *cfg, + const wchar_t *filename); diff --git a/divaio/meson.build b/divaio/meson.build index e8d498f..30eda9b 100644 --- a/divaio/meson.build +++ b/divaio/meson.build @@ -8,5 +8,7 @@ divaio_dll = shared_library( sources : [ 'divaio.c', 'divaio.h', + 'config.c', + 'config.h', ], ) From 82416115f5496dcd4ef83b1be989d1c26dfcc715 Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 6 Jun 2020 15:10:43 -0400 Subject: [PATCH 036/175] divaio: Wire up input config --- divaio/divaio.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/divaio/divaio.c b/divaio/divaio.c index e1dc7f9..4982482 100644 --- a/divaio/divaio.c +++ b/divaio/divaio.c @@ -6,6 +6,7 @@ #include #include "divaio/divaio.h" +#include "divaio/config.h" static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx); @@ -13,32 +14,33 @@ static bool diva_io_coin; static uint16_t diva_io_coins; static HANDLE diva_io_slider_thread; static bool diva_io_slider_stop_flag; +static struct diva_io_config diva_io_cfg; HRESULT diva_io_jvs_init(void) { + diva_io_config_load(&diva_io_cfg, L".\\segatools.ini"); + return S_OK; } void diva_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out) { - static const int opbtn_vk[] = { '1', '2' }; - static const int gamebtn_vk[] = { 'L', 'J', 'F', 'S', ' ' }; - uint8_t opbtn; uint8_t gamebtn; size_t i; opbtn = 0; - gamebtn = 0; - for (i = 0 ; i < _countof(opbtn_vk) ; i++) { - if (GetAsyncKeyState(opbtn_vk[i]) & 0x8000) { - opbtn |= 1 << i; - } + if (GetAsyncKeyState(diva_io_cfg.vk_test) & 0x8000) { + opbtn |= 1; } - for (i = 0 ; i < _countof(gamebtn_vk) ; i++) { - if (GetAsyncKeyState(gamebtn_vk[i]) & 0x8000) { + if (GetAsyncKeyState(diva_io_cfg.vk_service) & 0x8000) { + opbtn |= 2; + } + + for (i = 0 ; i < _countof(diva_io_cfg.vk_buttons) ; i++) { + if (GetAsyncKeyState(diva_io_cfg.vk_buttons[i]) & 0x8000) { gamebtn |= 1 << i; } } @@ -53,7 +55,7 @@ void diva_io_jvs_read_coin_counter(uint16_t *out) return; } - if (GetAsyncKeyState('3')) { + if (GetAsyncKeyState(diva_io_cfg.vk_coin) & 0x8000) { if (!diva_io_coin) { diva_io_coin = true; diva_io_coins++; @@ -103,10 +105,6 @@ void diva_io_slider_set_leds(const uint8_t *rgb) static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx) { - static const int keys[] = { - 'Q', 'W', 'E', 'R', 'U', 'I', 'O', 'P', - }; - diva_io_slider_callback_t callback; uint8_t pressure_val; uint8_t pressure[32]; @@ -116,7 +114,7 @@ static unsigned int __stdcall diva_io_slider_thread_proc(void *ctx) while (!diva_io_slider_stop_flag) { for (i = 0 ; i < 8 ; i++) { - if(GetAsyncKeyState(keys[i]) & 0x8000) { + if (GetAsyncKeyState(diva_io_cfg.vk_slider[i]) & 0x8000) { pressure_val = 20; } else { pressure_val = 0; From 30e1d033c563737378574ce038a9feb053880bcd Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Tue, 19 May 2020 22:09:38 +0200 Subject: [PATCH 037/175] Add support for specifing monitor number for dx9 games --- dist/chuni/segatools.ini | 2 ++ hooklib/config.c | 1 + hooklib/gfx.c | 4 +++- hooklib/gfx.h | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index 7fc0d82..06bfd51 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -30,6 +30,8 @@ subnet=192.168.100.0 windowed=1 ; Add a frame to the game window if running windowed. framed=1 +; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) +monitor=0 ; ----------------------------------------------------------------------------- ; Input settings diff --git a/hooklib/config.c b/hooklib/config.c index d52e6ff..1c4c9c4 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -15,4 +15,5 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"gfx", L"enable", 1, filename); cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename); cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); + cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); } diff --git a/hooklib/gfx.c b/hooklib/gfx.c index f442de9..30a576a 100644 --- a/hooklib/gfx.c +++ b/hooklib/gfx.c @@ -147,7 +147,9 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( gfx_frame_window(hwnd); } - return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev); + dprintf("Gfx: IDirect3D9:: Using Display No %x\n", gfx_config.monitor); + + return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev); } static HRESULT gfx_frame_window(HWND hwnd) diff --git a/hooklib/gfx.h b/hooklib/gfx.h index 5088e0e..9a7e27c 100644 --- a/hooklib/gfx.h +++ b/hooklib/gfx.h @@ -6,6 +6,7 @@ struct gfx_config { bool enable; bool windowed; bool framed; + int monitor; }; void gfx_hook_init(const struct gfx_config *cfg); From 76e5f9c3ee6cc21a9e9e19ae92cf1696d207a7ee Mon Sep 17 00:00:00 2001 From: Shiz Date: Fri, 7 Aug 2020 16:49:09 +0200 Subject: [PATCH 038/175] doc: fix ACA2 description and document ACA4 board type --- doc/config/common.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/config/common.md b/doc/config/common.md index 6506a59..6b911bd 100644 --- a/doc/config/common.md +++ b/doc/config/common.md @@ -293,7 +293,8 @@ values include: - `AAW1`: NuSX 1.1 - `ACA0`: ALLS UX - `ACA1`: ALLS HX -- `ACA2`: ALLS HX2 +- `ACA2`: ALLS UX (without dedicated GPU) +- `ACA4`: ALLS MX ## `region` From 840dd3f3df53af56e1623b6ee8e4373603b4ac98 Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Tue, 9 Feb 2021 01:24:46 +0100 Subject: [PATCH 039/175] mkdist: set correct shebang --- mkdist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdist b/mkdist index e596848..e8b9139 100755 --- a/mkdist +++ b/mkdist @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e From 2432b32338ce3cd1b6e17b401f2e0a2a0b813524 Mon Sep 17 00:00:00 2001 From: seika1 Date: Fri, 7 Aug 2020 20:31:40 -0700 Subject: [PATCH 040/175] eeprom: fill in actual geometry --- amex/eeprom.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/amex/eeprom.c b/amex/eeprom.c index e051b20..b9dd8b7 100644 --- a/amex/eeprom.c +++ b/amex/eeprom.c @@ -134,14 +134,12 @@ static HRESULT eeprom_ioctl_get_geometry(struct irp *irp) dprintf("EEPROM: Get geometry\n"); - /* Not the real values, just bullshitting something for now */ - memset(&out, 0, sizeof(out)); - out.Cylinders.QuadPart = 0x800; - out.MediaType = 0; - out.TracksPerCylinder = 1; - out.SectorsPerTrack = 2; - out.BytesPerSector = 4; + out.Cylinders.QuadPart = 1; + out.MediaType = FixedMedia; + out.TracksPerCylinder = 224; + out.SectorsPerTrack = 32; + out.BytesPerSector = 1; hr = iobuf_write(&irp->read, &out, sizeof(out)); From d2996c5271c8a0d77c8b1598ea394c5954c69e2c Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 9 Feb 2021 02:18:37 +0000 Subject: [PATCH 041/175] chunihook: Explicitly load dbghelp.dll so path hooks apply to it - This allows AMLib (in Chunithm and other games) to successfully initialize without requiring the game to be run as Administrator on first boot to create `C:\Users\AppUser\temp`. --- chunihook/dllmain.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index b543402..1a7dc39 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -30,6 +30,7 @@ static struct chuni_hook_config chuni_hook_cfg; static DWORD CALLBACK chuni_pre_startup(void) { HMODULE d3dc; + HMODULE dbghelp; HRESULT hr; dprintf("--- Begin chuni_pre_startup ---\n"); @@ -44,6 +45,16 @@ static DWORD CALLBACK chuni_pre_startup(void) dprintf("Failed to load shader compiler!\n"); } + /* Pin dbghelp so the path hooks apply to it. */ + + dbghelp = LoadLibraryW(L"dbghelp.dll"); + + if (dbghelp != NULL) { + dprintf("Pinned debug helper library, hMod=%p\n", dbghelp); + } else { + dprintf("Failed to load debug helper library!\n"); + } + /* Config load */ chuni_hook_config_load(&chuni_hook_cfg, L".\\segatools.ini"); From 921b976f2e77fb5d2cd84c9600ce4b7f4f76b468 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:00:16 +0200 Subject: [PATCH 042/175] Add Makefile with easy to use targets for building and dist packaging Similar to what we used on other projects. Makes the build process more streamlined --- Makefile | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package.mk | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 Makefile create mode 100644 Package.mk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a3c4197 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +V ?= @ + +.DEFAULT_GOAL := help + +BUILD_DIR := build +BUILD_DIR_32 := $(BUILD_DIR)/build32 +BUILD_DIR_64 := $(BUILD_DIR)/build64 +BUILD_DIR_DOCKER := $(BUILD_DIR)/docker +BUILD_DIR_ZIP := $(BUILD_DIR)/zip + +DOC_DIR := doc + +DIST_DIR := dist + +DOCKER_CONTAINER_NAME := "segatools-build" +DOCKER_IMAGE_NAME := "segatools:build" + +# ----------------------------------------------------------------------------- +# Targets +# ----------------------------------------------------------------------------- + +include Package.mk + +.PHONY: build # Build the project +build: + $(V)meson --cross cross-mingw-32.txt $(BUILD_DIR_32) + $(V)ninja -C $(BUILD_DIR_32) + $(V)meson --cross cross-mingw-64.txt $(BUILD_DIR_64) + $(V)ninja -C $(BUILD_DIR_64) + +.PHONY: dist # Build and create a zip distribution package +dist: build zip + +.PHONY: zip # Create a zip distribution pacakge +zip: $(BUILD_DIR_ZIP)/segatools.zip + +.PHONY: clean # Cleanup build output +clean: + $(V)rm -rf $(BUILD_DIR) subprojects/capnhook + +.PHONY: build-docker # Build the project in a docker container +build-docker: + $(V)docker rm -f $(DOCKER_CONTAINER_NAME) 2> /dev/null || true + $(V)docker build -t $(DOCKER_IMAGE_NAME) -f Dockerfile . + $(V)docker create --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME) + $(V)rm -rf $(BUILD_DIR_DOCKER) + $(V)mkdir -p $(BUILD_DIR_DOCKER) + $(V)docker cp $(DOCKER_CONTAINER_NAME):/segatools/$(BUILD_DIR_ZIP) $(BUILD_DIR_DOCKER) + +# ----------------------------------------------------------------------------- +# Utility, combo and alias targets +# ----------------------------------------------------------------------------- + +# Help screen note: +# Variables that need to be displayed in the help screen need to strictly +# follow the pattern "^[A-Z_]+ \?= .* # .*". +# Targets that need to be displayed in the help screen need to add a separate +# phony definition strictly following the pattern "^\.PHONY\: .* # .*". + +.PHONY: help # Print help screen +help: + $(V)echo segatools makefile. + $(V)echo + $(V)echo "Environment variables:" + $(V)grep -E '^[A-Z_]+ \?\= .* #' Makefile | gawk 'match($$0, /([A-Z_]+) \?= [$$\(]*([^\)]*)[\)]{0,1} # (.*)/, a) { printf(" \033[0;35m%-25s \033[0;0m%-45s [%s]\n", a[1], a[3], a[2]) }' + $(V)echo "" + $(V)echo "Targets:" + $(V)grep '^.PHONY: .* #' Makefile | gawk 'match($$0, /\.PHONY: (.*) # (.*)/, a) { printf(" \033[0;32m%-25s \033[0;0m%s\n", a[1], a[2]) }' diff --git a/Package.mk b/Package.mk new file mode 100644 index 0000000..245a82f --- /dev/null +++ b/Package.mk @@ -0,0 +1,50 @@ +$(BUILD_DIR_ZIP)/chuni.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni + $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE + $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_32)/aimeio/aimeio.dll \ + $(BUILD_DIR_32)/chuniio/chuniio.dll \ + $(BUILD_DIR_32)/chunihook/chunihook.dll \ + $(DIST_DIR)/chuni/segatools.ini \ + $(DIST_DIR)/chuni/start.bat \ + $(BUILD_DIR_ZIP)/chuni + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/chuni/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip * + +$(BUILD_DIR_ZIP)/idz.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/idz + $(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/aimeio/aimeio.dll \ + $(BUILD_DIR_64)/idzio/idzio.dll \ + $(BUILD_DIR_64)/idzhook/idzhook.dll \ + $(DIST_DIR)/idz/segatools.ini \ + $(DIST_DIR)/idz/start.bat \ + $(BUILD_DIR_ZIP)/idz + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/idz/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip * + +$(BUILD_DIR_ZIP)/doc.zip: \ + $(DOC_DIR)/config \ + $(DOC_DIR)/chunihook.md \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -r $@ $^ + +$(BUILD_DIR_ZIP)/segatools.zip: \ + $(BUILD_DIR_ZIP)/chuni.zip \ + $(BUILD_DIR_ZIP)/doc.zip \ + $(BUILD_DIR_ZIP)/idz.zip \ + CHANGELOG.md \ + README.md \ + + $(V)echo ... $@ + $(V)zip -j $@ $^ From 686c0102a8a0c6d0bb7200e23f85bb4b19b67807 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:01:12 +0200 Subject: [PATCH 043/175] Add Dockerfile for building in docker Make build environment (kinda) independent of the current machine --- .dockerignore | 1 + Dockerfile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ff2f85d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +FROM fedora:31 + +LABEL description="Build environment for segatools" + +RUN yum -y install meson +RUN yum -y install ninja-build +RUN yum -y install make +RUN yum -y install zip +RUN yum -y install clang +RUN yum -y install mingw64-gcc.x86_64 +RUN yum -y install mingw32-gcc.x86_64 +RUN yum -y install git + +RUN mkdir /segatools +WORKDIR /segatools + +COPY aimeio aimeio +COPY amex amex +COPY board board +COPY chunihook chunihook +COPY chuniio chuniio +COPY dist dist +COPY divahook divahook +COPY divaio divaio +COPY doc doc +COPY hooklib hooklib +COPY iccard iccard +COPY idzhook idzhook +COPY idzio idzio +COPY jvs jvs +COPY minihook minihook +COPY mu3hook mu3hook +COPY mu3io mu3io +COPY pki pki +COPY platform platform +COPY reg reg +COPY spike spike +COPY subprojects subprojects +COPY util util +COPY CHANGELOG.md CHANGELOG.md +COPY cross-mingw-32.txt cross-mingw-32.txt +COPY cross-mingw-64.txt cross-mingw-64.txt +COPY Makefile Makefile +COPY meson.build meson.build +COPY Package.mk Package.mk +COPY precompiled.h precompiled.h +COPY README.md README.md + +RUN make dist \ No newline at end of file From 92a36aab11c627b4898dd99c509621c387607872 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:01:53 +0200 Subject: [PATCH 044/175] Remove now obsolete mkdist script, part of Package.mk now --- mkdist | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100755 mkdist diff --git a/mkdist b/mkdist deleted file mode 100755 index e8b9139..0000000 --- a/mkdist +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -set -e - -mkdir -p _zip/chuni/DEVICE - -cp _build32/subprojects/capnhook/inject/inject.exe \ - _build32/aimeio/aimeio.dll \ - _build32/chuniio/chuniio.dll \ - _build32/chunihook/chunihook.dll \ - dist/chuni/segatools.ini \ - dist/chuni/start.bat \ - _zip/chuni/ - -cp pki/billing.pub \ - pki/ca.crt \ - _zip/chuni/DEVICE - -mkdir -p _zip/idz/DEVICE - -cp _build64/subprojects/capnhook/inject/inject.exe \ - _build64/aimeio/aimeio.dll \ - _build64/idzio/idzio.dll \ - _build64/idzhook/idzhook.dll \ - dist/idz/segatools.ini \ - dist/idz/start.bat \ - _zip/idz/ - -cp pki/billing.pub \ - pki/ca.crt \ - _zip/idz/DEVICE - -strip _zip/{chuni,idz}/*.{exe,dll} - From 411dbd5696d8bff0557d71b5c9cb874547889109 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:02:06 +0200 Subject: [PATCH 045/175] Update .gitignore, ignore single build/ output dir --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f638bc5..31da392 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ .*.swp +.vscode/ + # Suggested names for build dirs -_build32/ -_build64/ -_zip/ +build/ # External dependencies subprojects/capnhook From 85f53fbbb55ef0e7e3989f404799a312465edb37 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:02:39 +0200 Subject: [PATCH 046/175] doc: Add doc for chunihook Stolen from various threads from our favorite place --- doc/chunihook.md | 119 +++++++++++++++++++++++++++++++++++++++++ doc/config/chunithm.md | 46 ++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 doc/chunihook.md create mode 100644 doc/config/chunithm.md diff --git a/doc/chunihook.md b/doc/chunihook.md new file mode 100644 index 0000000..4849450 --- /dev/null +++ b/doc/chunihook.md @@ -0,0 +1,119 @@ +# chunihook + +## Supported games + +* Chunithm (Plus) +* Chunithm Air (Plus) +* Chunithm Star (Plus) +* Chunithm Amazon (Plus) +* Chunithm Crystal (Plus) + +## General remarks + +* The minimum version of Windows that this game supports is Windows 8 +* The entire user interface, including the operator menu, is in Japanese +* This game is hard to set up. You may encounter weird errors that are hard to diagnose + +## Known issues + +* JST LOCKOUT: Game is not playable between 1:30 AM and 7:00ish AM JST. +* Only on-board audio works +* Cross-shaped graphical artifacts during gameplay + +## Data and game setup + +1. Get the data +1. Ensure the game files are not marked read-only +1. Start up your favorite ALLNET server implementation in the background. Whether or not you plan to +save your scores the game must be "blessed" by a server at least once, otherwise there will be a +spinner on the title screen forever and you will be unable to start a credit +1. The data releases have the following structure: + * `app/`: Game data + * `option/`: Addon data + * `amfs/`: Metadata +1. Unpack segatools to the `app/bin` directory +1. Create an `appdata` foder (this isn't Windows APPDATA) in the data release next to `app/`, +`option/` and `amfs/` +1. In the `[vfs]` section of `app/bin/segatools.ini` set the paths for the folders: +```text +[vfs] +amfs=../../amfs +appdata=../../appdata +option=../../option +``` +1. In the `[dns]` section, set `default=` to your computer's hostname or LAN IP. Do not put +`127.0.0.1` here, the game specifically checks for and rejects loopback addresses. This setting +controls the address of the network services server +1. Right click `start.bat` in `app/bin` and run it as Administrator. I think you need to run it as +admin at least once, but once you have done that you can run the game as a regular user +1. A sequence of several start-up screens will be displayed. You should also see a bunch of debug +output in a command line window; if you're seeing hex dumps here then that's a good sign. There +will eventually be a screen with a red error message about LEDs. This is being displayed because +Segatools does not currently emulate the cabinet's RGB LED strips +1. Press 1 to go to the operator menu. Use either Test/Service or the touch bar to navigate the +options + 1. Select ゲーム設定 (Game settings) + 1. Select 配信サーバー設定 (Distribution server setting) + 1. Set this setting to サーバー (Server) + 1. Select 筐体グループ設定 (Cabinet group setting) + 1. Set this setting to OFF + 1. If desired, you can also set 音楽選択時間設定 (Music selection time setting) to 99 or whatever + 1. Select 終了 (Finish) + 1. There will be a centered prompt notifying you that a restart is necessary to put your new + settings into effect. Confirm both prompts. The game will now exit + +This setting has nothing to do with the game's central network services, it describes the role that +the cabinet has on the shop LAN. To be exact, every networked group of SEGA cabinets (one cabinet is +still a group) must have exactly one cabinet designated as the "Distribution Server" and all the +others configured as "Clients". The clients will search for a distribution server on their +inter-cabinet LAN: they will not finish starting up until they find one. + +Normally the client/server setting is controlled by DIP switch 1 on the Nu PCB chassis. However, for +some reason this is controlled from the operator menu in Chunithm. And the default setting after +NVRAM reset is Client mode. + +* Start the game up again, then dismiss the LED error message using the touch strip. You should see +a title screen with a red glow along the bottom (or, if you were less lucky, a red banner or a +spinner that doesn't go away). +* Press 2 or 3 a few times to add some credits, then *hold* the Enter key for a few seconds to scan +a card and start a credit. A random card ID will be written to `DEVICE\felica.txt` the first time +you do this. + +## Segatools configuration + +Configurable settings are exposed in the `segatools.ini` file. For a detailed description, please +refer to [this document](config/chunihook.md). + +## Chunithm specific configuration + +For configuring chuinthm specific features, e.g. IO, please refer to +[this document](doc/chunithm.md). + +## FAQ + +### Where is the Free Play setting? + +In the SEGA Nu system supervisor program, which is not included in this release. A command-line +tool to change this setting will be provided in a future release of Segatools. + +### The network test screen shows a bunch of BAD checks and (either a red RTC error screen or an infinite spinner on the title screen) appears which I can't get past + +This means that the game's ALLNET client software failed to start up. The ISP domain squatting +thing is a common cause for this problem, but unfortunately there are a large number of other +possibilities. Try uncommenting the `chuniApp.exe=chunispike.txt` line in `segatools.ini` to enable +the internal ALLNET debug log; this may possibly yield some clues. + +### What does the red "本日のプレイ受付は終了しました" text on the title screen mean? + +"Play acceptance has ended for today". Basically it means that it's outside legal operating hours +for Japanese arcades right now. Hopefully somebody can come up with a hex edit to bypass that +message but for now you'll need to change your PC's clock or something. + +### How do I play courses? + +This requires additional server support which might not be implemented currently. + +### How do I unlock a character other than the penguin? + +Pick any map other than the default. You will see characters available as potential rewards for +those maps. \ No newline at end of file diff --git a/doc/config/chunithm.md b/doc/config/chunithm.md new file mode 100644 index 0000000..cade7d8 --- /dev/null +++ b/doc/config/chunithm.md @@ -0,0 +1,46 @@ +# Chunithm configuration settings + +This file describes configuration settings specific to Chunithm. + +Keyboard binding settings use +[Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). + +## `[io3]` + +Cabinet specific inputs. + +### `test` + +Default `0x31` (`1 Key`) + +Key-binding for cabinet test button. + +### `service` + +Default `0x32` (`2 Key`) + +Key-binding for cabinet service button. + +### `coin` + +Default `0x33` (`3 Key`) + +Key-binding for cabinet coin switch. + +## `[slider]` + +Key bindings for each of the 32 touch cells. + +### `cellX` + +Defaults to key mappings in left-to-right-order as follows: + +```text +SSSSDDDDFFFFGGGGHHHHJJJJKKKKLLLL +``` + +Key binding for a single touch cell. Replace the `X` with a value from `1` to `32` to bind keys to +each cell, e.g. `cell32=0x53`, `cell1=0x53` etc. + +Touch cells are numbered FROM RIGHT TO LEFT! starting from 1. This is in order to match the +numbering used in the operator menu and service manual. From fe5d9c4f409be399a81822c9213b5028d4d3a451 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:03:49 +0200 Subject: [PATCH 047/175] Fix headline levels, first level needs to denote root of doc --- doc/config/common.md | 121 ++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/doc/config/common.md b/doc/config/common.md index 6b911bd..b311476 100644 --- a/doc/config/common.md +++ b/doc/config/common.md @@ -1,42 +1,43 @@ -# Introduction +# Segatools common configuration settings This file describes configuration settings for Segatools that are common to all games. -Keyboard binding settings use [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). +Keyboard binding settings use +[Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). -# `[aime]` +## `[aime]` Controls emulation of the Aime card reader assembly. -## `enable` +### `enable` Default: `1` Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime reader (COM port number varies by game). -## `aimePath` +### `aimePath` Default: `DEVICE\aime.txt` Path to a text file containing a classic Aime IC card ID. **This does not currently work**. -## `felicaPath` +### `felicaPath` Default: `DEVICE\felica.txt` Path to a text file containing a FeliCa e-cash card IDm serial number. -## `felicaGen` +### `felicaGen` Default: `1` Whether to generate a random FeliCa ID if the file at `felicaPath` does not exist. -## `scan` +### `scan` Default: `0x0D` (`VK_RETURN`) @@ -45,13 +46,13 @@ emulates an IC card in its proximity. A variety of different IC cards can be emulated; the exact choice of card that is emulated depends on the presence or absence of the configured card ID files. -# `[amvideo]` +## `[amvideo]` Controls the `amvideo.dll` stub built into Segatools. This is a DLL that is normally present on the SEGA operating system image which is responsible for changing screen resolution and orientation. -## `enable` +### `enable` Default: `1` @@ -60,11 +61,11 @@ you must have the correct registry settings installed and you must use the version of `amvideo.dll` that matches your GPU vendor (since these DLLs make use of vendor-specific APIs). -# `[clock]` +## `[clock]` Controls hooks for Windows time-of-day APIs. -## `timezone` +### `timezone` Default: `1` @@ -73,7 +74,7 @@ ways if the system time zone is not JST. There should not be any reason to disable this hook other than possible implementation bugs, but the option is provided if you need it. -## `timewarp` +### `timewarp` Default: `0` @@ -82,18 +83,18 @@ maintenance period. Causes an incorrect in-game time-of-day to be reported. Better solutions for this problem exist and this feature will probably be removed soon. -## `writeable` +### `writeable` Default: `0` Allow game to adjust system clock and time zone settings. This should normally be left at `0`, but the option is provided if you need it. -# `[dns]` +## `[dns]` Controls redirection of network server hostname lookups -## `default` +### `default` Default: `localhost` @@ -103,45 +104,45 @@ 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). -## `router` +### `router` Default: Empty string (i.e. use value from `default` setting) Overrides the target of the `tenporouter.loc` and `bbrouter.loc` hostname lookups. -## `startup` +### `startup` Default: Empty string (i.e. use value from `default` setting) Overrides the target of the `naominet.jp` host lookup. -## `billing` +### `billing` Default: Empty string (i.e. use value from `default` setting) Overrides the target of the `ib.naominet.jp` host lookup. -## `aimedb` +### `aimedb` Default: Empty string (i.e. use value from `default` setting) Overrides the target of the `aime.naominet.jp` host lookup. -# `[ds]` +## `[ds]` Controls emulation of the "DS (Dallas Semiconductor) EEPROM" chip on the AMEX PCIe board. This is a small (32 byte) EEPROM that contains serial number and region code information. It is not normally written to outside of inital factory provisioning of a Sega Nu. -## `enable` +### `enable` Default: `1` Enable DS EEPROM emulation. Disable to use the DS EEPROM chip on a real AMEX. -## `region` +### `region` Default: `1` @@ -152,7 +153,7 @@ AMEX Board region code. This appears to be a bit mask? - `4`: Export - `8`: China -## `serialNo` +### `serialNo` Default `AAVE-01A99999999` @@ -162,19 +163,19 @@ Default `AAVE-01A99999999` - `AAW`: NuSX-series - `ACA`: ALLS-series -# `[eeprom]` +## `[eeprom]` Controls emulation of the bulk EEPROM on the AMEX PCIe board. This chip stores status and configuration information. -## `enable` +### `enable` Default: `1` Enable bulk EEPROM emulation. Disable to use the bulk EEPROM chip on a real AMEX. -## `path` +### `path` Default: `DEVICE\eeprom.bin` @@ -182,30 +183,30 @@ Path to the storage file for EEPROM emulation. This file is automatically created and initialized with a suitable number of zero bytes if it does not already exist. -# `[gpio]` +## `[gpio]` Configure emulation of the AMEX PCIe GPIO (General Purpose Input Output) controller. -## `enable` +### `enable` Default: `1` Enable GPIO emulation. Disable to use the GPIO controller on a real AMEX. -## `sw1` +### `sw1` Default `0x70` (`VK_F1`) Keyboard binding for Nu chassis SW1 button (alternative Test) -## `sw2` +### `sw2` Default `0x71` (`VK_F2`) Keyboard binding for Nu chassis SW2 button (alternative Service) -## `dipsw1` .. `dipsw8` +### `dipsw1` .. `dipsw8` Defaults: `1`, `0`, `0`, `0`, `0`, `0`, `0`, `0` @@ -232,52 +233,52 @@ Nu chassis DIP switch settings: - `111`: 1920x1080 - Switch 8: Game-specific. Not used in any shipping game. -# `[hwmon]` +## `[hwmon]` Configure stub implementation of the platform hardware monitor driver. The real implementation of this driver monitors CPU temperatures by reading from Intel Model Specific Registers, which is an action that is only permitted from kernel mode. -## `enable` +### `enable` Default `1` Enable hwmon emulation. Disable to use the real hwmon driver. -# `[jvs]` +## `[jvs]` Configure emulation of the AMEX PCIe JVS *controller* (not IO board!) -## `enable` +### `enable` Default `1` Enable JVS port emulation. Disable to use the JVS port on a real AMEX. -# `[keychip]` +## `[keychip]` Configure keychip emulation. -## `enable` +### `enable` Enable keychip emulation. Disable to use a real keychip. -## `id` +### `id` Default: `A69E-01A88888888` Keychip serial number. Keychip serials observed in the wild follow this pattern: `A6xE-01Ayyyyyyyy`. -## `gameId` +### `gameId` Default: (Varies depending on game) Override the game's four-character model code. Changing this from the game's expected value will probably just cause a system error. -## `platformId` +### `platformId` Default: (Varies depending on game) @@ -296,7 +297,7 @@ values include: - `ACA2`: ALLS UX (without dedicated GPU) - `ACA4`: ALLS MX -## `region` +### `region` Default: `1` @@ -311,7 +312,7 @@ Bit values are: - 3: EXP: Export (for Asian markets) - 4: CHS: China (Simplified Chinese?) -## `systemFlag` +### `systemFlag` Default: `0x64` @@ -324,19 +325,19 @@ Other values observed in the wild: - `0x04`: SDCH, SDCA - `0x20`: SDCA -## `subnet` +### `subnet` Default `192.168.100.0` The LAN IP range that the game will expect. The prefix length is hardcoded into the game program: for some games this is `/24`, for others it is `/20`. -# `[netenv]` +## `[netenv]` Configure network environment virtualization. This module helps bypass various restrictions placed upon the game's LAN environment. -## `enable` +### `enable` Default `1` @@ -346,7 +347,7 @@ you want to do any head-to-head play on your LAN. Note: The virtualized LAN IP range is taken from the emulated keychip's `subnet` setting. -## `addrSuffix` +### `addrSuffix` Default: `11` @@ -354,26 +355,26 @@ The final octet of the local host's IP address on the virtualized subnet (so, if the keychip subnet is `192.168.32.0` and this value is set to `11`, then the local host's virtualized LAN IP is `192.168.32.11`). -## `routerSuffix` +### `routerSuffix` Default: `1` The final octet of the default gateway's IP address on the virtualized subnet. -## `macAddr` +### `macAddr` Default: `01:02:03:04:05:06` The MAC address of the virtualized Ethernet adapter. The exact value shouldn't ever matter. -# `[pcbid]` +## `[pcbid]` Configure Windows host name virtualization. The ALLS-series platform no longer has an AMEX board, so the MAIN ID serial number is stored in the Windows hostname. -## `enable` +### `enable` Default: `1` @@ -382,49 +383,49 @@ games (since the ALLS lacks an AMEX and therefore has no DS EEPROM, so it needs another way to store the PCB serial), but it does no harm on games that run on earlier hardware. -## `serialNo` +### `serialNo` Default: `ACAE01A99999999` Set the Windows host name. This should be an ALLS MAIN ID, without the hyphen (which is not a valid character in a Windows host name). -# `[sram]` +## `[sram]` Configure emulation of the AMEX PCIe battery-backed SRAM. This stores bookkeeping state and settings. This file is automatically created and initialized with a suitable number of zero bytes if it does not already exist. -## `enable` +### `enable` Default `1` Enable SRAM emulation. Disable to use the SRAM on a real AMEX. -## `path` +### `path` Default `DEVICE\sram.bin` Path to the storage file for SRAM emulation. -# `[vfs]` +## `[vfs]` Configure Windows path redirection hooks. -## `enable` +### `enable` Default: `1` Enable path redirection. -## `amfs` +### `amfs` Default: Empty string (causes a startup error) Configure the location of the SEGA AMFS volume. Stored on the `E` partition on real hardware. -## `appdata` +### `appdata` Default: Empty string (causes a startup error) @@ -432,7 +433,7 @@ Configure the location of the SEGA "APPDATA" volume (nothing to do with the Windows user's `%APPDATA%` directory). Stored on the `Y` partition on real hardware. -## `option` +### `option` Default: Empty string From 0a2cb705fbed6853d8fe5a4b4bc0bba0b4265c06 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 10 Apr 2021 15:04:28 +0200 Subject: [PATCH 048/175] Overhaul root readme Provide a more user/developer-friendly entry point to the project --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bdbbf34..729c4e6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,64 @@ # Segatools -Quick start on a Linux build host: +Version: `v005` -``` -# Install Meson and a recent build of MinGW-w64, then: +Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms. -$ meson --cross cross-mingw-32.txt _build32 -$ ninja -C _build32 -$ meson --cross cross-mingw-64.txt _build64 -$ ninja -C _build64 +## List of supported games + +* Chunithm + * [Chunithm (Plus)](doc/chunihook.md) + * [Chunithm Air (Plus)](doc/chunihook.md) + * [Chunithm Star (Plus)](doc/chunihook.md) + * [Chunithm Amazon (Plus)](doc/chunihook.md) + * [Chunithm Crystal (Plus)](doc/chunihook.md) +* Initial D + * Initial D Zero + +## End-users + +For setup and configuration guides, refer to the dedicated documents available for each game, see +[the links in the previous section](#list-of-supported-games). + +## Developers + +### Building + +The root `Makefile` contains various targets that allow you to build the project easily. + +#### Local build + +For a local build, you need to install Meson and a recent build of MinGW-w64. Then you can start the +build process: + +```shell +make build ``` -Building on MSYS2 is also possible; consult Meson documentation for details. +Build output will be located in `build/build32` and `build/build64` folders. -Additional documentation will be forthcoming. +#### Cleanup local build + +```shell +make clean +``` + +#### Create distribution package (zip file) + +```shell +make dist +``` + +The output will be located in `build/zip`. + +#### Build and create distribution package using docker + +You can also build using docker which avoids having to setup a full development environment if you +are just interested in building binaries of the latest changes. Naturally, this requires you to +have the docker daemon installed. + +```shell +make build-docker +``` + +Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. From a051faebdb0766061ec112f0c713fb7e68d94e2c Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 28 May 2021 20:55:47 +0200 Subject: [PATCH 049/175] Extend .dockerignore, ignore git related stuff --- .dockerignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.dockerignore b/.dockerignore index d163863..85be427 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,4 @@ +.git +.gitignore + build/ \ No newline at end of file From 5a376836a8ef0adfdae53443de278c4fd89aa894 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 28 May 2021 21:39:37 +0200 Subject: [PATCH 050/175] Add instructions to build on Windows using WSL2 and docker --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 729c4e6..1e7fbf2 100644 --- a/README.md +++ b/README.md @@ -62,3 +62,21 @@ make build-docker ``` Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. + +#### Building with docker on Windows using WSL2 + +In order to use docker for building on Windows, follow these steps: + +* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) +* Regarding Linux distribution, we recommend using Ubuntu 20.04 +* Run the "Ubuntu 20.04 LTS" App which opens a Linux shell +* Install `make` and `docker` by running + * `sudo apt-get update` + * `sudo apt-get install make docker.io` +* Add the current user to the docker group that you don't have to run docker commands with root: +`sudo usermod -a -G docker $USER` +* Run the docker daemon in the background: `sudo dockerd > /dev/null 2>&1 &` +* Navigate to your segatools folder. If it is located on the `C:` drive, WSL automatically provides +a mountpoint for that under `/mnt/c`, e.g. `cd /mnt/c/segatools` (if the folder `segatools` is +located under `C:\segatools` on Windows). +* Build segatools: `make build-docker` From 12ee51d1980d4bb0565d9161b547992169a23c69 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 28 May 2021 22:35:52 +0200 Subject: [PATCH 051/175] Add docker-build.bat for building on Windows with Docker Desktop Initial contribution by Vincent Kocks. Script simplified to fit the new build setup with a common build/ folder. Build folder can be deleted on every invocation because it will just be copied from the docker container anyway. Co-authored-by: Vincent Kocks --- docker-build.bat | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docker-build.bat diff --git a/docker-build.bat b/docker-build.bat new file mode 100644 index 0000000..62348bc --- /dev/null +++ b/docker-build.bat @@ -0,0 +1,40 @@ +@echo off +setlocal enabledelayedexpansion + +:: Static Environment Variables +set BUILD_OUTPUT_PATH=build\docker +set IMAGE_NAME=djhackers/segatools-build:latest +set CONTAINER_NAME=segatools-build + +:: Main Execution +docker build . -t %IMAGE_NAME% + +if ERRORLEVEL 1 ( + goto failure +) + +docker create --name %CONTAINER_NAME% %IMAGE_NAME% + +if ERRORLEVEL 1 ( + goto failure +) + +rd /s /q "!BUILD_OUTPUT_PATH!" +mkdir "!BUILD_OUTPUT_PATH!" + +docker cp %CONTAINER_NAME%:/segatools/build/zip %BUILD_OUTPUT_PATH% + +docker rm -f %CONTAINER_NAME% > nul + +goto success + +:failure +echo segatools Docker build FAILED! +goto finish + +:success +echo segatools Docker build completed successfully. +goto finish + +:finish +pause From 27ec081ca70aafc6101df6fd1aafde37076f9766 Mon Sep 17 00:00:00 2001 From: icex2 Date: Fri, 28 May 2021 22:39:09 +0200 Subject: [PATCH 052/175] Add another build option for Windows using Docker Desktop to README --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e7fbf2..3fe4bc0 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,16 @@ make build-docker Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. -#### Building with docker on Windows using WSL2 +#### Building with Docker Desktop on Windows -In order to use docker for building on Windows, follow these steps: +* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) +* [Install Docker Desktop](https://docs.docker.com/docker-for-windows/install/) +* Run Docker Desktop to start the Docker Engine +* Open a command prompt (`cmd.exe`) and `cd` to your `segatools` folder +* Run `docker-build.bat` +* Once completed successfully, build output is located in the `build/docker/zip` sub-folder. + +#### Building with Docker on Windows using WSL2 * [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) * Regarding Linux distribution, we recommend using Ubuntu 20.04 @@ -80,3 +87,4 @@ In order to use docker for building on Windows, follow these steps: a mountpoint for that under `/mnt/c`, e.g. `cd /mnt/c/segatools` (if the folder `segatools` is located under `C:\segatools` on Windows). * Build segatools: `make build-docker` +* Once completed successfully, build output is located in the `build/docker/zip` sub-folder. From eb2383187ae600300baaec5bdbdc1fd575a2674e Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 22 May 2021 12:17:46 -0400 Subject: [PATCH 053/175] Suppress vscode auto-format for the time being Eventually we would like to set up clang-format rules and enforce them from CI. --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7c2feb7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": false +} From 1627a0c8507537dcef900600d258830bd41d18fa Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 22 May 2021 12:19:28 -0400 Subject: [PATCH 054/175] Delete draft debris that was never used --- hooklib/dll.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hooklib/dll.h b/hooklib/dll.h index c1cbbd4..b582b71 100644 --- a/hooklib/dll.h +++ b/hooklib/dll.h @@ -5,12 +5,6 @@ #include "hook/table.h" -struct dll_symbol { - void *ptr; - const char *name; - uint16_t ordinal; -}; - HRESULT dll_hook_push( HMODULE redir_mod, const wchar_t *name, From 9543bd183d0b59198bea85d6fa005572416b72bb Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:42:29 -0400 Subject: [PATCH 055/175] Whitespace fix --- idzio/di.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idzio/di.c b/idzio/di.c index d8405da..f542f5d 100644 --- a/idzio/di.c +++ b/idzio/di.c @@ -513,7 +513,7 @@ static void idz_di_jvs_read_analogs(struct idz_io_analog_state *out) } else { out->brake = 65535 - *brake; } - + if (idz_di_reverse_accel_axis) { out->accel = *accel; } else { From 5410941944dee50a884c2aa1582d4fdab27ceff0 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 14:29:00 -0400 Subject: [PATCH 056/175] Fix DLL loader hook logic Seems this was quite buggy in its original form. --- hooklib/dll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hooklib/dll.c b/hooklib/dll.c index 94f7137..f98d9af 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -98,6 +98,7 @@ HRESULT dll_hook_push( new_item = &new_mem[dll_hook_count]; new_item->name = name; + new_item->redir_mod = redir_mod; new_item->syms = syms; new_item->nsyms = nsyms; From 49595d2c3767ce2b5d98215ee8818859172de521 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 14:17:14 -0400 Subject: [PATCH 057/175] Fix hook startup failure path Returning from the EXE PE entry point merely terminates the main thread and leaves the process hanging. We need to explicitly exit from the process since the NTDLL RTL Start code won't do it for us. --- chunihook/dllmain.c | 12 +++++++----- divahook/dllmain.c | 12 +++++++----- idzhook/dllmain.c | 10 ++++++---- minihook/dllmain.c | 26 +++++++++++++++++++++++--- mu3hook/dllmain.c | 13 +++++++++---- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index 1a7dc39..1cd3ebd 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -1,6 +1,5 @@ #include -#include #include #include "amex/amex.h" @@ -73,25 +72,25 @@ static DWORD CALLBACK chuni_pre_startup(void) chuni_hook_mod); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = amex_hook_init(&chuni_hook_cfg.amex, chunithm_jvs_init); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = slider_hook_init(&chuni_hook_cfg.slider); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } /* Initialize debug helpers */ @@ -103,6 +102,9 @@ static DWORD CALLBACK chuni_pre_startup(void) /* Jump to EXE start address */ return chuni_startup(); + +fail: + ExitProcess(EXIT_FAILURE); } BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) diff --git a/divahook/dllmain.c b/divahook/dllmain.c index 1a6449a..a59a066 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -1,6 +1,5 @@ #include -#include #include #include "amex/amex.h" @@ -48,25 +47,25 @@ static DWORD CALLBACK diva_pre_startup(void) diva_hook_mod); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = amex_hook_init(&diva_hook_cfg.amex, diva_jvs_init); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = sg_reader_hook_init(&diva_hook_cfg.aime, 10); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } hr = slider_hook_init(&diva_hook_cfg.slider); if (FAILED(hr)) { - return EXIT_FAILURE; + goto fail; } /* Initialize debug helpers */ @@ -78,6 +77,9 @@ static DWORD CALLBACK diva_pre_startup(void) /* Jump to EXE start address */ return diva_startup(); + +fail: + ExitProcess(EXIT_FAILURE); } BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index a54e4bd..80ca7ef 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -1,6 +1,5 @@ #include -#include #include #include "amex/amex.h" @@ -49,19 +48,19 @@ static DWORD CALLBACK idz_pre_startup(void) idz_hook_mod); if (FAILED(hr)) { - return hr; + goto fail; } hr = amex_hook_init(&idz_hook_cfg.amex, idz_jvs_init); if (FAILED(hr)) { - return hr; + goto fail; } hr = sg_reader_hook_init(&idz_hook_cfg.aime, 10); if (FAILED(hr)) { - return hr; + goto fail; } /* Initialize debug helpers */ @@ -73,6 +72,9 @@ static DWORD CALLBACK idz_pre_startup(void) /* Jump to EXE start address */ return idz_startup(); + +fail: + ExitProcess(EXIT_FAILURE); } BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) diff --git a/minihook/dllmain.c b/minihook/dllmain.c index 0c1512d..bdc45b8 100644 --- a/minihook/dllmain.c +++ b/minihook/dllmain.c @@ -1,5 +1,7 @@ #include +#include + #include "amex/config.h" #include "amex/ds.h" @@ -20,6 +22,7 @@ static DWORD CALLBACK app_pre_startup(void) struct clock_config clock_cfg; struct ds_config ds_cfg; struct nusec_config nusec_cfg; + HRESULT hr; dprintf("--- Begin %s ---\n", __func__); @@ -28,13 +31,30 @@ static DWORD CALLBACK app_pre_startup(void) nusec_config_load(&nusec_cfg, L".\\segatools.ini"); spike_hook_init(L".\\segatools.ini"); - clock_hook_init(&clock_cfg); - nusec_hook_init(&nusec_cfg, "SSSS", "AAV0"); - ds_hook_init(&ds_cfg); + hr = clock_hook_init(&clock_cfg); + + if (FAILED(hr)) { + goto fail; + } + + hr = nusec_hook_init(&nusec_cfg, "SSSS", "AAV0"); + + if (FAILED(hr)) { + goto fail; + } + + hr = ds_hook_init(&ds_cfg); + + if (FAILED(hr)) { + goto fail; + } dprintf("--- End %s ---\n", __func__); return app_startup(); + +fail: + ExitProcess(EXIT_FAILURE); } BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 7161261..ebfff0b 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -1,5 +1,7 @@ #include +#include + #include "board/io4.h" #include "board/sg-reader.h" #include "board/vfd.h" @@ -45,25 +47,25 @@ static DWORD CALLBACK mu3_pre_startup(void) mu3_hook_mod); if (FAILED(hr)) { - return hr; + goto fail; } hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1); if (FAILED(hr)) { - return hr; + goto fail; } hr = vfd_hook_init(2); if (FAILED(hr)) { - return hr; + goto fail; } hr = mu3_io4_hook_init(); if (FAILED(hr)) { - return hr; + goto fail; } /* Initialize Unity native plugin DLL hooks @@ -82,6 +84,9 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Jump to EXE start address */ return mu3_startup(); + +fail: + ExitProcess(EXIT_FAILURE); } BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) From 872ae9ca502adc6ea61d16f728c463698a70fe36 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 09:39:45 -0400 Subject: [PATCH 058/175] Fix Diva slider startup --- divahook/slider.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/divahook/slider.c b/divahook/slider.c index c9bd1dc..33a569e 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -82,7 +82,7 @@ static HRESULT slider_handle_irp_locked(struct irp *irp) if (irp->op == IRP_OP_OPEN) { dprintf("Diva slider: Starting backend DLL\n"); - hr = diva_io_jvs_init(); + hr = diva_io_slider_init(); if (FAILED(hr)) { dprintf("Diva slider: Backend DLL error: %x\n", (int) hr); From c71ff1bcb509538f705cc1a57ab16eb558131967 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 22 May 2021 12:16:46 -0400 Subject: [PATCH 059/175] Add module DEF file for chunihook --- chunihook/chunihook.def | 3 +++ chunihook/meson.build | 1 + 2 files changed, 4 insertions(+) create mode 100644 chunihook/chunihook.def diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def new file mode 100644 index 0000000..67bcc4c --- /dev/null +++ b/chunihook/chunihook.def @@ -0,0 +1,3 @@ +LIBRARY chunihook + +EXPORTS diff --git a/chunihook/meson.build b/chunihook/meson.build index f341d0f..2fab805 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -3,6 +3,7 @@ shared_library( name_prefix : '', include_directories : inc, implicit_include_directories : false, + vs_module_defs : 'chunihook.def', c_pch : '../precompiled.h', dependencies : [ capnhook.get_variable('hook_dep'), From e57aeb03c3beffbeb55a1468b4d1c47ded8f65f8 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 22 May 2021 12:29:39 -0400 Subject: [PATCH 060/175] Simplify DLL hook mechanism This change deletes the GetProcAddress hook and exports symbols corresponding to the hooked functions from each hook DLL instead; we stop at redirecting LoadLibrary/GetModuleHandle calls to the hook DLL. This simplified approach has less hidden magic going on behind the scenes and is more readily composable (i.e. a hook DLL can export redirect symbols for more than one dynamically-loaded DLL). --- chunihook/chunihook.def | 5 ++ chunihook/dllmain.c | 2 +- divahook/divahook.def | 7 +++ hooklib/dll.c | 118 ++++++++++++++-------------------------- hooklib/dll.h | 6 +- hooklib/gfx.c | 13 +++-- hooklib/gfx.h | 4 +- idzhook/idzhook.def | 7 +++ mu3hook/dllmain.c | 2 +- mu3hook/mu3hook.def | 7 +++ platform/amvideo.c | 39 ++----------- 11 files changed, 84 insertions(+), 126 deletions(-) diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def index 67bcc4c..513347d 100644 --- a/chunihook/chunihook.def +++ b/chunihook/chunihook.def @@ -1,3 +1,8 @@ LIBRARY chunihook EXPORTS + Direct3DCreate9 + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index 1cd3ebd..c6f1ed5 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -60,7 +60,7 @@ static DWORD CALLBACK chuni_pre_startup(void) /* Hook Win32 APIs */ - gfx_hook_init(&chuni_hook_cfg.gfx); + gfx_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); serial_hook_init(); /* Initialize emulation hooks */ diff --git a/divahook/divahook.def b/divahook/divahook.def index 9cef741..bd13faa 100644 --- a/divahook/divahook.def +++ b/divahook/divahook.def @@ -1 +1,8 @@ LIBRARY divahook + +EXPORTS + Direct3DCreate9 + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 diff --git a/hooklib/dll.c b/hooklib/dll.c index f98d9af..00adfd6 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -15,8 +15,6 @@ struct dll_hook_reg { const wchar_t *name; HMODULE redir_mod; - const struct hook_symbol *syms; - size_t nsyms; }; /* Helper functions */ @@ -26,6 +24,7 @@ static HMODULE dll_hook_search_dll(const wchar_t *name); /* Hook functions */ +static BOOL WINAPI hook_FreeLibrary(HMODULE mod); static HMODULE WINAPI hook_GetModuleHandleA(const char *name); static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name); static HMODULE WINAPI hook_LoadLibraryA(const char *name); @@ -34,6 +33,7 @@ static void * WINAPI hook_GetProcAddress(HMODULE mod, const char *name); /* Link pointers */ +static BOOL (WINAPI *next_FreeLibrary)(HMODULE mod); static HMODULE (WINAPI *next_GetModuleHandleA)(const char *name); static HMODULE (WINAPI *next_GetModuleHandleW)(const wchar_t *name); static HMODULE (WINAPI *next_LoadLibraryA)(const char *name); @@ -42,6 +42,10 @@ static void * (WINAPI *next_GetProcAddress)(HMODULE mod, const char *name); static const struct hook_symbol dll_loader_syms[] = { { + .name = "FreeLibrary", + .patch = hook_FreeLibrary, + .link = (void **) &next_FreeLibrary, + }, { .name = "GetModuleHandleA", .patch = hook_GetModuleHandleA, .link = (void **) &next_GetModuleHandleA, @@ -57,10 +61,6 @@ static const struct hook_symbol dll_loader_syms[] = { .name = "LoadLibraryW", .patch = hook_LoadLibraryW, .link = (void **) &next_LoadLibraryW, - }, { - .name = "GetProcAddress", - .patch = hook_GetProcAddress, - .link = (void **) &next_GetProcAddress, } }; @@ -71,16 +71,13 @@ static size_t dll_hook_count; HRESULT dll_hook_push( HMODULE redir_mod, - const wchar_t *name, - const struct hook_symbol *syms, - size_t nsyms) + const wchar_t *name) { struct dll_hook_reg *new_item; struct dll_hook_reg *new_mem; HRESULT hr; assert(name != NULL); - assert(syms != NULL); dll_hook_init(); @@ -99,8 +96,6 @@ HRESULT dll_hook_push( new_item = &new_mem[dll_hook_count]; new_item->name = name; new_item->redir_mod = redir_mod; - new_item->syms = syms; - new_item->nsyms = nsyms; dll_hook_list = new_mem; dll_hook_count++; @@ -167,6 +162,39 @@ static HMODULE dll_hook_search_dll(const wchar_t *name) return result; } +static BOOL WINAPI hook_FreeLibrary(HMODULE mod) +{ + bool match; + size_t i; + + match = false; + EnterCriticalSection(&dll_hook_lock); + + for (i = 0 ; i < dll_hook_count ; i++) { + if (mod == dll_hook_list[i].redir_mod) { + match = true; + + break; + } + } + + LeaveCriticalSection(&dll_hook_lock); + + if (match) { + /* Block attempts to unload redirected modules, since this could cause + a hook DLL to unexpectedly vanish and crash the whole application. + + Reference counting might be another solution, although it is + possible that a buggy application might cause a hook DLL unload in + that case. */ + SetLastError(ERROR_SUCCESS); + + return TRUE; + } + + return next_FreeLibrary(mod); +} + static HMODULE WINAPI hook_GetModuleHandleA(const char *name) { HMODULE result; @@ -262,69 +290,3 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name) } /* TODO LoadLibraryExA, LoadLibraryExW */ - -static void * WINAPI hook_GetProcAddress(HMODULE mod, const char *name) -{ - const struct hook_symbol *syms; - uintptr_t ordinal; - size_t nsyms; - size_t i; - - if (name == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); - - return NULL; - } - - syms = NULL; - nsyms = 0; - - EnterCriticalSection(&dll_hook_lock); - - for (i = 0 ; i < dll_hook_count ; i++) { - if (dll_hook_list[i].redir_mod == mod) { - syms = dll_hook_list[i].syms; - nsyms = dll_hook_list[i].nsyms; - - break; - } - } - - LeaveCriticalSection(&dll_hook_lock); - - if (syms == NULL) { - return next_GetProcAddress(mod, name); - } - - ordinal = (uintptr_t) name; - - if (ordinal > 0xFFFF) { - /* Import by name */ - - for (i = 0 ; i < nsyms ; i++) { - if (strcmp(name, syms[i].name) == 0) { - break; - } - } - } else { - /* Import by ordinal (and name != NULL so ordinal != 0) */ - - for (i = 0 ; i < nsyms ; i++) { - if (ordinal == syms[i].ordinal) { - break; - } - } - } - - if (i < nsyms) { - SetLastError(ERROR_SUCCESS); - - return syms[i].patch; - } else { - /* GetProcAddress sets this error on failure, although of course MSDN - does not see fit to document the exact error code. */ - SetLastError(ERROR_PROC_NOT_FOUND); - - return NULL; - } -} diff --git a/hooklib/dll.h b/hooklib/dll.h index b582b71..2927a50 100644 --- a/hooklib/dll.h +++ b/hooklib/dll.h @@ -3,10 +3,6 @@ #include #include -#include "hook/table.h" - HRESULT dll_hook_push( HMODULE redir_mod, - const wchar_t *name, - const struct hook_symbol *syms, - size_t nsyms); + const wchar_t *name); diff --git a/hooklib/gfx.c b/hooklib/gfx.c index 30a576a..6b8390a 100644 --- a/hooklib/gfx.c +++ b/hooklib/gfx.c @@ -24,21 +24,20 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev); -static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver); -static Direct3DCreate9_t next_Direct3DCreate9; static HRESULT gfx_frame_window(HWND hwnd); static struct gfx_config gfx_config; +static Direct3DCreate9_t next_Direct3DCreate9; static const struct hook_symbol gfx_hooks[] = { { .name = "Direct3DCreate9", - .patch = my_Direct3DCreate9, + .patch = Direct3DCreate9, .link = (void **) &next_Direct3DCreate9 }, }; -void gfx_hook_init(const struct gfx_config *cfg) +void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) { HMODULE d3d9; @@ -71,13 +70,15 @@ void gfx_hook_init(const struct gfx_config *cfg) } } - dll_hook_push(NULL, L"d3d9.dll", gfx_hooks, _countof(gfx_hooks)); + if (self != NULL) { + dll_hook_push(self, L"d3d9.dll"); + } fail: return; } -static IDirect3D9 * WINAPI my_Direct3DCreate9(UINT sdk_ver) +IDirect3D9 * WINAPI Direct3DCreate9(UINT sdk_ver) { struct com_proxy *proxy; IDirect3D9Vtbl *vtbl; diff --git a/hooklib/gfx.h b/hooklib/gfx.h index 9a7e27c..9182371 100644 --- a/hooklib/gfx.h +++ b/hooklib/gfx.h @@ -1,5 +1,7 @@ #pragma once +#include + #include struct gfx_config { @@ -9,4 +11,4 @@ struct gfx_config { int monitor; }; -void gfx_hook_init(const struct gfx_config *cfg); +void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index 02950f4..d7c7f07 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -1 +1,8 @@ LIBRARY idzhook + +EXPORTS + Direct3DCreate9 + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index ebfff0b..5b62506 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -35,7 +35,7 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Hook Win32 APIs */ - gfx_hook_init(&mu3_hook_cfg.gfx); + gfx_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); serial_hook_init(); /* Initialize emulation hooks */ diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index cd7d5bb..f0c5571 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -1 +1,8 @@ LIBRARY mu3hook + +EXPORTS + Direct3DCreate9 + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 diff --git a/platform/amvideo.c b/platform/amvideo.c index e06931e..ccd818f 100644 --- a/platform/amvideo.c +++ b/platform/amvideo.c @@ -15,11 +15,6 @@ /* Hook functions */ -static int amDllVideoOpen(void *ctx); -static int amDllVideoClose(void *ctx); -static int amDllVideoSetResolution(void *ctx, void *param); -static int amDllVideoGetVBiosVersion(void *ctx, char *dest, size_t nchars); - static HRESULT amvideo_reg_read_name(void *bytes, uint32_t *nbytes); static HRESULT amvideo_reg_read_port_X(void *bytes, uint32_t *nbytes); static HRESULT amvideo_reg_read_resolution_1(void *bytes, uint32_t *nbytes); @@ -88,26 +83,6 @@ static const struct reg_hook_val amvideo_reg_mode_vals[] = { } }; -static const struct hook_symbol amvideo_syms[] = { - { - .ordinal = 1, - .name = "amDllVideoOpen", - .patch = amDllVideoOpen, - }, { - .ordinal = 2, - .name = "amDllVideoClose", - .patch = amDllVideoClose, - }, { - .ordinal = 3, - .name = "amDllVideoSetResolution", - .patch = amDllVideoSetResolution, - }, { - .ordinal = 4, - .name = "amDllVideoGetVBiosVersion", - .patch = amDllVideoGetVBiosVersion, - } -}; - HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod) { HRESULT hr; @@ -138,11 +113,7 @@ HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod) return hr; } - hr = dll_hook_push( - redir_mod, - amvideo_dll_name, - amvideo_syms, - _countof(amvideo_syms)); + hr = dll_hook_push(redir_mod, amvideo_dll_name); if (FAILED(hr)) { return hr; @@ -151,28 +122,28 @@ HRESULT amvideo_hook_init(const struct amvideo_config *cfg, HMODULE redir_mod) return S_OK; } -static int amDllVideoOpen(void *ctx) +int amDllVideoOpen(void *ctx) { dprintf("AmVideo: %s\n", __func__); return 0; } -static int amDllVideoClose(void *ctx) +int amDllVideoClose(void *ctx) { dprintf("AmVideo: %s\n", __func__); return 0; } -static int amDllVideoSetResolution(void *ctx, void *param) +int amDllVideoSetResolution(void *ctx, void *param) { dprintf("AmVideo: %s\n", __func__); return 0; } -static int amDllVideoGetVBiosVersion(void *ctx, char *dest, size_t nchars) +int amDllVideoGetVBiosVersion(void *ctx, char *dest, size_t nchars) { dprintf("AmVideo: %s\n", __func__); strcpy(dest, "01.02.03.04.05"); From 90d908fe07d289ed3e45749c313d5941b9c9ee90 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 10:28:17 -0400 Subject: [PATCH 061/175] Statically link current aimeio implementation --- aimeio/aimeio.def | 9 --------- aimeio/meson.build | 3 +-- chunihook/meson.build | 2 +- divahook/meson.build | 2 +- idzhook/meson.build | 2 +- mu3hook/meson.build | 2 +- 6 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 aimeio/aimeio.def diff --git a/aimeio/aimeio.def b/aimeio/aimeio.def deleted file mode 100644 index 6192298..0000000 --- a/aimeio/aimeio.def +++ /dev/null @@ -1,9 +0,0 @@ -LIBRARY aimeio - -EXPORTS - aime_io_fini - aime_io_init - aime_io_led_set_color - aime_io_nfc_poll - aime_io_nfc_get_aime_id - aime_io_nfc_get_felica_id diff --git a/aimeio/meson.build b/aimeio/meson.build index 3715e5e..ddbb66a 100644 --- a/aimeio/meson.build +++ b/aimeio/meson.build @@ -1,9 +1,8 @@ -aimeio_dll = shared_library( +aimeio_lib = static_library( 'aimeio', name_prefix : '', include_directories: inc, implicit_include_directories : false, - vs_module_defs : 'aimeio.def', c_pch : '../precompiled.h', link_with : [ util_lib, diff --git a/chunihook/meson.build b/chunihook/meson.build index 2fab805..9fb6486 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -10,7 +10,7 @@ shared_library( capnhook.get_variable('hooklib_dep'), ], link_with : [ - aimeio_dll, + aimeio_lib, amex_lib, board_lib, chuniio_dll, diff --git a/divahook/meson.build b/divahook/meson.build index d49c157..1b09193 100644 --- a/divahook/meson.build +++ b/divahook/meson.build @@ -10,7 +10,7 @@ shared_library( capnhook.get_variable('hooklib_dep'), ], link_with : [ - aimeio_dll, + aimeio_lib, amex_lib, board_lib, divaio_dll, diff --git a/idzhook/meson.build b/idzhook/meson.build index f4a3e8b..e27007a 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -11,7 +11,7 @@ shared_library( xinput_lib, ], link_with : [ - aimeio_dll, + aimeio_lib, amex_lib, board_lib, hooklib_lib, diff --git a/mu3hook/meson.build b/mu3hook/meson.build index 4f3ba71..f777b9d 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -11,7 +11,7 @@ shared_library( xinput_lib, ], link_with : [ - aimeio_dll, + aimeio_lib, board_lib, hooklib_lib, mu3io_dll, From 1750f85f5195f79eabd7e316f78f050b4c037334 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 10:29:23 -0400 Subject: [PATCH 062/175] Drop aime_io_fini This isn't used, and due to the way that the hook machinery works it is not likely to come into use later on either. Since we can't rely on existing implementations actually being correct, if it does somehow start being used then we will need to advance the aimeio API version in order to support it. --- aimeio/aimeio.c | 4 ---- aimeio/aimeio.h | 1 - 2 files changed, 5 deletions(-) diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c index 747205d..b0f2e24 100644 --- a/aimeio/aimeio.c +++ b/aimeio/aimeio.c @@ -169,10 +169,6 @@ HRESULT aime_io_init(void) return S_OK; } -void aime_io_fini(void) -{ -} - HRESULT aime_io_nfc_poll(uint8_t unit_no) { bool sense; diff --git a/aimeio/aimeio.h b/aimeio/aimeio.h index 1fbf260..7e922ed 100644 --- a/aimeio/aimeio.h +++ b/aimeio/aimeio.h @@ -9,7 +9,6 @@ this DLL just yet unless you are prepared to deal with API breakages. */ HRESULT aime_io_init(void); -void aime_io_fini(void); HRESULT aime_io_nfc_poll(uint8_t unit_no); HRESULT aime_io_nfc_get_aime_id( uint8_t unit_no, From f59f4ec8c182fdd9360b0a872b93f3e4ae217e4c Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 10:31:03 -0400 Subject: [PATCH 063/175] Document the aimeio API This is de-facto a published API now so we have to support it. --- aimeio/aimeio.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/aimeio/aimeio.h b/aimeio/aimeio.h index 7e922ed..e8ceaca 100644 --- a/aimeio/aimeio.h +++ b/aimeio/aimeio.h @@ -5,14 +5,62 @@ #include #include -/* THIS API IS UNSTABLE. Please do not build alternative implementations of - this DLL just yet unless you are prepared to deal with API breakages. */ - +/* + Initialize Aime IO provider DLL. Only called once, before any other + functions exported from this DLL are called. + */ HRESULT aime_io_init(void); + +/* + Poll for IC cards in the vicinity. + + - unit_no: Always 0 as of the current API version + */ HRESULT aime_io_nfc_poll(uint8_t unit_no); + +/* + Attempt to read out a classic Aime card ID + + - unit_no: Always 0 as of the current API version + - luid: Pointer to a ten-byte buffer that will receive the ID + - luid_size: Size of the buffer at *luid. Always 10. + + Returns: + + - S_OK if a classic Aime is present and was read successfully + - S_FALSE if no classic Aime card is present (*luid will be ignored) + - Any HRESULT error if an error occured. +*/ HRESULT aime_io_nfc_get_aime_id( uint8_t unit_no, uint8_t *luid, size_t luid_size); + +/* + Attempt to read out a FeliCa card ID ("IDm"). The following are examples + of FeliCa cards: + + - Amuse IC (which includes new-style Aime-branded cards, among others) + - Smartphones with FeliCa NFC capability (uncommon outside Japan) + - Various Japanese e-cash cards and train passes + + Parameters: + + - unit_no: Always 0 as of the current API version + - IDm: Output parameter that will receive the card ID + + Returns: + + - S_OK if a FeliCa device is present and was read successfully + - S_FALSE if no FeliCa device is present (*IDm will be ignored) + - Any HRESULT error if an error occured. +*/ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm); + +/* + Change the color and brightness of the card reader's RGB lighting + + - unit_no: Always 0 as of the current API version + - r, g, b: Primary color intensity, from 0 to 255 inclusive. +*/ void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); From df5f08314bc223bf695e6b1f31f9996eb05b894f Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 10:38:37 -0400 Subject: [PATCH 064/175] Add API versioning scheme to aimeio --- aimeio/aimeio.c | 5 +++++ aimeio/aimeio.h | 23 ++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c index b0f2e24..b09d612 100644 --- a/aimeio/aimeio.c +++ b/aimeio/aimeio.c @@ -162,6 +162,11 @@ static HRESULT aime_io_generate_felica( return S_OK; } +uint16_t aime_io_get_api_version(void) +{ + return 0x0100; +} + HRESULT aime_io_init(void) { aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); diff --git a/aimeio/aimeio.h b/aimeio/aimeio.h index e8ceaca..221f8e0 100644 --- a/aimeio/aimeio.h +++ b/aimeio/aimeio.h @@ -5,9 +5,22 @@ #include #include +/* + Get the version of the Aime IO API that this DLL supports. This function + should return a positive 16-bit integer, where the high byte is the major + version and the low byte is the minor version (as defined by the Semantic + Versioning standard). + + The latest API version as of this writing is 0x0100. + */ +uint16_t aime_io_get_api_version(void); + /* Initialize Aime IO provider DLL. Only called once, before any other - functions exported from this DLL are called. + functions exported from this DLL are called (except for + aime_io_get_api_version). + + Minimum API version: 0x0100 */ HRESULT aime_io_init(void); @@ -15,6 +28,8 @@ HRESULT aime_io_init(void); Poll for IC cards in the vicinity. - unit_no: Always 0 as of the current API version + + Minimum API version: 0x0100 */ HRESULT aime_io_nfc_poll(uint8_t unit_no); @@ -30,6 +45,8 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no); - S_OK if a classic Aime is present and was read successfully - S_FALSE if no classic Aime card is present (*luid will be ignored) - Any HRESULT error if an error occured. + + Minimum API version: 0x0100 */ HRESULT aime_io_nfc_get_aime_id( uint8_t unit_no, @@ -54,6 +71,8 @@ HRESULT aime_io_nfc_get_aime_id( - S_OK if a FeliCa device is present and was read successfully - S_FALSE if no FeliCa device is present (*IDm will be ignored) - Any HRESULT error if an error occured. + + Minimum API version: 0x0100 */ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm); @@ -62,5 +81,7 @@ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm); - unit_no: Always 0 as of the current API version - r, g, b: Primary color intensity, from 0 to 255 inclusive. + + Minimum API version: 0x0100 */ void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); From edd26aafe7430c2cf30342154521cc5c3e4afa79 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 11:46:06 -0400 Subject: [PATCH 065/175] Export aimeio symbols from hook DLLs --- chunihook/chunihook.def | 6 ++++++ divahook/divahook.def | 6 ++++++ idzhook/idzhook.def | 6 ++++++ mu3hook/mu3hook.def | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def index 513347d..9da4d7f 100644 --- a/chunihook/chunihook.def +++ b/chunihook/chunihook.def @@ -2,6 +2,12 @@ LIBRARY chunihook EXPORTS Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll amDllVideoClose @2 amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 diff --git a/divahook/divahook.def b/divahook/divahook.def index bd13faa..49075b8 100644 --- a/divahook/divahook.def +++ b/divahook/divahook.def @@ -2,6 +2,12 @@ LIBRARY divahook EXPORTS Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll amDllVideoClose @2 amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index d7c7f07..43b8ef7 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -2,6 +2,12 @@ LIBRARY idzhook EXPORTS Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll amDllVideoClose @2 amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index f0c5571..c819437 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -2,6 +2,12 @@ LIBRARY mu3hook EXPORTS Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll amDllVideoClose @2 amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 From 10f2042bee77c057cbfe19492ab83ed41a9f1f27 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 11:39:33 -0400 Subject: [PATCH 066/175] Add DLL binding helper --- util/dll-bind.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ util/dll-bind.h | 33 +++++++++++++++++++++++++++++++++ util/meson.build | 2 ++ 3 files changed, 82 insertions(+) create mode 100644 util/dll-bind.c create mode 100644 util/dll-bind.h diff --git a/util/dll-bind.c b/util/dll-bind.c new file mode 100644 index 0000000..9568d33 --- /dev/null +++ b/util/dll-bind.c @@ -0,0 +1,47 @@ +#include + +#include +#include + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +HRESULT dll_bind( + void *dest, + HINSTANCE src, + const struct dll_bind_sym **syms_pos, + size_t syms_count) +{ + HRESULT hr; + void *src_result; + void **dest_field; + const struct dll_bind_sym *current_sym; + size_t i; + + assert(dest != NULL); + assert(src != NULL); + assert(syms_pos != NULL); + + hr = S_OK; + current_sym = *syms_pos; + + assert(current_sym != NULL); + + for (i = 0; i < syms_count; i++) { + src_result = GetProcAddress(src, current_sym->sym); + + if (src_result == NULL) { + hr = E_NOTIMPL; + + break; + } + + dest_field = (void **)(((uint8_t *)dest) + current_sym->off); + *dest_field = src_result; + current_sym++; + } + + *syms_pos = current_sym; + + return hr; +} diff --git a/util/dll-bind.h b/util/dll-bind.h new file mode 100644 index 0000000..08e647b --- /dev/null +++ b/util/dll-bind.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +struct dll_bind_sym { + /* Symbol name to locate in the source DLL */ + const char *sym; + + /* Offset where the symbol pointer should be written in the target + structure */ + ptrdiff_t off; +}; + +/* + Bind a list of DLL symbols into a structure that contains function + pointers. + + - dest: Pointer to destination structure. + - src: Handle to source DLL. + - syms_pos: Pointer to a (mutable) pointer which points to the start of an + (immutable) table of symbols and structure offsets. This mutable + pointer is advanced until either a symbol fails to bind, in which case + an error is returned, or the end of the table is reached, in which + case success is returned. + - syms_count: Number of entries in the symbol table. +*/ +HRESULT dll_bind( + void *dest, + HINSTANCE src, + const struct dll_bind_sym **syms_pos, + size_t syms_count); diff --git a/util/meson.build b/util/meson.build index 0b76284..c3c07f9 100644 --- a/util/meson.build +++ b/util/meson.build @@ -11,6 +11,8 @@ util_lib = static_library( 'async.h', 'crc.c', 'crc.h', + 'dll-bind.c', + 'dll-bind.h', 'dprintf.c', 'dprintf.h', 'dump.c', From 92b73df3e723258186c7e2414a880542ab5fe894 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 23 May 2021 11:54:24 -0400 Subject: [PATCH 067/175] Load and bind aimeio at runtime --- board/aime-dll.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ board/aime-dll.h | 25 ++++++++++ board/config.c | 17 +++++++ board/meson.build | 2 + board/sg-reader.c | 25 ++++++---- board/sg-reader.h | 6 ++- chunihook/dllmain.c | 2 +- divahook/dllmain.c | 2 +- idzhook/dllmain.c | 2 +- mu3hook/dllmain.c | 2 +- 10 files changed, 182 insertions(+), 13 deletions(-) create mode 100644 board/aime-dll.c create mode 100644 board/aime-dll.h diff --git a/board/aime-dll.c b/board/aime-dll.c new file mode 100644 index 0000000..0ada7f4 --- /dev/null +++ b/board/aime-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "board/aime-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym aime_dll_syms[] = { + { + .sym = "aime_io_init", + .off = offsetof(struct aime_dll, init), + }, { + .sym = "aime_io_nfc_poll", + .off = offsetof(struct aime_dll, nfc_poll), + }, { + .sym = "aime_io_nfc_get_aime_id", + .off = offsetof(struct aime_dll, nfc_get_aime_id), + }, { + .sym = "aime_io_nfc_get_felica_id", + .off = offsetof(struct aime_dll, nfc_get_felica_id), + }, { + .sym = "aime_io_led_set_color", + .off = offsetof(struct aime_dll, led_set_color), + } +}; + +struct aime_dll aime_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("NFC Assembly: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("NFC Assembly: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "aime_io_get_api_version"); + + if (get_api_version != NULL) { + aime_dll.api_version = get_api_version(); + } else { + aime_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose aime_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (aime_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("NFC Assembly: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + aime_dll.api_version); + + goto end; + } + + sym = aime_dll_syms; + hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("NFC Assembly: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/board/aime-dll.h b/board/aime-dll.h new file mode 100644 index 0000000..354516b --- /dev/null +++ b/board/aime-dll.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "aimeio/aimeio.h" + +struct aime_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*nfc_poll)(uint8_t unit_no); + HRESULT (*nfc_get_aime_id)( + uint8_t unit_no, + uint8_t *luid, + size_t luid_size); + HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm); + void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); +}; + +struct aime_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct aime_dll aime_dll; + +HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self); diff --git a/board/config.c b/board/config.c index 84d8956..e64e28a 100644 --- a/board/config.c +++ b/board/config.c @@ -3,14 +3,31 @@ #include #include #include +#include +#include "board/aime-dll.h" #include "board/config.h" #include "board/sg-reader.h" +static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"aimeio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void aime_config_load(struct aime_config *cfg, const wchar_t *filename) { assert(cfg != NULL); assert(filename != NULL); + aime_dll_config_load(&cfg->dll, filename); cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename); } diff --git a/board/meson.build b/board/meson.build index eb2caa7..d463d9a 100644 --- a/board/meson.build +++ b/board/meson.build @@ -10,6 +10,8 @@ board_lib = static_library( iccard_lib, ], sources : [ + 'aime-dll.c', + 'aime-dll.h', 'config.c', 'config.h', 'guid.c', diff --git a/board/sg-reader.c b/board/sg-reader.c index 5874544..dbf0392 100644 --- a/board/sg-reader.c +++ b/board/sg-reader.c @@ -4,8 +4,7 @@ #include #include -#include "aimeio/aimeio.h" - +#include "board/aime-dll.h" #include "board/sg-led.h" #include "board/sg-nfc.h" #include "board/sg-reader.h" @@ -48,14 +47,24 @@ static struct sg_led sg_reader_led; HRESULT sg_reader_hook_init( const struct aime_config *cfg, - unsigned int port_no) + unsigned int port_no, + HINSTANCE self) { + HRESULT hr; + assert(cfg != NULL); + assert(self != NULL); if (!cfg->enable) { return S_FALSE; } + hr = aime_dll_init(&cfg->dll, self); + + if (FAILED(hr)) { + return hr; + } + sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL); sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL); @@ -111,7 +120,7 @@ static HRESULT sg_reader_handle_irp_locked(struct irp *irp) if (!sg_reader_started) { dprintf("NFC Assembly: Starting backend DLL\n"); - hr = aime_io_init(); + hr = aime_dll.init(); sg_reader_started = true; sg_reader_start_hr = hr; @@ -155,7 +164,7 @@ static HRESULT sg_reader_handle_irp_locked(struct irp *irp) static HRESULT sg_reader_nfc_poll(void *ctx) { - return aime_io_nfc_poll(0); + return aime_dll.nfc_poll(0); } static HRESULT sg_reader_nfc_get_aime_id( @@ -163,15 +172,15 @@ static HRESULT sg_reader_nfc_get_aime_id( uint8_t *luid, size_t luid_size) { - return aime_io_nfc_get_aime_id(0, luid, luid_size); + return aime_dll.nfc_get_aime_id(0, luid, luid_size); } static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm) { - return aime_io_nfc_get_felica_id(0, IDm); + return aime_dll.nfc_get_felica_id(0, IDm); } static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b) { - aime_io_led_set_color(0, r, g, b); + aime_dll.led_set_color(0, r, g, b); } diff --git a/board/sg-reader.h b/board/sg-reader.h index 00b237c..673a8bd 100644 --- a/board/sg-reader.h +++ b/board/sg-reader.h @@ -4,10 +4,14 @@ #include +#include "board/aime-dll.h" + struct aime_config { + struct aime_dll_config dll; bool enable; }; HRESULT sg_reader_hook_init( const struct aime_config *cfg, - unsigned int port_no); + unsigned int port_no, + HINSTANCE self); diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index c6f1ed5..5ffa691 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -87,7 +87,7 @@ static DWORD CALLBACK chuni_pre_startup(void) goto fail; } - hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12); + hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, chuni_hook_mod); if (FAILED(hr)) { goto fail; diff --git a/divahook/dllmain.c b/divahook/dllmain.c index a59a066..067b9c0 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -56,7 +56,7 @@ static DWORD CALLBACK diva_pre_startup(void) goto fail; } - hr = sg_reader_hook_init(&diva_hook_cfg.aime, 10); + hr = sg_reader_hook_init(&diva_hook_cfg.aime, 10, diva_hook_mod); if (FAILED(hr)) { goto fail; diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 80ca7ef..d1dd208 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -57,7 +57,7 @@ static DWORD CALLBACK idz_pre_startup(void) goto fail; } - hr = sg_reader_hook_init(&idz_hook_cfg.aime, 10); + hr = sg_reader_hook_init(&idz_hook_cfg.aime, 10, idz_hook_mod); if (FAILED(hr)) { goto fail; diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 5b62506..08ca989 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -50,7 +50,7 @@ static DWORD CALLBACK mu3_pre_startup(void) goto fail; } - hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1); + hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, mu3_hook_mod); if (FAILED(hr)) { goto fail; From 58664e9b31b1bc610c49bfc61447c0094fe71a67 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 31 May 2021 12:52:53 -0400 Subject: [PATCH 068/175] Add API versioning mechanism to chuniio --- chuniio/chuniio.c | 5 +++++ chuniio/chuniio.h | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index be81d64..91971f3 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -16,6 +16,11 @@ static HANDLE chuni_io_slider_thread; static bool chuni_io_slider_stop_flag; static struct chuni_io_config chuni_io_cfg; +uint16_t chuni_io_get_api_version(void) +{ + return 0x0100; +} + HRESULT chuni_io_jvs_init(void) { chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); diff --git a/chuniio/chuniio.h b/chuniio/chuniio.h index 0823272..8d0bc1c 100644 --- a/chuniio/chuniio.h +++ b/chuniio/chuniio.h @@ -5,13 +5,24 @@ #include #include +/* Get the version of the Chunithm IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t chuni_io_get_api_version(void); + /* Initialize JVS-based input. This function will be called before any other chuni_io_jvs_*() function calls. Errors returned from this function will manifest as a disconnected JVS bus. All subsequent calls may originate from arbitrary threads and some may overlap with each other. Ensuring synchronization inside your IO DLL is - your responsibility. */ + your responsibility. + + Minimum API version: 0x0100 */ HRESULT chuni_io_jvs_init(void); @@ -28,13 +39,17 @@ HRESULT chuni_io_jvs_init(void); Note that you cannot instantly break the entire IR grid in a single frame to simulate hand movement; this will be judged as a miss. You need to simulate a gradual raising and lowering of the hands. Consult the proof-of-concept - implementation for details. */ + implementation for details. + + Minimum API version: 0x0100 */ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); /* Read the current state of the coin counter. This value should be incremented for every coin detected by the coin acceptor mechanism. This count does not - need to persist beyond the lifetime of the process. */ + need to persist beyond the lifetime of the process. + + Minimum API version: 0x0100 */ void chuni_io_jvs_read_coin_counter(uint16_t *total); @@ -49,7 +64,9 @@ void chuni_io_jvs_set_coin_blocker(bool open); All subsequent calls may originate from arbitrary threads and some may overlap with each other. Ensuring synchronization inside your IO DLL is - your responsibility. */ + your responsibility. + + Minimum API version: 0x0100 */ HRESULT chuni_io_slider_init(void); @@ -83,7 +100,9 @@ typedef void (*chuni_io_slider_callback_t)(const uint8_t *state); preferred interval then 1 kHz is a reasonable maximum frequency. Note that you do have to have to call the callback "occasionally" even if - nothing is changing, otherwise the game will raise a comm timeout error. */ + nothing is changing, otherwise the game will raise a comm timeout error. + + Minimum API version: 0x0100 */ void chuni_io_slider_start(chuni_io_slider_callback_t callback); @@ -96,13 +115,17 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback); Following on from the above, the slider polling loop *will* be restarted after being stopped in the course of regular operation. Do not permanently - tear down your input driver in response to this function call. */ + tear down your input driver in response to this function call. + + Minimum API version: 0x0100 */ void chuni_io_slider_stop(void); /* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96 bytes is supplied. The illuminated areas on the touch slider are some combination of rectangular regions and dividing lines between these regions - but the exact mapping of this lighting control buffer is still TBD. */ + but the exact mapping of this lighting control buffer is still TBD. + + Minimum API version: 0x0100 */ void chuni_io_slider_set_leds(const uint8_t *rgb); From 01eda0fbd5a784d07c68d074a9edcecffeb9d95f Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 31 May 2021 12:54:14 -0400 Subject: [PATCH 069/175] Statically link default chuniio --- chunihook/meson.build | 2 +- chuniio/chuniio.def | 11 ----------- chuniio/meson.build | 3 +-- 3 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 chuniio/chuniio.def diff --git a/chunihook/meson.build b/chunihook/meson.build index 9fb6486..9279c38 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -13,7 +13,7 @@ shared_library( aimeio_lib, amex_lib, board_lib, - chuniio_dll, + chuniio_lib, hooklib_lib, jvs_lib, platform_lib, diff --git a/chuniio/chuniio.def b/chuniio/chuniio.def deleted file mode 100644 index f3ba8c6..0000000 --- a/chuniio/chuniio.def +++ /dev/null @@ -1,11 +0,0 @@ -LIBRARY chuniio - -EXPORTS - chuni_io_jvs_init - chuni_io_jvs_poll - chuni_io_jvs_read_coin_counter - chuni_io_jvs_set_coin_blocker - chuni_io_slider_init - chuni_io_slider_set_leds - chuni_io_slider_start - chuni_io_slider_stop diff --git a/chuniio/meson.build b/chuniio/meson.build index 2e15e4d..9e61229 100644 --- a/chuniio/meson.build +++ b/chuniio/meson.build @@ -1,9 +1,8 @@ -chuniio_dll = shared_library( +chuniio_lib = static_library( 'chuniio', name_prefix : '', include_directories : inc, implicit_include_directories : false, - vs_module_defs : 'chuniio.def', c_pch : '../precompiled.h', sources : [ 'chuniio.c', From 94e9edb5a06dc87017f7fe4669c8da9d5c7d6a07 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 31 May 2021 12:58:22 -0400 Subject: [PATCH 070/175] Load and bind chuniio at runtime --- chunihook/chuni-dll.c | 121 ++++++++++++++++++++++++++++++++++++++++ chunihook/chuni-dll.h | 25 +++++++++ chunihook/chunihook.def | 9 +++ chunihook/config.c | 17 ++++++ chunihook/config.h | 5 ++ chunihook/dllmain.c | 6 ++ chunihook/jvs.c | 13 +++-- chunihook/meson.build | 2 + chunihook/slider.c | 22 +++++--- 9 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 chunihook/chuni-dll.c create mode 100644 chunihook/chuni-dll.h diff --git a/chunihook/chuni-dll.c b/chunihook/chuni-dll.c new file mode 100644 index 0000000..3750c86 --- /dev/null +++ b/chunihook/chuni-dll.c @@ -0,0 +1,121 @@ +#include + +#include +#include + +#include "chunihook/chuni-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym chuni_dll_syms[] = { + { + .sym = "chuni_io_jvs_init", + .off = offsetof(struct chuni_dll, jvs_init), + }, { + .sym = "chuni_io_jvs_poll", + .off = offsetof(struct chuni_dll, jvs_poll), + }, { + .sym = "chuni_io_jvs_read_coin_counter", + .off = offsetof(struct chuni_dll, jvs_read_coin_counter), + }, { + .sym = "chuni_io_jvs_set_coin_blocker", + .off = offsetof(struct chuni_dll, jvs_set_coin_blocker), + }, { + .sym = "chuni_io_slider_init", + .off = offsetof(struct chuni_dll, slider_init), + }, { + .sym = "chuni_io_slider_start", + .off = offsetof(struct chuni_dll, slider_start), + }, { + .sym = "chuni_io_slider_stop", + .off = offsetof(struct chuni_dll, slider_stop), + }, { + .sym = "chuni_io_slider_set_leds", + .off = offsetof(struct chuni_dll, slider_set_leds), + } +}; + +struct chuni_dll chuni_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Chunithm IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version"); + + if (get_api_version != NULL) { + chuni_dll.api_version = get_api_version(); + } else { + chuni_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose chuni_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (chuni_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Chunithm IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + chuni_dll.api_version); + + goto end; + } + + sym = chuni_dll_syms; + hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Chunithm IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/chunihook/chuni-dll.h b/chunihook/chuni-dll.h new file mode 100644 index 0000000..e4a24f8 --- /dev/null +++ b/chunihook/chuni-dll.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "chuniio/chuniio.h" + +struct chuni_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); + void (*jvs_read_coin_counter)(uint16_t *total); + void (*jvs_set_coin_blocker)(bool open); + HRESULT (*slider_init)(void); + void (*slider_start)(chuni_io_slider_callback_t callback); + void (*slider_stop)(void); + void (*slider_set_leds)(const uint8_t *rgb); +}; + +struct chuni_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct chuni_dll chuni_dll; + +HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self); diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def index 9da4d7f..5b310a5 100644 --- a/chunihook/chunihook.def +++ b/chunihook/chunihook.def @@ -12,3 +12,12 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + chuni_io_get_api_version + chuni_io_jvs_init + chuni_io_jvs_poll + chuni_io_jvs_read_coin_counter + chuni_io_jvs_set_coin_blocker + chuni_io_slider_init + chuni_io_slider_set_leds + chuni_io_slider_start + chuni_io_slider_stop diff --git a/chunihook/config.c b/chunihook/config.c index 66b3dc4..3016445 100644 --- a/chunihook/config.c +++ b/chunihook/config.c @@ -18,6 +18,22 @@ #include "platform/config.h" #include "platform/platform.h" +void chuni_dll_config_load( + struct chuni_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"chuniio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void slider_config_load(struct slider_config *cfg, const wchar_t *filename) { assert(cfg != NULL); @@ -39,5 +55,6 @@ void chuni_hook_config_load( amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); gfx_config_load(&cfg->gfx, filename); + chuni_dll_config_load(&cfg->dll, filename); slider_config_load(&cfg->slider, filename); } diff --git a/chunihook/config.h b/chunihook/config.h index 8d9e66d..d3c094a 100644 --- a/chunihook/config.h +++ b/chunihook/config.h @@ -7,6 +7,7 @@ #include "board/sg-reader.h" +#include "chunihook/chuni-dll.h" #include "chunihook/slider.h" #include "hooklib/gfx.h" @@ -18,9 +19,13 @@ struct chuni_hook_config { struct amex_config amex; struct aime_config aime; struct gfx_config gfx; + struct chuni_dll_config dll; struct slider_config slider; }; +void chuni_dll_config_load( + struct chuni_dll_config *cfg, + const wchar_t *filename); void slider_config_load(struct slider_config *cfg, const wchar_t *filename); void chuni_hook_config_load( struct chuni_hook_config *cfg, diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index 5ffa691..f2dff5e 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -75,6 +75,12 @@ static DWORD CALLBACK chuni_pre_startup(void) goto fail; } + hr = chuni_dll_init(&chuni_hook_cfg.dll, chuni_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = amex_hook_init(&chuni_hook_cfg.amex, chunithm_jvs_init); if (FAILED(hr)) { diff --git a/chunihook/jvs.c b/chunihook/jvs.c index a036fb7..a57c2a3 100644 --- a/chunihook/jvs.c +++ b/chunihook/jvs.c @@ -9,7 +9,7 @@ #include "board/io3.h" -#include "chuniio/chuniio.h" +#include "chunihook/chuni-dll.h" #include "jvs/jvs-bus.h" @@ -47,9 +47,10 @@ HRESULT chunithm_jvs_init(struct jvs_node **out) HRESULT hr; assert(out != NULL); + assert(chuni_dll.jvs_init != NULL); - dprintf("JVS I/O: Starting Chunithm backend DLL\n"); - hr = chuni_io_jvs_init(); + dprintf("JVS I/O: Starting IO backend\n"); + hr = chuni_dll.jvs_init(); if (FAILED(hr)) { dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); @@ -70,11 +71,12 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) size_t i; assert(out != NULL); + assert(chuni_dll.jvs_poll != NULL); opbtn = 0; beams = 0; - chuni_io_jvs_poll(&opbtn, &beams); + chuni_dll.jvs_poll(&opbtn, &beams); out->system = 0x00; out->p1 = 0x0000; @@ -105,10 +107,11 @@ static void chunithm_jvs_read_coin_counter( uint16_t *out) { assert(out != NULL); + assert(chuni_dll.jvs_read_coin_counter != NULL); if (slot_no > 0) { return; } - chuni_io_jvs_read_coin_counter(out); + chuni_dll.jvs_read_coin_counter(out); } diff --git a/chunihook/meson.build b/chunihook/meson.build index 9279c38..e2dfa08 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -20,6 +20,8 @@ shared_library( util_lib, ], sources : [ + 'chuni-dll.c', + 'chuni-dll.h', 'config.c', 'config.h', 'dllmain.c', diff --git a/chunihook/slider.c b/chunihook/slider.c index f4a2bfd..753a608 100644 --- a/chunihook/slider.c +++ b/chunihook/slider.c @@ -9,10 +9,9 @@ #include "board/slider-cmd.h" #include "board/slider-frame.h" +#include "chunihook/chuni-dll.h" #include "chunihook/slider.h" -#include "chuniio/chuniio.h" - #include "hook/iobuf.h" #include "hook/iohook.h" @@ -41,6 +40,7 @@ static uint8_t slider_readable_bytes[520]; HRESULT slider_hook_init(const struct slider_config *cfg) { assert(cfg != NULL); + assert(chuni_dll.slider_init != NULL); if (!cfg->enable) { return S_FALSE; @@ -81,11 +81,11 @@ static HRESULT slider_handle_irp_locked(struct irp *irp) HRESULT hr; if (irp->op == IRP_OP_OPEN) { - dprintf("Chunithm slider: Starting backend DLL\n"); - hr = chuni_io_slider_init(); + dprintf("Chunithm slider: Starting backend\n"); + hr = chuni_dll.slider_init(); if (FAILED(hr)) { - dprintf("Chunithm slider: Backend DLL error: %x\n", (int) hr); + dprintf("Chunithm slider: Backend error: %x\n", (int) hr); return hr; } @@ -189,8 +189,10 @@ static HRESULT slider_req_get_board_info(void) static HRESULT slider_req_auto_scan_start(void) { + assert(chuni_dll.slider_start != NULL); + dprintf("Chunithm slider: Start slider notifications\n"); - chuni_io_slider_start(slider_res_auto_scan); + chuni_dll.slider_start(slider_res_auto_scan); /* This message is not acknowledged */ @@ -201,6 +203,8 @@ static HRESULT slider_req_auto_scan_stop(void) { struct slider_hdr resp; + assert(chuni_dll.slider_stop != NULL); + dprintf("Chunithm slider: Stop slider notifications\n"); /* IO DLL worker thread might attempt to invoke the callback (which needs @@ -209,7 +213,7 @@ static HRESULT slider_req_auto_scan_stop(void) situation. */ LeaveCriticalSection(&slider_lock); - chuni_io_slider_stop(); + chuni_dll.slider_stop(); EnterCriticalSection(&slider_lock); resp.sync = SLIDER_FRAME_SYNC; @@ -221,7 +225,9 @@ static HRESULT slider_req_auto_scan_stop(void) static HRESULT slider_req_set_led(const struct slider_req_set_led *req) { - chuni_io_slider_set_leds(req->payload.rgb); + assert(chuni_dll.slider_set_leds != NULL); + + chuni_dll.slider_set_leds(req->payload.rgb); /* This message is not acknowledged */ From bda236b5adb04a42aeddb8e0171a670fe7ce0c7d Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 31 May 2021 13:00:22 -0400 Subject: [PATCH 071/175] chuniio 0x0101: Fix IR beam mapping --- chunihook/jvs.c | 25 ++++++++++++++++++++++--- chuniio/chuniio.c | 2 +- chuniio/chuniio.h | 19 +++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/chunihook/jvs.c b/chunihook/jvs.c index a57c2a3..8c630d4 100644 --- a/chunihook/jvs.c +++ b/chunihook/jvs.c @@ -31,7 +31,8 @@ static const struct io3_ops chunithm_jvs_io3_ops = { .read_coin_counter = chunithm_jvs_read_coin_counter, }; -static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = { +// Incorrect IR beam mappings retained for backward compatibility +static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks_v1[] = { { 0x0000, 0x0020 }, { 0x0020, 0x0000 }, { 0x0000, 0x0010 }, @@ -40,6 +41,15 @@ static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = { { 0x0008, 0x0000 }, }; +static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = { + { 0x0020, 0x0000 }, + { 0x0000, 0x0020 }, + { 0x0010, 0x0000 }, + { 0x0000, 0x0010 }, + { 0x0008, 0x0000 }, + { 0x0000, 0x0008 }, +}; + static struct io3 chunithm_jvs_io3; HRESULT chunithm_jvs_init(struct jvs_node **out) @@ -66,6 +76,7 @@ HRESULT chunithm_jvs_init(struct jvs_node **out) static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) { + const struct chunithm_jvs_ir_mask *masks; uint8_t opbtn; uint8_t beams; size_t i; @@ -73,6 +84,14 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) assert(out != NULL); assert(chuni_dll.jvs_poll != NULL); + if (chuni_dll.api_version >= 0x0101) { + // Use correct mapping + masks = chunithm_jvs_ir_masks; + } else { + // Use backwards-compatible incorrect mapping + masks = chunithm_jvs_ir_masks_v1; + } + opbtn = 0; beams = 0; @@ -95,8 +114,8 @@ static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out) for (i = 0 ; i < 6 ; i++) { /* Beam "press" is active-low hence the ~ */ if (~beams & (1 << i)) { - out->p1 |= chunithm_jvs_ir_masks[i].p1; - out->p2 |= chunithm_jvs_ir_masks[i].p2; + out->p1 |= masks[i].p1; + out->p2 |= masks[i].p2; } } } diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index 91971f3..8813e68 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -18,7 +18,7 @@ static struct chuni_io_config chuni_io_cfg; uint16_t chuni_io_get_api_version(void) { - return 0x0100; + return 0x0101; } HRESULT chuni_io_jvs_init(void) diff --git a/chuniio/chuniio.h b/chuniio/chuniio.h index 8d0bc1c..87e7e76 100644 --- a/chuniio/chuniio.h +++ b/chuniio/chuniio.h @@ -1,5 +1,15 @@ #pragma once +/* + CHUNITHM CUSTOM IO API + + Changelog: + + - 0x0100: Initial API version (assumed if chuni_io_get_api_version is not + exported) + - 0x0101: Fix IR beam mappings +*/ + #include #include @@ -10,7 +20,7 @@ the major version and the low byte is the minor version (as defined by the Semantic Versioning standard). - The latest API version as of this writing is 0x0100. */ + The latest API version as of this writing is 0x0101. */ uint16_t chuni_io_get_api_version(void); @@ -41,7 +51,12 @@ HRESULT chuni_io_jvs_init(void); a gradual raising and lowering of the hands. Consult the proof-of-concept implementation for details. - Minimum API version: 0x0100 */ + NOTE: Previous releases of Segatools mapped the IR beam inputs incorrectly. + Please ensure that you advertise an API version of at least 0x0101 so that + the correct mapping can be used. + + Minimum API version: 0x0100 + Latest API version: 0x0101 */ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); From 8dddb18793506af49b7feae36de17a97c9bf5717 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 09:39:00 -0400 Subject: [PATCH 072/175] Statically link default divaio --- divahook/divahook.def | 9 +++++++++ divahook/meson.build | 2 +- divaio/divaio.def | 11 ----------- divaio/meson.build | 3 +-- 4 files changed, 11 insertions(+), 14 deletions(-) delete mode 100644 divaio/divaio.def diff --git a/divahook/divahook.def b/divahook/divahook.def index 49075b8..a8f5d10 100644 --- a/divahook/divahook.def +++ b/divahook/divahook.def @@ -12,3 +12,12 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + diva_io_get_api_version + diva_io_jvs_init + diva_io_jvs_poll + diva_io_jvs_read_coin_counter + diva_io_jvs_set_coin_blocker + diva_io_slider_init + diva_io_slider_set_leds + diva_io_slider_start + diva_io_slider_stop diff --git a/divahook/meson.build b/divahook/meson.build index 1b09193..6310141 100644 --- a/divahook/meson.build +++ b/divahook/meson.build @@ -13,7 +13,7 @@ shared_library( aimeio_lib, amex_lib, board_lib, - divaio_dll, + divaio_lib, hooklib_lib, jvs_lib, platform_lib, diff --git a/divaio/divaio.def b/divaio/divaio.def deleted file mode 100644 index 3191f7b..0000000 --- a/divaio/divaio.def +++ /dev/null @@ -1,11 +0,0 @@ -LIBRARY divaio - -EXPORTS - diva_io_jvs_init - diva_io_jvs_poll - diva_io_jvs_read_coin_counter - diva_io_jvs_set_coin_blocker - diva_io_slider_init - diva_io_slider_set_leds - diva_io_slider_start - diva_io_slider_stop diff --git a/divaio/meson.build b/divaio/meson.build index 30eda9b..edac3b6 100644 --- a/divaio/meson.build +++ b/divaio/meson.build @@ -1,9 +1,8 @@ -divaio_dll = shared_library( +divaio_lib = static_library( 'divaio', name_prefix : '', include_directories : inc, implicit_include_directories : false, - vs_module_defs : 'divaio.def', c_pch : '../precompiled.h', sources : [ 'divaio.c', From a940c0b47bcd8619658b36a99ed7f7010675a627 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 09:40:55 -0400 Subject: [PATCH 073/175] Add api versioning to divaio --- divaio/divaio.c | 5 +++++ divaio/divaio.h | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/divaio/divaio.c b/divaio/divaio.c index 4982482..7db2075 100644 --- a/divaio/divaio.c +++ b/divaio/divaio.c @@ -16,6 +16,11 @@ static HANDLE diva_io_slider_thread; static bool diva_io_slider_stop_flag; static struct diva_io_config diva_io_cfg; +uint16_t diva_io_get_api_version(void) +{ + return 0x0100; +} + HRESULT diva_io_jvs_init(void) { diva_io_config_load(&diva_io_cfg, L".\\segatools.ini"); diff --git a/divaio/divaio.h b/divaio/divaio.h index 3de0d1a..202704e 100644 --- a/divaio/divaio.h +++ b/divaio/divaio.h @@ -5,13 +5,24 @@ #include #include +/* Get the version of the Project Diva IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t diva_io_get_api_version(void); + /* Initialize JVS-based input. This function will be called before any other diva_io_jvs_*() function calls. Errors returned from this function will manifest as a disconnected JVS bus. All subsequent calls may originate from arbitrary threads and some may overlap with each other. Ensuring synchronization inside your IO DLL is - your responsibility. */ + your responsibility. + + Minimum API version: 0x0100 */ HRESULT diva_io_jvs_init(void); @@ -22,13 +33,17 @@ HRESULT diva_io_jvs_init(void); gamebtn bits, from least significant to most significant, are: - Circle Cross Square Triangle Start UNUSED UNUSED UNUSED */ + Circle Cross Square Triangle Start UNUSED UNUSED UNUSED + + Minimum API version: 0x0100 */ void diva_io_jvs_poll(uint8_t *opbtn, uint8_t *gamebtn); /* Read the current state of the coin counter. This value should be incremented for every coin detected by the coin acceptor mechanism. This count does not - need to persist beyond the lifetime of the process. */ + need to persist beyond the lifetime of the process. + + Minimum API Version: 0x0100 */ void diva_io_jvs_read_coin_counter(uint16_t *out); @@ -43,7 +58,9 @@ void diva_io_jvs_set_coin_blocker(bool open); All subsequent calls may originate from arbitrary threads and some may overlap with each other. Ensuring synchronization inside your IO DLL is - your responsibility. */ + your responsibility. + + Minimum API version: 0x0100 */ HRESULT diva_io_slider_init(void); @@ -67,7 +84,9 @@ typedef void (*diva_io_slider_callback_t)(const uint8_t *state); preferred interval then 1 kHz is a reasonable maximum frequency. Note that you do have to have to call the callback "occasionally" even if - nothing is changing, otherwise the game will raise a comm timeout error. */ + nothing is changing, otherwise the game will raise a comm timeout error. + + Minimum API version: 0x0100 */ void diva_io_slider_start(diva_io_slider_callback_t callback); @@ -80,11 +99,15 @@ void diva_io_slider_start(diva_io_slider_callback_t callback); Following on from the above, the slider polling loop *will* be restarted after being stopped in the course of regular operation. Do not permanently - tear down your input driver in response to this function call. */ + tear down your input driver in response to this function call. + + Minimum API version: 0x0100 */ void diva_io_slider_stop(void); /* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96 - bytes is supplied. Layout is probably strictly linear but still TBD. */ + bytes is supplied. Layout is probably strictly linear but still TBD. + + Minimum API version: 0x0100 */ void diva_io_slider_set_leds(const uint8_t *rgb); From 4c2c941a1ce326cf6443630c0e1017d421c88e27 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 09:38:20 -0400 Subject: [PATCH 074/175] Load and bind divaio at runtime --- divahook/config.c | 18 +++++++ divahook/config.h | 5 ++ divahook/diva-dll.c | 121 +++++++++++++++++++++++++++++++++++++++++++ divahook/diva-dll.h | 25 +++++++++ divahook/dllmain.c | 7 +++ divahook/jvs.c | 12 +++-- divahook/meson.build | 2 + divahook/slider.c | 19 ++++--- 8 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 divahook/diva-dll.c create mode 100644 divahook/diva-dll.h diff --git a/divahook/config.c b/divahook/config.c index 7c21e46..29ca5c8 100644 --- a/divahook/config.c +++ b/divahook/config.c @@ -1,5 +1,6 @@ #include #include +#include #include "amex/amex.h" #include "amex/config.h" @@ -12,6 +13,22 @@ #include "platform/config.h" #include "platform/platform.h" +void diva_dll_config_load( + struct diva_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"divaio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void slider_config_load(struct slider_config *cfg, const wchar_t *filename) { assert(cfg != NULL); @@ -30,5 +47,6 @@ void diva_hook_config_load( platform_config_load(&cfg->platform, filename); amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); + diva_dll_config_load(&cfg->dll, filename); slider_config_load(&cfg->slider, filename); } diff --git a/divahook/config.h b/divahook/config.h index 3e484d5..a327f47 100644 --- a/divahook/config.h +++ b/divahook/config.h @@ -6,6 +6,7 @@ #include "board/sg-reader.h" +#include "divahook/diva-dll.h" #include "divahook/slider.h" #include "platform/platform.h" @@ -14,9 +15,13 @@ struct diva_hook_config { struct platform_config platform; struct amex_config amex; struct aime_config aime; + struct diva_dll_config dll; struct slider_config slider; }; +void diva_dll_config_load( + struct diva_dll_config *cfg, + const wchar_t *filename); void slider_config_load(struct slider_config *cfg, const wchar_t *filename); void diva_hook_config_load( struct diva_hook_config *cfg, diff --git a/divahook/diva-dll.c b/divahook/diva-dll.c new file mode 100644 index 0000000..c871dd9 --- /dev/null +++ b/divahook/diva-dll.c @@ -0,0 +1,121 @@ +#include + +#include +#include + +#include "divahook/diva-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym diva_dll_syms[] = { + { + .sym = "diva_io_jvs_init", + .off = offsetof(struct diva_dll, jvs_init), + }, { + .sym = "diva_io_jvs_poll", + .off = offsetof(struct diva_dll, jvs_poll), + }, { + .sym = "diva_io_jvs_read_coin_counter", + .off = offsetof(struct diva_dll, jvs_read_coin_counter), + }, { + .sym = "diva_io_jvs_set_coin_blocker", + .off = offsetof(struct diva_dll, jvs_set_coin_blocker), + }, { + .sym = "diva_io_slider_init", + .off = offsetof(struct diva_dll, slider_init), + }, { + .sym = "diva_io_slider_start", + .off = offsetof(struct diva_dll, slider_start), + }, { + .sym = "diva_io_slider_stop", + .off = offsetof(struct diva_dll, slider_stop), + }, { + .sym = "diva_io_slider_set_leds", + .off = offsetof(struct diva_dll, slider_set_leds), + } +}; + +struct diva_dll diva_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Diva IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Diva IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "diva_io_get_api_version"); + + if (get_api_version != NULL) { + diva_dll.api_version = get_api_version(); + } else { + diva_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose diva_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (diva_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Diva IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + diva_dll.api_version); + + goto end; + } + + sym = diva_dll_syms; + hr = dll_bind(&diva_dll, src, &sym, _countof(diva_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Diva IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/divahook/diva-dll.h b/divahook/diva-dll.h new file mode 100644 index 0000000..fe78ff2 --- /dev/null +++ b/divahook/diva-dll.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "divaio/divaio.h" + +struct diva_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); + void (*jvs_read_coin_counter)(uint16_t *total); + void (*jvs_set_coin_blocker)(bool open); + HRESULT (*slider_init)(void); + void (*slider_start)(diva_io_slider_callback_t callback); + void (*slider_stop)(void); + void (*slider_set_leds)(const uint8_t *rgb); +}; + +struct diva_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct diva_dll diva_dll; + +HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self); diff --git a/divahook/dllmain.c b/divahook/dllmain.c index 067b9c0..fd6ad9b 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -7,6 +7,7 @@ #include "board/sg-reader.h" #include "divahook/config.h" +#include "divahook/diva-dll.h" #include "divahook/jvs.h" #include "divahook/slider.h" @@ -50,6 +51,12 @@ static DWORD CALLBACK diva_pre_startup(void) goto fail; } + hr = diva_dll_init(&diva_hook_cfg.dll, diva_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = amex_hook_init(&diva_hook_cfg.amex, diva_jvs_init); if (FAILED(hr)) { diff --git a/divahook/jvs.c b/divahook/jvs.c index ebfaaae..14d222d 100644 --- a/divahook/jvs.c +++ b/divahook/jvs.c @@ -9,7 +9,7 @@ #include "board/io3.h" -#include "divaio/divaio.h" +#include "divahook/diva-dll.h" #include "jvs/jvs-bus.h" @@ -33,9 +33,10 @@ HRESULT diva_jvs_init(struct jvs_node **out) HRESULT hr; assert(out != NULL); + assert(diva_dll.jvs_init != NULL); dprintf("JVS I/O: Starting Diva backend DLL\n"); - hr = diva_io_jvs_init(); + hr = diva_dll.jvs_init(); if (FAILED(hr)) { dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); @@ -55,11 +56,12 @@ static void diva_jvs_read_switches(void *ctx, struct io3_switch_state *out) uint8_t gamebtn; assert(out != NULL); + assert(diva_dll.jvs_poll != NULL); opbtn = 0; gamebtn = 0; - diva_io_jvs_poll(&opbtn, &gamebtn); + diva_dll.jvs_poll(&opbtn, &gamebtn); if (gamebtn & 0x01) { out->p1 |= 1 << 6; @@ -97,9 +99,11 @@ static void diva_jvs_read_coin_counter( uint8_t slot_no, uint16_t *out) { + assert(diva_dll.jvs_read_coin_counter != NULL); + if (slot_no > 0) { return; } - diva_io_jvs_read_coin_counter(out); + diva_dll.jvs_read_coin_counter(out); } diff --git a/divahook/meson.build b/divahook/meson.build index 6310141..20c1ff2 100644 --- a/divahook/meson.build +++ b/divahook/meson.build @@ -22,6 +22,8 @@ shared_library( sources : [ 'config.c', 'config.h', + 'diva-dll.c', + 'diva-dll.h', 'dllmain.c', 'jvs.c', 'jvs.h', diff --git a/divahook/slider.c b/divahook/slider.c index 33a569e..8294923 100644 --- a/divahook/slider.c +++ b/divahook/slider.c @@ -8,10 +8,9 @@ #include "board/slider-cmd.h" #include "board/slider-frame.h" +#include "divahook/diva-dll.h" #include "divahook/slider.h" -#include "divaio/divaio.h" - #include "hook/iobuf.h" #include "hook/iohook.h" @@ -80,9 +79,11 @@ static HRESULT slider_handle_irp_locked(struct irp *irp) struct iobuf req_iobuf; HRESULT hr; + assert(diva_dll.slider_init != NULL); + if (irp->op == IRP_OP_OPEN) { dprintf("Diva slider: Starting backend DLL\n"); - hr = diva_io_slider_init(); + hr = diva_dll.slider_init(); if (FAILED(hr)) { dprintf("Diva slider: Backend DLL error: %x\n", (int) hr); @@ -207,8 +208,10 @@ static HRESULT slider_req_get_board_info(void) static HRESULT slider_req_auto_scan_start(void) { + assert(diva_dll.slider_start != NULL); + dprintf("Diva slider: Start slider thread\n"); - diva_io_slider_start(slider_res_auto_scan); + diva_dll.slider_start(slider_res_auto_scan); /* This message is not acknowledged */ @@ -219,6 +222,8 @@ static HRESULT slider_req_auto_scan_stop(void) { struct slider_hdr resp; + assert(diva_dll.slider_stop != NULL); + dprintf("Diva slider: Stop slider thread\n"); /* IO DLL worker thread might attempt to invoke the callback (which needs @@ -227,7 +232,7 @@ static HRESULT slider_req_auto_scan_stop(void) situation. */ LeaveCriticalSection(&slider_lock); - diva_io_slider_stop(); + diva_dll.slider_stop(); EnterCriticalSection(&slider_lock); resp.sync = SLIDER_FRAME_SYNC; @@ -239,8 +244,10 @@ static HRESULT slider_req_auto_scan_stop(void) static HRESULT slider_req_set_led(const struct slider_req_set_led *req) { + assert(diva_dll.slider_set_leds != NULL); + /* This message is not acknowledged */ - diva_io_slider_set_leds(req->payload.rgb); + diva_dll.slider_set_leds(req->payload.rgb); return S_OK; } From 98d8b0c3b9af5d7a03cdb4740b26d7c6d790d897 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 09:49:09 -0400 Subject: [PATCH 075/175] Fix comment in idzio/di.c --- idzio/di.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/idzio/di.c b/idzio/di.c index f542f5d..3ee27ff 100644 --- a/idzio/di.c +++ b/idzio/di.c @@ -98,10 +98,11 @@ HRESULT idz_di_init( } /* Initial D Zero has some built-in DirectInput support that is not - particularly useful. We short this out by dropping a no-op dinput8.dll - into the install directory. However, this DLL does need to talk to the - real operating system implementation of DirectInput without the stub DLL - interfering, so build a path to C:\Windows\System32\dinput.dll here. */ + particularly useful. idzhook shorts this out by redirecting dinput8.dll + to a no-op implementation of DirectInput. However, idzio does need to + talk to the real operating system implementation of DirectInput without + the stub DLL interfering, so build a path to + C:\Windows\System32\dinput.dll here. */ dll_path[0] = L'\0'; path_pos = GetSystemDirectoryW(dll_path, _countof(dll_path)); From 3c740af6ec27af334569c0869fd1fe992630c4b3 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 14:23:55 -0400 Subject: [PATCH 076/175] Load and bind mu3io at runtime --- mu3hook/config.c | 17 +++++++ mu3hook/config.h | 7 +++ mu3hook/dllmain.c | 7 +++ mu3hook/io4.c | 19 +++++--- mu3hook/meson.build | 2 + mu3hook/mu3-dll.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ mu3hook/mu3-dll.h | 22 +++++++++ mu3hook/mu3hook.def | 5 ++ 8 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 mu3hook/mu3-dll.c create mode 100644 mu3hook/mu3-dll.h diff --git a/mu3hook/config.c b/mu3hook/config.c index c611131..24086d8 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -10,6 +10,22 @@ #include "platform/config.h" +void mu3_dll_config_load( + struct mu3_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"mu3io", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void mu3_hook_config_load( struct mu3_hook_config *cfg, const wchar_t *filename) @@ -20,4 +36,5 @@ void mu3_hook_config_load( platform_config_load(&cfg->platform, filename); aime_config_load(&cfg->aime, filename); gfx_config_load(&cfg->gfx, filename); + mu3_dll_config_load(&cfg->dll, filename); } diff --git a/mu3hook/config.h b/mu3hook/config.h index e6ef4df..6b2afaa 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -6,14 +6,21 @@ #include "hooklib/gfx.h" +#include "mu3hook/mu3-dll.h" + #include "platform/config.h" struct mu3_hook_config { struct platform_config platform; struct aime_config aime; struct gfx_config gfx; + struct mu3_dll_config dll; }; +void mu3_dll_config_load( + struct mu3_dll_config *cfg, + const wchar_t *filename); + void mu3_hook_config_load( struct mu3_hook_config *cfg, const wchar_t *filename); diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 08ca989..c7fe361 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -13,6 +13,7 @@ #include "mu3hook/config.h" #include "mu3hook/io4.h" +#include "mu3hook/mu3-dll.h" #include "mu3hook/unity.h" #include "platform/platform.h" @@ -62,6 +63,12 @@ static DWORD CALLBACK mu3_pre_startup(void) goto fail; } + hr = mu3_dll_init(&mu3_hook_cfg.dll, mu3_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = mu3_io4_hook_init(); if (FAILED(hr)) { diff --git a/mu3hook/io4.c b/mu3hook/io4.c index 5a0b4c0..1fd875a 100644 --- a/mu3hook/io4.c +++ b/mu3hook/io4.c @@ -6,7 +6,7 @@ #include "board/io4.h" -#include "mu3io/mu3io.h" +#include "mu3hook/mu3-dll.h" #include "util/dprintf.h" @@ -20,13 +20,15 @@ HRESULT mu3_io4_hook_init(void) { HRESULT hr; + assert(mu3_dll.init != NULL); + hr = io4_hook_init(&mu3_io4_ops, NULL); if (FAILED(hr)) { return hr; } - return mu3_io_init(); + return mu3_dll.init(); } static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) @@ -37,9 +39,14 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) int16_t lever; HRESULT hr; + assert(mu3_dll.poll != NULL); + assert(mu3_dll.get_opbtns != NULL); + assert(mu3_dll.get_gamebtns != NULL); + assert(mu3_dll.get_lever != NULL); + memset(state, 0, sizeof(*state)); - hr = mu3_io_poll(); + hr = mu3_dll.poll(); if (FAILED(hr)) { return hr; @@ -50,9 +57,9 @@ static HRESULT mu3_io4_poll(void *ctx, struct io4_state *state) right = 0; lever = 0; - mu3_io_get_opbtns(&opbtn); - mu3_io_get_gamebtns(&left, &right); - mu3_io_get_lever(&lever); + mu3_dll.get_opbtns(&opbtn); + mu3_dll.get_gamebtns(&left, &right); + mu3_dll.get_lever(&lever); if (opbtn & MU3_IO_OPBTN_TEST) { state->buttons[0] |= IO4_BUTTON_TEST; diff --git a/mu3hook/meson.build b/mu3hook/meson.build index f777b9d..9931b8b 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -24,6 +24,8 @@ shared_library( 'dllmain.c', 'io4.c', 'io4.h', + 'mu3-dll.c', + 'mu3-dll.h', 'unity.h', 'unity.c', ], diff --git a/mu3hook/mu3-dll.c b/mu3hook/mu3-dll.c new file mode 100644 index 0000000..15ed6bb --- /dev/null +++ b/mu3hook/mu3-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "mu3hook/mu3-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym mu3_dll_syms[] = { + { + .sym = "mu3_io_init", + .off = offsetof(struct mu3_dll, init), + }, { + .sym = "mu3_io_poll", + .off = offsetof(struct mu3_dll, poll), + }, { + .sym = "mu3_io_get_opbtns", + .off = offsetof(struct mu3_dll, get_opbtns), + }, { + .sym = "mu3_io_get_gamebtns", + .off = offsetof(struct mu3_dll, get_gamebtns), + }, { + .sym = "mu3_io_get_lever", + .off = offsetof(struct mu3_dll, get_lever), + } +}; + +struct mu3_dll mu3_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Ongeki IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Ongeki IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "mu3_io_get_api_version"); + + if (get_api_version != NULL) { + mu3_dll.api_version = get_api_version(); + } else { + mu3_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose mu3_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (mu3_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Ongeki IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + mu3_dll.api_version); + + goto end; + } + + sym = mu3_dll_syms; + hr = dll_bind(&mu3_dll, src, &sym, _countof(mu3_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Ongeki IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/mu3hook/mu3-dll.h b/mu3hook/mu3-dll.h new file mode 100644 index 0000000..41f280f --- /dev/null +++ b/mu3hook/mu3-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "mu3io/mu3io.h" + +struct mu3_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*poll)(void); + void (*get_opbtns)(uint8_t *opbtn); + void (*get_gamebtns)(uint8_t *left, uint8_t *right); + void (*get_lever)(int16_t *pos); +}; + +struct mu3_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct mu3_dll mu3_dll; + +HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self); diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index c819437..b3a1840 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -12,3 +12,8 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + mu3_io_get_gamebtns + mu3_io_get_lever + mu3_io_get_opbtns + mu3_io_init + mu3_io_poll From ab3c2ad0b786b62773ed4afb0757500c669786af Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 14:25:20 -0400 Subject: [PATCH 077/175] Statically link mu3io --- mu3hook/meson.build | 2 +- mu3io/meson.build | 3 +-- mu3io/mu3io.def | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 mu3io/mu3io.def diff --git a/mu3hook/meson.build b/mu3hook/meson.build index 9931b8b..7ce2398 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -14,7 +14,7 @@ shared_library( aimeio_lib, board_lib, hooklib_lib, - mu3io_dll, + mu3io_lib, platform_lib, util_lib, ], diff --git a/mu3io/meson.build b/mu3io/meson.build index 74b91ad..3d6e60e 100644 --- a/mu3io/meson.build +++ b/mu3io/meson.build @@ -1,9 +1,8 @@ -mu3io_dll = shared_library( +mu3io_lib = static_library( 'mu3io', name_prefix : '', include_directories : inc, implicit_include_directories : false, - vs_module_defs : 'mu3io.def', c_pch : '../precompiled.h', dependencies : [ xinput_lib, diff --git a/mu3io/mu3io.def b/mu3io/mu3io.def deleted file mode 100644 index 33f747f..0000000 --- a/mu3io/mu3io.def +++ /dev/null @@ -1,8 +0,0 @@ -LIBRARY mu3io - -EXPORTS - mu3_io_get_gamebtns - mu3_io_get_lever - mu3_io_get_opbtns - mu3_io_init - mu3_io_poll From 8b4ea3f1ddc68541019de7aa9df680d3226e50e5 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 14:25:32 -0400 Subject: [PATCH 078/175] Add API versioning to mu3io --- mu3io/mu3io.c | 5 +++++ mu3io/mu3io.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/mu3io/mu3io.c b/mu3io/mu3io.c index 63b9652..0bbd37f 100644 --- a/mu3io/mu3io.c +++ b/mu3io/mu3io.c @@ -12,6 +12,11 @@ static uint8_t mu3_right_btn; static int16_t mu3_lever_pos; static int16_t mu3_lever_xpos; +uint16_t mu3_io_get_api_version(void) +{ + return 0x0100; +} + HRESULT mu3_io_init(void) { return S_OK; diff --git a/mu3io/mu3io.h b/mu3io/mu3io.h index fb140eb..9a006ca 100644 --- a/mu3io/mu3io.h +++ b/mu3io/mu3io.h @@ -17,6 +17,8 @@ enum { MU3_IO_GAMEBTN_MENU = 0x10, }; +uint16_t mu3_io_get_api_version(void); + HRESULT mu3_io_init(void); HRESULT mu3_io_poll(void); From 6a8b616bc238212a3b6b6a604183798726e2e9a4 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 6 Jun 2021 15:06:26 -0400 Subject: [PATCH 079/175] Document mu3io API --- mu3io/mu3io.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/mu3io/mu3io.h b/mu3io/mu3io.h index 9a006ca..d46a475 100644 --- a/mu3io/mu3io.h +++ b/mu3io/mu3io.h @@ -17,14 +17,68 @@ enum { MU3_IO_GAMEBTN_MENU = 0x10, }; +/* Get the version of the Ongeki IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + uint16_t mu3_io_get_api_version(void); +/* Initialize the IO DLL. This is the second function that will be called on + your DLL, after mu3_io_get_api_version. + + All subsequent calls to this API may originate from arbitrary threads. + + Minimum API version: 0x0100 */ + HRESULT mu3_io_init(void); +/* Send any queued outputs (of which there are currently none, though this may + change in subsequent API versions) and retrieve any new inputs. + + Minimum API version: 0x0100 */ + HRESULT mu3_io_poll(void); +/* Get the state of the cabinet's operator buttons as of the last poll. See + MU3_IO_OPBTN enum above: this contains bit mask definitions for button + states returned in *opbtn. All buttons are active-high. + + Minimum API version: 0x0100 */ + void mu3_io_get_opbtns(uint8_t *opbtn); +/* Get the state of the cabinet's gameplay buttons as of the last poll. See + MU3_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into + a left hand side set of inputs and a right hand side set of inputs: the bit + mappings are the same in both cases. + + All buttons are active-high, even though some buttons' electrical signals + on a real cabinet are active-low. + + Minimum API version: 0x0100 */ + void mu3_io_get_gamebtns(uint8_t *left, uint8_t *right); +/* Get the position of the cabinet lever as of the last poll. The center + position should be equal to or close to zero. + + The operator will be required to calibrate the lever's range of motion on + first power-on, so the lever position reported through this API does not + need to perfectly centered or cover every single position value possible, + but it should be reasonably close in order to make things easier for the + operator. + + The calibration screen displays the leftmost and rightmost position signal + returned from the cabinet's ADC encoder as a pair of raw two's complement + hexadecimal values. On a real cabinet these leftmost and rightmost + positions are somewhere around 0xB000 and 0x5000 respectively (remember + that negative values i.e. left positions have a high most-significant bit), + although these values can easily vary by +/- 0x1000 across different + cabinets. + + Minimum API version: 0x0100 */ + void mu3_io_get_lever(int16_t *pos); From 1fdeeb9139efd60c55695d8eb60871889f32981c Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:40:19 -0400 Subject: [PATCH 080/175] Load and bind idzio at runtime --- idzhook/config.c | 18 +++++++ idzhook/config.h | 6 +++ idzhook/dllmain.c | 7 +++ idzhook/idz-dll.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ idzhook/idz-dll.h | 22 +++++++++ idzhook/idzhook.def | 5 ++ idzhook/jvs.c | 19 +++++--- idzhook/meson.build | 2 + 8 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 idzhook/idz-dll.c create mode 100644 idzhook/idz-dll.h diff --git a/idzhook/config.c b/idzhook/config.c index 2987584..c8d551d 100644 --- a/idzhook/config.c +++ b/idzhook/config.c @@ -8,10 +8,27 @@ #include "board/sg-reader.h" #include "idzhook/config.h" +#include "idzhook/idz-dll.h" #include "platform/config.h" #include "platform/platform.h" +void idz_dll_config_load( + struct idz_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"idzio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + void idz_hook_config_load( struct idz_hook_config *cfg, const wchar_t *filename) @@ -22,6 +39,7 @@ void idz_hook_config_load( platform_config_load(&cfg->platform, filename); amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); + idz_dll_config_load(&cfg->dll, filename); zinput_config_load(&cfg->zinput, filename); } diff --git a/idzhook/config.h b/idzhook/config.h index a93cb11..a7cf964 100644 --- a/idzhook/config.h +++ b/idzhook/config.h @@ -7,6 +7,7 @@ #include "board/sg-reader.h" +#include "idzhook/idz-dll.h" #include "idzhook/zinput.h" #include "platform/platform.h" @@ -15,9 +16,14 @@ struct idz_hook_config { struct platform_config platform; struct amex_config amex; struct aime_config aime; + struct idz_dll_config dll; struct zinput_config zinput; }; +void idz_dll_config_load( + struct idz_dll_config *cfg, + const wchar_t *filename); + void idz_hook_config_load( struct idz_hook_config *cfg, const wchar_t *filename); diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index d1dd208..fd15f09 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -13,6 +13,7 @@ #include "hooklib/spike.h" #include "idzhook/config.h" +#include "idzhook/idz-dll.h" #include "idzhook/jvs.h" #include "idzhook/zinput.h" @@ -51,6 +52,12 @@ static DWORD CALLBACK idz_pre_startup(void) goto fail; } + hr = idz_dll_init(&idz_hook_cfg.dll, idz_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = amex_hook_init(&idz_hook_cfg.amex, idz_jvs_init); if (FAILED(hr)) { diff --git a/idzhook/idz-dll.c b/idzhook/idz-dll.c new file mode 100644 index 0000000..2ebc75b --- /dev/null +++ b/idzhook/idz-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "idzhook/idz-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym idz_dll_syms[] = { + { + .sym = "idz_io_jvs_init", + .off = offsetof(struct idz_dll, jvs_init), + }, { + .sym = "idz_io_jvs_read_analogs", + .off = offsetof(struct idz_dll, jvs_read_analogs), + }, { + .sym = "idz_io_jvs_read_buttons", + .off = offsetof(struct idz_dll, jvs_read_buttons), + }, { + .sym = "idz_io_jvs_read_shifter", + .off = offsetof(struct idz_dll, jvs_read_shifter), + }, { + .sym = "idz_io_jvs_read_coin_counter", + .off = offsetof(struct idz_dll, jvs_read_coin_counter), + } +}; + +struct idz_dll idz_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("IDZ IO: Failed to load IO DLL: %x: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("IDZ IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "idz_io_get_api_version"); + + if (get_api_version != NULL) { + idz_dll.api_version = get_api_version(); + } else { + idz_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose idz_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (idz_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("IDZ IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + idz_dll.api_version); + + goto end; + } + + sym = idz_dll_syms; + hr = dll_bind(&idz_dll, src, &sym, _countof(idz_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("IDZ IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/idzhook/idz-dll.h b/idzhook/idz-dll.h new file mode 100644 index 0000000..e67cb45 --- /dev/null +++ b/idzhook/idz-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "idzio/idzio.h" + +struct idz_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_read_analogs)(struct idz_io_analog_state *out); + void (*jvs_read_buttons)(uint8_t *opbtn, uint8_t *gamebtn); + void (*jvs_read_shifter)(uint8_t *gear); + void (*jvs_read_coin_counter)(uint16_t *total); +}; + +struct idz_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct idz_dll idz_dll; + +HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self); diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index 43b8ef7..a9b9f74 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -12,3 +12,8 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + idz_io_jvs_init + idz_io_jvs_read_analogs + idz_io_jvs_read_buttons + idz_io_jvs_read_coin_counter + idz_io_jvs_read_shifter diff --git a/idzhook/jvs.c b/idzhook/jvs.c index fce5849..ab2f6aa 100644 --- a/idzhook/jvs.c +++ b/idzhook/jvs.c @@ -8,10 +8,9 @@ #include "board/io3.h" +#include "idzhook/idz-dll.h" #include "idzhook/jvs.h" -#include "idzio/idzio.h" - #include "jvs/jvs-bus.h" #include "util/dprintf.h" @@ -56,9 +55,10 @@ HRESULT idz_jvs_init(struct jvs_node **out) HRESULT hr; assert(out != NULL); + assert(idz_dll.jvs_init != NULL); dprintf("JVS I/O: Starting Initial D Zero backend DLL\n"); - hr = idz_io_jvs_init(); + hr = idz_dll.jvs_init(); if (FAILED(hr)) { dprintf("JVS I/O: Backend error, I/O disconnected; %x\n", (int) hr); @@ -79,13 +79,15 @@ static void idz_jvs_read_switches(void *ctx, struct io3_switch_state *out) uint8_t gear; assert(out != NULL); + assert(idz_dll.jvs_read_buttons != NULL); + assert(idz_dll.jvs_read_shifter != NULL); opbtn = 0; gamebtn = 0; gear = 0; - idz_io_jvs_read_buttons(&opbtn, &gamebtn); - idz_io_jvs_read_shifter(&gear); + idz_dll.jvs_read_buttons(&opbtn, &gamebtn); + idz_dll.jvs_read_shifter(&gear); /* Update gameplay buttons */ @@ -142,9 +144,10 @@ static void idz_jvs_read_analogs( struct idz_io_analog_state state; assert(analogs != NULL); + assert(idz_dll.jvs_read_analogs != NULL); memset(&state, 0, sizeof(state)); - idz_io_jvs_read_analogs(&state); + idz_dll.jvs_read_analogs(&state); if (nanalogs > 0) { analogs[0] = 0x8000 + state.wheel; @@ -164,9 +167,11 @@ static void idz_jvs_read_coin_counter( uint8_t slot_no, uint16_t *out) { + assert(idz_dll.jvs_read_coin_counter != NULL); + if (slot_no > 0) { return; } - idz_io_jvs_read_coin_counter(out); + idz_dll.jvs_read_coin_counter(out); } diff --git a/idzhook/meson.build b/idzhook/meson.build index e27007a..668ad8a 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -24,6 +24,8 @@ shared_library( 'config.c', 'config.h', 'dllmain.c', + 'idz-dll.c', + 'idz-dll.h', 'jvs.c', 'jvs.h', 'zinput.c', From bead904f1ede4549d80447483d4eec9b6659957e Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:41:37 -0400 Subject: [PATCH 081/175] Statically link idzio --- idzhook/meson.build | 2 +- idzio/dllmain.c | 22 +++++++++++----------- idzio/meson.build | 3 +-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/idzhook/meson.build b/idzhook/meson.build index 668ad8a..b89e4e3 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -15,7 +15,7 @@ shared_library( amex_lib, board_lib, hooklib_lib, - idzio_dll, + idzio_lib, jvs_lib, platform_lib, util_lib, diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 5edf67c..754e87a 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -13,7 +13,6 @@ #include "util/dprintf.h" #include "util/str.h" -static HMODULE idz_io_hmodule; static struct idz_io_config idz_io_cfg; static const struct idz_io_backend *idz_io_backend; static bool idz_io_coin; @@ -21,14 +20,24 @@ static uint16_t idz_io_coins; HRESULT idz_io_jvs_init(void) { + HINSTANCE inst; HRESULT hr; assert(idz_io_backend == NULL); + inst = GetModuleHandleW(NULL); + + if (inst == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("GetModuleHandleW failed: %x\n", hr); + + return hr; + } + idz_io_config_load(&idz_io_cfg, L".\\segatools.ini"); if (wstr_ieq(idz_io_cfg.mode, L"dinput")) { - hr = idz_di_init(&idz_io_cfg.di, idz_io_hmodule, &idz_io_backend); + hr = idz_di_init(&idz_io_cfg.di, inst, &idz_io_backend); } else if (wstr_ieq(idz_io_cfg.mode, L"xinput")) { hr = idz_xi_init(&idz_io_cfg.xi, &idz_io_backend); } else { @@ -109,12 +118,3 @@ void idz_io_jvs_read_coin_counter(uint16_t *out) *out = idz_io_coins; } - -BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx) -{ - if (reason == DLL_PROCESS_ATTACH) { - idz_io_hmodule = self; - } - - return TRUE; -} diff --git a/idzio/meson.build b/idzio/meson.build index ce1668c..bfab168 100644 --- a/idzio/meson.build +++ b/idzio/meson.build @@ -1,9 +1,8 @@ -idzio_dll = shared_library( +idzio_lib = static_library( 'idzio', name_prefix : '', include_directories : inc, implicit_include_directories : false, - vs_module_defs : 'idzio.def', c_pch : '../precompiled.h', dependencies : [ dinput8_lib, From 7d15f6581890bca4ee46bb5b6d694afacc7cee75 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:43:42 -0400 Subject: [PATCH 082/175] Add API versioning to idzio --- idzio/dllmain.c | 5 +++++ idzio/idzio.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 754e87a..32619cb 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -18,6 +18,11 @@ static const struct idz_io_backend *idz_io_backend; static bool idz_io_coin; static uint16_t idz_io_coins; +uint16_t idz_io_get_api_version(void) +{ + return 0x0100; +} + HRESULT idz_io_jvs_init(void) { HINSTANCE inst; diff --git a/idzio/idzio.h b/idzio/idzio.h index 9900791..5c14c55 100644 --- a/idzio/idzio.h +++ b/idzio/idzio.h @@ -24,6 +24,8 @@ struct idz_io_analog_state { uint16_t brake; }; +uint16_t idz_io_get_api_version(void); + HRESULT idz_io_jvs_init(void); void idz_io_jvs_read_analogs(struct idz_io_analog_state *out); From 72c6bc7b1d6d912660b52ddccd9f6db6c5342b8c Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:44:24 -0400 Subject: [PATCH 083/175] Document IDZIO API --- idzio/idzio.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/idzio/idzio.h b/idzio/idzio.h index 5c14c55..395b012 100644 --- a/idzio/idzio.h +++ b/idzio/idzio.h @@ -1,5 +1,17 @@ #pragma once +/* INITIAL D ZERO CUSTOM IO API + + This API definition allows custom driver DLLs to be defined for the + emulation of Initial D Zero cabinets. To be honest, there is very + little reason to want to do this, since driving game controllers are a + mostly-standardized PC peripheral which can be adequately controlled by the + built-in DirectInput and XInput support in idzhook. However, previous + versions of Segatools broke this functionality out into a separate DLL just + like all of the other supported games, so in the interests of maintaining + backwards compatibility we provide the option to load custom IDZIO + implementations as well. */ + #include #include @@ -19,21 +31,76 @@ enum { }; struct idz_io_analog_state { + /* Current steering wheel position, where zero is the centered position. + + The game will accept any signed 16-bit position value, however a real + cabinet will report a value of approximately +/- 25230 when the wheel + is at full lock. Steering wheel positions of a magnitude greater than + this value are not possible on a real cabinet. */ + int16_t wheel; + + /* Current position of the accelerator pedal, where 0 is released. */ + uint16_t accel; + + /* Current position of the brake pedal, where 0 is released. */ + uint16_t brake; }; +/* Get the version of the IDZ IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + uint16_t idz_io_get_api_version(void); +/* Initialize JVS-based input. This function will be called before any other + idz_io_jvs_*() function calls. Errors returned from this function will + manifest as a disconnected JVS bus. + + All subsequent calls may originate from arbitrary threads and some may + overlap with each other. Ensuring synchronization inside your IO DLL is + your responsibility. + + Minimum API version: 0x0100 */ + HRESULT idz_io_jvs_init(void); +/* Poll the current state of the cabinet's JVS analog inputs. See structure + definition above for details. + + Minimum API version: 0x0100 */ + void idz_io_jvs_read_analogs(struct idz_io_analog_state *out); +/* Poll the current state of the cabinet's JVS input buttons and return them + through the opbtn and gamebtn out parameters. See enum definitions at the + top of this file for a list of bit masks to be used with these out + parameters. + + Minimum API version: 0x0100 */ + void idz_io_jvs_read_buttons(uint8_t *opbtn, uint8_t *gamebtn); +/* Poll the current position of the six-speed shifter and return it via the + gear out parameter. Valid values are 0 for neutral and 1-6 for gears 1-6. + + idzhook internally translates this gear position value into the correct + combination of Gear Left, Gear Right, Gear Up, Gear Down buttons that the + game will then interpret as the current position of the gear lever. + + Minimum API version: 0x0100 */ + void idz_io_jvs_read_shifter(uint8_t *gear); -void idz_io_jvs_read_coin_counter(uint16_t *total); +/* Read the current state of the coin counter. This value should be incremented + for every coin detected by the coin acceptor mechanism. This count does not + need to persist beyond the lifetime of the process. -// TODO force feedback once that gets reverse engineered + Minimum API version: 0x0100 */ + +void idz_io_jvs_read_coin_counter(uint16_t *total); From 26cf9aa4a751030907b175f3963e9f7e5afef300 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 12 Jun 2021 12:55:49 -0400 Subject: [PATCH 084/175] Delete coin blocker exports from IO DLLs These never get called at the moment, so we have no way of guaranteeing that any existing implementations are even correct. If we do decide to start emulating the coin blocker then we will need to advance the API versions, which will (hopefully) force any third-party devs to test their coin blocker code before opting in. --- chunihook/chuni-dll.c | 3 --- chunihook/chuni-dll.h | 1 - chunihook/chunihook.def | 1 - chuniio/chuniio.c | 3 --- chuniio/chuniio.h | 5 ----- divahook/diva-dll.c | 3 --- divahook/diva-dll.h | 1 - divahook/divahook.def | 1 - divaio/divaio.c | 3 --- divaio/divaio.h | 6 ------ 10 files changed, 27 deletions(-) diff --git a/chunihook/chuni-dll.c b/chunihook/chuni-dll.c index 3750c86..24efa5c 100644 --- a/chunihook/chuni-dll.c +++ b/chunihook/chuni-dll.c @@ -18,9 +18,6 @@ const struct dll_bind_sym chuni_dll_syms[] = { }, { .sym = "chuni_io_jvs_read_coin_counter", .off = offsetof(struct chuni_dll, jvs_read_coin_counter), - }, { - .sym = "chuni_io_jvs_set_coin_blocker", - .off = offsetof(struct chuni_dll, jvs_set_coin_blocker), }, { .sym = "chuni_io_slider_init", .off = offsetof(struct chuni_dll, slider_init), diff --git a/chunihook/chuni-dll.h b/chunihook/chuni-dll.h index e4a24f8..ecd88ee 100644 --- a/chunihook/chuni-dll.h +++ b/chunihook/chuni-dll.h @@ -9,7 +9,6 @@ struct chuni_dll { HRESULT (*jvs_init)(void); void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); void (*jvs_read_coin_counter)(uint16_t *total); - void (*jvs_set_coin_blocker)(bool open); HRESULT (*slider_init)(void); void (*slider_start)(chuni_io_slider_callback_t callback); void (*slider_stop)(void); diff --git a/chunihook/chunihook.def b/chunihook/chunihook.def index 5b310a5..2a90ab6 100644 --- a/chunihook/chunihook.def +++ b/chunihook/chunihook.def @@ -16,7 +16,6 @@ EXPORTS chuni_io_jvs_init chuni_io_jvs_poll chuni_io_jvs_read_coin_counter - chuni_io_jvs_set_coin_blocker chuni_io_slider_init chuni_io_slider_set_leds chuni_io_slider_start diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c index 8813e68..4fffe69 100644 --- a/chuniio/chuniio.c +++ b/chuniio/chuniio.c @@ -75,9 +75,6 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) } } -void chuni_io_jvs_set_coin_blocker(bool open) -{} - HRESULT chuni_io_slider_init(void) { return S_OK; diff --git a/chuniio/chuniio.h b/chuniio/chuniio.h index 87e7e76..2a24600 100644 --- a/chuniio/chuniio.h +++ b/chuniio/chuniio.h @@ -68,11 +68,6 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); void chuni_io_jvs_read_coin_counter(uint16_t *total); -/* Set the state of the coin blocker. Parameter is true if the blocker is - disengaged (i.e. coins can be inserted) and false if the blocker is engaged - (i.e. the coin slot should be physically blocked). */ - -void chuni_io_jvs_set_coin_blocker(bool open); /* Initialize touch slider emulation. This function will be called before any other chuni_io_slider_*() function calls. diff --git a/divahook/diva-dll.c b/divahook/diva-dll.c index c871dd9..2f5793e 100644 --- a/divahook/diva-dll.c +++ b/divahook/diva-dll.c @@ -18,9 +18,6 @@ const struct dll_bind_sym diva_dll_syms[] = { }, { .sym = "diva_io_jvs_read_coin_counter", .off = offsetof(struct diva_dll, jvs_read_coin_counter), - }, { - .sym = "diva_io_jvs_set_coin_blocker", - .off = offsetof(struct diva_dll, jvs_set_coin_blocker), }, { .sym = "diva_io_slider_init", .off = offsetof(struct diva_dll, slider_init), diff --git a/divahook/diva-dll.h b/divahook/diva-dll.h index fe78ff2..1e57947 100644 --- a/divahook/diva-dll.h +++ b/divahook/diva-dll.h @@ -9,7 +9,6 @@ struct diva_dll { HRESULT (*jvs_init)(void); void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); void (*jvs_read_coin_counter)(uint16_t *total); - void (*jvs_set_coin_blocker)(bool open); HRESULT (*slider_init)(void); void (*slider_start)(diva_io_slider_callback_t callback); void (*slider_stop)(void); diff --git a/divahook/divahook.def b/divahook/divahook.def index a8f5d10..d6a11d4 100644 --- a/divahook/divahook.def +++ b/divahook/divahook.def @@ -16,7 +16,6 @@ EXPORTS diva_io_jvs_init diva_io_jvs_poll diva_io_jvs_read_coin_counter - diva_io_jvs_set_coin_blocker diva_io_slider_init diva_io_slider_set_leds diva_io_slider_start diff --git a/divaio/divaio.c b/divaio/divaio.c index 7db2075..760e198 100644 --- a/divaio/divaio.c +++ b/divaio/divaio.c @@ -72,9 +72,6 @@ void diva_io_jvs_read_coin_counter(uint16_t *out) *out = diva_io_coins; } -void diva_io_jvs_set_coin_blocker(bool open) -{} - HRESULT diva_io_slider_init(void) { return S_OK; diff --git a/divaio/divaio.h b/divaio/divaio.h index 202704e..bac3627 100644 --- a/divaio/divaio.h +++ b/divaio/divaio.h @@ -47,12 +47,6 @@ void diva_io_jvs_poll(uint8_t *opbtn, uint8_t *gamebtn); void diva_io_jvs_read_coin_counter(uint16_t *out); -/* Set the state of the coin blocker. Parameter is true if the blocker is - disengaged (i.e. coins can be inserted) and false if the blocker is engaged - (i.e. the coin slot should be physically blocked). */ - -void diva_io_jvs_set_coin_blocker(bool open); - /* Initialize touch slider emulation. This function will be called before any other diva_io_slider_*() function calls. From 987f9652da7628e80213b5b21d013082ae8cb8cb Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Tue, 15 Jun 2021 18:54:10 +0200 Subject: [PATCH 085/175] Fix doc hyperlinks in chunihook.md --- doc/chunihook.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/chunihook.md b/doc/chunihook.md index 4849450..57e19b0 100644 --- a/doc/chunihook.md +++ b/doc/chunihook.md @@ -41,7 +41,7 @@ amfs=../../amfs appdata=../../appdata option=../../option ``` -1. In the `[dns]` section, set `default=` to your computer's hostname or LAN IP. Do not put +1. In the `[dns]` section, set `default=` to your computer's hostname or LAN IP. Do not put `127.0.0.1` here, the game specifically checks for and rejects loopback addresses. This setting controls the address of the network services server 1. Right click `start.bat` in `app/bin` and run it as Administrator. I think you need to run it as @@ -82,12 +82,12 @@ you do this. ## Segatools configuration Configurable settings are exposed in the `segatools.ini` file. For a detailed description, please -refer to [this document](config/chunihook.md). +refer to [this document](doc/config/common.md). ## Chunithm specific configuration For configuring chuinthm specific features, e.g. IO, please refer to -[this document](doc/chunithm.md). +[this document](doc/config/chunithm.md). ## FAQ @@ -116,4 +116,4 @@ This requires additional server support which might not be implemented currently ### How do I unlock a character other than the penguin? Pick any map other than the default. You will see characters available as potential rewards for -those maps. \ No newline at end of file +those maps. From e4999005cbfd56587953d707ec479e7d4053e4c6 Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Wed, 16 Jun 2021 00:03:07 +0200 Subject: [PATCH 086/175] doc: Add doc for idzhook Apply 8 suggestion(s) to 2 file(s) doc: Fix up initial d docs --- doc/config/initiald.md | 166 +++++++++++++++++++++++++++++++++++++++++ doc/idzhook.md | 111 +++++++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 doc/config/initiald.md create mode 100644 doc/idzhook.md diff --git a/doc/config/initiald.md b/doc/config/initiald.md new file mode 100644 index 0000000..870d8b9 --- /dev/null +++ b/doc/config/initiald.md @@ -0,0 +1,166 @@ +# Initial D Arcade Stage Zero configuration settings + +This file describes configuration settings specific to Initial D Arcade Stage +Zero. + +Keyboard binding settings use +[Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). + +## `[io3]` + +Cabinet specific inputs. + +### `test` + +Default `0x31` (`1 Key`) + +Key-binding for cabinet test button. + +### `service` + +Default `0x32` (`2 Key`) + +Key-binding for cabinet service button. + +### `coin` + +Default `0x33` (`3 Key`) + +Key-binding for cabinet coin switch. + +### `mode` + +Choose whether you want to use a xinput (Xbox controllers, etc) or a dinput (PS +controllers, most wheels) type controller. + +### `restrict` + +Default `97` + +This setting scales the steering wheel input so that the maximum positive and +minimum negative steering inputs reported in the operator menu's input test +screen do not exceed the value below. The maximum possible value is 128, and the +value that matches the input range of a real cabinet is 97. + +NOTE: This is not the same thing as DirectInput steering wheel movement range! +Segatools cannot control the maximum angle of your physical steering wheel +controller, this setting is vendor-specific and can only be adjusted in the +Control Panel. + +### `autoNeutral` + +Default `1` + +Automatically reset the simulated shifter to Neutral when XInput Start is +pressed (e.g. when navigating menus between races). + +### `singleStickSteering` + +Default `0` + +By default if you use a xinput controller, segatools will use both sticks for +steering. While this is recommended for precision, it may be confusing to some +people. Set this to `1` to use a more traditional control scheme where only the +left stick is used to steer. + +## `[dinput]` + +Configuration for dinput controllers. + +### `deviceName` + +Default ` ` + +Name of the DirectInput wheel or controller to use (or any text that occurs in +its name) Example: TMX, Wireless Controller, T300, etc + +If this is left blank then the first DirectInput device will be used. If you +don't know the name of your input device, you can find it in the windows +controller panel. The quickest way to access it is to press Win+R, then type in +`joy.cpl` and look at the list it will display. + +### `shifterName` + +Default ` ` + +Name of the positional shifter to use (or any subset thereof). Leave blank if +you do not have a positional shifter; a positional shifter will be simulated +using the configured Shift Down and Shift Up buttons in this case. If you don't +know the name of your input device, you can find it in the windows controller +panel. The quickest way to access it is to press Win+R, then type in `joy.cpl` +and look at the list it will display. + +### Note on pedal mappings + +Pedal mappings. Valid axis names are: + +`X, Y, Z, RX, RY, RZ, U, V` + +(U and V are old names for Slider 1 and Slider 2). The default values below are +valid for a Thrustmaster TMX. + +### `brakeAxis` + +Default `RZ` + +Which axis to use for the brake pedal. + +### `accelAxis` + +Default `Y` + +Which axis to use for the accelerator pedal. + +### Note on button mappings + +These are dinput button numbers. Note that buttons are numbered from 1, while +some software numbers buttons from 0. The default windows controller panel +should show the correct ones, if it hasn't been replaced by the controller +driver. + +### `start` + +Default `2` + +Button mapping for the `Start` button. + +### `viewChg` + +Default `3` + +Button mapping for the `View Change` button. + +### `shiftDn` + +Default `5` + +Button mapping for shifting down with the emulated shifter. (If no `shifterName` +was specified) + +### `shiftUp` + +Default `6` + +Button mapping for shifting up with the emulated shifter. (If no `shifterName` +was specified) + +### `gear1`, `gear2`, `gear3`, `gear4`, `gear5`, `gear6` + +Default `1, 2, 3, 4, 5, 6` + +Button mappings for the physical shifter, if one was specified via the +`shifterName` setting. + +### `reverseAccelAxis` + +Default `0` + +Inverts the accelerator axis if set to ‘1’. This is needed for certain +controllers like the DS4 or Dualsense. + +### `reverseBrakeAxis` + +Default `0` + +Inverts the brake axis if set to ‘1’. This is needed for certain controllers +like the DS4 or Dualsense. diff --git a/doc/idzhook.md b/doc/idzhook.md new file mode 100644 index 0000000..e23c18c --- /dev/null +++ b/doc/idzhook.md @@ -0,0 +1,111 @@ +# idzhook + +## Supported games + +- Initial D Arcade Stage Zero Version 1 +- Initial D Arcade Stage Zero Version 2 + +## General remarks + +- The minimum version of Windows that this game supports is Windows 8 +- Most of the game is in japanese, even in EXP region mode. +- This game is hard to set up. You may encounter weird errors that are hard to + diagnose + +## Known issues + +- The game will run in a semi windowed mode, even when set to fullscreen. +- No DirectInput force feedback (future Segatools enhancement) +- Felica.txt cards do not work with Version 2 of the game + +## Data and game setup + +1. Get the data +1. Ensure the game files are not marked read-only +1. Start up your favorite ALLNET server implementation in the background. + Without it you won't be able to save your progress whatsoever. +1. The data releases have the following structure: + +- `package/`: Game data +- `opt/`: Addon data +- `amfs/`: Metadata + +4. Unpack segatools to the `package` directory +5. Create an `appdata` folder (this isn't Windows APPDATA) in the data release + next to `package/`, `opt/` and `amfs/` +6. In the `[vfs]` section of `package/segatools.ini` set the paths for the + folders: + +```text +[vfs] +amfs=../../amfs +appdata=../../appdata +option=../../opt +``` + +1. In the `[dns]` section, set `default=` to your computer's hostname or LAN IP. + Do not put `127.0.0.1` here, the game specifically checks for and rejects + loopback addresses. This setting controls the address of the network services + server +1. If you want to change the language to english, you can set `region=` in the + `[ds]` section to `4`. +1. This will change the region to `EXP` which comes with a rudimentary english + translation. + +- Note for Version 2 of the game: You'll have to manually create a card ID and + set it up. To do so, change your `package/segatools.ini` so that the `[aime]` + section looks like this: + +``` +aimePath=DEVICE\aime.txt +felicaGen=0 +``` + +- Then, go into the `package\DEVICE` folder, create a text file called aime.txt + and fill it with 20 digits, for example `01234567891234567890`. Make sure to + save the file. + +1. Right click `start.bat` in `package` and run it as Administrator. After the + first run you may be able to run the game as a normal user. +1. Once you're at the title screen, press 2 or 3 a few times to add some + credits, then _hold_ the Enter key for a few seconds to scan a card and start + a credit. + +- In some cases it might be necessary to run the game from the `C:\` drive. Try + copying the game there if for some reason it won't boot on a different drive. + +## Segatools configuration + +Configurable settings are exposed in the `segatools.ini` file. For a detailed +description, please refer to [this document](doc/config/common.md). + +## Initial D Arcade Stage Zero specific configuration + +For configuring IDZ specific features, e.g. IO, please refer to +[this document](doc/config/initiald.md). + +## FAQ + +### Where is the Free Play setting? + +In the SEGA Nu system supervisor program, which is not included in this release. +A command-line tool to change this setting will be provided in a future release +of Segatools. + +Do note however that Free Play is not advised for this game, as it will prohibit +you from purchasing additional cars. + +### How do I get extra music? + +Play through the story and also rack up your mileage. + +### How do I unlock extra gauges? + +Some will unlock in the story but most were time-limited online events. Try +editing the "idz_unlocks" table in the MiniMe database to force-unlock them on +your profile. + +### Why does my story progress keep resetting? + +If you are using minime, this is a known issue and will be fixed in a future +release. From 9cd231f2ebbd935eee393281a41a74cf2548e962 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 16 Jun 2021 17:51:20 +0000 Subject: [PATCH 087/175] Fix format warnings for HRESULT --- board/aime-dll.c | 2 +- chunihook/chuni-dll.c | 2 +- divahook/diva-dll.c | 2 +- idzhook/idz-dll.c | 2 +- idzio/dllmain.c | 2 +- mu3hook/mu3-dll.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/board/aime-dll.c b/board/aime-dll.c index 0ada7f4..465fcfd 100644 --- a/board/aime-dll.c +++ b/board/aime-dll.c @@ -51,7 +51,7 @@ HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self) if (owned == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("NFC Assembly: Failed to load IO DLL: %x: %S\n", + dprintf("NFC Assembly: Failed to load IO DLL: %lx: %S\n", hr, cfg->path); diff --git a/chunihook/chuni-dll.c b/chunihook/chuni-dll.c index 24efa5c..c98a9de 100644 --- a/chunihook/chuni-dll.c +++ b/chunihook/chuni-dll.c @@ -57,7 +57,7 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self) if (owned == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Chunithm IO: Failed to load IO DLL: %x: %S\n", + dprintf("Chunithm IO: Failed to load IO DLL: %lx: %S\n", hr, cfg->path); diff --git a/divahook/diva-dll.c b/divahook/diva-dll.c index 2f5793e..c24cf01 100644 --- a/divahook/diva-dll.c +++ b/divahook/diva-dll.c @@ -57,7 +57,7 @@ HRESULT diva_dll_init(const struct diva_dll_config *cfg, HINSTANCE self) if (owned == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Diva IO: Failed to load IO DLL: %x: %S\n", + dprintf("Diva IO: Failed to load IO DLL: %lx: %S\n", hr, cfg->path); diff --git a/idzhook/idz-dll.c b/idzhook/idz-dll.c index 2ebc75b..eb1b6e1 100644 --- a/idzhook/idz-dll.c +++ b/idzhook/idz-dll.c @@ -51,7 +51,7 @@ HRESULT idz_dll_init(const struct idz_dll_config *cfg, HINSTANCE self) if (owned == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("IDZ IO: Failed to load IO DLL: %x: %S\n", + dprintf("IDZ IO: Failed to load IO DLL: %lx: %S\n", hr, cfg->path); diff --git a/idzio/dllmain.c b/idzio/dllmain.c index 32619cb..278c8c2 100644 --- a/idzio/dllmain.c +++ b/idzio/dllmain.c @@ -34,7 +34,7 @@ HRESULT idz_io_jvs_init(void) if (inst == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("GetModuleHandleW failed: %x\n", hr); + dprintf("GetModuleHandleW failed: %lx\n", hr); return hr; } diff --git a/mu3hook/mu3-dll.c b/mu3hook/mu3-dll.c index 15ed6bb..9e8e93e 100644 --- a/mu3hook/mu3-dll.c +++ b/mu3hook/mu3-dll.c @@ -51,7 +51,7 @@ HRESULT mu3_dll_init(const struct mu3_dll_config *cfg, HINSTANCE self) if (owned == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Ongeki IO: Failed to load IO DLL: %x: %S\n", + dprintf("Ongeki IO: Failed to load IO DLL: %lx: %S\n", hr, cfg->path); From 872ddd5a4abcc63d45a2aaf673cd4018e04c471a Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 16 Jun 2021 17:52:17 +0000 Subject: [PATCH 088/175] Remove undefined Direct3DCreate9 references --- divahook/divahook.def | 1 - idzhook/idzhook.def | 1 - 2 files changed, 2 deletions(-) diff --git a/divahook/divahook.def b/divahook/divahook.def index d6a11d4..9101509 100644 --- a/divahook/divahook.def +++ b/divahook/divahook.def @@ -1,7 +1,6 @@ LIBRARY divahook EXPORTS - Direct3DCreate9 aime_io_get_api_version aime_io_init aime_io_led_set_color diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index a9b9f74..0a8979e 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -1,7 +1,6 @@ LIBRARY idzhook EXPORTS - Direct3DCreate9 aime_io_get_api_version aime_io_init aime_io_led_set_color From 88128af7c867e623d8e203de7f67a53e0ccaaaad Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 16 Jun 2021 17:53:11 +0000 Subject: [PATCH 089/175] Remove leftover GetProcAddress definition --- hooklib/dll.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hooklib/dll.c b/hooklib/dll.c index 00adfd6..be9428f 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -29,7 +29,6 @@ static HMODULE WINAPI hook_GetModuleHandleA(const char *name); static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name); static HMODULE WINAPI hook_LoadLibraryA(const char *name); static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name); -static void * WINAPI hook_GetProcAddress(HMODULE mod, const char *name); /* Link pointers */ @@ -38,7 +37,6 @@ static HMODULE (WINAPI *next_GetModuleHandleA)(const char *name); static HMODULE (WINAPI *next_GetModuleHandleW)(const wchar_t *name); static HMODULE (WINAPI *next_LoadLibraryA)(const char *name); static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name); -static void * (WINAPI *next_GetProcAddress)(HMODULE mod, const char *name); static const struct hook_symbol dll_loader_syms[] = { { From 457ed9cd2bb555681f8a0110dddd65c9c537ea4c Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 16 Jun 2021 20:33:27 +0000 Subject: [PATCH 090/175] Silence stdcall fixup warning during linking --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 05a2bce..50660ad 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,7 @@ if meson.get_compiler('c').get_id() != 'msvc' ) add_project_link_arguments( + '-Wl,--enable-stdcall-fixup', '-Wl,--exclude-all-symbols', '-Wl,--gc-sections', '-static-libgcc', From f79144edc13a63112011752fbbe48664909f5ebe Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Wed, 16 Jun 2021 12:08:08 +0200 Subject: [PATCH 091/175] Add dvd hook to allow hiding connected dvd drives --- hooklib/config.c | 9 +++++ hooklib/config.h | 2 ++ hooklib/dvd.c | 85 +++++++++++++++++++++++++++++++++++++++++++++ hooklib/dvd.h | 11 ++++++ hooklib/meson.build | 2 ++ idzhook/config.c | 4 +++ idzhook/config.h | 3 ++ idzhook/dllmain.c | 3 +- mu3hook/config.c | 2 ++ mu3hook/config.h | 2 ++ mu3hook/dllmain.c | 2 ++ 11 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 hooklib/dvd.c create mode 100644 hooklib/dvd.h diff --git a/hooklib/config.c b/hooklib/config.c index 1c4c9c4..a7a5a57 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -6,6 +6,7 @@ #include "hooklib/config.h" #include "hooklib/gfx.h" +#include "hooklib/dvd.h" void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) { @@ -17,3 +18,11 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); } + +void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); +} diff --git a/hooklib/config.h b/hooklib/config.h index 0d737aa..ed3d7d4 100644 --- a/hooklib/config.h +++ b/hooklib/config.h @@ -4,5 +4,7 @@ #include #include "hooklib/gfx.h" +#include "hooklib/dvd.h" void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); +void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); diff --git a/hooklib/dvd.c b/hooklib/dvd.c new file mode 100644 index 0000000..f8050f4 --- /dev/null +++ b/hooklib/dvd.c @@ -0,0 +1,85 @@ +#include + +#include +#include +#include + +#include "hook/com-proxy.h" +#include "hook/table.h" + +#include "hooklib/config.h" +#include "hooklib/dll.h" +#include "hooklib/dvd.h" + +#include "util/dprintf.h" + +/* API hooks */ + +static DWORD WINAPI hook_QueryDosDeviceW( + const wchar_t *lpDeviceName, + wchar_t *lpTargetPath, + DWORD ucchMax); + +/* Link pointers */ + +static DWORD (WINAPI *next_QueryDosDeviceW)( + const wchar_t *lpDeviceName, + wchar_t *lpTargetPath, + DWORD ucchMax); + +static bool dvd_hook_initted; +static struct dvd_config dvd_config; + +static const struct hook_symbol dvd_hooks[] = { + { + .name = "QueryDosDeviceW", + .patch = hook_QueryDosDeviceW, + .link = (void **) &next_QueryDosDeviceW + }, +}; + +void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + /* Init is not thread safe because API hook init is not thread safe blah + blah blah you know the drill by now. */ + + if (dvd_hook_initted) { + return; + } + + dvd_hook_initted = true; + + memcpy(&dvd_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "kernel32.dll", dvd_hooks, _countof(dvd_hooks)); + dprintf("DVD: hook enabled.\n"); +} + +DWORD WINAPI hook_QueryDosDeviceW( + const wchar_t *lpDeviceName, + wchar_t *lpTargetPath, + DWORD ucchMax) +{ + DWORD ok; + wchar_t *p_dest; + wchar_t *dvd_string = L"CdRom"; + + ok = next_QueryDosDeviceW( + lpDeviceName, + lpTargetPath, + ucchMax); + + p_dest = wcsstr (lpTargetPath, dvd_string); + + if ( p_dest != NULL ) { + dprintf("DVD: Hiding DVD drive.\n"); + return 0; + } + + return ok; +} diff --git a/hooklib/dvd.h b/hooklib/dvd.h new file mode 100644 index 0000000..ce2f09b --- /dev/null +++ b/hooklib/dvd.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +struct dvd_config { + bool enable; +}; + +void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self); diff --git a/hooklib/meson.build b/hooklib/meson.build index 80abf6e..e75056a 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -13,6 +13,8 @@ hooklib_lib = static_library( 'dll.h', 'dns.c', 'dns.h', + 'dvd.c', + 'dvd.h', 'fdshark.c', 'fdshark.h', 'gfx.c', diff --git a/idzhook/config.c b/idzhook/config.c index c8d551d..a7d095d 100644 --- a/idzhook/config.c +++ b/idzhook/config.c @@ -7,6 +7,9 @@ #include "board/config.h" #include "board/sg-reader.h" +#include "hooklib/config.h" +#include "hooklib/dvd.h" + #include "idzhook/config.h" #include "idzhook/idz-dll.h" @@ -41,6 +44,7 @@ void idz_hook_config_load( aime_config_load(&cfg->aime, filename); idz_dll_config_load(&cfg->dll, filename); zinput_config_load(&cfg->zinput, filename); + dvd_config_load(&cfg->dvd, filename); } void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename) diff --git a/idzhook/config.h b/idzhook/config.h index a7cf964..58e421e 100644 --- a/idzhook/config.h +++ b/idzhook/config.h @@ -7,6 +7,8 @@ #include "board/sg-reader.h" +#include "hooklib/dvd.h" + #include "idzhook/idz-dll.h" #include "idzhook/zinput.h" @@ -16,6 +18,7 @@ struct idz_hook_config { struct platform_config platform; struct amex_config amex; struct aime_config aime; + struct dvd_config dvd; struct idz_dll_config dll; struct zinput_config zinput; }; diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index fd15f09..8349800 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -8,7 +8,7 @@ #include "hook/process.h" -#include "hooklib/gfx.h" +#include "hooklib/dvd.h" #include "hooklib/serial.h" #include "hooklib/spike.h" @@ -39,6 +39,7 @@ static DWORD CALLBACK idz_pre_startup(void) serial_hook_init(); zinput_hook_init(&idz_hook_cfg.zinput); + dvd_hook_init(&idz_hook_cfg.dvd, idz_hook_mod); /* Initialize emulation hooks */ diff --git a/mu3hook/config.c b/mu3hook/config.c index 24086d8..0205a3f 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -4,6 +4,7 @@ #include "board/config.h" #include "hooklib/config.h" +#include "hooklib/dvd.h" #include "hooklib/gfx.h" #include "mu3hook/config.h" @@ -35,6 +36,7 @@ void mu3_hook_config_load( platform_config_load(&cfg->platform, filename); aime_config_load(&cfg->aime, filename); + dvd_config_load(&cfg->dvd, filename); gfx_config_load(&cfg->gfx, filename); mu3_dll_config_load(&cfg->dll, filename); } diff --git a/mu3hook/config.h b/mu3hook/config.h index 6b2afaa..1d31ec5 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -4,6 +4,7 @@ #include "board/config.h" +#include "hooklib/dvd.h" #include "hooklib/gfx.h" #include "mu3hook/mu3-dll.h" @@ -13,6 +14,7 @@ struct mu3_hook_config { struct platform_config platform; struct aime_config aime; + struct dvd_config dvd; struct gfx_config gfx; struct mu3_dll_config dll; }; diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index c7fe361..6d307de 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -8,6 +8,7 @@ #include "hook/process.h" +#include "hooklib/dvd.h" #include "hooklib/serial.h" #include "hooklib/spike.h" @@ -36,6 +37,7 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Hook Win32 APIs */ + dvd_hook_init(&mu3_hook_cfg.dvd, mu3_hook_mod); gfx_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); serial_hook_init(); From 3a5258a9c9c6755df3a710d519a1f8deaa481c9a Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Thu, 17 Jun 2021 04:13:50 +0200 Subject: [PATCH 092/175] dvd: move thread safety comment into header file --- hooklib/dvd.c | 3 --- hooklib/dvd.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hooklib/dvd.c b/hooklib/dvd.c index f8050f4..2c135eb 100644 --- a/hooklib/dvd.c +++ b/hooklib/dvd.c @@ -46,9 +46,6 @@ void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self) return; } - /* Init is not thread safe because API hook init is not thread safe blah - blah blah you know the drill by now. */ - if (dvd_hook_initted) { return; } diff --git a/hooklib/dvd.h b/hooklib/dvd.h index ce2f09b..ba7777a 100644 --- a/hooklib/dvd.h +++ b/hooklib/dvd.h @@ -8,4 +8,7 @@ struct dvd_config { bool enable; }; +/* Init is not thread safe because API hook init is not thread safe blah + blah blah you know the drill by now. */ + void dvd_hook_init(const struct dvd_config *cfg, HINSTANCE self); From 360121ce1fb7efd4e950100104ab0f8857e5821d Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Fri, 18 Jun 2021 23:45:02 +0200 Subject: [PATCH 093/175] doc: fixed missing link to idz docs in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3fe4bc0..f7dd0a0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo * [Chunithm Amazon (Plus)](doc/chunihook.md) * [Chunithm Crystal (Plus)](doc/chunihook.md) * Initial D - * Initial D Zero + * [Initial D Arcade Stage Zero](doc/idzhook.md) ## End-users From 78f1eea44ec02d3bb60673fce72a0f7a1d0ac3c1 Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Sat, 19 Jun 2021 17:30:29 +0200 Subject: [PATCH 094/175] dist: remove obsolete file copy commands --- Package.mk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Package.mk b/Package.mk index 245a82f..4132a54 100644 --- a/Package.mk +++ b/Package.mk @@ -3,8 +3,6 @@ $(BUILD_DIR_ZIP)/chuni.zip: $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni $(V)mkdir -p $(BUILD_DIR_ZIP)/chuni/DEVICE $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ - $(BUILD_DIR_32)/aimeio/aimeio.dll \ - $(BUILD_DIR_32)/chuniio/chuniio.dll \ $(BUILD_DIR_32)/chunihook/chunihook.dll \ $(DIST_DIR)/chuni/segatools.ini \ $(DIST_DIR)/chuni/start.bat \ @@ -20,8 +18,6 @@ $(BUILD_DIR_ZIP)/idz.zip: $(V)mkdir -p $(BUILD_DIR_ZIP)/idz $(V)mkdir -p $(BUILD_DIR_ZIP)/idz/DEVICE $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ - $(BUILD_DIR_64)/aimeio/aimeio.dll \ - $(BUILD_DIR_64)/idzio/idzio.dll \ $(BUILD_DIR_64)/idzhook/idzhook.dll \ $(DIST_DIR)/idz/segatools.ini \ $(DIST_DIR)/idz/start.bat \ From 63c27ef01592f2bd8aeedab8fcfd3ab88800c96f Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Sat, 19 Jun 2021 17:32:02 +0200 Subject: [PATCH 095/175] dist: add idzhook.md to doc.zip --- Package.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.mk b/Package.mk index 4132a54..c18c033 100644 --- a/Package.mk +++ b/Package.mk @@ -31,6 +31,7 @@ $(BUILD_DIR_ZIP)/idz.zip: $(BUILD_DIR_ZIP)/doc.zip: \ $(DOC_DIR)/config \ $(DOC_DIR)/chunihook.md \ + $(DOC_DIR)/idzhook.md \ | $(zipdir)/ $(V)echo ... $@ $(V)zip -r $@ $^ From 7a807e81ad0bfb28aa9d9cd939cd31200a794d19 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 7 Oct 2020 17:26:12 +0000 Subject: [PATCH 096/175] board/io4.c: add configuration section to enable/disable emulation --- board/config.c | 8 ++++++++ board/config.h | 2 ++ board/io4.c | 11 ++++++++++- board/io4.h | 9 ++++++++- mu3hook/config.c | 1 + mu3hook/config.h | 1 + mu3hook/dllmain.c | 2 +- mu3hook/io4.c | 4 ++-- mu3hook/io4.h | 4 +++- 9 files changed, 36 insertions(+), 6 deletions(-) diff --git a/board/config.c b/board/config.c index e64e28a..191425a 100644 --- a/board/config.c +++ b/board/config.c @@ -31,3 +31,11 @@ void aime_config_load(struct aime_config *cfg, const wchar_t *filename) aime_dll_config_load(&cfg->dll, filename); cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename); } + +void io4_config_load(struct io4_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename); +} diff --git a/board/config.h b/board/config.h index 840c963..f436a82 100644 --- a/board/config.h +++ b/board/config.h @@ -3,6 +3,8 @@ #include #include +#include "board/io4.h" #include "board/sg-reader.h" void aime_config_load(struct aime_config *cfg, const wchar_t *filename); +void io4_config_load(struct io4_config *cfg, const wchar_t *filename); diff --git a/board/io4.c b/board/io4.c index c956700..efad62f 100644 --- a/board/io4.c +++ b/board/io4.c @@ -8,6 +8,7 @@ #include #include +#include "board/config.h" #include "board/guid.h" #include "board/io4.h" @@ -97,12 +98,20 @@ static uint8_t io4_system_status; static const struct io4_ops *io4_ops; static void *io4_ops_ctx; -HRESULT io4_hook_init(const struct io4_ops *ops, void *ctx) +HRESULT io4_hook_init( + const struct io4_config *cfg, + const struct io4_ops *ops, + void *ctx) { HRESULT hr; + assert(cfg != NULL); assert(ops != NULL); + if (!cfg->enable) { + return S_FALSE; + } + async_init(&io4_async, NULL); hr = iohook_open_nul_fd(&io4_fd); diff --git a/board/io4.h b/board/io4.h index 1ce7bcf..1a6cc05 100644 --- a/board/io4.h +++ b/board/io4.h @@ -11,6 +11,10 @@ enum { IO4_BUTTON_SERVICE = 1 << 6, }; +struct io4_config { + bool enable; +}; + struct io4_state { uint16_t adcs[8]; uint16_t spinners[4]; @@ -22,4 +26,7 @@ struct io4_ops { HRESULT (*poll)(void *ctx, struct io4_state *state); }; -HRESULT io4_hook_init(const struct io4_ops *ops, void *ctx); +HRESULT io4_hook_init( + const struct io4_config *cfg, + const struct io4_ops *ops, + void *ctx); diff --git a/mu3hook/config.c b/mu3hook/config.c index 0205a3f..aba8cf7 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -37,6 +37,7 @@ void mu3_hook_config_load( platform_config_load(&cfg->platform, filename); aime_config_load(&cfg->aime, filename); dvd_config_load(&cfg->dvd, filename); + io4_config_load(&cfg->io4, filename); gfx_config_load(&cfg->gfx, filename); mu3_dll_config_load(&cfg->dll, filename); } diff --git a/mu3hook/config.h b/mu3hook/config.h index 1d31ec5..982a688 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -15,6 +15,7 @@ struct mu3_hook_config { struct platform_config platform; struct aime_config aime; struct dvd_config dvd; + struct io4_config io4; struct gfx_config gfx; struct mu3_dll_config dll; }; diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 6d307de..242c579 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -71,7 +71,7 @@ static DWORD CALLBACK mu3_pre_startup(void) goto fail; } - hr = mu3_io4_hook_init(); + hr = mu3_io4_hook_init(&mu3_hook_cfg.io4); if (FAILED(hr)) { goto fail; diff --git a/mu3hook/io4.c b/mu3hook/io4.c index 1fd875a..7edcb0c 100644 --- a/mu3hook/io4.c +++ b/mu3hook/io4.c @@ -16,13 +16,13 @@ static const struct io4_ops mu3_io4_ops = { .poll = mu3_io4_poll, }; -HRESULT mu3_io4_hook_init(void) +HRESULT mu3_io4_hook_init(const struct io4_config *cfg) { HRESULT hr; assert(mu3_dll.init != NULL); - hr = io4_hook_init(&mu3_io4_ops, NULL); + hr = io4_hook_init(cfg, &mu3_io4_ops, NULL); if (FAILED(hr)) { return hr; diff --git a/mu3hook/io4.h b/mu3hook/io4.h index e427d20..acb53b3 100644 --- a/mu3hook/io4.h +++ b/mu3hook/io4.h @@ -2,4 +2,6 @@ #include -HRESULT mu3_io4_hook_init(void); +#include "board/io4.h" + +HRESULT mu3_io4_hook_init(const struct io4_config *cfg); From 3a71af7405e03813903f6d5bfa5968eec3d8d522 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 7 Oct 2020 17:27:23 +0000 Subject: [PATCH 097/175] platform: Add basic hwreset implementation for ALLS --- platform/config.c | 11 +++++ platform/config.h | 2 + platform/hwreset.c | 107 +++++++++++++++++++++++++++++++++++++++++++ platform/hwreset.h | 11 +++++ platform/meson.build | 2 + platform/platform.h | 2 + 6 files changed, 135 insertions(+) create mode 100644 platform/hwreset.c create mode 100644 platform/hwreset.h diff --git a/platform/config.c b/platform/config.c index 94b9e40..848cfbd 100644 --- a/platform/config.c +++ b/platform/config.c @@ -14,6 +14,7 @@ #include "platform/config.h" #include "platform/dns.h" #include "platform/hwmon.h" +#include "platform/hwreset.h" #include "platform/misc.h" #include "platform/netenv.h" #include "platform/nusec.h" @@ -30,6 +31,7 @@ void platform_config_load(struct platform_config *cfg, const wchar_t *filename) clock_config_load(&cfg->clock, filename); dns_config_load(&cfg->dns, filename); hwmon_config_load(&cfg->hwmon, filename); + hwreset_config_load(&cfg->hwreset, filename); misc_config_load(&cfg->misc, filename); pcbid_config_load(&cfg->pcbid, filename); netenv_config_load(&cfg->netenv, filename); @@ -117,9 +119,18 @@ void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"hwmon", L"enable", 1, filename); } +void hwreset_config_load(struct hwreset_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"hwreset", L"enable", 1, filename); +} + void misc_config_load(struct misc_config *cfg, const wchar_t *filename) { assert(cfg != NULL); + assert(filename != NULL); cfg->enable = GetPrivateProfileIntW(L"misc", L"enable", 1, filename); } diff --git a/platform/config.h b/platform/config.h index 9aeb5d3..7ece41d 100644 --- a/platform/config.h +++ b/platform/config.h @@ -10,6 +10,7 @@ #include "platform/clock.h" #include "platform/dns.h" #include "platform/hwmon.h" +#include "platform/hwreset.h" #include "platform/misc.h" #include "platform/netenv.h" #include "platform/nusec.h" @@ -25,6 +26,7 @@ void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename); void clock_config_load(struct clock_config *cfg, const wchar_t *filename); void dns_config_load(struct dns_config *cfg, const wchar_t *filename); void hwmon_config_load(struct hwmon_config *cfg, const wchar_t *filename); +void hwreset_config_load(struct hwreset_config *cfg, const wchar_t *filename); void misc_config_load(struct misc_config *cfg, const wchar_t *filename); void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename); void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename); diff --git a/platform/hwreset.c b/platform/hwreset.c new file mode 100644 index 0000000..51ec337 --- /dev/null +++ b/platform/hwreset.c @@ -0,0 +1,107 @@ +#include + +#include +#include + +#include "hook/iohook.h" + +#include "platform/hwreset.h" + +#include "util/dprintf.h" +#include "util/str.h" + +enum { + HWRESET_IOCTL_RESTART = 0x80002000, +}; + +static HRESULT hwreset_handle_irp(struct irp *irp); +static HRESULT hwreset_handle_open(struct irp *irp); +static HRESULT hwreset_handle_close(struct irp *irp); +static HRESULT hwreset_handle_ioctl(struct irp *irp); + +static HRESULT hwreset_ioctl_restart(struct irp *irp); + +static HANDLE hwreset_fd; + +HRESULT hwreset_hook_init(const struct hwreset_config *cfg) +{ + HRESULT hr; + + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + hr = iohook_open_nul_fd(&hwreset_fd); + + if (FAILED(hr)) { + return hr; + } + + hr = iohook_push_handler(hwreset_handle_irp); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +static HRESULT hwreset_handle_irp(struct irp *irp) +{ + assert(irp != NULL); + + if (irp->op != IRP_OP_OPEN && irp->fd != hwreset_fd) { + return iohook_invoke_next(irp); + } + + switch (irp->op) { + case IRP_OP_OPEN: return hwreset_handle_open(irp); + case IRP_OP_CLOSE: return hwreset_handle_close(irp); + case IRP_OP_IOCTL: return hwreset_handle_ioctl(irp); + default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + } +} + +static HRESULT hwreset_handle_open(struct irp *irp) +{ + if (!wstr_ieq(irp->open_filename, L"\\\\.\\sghwreset")) { + return iohook_invoke_next(irp); + } + + dprintf("Hwreset: Opened device\n"); + irp->fd = hwreset_fd; + + return S_OK; +} + +static HRESULT hwreset_handle_close(struct irp *irp) +{ + dprintf("Hwreset: Closed device\n"); + + return S_OK; +} + +static HRESULT hwreset_handle_ioctl(struct irp *irp) +{ + switch (irp->ioctl) { + case HWRESET_IOCTL_RESTART: + return hwreset_ioctl_restart(irp); + + default: + dprintf("Hwreset: Unknown ioctl %08x, write %i read %i\n", + irp->ioctl, + (int) irp->write.nbytes, + (int) irp->read.nbytes); + + return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); + } +} + +static HRESULT hwreset_ioctl_restart(struct irp *irp) +{ + dprintf("Hwreset: Reset requested\n"); + + return iobuf_write_le32(&irp->read, 1); +} diff --git a/platform/hwreset.h b/platform/hwreset.h new file mode 100644 index 0000000..9f42615 --- /dev/null +++ b/platform/hwreset.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +struct hwreset_config { + bool enable; +}; + +HRESULT hwreset_hook_init(const struct hwreset_config *cfg); diff --git a/platform/meson.build b/platform/meson.build index 7e5c544..4f0fbc9 100644 --- a/platform/meson.build +++ b/platform/meson.build @@ -18,6 +18,8 @@ platform_lib = static_library( 'dns.h', 'hwmon.c', 'hwmon.h', + 'hwreset.c', + 'hwreset.h', 'misc.c', 'misc.h', 'netenv.c', diff --git a/platform/platform.h b/platform/platform.h index aaaff51..69c65e2 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -6,6 +6,7 @@ #include "platform/clock.h" #include "platform/dns.h" #include "platform/hwmon.h" +#include "platform/hwreset.h" #include "platform/misc.h" #include "platform/netenv.h" #include "platform/nusec.h" @@ -17,6 +18,7 @@ struct platform_config { struct clock_config clock; struct dns_config dns; struct hwmon_config hwmon; + struct hwreset_config hwreset; struct misc_config misc; struct pcbid_config pcbid; struct netenv_config netenv; From da6879606ae867f6f262d9548911dc8e72c1162f Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 12 Aug 2021 23:13:58 +0000 Subject: [PATCH 098/175] platform: Prevent amdaemon from shutting down the computer or logging off user --- platform/misc.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/platform/misc.c b/platform/misc.c index f0feadf..f4bc87d 100644 --- a/platform/misc.c +++ b/platform/misc.c @@ -5,16 +5,29 @@ #include #include +#include "hook/table.h" + #include "hooklib/reg.h" #include "platform/misc.h" +#include "util/dprintf.h" + +static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason); + static HRESULT misc_read_os_version(void *bytes, uint32_t *nbytes); static HRESULT misc_read_app_loader_count(void *bytes, uint32_t *nbytes); static HRESULT misc_read_cpu_temp_error(void *bytes, uint32_t *nbytes); static HRESULT misc_read_cpu_temp_warning(void *bytes, uint32_t *nbytes); static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes); +static const struct hook_symbol misc_syms[] = { + { + .name = "ExitWindowsEx", + .patch = misc_ExitWindowsEx, + } +}; + static const struct reg_hook_val misc_root_keys[] = { { .name = L"OSVersion", @@ -109,9 +122,20 @@ HRESULT misc_hook_init(const struct misc_config *cfg, const char *platform_id) return hr; } + /* Apply function hooks */ + + hook_table_apply(NULL, "user32.dll", misc_syms, _countof(misc_syms)); + return S_OK; } +static BOOL WINAPI misc_ExitWindowsEx(unsigned int flags, uint32_t reason) +{ + dprintf("Misc: Blocked system reboot\n"); + + return TRUE; +} + static HRESULT misc_read_os_version(void *bytes, uint32_t *nbytes) { return reg_hook_read_wstr(bytes, nbytes, L"0_0_0"); From 25f5ca944ef67e3b7c7c6ccda2c01fa3c514c0a6 Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 5 Aug 2021 21:57:50 +0000 Subject: [PATCH 099/175] hooklib/path.c: Hook RemoveDirectory - A certain game uses RemoveDirectory to clear out some data in the Y: --- hooklib/path.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/hooklib/path.c b/hooklib/path.c index 6d27efc..0c6fe42 100644 --- a/hooklib/path.c +++ b/hooklib/path.c @@ -93,6 +93,10 @@ static BOOL WINAPI hook_GetFileAttributesExW( GET_FILEEX_INFO_LEVELS fInfoLevelId, void *lpFileInformation); +static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName); + +static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName); + /* Link pointers */ static BOOL (WINAPI *next_CreateDirectoryA)( @@ -169,6 +173,10 @@ static BOOL (WINAPI *next_GetFileAttributesExW)( GET_FILEEX_INFO_LEVELS fInfoLevelId, void *lpFileInformation); +static BOOL (WINAPI *next_RemoveDirectoryA)(const char *lpFileName); + +static BOOL (WINAPI *next_RemoveDirectoryW)(const wchar_t *lpFileName); + /* Hook table */ static const struct hook_symbol path_hook_syms[] = { @@ -228,6 +236,14 @@ static const struct hook_symbol path_hook_syms[] = { .name = "GetFileAttributesExW", .patch = hook_GetFileAttributesExW, .link = (void **) &next_GetFileAttributesExW, + }, { + .name = "RemoveDirectoryA", + .patch = hook_RemoveDirectoryA, + .link = (void **) &next_RemoveDirectoryA, + }, { + .name = "RemoveDirectoryW", + .patch = hook_RemoveDirectoryW, + .link = (void **) &next_RemoveDirectoryW, } }; @@ -802,3 +818,39 @@ static BOOL WINAPI hook_GetFileAttributesExW( return ok; } + +static BOOL WINAPI hook_RemoveDirectoryA(const char *lpFileName) +{ + char *trans; + BOOL ok; + + ok = path_transform_a(&trans, lpFileName); + + if (!ok) { + return FALSE; + } + + ok = next_RemoveDirectoryA(trans ? trans : lpFileName); + + free(trans); + + return ok; +} + +static BOOL WINAPI hook_RemoveDirectoryW(const wchar_t *lpFileName) +{ + wchar_t *trans; + BOOL ok; + + ok = path_transform_w(&trans, lpFileName); + + if (!ok) { + return FALSE; + } + + ok = next_RemoveDirectoryW(trans ? trans : lpFileName); + + free(trans); + + return ok; +} From a1a41595d44a7d4354adab9a4296bf737789cad5 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 12 Jun 2021 20:27:48 +0000 Subject: [PATCH 100/175] Add CONTRIBUTING Taken from BT with some tweaks here and there --- CONTRIBUTING.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a80fdf4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,83 @@ +# Contributing + +This document outlines different types of contributions and how YOU can help us to improve the +project. Read it, as it provides guidelines that are there to help you and the maintainers. + +## Reporting and discussions: Issues section on gitlab + +In order to avoid information management overehad on different communication channels, discussions +we ask everyone to treat the issue section on gitlab as the place to open their relevant discussions +and bug reports regarding the project. + +The maintainers of the project do not have the time nor motivation to micromanage information from +various channels. We ask *EVERYONE* to support the maintainers and developers to allow them to spend +their time on developing tasks. + +## Bug reports + +Follow these steps when reporting bugs to ensure you provide all information we *always* need and +make your report valuable and actionable. + +1. On the segatools repository, go `Issues` on the left-hand sidebar. +1. Use the search function to check if there is an already open issue regarding what you want to +report + 1. If that applies, read the open issue to check what's already covered regarding the bug + 1. Provide additional information or things that are missing. Upload your log files, + screenshots, videos etc. Be careful and remove sensitive information + 1. Give a thumbs up to the issue to show you are interested/affected as well +1. If no existing issue is avilable, create a new one +1. Come up with a descriptive title +1. **USE OUR BUG REPORTING TEMPLATE**: Pick it by selecting `Bug` on the `Description` section +1. Follow the sections and their instructions provided by the template and fill them in. All fields +are mandatory to provide a comprehensive report if not stated otherwise +1. When finished, submit the issue + +## Merge requests: bugfixes, new features or other code contributions + +Merge requests are welcome! May it be a merge request to an already known issue or a new feature that +you consider as a valuable contribution, please open a MR. + +**!!! Maintaining documentation by adding new or improving existing documentation is as important as +code !!!** + +If you want to start working on a new feature that was proposed in an issue, yet, it is recommended +to reach out to the maintainers about this, first, to discuss if this contribution is valuable +to the project. Otherwise, you might waste your time on implementing something that won't make it +into master or someone else is already working on. + +Please read our [development guidelines](doc/development.md) as they contain valuable information +that your contribution meets our standards. This is not meant to annoy people but ensures +consistency that the project stays maintainable for everyone. + +Steps for contributing to the repository using a merge request: + +1. If you are new to git, take a bit of time to learn the basics which are very simple, e.g. Google +for "git tutorial for non-programmers" +1. Fork the upstream repository (Fork button on the top right on the main page of the repository) +1. You can start editing files like documentation easily inside gitlab which might be the prefered +option for many non-coders +1. Clone your fork to your local machine and start working on stuff +1. Ensure you push your changes to your fork on gitlab +1. When done, go to the `Merge Requests` section on the left sidebar of the upstream repository +1. Hit the `New merge request` button +1. Select the `master` branch as the source branch +1. Select whatever branch you worked, likely `master` if you didn't change that, as the target +branch +1. Hit `Compare branches and continue` +1. Provide a descriptive title of what your change is about +1. **USE OUR MR TEMPLATES** + 1. If you submit a bugfix, use the `Bugfix` tempalte and fill in the sections + 1. If you submit a new feature, use the `Feature` template and fill in the sections +1. If you submit some minor fixes or documentation improvements, there is no template for that. +Please provide a expressive description what you did and *why* you did that +1. If any of your changes are tied to one or multiple issues, link them in the description +1. When done, hit `Submit merge request` + +The maintainers will take a look at your submission and provide their feedback. The intention of +this process is to ensure the contribution meets the quality standards. Please also see this is +a learning opportunity, especially with your first contribution, if a lot of comments and change +requests are being made. The maintainers are open to discuss their suggestions/feedback if +reasonable feedback is given back to them. + +Once all discussion is resolved and the involved maintainers approved your submission, it will be +merged into master and also included in the next release. From d6a66b17459c33b41b9d526023b749a7544ea907 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 12 Jun 2021 22:35:55 +0200 Subject: [PATCH 101/175] Add development.md documentation Move stuff from root README and add a few more things that I took from BT5. --- README.md | 73 +++------------------------- doc/development.md | 117 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 66 deletions(-) create mode 100644 doc/development.md diff --git a/README.md b/README.md index f7dd0a0..e6dcd78 100644 --- a/README.md +++ b/README.md @@ -20,71 +20,12 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo For setup and configuration guides, refer to the dedicated documents available for each game, see [the links in the previous section](#list-of-supported-games). +## Contributors + +If you are/want to be a contributor of any kind, e.g. new features, bug fixes, documentation improvements, ..., please +read the [contributing documentation](CONTRIBUTING.md), first. + ## Developers -### Building - -The root `Makefile` contains various targets that allow you to build the project easily. - -#### Local build - -For a local build, you need to install Meson and a recent build of MinGW-w64. Then you can start the -build process: - -```shell -make build -``` - -Build output will be located in `build/build32` and `build/build64` folders. - -#### Cleanup local build - -```shell -make clean -``` - -#### Create distribution package (zip file) - -```shell -make dist -``` - -The output will be located in `build/zip`. - -#### Build and create distribution package using docker - -You can also build using docker which avoids having to setup a full development environment if you -are just interested in building binaries of the latest changes. Naturally, this requires you to -have the docker daemon installed. - -```shell -make build-docker -``` - -Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. - -#### Building with Docker Desktop on Windows - -* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) -* [Install Docker Desktop](https://docs.docker.com/docker-for-windows/install/) -* Run Docker Desktop to start the Docker Engine -* Open a command prompt (`cmd.exe`) and `cd` to your `segatools` folder -* Run `docker-build.bat` -* Once completed successfully, build output is located in the `build/docker/zip` sub-folder. - -#### Building with Docker on Windows using WSL2 - -* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) -* Regarding Linux distribution, we recommend using Ubuntu 20.04 -* Run the "Ubuntu 20.04 LTS" App which opens a Linux shell -* Install `make` and `docker` by running - * `sudo apt-get update` - * `sudo apt-get install make docker.io` -* Add the current user to the docker group that you don't have to run docker commands with root: -`sudo usermod -a -G docker $USER` -* Run the docker daemon in the background: `sudo dockerd > /dev/null 2>&1 &` -* Navigate to your segatools folder. If it is located on the `C:` drive, WSL automatically provides -a mountpoint for that under `/mnt/c`, e.g. `cd /mnt/c/segatools` (if the folder `segatools` is -located under `C:\segatools` on Windows). -* Build segatools: `make build-docker` -* Once completed successfully, build output is located in the `build/docker/zip` sub-folder. +For development setup and instructions how to build the project, refer to the +[dedicated development documentation](doc/development.md). diff --git a/doc/development.md b/doc/development.md new file mode 100644 index 0000000..8f3a4ab --- /dev/null +++ b/doc/development.md @@ -0,0 +1,117 @@ +# Development + +This document is intended for developers interested in contributing to segatools. Please read this document before +you start developing/contributing. + +## Goals + +We want you to understand what this project is about and its goals. The following list serves as a guidance for all +developers to identify valuable contributions for this project. As the project evolves, these gaols might do as well. + +* Allow running Sega arcade (rhythm) games on arbitrary hardware + * Emulate required software and hardware features + * Provide means to cope with incompatibility issues resulting from using a different software platform (e.g. version of Windows). +* Provide an API for custom interfaces and configuring fundamental application features + +## Development environment + +The following tooling is required in order to build this project. + +### Tooling + +#### Linux / MacOSX + +* git +* make +* mingw-w64 +* docker (optional) + +On MacOSX, you can use homebrew or macports to install these packages. + +#### Windows + +TODO + +### IDE + +Ultimately, you are free to use whatever you feel comfortable with for development. The following is our preferred +development environment which we run on a Linux distribution of our choice: + +* Visual Studio Code with the following extensions + * C/C++ + * C++ Intellisense + +### Further tools for testing and debugging + +* Debugger: Can be part of your reverse engineering IDE of your choice or stand-along like +[OllyDbg](http://www.ollydbg.de/) +* [apitrace](https://apitrace.github.io/): Trace render calls to graphics APIs like D3D and OpenGL. +This tool allows you to record and re-play render calls of an application with frame-by-frame +debugging. Very useful to analyze the render pipeline or debug graphicial glitches + +## Building + +The root `Makefile` contains various targets that allow you to build the project easily. + +### Local build + +For a local build, you need to install Meson and a recent build of MinGW-w64. Then you can start the +build process: + +```shell +make build +``` + +Build output will be located in `build/build32` and `build/build64` folders. + +### Cleanup local build + +```shell +make clean +``` + +### Create distribution package (zip file) + +```shell +make dist +``` + +The output will be located in `build/zip`. + +### Build and create distribution package using docker + +You can also build using docker which avoids having to setup a full development environment if you +are just interested in building binaries of the latest changes. Naturally, this requires you to +have the docker daemon installed. + +```shell +make build-docker +``` + +Once completed successfully, the build output is located in the `build/docker/zip` sub-folder. + +### Building with Docker Desktop on Windows + +* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) +* [Install Docker Desktop](https://docs.docker.com/docker-for-windows/install/) +* Run Docker Desktop to start the Docker Engine +* Open a command prompt (`cmd.exe`) and `cd` to your `segatools` folder +* Run `docker-build.bat` +* Once completed successfully, build output is located in the `build/docker/zip` sub-folder. + +### Building with Docker on Windows using WSL2 + +* [Install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) +* Regarding Linux distribution, we recommend using Ubuntu 20.04 +* Run the "Ubuntu 20.04 LTS" App which opens a Linux shell +* Install `make` and `docker` by running + * `sudo apt-get update` + * `sudo apt-get install make docker.io` +* Add the current user to the docker group that you don't have to run docker commands with root: +`sudo usermod -a -G docker $USER` +* Run the docker daemon in the background: `sudo dockerd > /dev/null 2>&1 &` +* Navigate to your segatools folder. If it is located on the `C:` drive, WSL automatically provides +a mountpoint for that under `/mnt/c`, e.g. `cd /mnt/c/segatools` (if the folder `segatools` is +located under `C:\segatools` on Windows). +* Build segatools: `make build-docker` +* Once completed successfully, build output is located in the `build/docker/zip` sub-folder \ No newline at end of file From 85e644cbd602cf23d2af0db83bf1410c555ed56f Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 12 Jun 2021 22:45:30 +0200 Subject: [PATCH 102/175] Add gitlab MR and issue templates Ported with some tweaks from BT5 --- .gitlab/issue_templates/Bug.md | 108 +++++++++++++++++++++ .gitlab/issue_templates/Feature.md | 21 ++++ .gitlab/merge_request_templates/Bugfix.md | 34 +++++++ .gitlab/merge_request_templates/Feature.md | 34 +++++++ 4 files changed, 197 insertions(+) create mode 100644 .gitlab/issue_templates/Bug.md create mode 100644 .gitlab/issue_templates/Feature.md create mode 100644 .gitlab/merge_request_templates/Bugfix.md create mode 100644 .gitlab/merge_request_templates/Feature.md diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md new file mode 100644 index 0000000..459c9fc --- /dev/null +++ b/.gitlab/issue_templates/Bug.md @@ -0,0 +1,108 @@ +# Bug Report + +## Summary + + + +## Expected behavior + + + +## Current behavior + + + +## Detailed Description + + + +## Steps to reproduce + + + + + +1. +2. +3. +4. + +## Possible solution + + + +## Context (Environment) + +### Segatools version(s) affected + +* + +### Game(s) and version(s) affected + +* + +### Log output + +See attachment. + + +### Configuration files + +See attachment. + + + +### Command line arguments + + + +### APIs used + +* + +### OS version + + + +### Hardware specs + +* CPU: +* RAM: +* GPU: +* Controllers/IO: diff --git a/.gitlab/issue_templates/Feature.md b/.gitlab/issue_templates/Feature.md new file mode 100644 index 0000000..ee93ab6 --- /dev/null +++ b/.gitlab/issue_templates/Feature.md @@ -0,0 +1,21 @@ +# Feature Request + +## Summary + + + +## Detailed description + + + +## Benefits + + + +## Drawbacks + + + +## Current blockers + + \ No newline at end of file diff --git a/.gitlab/merge_request_templates/Bugfix.md b/.gitlab/merge_request_templates/Bugfix.md new file mode 100644 index 0000000..dd18823 --- /dev/null +++ b/.gitlab/merge_request_templates/Bugfix.md @@ -0,0 +1,34 @@ +# Bugfix + +## Summary + + + +## Description + + + +## Related Issue + + + + + + +## How Has This Been Tested? + + + + + +## Checklist + + + + +* Tested with the following games: + * [ ] ... + * [ ] ... +* [ ] Followed the developer (style) guidelines. +* [ ] Updated existing doc of or add new doc to README file(s) +* [ ] Updated development documentation \ No newline at end of file diff --git a/.gitlab/merge_request_templates/Feature.md b/.gitlab/merge_request_templates/Feature.md new file mode 100644 index 0000000..1e4ce9f --- /dev/null +++ b/.gitlab/merge_request_templates/Feature.md @@ -0,0 +1,34 @@ +# New Feature + +## Summary + + + +## Description + + + +## Related Issue + + + + + + +## How Has This Been Tested? + + + + + +## Checklist + + + + +* Tested with the following games: + * [ ] ... + * [ ] ... +* [ ] Followed the developer (style) guidelines +* [ ] Updated existing doc of or add new doc to README file(s) +* [ ] Updated development documentation \ No newline at end of file From b95f1df54a2b704d93bde6a632db9d45d373d5a9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sat, 12 Jun 2021 22:48:21 +0200 Subject: [PATCH 103/175] Add section about git history to contributing docs --- CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a80fdf4..322c9bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,15 @@ report are mandatory to provide a comprehensive report if not stated otherwise 1. When finished, submit the issue +## Git history + +The project is aiming for a linear git history approach since we are convinced that it beneifts +maintenance of the project a lot in the long term. + +For further details on that topic, please refer to +[the many external articles](https://dev.to/bladesensei/avoid-messy-git-history-3g26) available +about this topic by using your favorite search engine. + ## Merge requests: bugfixes, new features or other code contributions Merge requests are welcome! May it be a merge request to an already known issue or a new feature that From db12772b7ae8e15accc77d2072925a7b1d6da231 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 20 Jun 2021 16:02:00 +0000 Subject: [PATCH 104/175] Fix typo in docs --- doc/development.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development.md b/doc/development.md index 8f3a4ab..2352f87 100644 --- a/doc/development.md +++ b/doc/development.md @@ -6,7 +6,7 @@ you start developing/contributing. ## Goals We want you to understand what this project is about and its goals. The following list serves as a guidance for all -developers to identify valuable contributions for this project. As the project evolves, these gaols might do as well. +developers to identify valuable contributions for this project. As the project evolves, these goals might do as well. * Allow running Sega arcade (rhythm) games on arbitrary hardware * Emulate required software and hardware features From 47581259c85360ccf2ea9f3ac551700f9034cc96 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 15 Aug 2021 11:11:06 +0200 Subject: [PATCH 105/175] Simplified bug reporting template The prior version was a little too optimistic and more difficult to fill in for the average user --- .gitlab/issue_templates/Bug.md | 105 ++++----------------------------- 1 file changed, 12 insertions(+), 93 deletions(-) diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 459c9fc..5406092 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,107 +1,26 @@ # Bug Report -## Summary +## Description of the bug - - -## Expected behavior - - - -## Current behavior - - - -## Detailed Description - - - -## Steps to reproduce - - - - - -1. -2. -3. -4. - -## Possible solution - - - -## Context (Environment) - -### Segatools version(s) affected - -* - -### Game(s) and version(s) affected - -* - -### Log output - -See attachment. -### Configuration files +## Affected games and versions -See attachment. + - -[REDACTED] - -BEFORE UPLOADING/POSTING ANY LOG DATA!!! - -Use the search and replace feature of any kind of text editor. -!!!!!! ---> - -### Command line arguments - - - -### APIs used - -* - -### OS version - - - -### Hardware specs +## OS and hardware specs +* OS: * CPU: * RAM: * GPU: From dd363ed00008ff8c3a8569111a6c3ce36ba71d0b Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 12 Aug 2021 23:31:50 +0000 Subject: [PATCH 106/175] hooklib/dll.c: Add LoadLibraryEx hooks - Initial D: Zero uses `LoadLibraryExW` to load `d3d11.dll` and `dxgi.dll`. --- hooklib/dll.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/hooklib/dll.c b/hooklib/dll.c index be9428f..5987c1d 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -29,6 +29,8 @@ static HMODULE WINAPI hook_GetModuleHandleA(const char *name); static HMODULE WINAPI hook_GetModuleHandleW(const wchar_t *name); static HMODULE WINAPI hook_LoadLibraryA(const char *name); static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name); +static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags); +static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags); /* Link pointers */ @@ -37,6 +39,8 @@ static HMODULE (WINAPI *next_GetModuleHandleA)(const char *name); static HMODULE (WINAPI *next_GetModuleHandleW)(const wchar_t *name); static HMODULE (WINAPI *next_LoadLibraryA)(const char *name); static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name); +static HMODULE (WINAPI *next_LoadLibraryExA)(const char *name, HANDLE file, DWORD flags); +static HMODULE (WINAPI *next_LoadLibraryExW)(const wchar_t *name, HANDLE file, DWORD flags); static const struct hook_symbol dll_loader_syms[] = { { @@ -59,6 +63,14 @@ static const struct hook_symbol dll_loader_syms[] = { .name = "LoadLibraryW", .patch = hook_LoadLibraryW, .link = (void **) &next_LoadLibraryW, + }, { + .name = "LoadLibraryExA", + .patch = hook_LoadLibraryExA, + .link = (void **) &next_LoadLibraryExA, + }, { + .name = "LoadLibraryExW", + .patch = hook_LoadLibraryExW, + .link = (void **) &next_LoadLibraryExW, } }; @@ -287,4 +299,51 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name) return result; } -/* TODO LoadLibraryExA, LoadLibraryExW */ +static HMODULE WINAPI hook_LoadLibraryExA(const char *name, HANDLE file, DWORD flags) +{ + HMODULE result; + wchar_t *name_w; + size_t name_c; + + if (name == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + + return NULL; + } + + mbstowcs_s(&name_c, NULL, 0, name, 0); + name_w = malloc(name_c * sizeof(wchar_t)); + + if (name_w == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + + return NULL; + } + + mbstowcs_s(NULL, name_w, name_c, name, name_c - 1); + result = hook_LoadLibraryExW(name_w, file, flags); + free(name_w); + + return result; +} + +static HMODULE WINAPI hook_LoadLibraryExW(const wchar_t *name, HANDLE file, DWORD flags) +{ + HMODULE result; + + if (name == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + + return NULL; + } + + result = dll_hook_search_dll(name); + + if (result != NULL) { + SetLastError(ERROR_SUCCESS); + } else { + result = next_LoadLibraryExW(name, file, flags); + } + + return result; +} From 5a383b804b6763a3396488162a6fc126b6806ee4 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 13 Aug 2021 08:24:36 +0000 Subject: [PATCH 107/175] hooklib/dll.c: Ensure LoadLibraryExW is always available --- hooklib/dll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hooklib/dll.c b/hooklib/dll.c index 5987c1d..9400bb9 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -131,7 +131,8 @@ static void dll_hook_init(void) InitializeCriticalSection(&dll_hook_lock); /* Protect against the (probably impossible) scenario where nothing in the - process imports LoadLibraryW but something imports LoadLibraryA. + process imports LoadLibraryW but something imports LoadLibraryA. Also + do the same with LoadLibraryExW. We know something imports GetModuleHandleW because we do, right here. @@ -140,6 +141,7 @@ static void dll_hook_init(void) kernel32 = GetModuleHandleW(L"kernel32.dll"); next_LoadLibraryW = (void *) GetProcAddress(kernel32, "LoadLibraryW"); + next_LoadLibraryExW = (void *) GetProcAddress(kernel32, "LoadLibraryExW"); /* Now we can apply the hook table */ From b7f52232a0d690d99304677b653f3530e1eb1f5c Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 15 Aug 2021 23:07:46 +0000 Subject: [PATCH 108/175] idzhook: Add missing idz_io_get_api_version export --- idzhook/idzhook.def | 1 + 1 file changed, 1 insertion(+) diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index 0a8979e..486400c 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -11,6 +11,7 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + idz_io_get_api_version idz_io_jvs_init idz_io_jvs_read_analogs idz_io_jvs_read_buttons From e6810da3acd0f4a9fd0c749823c9508644491a2a Mon Sep 17 00:00:00 2001 From: Felix Date: Sun, 15 Aug 2021 23:08:04 +0000 Subject: [PATCH 109/175] mu3hook: Add missing mu3_io_get_api_version export --- mu3hook/mu3hook.def | 1 + 1 file changed, 1 insertion(+) diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index b3a1840..e7367fb 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -12,6 +12,7 @@ EXPORTS amDllVideoGetVBiosVersion @4 amDllVideoOpen @1 amDllVideoSetResolution @3 + mu3_io_get_api_version mu3_io_get_gamebtns mu3_io_get_lever mu3_io_get_opbtns From c6b245d1033e6178ea254bb063cabbfc40493d96 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 1 Sep 2021 22:52:07 +0000 Subject: [PATCH 110/175] amex/ds.c: Handle ABI version ioctl --- amex/ds.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/amex/ds.c b/amex/ds.c index ac3919f..c0f357f 100644 --- a/amex/ds.c +++ b/amex/ds.c @@ -22,8 +22,9 @@ #pragma pack(push, 1) enum { - DS_IOCTL_SETUP = 0x80006004, - DS_IOCTL_READ_SECTOR = 0x80006010, + DS_IOCTL_GET_ABI_VERSION = 0x80006000, + DS_IOCTL_SETUP = 0x80006004, + DS_IOCTL_READ_SECTOR = 0x80006010, }; struct ds_eeprom { @@ -44,6 +45,7 @@ static HRESULT ds_handle_close(struct irp *irp); static HRESULT ds_handle_ioctl(struct irp *irp); static HRESULT ds_ioctl_get_geometry(struct irp *irp); +static HRESULT ds_ioctl_get_abi_version(struct irp *irp); static HRESULT ds_ioctl_setup(struct irp *irp); static HRESULT ds_ioctl_read_sector(struct irp *irp); @@ -134,6 +136,9 @@ static HRESULT ds_handle_ioctl(struct irp *irp) case IOCTL_DISK_GET_DRIVE_GEOMETRY: return ds_ioctl_get_geometry(irp); + case DS_IOCTL_GET_ABI_VERSION: + return ds_ioctl_get_abi_version(irp); + case DS_IOCTL_SETUP: return ds_ioctl_setup(irp); @@ -173,6 +178,11 @@ static HRESULT ds_ioctl_get_geometry(struct irp *irp) return hr; } +static HRESULT ds_ioctl_get_abi_version(struct irp *irp) +{ + return iobuf_write_le16(&irp->read, 256); +} + static HRESULT ds_ioctl_setup(struct irp *irp) { dprintf("DS: Setup IOCTL\n"); From 0b9f81fb2a64ac2c549bb1ea30aa7dab8c6eda36 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 1 Sep 2021 22:52:32 +0000 Subject: [PATCH 111/175] amex/eeprom.c: Handle ABI version ioctl --- amex/eeprom.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/amex/eeprom.c b/amex/eeprom.c index b9dd8b7..84e4701 100644 --- a/amex/eeprom.c +++ b/amex/eeprom.c @@ -20,6 +20,10 @@ #include "util/dprintf.h" #include "util/str.h" +enum { + EEPROM_IOCTL_GET_ABI_VERSION = 0x80006000, +}; + static HRESULT eeprom_handle_irp(struct irp *irp); static HRESULT eeprom_handle_open(struct irp *irp); static HRESULT eeprom_handle_close(struct irp *irp); @@ -28,6 +32,7 @@ static HRESULT eeprom_handle_read(struct irp *irp); static HRESULT eeprom_handle_write(struct irp *irp); static HRESULT eeprom_ioctl_get_geometry(struct irp *irp); +static HRESULT eeprom_ioctl_get_abi_version(struct irp *irp); static struct eeprom_config eeprom_config; static HANDLE eeprom_file; @@ -117,6 +122,9 @@ static HRESULT eeprom_handle_ioctl(struct irp *irp) case IOCTL_DISK_GET_DRIVE_GEOMETRY: return eeprom_ioctl_get_geometry(irp); + case EEPROM_IOCTL_GET_ABI_VERSION: + return eeprom_ioctl_get_abi_version(irp); + default: dprintf("EEPROM: Unknown ioctl %x, write %i read %i\n", irp->ioctl, @@ -150,6 +158,11 @@ static HRESULT eeprom_ioctl_get_geometry(struct irp *irp) return hr; } +static HRESULT eeprom_ioctl_get_abi_version(struct irp *irp) +{ + return iobuf_write_le16(&irp->read, 256); +} + static HRESULT eeprom_handle_read(struct irp *irp) { if (irp->ovl == NULL) { From bbb7cdf4a5152b4d348cf6208e6128fff2cad246 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 1 Sep 2021 22:52:40 +0000 Subject: [PATCH 112/175] amex/sram.c: Handle ABI version ioctl --- amex/sram.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/amex/sram.c b/amex/sram.c index 28fdfa0..c5a195a 100644 --- a/amex/sram.c +++ b/amex/sram.c @@ -20,12 +20,17 @@ #include "util/dprintf.h" #include "util/str.h" +enum { + SRAM_IOCTL_GET_ABI_VERSION = 0x80006000, +}; + static HRESULT sram_handle_irp(struct irp *irp); static HRESULT sram_handle_open(struct irp *irp); static HRESULT sram_handle_close(struct irp *irp); static HRESULT sram_handle_ioctl(struct irp *irp); static HRESULT sram_ioctl_get_geometry(struct irp *irp); +static HRESULT sram_ioctl_get_abi_version(struct irp *irp); static struct sram_config sram_config; static HANDLE sram_file; @@ -113,6 +118,9 @@ static HRESULT sram_handle_ioctl(struct irp *irp) case IOCTL_DISK_GET_DRIVE_GEOMETRY: return sram_ioctl_get_geometry(irp); + case SRAM_IOCTL_GET_ABI_VERSION: + return sram_ioctl_get_abi_version(irp); + default: dprintf("SRAM: Unknown ioctl %x, write %i read %i\n", irp->ioctl, @@ -145,3 +153,8 @@ static HRESULT sram_ioctl_get_geometry(struct irp *irp) return hr; } + +static HRESULT sram_ioctl_get_abi_version(struct irp *irp) +{ + return iobuf_write_le16(&irp->read, 256); +} From 8412ee2c16db2a4d76a43a7cb34cd57c5cea55b5 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 25 Oct 2021 09:30:34 +0000 Subject: [PATCH 113/175] util/lib.c: Add module file path utility --- util/lib.c | 29 +++++++++++++++++++++++++++++ util/lib.h | 5 +++++ util/meson.build | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 util/lib.c create mode 100644 util/lib.h diff --git a/util/lib.c b/util/lib.c new file mode 100644 index 0000000..bd7a5eb --- /dev/null +++ b/util/lib.c @@ -0,0 +1,29 @@ +#include + +#include + +wchar_t *module_file_name(HMODULE module) +{ + size_t buf_len; + DWORD len; + wchar_t *buf; + + buf_len = MAX_PATH; + buf = malloc(buf_len * sizeof(*buf)); + + while (true) { + len = GetModuleFileNameW(module, buf, buf_len); + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + buf_len = len; + buf = realloc(buf, buf_len * sizeof(*buf)); + + break; + } + + buf_len *= 2; + buf = realloc(buf, buf_len * sizeof(*buf)); + } + + return buf; +} diff --git a/util/lib.h b/util/lib.h new file mode 100644 index 0000000..f3d717e --- /dev/null +++ b/util/lib.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +wchar_t *module_file_name(HMODULE module); diff --git a/util/meson.build b/util/meson.build index c3c07f9..575d123 100644 --- a/util/meson.build +++ b/util/meson.build @@ -17,6 +17,8 @@ util_lib = static_library( 'dprintf.h', 'dump.c', 'dump.h', + 'lib.c', + 'lib.h', 'str.c', 'str.h', ], From f251283eb6b90b38376975ec55b73ece4d8b88d9 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 25 Oct 2021 09:16:14 +0000 Subject: [PATCH 114/175] hooklib: Add D3D11 and DXGI graphics hooks IDZ only supports D3D11 and Ongeki/Unity uses D3D11 by default. This also includes a window hook and fullscreen fix for problematic games (I am looking at you IDZ). --- chunihook/config.c | 2 +- chunihook/config.h | 2 +- chunihook/dllmain.c | 4 +- divahook/dllmain.c | 2 +- hooklib/config.c | 18 +-- hooklib/config.h | 4 +- hooklib/gfx/d3d11.c | 219 ++++++++++++++++++++++++++++ hooklib/gfx/d3d11.h | 7 + hooklib/{gfx.c => gfx/d3d9.c} | 68 +-------- hooklib/gfx/d3d9.h | 7 + hooklib/gfx/dxgi.c | 263 ++++++++++++++++++++++++++++++++++ hooklib/gfx/dxgi.h | 7 + hooklib/gfx/gfx.c | 113 +++++++++++++++ hooklib/{ => gfx}/gfx.h | 2 + hooklib/meson.build | 10 +- idzhook/config.c | 4 +- idzhook/config.h | 2 + idzhook/dllmain.c | 6 + idzhook/idzhook.def | 5 + mu3hook/config.c | 2 +- mu3hook/config.h | 2 +- mu3hook/dllmain.c | 7 + mu3hook/mu3hook.def | 5 + 23 files changed, 675 insertions(+), 86 deletions(-) create mode 100644 hooklib/gfx/d3d11.c create mode 100644 hooklib/gfx/d3d11.h rename hooklib/{gfx.c => gfx/d3d9.c} (66%) create mode 100644 hooklib/gfx/d3d9.h create mode 100644 hooklib/gfx/dxgi.c create mode 100644 hooklib/gfx/dxgi.h create mode 100644 hooklib/gfx/gfx.c rename hooklib/{ => gfx}/gfx.h (85%) diff --git a/chunihook/config.c b/chunihook/config.c index 3016445..9216d6a 100644 --- a/chunihook/config.c +++ b/chunihook/config.c @@ -13,7 +13,7 @@ #include "chunihook/config.h" #include "hooklib/config.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "platform/config.h" #include "platform/platform.h" diff --git a/chunihook/config.h b/chunihook/config.h index d3c094a..ded7a82 100644 --- a/chunihook/config.h +++ b/chunihook/config.h @@ -10,7 +10,7 @@ #include "chunihook/chuni-dll.h" #include "chunihook/slider.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "platform/platform.h" diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index f2dff5e..b209c3f 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -14,7 +14,8 @@ #include "hook/process.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/d3d9.h" +#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" @@ -61,6 +62,7 @@ static DWORD CALLBACK chuni_pre_startup(void) /* Hook Win32 APIs */ gfx_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); + gfx_d3d9_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); serial_hook_init(); /* Initialize emulation hooks */ diff --git a/divahook/dllmain.c b/divahook/dllmain.c index fd6ad9b..d06949c 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -13,7 +13,7 @@ #include "hook/process.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" diff --git a/hooklib/config.c b/hooklib/config.c index a7a5a57..5f5c080 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -5,8 +5,16 @@ #include #include "hooklib/config.h" -#include "hooklib/gfx.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/gfx.h" + +void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); +} void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) { @@ -18,11 +26,3 @@ void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); } - -void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) -{ - assert(cfg != NULL); - assert(filename != NULL); - - cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); -} diff --git a/hooklib/config.h b/hooklib/config.h index ed3d7d4..0c19443 100644 --- a/hooklib/config.h +++ b/hooklib/config.h @@ -3,8 +3,8 @@ #include #include -#include "hooklib/gfx.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/gfx.h" -void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); +void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); diff --git a/hooklib/gfx/d3d11.c b/hooklib/gfx/d3d11.c new file mode 100644 index 0000000..37f7fb7 --- /dev/null +++ b/hooklib/gfx/d3d11.c @@ -0,0 +1,219 @@ +#include +#include +#include + +#include +#include +#include + +#include "hook/com-proxy.h" +#include "hook/table.h" + +#include "hooklib/config.h" +#include "hooklib/dll.h" +#include "hooklib/gfx/gfx.h" + +#include "util/dprintf.h" + +typedef HRESULT (WINAPI *D3D11CreateDevice_t)( + IDXGIAdapter *pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL *ppFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + ID3D11Device **ppDevice, + D3D_FEATURE_LEVEL *pFeatureLevel, + ID3D11DeviceContext **ppImmediateContext); +typedef HRESULT (WINAPI *D3D11CreateDeviceAndSwapChain_t)( + IDXGIAdapter *pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL *ppFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, + IDXGISwapChain **ppSwapChain, + ID3D11Device **ppDevice, + D3D_FEATURE_LEVEL *pFeatureLevel, + ID3D11DeviceContext **ppImmediateContext); + +static struct gfx_config gfx_config; +static D3D11CreateDevice_t next_D3D11CreateDevice; +static D3D11CreateDeviceAndSwapChain_t next_D3D11CreateDeviceAndSwapChain; + +static const struct hook_symbol d3d11_hooks[] = { + { + .name = "D3D11CreateDevice", + .patch = D3D11CreateDevice, + .link = (void **) &next_D3D11CreateDevice, + }, { + .name = "D3D11CreateDeviceAndSwapChain", + .patch = D3D11CreateDeviceAndSwapChain, + .link = (void **) &next_D3D11CreateDeviceAndSwapChain, + }, +}; + +void gfx_d3d11_hook_init(const struct gfx_config *cfg, HINSTANCE self) +{ + HMODULE d3d11; + + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + memcpy(&gfx_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "d3d11.dll", d3d11_hooks, _countof(d3d11_hooks)); + + if (next_D3D11CreateDevice == NULL || next_D3D11CreateDeviceAndSwapChain == NULL) { + d3d11 = LoadLibraryW(L"d3d11.dll"); + + if (d3d11 == NULL) { + dprintf("D3D11: d3d11.dll not found or failed initialization\n"); + + goto fail; + } + + if (next_D3D11CreateDevice == NULL) { + next_D3D11CreateDevice = (D3D11CreateDevice_t) GetProcAddress( + d3d11, + "D3D11CreateDevice"); + } + if (next_D3D11CreateDeviceAndSwapChain == NULL) { + next_D3D11CreateDeviceAndSwapChain = (D3D11CreateDeviceAndSwapChain_t) GetProcAddress( + d3d11, + "D3D11CreateDeviceAndSwapChain"); + } + + if (next_D3D11CreateDevice == NULL) { + dprintf("D3D11: D3D11CreateDevice not found in loaded d3d11.dll\n"); + + goto fail; + } + if (next_D3D11CreateDeviceAndSwapChain == NULL) { + dprintf("D3D11: D3D11CreateDeviceAndSwapChain not found in loaded d3d11.dll\n"); + + goto fail; + } + } + + if (self != NULL) { + dll_hook_push(self, L"d3d11.dll"); + } + + return; + +fail: + if (d3d11 != NULL) { + FreeLibrary(d3d11); + } +} + +HRESULT WINAPI D3D11CreateDevice( + IDXGIAdapter *pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL *ppFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + ID3D11Device **ppDevice, + D3D_FEATURE_LEVEL *pFeatureLevel, + ID3D11DeviceContext **ppImmediateContext) +{ + dprintf("D3D11: D3D11CreateDevice hook hit\n"); + + return next_D3D11CreateDevice( + pAdapter, + DriverType, + Software, + Flags, + ppFeatureLevels, + FeatureLevels, + SDKVersion, + ppDevice, + pFeatureLevel, + ppImmediateContext); +} + +HRESULT WINAPI D3D11CreateDeviceAndSwapChain( + IDXGIAdapter *pAdapter, + D3D_DRIVER_TYPE DriverType, + HMODULE Software, + UINT Flags, + const D3D_FEATURE_LEVEL *ppFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, + IDXGISwapChain **ppSwapChain, + ID3D11Device **ppDevice, + D3D_FEATURE_LEVEL *pFeatureLevel, + ID3D11DeviceContext **ppImmediateContext) +{ + DXGI_SWAP_CHAIN_DESC *desc; + HWND hwnd; + LONG width; + LONG height; + + dprintf("D3D11: D3D11CreateDeviceAndSwapChain hook hit\n"); + + desc = (DXGI_SWAP_CHAIN_DESC *) pSwapChainDesc; + + if (desc != NULL) { + desc->Windowed = gfx_config.windowed; + + hwnd = desc->OutputWindow; + width = desc->BufferDesc.Width; + height = desc->BufferDesc.Height; + } else { + hwnd = NULL; + width = 0; + height = 0; + } + + if (hwnd != NULL) { + /* + * Ensure window is maximized to avoid a Windows 10 issue where a + * fullscreen swap chain is not created because the window is minimized + * at the time of creation. + */ + ShowWindow(hwnd, SW_RESTORE); + + if (!gfx_config.framed && width > 0 && height > 0) { + dprintf("DXGI: Resizing window to %ldx%ld\n", width, height); + + SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); + SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); + + SetWindowPos( + hwnd, + HWND_TOP, + 0, + 0, + width, + height, + SWP_FRAMECHANGED | SWP_NOSENDCHANGING); + + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + } + } + + return next_D3D11CreateDeviceAndSwapChain( + pAdapter, + DriverType, + Software, + Flags, + ppFeatureLevels, + FeatureLevels, + SDKVersion, + pSwapChainDesc, + ppSwapChain, + ppDevice, + pFeatureLevel, + ppImmediateContext); +} + diff --git a/hooklib/gfx/d3d11.h b/hooklib/gfx/d3d11.h new file mode 100644 index 0000000..7126d2e --- /dev/null +++ b/hooklib/gfx/d3d11.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "hooklib/gfx/gfx.h" + +void gfx_d3d11_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/hooklib/gfx.c b/hooklib/gfx/d3d9.c similarity index 66% rename from hooklib/gfx.c rename to hooklib/gfx/d3d9.c index 6b8390a..d3b810a 100644 --- a/hooklib/gfx.c +++ b/hooklib/gfx/d3d9.c @@ -10,7 +10,7 @@ #include "hooklib/config.h" #include "hooklib/dll.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "util/dprintf.h" @@ -24,7 +24,6 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev); -static HRESULT gfx_frame_window(HWND hwnd); static struct gfx_config gfx_config; static Direct3DCreate9_t next_Direct3DCreate9; @@ -37,7 +36,7 @@ static const struct hook_symbol gfx_hooks[] = { }, }; -void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) +void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self) { HMODULE d3d9; @@ -152,66 +151,3 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev); } - -static HRESULT gfx_frame_window(HWND hwnd) -{ - HRESULT hr; - DWORD error; - LONG style; - RECT rect; - BOOL ok; - - SetLastError(ERROR_SUCCESS); - style = GetWindowLongW(hwnd, GWL_STYLE); - error = GetLastError(); - - if (error != ERROR_SUCCESS) { - hr = HRESULT_FROM_WIN32(error); - dprintf("Gfx: GetWindowLongPtrW(%p, GWL_STYLE) failed: %x\n", - hwnd, - (int) hr); - - return hr; - } - - ok = GetClientRect(hwnd, &rect); - - if (!ok) { - hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Gfx: GetClientRect(%p) failed: %x\n", hwnd, (int) hr); - - return hr; - } - - style |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; - ok = AdjustWindowRect(&rect, style, FALSE); - - if (!ok) { - /* come on... */ - hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Gfx: AdjustWindowRect failed: %x\n", (int) hr); - - return hr; - } - - /* This... always seems to set an error, even though it works? idk */ - SetWindowLongW(hwnd, GWL_STYLE, style); - - ok = SetWindowPos( - hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_FRAMECHANGED | SWP_NOMOVE); - - if (!ok) { - hr = HRESULT_FROM_WIN32(GetLastError()); - dprintf("Gfx: SetWindowPos(%p) failed: %x\n", hwnd, (int) hr); - - return hr; - } - - return S_OK; -} diff --git a/hooklib/gfx/d3d9.h b/hooklib/gfx/d3d9.h new file mode 100644 index 0000000..fb5607a --- /dev/null +++ b/hooklib/gfx/d3d9.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "hooklib/gfx/gfx.h" + +void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/hooklib/gfx/dxgi.c b/hooklib/gfx/dxgi.c new file mode 100644 index 0000000..bbcc561 --- /dev/null +++ b/hooklib/gfx/dxgi.c @@ -0,0 +1,263 @@ +#include +#include + +#include +#include + +#include "hook/com-proxy.h" +#include "hook/table.h" + +#include "hooklib/config.h" +#include "hooklib/dll.h" +#include "hooklib/gfx/gfx.h" + +#include "util/dprintf.h" + +typedef HRESULT (WINAPI *CreateDXGIFactory_t)(REFIID riid, void **factory); +typedef HRESULT (WINAPI *CreateDXGIFactory1_t)(REFIID riid, void **factory); + +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( + IDXGIFactory *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain); +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain( + IDXGIFactory1 *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain); + +static struct gfx_config gfx_config; +static CreateDXGIFactory_t next_CreateDXGIFactory; +static CreateDXGIFactory1_t next_CreateDXGIFactory1; + +static const struct hook_symbol dxgi_hooks[] = { + { + .name = "CreateDXGIFactory", + .patch = CreateDXGIFactory, + .link = (void **) &next_CreateDXGIFactory, + }, { + .name = "CreateDXGIFactory1", + .patch = CreateDXGIFactory1, + .link = (void **) &next_CreateDXGIFactory1, + }, +}; + +void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self) +{ + HMODULE dxgi; + + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + memcpy(&gfx_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "dxgi.dll", dxgi_hooks, _countof(dxgi_hooks)); + + if (next_CreateDXGIFactory == NULL || next_CreateDXGIFactory1 == NULL) { + dxgi = LoadLibraryW(L"dxgi.dll"); + + if (dxgi == NULL) { + dprintf("DXGI: dxgi.dll not found or failed initialization\n"); + + goto fail; + } + + if (next_CreateDXGIFactory == NULL) { + next_CreateDXGIFactory = (CreateDXGIFactory_t) GetProcAddress( + dxgi, + "CreateDXGIFactory"); + } + if (next_CreateDXGIFactory1 == NULL) { + next_CreateDXGIFactory1 = (CreateDXGIFactory1_t) GetProcAddress( + dxgi, + "CreateDXGIFactory1"); + } + + if (next_CreateDXGIFactory == NULL) { + dprintf("DXGI: CreateDXGIFactory not found in loaded dxgi.dll\n"); + + goto fail; + } + if (next_CreateDXGIFactory1 == NULL) { + dprintf("DXGI: CreateDXGIFactory1 not found in loaded dxgi.dll\n"); + + goto fail; + } + } + + if (self != NULL) { + dll_hook_push(self, L"dxgi.dll"); + } + + return; + +fail: + if (dxgi != NULL) { + FreeLibrary(dxgi); + } +} + +HRESULT WINAPI CreateDXGIFactory(REFIID riid, void **factory) +{ + struct com_proxy *proxy; + IDXGIFactoryVtbl *vtbl; + IDXGIFactory *api; + HRESULT hr; + + dprintf("DXGI: CreateDXGIFactory hook hit\n"); + + api = NULL; + hr = next_CreateDXGIFactory(riid, factory); + + if (FAILED(hr)) { + dprintf("DXGI: CreateDXGIFactory returned %x\n", (int) hr); + + goto fail; + } + + if (memcmp(riid, &IID_IDXGIFactory, sizeof(*riid)) == 0) { + api = *factory; + hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl = proxy->vptr; + vtbl->CreateSwapChain = my_IDXGIFactory_CreateSwapChain; + + *factory = proxy; + } + + return hr; + +fail: + if (api != NULL) { + IDXGIFactory_Release(api); + } + + return hr; +} + +HRESULT WINAPI CreateDXGIFactory1(REFIID riid, void **factory) +{ + struct com_proxy *proxy; + IDXGIFactory1 *api; + IDXGIFactory1Vtbl *vtbl; + HRESULT hr; + + dprintf("DXGI: CreateDXGIFactory1 hook hit\n"); + + api = NULL; + hr = next_CreateDXGIFactory1(riid, factory); + + if (FAILED(hr)) { + dprintf("DXGI: CreateDXGIFactory1 returned %x\n", (int) hr); + + goto fail; + } + + if (memcmp(riid, &IID_IDXGIFactory1, sizeof(*riid)) == 0) { + api = *factory; + hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl = proxy->vptr; + vtbl->CreateSwapChain = my_IDXGIFactory1_CreateSwapChain; + + *factory = proxy; + } + + return hr; + +fail: + if (api != NULL) { + IDXGIFactory1_Release(api); + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( + IDXGIFactory *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain) +{ + struct com_proxy *proxy; + IDXGIFactory *real; + HWND hwnd; + LONG width; + LONG height; + + dprintf("DXGI: IDXGIFactory::CreateSwapChain hook hit\n"); + + proxy = com_proxy_downcast(self); + real = proxy->real; + + if (desc != NULL) { + desc->Windowed = gfx_config.windowed; + + hwnd = desc->OutputWindow; + width = desc->BufferDesc.Width; + height = desc->BufferDesc.Height; + } else { + hwnd = NULL; + width = 0; + height = 0; + } + + if (hwnd != NULL) { + /* + * Ensure window is maximized to avoid a Windows 10 issue where a + * fullscreen swap chain is not created because the window is minimized + * at the time of creation. + */ + ShowWindow(hwnd, SW_RESTORE); + + if (!gfx_config.framed && width > 0 && height > 0) { + dprintf("DXGI: Resizing window to %ldx%ld\n", width, height); + + SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); + SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); + + SetWindowPos( + hwnd, + HWND_TOP, + 0, + 0, + width, + height, + SWP_FRAMECHANGED | SWP_NOSENDCHANGING); + + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + } + } + + return IDXGIFactory_CreateSwapChain(real, device, desc, swapchain); +} + +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain( + IDXGIFactory1 *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain) +{ + dprintf("DXGI: IDXGIFactory1::CreateSwapChain hook forwarding to my_IDXGIFactory_CreateSwapChain\n"); + + return my_IDXGIFactory_CreateSwapChain( + (IDXGIFactory *) self, + device, + desc, + swapchain); +} diff --git a/hooklib/gfx/dxgi.h b/hooklib/gfx/dxgi.h new file mode 100644 index 0000000..114822e --- /dev/null +++ b/hooklib/gfx/dxgi.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "hooklib/gfx/gfx.h" + +void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/hooklib/gfx/gfx.c b/hooklib/gfx/gfx.c new file mode 100644 index 0000000..ff3b1bd --- /dev/null +++ b/hooklib/gfx/gfx.c @@ -0,0 +1,113 @@ +#include + +#include +#include +#include + +#include "hook/table.h" + +#include "hooklib/config.h" +#include "hooklib/gfx/gfx.h" + +#include "util/dprintf.h" + +typedef BOOL (WINAPI *ShowWindow_t)(HWND hWnd, int nCmdShow); + +static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow); + +static struct gfx_config gfx_config; +static ShowWindow_t next_ShowWindow; + +static const struct hook_symbol gfx_hooks[] = { + { + .name = "ShowWindow", + .patch = hook_ShowWindow, + .link = (void **) &next_ShowWindow, + }, +}; + +void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + memcpy(&gfx_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks)); +} + +static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow) +{ + dprintf("Gfx: ShowWindow hook hit\n"); + + if (!gfx_config.framed && nCmdShow == SW_RESTORE) { + nCmdShow = SW_SHOW; + } + + return next_ShowWindow(hWnd, nCmdShow); +} + +HRESULT gfx_frame_window(HWND hwnd) +{ + HRESULT hr; + DWORD error; + LONG style; + RECT rect; + BOOL ok; + + SetLastError(ERROR_SUCCESS); + style = GetWindowLongW(hwnd, GWL_STYLE); + error = GetLastError(); + + if (error != ERROR_SUCCESS) { + hr = HRESULT_FROM_WIN32(error); + dprintf("Gfx: GetWindowLongPtrW(%p, GWL_STYLE) failed: %x\n", + hwnd, + (int) hr); + + return hr; + } + + ok = GetClientRect(hwnd, &rect); + + if (!ok) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Gfx: GetClientRect(%p) failed: %x\n", hwnd, (int) hr); + + return hr; + } + + style |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; + ok = AdjustWindowRect(&rect, style, FALSE); + + if (!ok) { + /* come on... */ + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Gfx: AdjustWindowRect failed: %x\n", (int) hr); + + return hr; + } + + /* This... always seems to set an error, even though it works? idk */ + SetWindowLongW(hwnd, GWL_STYLE, style); + + ok = SetWindowPos( + hwnd, + HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_FRAMECHANGED | SWP_NOMOVE); + + if (!ok) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Gfx: SetWindowPos(%p) failed: %x\n", hwnd, (int) hr); + + return hr; + } + + return S_OK; +} diff --git a/hooklib/gfx.h b/hooklib/gfx/gfx.h similarity index 85% rename from hooklib/gfx.h rename to hooklib/gfx/gfx.h index 9182371..8273a8c 100644 --- a/hooklib/gfx.h +++ b/hooklib/gfx/gfx.h @@ -12,3 +12,5 @@ struct gfx_config { }; void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self); + +HRESULT gfx_frame_window(HWND hwnd); diff --git a/hooklib/meson.build b/hooklib/meson.build index e75056a..1a78069 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -17,8 +17,14 @@ hooklib_lib = static_library( 'dvd.h', 'fdshark.c', 'fdshark.h', - 'gfx.c', - 'gfx.h', + 'gfx/d3d9.c', + 'gfx/d3d9.h', + 'gfx/d3d11.c', + 'gfx/d3d11.h', + 'gfx/dxgi.c', + 'gfx/dxgi.h', + 'gfx/gfx.c', + 'gfx/gfx.h', 'path.c', 'path.h', 'reg.c', diff --git a/idzhook/config.c b/idzhook/config.c index a7d095d..bf95f1b 100644 --- a/idzhook/config.c +++ b/idzhook/config.c @@ -9,6 +9,7 @@ #include "hooklib/config.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/gfx.h" #include "idzhook/config.h" #include "idzhook/idz-dll.h" @@ -42,9 +43,10 @@ void idz_hook_config_load( platform_config_load(&cfg->platform, filename); amex_config_load(&cfg->amex, filename); aime_config_load(&cfg->aime, filename); + dvd_config_load(&cfg->dvd, filename); + gfx_config_load(&cfg->gfx, filename); idz_dll_config_load(&cfg->dll, filename); zinput_config_load(&cfg->zinput, filename); - dvd_config_load(&cfg->dvd, filename); } void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename) diff --git a/idzhook/config.h b/idzhook/config.h index 58e421e..4ebb548 100644 --- a/idzhook/config.h +++ b/idzhook/config.h @@ -8,6 +8,7 @@ #include "board/sg-reader.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/gfx.h" #include "idzhook/idz-dll.h" #include "idzhook/zinput.h" @@ -19,6 +20,7 @@ struct idz_hook_config { struct amex_config amex; struct aime_config aime; struct dvd_config dvd; + struct gfx_config gfx; struct idz_dll_config dll; struct zinput_config zinput; }; diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 8349800..d68df31 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -9,6 +9,9 @@ #include "hook/process.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/d3d11.h" +#include "hooklib/gfx/dxgi.h" +#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" @@ -38,6 +41,9 @@ static DWORD CALLBACK idz_pre_startup(void) /* Hook Win32 APIs */ serial_hook_init(); + gfx_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); + gfx_d3d11_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); + gfx_dxgi_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); zinput_hook_init(&idz_hook_cfg.zinput); dvd_hook_init(&idz_hook_cfg.dvd, idz_hook_mod); diff --git a/idzhook/idzhook.def b/idzhook/idzhook.def index 486400c..d8db3b0 100644 --- a/idzhook/idzhook.def +++ b/idzhook/idzhook.def @@ -1,6 +1,11 @@ LIBRARY idzhook EXPORTS + CreateDXGIFactory + CreateDXGIFactory1 + CreateDXGIFactory2 + D3D11CreateDevice + D3D11CreateDeviceAndSwapChain aime_io_get_api_version aime_io_init aime_io_led_set_color diff --git a/mu3hook/config.c b/mu3hook/config.c index aba8cf7..28f06b7 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -5,7 +5,7 @@ #include "hooklib/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "mu3hook/config.h" diff --git a/mu3hook/config.h b/mu3hook/config.h index 982a688..2e285e6 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -5,7 +5,7 @@ #include "board/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx.h" +#include "hooklib/gfx/gfx.h" #include "mu3hook/mu3-dll.h" diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 242c579..55f676b 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -9,6 +9,10 @@ #include "hook/process.h" #include "hooklib/dvd.h" +#include "hooklib/gfx/d3d9.h" +#include "hooklib/gfx/d3d11.h" +#include "hooklib/gfx/dxgi.h" +#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" @@ -39,6 +43,9 @@ static DWORD CALLBACK mu3_pre_startup(void) dvd_hook_init(&mu3_hook_cfg.dvd, mu3_hook_mod); gfx_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); + gfx_d3d9_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); + gfx_d3d11_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); + gfx_dxgi_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); serial_hook_init(); /* Initialize emulation hooks */ diff --git a/mu3hook/mu3hook.def b/mu3hook/mu3hook.def index e7367fb..d90abd5 100644 --- a/mu3hook/mu3hook.def +++ b/mu3hook/mu3hook.def @@ -1,6 +1,11 @@ LIBRARY mu3hook EXPORTS + CreateDXGIFactory + CreateDXGIFactory1 + CreateDXGIFactory2 + D3D11CreateDevice + D3D11CreateDeviceAndSwapChain Direct3DCreate9 aime_io_get_api_version aime_io_init From b072f2d82ab5ac814c71cac1b395d0e2f7d91698 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 25 Oct 2021 09:26:16 +0000 Subject: [PATCH 115/175] idzhook: Force framed window for ServerBox.exe in graphics hook --- idzhook/dllmain.c | 25 +++++++++++++++++++++++++ idzhook/meson.build | 1 + 2 files changed, 26 insertions(+) diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index d68df31..545261d 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -1,6 +1,9 @@ #include +#include +#include #include +#include #include "amex/amex.h" @@ -23,6 +26,7 @@ #include "platform/platform.h" #include "util/dprintf.h" +#include "util/lib.h" static HMODULE idz_hook_mod; static process_entry_t idz_startup; @@ -30,6 +34,8 @@ static struct idz_hook_config idz_hook_cfg; static DWORD CALLBACK idz_pre_startup(void) { + wchar_t *module_path; + wchar_t *file_name; HRESULT hr; dprintf("--- Begin idz_pre_startup ---\n"); @@ -38,6 +44,25 @@ static DWORD CALLBACK idz_pre_startup(void) idz_hook_config_load(&idz_hook_cfg, L".\\segatools.ini"); + module_path = module_file_name(NULL); + + if (module_path != NULL) { + file_name = PathFindFileNameW(module_path); + + _wcslwr(file_name); + + if (wcsstr(file_name, L"serverbox") != NULL) { + dprintf("Executable filename contains 'ServerBox', disabling full-screen mode\n"); + + idz_hook_cfg.gfx.windowed = true; + idz_hook_cfg.gfx.framed = true; + } + + free(module_path); + + module_path = NULL; + } + /* Hook Win32 APIs */ serial_hook_init(); diff --git a/idzhook/meson.build b/idzhook/meson.build index b89e4e3..17b6484 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -8,6 +8,7 @@ shared_library( dependencies : [ capnhook.get_variable('hook_dep'), capnhook.get_variable('hooklib_dep'), + shlwapi_lib, xinput_lib, ], link_with : [ From 636ed0f41bf0115e04e7ebbfbbf0140c0c1edfed Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 25 Oct 2021 21:52:35 +0000 Subject: [PATCH 116/175] Draft: hooklib/gfx/dxgi.c: Add CreateDXGIFactory2 hook --- hooklib/gfx/dxgi.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/hooklib/gfx/dxgi.c b/hooklib/gfx/dxgi.c index bbcc561..5decbbc 100644 --- a/hooklib/gfx/dxgi.c +++ b/hooklib/gfx/dxgi.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -15,6 +16,10 @@ typedef HRESULT (WINAPI *CreateDXGIFactory_t)(REFIID riid, void **factory); typedef HRESULT (WINAPI *CreateDXGIFactory1_t)(REFIID riid, void **factory); +typedef HRESULT (WINAPI *CreateDXGIFactory2_t)( + UINT flags, + REFIID riid, + void **factory); static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( IDXGIFactory *self, @@ -26,10 +31,16 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain( IUnknown *device, DXGI_SWAP_CHAIN_DESC *desc, IDXGISwapChain **swapchain); +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory2_CreateSwapChain( + IDXGIFactory2 *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain); static struct gfx_config gfx_config; static CreateDXGIFactory_t next_CreateDXGIFactory; static CreateDXGIFactory1_t next_CreateDXGIFactory1; +static CreateDXGIFactory2_t next_CreateDXGIFactory2; static const struct hook_symbol dxgi_hooks[] = { { @@ -40,6 +51,10 @@ static const struct hook_symbol dxgi_hooks[] = { .name = "CreateDXGIFactory1", .patch = CreateDXGIFactory1, .link = (void **) &next_CreateDXGIFactory1, + }, { + .name = "CreateDXGIFactory2", + .patch = CreateDXGIFactory2, + .link = (void **) &next_CreateDXGIFactory2, }, }; @@ -75,6 +90,11 @@ void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self) dxgi, "CreateDXGIFactory1"); } + if (next_CreateDXGIFactory2 == NULL) { + next_CreateDXGIFactory2 = (CreateDXGIFactory2_t) GetProcAddress( + dxgi, + "CreateDXGIFactory2"); + } if (next_CreateDXGIFactory == NULL) { dprintf("DXGI: CreateDXGIFactory not found in loaded dxgi.dll\n"); @@ -86,6 +106,9 @@ void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self) goto fail; } + + /* `CreateDXGIFactory2` was introduced in Windows 8.1 and the original + * Nu runs Windows 8, so do not require it to exist */ } if (self != NULL) { @@ -188,6 +211,56 @@ fail: return hr; } +HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory) +{ + struct com_proxy *proxy; + IDXGIFactory2 *api; + IDXGIFactory2Vtbl *vtbl; + HRESULT hr; + + dprintf("DXGI: CreateDXGIFactory2 hook hit\n"); + + if (next_CreateDXGIFactory2 == NULL) { + dprintf("DXGI: CreateDXGIFactory2 not available, forwarding to CreateDXGIFactory1\n"); + + return CreateDXGIFactory1(riid, factory); + } + + api = NULL; + hr = next_CreateDXGIFactory2(flags, riid, factory); + + if (FAILED(hr)) { + dprintf("DXGI: CreateDXGIFactory2 returned %x\n", (int) hr); + + goto fail; + } + + if (memcmp(riid, &IID_IDXGIFactory2, sizeof(*riid)) == 0) { + api = *factory; + hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl = proxy->vptr; + vtbl->CreateSwapChain = my_IDXGIFactory2_CreateSwapChain; + + *factory = proxy; + } + + return hr; + +fail: + if (api != NULL) { + IDXGIFactory2_Release(api); + } + + return hr; +} + static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( IDXGIFactory *self, IUnknown *device, @@ -261,3 +334,18 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory1_CreateSwapChain( desc, swapchain); } + +static HRESULT STDMETHODCALLTYPE my_IDXGIFactory2_CreateSwapChain( + IDXGIFactory2 *self, + IUnknown *device, + DXGI_SWAP_CHAIN_DESC *desc, + IDXGISwapChain **swapchain) +{ + dprintf("DXGI: IDXGIFactory2::CreateSwapChain hook forwarding to my_IDXGIFactory_CreateSwapChain\n"); + + return my_IDXGIFactory_CreateSwapChain( + (IDXGIFactory *) self, + device, + desc, + swapchain); +} From a24cd0a1a56e91643ab3bb90612b49a8aaac1b08 Mon Sep 17 00:00:00 2001 From: Felix Date: Mon, 25 Oct 2021 21:57:38 +0000 Subject: [PATCH 117/175] Draft: hooklib/gfx/dxgi.c: Delegate IDXGIFactory hook selection to helper method --- hooklib/gfx/dxgi.c | 140 +++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/hooklib/gfx/dxgi.c b/hooklib/gfx/dxgi.c index 5decbbc..a7b9c5c 100644 --- a/hooklib/gfx/dxgi.c +++ b/hooklib/gfx/dxgi.c @@ -21,6 +21,8 @@ typedef HRESULT (WINAPI *CreateDXGIFactory2_t)( REFIID riid, void **factory); +static HRESULT hook_factory(REFIID riid, void **factory); + static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( IDXGIFactory *self, IUnknown *device, @@ -125,43 +127,22 @@ fail: HRESULT WINAPI CreateDXGIFactory(REFIID riid, void **factory) { - struct com_proxy *proxy; - IDXGIFactoryVtbl *vtbl; - IDXGIFactory *api; HRESULT hr; dprintf("DXGI: CreateDXGIFactory hook hit\n"); - api = NULL; hr = next_CreateDXGIFactory(riid, factory); if (FAILED(hr)) { dprintf("DXGI: CreateDXGIFactory returned %x\n", (int) hr); - goto fail; + return hr; } - if (memcmp(riid, &IID_IDXGIFactory, sizeof(*riid)) == 0) { - api = *factory; - hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + hr = hook_factory(riid, factory); - if (FAILED(hr)) { - dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); - - goto fail; - } - - vtbl = proxy->vptr; - vtbl->CreateSwapChain = my_IDXGIFactory_CreateSwapChain; - - *factory = proxy; - } - - return hr; - -fail: - if (api != NULL) { - IDXGIFactory_Release(api); + if (FAILED(hr)) { + return hr; } return hr; @@ -169,43 +150,22 @@ fail: HRESULT WINAPI CreateDXGIFactory1(REFIID riid, void **factory) { - struct com_proxy *proxy; - IDXGIFactory1 *api; - IDXGIFactory1Vtbl *vtbl; HRESULT hr; dprintf("DXGI: CreateDXGIFactory1 hook hit\n"); - api = NULL; hr = next_CreateDXGIFactory1(riid, factory); if (FAILED(hr)) { dprintf("DXGI: CreateDXGIFactory1 returned %x\n", (int) hr); - goto fail; + return hr; } - if (memcmp(riid, &IID_IDXGIFactory1, sizeof(*riid)) == 0) { - api = *factory; - hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + hr = hook_factory(riid, factory); - if (FAILED(hr)) { - dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); - - goto fail; - } - - vtbl = proxy->vptr; - vtbl->CreateSwapChain = my_IDXGIFactory1_CreateSwapChain; - - *factory = proxy; - } - - return hr; - -fail: - if (api != NULL) { - IDXGIFactory1_Release(api); + if (FAILED(hr)) { + return hr; } return hr; @@ -213,9 +173,6 @@ fail: HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory) { - struct com_proxy *proxy; - IDXGIFactory2 *api; - IDXGIFactory2Vtbl *vtbl; HRESULT hr; dprintf("DXGI: CreateDXGIFactory2 hook hit\n"); @@ -226,18 +183,41 @@ HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory) return CreateDXGIFactory1(riid, factory); } - api = NULL; hr = next_CreateDXGIFactory2(flags, riid, factory); if (FAILED(hr)) { dprintf("DXGI: CreateDXGIFactory2 returned %x\n", (int) hr); - goto fail; + return hr; } - if (memcmp(riid, &IID_IDXGIFactory2, sizeof(*riid)) == 0) { - api = *factory; - hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + hr = hook_factory(riid, factory); + + if (FAILED(hr)) { + return hr; + } + + return hr; +} + +static HRESULT hook_factory(REFIID riid, void **factory) +{ + struct com_proxy *proxy; + IDXGIFactory *api_0; + IDXGIFactory1 *api_1; + IDXGIFactory2 *api_2; + IDXGIFactoryVtbl *vtbl_0; + IDXGIFactory1Vtbl *vtbl_1; + IDXGIFactory2Vtbl *vtbl_2; + HRESULT hr; + + api_0 = NULL; + api_1 = NULL; + api_2 = NULL; + + if (memcmp(riid, &IID_IDXGIFactory, sizeof(*riid)) == 0) { + api_0 = *factory; + hr = com_proxy_wrap(&proxy, api_0, sizeof(*api_0->lpVtbl)); if (FAILED(hr)) { dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); @@ -245,17 +225,51 @@ HRESULT WINAPI CreateDXGIFactory2(UINT flags, REFIID riid, void **factory) goto fail; } - vtbl = proxy->vptr; - vtbl->CreateSwapChain = my_IDXGIFactory2_CreateSwapChain; + vtbl_0 = proxy->vptr; + vtbl_0->CreateSwapChain = my_IDXGIFactory_CreateSwapChain; + + *factory = proxy; + } else if (memcmp(riid, &IID_IDXGIFactory1, sizeof(*riid)) == 0) { + api_1 = *factory; + hr = com_proxy_wrap(&proxy, api_1, sizeof(*api_1->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl_1 = proxy->vptr; + vtbl_1->CreateSwapChain = my_IDXGIFactory1_CreateSwapChain; + + *factory = proxy; + } else if (memcmp(riid, &IID_IDXGIFactory2, sizeof(*riid)) == 0) { + api_2 = *factory; + hr = com_proxy_wrap(&proxy, api_2, sizeof(*api_2->lpVtbl)); + + if (FAILED(hr)) { + dprintf("DXGI: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl_2 = proxy->vptr; + vtbl_2->CreateSwapChain = my_IDXGIFactory2_CreateSwapChain; *factory = proxy; } - return hr; + return S_OK; fail: - if (api != NULL) { - IDXGIFactory2_Release(api); + if (api_0 != NULL) { + IDXGIFactory_Release(api_0); + } + if (api_1 != NULL) { + IDXGIFactory1_Release(api_1); + } + if (api_2 != NULL) { + IDXGIFactory2_Release(api_2); } return hr; From ea94dd808516f83b5f2b0c473db842ad96664908 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 3 Nov 2021 17:14:24 +0000 Subject: [PATCH 118/175] wip: move gfx to gfxhook, some borderless window stuff --- chunihook/config.c | 3 +- chunihook/config.h | 2 +- chunihook/dllmain.c | 5 +- chunihook/meson.build | 1 + divahook/dllmain.c | 1 - gfxhook/config.c | 18 +++++++ gfxhook/config.h | 7 +++ {hooklib/gfx => gfxhook}/d3d11.c | 31 ++---------- {hooklib/gfx => gfxhook}/d3d11.h | 2 +- {hooklib/gfx => gfxhook}/d3d9.c | 10 ++-- {hooklib/gfx => gfxhook}/d3d9.h | 2 +- {hooklib/gfx => gfxhook}/dxgi.c | 4 +- {hooklib/gfx => gfxhook}/dxgi.h | 2 +- gfxhook/gfx.c | 50 +++++++++++++++++++ {hooklib/gfx => gfxhook}/gfx.h | 2 - gfxhook/meson.build | 27 +++++++++++ hooklib/gfx/gfx.c => gfxhook/util.c | 75 +++++++++++++++-------------- gfxhook/util.h | 7 +++ hooklib/config.c | 12 ----- hooklib/config.h | 3 -- hooklib/meson.build | 8 --- idzhook/config.c | 3 +- idzhook/config.h | 3 +- idzhook/dllmain.c | 7 +-- idzhook/meson.build | 1 + meson.build | 2 + mu3hook/config.c | 3 +- mu3hook/config.h | 3 +- mu3hook/dllmain.c | 9 ++-- mu3hook/meson.build | 1 + 30 files changed, 193 insertions(+), 111 deletions(-) create mode 100644 gfxhook/config.c create mode 100644 gfxhook/config.h rename {hooklib/gfx => gfxhook}/d3d11.c (85%) rename {hooklib/gfx => gfxhook}/d3d11.h (78%) rename {hooklib/gfx => gfxhook}/d3d9.c (94%) rename {hooklib/gfx => gfxhook}/d3d9.h (78%) rename {hooklib/gfx => gfxhook}/dxgi.c (99%) rename {hooklib/gfx => gfxhook}/dxgi.h (78%) create mode 100644 gfxhook/gfx.c rename {hooklib/gfx => gfxhook}/gfx.h (85%) create mode 100644 gfxhook/meson.build rename hooklib/gfx/gfx.c => gfxhook/util.c (57%) create mode 100644 gfxhook/util.h diff --git a/chunihook/config.c b/chunihook/config.c index 9216d6a..0f049b2 100644 --- a/chunihook/config.c +++ b/chunihook/config.c @@ -12,8 +12,9 @@ #include "chunihook/config.h" +#include "gfxhook/config.h" + #include "hooklib/config.h" -#include "hooklib/gfx/gfx.h" #include "platform/config.h" #include "platform/platform.h" diff --git a/chunihook/config.h b/chunihook/config.h index ded7a82..7740822 100644 --- a/chunihook/config.h +++ b/chunihook/config.h @@ -10,7 +10,7 @@ #include "chunihook/chuni-dll.h" #include "chunihook/slider.h" -#include "hooklib/gfx/gfx.h" +#include "gfxhook/gfx.h" #include "platform/platform.h" diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index b209c3f..e0db84c 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -12,10 +12,11 @@ #include "chuniio/chuniio.h" +#include "gfxhook/d3d9.h" +#include "gfxhook/gfx.h" + #include "hook/process.h" -#include "hooklib/gfx/d3d9.h" -#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" diff --git a/chunihook/meson.build b/chunihook/meson.build index e2dfa08..3f4a35d 100644 --- a/chunihook/meson.build +++ b/chunihook/meson.build @@ -14,6 +14,7 @@ shared_library( amex_lib, board_lib, chuniio_lib, + gfxhook_lib, hooklib_lib, jvs_lib, platform_lib, diff --git a/divahook/dllmain.c b/divahook/dllmain.c index d06949c..fb3c678 100644 --- a/divahook/dllmain.c +++ b/divahook/dllmain.c @@ -13,7 +13,6 @@ #include "hook/process.h" -#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" diff --git a/gfxhook/config.c b/gfxhook/config.c new file mode 100644 index 0000000..230ee0d --- /dev/null +++ b/gfxhook/config.c @@ -0,0 +1,18 @@ +#include + +#include +#include +#include + +#include "gfxhook/config.h" + +void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"gfx", L"enable", 1, filename); + cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename); + cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); + cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); +} diff --git a/gfxhook/config.h b/gfxhook/config.h new file mode 100644 index 0000000..a6ced69 --- /dev/null +++ b/gfxhook/config.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "gfxhook/gfx.h" + +void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); diff --git a/hooklib/gfx/d3d11.c b/gfxhook/d3d11.c similarity index 85% rename from hooklib/gfx/d3d11.c rename to gfxhook/d3d11.c index 37f7fb7..4313431 100644 --- a/hooklib/gfx/d3d11.c +++ b/gfxhook/d3d11.c @@ -6,12 +6,13 @@ #include #include +#include "gfxhook/gfx.h" +#include "gfxhook/util.h" + #include "hook/com-proxy.h" #include "hook/table.h" -#include "hooklib/config.h" #include "hooklib/dll.h" -#include "hooklib/gfx/gfx.h" #include "util/dprintf.h" @@ -176,30 +177,8 @@ HRESULT WINAPI D3D11CreateDeviceAndSwapChain( } if (hwnd != NULL) { - /* - * Ensure window is maximized to avoid a Windows 10 issue where a - * fullscreen swap chain is not created because the window is minimized - * at the time of creation. - */ - ShowWindow(hwnd, SW_RESTORE); - - if (!gfx_config.framed && width > 0 && height > 0) { - dprintf("DXGI: Resizing window to %ldx%ld\n", width, height); - - SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); - SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); - - SetWindowPos( - hwnd, - HWND_TOP, - 0, - 0, - width, - height, - SWP_FRAMECHANGED | SWP_NOSENDCHANGING); - - ShowWindow(hwnd, SW_SHOWMAXIMIZED); - } + gfx_util_ensure_win_visible(hwnd); + gfx_util_borderless_fullscreen_windowed(hwnd, width, height); } return next_D3D11CreateDeviceAndSwapChain( diff --git a/hooklib/gfx/d3d11.h b/gfxhook/d3d11.h similarity index 78% rename from hooklib/gfx/d3d11.h rename to gfxhook/d3d11.h index 7126d2e..c7cca9f 100644 --- a/hooklib/gfx/d3d11.h +++ b/gfxhook/d3d11.h @@ -2,6 +2,6 @@ #include -#include "hooklib/gfx/gfx.h" +#include "gfxhook/gfx.h" void gfx_d3d11_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/hooklib/gfx/d3d9.c b/gfxhook/d3d9.c similarity index 94% rename from hooklib/gfx/d3d9.c rename to gfxhook/d3d9.c index d3b810a..4c39178 100644 --- a/hooklib/gfx/d3d9.c +++ b/gfxhook/d3d9.c @@ -8,9 +8,11 @@ #include "hook/com-proxy.h" #include "hook/table.h" -#include "hooklib/config.h" #include "hooklib/dll.h" -#include "hooklib/gfx/gfx.h" + +#include "gfxhook/config.h" +#include "gfxhook/gfx.h" +#include "gfxhook/util.h" #include "util/dprintf.h" @@ -144,10 +146,10 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( } if (gfx_config.framed) { - gfx_frame_window(hwnd); + gfx_util_frame_window(hwnd); } - dprintf("Gfx: IDirect3D9:: Using Display No %x\n", gfx_config.monitor); + dprintf("Gfx: Using adapter %d\n", gfx_config.monitor); return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev); } diff --git a/hooklib/gfx/d3d9.h b/gfxhook/d3d9.h similarity index 78% rename from hooklib/gfx/d3d9.h rename to gfxhook/d3d9.h index fb5607a..90acdf4 100644 --- a/hooklib/gfx/d3d9.h +++ b/gfxhook/d3d9.h @@ -2,6 +2,6 @@ #include -#include "hooklib/gfx/gfx.h" +#include "gfxhook/gfx.h" void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/hooklib/gfx/dxgi.c b/gfxhook/dxgi.c similarity index 99% rename from hooklib/gfx/dxgi.c rename to gfxhook/dxgi.c index a7b9c5c..4de0f74 100644 --- a/hooklib/gfx/dxgi.c +++ b/gfxhook/dxgi.c @@ -5,12 +5,12 @@ #include #include +#include "gfxhook/gfx.h" + #include "hook/com-proxy.h" #include "hook/table.h" -#include "hooklib/config.h" #include "hooklib/dll.h" -#include "hooklib/gfx/gfx.h" #include "util/dprintf.h" diff --git a/hooklib/gfx/dxgi.h b/gfxhook/dxgi.h similarity index 78% rename from hooklib/gfx/dxgi.h rename to gfxhook/dxgi.h index 114822e..1834300 100644 --- a/hooklib/gfx/dxgi.h +++ b/gfxhook/dxgi.h @@ -2,6 +2,6 @@ #include -#include "hooklib/gfx/gfx.h" +#include "gfxhook/gfx.h" void gfx_dxgi_hook_init(const struct gfx_config *cfg, HINSTANCE self); diff --git a/gfxhook/gfx.c b/gfxhook/gfx.c new file mode 100644 index 0000000..34533af --- /dev/null +++ b/gfxhook/gfx.c @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +#include "gfxhook/config.h" +#include "gfxhook/gfx.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + +typedef BOOL (WINAPI *ShowWindow_t)(HWND hWnd, int nCmdShow); + +static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow); + +static struct gfx_config gfx_config; +static ShowWindow_t next_ShowWindow; + +static const struct hook_symbol gfx_hooks[] = { + { + .name = "ShowWindow", + .patch = hook_ShowWindow, + .link = (void **) &next_ShowWindow, + }, +}; + +void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + memcpy(&gfx_config, cfg, sizeof(*cfg)); + hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks)); +} + +static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow) +{ + dprintf("Gfx: ShowWindow hook hit\n"); + + if (!gfx_config.framed && nCmdShow == SW_RESTORE) { + nCmdShow = SW_SHOW; + } + + return next_ShowWindow(hWnd, nCmdShow); +} diff --git a/hooklib/gfx/gfx.h b/gfxhook/gfx.h similarity index 85% rename from hooklib/gfx/gfx.h rename to gfxhook/gfx.h index 8273a8c..9182371 100644 --- a/hooklib/gfx/gfx.h +++ b/gfxhook/gfx.h @@ -12,5 +12,3 @@ struct gfx_config { }; void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self); - -HRESULT gfx_frame_window(HWND hwnd); diff --git a/gfxhook/meson.build b/gfxhook/meson.build new file mode 100644 index 0000000..f37accb --- /dev/null +++ b/gfxhook/meson.build @@ -0,0 +1,27 @@ +gfxhook_lib = static_library( + 'gfxhook', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + ], + link_with : [ + hooklib_lib, + util_lib, + ], + sources : [ + 'config.c', + 'config.h', + 'd3d9.c', + 'd3d9.h', + 'd3d11.c', + 'd3d11.h', + 'dxgi.c', + 'dxgi.h', + 'gfx.c', + 'gfx.h', + 'util.c', + 'util.h', + ], +) diff --git a/hooklib/gfx/gfx.c b/gfxhook/util.c similarity index 57% rename from hooklib/gfx/gfx.c rename to gfxhook/util.c index ff3b1bd..14268f3 100644 --- a/hooklib/gfx/gfx.c +++ b/gfxhook/util.c @@ -1,55 +1,60 @@ #include -#include #include -#include -#include "hook/table.h" - -#include "hooklib/config.h" -#include "hooklib/gfx/gfx.h" +#include "gfxhook/util.h" #include "util/dprintf.h" -typedef BOOL (WINAPI *ShowWindow_t)(HWND hWnd, int nCmdShow); - -static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow); - -static struct gfx_config gfx_config; -static ShowWindow_t next_ShowWindow; - -static const struct hook_symbol gfx_hooks[] = { - { - .name = "ShowWindow", - .patch = hook_ShowWindow, - .link = (void **) &next_ShowWindow, - }, -}; - -void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) +void gfx_util_ensure_win_visible(HWND hwnd) { - assert(cfg != NULL); + /* + * Ensure window is maximized to avoid a Windows 10 issue where a + * fullscreen swap chain is not created because the window is minimized + * at the time of creation. + */ + ShowWindow(hwnd, SW_RESTORE); +} + +void gfx_util_borderless_fullscreen_windowed(HWND hwnd, LONG width, LONG height) +{ + BOOL ok; + HRESULT hr; + + dprintf("Gfx: Resizing window to %ldx%ld\n", width, height); + + SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); + SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); + + ok = SetWindowPos( + hwnd, + HWND_TOP, + 0, + 0, + width, + height, + SWP_FRAMECHANGED | SWP_NOSENDCHANGING); + + if (!ok) { + /* come on... */ + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Gfx: SetWindowPos failed: %x\n", (int) hr); - if (!cfg->enable) { return; } - memcpy(&gfx_config, cfg, sizeof(*cfg)); - hook_table_apply(NULL, "user32.dll", gfx_hooks, _countof(gfx_hooks)); -} + ok = ShowWindow(hwnd, SW_SHOWMAXIMIZED); -static BOOL WINAPI hook_ShowWindow(HWND hWnd, int nCmdShow) -{ - dprintf("Gfx: ShowWindow hook hit\n"); + if (!ok) { + /* come on... */ + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Gfx: ShowWindow failed: %x\n", (int) hr); - if (!gfx_config.framed && nCmdShow == SW_RESTORE) { - nCmdShow = SW_SHOW; + return; } - - return next_ShowWindow(hWnd, nCmdShow); } -HRESULT gfx_frame_window(HWND hwnd) +HRESULT gfx_util_frame_window(HWND hwnd) { HRESULT hr; DWORD error; diff --git a/gfxhook/util.h b/gfxhook/util.h new file mode 100644 index 0000000..b300bba --- /dev/null +++ b/gfxhook/util.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void gfx_util_ensure_win_visible(HWND hwnd); +void gfx_util_borderless_fullscreen_windowed(HWND hwnd, LONG width, LONG height); +HRESULT gfx_util_frame_window(HWND hwnd); diff --git a/hooklib/config.c b/hooklib/config.c index 5f5c080..5fc9383 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -6,7 +6,6 @@ #include "hooklib/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) { @@ -15,14 +14,3 @@ void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); } - -void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename) -{ - assert(cfg != NULL); - assert(filename != NULL); - - cfg->enable = GetPrivateProfileIntW(L"gfx", L"enable", 1, filename); - cfg->windowed = GetPrivateProfileIntW(L"gfx", L"windowed", 0, filename); - cfg->framed = GetPrivateProfileIntW(L"gfx", L"framed", 1, filename); - cfg->monitor = GetPrivateProfileIntW(L"gfx", L"monitor", 0, filename); -} diff --git a/hooklib/config.h b/hooklib/config.h index 0c19443..5da045d 100644 --- a/hooklib/config.h +++ b/hooklib/config.h @@ -1,10 +1,7 @@ #pragma once -#include #include #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); -void gfx_config_load(struct gfx_config *cfg, const wchar_t *filename); diff --git a/hooklib/meson.build b/hooklib/meson.build index 1a78069..ce1c96e 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -17,14 +17,6 @@ hooklib_lib = static_library( 'dvd.h', 'fdshark.c', 'fdshark.h', - 'gfx/d3d9.c', - 'gfx/d3d9.h', - 'gfx/d3d11.c', - 'gfx/d3d11.h', - 'gfx/dxgi.c', - 'gfx/dxgi.h', - 'gfx/gfx.c', - 'gfx/gfx.h', 'path.c', 'path.h', 'reg.c', diff --git a/idzhook/config.c b/idzhook/config.c index bf95f1b..db83b9f 100644 --- a/idzhook/config.c +++ b/idzhook/config.c @@ -7,9 +7,10 @@ #include "board/config.h" #include "board/sg-reader.h" +#include "gfxhook/config.h" + #include "hooklib/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" #include "idzhook/config.h" #include "idzhook/idz-dll.h" diff --git a/idzhook/config.h b/idzhook/config.h index 4ebb548..0684949 100644 --- a/idzhook/config.h +++ b/idzhook/config.h @@ -7,8 +7,9 @@ #include "board/sg-reader.h" +#include "gfxhook/gfx.h" + #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" #include "idzhook/idz-dll.h" #include "idzhook/zinput.h" diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 545261d..80b8c66 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -9,12 +9,13 @@ #include "board/sg-reader.h" +#include "gfxhook/d3d11.h" +#include "gfxhook/dxgi.h" +#include "gfxhook/gfx.h" + #include "hook/process.h" #include "hooklib/dvd.h" -#include "hooklib/gfx/d3d11.h" -#include "hooklib/gfx/dxgi.h" -#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" diff --git a/idzhook/meson.build b/idzhook/meson.build index 17b6484..ab90815 100644 --- a/idzhook/meson.build +++ b/idzhook/meson.build @@ -15,6 +15,7 @@ shared_library( aimeio_lib, amex_lib, board_lib, + gfxhook_lib, hooklib_lib, idzio_lib, jvs_lib, diff --git a/meson.build b/meson.build index 50660ad..c15b7ff 100644 --- a/meson.build +++ b/meson.build @@ -43,6 +43,8 @@ subdir('jvs') subdir('platform') subdir('util') +subdir('gfxhook') + subdir('aimeio') subdir('chuniio') subdir('divaio') diff --git a/mu3hook/config.c b/mu3hook/config.c index 28f06b7..6e3991d 100644 --- a/mu3hook/config.c +++ b/mu3hook/config.c @@ -3,9 +3,10 @@ #include "board/config.h" +#include "gfxhook/config.h" + #include "hooklib/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" #include "mu3hook/config.h" diff --git a/mu3hook/config.h b/mu3hook/config.h index 2e285e6..58af239 100644 --- a/mu3hook/config.h +++ b/mu3hook/config.h @@ -4,8 +4,9 @@ #include "board/config.h" +#include "gfxhook/gfx.h" + #include "hooklib/dvd.h" -#include "hooklib/gfx/gfx.h" #include "mu3hook/mu3-dll.h" diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index 55f676b..fc4b1ab 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -6,13 +6,14 @@ #include "board/sg-reader.h" #include "board/vfd.h" +#include "gfxhook/d3d9.h" +#include "gfxhook/d3d11.h" +#include "gfxhook/dxgi.h" +#include "gfxhook/gfx.h" + #include "hook/process.h" #include "hooklib/dvd.h" -#include "hooklib/gfx/d3d9.h" -#include "hooklib/gfx/d3d11.h" -#include "hooklib/gfx/dxgi.h" -#include "hooklib/gfx/gfx.h" #include "hooklib/serial.h" #include "hooklib/spike.h" diff --git a/mu3hook/meson.build b/mu3hook/meson.build index 7ce2398..27ba7f7 100644 --- a/mu3hook/meson.build +++ b/mu3hook/meson.build @@ -13,6 +13,7 @@ shared_library( link_with : [ aimeio_lib, board_lib, + gfxhook_lib, hooklib_lib, mu3io_lib, platform_lib, From 0b5cc281e7c2faf86b4fe3f7bd50b035da62e023 Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Fri, 17 Dec 2021 06:38:46 +0100 Subject: [PATCH 119/175] iccard/felica.c: replace pmm with new, more compatible one --- iccard/felica.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/iccard/felica.c b/iccard/felica.c index ba71232..550547f 100644 --- a/iccard/felica.c +++ b/iccard/felica.c @@ -31,9 +31,12 @@ uint64_t felica_get_generic_PMm(void) /* A FeliCa PMm contains low-level protocol timing information for communicating with a particular IC card. The exact values are not particularly important for our purposes, so we'll just return a hard- - coded PMm. This was taken from a SuiCa train pass I bought in Japan. */ + coded PMm. This current value has been taken from an iPhone, emulating + a suica pass via apple wallet, which seems to be one of the few + universally accepted felica types for these games. Certain older + suica passes etc do not seem to be supported anymore. */ - return 0x053143454682B7FFULL; + return 0x01168B868FBECBFFULL; } HRESULT felica_transact( From 68e71c845be4ce268e6654349834622b5c31684b Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 21 Dec 2021 04:56:59 +0000 Subject: [PATCH 120/175] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e6dcd78..a9c53d7 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo * [Chunithm Crystal (Plus)](doc/chunihook.md) * Initial D * [Initial D Arcade Stage Zero](doc/idzhook.md) +* Wacca + * Wacca Lilly R (WIP) ## End-users From 6b2a4e5c656a94e635ce23539e1fb9b2a8195195 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 21 Dec 2021 00:02:17 -0500 Subject: [PATCH 121/175] Added wacca stub code --- Dockerfile | 2 + Package.mk | 16 +++++ board/io4.c | 8 ++- dist/mercury/segatools.ini | 42 ++++++++++++ dist/mercury/start.bat | 10 +++ mercuryhook/config.c | 43 ++++++++++++ mercuryhook/config.h | 29 ++++++++ mercuryhook/dllmain.c | 109 +++++++++++++++++++++++++++++ mercuryhook/io4.c | 64 +++++++++++++++++ mercuryhook/io4.h | 7 ++ mercuryhook/mercury-dll.c | 109 +++++++++++++++++++++++++++++ mercuryhook/mercury-dll.h | 21 ++++++ mercuryhook/mercuryhook.def | 19 ++++++ mercuryhook/meson.build | 29 ++++++++ mercuryio/config.c | 25 +++++++ mercuryio/config.h | 17 +++++ mercuryio/mercuryio.c | 133 ++++++++++++++++++++++++++++++++++++ mercuryio/mercuryio.h | 67 ++++++++++++++++++ mercuryio/meson.build | 13 ++++ meson.build | 2 + 20 files changed, 764 insertions(+), 1 deletion(-) create mode 100644 dist/mercury/segatools.ini create mode 100644 dist/mercury/start.bat create mode 100644 mercuryhook/config.c create mode 100644 mercuryhook/config.h create mode 100644 mercuryhook/dllmain.c create mode 100644 mercuryhook/io4.c create mode 100644 mercuryhook/io4.h create mode 100644 mercuryhook/mercury-dll.c create mode 100644 mercuryhook/mercury-dll.h create mode 100644 mercuryhook/mercuryhook.def create mode 100644 mercuryhook/meson.build create mode 100644 mercuryio/config.c create mode 100644 mercuryio/config.h create mode 100644 mercuryio/mercuryio.c create mode 100644 mercuryio/mercuryio.h create mode 100644 mercuryio/meson.build diff --git a/Dockerfile b/Dockerfile index ff2f85d..6aa360d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,8 @@ COPY hooklib hooklib COPY iccard iccard COPY idzhook idzhook COPY idzio idzio +COPY mercuryhook mercuryhook +COPY mercuryio mercuryio COPY jvs jvs COPY minihook minihook COPY mu3hook mu3hook diff --git a/Package.mk b/Package.mk index c18c033..fcac6f9 100644 --- a/Package.mk +++ b/Package.mk @@ -28,6 +28,21 @@ $(BUILD_DIR_ZIP)/idz.zip: $(V)strip $(BUILD_DIR_ZIP)/idz/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/idz ; zip -r ../idz.zip * +$(BUILD_DIR_ZIP)/mercury.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury + $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \ + $(DIST_DIR)/mercury/segatools.ini \ + $(DIST_DIR)/mercury/start.bat \ + $(BUILD_DIR_ZIP)/mercury + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/mercury/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip * + $(BUILD_DIR_ZIP)/doc.zip: \ $(DOC_DIR)/config \ $(DOC_DIR)/chunihook.md \ @@ -40,6 +55,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/chuni.zip \ $(BUILD_DIR_ZIP)/doc.zip \ $(BUILD_DIR_ZIP)/idz.zip \ + $(BUILD_DIR_ZIP)/mercury.zip \ CHANGELOG.md \ README.md \ diff --git a/board/io4.c b/board/io4.c index efad62f..e7fe9e3 100644 --- a/board/io4.c +++ b/board/io4.c @@ -28,6 +28,7 @@ enum { IO4_CMD_CLEAR_BOARD_STATUS = 0x03, IO4_CMD_SET_GENERAL_OUTPUT = 0x04, IO4_CMD_SET_PWM_OUTPUT = 0x05, + IO4_CMD_UNIMPLEMENTED = 0x41, IO4_CMD_UPDATE_FIRMWARE = 0x85, }; @@ -234,7 +235,12 @@ static HRESULT io4_handle_write(struct irp *irp) case IO4_CMD_UPDATE_FIRMWARE: dprintf("USB I/O: Update firmware..?\n"); - return E_FAIL; + return E_FAIL; + + case IO4_CMD_UNIMPLEMENTED: + //dprintf("USB I/O: Unimplemented cmd 41\n"); + + return S_OK; default: dprintf("USB I/O: Unknown command %02x\n", out.cmd); diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini new file mode 100644 index 0000000..c439f13 --- /dev/null +++ b/dist/mercury/segatools.ini @@ -0,0 +1,42 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs=amfs +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata=appdata +option=option + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[ds] +; Region code on the emulated AMEX board DS EEPROM. +; 1: Japan +; 4: Export (some UI elements in English) +; +; NOTE: Changing this setting causes a factory reset. +region=1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; SEGA games are somewhat picky about their LAN environment, so leaving this +; setting enabled is recommended. +enable=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.250.0 + + +[io4] +; Input API selection for JVS input emulator. +test=0x31 +service=0x32 + +[gfx] +enable=0 diff --git a/dist/mercury/start.bat b/dist/mercury/start.bat new file mode 100644 index 0000000..437722e --- /dev/null +++ b/dist/mercury/start.bat @@ -0,0 +1,10 @@ +@echo off +pushd %~dp0 +taskkill /f /im amdaemon.exe > nul 2>&1 +:LOOP +inject -d -k mercuryhook.dll amdaemon.exe -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json +inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury.exe +taskkill /f /im amdaemon.exe > nul 2>&1 +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/mercuryhook/config.c b/mercuryhook/config.c new file mode 100644 index 0000000..9a61aad --- /dev/null +++ b/mercuryhook/config.c @@ -0,0 +1,43 @@ +#include +#include + +#include "board/config.h" + +#include "hooklib/config.h" +#include "hooklib/dvd.h" +#include "hooklib/gfx.h" + +#include "mercuryhook/config.h" + +#include "platform/config.h" + +void mercury_dll_config_load( + struct mercury_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"mercuryio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + +void mercury_hook_config_load( + struct mercury_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + platform_config_load(&cfg->platform, filename); + aime_config_load(&cfg->aime, filename); + dvd_config_load(&cfg->dvd, filename); + io4_config_load(&cfg->io4, filename); + gfx_config_load(&cfg->gfx, filename); + mercury_dll_config_load(&cfg->dll, filename); +} diff --git a/mercuryhook/config.h b/mercuryhook/config.h new file mode 100644 index 0000000..627e69c --- /dev/null +++ b/mercuryhook/config.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "board/config.h" + +#include "hooklib/dvd.h" +#include "hooklib/gfx.h" + +#include "mercuryhook/mercury-dll.h" + +#include "platform/config.h" + +struct mercury_hook_config { + struct platform_config platform; + struct aime_config aime; + struct dvd_config dvd; + struct io4_config io4; + struct gfx_config gfx; + struct mercury_dll_config dll; +}; + +void mercury_dll_config_load( + struct mercury_dll_config *cfg, + const wchar_t *filename); + +void mercury_hook_config_load( + struct mercury_hook_config *cfg, + const wchar_t *filename); diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c new file mode 100644 index 0000000..a362f7b --- /dev/null +++ b/mercuryhook/dllmain.c @@ -0,0 +1,109 @@ +#include + +#include "board/io4.h" +#include "board/sg-reader.h" +#include "board/vfd.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "mercuryhook/config.h" +#include "mercuryhook/io4.h" +#include "mercuryhook/mercury-dll.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE mercury_hook_mod; +static process_entry_t mercury_startup; +static struct mercury_hook_config mercury_hook_cfg; + +/* This hook is based on mu3hook, with leaked mercuryhook i/o codes. */ + +static DWORD CALLBACK mercury_pre_startup(void) +{ + HRESULT hr; + + dprintf("--- Begin mercury_pre_startup ---\n"); + + /* Load config */ + + mercury_hook_config_load(&mercury_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod); + gfx_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod); + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &mercury_hook_cfg.platform, + "SDFE", + "ACA1", + mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&mercury_hook_cfg.aime, 1, mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = vfd_hook_init(2); + + if (FAILED(hr)) { + goto fail; + } + + hr = mercury_dll_init(&mercury_hook_cfg.dll, mercury_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = mercury_io4_hook_init(&mercury_hook_cfg.io4); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End mercury_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return mercury_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + mercury_hook_mod = mod; + + hr = process_hijack_startup(mercury_pre_startup, &mercury_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c new file mode 100644 index 0000000..2ff0e83 --- /dev/null +++ b/mercuryhook/io4.c @@ -0,0 +1,64 @@ +#include + +#include +#include +#include + +#include "board/io4.h" + +#include "mercuryhook/mercury-dll.h" + +#include "util/dprintf.h" + +static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state); + +static const struct io4_ops mercury_io4_ops = { + .poll = mercury_io4_poll, +}; + +HRESULT mercury_io4_hook_init(const struct io4_config *cfg) +{ + HRESULT hr; + + assert(mercury_dll.init != NULL); + + hr = io4_hook_init(cfg, &mercury_io4_ops, NULL); + + if (FAILED(hr)) { + return hr; + } + + return mercury_dll.init(); +} + +static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) +{ + uint8_t opbtn; + HRESULT hr; + + assert(mercury_dll.poll != NULL); + assert(mercury_dll.get_opbtns != NULL); + assert(mercury_dll.get_gamebtns != NULL); + + memset(state, 0, sizeof(*state)); + + hr = mercury_dll.poll(); + + if (FAILED(hr)) { + return hr; + } + + opbtn = 0; + + mercury_dll.get_opbtns(&opbtn); + + if (opbtn & MAI2_IO_OPBTN_TEST) { + state->buttons[0] |= IO4_BUTTON_TEST; + } + + if (opbtn & MAI2_IO_OPBTN_SERVICE) { + state->buttons[0] |= IO4_BUTTON_SERVICE; + } + + return S_OK; +} diff --git a/mercuryhook/io4.h b/mercuryhook/io4.h new file mode 100644 index 0000000..87dc6d7 --- /dev/null +++ b/mercuryhook/io4.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "board/io4.h" + +HRESULT mercury_io4_hook_init(const struct io4_config *cfg); diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c new file mode 100644 index 0000000..6873820 --- /dev/null +++ b/mercuryhook/mercury-dll.c @@ -0,0 +1,109 @@ +#include + +#include +#include + +#include "mercuryhook/mercury-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym mercury_dll_syms[] = { + { + .sym = "mercury_io_init", + .off = offsetof(struct mercury_dll, init), + }, { + .sym = "mercury_io_poll", + .off = offsetof(struct mercury_dll, poll), + }, { + .sym = "mercury_io_get_opbtns", + .off = offsetof(struct mercury_dll, get_opbtns), + }, { + .sym = "mercury_io_get_gamebtns", + .off = offsetof(struct mercury_dll, get_gamebtns), + } +}; + +struct mercury_dll mercury_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Wacca IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Wacca IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "mercury_io_get_api_version"); + + if (get_api_version != NULL) { + mercury_dll.api_version = get_api_version(); + } else { + mercury_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose mercury_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (mercury_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Wacca IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + mercury_dll.api_version); + + goto end; + } + + sym = mercury_dll_syms; + hr = dll_bind(&mercury_dll, src, &sym, _countof(mercury_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Wacca IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h new file mode 100644 index 0000000..4e679a8 --- /dev/null +++ b/mercuryhook/mercury-dll.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "mercuryio/mercuryio.h" + +struct mercury_dll { + uint16_t api_version; + HRESULT (*init)(void); + HRESULT (*poll)(void); + void (*get_opbtns)(uint8_t *opbtn); + void (*get_gamebtns)(uint16_t *player1, uint16_t *player2); +}; + +struct mercury_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct mercury_dll mercury_dll; + +HRESULT mercury_dll_init(const struct mercury_dll_config *cfg, HINSTANCE self); diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def new file mode 100644 index 0000000..916d278 --- /dev/null +++ b/mercuryhook/mercuryhook.def @@ -0,0 +1,19 @@ +LIBRARY mercuryhook + +EXPORTS + Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 + mercury_io_get_api_version + mercury_io_get_gamebtns + mercury_io_get_opbtns + mercury_io_init + mercury_io_poll \ No newline at end of file diff --git a/mercuryhook/meson.build b/mercuryhook/meson.build new file mode 100644 index 0000000..5fc141c --- /dev/null +++ b/mercuryhook/meson.build @@ -0,0 +1,29 @@ +shared_library( + 'mercuryhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'mercuryhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + board_lib, + hooklib_lib, + mercuryio_lib, + platform_lib, + util_lib, + ], + sources : [ + 'config.c', + 'config.h', + 'dllmain.c', + 'io4.c', + 'io4.h', + 'mercury-dll.c', + 'mercury-dll.h' + ], +) diff --git a/mercuryio/config.c b/mercuryio/config.c new file mode 100644 index 0000000..3b62789 --- /dev/null +++ b/mercuryio/config.c @@ -0,0 +1,25 @@ +#include + +#include +#include +#include + +#include "mercuryio/config.h" + +/* +Wacca Default key binding +*/ + +void mercury_io_config_load( + struct mercury_io_config *cfg, + const wchar_t *filename) +{ + wchar_t key[16]; + int i; + + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename); +} diff --git a/mercuryio/config.h b/mercuryio/config.h new file mode 100644 index 0000000..69cdf32 --- /dev/null +++ b/mercuryio/config.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +struct mercury_io_config { + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_1p_btn[9]; + uint8_t vk_2p_btn[9]; +}; + +void mercury_io_config_load( + struct mercury_io_config *cfg, + const wchar_t *filename); diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c new file mode 100644 index 0000000..93266ab --- /dev/null +++ b/mercuryio/mercuryio.c @@ -0,0 +1,133 @@ +#include + +#include +#include + +#include "mercuryio/mercuryio.h" +#include "mercuryio/config.h" + +static uint8_t mercury_opbtn; +static uint16_t mercury_player1_btn; +static uint16_t mercury_player2_btn; +static struct mercury_io_config mercury_io_cfg; + +uint16_t mercury_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT mercury_io_init(void) +{ + mercury_io_config_load(&mercury_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +HRESULT mercury_io_poll(void) +{ + mercury_opbtn = 0; + mercury_player1_btn = 0; + mercury_player2_btn = 0; + + if (GetAsyncKeyState(mercury_io_cfg.vk_test)) { + mercury_opbtn |= MAI2_IO_OPBTN_TEST; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_service)) { + mercury_opbtn |= MAI2_IO_OPBTN_SERVICE; + } + + //Player 1 + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[0])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_1; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[1])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_2; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[2])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_3; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[3])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_4; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[4])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_5; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[5])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_6; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[6])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_7; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[7])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_8; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[8])) { + mercury_player1_btn |= MAI2_IO_GAMEBTN_SELECT; + } + + //Player 2 + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[0])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_1; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[1])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_2; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[2])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_3; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[3])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_4; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[4])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_5; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[5])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_6; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[6])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_7; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[7])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_8; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[8])) { + mercury_player2_btn |= MAI2_IO_GAMEBTN_SELECT; + } + + return S_OK; +} + +void mercury_io_get_opbtns(uint8_t *opbtn) +{ + if (opbtn != NULL) { + *opbtn = mercury_opbtn; + } +} + +void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) +{ + if (player1 != NULL) { + *player1 = mercury_player1_btn; + } + + if (player2 != NULL ){ + *player2 = mercury_player2_btn; + } +} \ No newline at end of file diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h new file mode 100644 index 0000000..85b3d5a --- /dev/null +++ b/mercuryio/mercuryio.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +enum { + MAI2_IO_OPBTN_TEST = 0x01, + MAI2_IO_OPBTN_SERVICE = 0x02, +}; + +enum { + MAI2_IO_GAMEBTN_1 = 0x01, + MAI2_IO_GAMEBTN_2 = 0x02, + MAI2_IO_GAMEBTN_3 = 0x04, + MAI2_IO_GAMEBTN_4 = 0x08, + MAI2_IO_GAMEBTN_5 = 0x10, + MAI2_IO_GAMEBTN_6 = 0x20, + MAI2_IO_GAMEBTN_7 = 0x40, + MAI2_IO_GAMEBTN_8 = 0x80, + MAI2_IO_GAMEBTN_SELECT = 0x100, +}; + +/* Get the version of the Wacca IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t mercury_io_get_api_version(void); + +/* Initialize the IO DLL. This is the second function that will be called on + your DLL, after mercury_io_get_api_version. + + All subsequent calls to this API may originate from arbitrary threads. + + Minimum API version: 0x0100 */ + +HRESULT mercury_io_init(void); + +/* Send any queued outputs (of which there are currently none, though this may + change in subsequent API versions) and retrieve any new inputs. + + Minimum API version: 0x0100 */ + +HRESULT mercury_io_poll(void); + +/* Get the state of the cabinet's operator buttons as of the last poll. See + MAI2_IO_OPBTN enum above: this contains bit mask definitions for button + states returned in *opbtn. All buttons are active-high. + + Minimum API version: 0x0100 */ + +void mercury_io_get_opbtns(uint8_t *opbtn); + +/* Get the state of the cabinet's gameplay buttons as of the last poll. See + MAI2_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into + a left hand side set of inputs and a right hand side set of inputs: the bit + mappings are the same in both cases. + + All buttons are active-high, even though some buttons' electrical signals + on a real cabinet are active-low. + + Minimum API version: 0x0100 */ + +void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2); diff --git a/mercuryio/meson.build b/mercuryio/meson.build new file mode 100644 index 0000000..813958a --- /dev/null +++ b/mercuryio/meson.build @@ -0,0 +1,13 @@ +mercuryio_lib = static_library( + 'mercuryio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + sources : [ + 'mercuryio.c', + 'mercuryio.h', + 'config.c', + 'config.h', + ], +) diff --git a/meson.build b/meson.build index 50660ad..f143e72 100644 --- a/meson.build +++ b/meson.build @@ -48,9 +48,11 @@ subdir('chuniio') subdir('divaio') subdir('idzio') subdir('mu3io') +subdir('mercuryio') subdir('chunihook') subdir('divahook') subdir('idzhook') subdir('minihook') subdir('mu3hook') +subdir('mercuryhook') From 7c7dbe918ffd305f9ac47709d16a37009dd67e65 Mon Sep 17 00:00:00 2001 From: Bemani Witch Date: Tue, 21 Dec 2021 06:23:56 +0100 Subject: [PATCH 122/175] iccard/felica.c: improve spelling in comment --- iccard/felica.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iccard/felica.c b/iccard/felica.c index 550547f..09e3a77 100644 --- a/iccard/felica.c +++ b/iccard/felica.c @@ -32,9 +32,10 @@ uint64_t felica_get_generic_PMm(void) communicating with a particular IC card. The exact values are not particularly important for our purposes, so we'll just return a hard- coded PMm. This current value has been taken from an iPhone, emulating - a suica pass via apple wallet, which seems to be one of the few - universally accepted felica types for these games. Certain older - suica passes etc do not seem to be supported anymore. */ + a Suica pass via Apple Wallet, which seems to be one of the few + universally accepted FeliCa types for these games. Certain older + Suica passes and other payment and transportation cards + do not seem to be supported anymore. */ return 0x01168B868FBECBFFULL; } From 798446ec79d8a4fec2373e94d275a39169b7b1ff Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 11 Nov 2021 12:51:44 +0000 Subject: [PATCH 123/175] gfxhook/d3d9.c: Add Direct3D 9 Ex hook --- gfxhook/d3d9.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 7 deletions(-) diff --git a/gfxhook/d3d9.c b/gfxhook/d3d9.c index 4c39178..c8de146 100644 --- a/gfxhook/d3d9.c +++ b/gfxhook/d3d9.c @@ -17,8 +17,9 @@ #include "util/dprintf.h" typedef IDirect3D9 * (WINAPI *Direct3DCreate9_t)(UINT sdk_ver); +typedef HRESULT (WINAPI *Direct3DCreate9Ex_t)(UINT sdk_ver, IDirect3D9Ex **d3d9ex); -static HRESULT STDMETHODCALLTYPE my_CreateDevice( +static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice( IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, @@ -26,15 +27,28 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( DWORD flags, D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev); +static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice( + IDirect3D9Ex *self, + UINT adapter, + D3DDEVTYPE type, + HWND hwnd, + DWORD flags, + D3DPRESENT_PARAMETERS *pp, + IDirect3DDevice9 **pdev); static struct gfx_config gfx_config; static Direct3DCreate9_t next_Direct3DCreate9; +static Direct3DCreate9Ex_t next_Direct3DCreate9Ex; static const struct hook_symbol gfx_hooks[] = { { .name = "Direct3DCreate9", .patch = Direct3DCreate9, - .link = (void **) &next_Direct3DCreate9 + .link = (void **) &next_Direct3DCreate9, + }, { + .name = "Direct3DCreate9Ex", + .patch = Direct3DCreate9Ex, + .link = (void **) &next_Direct3DCreate9Ex, }, }; @@ -51,7 +65,7 @@ void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self) memcpy(&gfx_config, cfg, sizeof(*cfg)); hook_table_apply(NULL, "d3d9.dll", gfx_hooks, _countof(gfx_hooks)); - if (next_Direct3DCreate9 == NULL) { + if (next_Direct3DCreate9 == NULL || next_Direct3DCreate9Ex == NULL) { d3d9 = LoadLibraryW(L"d3d9.dll"); if (d3d9 == NULL) { @@ -60,12 +74,20 @@ void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self) goto fail; } - next_Direct3DCreate9 = (Direct3DCreate9_t) GetProcAddress(d3d9, "Direct3DCreate9"); + if (next_Direct3DCreate9 == NULL) { + next_Direct3DCreate9 = (Direct3DCreate9_t) GetProcAddress(d3d9, "Direct3DCreate9"); + } + if (next_Direct3DCreate9Ex == NULL) { + next_Direct3DCreate9Ex = (Direct3DCreate9Ex_t) GetProcAddress(d3d9, "Direct3DCreate9Ex"); + } if (next_Direct3DCreate9 == NULL) { dprintf("Gfx: Direct3DCreate9 not found in loaded d3d9.dll\n"); - FreeLibrary(d3d9); + goto fail; + } + if (next_Direct3DCreate9Ex == NULL) { + dprintf("Gfx: Direct3DCreate9Ex not found in loaded d3d9.dll\n"); goto fail; } @@ -75,7 +97,13 @@ void gfx_d3d9_hook_init(const struct gfx_config *cfg, HINSTANCE self) dll_hook_push(self, L"d3d9.dll"); } + return; + fail: + if (d3d9 != NULL) { + FreeLibrary(d3d9); + } + return; } @@ -88,6 +116,8 @@ IDirect3D9 * WINAPI Direct3DCreate9(UINT sdk_ver) dprintf("Gfx: Direct3DCreate9 hook hit\n"); + api = NULL; + if (next_Direct3DCreate9 == NULL) { dprintf("Gfx: next_Direct3DCreate9 == NULL\n"); @@ -111,7 +141,7 @@ IDirect3D9 * WINAPI Direct3DCreate9(UINT sdk_ver) } vtbl = proxy->vptr; - vtbl->CreateDevice = my_CreateDevice; + vtbl->CreateDevice = my_IDirect3D9_CreateDevice; return (IDirect3D9 *) proxy; @@ -123,7 +153,56 @@ fail: return NULL; } -static HRESULT STDMETHODCALLTYPE my_CreateDevice( +HRESULT WINAPI Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **d3d9ex) +{ + struct com_proxy *proxy; + IDirect3D9ExVtbl *vtbl; + IDirect3D9Ex *api; + HRESULT hr; + + dprintf("Gfx: Direct3DCreate9Ex hook hit\n"); + + api = NULL; + + if (next_Direct3DCreate9Ex == NULL) { + dprintf("Gfx: next_Direct3DCreate9Ex == NULL\n"); + + goto fail; + } + + hr = next_Direct3DCreate9Ex(sdk_ver, d3d9ex); + + if (FAILED(hr)) { + dprintf("Gfx: next_Direct3DCreate9Ex returned %x\n", (int) hr); + + goto fail; + } + + api = *d3d9ex; + hr = com_proxy_wrap(&proxy, api, sizeof(*api->lpVtbl)); + + if (FAILED(hr)) { + dprintf("Gfx: com_proxy_wrap returned %x\n", (int) hr); + + goto fail; + } + + vtbl = proxy->vptr; + vtbl->CreateDevice = my_IDirect3D9Ex_CreateDevice; + + *d3d9ex = (IDirect3D9Ex *) proxy; + + return S_OK; + +fail: + if (api != NULL) { + IDirect3D9Ex_Release(api); + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice( IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, @@ -153,3 +232,24 @@ static HRESULT STDMETHODCALLTYPE my_CreateDevice( return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev); } + +static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice( + IDirect3D9Ex *self, + UINT adapter, + D3DDEVTYPE type, + HWND hwnd, + DWORD flags, + D3DPRESENT_PARAMETERS *pp, + IDirect3DDevice9 **pdev) +{ + dprintf("Gfx: IDirect3D9Ex::CreateDevice hook forwarding to my_IDirect3D9_CreateDevice\n"); + + return my_IDirect3D9_CreateDevice( + (IDirect3D9 *) self, + adapter, + type, + hwnd, + flags, + pp, + pdev); +} From cdbf6ad4e2ee4c24626bcd8c2554662ceb7d3676 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 21 Dec 2021 23:32:33 -0500 Subject: [PATCH 124/175] gfxhook/gfx.c: Remove unused self parameter --- chunihook/dllmain.c | 2 +- gfxhook/gfx.c | 2 +- gfxhook/gfx.h | 4 +--- idzhook/dllmain.c | 2 +- mu3hook/dllmain.c | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index e0db84c..5350eb9 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -62,7 +62,7 @@ static DWORD CALLBACK chuni_pre_startup(void) /* Hook Win32 APIs */ - gfx_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); + gfx_hook_init(&chuni_hook_cfg.gfx); gfx_d3d9_hook_init(&chuni_hook_cfg.gfx, chuni_hook_mod); serial_hook_init(); diff --git a/gfxhook/gfx.c b/gfxhook/gfx.c index 34533af..6c341b9 100644 --- a/gfxhook/gfx.c +++ b/gfxhook/gfx.c @@ -26,7 +26,7 @@ static const struct hook_symbol gfx_hooks[] = { }, }; -void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self) +void gfx_hook_init(const struct gfx_config *cfg) { assert(cfg != NULL); diff --git a/gfxhook/gfx.h b/gfxhook/gfx.h index 9182371..9a7e27c 100644 --- a/gfxhook/gfx.h +++ b/gfxhook/gfx.h @@ -1,7 +1,5 @@ #pragma once -#include - #include struct gfx_config { @@ -11,4 +9,4 @@ struct gfx_config { int monitor; }; -void gfx_hook_init(const struct gfx_config *cfg, HINSTANCE self); +void gfx_hook_init(const struct gfx_config *cfg); diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 80b8c66..fe78b6f 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -67,7 +67,7 @@ static DWORD CALLBACK idz_pre_startup(void) /* Hook Win32 APIs */ serial_hook_init(); - gfx_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); + gfx_hook_init(&idz_hook_cfg.gfx); gfx_d3d11_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); gfx_dxgi_hook_init(&idz_hook_cfg.gfx, idz_hook_mod); zinput_hook_init(&idz_hook_cfg.zinput); diff --git a/mu3hook/dllmain.c b/mu3hook/dllmain.c index fc4b1ab..dd01104 100644 --- a/mu3hook/dllmain.c +++ b/mu3hook/dllmain.c @@ -43,7 +43,7 @@ static DWORD CALLBACK mu3_pre_startup(void) /* Hook Win32 APIs */ dvd_hook_init(&mu3_hook_cfg.dvd, mu3_hook_mod); - gfx_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); + gfx_hook_init(&mu3_hook_cfg.gfx); gfx_d3d9_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); gfx_d3d11_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); gfx_dxgi_hook_init(&mu3_hook_cfg.gfx, mu3_hook_mod); From 494b3d684b63869b9ed9a4cea89228820efb0fc0 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 21 Dec 2021 23:35:30 -0500 Subject: [PATCH 125/175] gfxhook: Fix MSVC build --- gfxhook/d3d11.c | 2 +- gfxhook/meson.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gfxhook/d3d11.c b/gfxhook/d3d11.c index 4313431..1fc3295 100644 --- a/gfxhook/d3d11.c +++ b/gfxhook/d3d11.c @@ -1,6 +1,6 @@ #include -#include #include +#include #include #include diff --git a/gfxhook/meson.build b/gfxhook/meson.build index f37accb..b973ddd 100644 --- a/gfxhook/meson.build +++ b/gfxhook/meson.build @@ -5,6 +5,7 @@ gfxhook_lib = static_library( c_pch : '../precompiled.h', dependencies : [ capnhook.get_variable('hook_dep'), + dxguid_lib, ], link_with : [ hooklib_lib, From dc715baa5520748c7fa4a8622551bbf5298e4d74 Mon Sep 17 00:00:00 2001 From: Felix Date: Tue, 21 Dec 2021 23:54:29 -0500 Subject: [PATCH 126/175] gfxhook: Fix some clang-tidy warnings --- gfxhook/config.c | 1 - gfxhook/d3d11.c | 8 ++------ gfxhook/d3d9.c | 4 ---- gfxhook/dxgi.c | 10 ++++------ gfxhook/gfx.c | 1 - gfxhook/util.c | 8 +++----- gfxhook/util.h | 2 +- hooklib/dll.h | 2 ++ 8 files changed, 12 insertions(+), 24 deletions(-) diff --git a/gfxhook/config.c b/gfxhook/config.c index 230ee0d..98db059 100644 --- a/gfxhook/config.c +++ b/gfxhook/config.c @@ -1,7 +1,6 @@ #include #include -#include #include #include "gfxhook/config.h" diff --git a/gfxhook/d3d11.c b/gfxhook/d3d11.c index 1fc3295..cd60529 100644 --- a/gfxhook/d3d11.c +++ b/gfxhook/d3d11.c @@ -3,13 +3,11 @@ #include #include -#include #include #include "gfxhook/gfx.h" #include "gfxhook/util.h" -#include "hook/com-proxy.h" #include "hook/table.h" #include "hooklib/dll.h" @@ -157,8 +155,8 @@ HRESULT WINAPI D3D11CreateDeviceAndSwapChain( { DXGI_SWAP_CHAIN_DESC *desc; HWND hwnd; - LONG width; - LONG height; + UINT width; + UINT height; dprintf("D3D11: D3D11CreateDeviceAndSwapChain hook hit\n"); @@ -172,8 +170,6 @@ HRESULT WINAPI D3D11CreateDeviceAndSwapChain( height = desc->BufferDesc.Height; } else { hwnd = NULL; - width = 0; - height = 0; } if (hwnd != NULL) { diff --git a/gfxhook/d3d9.c b/gfxhook/d3d9.c index c8de146..34a165d 100644 --- a/gfxhook/d3d9.c +++ b/gfxhook/d3d9.c @@ -2,7 +2,6 @@ #include #include -#include #include #include "hook/com-proxy.h" @@ -10,7 +9,6 @@ #include "hooklib/dll.h" -#include "gfxhook/config.h" #include "gfxhook/gfx.h" #include "gfxhook/util.h" @@ -103,8 +101,6 @@ fail: if (d3d9 != NULL) { FreeLibrary(d3d9); } - - return; } IDirect3D9 * WINAPI Direct3DCreate9(UINT sdk_ver) diff --git a/gfxhook/dxgi.c b/gfxhook/dxgi.c index 4de0f74..f97c506 100644 --- a/gfxhook/dxgi.c +++ b/gfxhook/dxgi.c @@ -284,8 +284,8 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( struct com_proxy *proxy; IDXGIFactory *real; HWND hwnd; - LONG width; - LONG height; + UINT width; + UINT height; dprintf("DXGI: IDXGIFactory::CreateSwapChain hook hit\n"); @@ -300,8 +300,6 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( height = desc->BufferDesc.Height; } else { hwnd = NULL; - width = 0; - height = 0; } if (hwnd != NULL) { @@ -323,8 +321,8 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( HWND_TOP, 0, 0, - width, - height, + (int) width, + (int) height, SWP_FRAMECHANGED | SWP_NOSENDCHANGING); ShowWindow(hwnd, SW_SHOWMAXIMIZED); diff --git a/gfxhook/gfx.c b/gfxhook/gfx.c index 6c341b9..0ac1761 100644 --- a/gfxhook/gfx.c +++ b/gfxhook/gfx.c @@ -1,7 +1,6 @@ #include #include -#include #include #include "gfxhook/config.h" diff --git a/gfxhook/util.c b/gfxhook/util.c index 14268f3..4da45b3 100644 --- a/gfxhook/util.c +++ b/gfxhook/util.c @@ -1,7 +1,5 @@ #include -#include - #include "gfxhook/util.h" #include "util/dprintf.h" @@ -16,7 +14,7 @@ void gfx_util_ensure_win_visible(HWND hwnd) ShowWindow(hwnd, SW_RESTORE); } -void gfx_util_borderless_fullscreen_windowed(HWND hwnd, LONG width, LONG height) +void gfx_util_borderless_fullscreen_windowed(HWND hwnd, UINT width, UINT height) { BOOL ok; HRESULT hr; @@ -31,8 +29,8 @@ void gfx_util_borderless_fullscreen_windowed(HWND hwnd, LONG width, LONG height) HWND_TOP, 0, 0, - width, - height, + (int) width, + (int) height, SWP_FRAMECHANGED | SWP_NOSENDCHANGING); if (!ok) { diff --git a/gfxhook/util.h b/gfxhook/util.h index b300bba..d0c2401 100644 --- a/gfxhook/util.h +++ b/gfxhook/util.h @@ -3,5 +3,5 @@ #include void gfx_util_ensure_win_visible(HWND hwnd); -void gfx_util_borderless_fullscreen_windowed(HWND hwnd, LONG width, LONG height); +void gfx_util_borderless_fullscreen_windowed(HWND hwnd, UINT width, UINT height); HRESULT gfx_util_frame_window(HWND hwnd); diff --git a/hooklib/dll.h b/hooklib/dll.h index 2927a50..b4fc1b7 100644 --- a/hooklib/dll.h +++ b/hooklib/dll.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include From 3ca251374ef5dd41c96180d228462623f535d972 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 22 Dec 2021 11:32:20 -0500 Subject: [PATCH 127/175] gfxhook: Fix imports --- gfxhook/d3d11.c | 1 + gfxhook/dxgi.c | 1 + gfxhook/gfx.c | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gfxhook/d3d11.c b/gfxhook/d3d11.c index cd60529..4c94448 100644 --- a/gfxhook/d3d11.c +++ b/gfxhook/d3d11.c @@ -5,6 +5,7 @@ #include #include +#include "gfxhook/d3d11.h" #include "gfxhook/gfx.h" #include "gfxhook/util.h" diff --git a/gfxhook/dxgi.c b/gfxhook/dxgi.c index f97c506..e27e68d 100644 --- a/gfxhook/dxgi.c +++ b/gfxhook/dxgi.c @@ -5,6 +5,7 @@ #include #include +#include "gfxhook/dxgi.h" #include "gfxhook/gfx.h" #include "hook/com-proxy.h" diff --git a/gfxhook/gfx.c b/gfxhook/gfx.c index 0ac1761..1af3647 100644 --- a/gfxhook/gfx.c +++ b/gfxhook/gfx.c @@ -3,7 +3,6 @@ #include #include -#include "gfxhook/config.h" #include "gfxhook/gfx.h" #include "hook/table.h" From 7ba14fe07123d6f02db4e8b4d7cacce3e96209c6 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 22 Dec 2021 23:23:42 -0500 Subject: [PATCH 128/175] renaming --- mercuryhook/io4.c | 4 +- mercuryio/mercuryio.c | 88 ++----------------------------------------- mercuryio/mercuryio.h | 26 ++++++------- 3 files changed, 18 insertions(+), 100 deletions(-) diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index 2ff0e83..30c7e35 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -52,11 +52,11 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) mercury_dll.get_opbtns(&opbtn); - if (opbtn & MAI2_IO_OPBTN_TEST) { + if (opbtn & MERCURY_IO_OPBTN_TEST) { state->buttons[0] |= IO4_BUTTON_TEST; } - if (opbtn & MAI2_IO_OPBTN_SERVICE) { + if (opbtn & MERCURY_IO_OPBTN_SERVICE) { state->buttons[0] |= IO4_BUTTON_SERVICE; } diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 93266ab..96a6050 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -26,89 +26,13 @@ HRESULT mercury_io_init(void) HRESULT mercury_io_poll(void) { mercury_opbtn = 0; - mercury_player1_btn = 0; - mercury_player2_btn = 0; if (GetAsyncKeyState(mercury_io_cfg.vk_test)) { - mercury_opbtn |= MAI2_IO_OPBTN_TEST; + mercury_opbtn |= MERCURY_IO_OPBTN_TEST; } if (GetAsyncKeyState(mercury_io_cfg.vk_service)) { - mercury_opbtn |= MAI2_IO_OPBTN_SERVICE; - } - - //Player 1 - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[0])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_1; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[1])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_2; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[2])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_3; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[3])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_4; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[4])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_5; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[5])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_6; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[6])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_7; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[7])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_8; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_1p_btn[8])) { - mercury_player1_btn |= MAI2_IO_GAMEBTN_SELECT; - } - - //Player 2 - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[0])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_1; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[1])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_2; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[2])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_3; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[3])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_4; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[4])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_5; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[5])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_6; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[6])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_7; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[7])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_8; - } - - if (GetAsyncKeyState(mercury_io_cfg.vk_2p_btn[8])) { - mercury_player2_btn |= MAI2_IO_GAMEBTN_SELECT; + mercury_opbtn |= MERCURY_IO_OPBTN_SERVICE; } return S_OK; @@ -123,11 +47,5 @@ void mercury_io_get_opbtns(uint8_t *opbtn) void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) { - if (player1 != NULL) { - *player1 = mercury_player1_btn; - } - - if (player2 != NULL ){ - *player2 = mercury_player2_btn; - } + } \ No newline at end of file diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 85b3d5a..a447f20 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -5,20 +5,20 @@ #include enum { - MAI2_IO_OPBTN_TEST = 0x01, - MAI2_IO_OPBTN_SERVICE = 0x02, + MERCURY_IO_OPBTN_TEST = 0x01, + MERCURY_IO_OPBTN_SERVICE = 0x02, }; enum { - MAI2_IO_GAMEBTN_1 = 0x01, - MAI2_IO_GAMEBTN_2 = 0x02, - MAI2_IO_GAMEBTN_3 = 0x04, - MAI2_IO_GAMEBTN_4 = 0x08, - MAI2_IO_GAMEBTN_5 = 0x10, - MAI2_IO_GAMEBTN_6 = 0x20, - MAI2_IO_GAMEBTN_7 = 0x40, - MAI2_IO_GAMEBTN_8 = 0x80, - MAI2_IO_GAMEBTN_SELECT = 0x100, + MERCURY_IO_GAMEBTN_1 = 0x01, + MERCURY_IO_GAMEBTN_2 = 0x02, + MERCURY_IO_GAMEBTN_3 = 0x04, + MERCURY_IO_GAMEBTN_4 = 0x08, + MERCURY_IO_GAMEBTN_5 = 0x10, + MERCURY_IO_GAMEBTN_6 = 0x20, + MERCURY_IO_GAMEBTN_7 = 0x40, + MERCURY_IO_GAMEBTN_8 = 0x80, + MERCURY_IO_GAMEBTN_SELECT = 0x100, }; /* Get the version of the Wacca IO API that this DLL supports. This @@ -47,7 +47,7 @@ HRESULT mercury_io_init(void); HRESULT mercury_io_poll(void); /* Get the state of the cabinet's operator buttons as of the last poll. See - MAI2_IO_OPBTN enum above: this contains bit mask definitions for button + MERCURY_IO_OPBTN enum above: this contains bit mask definitions for button states returned in *opbtn. All buttons are active-high. Minimum API version: 0x0100 */ @@ -55,7 +55,7 @@ HRESULT mercury_io_poll(void); void mercury_io_get_opbtns(uint8_t *opbtn); /* Get the state of the cabinet's gameplay buttons as of the last poll. See - MAI2_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into + MERCURY_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into a left hand side set of inputs and a right hand side set of inputs: the bit mappings are the same in both cases. From 25977052d6621503ac04895f7f0d75207a5c6267 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 24 Dec 2021 03:05:37 -0500 Subject: [PATCH 129/175] Set up hooks for GetProcAddress and LoadLibraryW --- mercuryhook/dllmain.c | 4 ++ mercuryhook/elisabeth.c | 135 ++++++++++++++++++++++++++++++++++++++++ mercuryhook/elisabeth.h | 3 + mercuryhook/meson.build | 4 +- 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 mercuryhook/elisabeth.c create mode 100644 mercuryhook/elisabeth.h diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c index a362f7b..3c7786d 100644 --- a/mercuryhook/dllmain.c +++ b/mercuryhook/dllmain.c @@ -12,6 +12,7 @@ #include "mercuryhook/config.h" #include "mercuryhook/io4.h" #include "mercuryhook/mercury-dll.h" +#include "mercuryhook/elisabeth.h" #include "platform/platform.h" @@ -75,6 +76,9 @@ static DWORD CALLBACK mercury_pre_startup(void) goto fail; } + /* Start elisabeth Hooks for the LED and IO Board DLLs */ + elisabeth_hook_init(); + /* Initialize debug helpers */ spike_hook_init(L".\\segatools.ini"); diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c new file mode 100644 index 0000000..82d24d2 --- /dev/null +++ b/mercuryhook/elisabeth.c @@ -0,0 +1,135 @@ +#include + +#include +#include +#include + +#include "mercuryhook/elisabeth.h" + +#include "hook/table.h" + +#include "hooklib/dll.h" +#include "hooklib/path.h" + +#include "util/dprintf.h" + +/* Hooks targeted DLLs dynamically loaded by elisabeth. */ + +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 FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); +static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); + +static const struct hook_symbol elisabeth_hooks[] = { + { + .name = "LoadLibraryW", + .patch = my_LoadLibraryW, + .link = (void **) &next_LoadLibraryW, + }, + { + .name = "GetProcAddress", + .patch = my_GetProcAddress, + .link = (void **) &next_GetProcAddress + } +}; + +static const wchar_t *target_modules[] = { + L"USBIntLED.DLL", + L"ftd2XX.dll", +}; + +static const char *target_functions[] = { + "FT_Read", + "FT_Write", + "USBIntLED_Init", +}; + +static const size_t target_modules_len = _countof(target_modules); +static const size_t target_functions_len = _countof(target_functions); + +void elisabeth_hook_init() +{ + dll_hook_insert_hooks(NULL); +} + +static void dll_hook_insert_hooks(HMODULE target) +{ + hook_table_apply( + target, + "kernel32.dll", + elisabeth_hooks, + _countof(elisabeth_hooks)); +} + +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("Elisabeth: Loaded %S\n", target_module); + + dll_hook_insert_hooks(result); + path_hook_insert_hooks(result); + } + } + + return result; +} + +FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) +{ + uintptr_t ordinal; + + FARPROC result = next_GetProcAddress(hModule, name); + + + for (size_t i = 0; i < target_functions_len; i++) { + ordinal = (uintptr_t) name; + + if (ordinal > 0xFFFF) { + /* Import by name */ + if (strcmp(target_functions[i], name) == 0) + dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); + } + } + + return result; +} diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h new file mode 100644 index 0000000..30285c9 --- /dev/null +++ b/mercuryhook/elisabeth.h @@ -0,0 +1,3 @@ +#pragma once + +void elisabeth_hook_init(); diff --git a/mercuryhook/meson.build b/mercuryhook/meson.build index 5fc141c..6269e27 100644 --- a/mercuryhook/meson.build +++ b/mercuryhook/meson.build @@ -24,6 +24,8 @@ shared_library( 'io4.c', 'io4.h', 'mercury-dll.c', - 'mercury-dll.h' + 'mercury-dll.h', + 'elisabeth.h', + 'elisabeth.c' ], ) From 7c2fefb515849d56946c8d69b5035f50fe1447ea Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 27 Dec 2021 20:55:10 -0500 Subject: [PATCH 130/175] Document IO DLL configuration --- dist/chuni/segatools.ini | 10 ++++++++++ dist/idz/segatools.ini | 10 ++++++++++ doc/config/chunithm.md | 13 +++++++++++++ doc/config/common.md | 14 ++++++++++++++ doc/config/initiald.md | 13 +++++++++++++ 5 files changed, 60 insertions(+) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index 06bfd51..e94c405 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -33,6 +33,16 @@ framed=1 ; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) monitor=0 +[aimeio] +; To use a custom card reader IO DLL enter its path here. +; Leave empty if you want to use Segatools built-in keyboard input. +path= + +[chuniio] +; To use a custom Chunithm IO DLL enter its path here. +; Leave empty if you want to use Segatools built-in keyboard input. +path= + ; ----------------------------------------------------------------------------- ; Input settings ; ----------------------------------------------------------------------------- diff --git a/dist/idz/segatools.ini b/dist/idz/segatools.ini index 4c4f196..367d27e 100644 --- a/dist/idz/segatools.ini +++ b/dist/idz/segatools.ini @@ -40,6 +40,16 @@ subnet=192.168.100.0 ; exactly one machine and set this to 0 on all others. dipsw1=1 +[aimeio] +; To use a custom card reader IO DLL enter its path here. +; Leave empty if you want to use Segatools built-in keyboard input. +path= + +[idzio] +; To use a custom Initial D Zero IO DLL enter its path here. +; Leave empty if you want to use Segatools built-in gamepad/wheel input. +path= + [io3] ; Input API selection for JVS input emulator. ; Set "xinput" to use a gamepad and "dinput" to use a steering wheel. diff --git a/doc/config/chunithm.md b/doc/config/chunithm.md index cade7d8..c7323e1 100644 --- a/doc/config/chunithm.md +++ b/doc/config/chunithm.md @@ -5,6 +5,19 @@ This file describes configuration settings specific to Chunithm. Keyboard binding settings use [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). +## `[chuniio]` + +Controls the input driver. + +### `path` + +Specify a path for a third-party Chunithm input driver DLL. Default is empty +(use built-in keyboard IO emulation). + +In previous versions of Segatools this was accomplished by replacing the +CHUNIIO.DLL file that came with Segatools. Segatools no longer ships with a +separate CHUNIIO.DLL file (its functionality is now built into CHUNIHOOK.DLL). + ## `[io3]` Cabinet specific inputs. diff --git a/doc/config/common.md b/doc/config/common.md index b311476..4dcaf43 100644 --- a/doc/config/common.md +++ b/doc/config/common.md @@ -6,6 +6,20 @@ all games. Keyboard binding settings use [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). +## `[aimeio]` + +Controls the card reader driver. + +### `path` + +Specify a path for a third-party card reader driver DLL. Default is empty +(use built-in emulation based on text files and keyboard input). + +In previous versions of Segatools this was accomplished by replacing the +AIMEIO.DLL file that came with Segatools. Segatools no longer ships with a +separate AIMEIO.DLL file (its functionality is now built into the various hook +DLLs). + ## `[aime]` Controls emulation of the Aime card reader assembly. diff --git a/doc/config/initiald.md b/doc/config/initiald.md index 870d8b9..d7d6e60 100644 --- a/doc/config/initiald.md +++ b/doc/config/initiald.md @@ -6,6 +6,19 @@ Zero. Keyboard binding settings use [Virtual-Key Codes](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). +## `[idzio]` + +Controls the input driver. + +### `path` + +Specify a path for a third-party Initial D Zero input driver DLL. Default is +empty (use built-in DirectInput and XInput based IO emulation). + +In previous versions of Segatools this was accomplished by replacing the +IDZIO.DLL file that came with Segatools. Segatools no longer ships with a +separate IDZIO.DLL file (its functionality is now built into IDZHOOK.DLL). + ## `[io3]` Cabinet specific inputs. From 21d52482be3a17b122bf7b80395cb9455e8f4e0d Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 27 Dec 2021 20:55:20 -0500 Subject: [PATCH 131/175] Add the Unlicense to the repository --- LICENSE | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to From e374c0ab42b8b552a0409d23362ce22b900c167f Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 27 Dec 2021 20:56:30 -0500 Subject: [PATCH 132/175] Update CHANGELOG --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee4bd9d..c51a4ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v005 + +* Allow custom IO DLLs to be specified in INI files: + * `[aimeio] path=` for aime reader drivers + * `[chuniio] path=` for Chunithm input drivers + * `[idzio] path=` for Initial D Zero input drivers +* Add INI documentation +* Build system and contribution workflow improvements (contributed by icex2) +* Add hook to hide DVD drives (contributed by BemaniWitch) +* Add option to disable Diva slider emulation (contributed by dogtopus) +* AMEX board accuracy fixes (contributed by seika1, Felix) +* Improve multi-monitor support (contributed by BemaniWitch) +* Various Ongeki fixes (contributed by Felix) +* Various Diva slider fixes (contributed by dogtopus) + # v004 * Add initial support for mounting DLC package dumps (contributed by Shiz) From 7473791db21b86062d5a258e501098c2ed7cd0af Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 29 Dec 2021 21:58:00 -0500 Subject: [PATCH 133/175] added setupapi hook to elisabeth --- hooklib/dll.c | 2 ++ hooklib/setupapi.c | 18 ++++++++++++------ hooklib/setupapi.h | 1 + mercuryhook/elisabeth.c | 34 ++++++++++++---------------------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/hooklib/dll.c b/hooklib/dll.c index 9400bb9..1eab2b7 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -284,6 +284,8 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name) { HMODULE result; + dprintf("hook_LoadLibraryW: Loading %S\n", name); + if (name == NULL) { SetLastError(ERROR_INVALID_PARAMETER); diff --git a/hooklib/setupapi.c b/hooklib/setupapi.c index eb50323..26a5b53 100644 --- a/hooklib/setupapi.c +++ b/hooklib/setupapi.c @@ -134,22 +134,27 @@ end: return hr; } -static void setupapi_hook_init(void) +static void setupapi_hook_init() { if (setupapi_initted) { return; } - hook_table_apply( - NULL, - "setupapi.dll", - setupapi_syms, - _countof(setupapi_syms)); + setupapi_hook_insert_hooks(NULL); InitializeCriticalSection(&setupapi_lock); setupapi_initted = true; } +void setupapi_hook_insert_hooks(HMODULE target) +{ + hook_table_apply( + target, + "setupapi.dll", + setupapi_syms, + _countof(setupapi_syms)); +} + static HDEVINFO WINAPI my_SetupDiGetClassDevsW( const GUID *ClassGuid, wchar_t *Enumerator, @@ -191,6 +196,7 @@ static BOOL WINAPI my_SetupDiEnumDeviceInterfaces( DWORD MemberIndex, SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData) { + dprintf("my_SetupDiEnumDeviceInterfaces hit!\n"); const struct setupapi_class *class_; size_t i; diff --git a/hooklib/setupapi.h b/hooklib/setupapi.h index a09520c..94eea13 100644 --- a/hooklib/setupapi.h +++ b/hooklib/setupapi.h @@ -5,3 +5,4 @@ #include HRESULT setupapi_add_phantom_dev(const GUID *iface_class, const wchar_t *path); +void setupapi_hook_insert_hooks(HMODULE target); diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index 82d24d2..8dbc834 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -10,6 +10,7 @@ #include "hooklib/dll.h" #include "hooklib/path.h" +#include "hooklib/setupapi.h" #include "util/dprintf.h" @@ -21,7 +22,7 @@ static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name); static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); -static const struct hook_symbol elisabeth_hooks[] = { +static const struct hook_symbol win32_hooks[] = { { .name = "LoadLibraryW", .patch = my_LoadLibraryW, @@ -39,14 +40,7 @@ static const wchar_t *target_modules[] = { L"ftd2XX.dll", }; -static const char *target_functions[] = { - "FT_Read", - "FT_Write", - "USBIntLED_Init", -}; - static const size_t target_modules_len = _countof(target_modules); -static const size_t target_functions_len = _countof(target_functions); void elisabeth_hook_init() { @@ -58,8 +52,8 @@ static void dll_hook_insert_hooks(HMODULE target) hook_table_apply( target, "kernel32.dll", - elisabeth_hooks, - _countof(elisabeth_hooks)); + win32_hooks, + _countof(win32_hooks)); } static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) @@ -71,6 +65,8 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) size_t name_len; size_t target_module_len; + dprintf("Elisabeth: Trying to load %S\n", name); + if (name == NULL) { SetLastError(ERROR_INVALID_PARAMETER); @@ -107,7 +103,7 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) dprintf("Elisabeth: Loaded %S\n", target_module); dll_hook_insert_hooks(result); - path_hook_insert_hooks(result); + setupapi_hook_insert_hooks(result); } } @@ -116,19 +112,13 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) { - uintptr_t ordinal; + uintptr_t ordinal = (uintptr_t) name; - FARPROC result = next_GetProcAddress(hModule, name); + FARPROC result = next_GetProcAddress(hModule, name); - - for (size_t i = 0; i < target_functions_len; i++) { - ordinal = (uintptr_t) name; - - if (ordinal > 0xFFFF) { - /* Import by name */ - if (strcmp(target_functions[i], name) == 0) - dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); - } + if (ordinal > 0xFFFF) { + /* Import by name */ + dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); } return result; From 99068f4fb3622f7944a1c073615752af8661334a Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 30 Dec 2021 00:51:44 -0500 Subject: [PATCH 134/175] removed prints, added phantom device --- hooklib/dll.c | 2 -- hooklib/setupapi.c | 1 - mercuryhook/elisabeth.c | 11 +++++------ mercuryhook/elisabeth.h | 8 ++++++++ mercuryio/mercuryio.c | 6 ++---- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/hooklib/dll.c b/hooklib/dll.c index 1eab2b7..9400bb9 100644 --- a/hooklib/dll.c +++ b/hooklib/dll.c @@ -284,8 +284,6 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name) { HMODULE result; - dprintf("hook_LoadLibraryW: Loading %S\n", name); - if (name == NULL) { SetLastError(ERROR_INVALID_PARAMETER); diff --git a/hooklib/setupapi.c b/hooklib/setupapi.c index 26a5b53..edd416a 100644 --- a/hooklib/setupapi.c +++ b/hooklib/setupapi.c @@ -196,7 +196,6 @@ static BOOL WINAPI my_SetupDiEnumDeviceInterfaces( DWORD MemberIndex, SP_DEVICE_INTERFACE_DATA *DeviceInterfaceData) { - dprintf("my_SetupDiEnumDeviceInterfaces hit!\n"); const struct setupapi_class *class_; size_t i; diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index 8dbc834..2de12dc 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -1,3 +1,4 @@ +#include #include #include @@ -36,8 +37,7 @@ static const struct hook_symbol win32_hooks[] = { }; static const wchar_t *target_modules[] = { - L"USBIntLED.DLL", - L"ftd2XX.dll", + L"USBIntLED.DLL" }; static const size_t target_modules_len = _countof(target_modules); @@ -45,6 +45,7 @@ static const size_t target_modules_len = _countof(target_modules); void elisabeth_hook_init() { dll_hook_insert_hooks(NULL); + setupapi_add_phantom_dev(&elisabeth_guid, L"USB\\VID_0403&PID_6001"); } static void dll_hook_insert_hooks(HMODULE target) @@ -65,8 +66,6 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name) size_t name_len; size_t target_module_len; - dprintf("Elisabeth: Trying to load %S\n", name); - if (name == NULL) { SetLastError(ERROR_INVALID_PARAMETER); @@ -114,11 +113,11 @@ FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) { uintptr_t ordinal = (uintptr_t) name; - FARPROC result = next_GetProcAddress(hModule, name); + FARPROC result = next_GetProcAddress(hModule, name); if (ordinal > 0xFFFF) { /* Import by name */ - dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); + //dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); } return result; diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index 30285c9..0891f48 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -1,3 +1,11 @@ #pragma once +#include + +DEFINE_GUID( + elisabeth_guid, + 0x219D058, + 0x57A8, + 0x4FF5, + 0x97, 0x1A, 0xBD, 0x86, 0x58, 0x7C, 0x6C, 0x7E); void elisabeth_hook_init(); diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 96a6050..6bd410f 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -7,8 +7,6 @@ #include "mercuryio/config.h" static uint8_t mercury_opbtn; -static uint16_t mercury_player1_btn; -static uint16_t mercury_player2_btn; static struct mercury_io_config mercury_io_cfg; uint16_t mercury_io_get_api_version(void) @@ -47,5 +45,5 @@ void mercury_io_get_opbtns(uint8_t *opbtn) void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) { - -} \ No newline at end of file + +} From 2dff5278ef51ae790402cc797f8c502f6ea73ed8 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Thu, 30 Dec 2021 13:08:20 -0500 Subject: [PATCH 135/175] fixed messed up guid on elisabeth.h --- mercuryhook/elisabeth.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index 0891f48..2583742 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -3,9 +3,9 @@ DEFINE_GUID( elisabeth_guid, - 0x219D058, + 0x219D0508, 0x57A8, 0x4FF5, - 0x97, 0x1A, 0xBD, 0x86, 0x58, 0x7C, 0x6C, 0x7E); + 0x97, 0x0A1, 0x0BD, 0x86, 0x58, 0x7C, 0x6C, 0x7E); void elisabeth_hook_init(); From cc0f8dc595e7be77c002955fe0a8e7d03130b9f0 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 30 Dec 2021 13:57:05 -0500 Subject: [PATCH 136/175] changed everything and nothing --- mercuryhook/elisabeth.c | 87 ++++++++++++++++++++++++++++++++++++- mercuryhook/elisabeth.h | 2 +- mercuryhook/mercury-dll.c | 3 ++ mercuryhook/mercury-dll.h | 1 + mercuryhook/mercuryhook.def | 3 +- mercuryio/mercuryio.c | 5 +++ mercuryio/mercuryio.h | 2 + 7 files changed, 99 insertions(+), 4 deletions(-) diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index 2de12dc..f2bfd38 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -6,9 +6,11 @@ #include #include "mercuryhook/elisabeth.h" +#include "mercuryhook/mercury-dll.h" #include "hook/table.h" +#include "hooklib/uart.h" #include "hooklib/dll.h" #include "hooklib/path.h" #include "hooklib/setupapi.h" @@ -22,6 +24,13 @@ static HMODULE WINAPI my_LoadLibraryW(const wchar_t *name); static HMODULE (WINAPI *next_LoadLibraryW)(const wchar_t *name); static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); +static HRESULT elisabeth_handle_irp(struct irp *irp); +static HRESULT elisabeth_handle_irp_locked(struct irp *irp); + +static CRITICAL_SECTION elisabeth_lock; +static struct uart elisabeth_uart; +static uint8_t elisabeth_written_bytes[520]; +static uint8_t elisabeth_readable_bytes[520]; static const struct hook_symbol win32_hooks[] = { { @@ -42,10 +51,84 @@ static const wchar_t *target_modules[] = { static const size_t target_modules_len = _countof(target_modules); -void elisabeth_hook_init() +HRESULT elisabeth_hook_init() { dll_hook_insert_hooks(NULL); - setupapi_add_phantom_dev(&elisabeth_guid, L"USB\\VID_0403&PID_6001"); + setupapi_add_phantom_dev(&elisabeth_guid, L"$ftdi"); + + InitializeCriticalSection(&elisabeth_lock); + + uart_init(&elisabeth_uart, 1); + elisabeth_uart.written.bytes = elisabeth_written_bytes; + elisabeth_uart.written.nbytes = sizeof(elisabeth_written_bytes); + elisabeth_uart.readable.bytes = elisabeth_readable_bytes; + elisabeth_uart.readable.nbytes = sizeof(elisabeth_readable_bytes); + + return iohook_push_handler(elisabeth_handle_irp); +} + +static HRESULT elisabeth_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&elisabeth_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&elisabeth_lock); + hr = elisabeth_handle_irp_locked(irp); + LeaveCriticalSection(&elisabeth_lock); + + return hr; +} + +static HRESULT elisabeth_handle_irp_locked(struct irp *irp) +{ + //union elisabeth_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Elisabeth: Starting backend\n"); + hr = mercury_dll.elisabeth_init(); + + if (FAILED(hr)) { + dprintf("Elisabeth: Backend error: %x\n", (int) hr); + + return hr; + } + } + + hr = uart_handle_irp(&elisabeth_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { + + //req_iobuf.bytes = req.bytes; + //req_iobuf.nbytes = sizeof(req.bytes); + //req_iobuf.pos = 0; + + /*hr = elisabeth_frame_decode(&req_iobuf, &elisabeth_uart.written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Elisabeth: Deframe error: %x\n", (int) hr); + } + + return hr; + } + + hr = elisabeth_req_dispatch(&req); + + if (FAILED(hr)) { + dprintf("Elisabeth: Processing error: %x\n", (int) hr); + }*/ + } } static void dll_hook_insert_hooks(HMODULE target) diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index 2583742..d553ba8 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -8,4 +8,4 @@ DEFINE_GUID( 0x4FF5, 0x97, 0x0A1, 0x0BD, 0x86, 0x58, 0x7C, 0x6C, 0x7E); -void elisabeth_hook_init(); +HRESULT elisabeth_hook_init(); diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c index 6873820..1962709 100644 --- a/mercuryhook/mercury-dll.c +++ b/mercuryhook/mercury-dll.c @@ -21,6 +21,9 @@ const struct dll_bind_sym mercury_dll_syms[] = { }, { .sym = "mercury_io_get_gamebtns", .off = offsetof(struct mercury_dll, get_gamebtns), + }, { + .sym = "mercury_io_elisabeth_init", + .off = offsetof(struct mercury_dll, elisabeth_init), } }; diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h index 4e679a8..1942f0e 100644 --- a/mercuryhook/mercury-dll.h +++ b/mercuryhook/mercury-dll.h @@ -10,6 +10,7 @@ struct mercury_dll { HRESULT (*poll)(void); void (*get_opbtns)(uint8_t *opbtn); void (*get_gamebtns)(uint16_t *player1, uint16_t *player2); + HRESULT (*elisabeth_init)(void); }; struct mercury_dll_config { diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def index 916d278..d331e90 100644 --- a/mercuryhook/mercuryhook.def +++ b/mercuryhook/mercuryhook.def @@ -15,5 +15,6 @@ EXPORTS mercury_io_get_api_version mercury_io_get_gamebtns mercury_io_get_opbtns + mercury_io_elisabeth_init mercury_io_init - mercury_io_poll \ No newline at end of file + mercury_io_poll diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 6bd410f..4c0b2a5 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -47,3 +47,8 @@ void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) { } + +HRESULT mercury_io_elisabeth_init(void) +{ + return S_OK; +} diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index a447f20..827efb5 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -65,3 +65,5 @@ void mercury_io_get_opbtns(uint8_t *opbtn); Minimum API version: 0x0100 */ void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2); + +HRESULT mercury_io_elisabeth_init(void); From 8702cc9e99c6621aaac77d019708229d04153b98 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Tue, 4 Jan 2022 00:10:46 -0500 Subject: [PATCH 137/175] LED board done --- mercuryhook/elisabeth.c | 159 +++--------------------------------- mercuryhook/elisabeth.h | 8 -- mercuryhook/mercury-dll.c | 4 +- mercuryhook/mercury-dll.h | 2 +- mercuryhook/mercuryhook.def | 2 +- mercuryio/mercuryio.c | 2 +- mercuryio/mercuryio.h | 2 +- 7 files changed, 17 insertions(+), 162 deletions(-) diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index f2bfd38..c680ca7 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -20,24 +20,11 @@ /* Hooks targeted DLLs dynamically loaded by elisabeth. */ 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 FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); -static HRESULT elisabeth_handle_irp(struct irp *irp); -static HRESULT elisabeth_handle_irp_locked(struct irp *irp); - -static CRITICAL_SECTION elisabeth_lock; -static struct uart elisabeth_uart; -static uint8_t elisabeth_written_bytes[520]; -static uint8_t elisabeth_readable_bytes[520]; +static int my_USBIntLED_Init(); static const struct hook_symbol win32_hooks[] = { - { - .name = "LoadLibraryW", - .patch = my_LoadLibraryW, - .link = (void **) &next_LoadLibraryW, - }, { .name = "GetProcAddress", .patch = my_GetProcAddress, @@ -45,90 +32,9 @@ static const struct hook_symbol win32_hooks[] = { } }; -static const wchar_t *target_modules[] = { - L"USBIntLED.DLL" -}; - -static const size_t target_modules_len = _countof(target_modules); - HRESULT elisabeth_hook_init() { dll_hook_insert_hooks(NULL); - setupapi_add_phantom_dev(&elisabeth_guid, L"$ftdi"); - - InitializeCriticalSection(&elisabeth_lock); - - uart_init(&elisabeth_uart, 1); - elisabeth_uart.written.bytes = elisabeth_written_bytes; - elisabeth_uart.written.nbytes = sizeof(elisabeth_written_bytes); - elisabeth_uart.readable.bytes = elisabeth_readable_bytes; - elisabeth_uart.readable.nbytes = sizeof(elisabeth_readable_bytes); - - return iohook_push_handler(elisabeth_handle_irp); -} - -static HRESULT elisabeth_handle_irp(struct irp *irp) -{ - HRESULT hr; - - assert(irp != NULL); - - if (!uart_match_irp(&elisabeth_uart, irp)) { - return iohook_invoke_next(irp); - } - - EnterCriticalSection(&elisabeth_lock); - hr = elisabeth_handle_irp_locked(irp); - LeaveCriticalSection(&elisabeth_lock); - - return hr; -} - -static HRESULT elisabeth_handle_irp_locked(struct irp *irp) -{ - //union elisabeth_req_any req; - struct iobuf req_iobuf; - HRESULT hr; - - if (irp->op == IRP_OP_OPEN) { - dprintf("Elisabeth: Starting backend\n"); - hr = mercury_dll.elisabeth_init(); - - if (FAILED(hr)) { - dprintf("Elisabeth: Backend error: %x\n", (int) hr); - - return hr; - } - } - - hr = uart_handle_irp(&elisabeth_uart, irp); - - if (FAILED(hr) || irp->op != IRP_OP_WRITE) { - return hr; - } - - for (;;) { - - //req_iobuf.bytes = req.bytes; - //req_iobuf.nbytes = sizeof(req.bytes); - //req_iobuf.pos = 0; - - /*hr = elisabeth_frame_decode(&req_iobuf, &elisabeth_uart.written); - - if (hr != S_OK) { - if (FAILED(hr)) { - dprintf("Elisabeth: Deframe error: %x\n", (int) hr); - } - - return hr; - } - - hr = elisabeth_req_dispatch(&req); - - if (FAILED(hr)) { - dprintf("Elisabeth: Processing error: %x\n", (int) hr); - }*/ - } } static void dll_hook_insert_hooks(HMODULE target) @@ -140,58 +46,6 @@ static void dll_hook_insert_hooks(HMODULE target) _countof(win32_hooks)); } -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("Elisabeth: Loaded %S\n", target_module); - - dll_hook_insert_hooks(result); - setupapi_hook_insert_hooks(result); - } - } - - return result; -} - FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) { uintptr_t ordinal = (uintptr_t) name; @@ -200,8 +54,17 @@ FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) if (ordinal > 0xFFFF) { /* Import by name */ - //dprintf("Elisabeth: GetProcAddress %s is %p\n", name, result); + if (strcmp(name, "USBIntLED_Init") == 0) { + result = my_USBIntLED_Init; + } } return result; } + +/* Intercept the call to initialize the LED board. */ +static int my_USBIntLED_Init() +{ + dprintf("Elisabeth: my_USBIntLED_Init hit!\n"); + return 1; +} diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index d553ba8..9692c34 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -1,11 +1,3 @@ #pragma once -#include - -DEFINE_GUID( - elisabeth_guid, - 0x219D0508, - 0x57A8, - 0x4FF5, - 0x97, 0x0A1, 0x0BD, 0x86, 0x58, 0x7C, 0x6C, 0x7E); HRESULT elisabeth_hook_init(); diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c index 1962709..3751750 100644 --- a/mercuryhook/mercury-dll.c +++ b/mercuryhook/mercury-dll.c @@ -22,8 +22,8 @@ const struct dll_bind_sym mercury_dll_syms[] = { .sym = "mercury_io_get_gamebtns", .off = offsetof(struct mercury_dll, get_gamebtns), }, { - .sym = "mercury_io_elisabeth_init", - .off = offsetof(struct mercury_dll, elisabeth_init), + .sym = "mercury_io_touch_init", + .off = offsetof(struct mercury_dll, touch_init), } }; diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h index 1942f0e..a942c9d 100644 --- a/mercuryhook/mercury-dll.h +++ b/mercuryhook/mercury-dll.h @@ -10,7 +10,7 @@ struct mercury_dll { HRESULT (*poll)(void); void (*get_opbtns)(uint8_t *opbtn); void (*get_gamebtns)(uint16_t *player1, uint16_t *player2); - HRESULT (*elisabeth_init)(void); + HRESULT (*touch_init)(void); }; struct mercury_dll_config { diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def index d331e90..1b44346 100644 --- a/mercuryhook/mercuryhook.def +++ b/mercuryhook/mercuryhook.def @@ -15,6 +15,6 @@ EXPORTS mercury_io_get_api_version mercury_io_get_gamebtns mercury_io_get_opbtns - mercury_io_elisabeth_init + mercury_io_touch_init mercury_io_init mercury_io_poll diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 4c0b2a5..39f4d29 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -48,7 +48,7 @@ void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) } -HRESULT mercury_io_elisabeth_init(void) +HRESULT mercury_io_touch_init(void) { return S_OK; } diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 827efb5..d06e37c 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -66,4 +66,4 @@ void mercury_io_get_opbtns(uint8_t *opbtn); void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2); -HRESULT mercury_io_elisabeth_init(void); +HRESULT mercury_io_touch_init(void); From c0a5f97ea972db013778c9931a4dbac0b764f61f Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Tue, 4 Jan 2022 03:46:30 -0500 Subject: [PATCH 138/175] added touch emulator --- mercuryhook/config.c | 15 ++++ mercuryhook/config.h | 2 + mercuryhook/dllmain.c | 3 + mercuryhook/meson.build | 4 +- mercuryhook/touch.c | 164 ++++++++++++++++++++++++++++++++++++++++ mercuryhook/touch.h | 11 +++ mercuryio/config.c | 3 - 7 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 mercuryhook/touch.c create mode 100644 mercuryhook/touch.h diff --git a/mercuryhook/config.c b/mercuryhook/config.c index 9a61aad..3b31afd 100644 --- a/mercuryhook/config.c +++ b/mercuryhook/config.c @@ -27,6 +27,20 @@ void mercury_dll_config_load( filename); } +void touch_config_load( + struct touch_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileIntW( + L"touch", + L"enable", + 1, + filename); +} + void mercury_hook_config_load( struct mercury_hook_config *cfg, const wchar_t *filename) @@ -40,4 +54,5 @@ void mercury_hook_config_load( io4_config_load(&cfg->io4, filename); gfx_config_load(&cfg->gfx, filename); mercury_dll_config_load(&cfg->dll, filename); + touch_config_load(&cfg->touch, filename); } diff --git a/mercuryhook/config.h b/mercuryhook/config.h index 627e69c..1ead488 100644 --- a/mercuryhook/config.h +++ b/mercuryhook/config.h @@ -8,6 +8,7 @@ #include "hooklib/gfx.h" #include "mercuryhook/mercury-dll.h" +#include "mercuryhook/touch.h" #include "platform/config.h" @@ -18,6 +19,7 @@ struct mercury_hook_config { struct io4_config io4; struct gfx_config gfx; struct mercury_dll_config dll; + struct touch_config touch; }; void mercury_dll_config_load( diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c index 3c7786d..7296c61 100644 --- a/mercuryhook/dllmain.c +++ b/mercuryhook/dllmain.c @@ -13,6 +13,7 @@ #include "mercuryhook/io4.h" #include "mercuryhook/mercury-dll.h" #include "mercuryhook/elisabeth.h" +#include "mercuryhook/touch.h" #include "platform/platform.h" @@ -79,6 +80,8 @@ static DWORD CALLBACK mercury_pre_startup(void) /* Start elisabeth Hooks for the LED and IO Board DLLs */ elisabeth_hook_init(); + touch_hook_init(&mercury_hook_cfg.touch); + /* Initialize debug helpers */ spike_hook_init(L".\\segatools.ini"); diff --git a/mercuryhook/meson.build b/mercuryhook/meson.build index 6269e27..ffa0f88 100644 --- a/mercuryhook/meson.build +++ b/mercuryhook/meson.build @@ -26,6 +26,8 @@ shared_library( 'mercury-dll.c', 'mercury-dll.h', 'elisabeth.h', - 'elisabeth.c' + 'elisabeth.c', + 'touch.h', + 'touch.c' ], ) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c new file mode 100644 index 0000000..60e7d96 --- /dev/null +++ b/mercuryhook/touch.c @@ -0,0 +1,164 @@ +#include + +#include +#include +#include +#include +#include + +#include "board/slider-cmd.h" +#include "board/slider-frame.h" + +#include "mercuryhook/mercury-dll.h" +#include "mercuryhook/touch.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT touch0_handle_irp(struct irp *irp); +static HRESULT touch0_handle_irp_locked(struct irp *irp); + +static HRESULT touch_req_dispatch(const union slider_req_any *req); + +static void touch_res_auto_scan(const uint8_t *state); + +static CRITICAL_SECTION touch0_lock; +static struct uart touch0_uart; +static uint8_t touch0_written_bytes[520]; +static uint8_t touch0_readable_bytes[520]; + +static CRITICAL_SECTION touch1_lock; +static struct uart touch1_uart; +static uint8_t touch1_written_bytes[520]; +static uint8_t touch1_readable_bytes[520]; + +HRESULT touch_hook_init(const struct touch_config *cfg) +{ + assert(cfg != NULL); + assert(mercury_dll.touch_init != NULL); + + // not sure why this always returns false... + /*if (!cfg->enable) { + return S_FALSE; + }*/ + + InitializeCriticalSection(&touch0_lock); + InitializeCriticalSection(&touch1_lock); + dprintf("Wacca touch: init\n"); + + uart_init(&touch0_uart, 3); + touch0_uart.written.bytes = touch0_written_bytes; + touch0_uart.written.nbytes = sizeof(touch0_written_bytes); + touch0_uart.readable.bytes = touch0_readable_bytes; + touch0_uart.readable.nbytes = sizeof(touch0_readable_bytes); + + uart_init(&touch1_uart, 4); + touch1_uart.written.bytes = touch1_written_bytes; + touch1_uart.written.nbytes = sizeof(touch1_written_bytes); + touch1_uart.readable.bytes = touch1_readable_bytes; + touch1_uart.readable.nbytes = sizeof(touch1_readable_bytes); + + return iohook_push_handler(touch0_handle_irp); +} + +static HRESULT touch0_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&touch0_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&touch0_lock); + hr = touch0_handle_irp_locked(irp); + LeaveCriticalSection(&touch0_lock); + + return hr; +} + +static HRESULT touch0_handle_irp_locked(struct irp *irp) +{ + union slider_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Wacca touch: Starting backend\n"); + hr = mercury_dll.touch_init(); + + if (FAILED(hr)) { + dprintf("Wacca touch: Backend error: %x\n", (int) hr); + + return hr; + } + } + + hr = uart_handle_irp(&touch0_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&touch0_uart.written); +#endif + + req_iobuf.bytes = req.bytes; + req_iobuf.nbytes = sizeof(req.bytes); + req_iobuf.pos = 0; + + hr = slider_frame_decode(&req_iobuf, &touch0_uart.written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Wacca touch: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = touch_req_dispatch(&req); + + if (FAILED(hr)) { + dprintf("Wacca touch: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT touch_req_dispatch(const union slider_req_any *req) +{ + switch (req->hdr.cmd) { + default: + dprintf("Unhandled command %02x\n", req->hdr.cmd); + + return S_OK; + } +} + +static void slider_res_auto_scan(const uint8_t *state) +{ + struct slider_resp_auto_scan resp; + + resp.hdr.sync = SLIDER_FRAME_SYNC; + resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN; + resp.hdr.nbytes = sizeof(resp.pressure); + memcpy(resp.pressure, state, sizeof(resp.pressure)); + + EnterCriticalSection(&touch0_lock); + slider_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + LeaveCriticalSection(&touch0_lock); +} diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h new file mode 100644 index 0000000..c3601fe --- /dev/null +++ b/mercuryhook/touch.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include + +struct touch_config { + bool enable; +}; + +HRESULT touch_hook_init(const struct touch_config *cfg); diff --git a/mercuryio/config.c b/mercuryio/config.c index 3b62789..d2d173b 100644 --- a/mercuryio/config.c +++ b/mercuryio/config.c @@ -14,9 +14,6 @@ void mercury_io_config_load( struct mercury_io_config *cfg, const wchar_t *filename) { - wchar_t key[16]; - int i; - assert(cfg != NULL); assert(filename != NULL); From 802b827ab70125a13a7fb563fc3650b56a355909 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 5 Jan 2022 02:03:55 -0500 Subject: [PATCH 139/175] broke everything do not use --- mercuryhook/touch.c | 150 +++++++++++++++++++++++++++++++++++--------- mercuryhook/touch.h | 20 ++++++ 2 files changed, 139 insertions(+), 31 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 60e7d96..f44f744 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -16,16 +16,21 @@ #include "hook/iohook.h" #include "hooklib/uart.h" +#include "hooklib/fdshark.h" #include "util/dprintf.h" #include "util/dump.h" static HRESULT touch0_handle_irp(struct irp *irp); static HRESULT touch0_handle_irp_locked(struct irp *irp); +static HRESULT touch1_handle_irp(struct irp *irp); +static HRESULT touch1_handle_irp_locked(struct irp *irp); -static HRESULT touch_req_dispatch(const union slider_req_any *req); +static HRESULT touch_req_dispatch(const struct touch_req *req); -static void touch_res_auto_scan(const uint8_t *state); +static HRESULT touch_frame_decode(struct touch_req *dest, const struct iobuf *iobuf, int side); + +static HRESULT touch_handle_get_rev_date(const struct touch_req *req); static CRITICAL_SECTION touch0_lock; static struct uart touch0_uart; @@ -63,7 +68,9 @@ HRESULT touch_hook_init(const struct touch_config *cfg) touch1_uart.readable.bytes = touch1_readable_bytes; touch1_uart.readable.nbytes = sizeof(touch1_readable_bytes); - return iohook_push_handler(touch0_handle_irp); + iohook_push_handler(touch0_handle_irp); + iohook_push_handler(touch1_handle_irp); + return S_OK; } static HRESULT touch0_handle_irp(struct irp *irp) @@ -85,12 +92,11 @@ static HRESULT touch0_handle_irp(struct irp *irp) static HRESULT touch0_handle_irp_locked(struct irp *irp) { - union slider_req_any req; - struct iobuf req_iobuf; + struct touch_req req; HRESULT hr; if (irp->op == IRP_OP_OPEN) { - dprintf("Wacca touch: Starting backend\n"); + dprintf("Wacca touch0: Starting backend\n"); hr = mercury_dll.touch_init(); if (FAILED(hr)) { @@ -107,16 +113,11 @@ static HRESULT touch0_handle_irp_locked(struct irp *irp) } for (;;) { -#if 0 - dprintf("TX Buffer:\n"); +#if 1 + dprintf("TX0 Buffer:\n"); dump_iobuf(&touch0_uart.written); #endif - - req_iobuf.bytes = req.bytes; - req_iobuf.nbytes = sizeof(req.bytes); - req_iobuf.pos = 0; - - hr = slider_frame_decode(&req_iobuf, &touch0_uart.written); + hr = touch_frame_decode(&req, &touch0_uart.written, 0); if (hr != S_OK) { if (FAILED(hr)) { @@ -126,11 +127,69 @@ static HRESULT touch0_handle_irp_locked(struct irp *irp) return hr; } -#if 0 - dprintf("Deframe Buffer:\n"); - dump_iobuf(&req_iobuf); + hr = touch_req_dispatch(&req); + + if (FAILED(hr)) { + dprintf("Wacca touch: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT touch1_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&touch1_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&touch1_lock); + hr = touch1_handle_irp_locked(irp); + LeaveCriticalSection(&touch1_lock); + + return hr; +} + +static HRESULT touch1_handle_irp_locked(struct irp *irp) +{ + struct touch_req req; + HRESULT hr; + + if (irp->op == IRP_OP_OPEN) { + dprintf("Wacca touch1: Starting backend\n"); + hr = mercury_dll.touch_init(); + + if (FAILED(hr)) { + dprintf("Wacca touch: Backend error: %x\n", (int) hr); + + return hr; + } + } + + hr = uart_handle_irp(&touch1_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 1 + dprintf("TX1 Buffer:\n"); + dump_iobuf(&touch1_uart.written); #endif + hr = touch_frame_decode(&req, &touch0_uart.written, 1); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("Wacca touch: Deframe error: %x\n", (int) hr); + } + + return hr; + } + hr = touch_req_dispatch(&req); if (FAILED(hr)) { @@ -139,26 +198,55 @@ static HRESULT touch0_handle_irp_locked(struct irp *irp) } } -static HRESULT touch_req_dispatch(const union slider_req_any *req) +static HRESULT touch_req_dispatch(const struct touch_req *req) { - switch (req->hdr.cmd) { + switch (req->cmd) { + case CMD_GET_REV_DATE: + return touch_handle_get_rev_date(req); default: - dprintf("Unhandled command %02x\n", req->hdr.cmd); + dprintf("Wacca touch: Unhandled command %02x\n", req->cmd); return S_OK; } } -static void slider_res_auto_scan(const uint8_t *state) +static HRESULT touch_handle_get_rev_date(const struct touch_req *req) { - struct slider_resp_auto_scan resp; - - resp.hdr.sync = SLIDER_FRAME_SYNC; - resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN; - resp.hdr.nbytes = sizeof(resp.pressure); - memcpy(resp.pressure, state, sizeof(resp.pressure)); - - EnterCriticalSection(&touch0_lock); - slider_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); - LeaveCriticalSection(&touch0_lock); + dprintf("Wacca Touch%d: Get board rev date\n", req->side); + if (req->side) { + touch0_uart.readable.bytes[0] = 0xa0; + touch0_uart.readable.bytes[1] = 0x31; + touch0_uart.readable.bytes[2] = 0x39; + touch0_uart.readable.bytes[3] = 0x30; + touch0_uart.readable.bytes[4] = 0x35; + touch0_uart.readable.bytes[5] = 0x32; + touch0_uart.readable.bytes[6] = 0x33; + touch0_uart.readable.bytes[7] = 0x2c; + } + else { + touch1_uart.readable.bytes[0] = 0xa0; + touch1_uart.readable.bytes[1] = 0x31; + touch1_uart.readable.bytes[2] = 0x39; + touch1_uart.readable.bytes[3] = 0x30; + touch1_uart.readable.bytes[4] = 0x35; + touch1_uart.readable.bytes[5] = 0x32; + touch1_uart.readable.bytes[6] = 0x33; + touch1_uart.readable.bytes[7] = 0x2c; + } + return S_OK; +} + +static HRESULT touch_frame_decode(struct touch_req *dest, const struct iobuf *iobuf, int side) +{ + dest->side = side; + dest->cmd = iobuf->bytes[0]; + dest->data_length = _countof(iobuf->bytes) - 1; + + if (dest->data_length > 0) { + for (int i = 1; i < dest->data_length; i++) { + dest->data[i-1] = iobuf->bytes[i]; + } + } + + return S_OK; } diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h index c3601fe..dd2f760 100644 --- a/mercuryhook/touch.h +++ b/mercuryhook/touch.h @@ -8,4 +8,24 @@ struct touch_config { bool enable; }; +enum touch_cmd { + CMD_GET_REV_DATE = 0xa0, + CMD_STARTUP = 0x72, + CMD_GET_REV_DATE_DETAIL = 0xa8, + CMD_UNKNOWN1 = 0xa2, + CMD_UNKNOWN2 = 0x94, + CMD_START_AUTO = 0xc9 +}; + +struct touch_req { + int side; + int cmd; // First byte is the command byte + int data[256]; // rest of the data goes here + int data_length; // Size of the data including command byte +}; + +struct touch_input_frame { + int data[36]; +}; + HRESULT touch_hook_init(const struct touch_config *cfg); From 5bfd5ae850b38df81b381f1364b05b156dfe8e74 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 5 Jan 2022 05:07:29 -0500 Subject: [PATCH 140/175] added werror, slider doesn't freeze the game --- mercuryhook/elisabeth.c | 3 +- mercuryhook/touch.c | 91 +++++++++++++++++++---------------------- meson.build | 9 +++- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index c680ca7..7e2f268 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -35,6 +35,7 @@ static const struct hook_symbol win32_hooks[] = { HRESULT elisabeth_hook_init() { dll_hook_insert_hooks(NULL); + return S_OK; } static void dll_hook_insert_hooks(HMODULE target) @@ -55,7 +56,7 @@ FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) if (ordinal > 0xFFFF) { /* Import by name */ if (strcmp(name, "USBIntLED_Init") == 0) { - result = my_USBIntLED_Init; + result = (FARPROC) my_USBIntLED_Init; } } diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index f44f744..2c2c594 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -21,16 +21,16 @@ #include "util/dprintf.h" #include "util/dump.h" -static HRESULT touch0_handle_irp(struct irp *irp); +static HRESULT touch_handle_irp(struct irp *irp); static HRESULT touch0_handle_irp_locked(struct irp *irp); -static HRESULT touch1_handle_irp(struct irp *irp); static HRESULT touch1_handle_irp_locked(struct irp *irp); static HRESULT touch_req_dispatch(const struct touch_req *req); -static HRESULT touch_frame_decode(struct touch_req *dest, const struct iobuf *iobuf, int side); +static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side); static HRESULT touch_handle_get_rev_date(const struct touch_req *req); +static HRESULT touch_handle_startup(const struct touch_req *req); static CRITICAL_SECTION touch0_lock; static struct uart touch0_uart; @@ -68,25 +68,29 @@ HRESULT touch_hook_init(const struct touch_config *cfg) touch1_uart.readable.bytes = touch1_readable_bytes; touch1_uart.readable.nbytes = sizeof(touch1_readable_bytes); - iohook_push_handler(touch0_handle_irp); - iohook_push_handler(touch1_handle_irp); - return S_OK; + return iohook_push_handler(touch_handle_irp); } -static HRESULT touch0_handle_irp(struct irp *irp) +static HRESULT touch_handle_irp(struct irp *irp) { HRESULT hr; assert(irp != NULL); - if (!uart_match_irp(&touch0_uart, irp)) { + if (uart_match_irp(&touch0_uart, irp)) { + EnterCriticalSection(&touch0_lock); + hr = touch0_handle_irp_locked(irp); + LeaveCriticalSection(&touch0_lock); + } + else if (uart_match_irp(&touch1_uart, irp)) { + EnterCriticalSection(&touch1_lock); + hr = touch1_handle_irp_locked(irp); + LeaveCriticalSection(&touch1_lock); + } + else { return iohook_invoke_next(irp); } - EnterCriticalSection(&touch0_lock); - hr = touch0_handle_irp_locked(irp); - LeaveCriticalSection(&touch0_lock); - return hr; } @@ -132,26 +136,11 @@ static HRESULT touch0_handle_irp_locked(struct irp *irp) if (FAILED(hr)) { dprintf("Wacca touch: Processing error: %x\n", (int) hr); } + + return hr; } } -static HRESULT touch1_handle_irp(struct irp *irp) -{ - HRESULT hr; - - assert(irp != NULL); - - if (!uart_match_irp(&touch1_uart, irp)) { - return iohook_invoke_next(irp); - } - - EnterCriticalSection(&touch1_lock); - hr = touch1_handle_irp_locked(irp); - LeaveCriticalSection(&touch1_lock); - - return hr; -} - static HRESULT touch1_handle_irp_locked(struct irp *irp) { struct touch_req req; @@ -180,7 +169,7 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp) dump_iobuf(&touch1_uart.written); #endif - hr = touch_frame_decode(&req, &touch0_uart.written, 1); + hr = touch_frame_decode(&req, &touch1_uart.written, 1); if (hr != S_OK) { if (FAILED(hr)) { @@ -195,6 +184,8 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp) if (FAILED(hr)) { dprintf("Wacca touch: Processing error: %x\n", (int) hr); } + + return hr; } } @@ -203,6 +194,8 @@ static HRESULT touch_req_dispatch(const struct touch_req *req) switch (req->cmd) { case CMD_GET_REV_DATE: return touch_handle_get_rev_date(req); + case CMD_STARTUP: + return touch_handle_startup(req); default: dprintf("Wacca touch: Unhandled command %02x\n", req->cmd); @@ -213,40 +206,40 @@ static HRESULT touch_req_dispatch(const struct touch_req *req) static HRESULT touch_handle_get_rev_date(const struct touch_req *req) { dprintf("Wacca Touch%d: Get board rev date\n", req->side); - if (req->side) { - touch0_uart.readable.bytes[0] = 0xa0; - touch0_uart.readable.bytes[1] = 0x31; - touch0_uart.readable.bytes[2] = 0x39; - touch0_uart.readable.bytes[3] = 0x30; - touch0_uart.readable.bytes[4] = 0x35; - touch0_uart.readable.bytes[5] = 0x32; - touch0_uart.readable.bytes[6] = 0x33; - touch0_uart.readable.bytes[7] = 0x2c; + if (req->side == 0) { + // TODO: a0 31 39 30 35 32 33 2c } else { - touch1_uart.readable.bytes[0] = 0xa0; - touch1_uart.readable.bytes[1] = 0x31; - touch1_uart.readable.bytes[2] = 0x39; - touch1_uart.readable.bytes[3] = 0x30; - touch1_uart.readable.bytes[4] = 0x35; - touch1_uart.readable.bytes[5] = 0x32; - touch1_uart.readable.bytes[6] = 0x33; - touch1_uart.readable.bytes[7] = 0x2c; + // TODO: see above } return S_OK; } -static HRESULT touch_frame_decode(struct touch_req *dest, const struct iobuf *iobuf, int side) +static HRESULT touch_handle_startup(const struct touch_req *req) +{ + dprintf("Wacca Touch%d: Startup\n", req->side); + if (req->side == 0) { + // TODO: 20 20 20 20 30 20 20 20 20 30 20 20 20 20 31 20 20 20 20 32 20 20 20 20 33 20 20 20 20 34 20 20 20 20 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 31 20 20 20 31 31 20 20 20 31 31 0d + } + else { + // TODO: see above + } + return S_OK; +} + +static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side) { dest->side = side; dest->cmd = iobuf->bytes[0]; - dest->data_length = _countof(iobuf->bytes) - 1; + iobuf->pos--; + dest->data_length = iobuf->pos; if (dest->data_length > 0) { for (int i = 1; i < dest->data_length; i++) { dest->data[i-1] = iobuf->bytes[i]; } } + iobuf->pos -= dest->data_length; return S_OK; } diff --git a/meson.build b/meson.build index f143e72..d24723f 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,11 @@ -project('segatools', 'c', version: '0.1.0') +project( + 'segatools', + 'c', + version: '0.1.0', + default_options: [ + 'werror=true', + ], +) add_project_arguments( '-DCOBJMACROS', From 0d3e93ce82487d856175c834beaceee6158a306d Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Fri, 7 Jan 2022 21:16:33 -0500 Subject: [PATCH 141/175] IO device shows as connected, no auto read tho --- mercuryhook/touch.c | 224 +++++++++++++++++++++++++++++++++++++++++--- mercuryhook/touch.h | 56 +++++++++-- 2 files changed, 259 insertions(+), 21 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 2c2c594..66c483a 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -28,9 +28,17 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp); static HRESULT touch_req_dispatch(const struct touch_req *req); static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side); +static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes); +static uint8_t calc_checksum(const void *ptr, size_t nbytes); static HRESULT touch_handle_get_rev_date(const struct touch_req *req); static HRESULT touch_handle_startup(const struct touch_req *req); +static HRESULT touch_handle_get_rev_date_detail(const struct touch_req *req); +static HRESULT touch_handle_mystery1(const struct touch_req *req); +static HRESULT touch_handle_mystery2(const struct touch_req *req); +static HRESULT touch_handle_start_auto_scan(const struct touch_req *req); + +uint8_t input_frame_count = 0x7b; static CRITICAL_SECTION touch0_lock; static struct uart touch0_uart; @@ -43,9 +51,9 @@ static uint8_t touch1_written_bytes[520]; static uint8_t touch1_readable_bytes[520]; HRESULT touch_hook_init(const struct touch_config *cfg) -{ +{ assert(cfg != NULL); - assert(mercury_dll.touch_init != NULL); + assert(mercury_dll.touch_init != NULL); // not sure why this always returns false... /*if (!cfg->enable) { @@ -82,7 +90,7 @@ static HRESULT touch_handle_irp(struct irp *irp) hr = touch0_handle_irp_locked(irp); LeaveCriticalSection(&touch0_lock); } - else if (uart_match_irp(&touch1_uart, irp)) { + else if (uart_match_irp(&touch1_uart, irp)) { EnterCriticalSection(&touch1_lock); hr = touch1_handle_irp_locked(irp); LeaveCriticalSection(&touch1_lock); @@ -117,7 +125,7 @@ static HRESULT touch0_handle_irp_locked(struct irp *irp) } for (;;) { -#if 1 +#if 0 dprintf("TX0 Buffer:\n"); dump_iobuf(&touch0_uart.written); #endif @@ -164,7 +172,7 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp) } for (;;) { -#if 1 +#if 0 dprintf("TX1 Buffer:\n"); dump_iobuf(&touch1_uart.written); #endif @@ -196,37 +204,191 @@ static HRESULT touch_req_dispatch(const struct touch_req *req) return touch_handle_get_rev_date(req); case CMD_STARTUP: return touch_handle_startup(req); + case CMD_GET_REV_DATE_DETAIL: + return touch_handle_get_rev_date_detail(req); + case CMD_MYSTERY1: + return touch_handle_mystery1(req); + case CMD_MYSTERY2: + return touch_handle_mystery2(req); + case CMD_START_AUTO_SCAN: + return touch_handle_start_auto_scan(req); + case CMD_BEGIN_WRITE: + dprintf("Wacca touch: Begin write for side %d\n", req->side); + return S_OK; + case CMD_NEXT_WRITE: + dprintf("Wacca touch: continue write for side %d\n", req->side); + return S_OK; default: dprintf("Wacca touch: Unhandled command %02x\n", req->cmd); - return S_OK; } } static HRESULT touch_handle_get_rev_date(const struct touch_req *req) { + struct touch_resp_get_rev_date resp; + HRESULT hr; + uint8_t rev[6] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33 }; + dprintf("Wacca Touch%d: Get board rev date\n", req->side); + + resp.cmd = 0xa0; + memcpy(resp.data, rev, sizeof(rev)); + //resp.data = rev; + if (req->side == 0) { - // TODO: a0 31 39 30 35 32 33 2c + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); } else { - // TODO: see above + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); } - return S_OK; + return hr; } +/* TODO: Very ugly please make better before upstreaming */ static HRESULT touch_handle_startup(const struct touch_req *req) { + struct touch_resp_startup resp; + HRESULT hr; + uint8_t *rev; + dprintf("Wacca Touch%d: Startup\n", req->side); - if (req->side == 0) { - // TODO: 20 20 20 20 30 20 20 20 20 30 20 20 20 20 31 20 20 20 20 32 20 20 20 20 33 20 20 20 20 34 20 20 20 20 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 35 20 20 20 31 31 20 20 20 31 31 20 20 20 31 31 0d + + switch (req->data[2]) { + case 0x30: + rev = (uint8_t[80]) { 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, + 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x20, 0x32, 0x20, 0x20, 0x20, 0x20, + 0x33, 0x20, 0x20, 0x20, 0x20, 0x34, 0x20, 0x20, 0x20, 0x20, 0x35, 0x20, 0x20, + 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, + 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, + 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, + 0x20, 0x20, 0x31, 0x31 }; + break; + case 0x31: + rev = (uint8_t[80]) { 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, + 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x31, 0x32, 0x38, 0x20, 0x20, 0x31, 0x30, + 0x33, 0x20, 0x20, 0x31, 0x30, 0x33, 0x20, 0x20, 0x31, 0x31, 0x35, 0x20, 0x20, + 0x31, 0x33, 0x38, 0x20, 0x20, 0x31, 0x32, 0x37, 0x20, 0x20, 0x31, 0x30, 0x33, + 0x20, 0x20, 0x31, 0x30, 0x35, 0x20, 0x20, 0x31, 0x31, 0x31, 0x20, 0x20, 0x31, + 0x32, 0x36, 0x20, 0x20, 0x31, 0x31, 0x33, 0x20, 0x20, 0x20, 0x39, 0x35, 0x20, + 0x20, 0x31, 0x30, 0x30 }; + break; + case 0x33: + rev = (uint8_t[80]) { 0x20, 0x20, 0x31, 0x30, 0x31, 0x20, 0x20, 0x31, 0x31, 0x35, 0x20, + 0x20, 0x20, 0x39, 0x38, 0x20, 0x20, 0x20, 0x38, 0x36, 0x20, 0x20, 0x20, 0x37, + 0x36, 0x20, 0x20, 0x20, 0x36, 0x37, 0x20, 0x20, 0x20, 0x36, 0x38, 0x20, 0x20, + 0x20, 0x34, 0x38, 0x20, 0x20, 0x31, 0x31, 0x37, 0x20, 0x20, 0x20, 0x20, 0x30, + 0x20, 0x20, 0x20, 0x38, 0x32, 0x20, 0x20, 0x31, 0x35, 0x34, 0x20, 0x20, 0x20, + 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x36, 0x20, 0x20, 0x20, 0x33, 0x35, 0x20, + 0x20, 0x20, 0x20, 0x34 }; + break; + default: + dprintf("Wacca touch: BAD STARTUP REQUEST %2hx\n", req->data[2]); + return 1; + } + + memcpy(resp.data, rev, 80 * sizeof(uint8_t)); + + if (req->side == 0) { + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); } else { - // TODO: see above + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); } - return S_OK; + return hr; } +static HRESULT touch_handle_get_rev_date_detail(const struct touch_req *req) +{ + struct touch_resp_get_rev_date_detail resp; + HRESULT hr; + uint8_t rev[43] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33, 0x52, 0x31, + 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, + 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, + 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34 }; + + dprintf("Wacca Touch%d: get rev date detail\n", req->side); + + resp.cmd = 0xa8; + memcpy(resp.data, rev, sizeof(rev)); + + if (req->side == 0) { + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + } + else { + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + } + return hr; + +} + +static HRESULT touch_handle_mystery1(const struct touch_req *req) +{ + struct touch_resp_mystery1 resp; + HRESULT hr; + + dprintf("Wacca Touch%d: mystery command 1\n", req->side); + + resp.cmd = 0xa2; + resp.data = 0x3f; + + if (req->side == 0) { + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + } + else { + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + } + return hr; +} + +static HRESULT touch_handle_mystery2(const struct touch_req *req) +{ + struct touch_resp_mystery2 resp; + HRESULT hr; + + dprintf("Wacca Touch%d: mystery command 2\n", req->side); + + resp.cmd = 0x94; + resp.data = 0; + + if (req->side == 0) { + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + } + else { + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + } + return hr; +} + +static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) +{ + struct touch_resp_start_auto resp; + HRESULT hr; + uint8_t data1[24] = { 0 }; + uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; + + dprintf("Wacca Touch%d: Start Auto\n", req->side); + + resp.cmd = 0x9c; + resp.data = 0; + resp.checksum = 0x49; + + resp.frame.cmd= 0x81; + resp.frame.count = input_frame_count++; + memcpy(resp.frame.data1, data1, sizeof(data1)); + memcpy(resp.frame.data2, data2, sizeof(data2)); + resp.frame.checksum = calc_checksum(&resp.frame, sizeof(resp.frame)); + + if (req->side == 0) { + hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + } + else { + hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + } + return hr; +} + +/* Decodes the response into a struct that's easier to work with. */ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side) { dest->side = side; @@ -243,3 +405,39 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i return S_OK; } + +/* Encode and send the response. + * The last byte of every response is a checksum. + * This checksum is calculated by bitwise XORing + * every byte in the response, then dropping the MSB. + * Thanks the CrazyRedMachine for figuring that out!! + */ +static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) +{ + const uint8_t *src; + uint8_t checksum = 0; + + src = ptr; + + for (size_t i = 0; i < nbytes; i++) { + dest->bytes[dest->pos++] = src[i]; + checksum = checksum^(src[i]); + } + + dest->bytes[dest->pos++] = checksum&0x7f; + return S_OK; +} + +static uint8_t calc_checksum(const void *ptr, size_t nbytes) +{ + const uint8_t *src; + uint8_t checksum = 0; + + src = ptr; + + for (size_t i = 0; i < nbytes; i++) { + checksum = checksum^(src[i]); + } + + return checksum&0x7f; +} diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h index dd2f760..77784dc 100644 --- a/mercuryhook/touch.h +++ b/mercuryhook/touch.h @@ -12,20 +12,60 @@ enum touch_cmd { CMD_GET_REV_DATE = 0xa0, CMD_STARTUP = 0x72, CMD_GET_REV_DATE_DETAIL = 0xa8, - CMD_UNKNOWN1 = 0xa2, - CMD_UNKNOWN2 = 0x94, - CMD_START_AUTO = 0xc9 + CMD_MYSTERY1 = 0xa2, + CMD_MYSTERY2 = 0x94, + CMD_START_AUTO_SCAN = 0xc9, + CMD_BEGIN_WRITE = 0x77, + CMD_NEXT_WRITE = 0x20 }; struct touch_req { - int side; - int cmd; // First byte is the command byte - int data[256]; // rest of the data goes here - int data_length; // Size of the data including command byte + uint8_t side; // COM3 or COM4 + uint8_t cmd; // First byte is the command byte + uint8_t data[256]; // rest of the data goes here + uint8_t data_length; // Size of the data including command byte }; +// The checksum is only calculated when we're about to send it so +// it's not part of any of these structs. Just note that the last +// byte of every response is a checksum struct touch_input_frame { - int data[36]; + uint8_t cmd; + uint8_t data1[24]; + uint8_t data2[9]; + uint8_t count; + uint8_t checksum; +}; + +struct touch_resp_get_rev_date { + uint8_t cmd; + uint8_t data[6]; +}; + +struct touch_resp_startup { + uint8_t data[80]; +}; + +struct touch_resp_get_rev_date_detail { + uint8_t cmd; + uint8_t data[43]; +}; + +struct touch_resp_mystery1 { + uint8_t cmd; + uint8_t data; +}; + +struct touch_resp_mystery2 { + uint8_t cmd; + uint8_t data; +}; + +struct touch_resp_start_auto { + uint8_t cmd; + uint8_t data; + uint8_t checksum; + struct touch_input_frame frame; }; HRESULT touch_hook_init(const struct touch_config *cfg); From 1fa485143c7186169a4f9da8b04bcda1bf7f2010 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sat, 8 Jan 2022 02:25:42 -0500 Subject: [PATCH 142/175] + vol btns, / test and service remap, - gfx hook --- dist/mercury/segatools.ini | 8 +++--- mercuryhook/config.c | 4 +-- mercuryhook/config.h | 2 -- mercuryhook/dllmain.c | 1 - mercuryhook/io4.c | 11 ++++++++ mercuryhook/mercury-dll.c | 3 ++ mercuryhook/mercury-dll.h | 3 +- mercuryhook/mercuryhook.def | 2 +- mercuryhook/touch.c | 56 +++++++++++++++++++------------------ mercuryhook/touch.h | 14 +++++----- mercuryio/config.c | 37 ++++++++++++++++++++++-- mercuryio/config.h | 5 ++-- mercuryio/mercuryio.c | 21 ++++++++++++-- mercuryio/mercuryio.h | 15 ++++------ 14 files changed, 120 insertions(+), 62 deletions(-) diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index c439f13..4dac2cf 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -35,8 +35,8 @@ subnet=192.168.250.0 [io4] ; Input API selection for JVS input emulator. -test=0x31 -service=0x32 +test=0x2D +service=0x2E +volup=0x24 +voldown=0x23 -[gfx] -enable=0 diff --git a/mercuryhook/config.c b/mercuryhook/config.c index 3b31afd..a66efa0 100644 --- a/mercuryhook/config.c +++ b/mercuryhook/config.c @@ -5,7 +5,6 @@ #include "hooklib/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx.h" #include "mercuryhook/config.h" @@ -34,7 +33,7 @@ void touch_config_load( assert(cfg != NULL); assert(filename != NULL); - GetPrivateProfileIntW( + cfg->enable = GetPrivateProfileIntW( L"touch", L"enable", 1, @@ -52,7 +51,6 @@ void mercury_hook_config_load( aime_config_load(&cfg->aime, filename); dvd_config_load(&cfg->dvd, filename); io4_config_load(&cfg->io4, filename); - gfx_config_load(&cfg->gfx, filename); mercury_dll_config_load(&cfg->dll, filename); touch_config_load(&cfg->touch, filename); } diff --git a/mercuryhook/config.h b/mercuryhook/config.h index 1ead488..bbfc4a4 100644 --- a/mercuryhook/config.h +++ b/mercuryhook/config.h @@ -5,7 +5,6 @@ #include "board/config.h" #include "hooklib/dvd.h" -#include "hooklib/gfx.h" #include "mercuryhook/mercury-dll.h" #include "mercuryhook/touch.h" @@ -17,7 +16,6 @@ struct mercury_hook_config { struct aime_config aime; struct dvd_config dvd; struct io4_config io4; - struct gfx_config gfx; struct mercury_dll_config dll; struct touch_config touch; }; diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c index 7296c61..9da0b25 100644 --- a/mercuryhook/dllmain.c +++ b/mercuryhook/dllmain.c @@ -38,7 +38,6 @@ static DWORD CALLBACK mercury_pre_startup(void) /* Hook Win32 APIs */ dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod); - gfx_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod); serial_hook_init(); /* Initialize emulation hooks */ diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index 30c7e35..f178ab9 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -34,6 +34,7 @@ HRESULT mercury_io4_hook_init(const struct io4_config *cfg) static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) { uint8_t opbtn; + uint8_t gamebtn; HRESULT hr; assert(mercury_dll.poll != NULL); @@ -49,8 +50,10 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) } opbtn = 0; + gamebtn = 0; mercury_dll.get_opbtns(&opbtn); + mercury_dll.get_gamebtns(&gamebtn); if (opbtn & MERCURY_IO_OPBTN_TEST) { state->buttons[0] |= IO4_BUTTON_TEST; @@ -60,5 +63,13 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) state->buttons[0] |= IO4_BUTTON_SERVICE; } + if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { + state->buttons[0] |= 1 << 1; + } + + if (gamebtn & MERCURY_IO_GAMEBTN_VOL_DOWN) { + state->buttons[0] |= 1 << 0; + } + return S_OK; } diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c index 3751750..19c183a 100644 --- a/mercuryhook/mercury-dll.c +++ b/mercuryhook/mercury-dll.c @@ -24,6 +24,9 @@ const struct dll_bind_sym mercury_dll_syms[] = { }, { .sym = "mercury_io_touch_init", .off = offsetof(struct mercury_dll, touch_init), + }, { + .sym = "mercury_io_touch_start", + .off = offsetof(struct mercury_dll, touch_start), } }; diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h index a942c9d..b28b8ca 100644 --- a/mercuryhook/mercury-dll.h +++ b/mercuryhook/mercury-dll.h @@ -9,8 +9,9 @@ struct mercury_dll { HRESULT (*init)(void); HRESULT (*poll)(void); void (*get_opbtns)(uint8_t *opbtn); - void (*get_gamebtns)(uint16_t *player1, uint16_t *player2); + void (*get_gamebtns)(uint8_t *gamebtn); HRESULT (*touch_init)(void); + HRESULT (*touch_start)(void); }; struct mercury_dll_config { diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def index 1b44346..b12bc79 100644 --- a/mercuryhook/mercuryhook.def +++ b/mercuryhook/mercuryhook.def @@ -1,7 +1,6 @@ LIBRARY mercuryhook EXPORTS - Direct3DCreate9 aime_io_get_api_version aime_io_init aime_io_led_set_color @@ -16,5 +15,6 @@ EXPORTS mercury_io_get_gamebtns mercury_io_get_opbtns mercury_io_touch_init + mercury_io_touch_start mercury_io_init mercury_io_poll diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 66c483a..6c0c942 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -31,9 +31,9 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes); static uint8_t calc_checksum(const void *ptr, size_t nbytes); -static HRESULT touch_handle_get_rev_date(const struct touch_req *req); +static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req); static HRESULT touch_handle_startup(const struct touch_req *req); -static HRESULT touch_handle_get_rev_date_detail(const struct touch_req *req); +static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req); static HRESULT touch_handle_mystery1(const struct touch_req *req); static HRESULT touch_handle_mystery2(const struct touch_req *req); static HRESULT touch_handle_start_auto_scan(const struct touch_req *req); @@ -55,10 +55,9 @@ HRESULT touch_hook_init(const struct touch_config *cfg) assert(cfg != NULL); assert(mercury_dll.touch_init != NULL); - // not sure why this always returns false... - /*if (!cfg->enable) { + if (!cfg->enable) { return S_FALSE; - }*/ + } InitializeCriticalSection(&touch0_lock); InitializeCriticalSection(&touch1_lock); @@ -200,12 +199,12 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp) static HRESULT touch_req_dispatch(const struct touch_req *req) { switch (req->cmd) { - case CMD_GET_REV_DATE: - return touch_handle_get_rev_date(req); + case CMD_GET_SYNC_BOARD_VER: + return touch_handle_get_sync_board_ver(req); case CMD_STARTUP: return touch_handle_startup(req); - case CMD_GET_REV_DATE_DETAIL: - return touch_handle_get_rev_date_detail(req); + case CMD_GET_UNIT_BOARD_VER: + return touch_handle_get_unit_board_ver(req); case CMD_MYSTERY1: return touch_handle_mystery1(req); case CMD_MYSTERY2: @@ -224,17 +223,19 @@ static HRESULT touch_req_dispatch(const struct touch_req *req) } } -static HRESULT touch_handle_get_rev_date(const struct touch_req *req) +static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) { - struct touch_resp_get_rev_date resp; + struct touch_resp_get_sync_board_ver resp; HRESULT hr; - uint8_t rev[6] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33 }; + uint8_t sync_board_ver[6] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33 }; - dprintf("Wacca Touch%d: Get board rev date\n", req->side); + dprintf("Wacca Touch%d: Get sync board version\n", req->side); resp.cmd = 0xa0; - memcpy(resp.data, rev, sizeof(rev)); - //resp.data = rev; + // TODO: Why does strcpy_s here give a runtime warning and not work???? + //strcpy_s(resp.version, sizeof(resp.version), "190523"); + memcpy(resp.version, sync_board_ver, sizeof(sync_board_ver)); + if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -252,7 +253,8 @@ static HRESULT touch_handle_startup(const struct touch_req *req) HRESULT hr; uint8_t *rev; - dprintf("Wacca Touch%d: Startup\n", req->side); + dprintf("Wacca Touch%d: Startup %2hx\n", req->side, req->data[2]); + switch (req->data[2]) { case 0x30: @@ -298,19 +300,19 @@ static HRESULT touch_handle_startup(const struct touch_req *req) return hr; } -static HRESULT touch_handle_get_rev_date_detail(const struct touch_req *req) +static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) { - struct touch_resp_get_rev_date_detail resp; + struct touch_resp_get_unit_board_ver resp; HRESULT hr; - uint8_t rev[43] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33, 0x52, 0x31, + uint8_t unit_board_ver[43] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33, 0x52, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34 }; - dprintf("Wacca Touch%d: get rev date detail\n", req->side); + dprintf("Wacca Touch%d: get unit board version\n", req->side); resp.cmd = 0xa8; - memcpy(resp.data, rev, sizeof(rev)); + memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -406,12 +408,7 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i return S_OK; } -/* Encode and send the response. - * The last byte of every response is a checksum. - * This checksum is calculated by bitwise XORing - * every byte in the response, then dropping the MSB. - * Thanks the CrazyRedMachine for figuring that out!! - */ +/* Encode and send the response. */ static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) { const uint8_t *src; @@ -428,6 +425,11 @@ static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nb return S_OK; } +/* The last byte of every response is a checksum. + * This checksum is calculated by bitwise XORing + * every byte in the response, then dropping the MSB. + * Thanks the CrazyRedMachine for figuring that out!! + */ static uint8_t calc_checksum(const void *ptr, size_t nbytes) { const uint8_t *src; diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h index 77784dc..6e79375 100644 --- a/mercuryhook/touch.h +++ b/mercuryhook/touch.h @@ -9,9 +9,9 @@ struct touch_config { }; enum touch_cmd { - CMD_GET_REV_DATE = 0xa0, + CMD_GET_SYNC_BOARD_VER = 0xa0, CMD_STARTUP = 0x72, - CMD_GET_REV_DATE_DETAIL = 0xa8, + CMD_GET_UNIT_BOARD_VER = 0xa8, CMD_MYSTERY1 = 0xa2, CMD_MYSTERY2 = 0x94, CMD_START_AUTO_SCAN = 0xc9, @@ -37,18 +37,18 @@ struct touch_input_frame { uint8_t checksum; }; -struct touch_resp_get_rev_date { +struct touch_resp_get_sync_board_ver { uint8_t cmd; - uint8_t data[6]; + char version[6]; }; struct touch_resp_startup { - uint8_t data[80]; + char data[80]; }; -struct touch_resp_get_rev_date_detail { +struct touch_resp_get_unit_board_ver { uint8_t cmd; - uint8_t data[43]; + uint8_t version[43]; }; struct touch_resp_mystery1 { diff --git a/mercuryio/config.c b/mercuryio/config.c index d2d173b..b9695f8 100644 --- a/mercuryio/config.c +++ b/mercuryio/config.c @@ -8,15 +8,48 @@ /* Wacca Default key binding +Inner left +Inner right +2nd inner left +2nd inner right +3rd inner left +3rd inner right +outer left +outer right */ +static const int mercury_io_default_cells[] = { + '1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0', + 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',';',';',';', + '1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0', + 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',';',';',';', + 'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P', + 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',',',',',',','.','.','.','/','/','/', + 'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P', + 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',',',',',',','.','.','.','/','/','/', +}; + void mercury_io_config_load( struct mercury_io_config *cfg, const wchar_t *filename) { + wchar_t key[240]; + int i; + assert(cfg != NULL); assert(filename != NULL); - cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename); - cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename); + cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", 0x2D, filename); + cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", 0x2E, filename); + cfg->vk_vol_up = GetPrivateProfileIntW(L"io4", L"volup", 0x24, filename); + cfg->vk_vol_down = GetPrivateProfileIntW(L"io4", L"voldown", 0x23, filename); + + for (i = 0 ; i < 240 ; i++) { + swprintf_s(key, _countof(key), L"cell%i", i + 1); + cfg->vk_cell[i] = GetPrivateProfileIntW( + L"touch", + key, + mercury_io_default_cells[i], + filename); + } } diff --git a/mercuryio/config.h b/mercuryio/config.h index 69cdf32..18ff5de 100644 --- a/mercuryio/config.h +++ b/mercuryio/config.h @@ -8,8 +8,9 @@ struct mercury_io_config { uint8_t vk_test; uint8_t vk_service; - uint8_t vk_1p_btn[9]; - uint8_t vk_2p_btn[9]; + uint8_t vk_vol_up; + uint8_t vk_vol_down; + uint8_t vk_cell[240]; }; void mercury_io_config_load( diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 39f4d29..e51daf4 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -7,6 +7,7 @@ #include "mercuryio/config.h" static uint8_t mercury_opbtn; +static uint8_t mercury_gamebtn; static struct mercury_io_config mercury_io_cfg; uint16_t mercury_io_get_api_version(void) @@ -24,6 +25,7 @@ HRESULT mercury_io_init(void) HRESULT mercury_io_poll(void) { mercury_opbtn = 0; + mercury_gamebtn = 0; if (GetAsyncKeyState(mercury_io_cfg.vk_test)) { mercury_opbtn |= MERCURY_IO_OPBTN_TEST; @@ -33,6 +35,14 @@ HRESULT mercury_io_poll(void) mercury_opbtn |= MERCURY_IO_OPBTN_SERVICE; } + if (GetAsyncKeyState(mercury_io_cfg.vk_vol_up)) { + mercury_gamebtn |= MERCURY_IO_GAMEBTN_VOL_UP; + } + + if (GetAsyncKeyState(mercury_io_cfg.vk_vol_down)) { + mercury_gamebtn |= MERCURY_IO_GAMEBTN_VOL_DOWN; + } + return S_OK; } @@ -43,12 +53,19 @@ void mercury_io_get_opbtns(uint8_t *opbtn) } } -void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2) +void mercury_io_get_gamebtns(uint8_t *gamebtn) { - + if (gamebtn != NULL) { + *gamebtn = mercury_gamebtn; + } } HRESULT mercury_io_touch_init(void) { return S_OK; } + +HRESULT mercury_io_touch_start(void) +{ + return S_OK; +} diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index d06e37c..07d14e3 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -10,15 +10,8 @@ enum { }; enum { - MERCURY_IO_GAMEBTN_1 = 0x01, - MERCURY_IO_GAMEBTN_2 = 0x02, - MERCURY_IO_GAMEBTN_3 = 0x04, - MERCURY_IO_GAMEBTN_4 = 0x08, - MERCURY_IO_GAMEBTN_5 = 0x10, - MERCURY_IO_GAMEBTN_6 = 0x20, - MERCURY_IO_GAMEBTN_7 = 0x40, - MERCURY_IO_GAMEBTN_8 = 0x80, - MERCURY_IO_GAMEBTN_SELECT = 0x100, + MERCURY_IO_GAMEBTN_VOL_UP = 0x01, + MERCURY_IO_GAMEBTN_VOL_DOWN = 0x02, }; /* Get the version of the Wacca IO API that this DLL supports. This @@ -64,6 +57,8 @@ void mercury_io_get_opbtns(uint8_t *opbtn); Minimum API version: 0x0100 */ -void mercury_io_get_gamebtns(uint16_t *player1, uint16_t *player2); +void mercury_io_get_gamebtns(uint8_t *gamebtn); HRESULT mercury_io_touch_init(void); + +HRESULT mercury_io_touch_start(void); From 7c3719e352def0fe94ec827f051055434fd6f5b3 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sat, 8 Jan 2022 17:19:10 -0500 Subject: [PATCH 143/175] added code for auto scanning, but it crashes... --- mercuryhook/mercury-dll.h | 2 +- mercuryhook/touch.c | 66 +++++++++++++++++++++++++++++++++++---- mercuryhook/touch.h | 8 +++-- mercuryio/mercuryio.c | 44 ++++++++++++++++++++++++-- mercuryio/mercuryio.h | 3 +- 5 files changed, 110 insertions(+), 13 deletions(-) diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h index b28b8ca..086fd92 100644 --- a/mercuryhook/mercury-dll.h +++ b/mercuryhook/mercury-dll.h @@ -11,7 +11,7 @@ struct mercury_dll { void (*get_opbtns)(uint8_t *opbtn); void (*get_gamebtns)(uint8_t *gamebtn); HRESULT (*touch_init)(void); - HRESULT (*touch_start)(void); + void (*touch_start)(mercury_io_touch_callback_t callback); }; struct mercury_dll_config { diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 6c0c942..a32f6a2 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -37,8 +37,12 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req); static HRESULT touch_handle_mystery1(const struct touch_req *req); static HRESULT touch_handle_mystery2(const struct touch_req *req); static HRESULT touch_handle_start_auto_scan(const struct touch_req *req); +static void touch_res_auto_scan(const uint8_t *state); -uint8_t input_frame_count = 0x7b; +uint8_t input_frame_count_0 = 0x7b; +uint8_t input_frame_count_1 = 0x7b; +bool touch0_auto = false; +bool touch1_auto = false; static CRITICAL_SECTION touch0_lock; static struct uart touch0_uart; @@ -235,6 +239,8 @@ static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) // TODO: Why does strcpy_s here give a runtime warning and not work???? //strcpy_s(resp.version, sizeof(resp.version), "190523"); memcpy(resp.version, sync_board_ver, sizeof(sync_board_ver)); + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { @@ -290,6 +296,8 @@ static HRESULT touch_handle_startup(const struct touch_req *req) } memcpy(resp.data, rev, 80 * sizeof(uint8_t)); + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -313,6 +321,8 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) resp.cmd = 0xa8; memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -333,6 +343,8 @@ static HRESULT touch_handle_mystery1(const struct touch_req *req) resp.cmd = 0xa2; resp.data = 0x3f; + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -352,6 +364,8 @@ static HRESULT touch_handle_mystery2(const struct touch_req *req) resp.cmd = 0x94; resp.data = 0; + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); @@ -376,20 +390,62 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) resp.checksum = 0x49; resp.frame.cmd= 0x81; - resp.frame.count = input_frame_count++; memcpy(resp.frame.data1, data1, sizeof(data1)); memcpy(resp.frame.data2, data2, sizeof(data2)); + resp.frame.checksum = 0; resp.frame.checksum = calc_checksum(&resp.frame, sizeof(resp.frame)); if (req->side == 0) { + resp.frame.count = input_frame_count_0++; hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + touch0_auto = true; } else { + resp.frame.count = input_frame_count_1++; hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + touch1_auto = true; } + + //mercury_dll.touch_start(touch_res_auto_scan); return hr; } +static void touch_res_auto_scan(const uint8_t *state) +{ + struct touch_input_frame frame0; + //struct touch_input_frame frame1; + uint8_t data1[24] = { 0 }; + uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; + + frame0.cmd = 0x81; + if (input_frame_count_0 == 0x7f) { + frame0.count = 0x7f; + input_frame_count_0 = 0; + } + else { + frame0.count = input_frame_count_0++; + } + // for now return no data + memcpy(frame0.data1, data1, sizeof(data1)); + memcpy(frame0.data2, data2, sizeof(data2)); + frame0.checksum = 0; + frame0.checksum = calc_checksum(&frame0, sizeof(frame0)); + + if (touch0_auto) { + //dprintf("Wacca touch: Touch0 auto frame #%2hx sent\n", frame0.count); + EnterCriticalSection(&touch0_lock); + touch_frame_encode(&touch0_uart.readable, &frame0, sizeof(frame0)); + LeaveCriticalSection(&touch0_lock); + } + + if (touch1_auto) { + //dprintf("Wacca touch: Touch1 auto frame #%2hx sent\n", frame0.count); + EnterCriticalSection(&touch1_lock); + touch_frame_encode(&touch1_uart.readable, &frame0, sizeof(frame0)); + LeaveCriticalSection(&touch1_lock); + } +} + /* Decodes the response into a struct that's easier to work with. */ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side) { @@ -412,16 +468,13 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) { const uint8_t *src; - uint8_t checksum = 0; src = ptr; for (size_t i = 0; i < nbytes; i++) { dest->bytes[dest->pos++] = src[i]; - checksum = checksum^(src[i]); } - dest->bytes[dest->pos++] = checksum&0x7f; return S_OK; } @@ -438,8 +491,9 @@ static uint8_t calc_checksum(const void *ptr, size_t nbytes) src = ptr; for (size_t i = 0; i < nbytes; i++) { + //dprintf("Wacca touch: Calculating %2hx\n", src[i]); checksum = checksum^(src[i]); } - + //dprintf("Wacca touch: Checksum is %2hx\n", checksum&0x7f); return checksum&0x7f; } diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h index 6e79375..d98fc0b 100644 --- a/mercuryhook/touch.h +++ b/mercuryhook/touch.h @@ -26,9 +26,6 @@ struct touch_req { uint8_t data_length; // Size of the data including command byte }; -// The checksum is only calculated when we're about to send it so -// it's not part of any of these structs. Just note that the last -// byte of every response is a checksum struct touch_input_frame { uint8_t cmd; uint8_t data1[24]; @@ -40,25 +37,30 @@ struct touch_input_frame { struct touch_resp_get_sync_board_ver { uint8_t cmd; char version[6]; + uint8_t checksum; }; struct touch_resp_startup { char data[80]; + uint8_t checksum; }; struct touch_resp_get_unit_board_ver { uint8_t cmd; uint8_t version[43]; + uint8_t checksum; }; struct touch_resp_mystery1 { uint8_t cmd; uint8_t data; + uint8_t checksum; }; struct touch_resp_mystery2 { uint8_t cmd; uint8_t data; + uint8_t checksum; }; struct touch_resp_start_auto { diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index e51daf4..3d4a735 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -2,13 +2,18 @@ #include #include +#include #include "mercuryio/mercuryio.h" #include "mercuryio/config.h" +static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx); + static uint8_t mercury_opbtn; static uint8_t mercury_gamebtn; static struct mercury_io_config mercury_io_cfg; +static bool mercury_io_touch_stop_flag; +static HANDLE mercury_io_touch_thread; uint16_t mercury_io_get_api_version(void) { @@ -65,7 +70,42 @@ HRESULT mercury_io_touch_init(void) return S_OK; } -HRESULT mercury_io_touch_start(void) +void mercury_io_touch_start(mercury_io_touch_callback_t callback) { - return S_OK; + if (mercury_io_touch_thread != NULL) { + return; + } + + mercury_io_touch_thread = (HANDLE) _beginthreadex( + NULL, + 0, + mercury_io_touch_thread_proc, + callback, + 0, + NULL + ); +} + +static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx) +{ + mercury_io_touch_callback_t callback; + uint8_t pressure[240]; + size_t i; + + callback = ctx; + + while (!mercury_io_touch_stop_flag) { + for (i = 0 ; i < _countof(pressure) ; i++) { + if (GetAsyncKeyState(mercury_io_cfg.vk_cell[i]) & 0x8000) { + pressure[i] = 128; + } else { + pressure[i] = 0; + } + } + + callback(pressure); + Sleep(1); + } + + return 0; } diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 07d14e3..6f32989 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -14,6 +14,7 @@ enum { MERCURY_IO_GAMEBTN_VOL_DOWN = 0x02, }; +typedef void (*mercury_io_touch_callback_t)(const uint8_t *state); /* Get the version of the Wacca IO API that this DLL supports. This function should return a positive 16-bit integer, where the high byte is the major version and the low byte is the minor version (as defined by the @@ -61,4 +62,4 @@ void mercury_io_get_gamebtns(uint8_t *gamebtn); HRESULT mercury_io_touch_init(void); -HRESULT mercury_io_touch_start(void); +void mercury_io_touch_start(mercury_io_touch_callback_t callback); From 028b6bb6e7cdce867e30a66f1cb42a4a7324b440 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sat, 8 Jan 2022 17:42:09 -0500 Subject: [PATCH 144/175] whoops, at least it compiles now --- mercuryhook/touch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index a32f6a2..6e44027 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -406,7 +406,7 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) touch1_auto = true; } - //mercury_dll.touch_start(touch_res_auto_scan); + mercury_dll.touch_start(touch_res_auto_scan); return hr; } From 1bc2fc141bd43f1694f29aec35e35ad218a9c169 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sat, 8 Jan 2022 22:57:34 -0500 Subject: [PATCH 145/175] crash is fixed, new touch board dip sw error --- mercuryhook/touch.c | 52 ++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 6e44027..8ec616a 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -28,7 +28,6 @@ static HRESULT touch1_handle_irp_locked(struct irp *irp); static HRESULT touch_req_dispatch(const struct touch_req *req); static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, int side); -static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes); static uint8_t calc_checksum(const void *ptr, size_t nbytes); static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req); @@ -244,10 +243,10 @@ static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) if (req->side == 0) { - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; } @@ -300,10 +299,10 @@ static HRESULT touch_handle_startup(const struct touch_req *req) resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; } @@ -325,10 +324,10 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; @@ -347,10 +346,10 @@ static HRESULT touch_handle_mystery1(const struct touch_req *req) resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; } @@ -368,10 +367,10 @@ static HRESULT touch_handle_mystery2(const struct touch_req *req) resp.checksum = calc_checksum(&resp, sizeof(resp)); if (req->side == 0) { - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; } @@ -397,12 +396,12 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) if (req->side == 0) { resp.frame.count = input_frame_count_0++; - hr = touch_frame_encode(&touch0_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); touch0_auto = true; } else { resp.frame.count = input_frame_count_1++; - hr = touch_frame_encode(&touch1_uart.readable, &resp, sizeof(resp)); + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); touch1_auto = true; } @@ -418,13 +417,8 @@ static void touch_res_auto_scan(const uint8_t *state) uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; frame0.cmd = 0x81; - if (input_frame_count_0 == 0x7f) { - frame0.count = 0x7f; - input_frame_count_0 = 0; - } - else { - frame0.count = input_frame_count_0++; - } + frame0.count = input_frame_count_0++; + input_frame_count_0 %= 0x7f; // for now return no data memcpy(frame0.data1, data1, sizeof(data1)); memcpy(frame0.data2, data2, sizeof(data2)); @@ -434,14 +428,14 @@ static void touch_res_auto_scan(const uint8_t *state) if (touch0_auto) { //dprintf("Wacca touch: Touch0 auto frame #%2hx sent\n", frame0.count); EnterCriticalSection(&touch0_lock); - touch_frame_encode(&touch0_uart.readable, &frame0, sizeof(frame0)); + iobuf_write(&touch0_uart.readable, &frame0, sizeof(frame0)); LeaveCriticalSection(&touch0_lock); } if (touch1_auto) { //dprintf("Wacca touch: Touch1 auto frame #%2hx sent\n", frame0.count); EnterCriticalSection(&touch1_lock); - touch_frame_encode(&touch1_uart.readable, &frame0, sizeof(frame0)); + iobuf_write(&touch1_uart.readable, &frame0, sizeof(frame0)); LeaveCriticalSection(&touch1_lock); } } @@ -464,20 +458,6 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i return S_OK; } -/* Encode and send the response. */ -static HRESULT touch_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) -{ - const uint8_t *src; - - src = ptr; - - for (size_t i = 0; i < nbytes; i++) { - dest->bytes[dest->pos++] = src[i]; - } - - return S_OK; -} - /* The last byte of every response is a checksum. * This checksum is calculated by bitwise XORing * every byte in the response, then dropping the MSB. From c91ef4005a74194537a71e05c2f5944ecec78269 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sun, 9 Jan 2022 12:30:31 -0500 Subject: [PATCH 146/175] switched uint8_t -> bool in input, no pressure sense --- mercuryhook/touch.c | 8 ++++---- mercuryio/mercuryio.c | 12 ++++++------ mercuryio/mercuryio.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 8ec616a..a26bd31 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -36,7 +36,7 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req); static HRESULT touch_handle_mystery1(const struct touch_req *req); static HRESULT touch_handle_mystery2(const struct touch_req *req); static HRESULT touch_handle_start_auto_scan(const struct touch_req *req); -static void touch_res_auto_scan(const uint8_t *state); +static void touch_res_auto_scan(const bool *state); uint8_t input_frame_count_0 = 0x7b; uint8_t input_frame_count_1 = 0x7b; @@ -409,18 +409,18 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) return hr; } -static void touch_res_auto_scan(const uint8_t *state) +static void touch_res_auto_scan(const bool *state) { struct touch_input_frame frame0; //struct touch_input_frame frame1; - uint8_t data1[24] = { 0 }; + uint8_t data[24] = { 0 }; uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; frame0.cmd = 0x81; frame0.count = input_frame_count_0++; input_frame_count_0 %= 0x7f; // for now return no data - memcpy(frame0.data1, data1, sizeof(data1)); + memcpy(frame0.data1, data, sizeof(data)); memcpy(frame0.data2, data2, sizeof(data2)); frame0.checksum = 0; frame0.checksum = calc_checksum(&frame0, sizeof(frame0)); diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 3d4a735..75ee77d 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -89,21 +89,21 @@ void mercury_io_touch_start(mercury_io_touch_callback_t callback) static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx) { mercury_io_touch_callback_t callback; - uint8_t pressure[240]; + bool cellPressed[240]; size_t i; callback = ctx; while (!mercury_io_touch_stop_flag) { - for (i = 0 ; i < _countof(pressure) ; i++) { - if (GetAsyncKeyState(mercury_io_cfg.vk_cell[i]) & 0x8000) { - pressure[i] = 128; + for (i = 0 ; i < _countof(cellPressed) ; i++) { + if (GetAsyncKeyState(mercury_io_cfg.vk_cell[i])) { + cellPressed[i] = true; } else { - pressure[i] = 0; + cellPressed[i] = false; } } - callback(pressure); + callback(cellPressed); Sleep(1); } diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 6f32989..4b36dee 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -14,7 +14,7 @@ enum { MERCURY_IO_GAMEBTN_VOL_DOWN = 0x02, }; -typedef void (*mercury_io_touch_callback_t)(const uint8_t *state); +typedef void (*mercury_io_touch_callback_t)(const bool *state); /* Get the version of the Wacca IO API that this DLL supports. This function should return a positive 16-bit integer, where the high byte is the major version and the low byte is the minor version (as defined by the From 95192fbee246b96a3240e4147a37e1753b4897ea Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Mon, 10 Jan 2022 01:07:01 -0500 Subject: [PATCH 147/175] WACCA Console - Touch : Success --- mercuryhook/touch.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index a26bd31..2c776a5 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -319,14 +319,18 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) dprintf("Wacca Touch%d: get unit board version\n", req->side); resp.cmd = 0xa8; - memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); - resp.checksum = 0; - resp.checksum = calc_checksum(&resp, sizeof(resp)); - if (req->side == 0) { + if (req->side == 0) { + memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { + unit_board_ver[6] = 0x4c; + memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); + resp.checksum = 0; + resp.checksum = calc_checksum(&resp, sizeof(resp)); hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } return hr; @@ -368,7 +372,7 @@ static HRESULT touch_handle_mystery2(const struct touch_req *req) if (req->side == 0) { hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); - } + } else { hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } @@ -419,7 +423,7 @@ static void touch_res_auto_scan(const bool *state) frame0.cmd = 0x81; frame0.count = input_frame_count_0++; input_frame_count_0 %= 0x7f; - // for now return no data + memcpy(frame0.data1, data, sizeof(data)); memcpy(frame0.data2, data2, sizeof(data2)); frame0.checksum = 0; From ca69474837671afbdeb132d13a3bc03cd2a49f33 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Mon, 10 Jan 2022 19:36:11 -0500 Subject: [PATCH 148/175] it finally, FINALLY works --- mercuryhook/touch.c | 35 +++++++++++++++++++++++++++++++---- mercuryio/config.c | 20 ++++---------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 2c776a5..2f86645 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -416,19 +416,46 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) static void touch_res_auto_scan(const bool *state) { struct touch_input_frame frame0; - //struct touch_input_frame frame1; - uint8_t data[24] = { 0 }; + struct touch_input_frame frame1; + uint8_t dataR[24] = { 0 }; + uint8_t dataL[24] = { 0 }; + // this changes every input on a real board but + // the game doesn't seem to care about it... uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; + uint8_t counter = 0; frame0.cmd = 0x81; frame0.count = input_frame_count_0++; input_frame_count_0 %= 0x7f; + + frame1.cmd = 0x81; + frame1.count = input_frame_count_1++; + input_frame_count_1 %= 0x7f; + + for (int i = 0; i < 24; i++) { + for (int j = 0; j < 5; j++) { + if (state[counter]) { + dataR[i] |= (1 << j); + } + if (state[counter+120]) { + dataL[i] |= (1 << j); + } + counter++; + } + } - memcpy(frame0.data1, data, sizeof(data)); + memcpy(frame0.data1, dataR, sizeof(dataR)); memcpy(frame0.data2, data2, sizeof(data2)); + + memcpy(frame1.data1, dataL, sizeof(dataL)); + memcpy(frame1.data2, data2, sizeof(data2)); + frame0.checksum = 0; frame0.checksum = calc_checksum(&frame0, sizeof(frame0)); + frame1.checksum = 0; + frame1.checksum = calc_checksum(&frame1, sizeof(frame1)); + if (touch0_auto) { //dprintf("Wacca touch: Touch0 auto frame #%2hx sent\n", frame0.count); EnterCriticalSection(&touch0_lock); @@ -439,7 +466,7 @@ static void touch_res_auto_scan(const bool *state) if (touch1_auto) { //dprintf("Wacca touch: Touch1 auto frame #%2hx sent\n", frame0.count); EnterCriticalSection(&touch1_lock); - iobuf_write(&touch1_uart.readable, &frame0, sizeof(frame0)); + iobuf_write(&touch1_uart.readable, &frame1, sizeof(frame1)); LeaveCriticalSection(&touch1_lock); } } diff --git a/mercuryio/config.c b/mercuryio/config.c index b9695f8..1b2a30f 100644 --- a/mercuryio/config.c +++ b/mercuryio/config.c @@ -6,27 +6,15 @@ #include "mercuryio/config.h" -/* -Wacca Default key binding -Inner left -Inner right -2nd inner left -2nd inner right -3rd inner left -3rd inner right -outer left -outer right -*/ - static const int mercury_io_default_cells[] = { '1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0', - 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',';',';',';', '1','1','1','2','2','2','3','3','3','4','4','4','5','5','5','6','6','6','7','7','7','8','8','8','9','9','9','0','0','0', - 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',';',';',';', 'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P', - 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',',',',',',','.','.','.','/','/','/', 'Q','Q','Q','W','W','W','E','E','E','R','R','R','T','T','T','Y','Y','Y','U','U','U','I','I','I','O','O','O','P','P','P', - 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',',',',',',','.','.','.','/','/','/', + 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',VK_OEM_1,VK_OEM_1,VK_OEM_1, + 'A','A','A','S','S','S','D','D','D','F','F','F','G','G','G','H','H','H','J','J','J','K','K','K','L','L','L',VK_OEM_1,VK_OEM_1,VK_OEM_1, + 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_2,VK_OEM_2, + 'Z','Z','Z','X','X','X','C','C','C','V','V','V','B','B','B','N','N','N','M','M','M',VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_2,VK_OEM_2, }; void mercury_io_config_load( From 56d08a93c37f08050813f4be6a988c0a55605295 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Mon, 10 Jan 2022 20:31:01 -0500 Subject: [PATCH 149/175] update dist start.bat for wacca --- dist/mercury/start.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/mercury/start.bat b/dist/mercury/start.bat index 437722e..3a55aaa 100644 --- a/dist/mercury/start.bat +++ b/dist/mercury/start.bat @@ -2,8 +2,8 @@ pushd %~dp0 taskkill /f /im amdaemon.exe > nul 2>&1 :LOOP -inject -d -k mercuryhook.dll amdaemon.exe -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json -inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury.exe +start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json +inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe taskkill /f /im amdaemon.exe > nul 2>&1 echo. echo Game processes have terminated From 0fae1157344f2feddbc30d01cba1e8a1b5bba0d0 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Fri, 14 Jan 2022 16:07:26 -0500 Subject: [PATCH 150/175] update wacca start.bat to more closely mirror official game.bat --- dist/mercury/start.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/mercury/start.bat b/dist/mercury/start.bat index 3a55aaa..887274f 100644 --- a/dist/mercury/start.bat +++ b/dist/mercury/start.bat @@ -2,7 +2,7 @@ pushd %~dp0 taskkill /f /im amdaemon.exe > nul 2>&1 :LOOP -start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_region_jpn.json config_video_clone.json config_video_clone_flip.json config_video_dual.json config_video_dual_flip.json +start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe taskkill /f /im amdaemon.exe > nul 2>&1 echo. From 0d84a59b942ec91bb855a65b5c46d9120d625fb0 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sat, 15 Jan 2022 23:18:52 -0500 Subject: [PATCH 151/175] changed default vol buttons to up and down because that's what a sane person would set them to --- dist/mercury/segatools.ini | 4 ++-- mercuryio/config.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index 4dac2cf..009ce65 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -37,6 +37,6 @@ subnet=192.168.250.0 ; Input API selection for JVS input emulator. test=0x2D service=0x2E -volup=0x24 -voldown=0x23 +volup=0x26 +voldown=0x28 diff --git a/mercuryio/config.c b/mercuryio/config.c index 1b2a30f..6e67e42 100644 --- a/mercuryio/config.c +++ b/mercuryio/config.c @@ -29,8 +29,8 @@ void mercury_io_config_load( cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", 0x2D, filename); cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", 0x2E, filename); - cfg->vk_vol_up = GetPrivateProfileIntW(L"io4", L"volup", 0x24, filename); - cfg->vk_vol_down = GetPrivateProfileIntW(L"io4", L"voldown", 0x23, filename); + cfg->vk_vol_up = GetPrivateProfileIntW(L"io4", L"volup", 0x26, filename); + cfg->vk_vol_down = GetPrivateProfileIntW(L"io4", L"voldown", 0x28, filename); for (i = 0 ; i < 240 ; i++) { swprintf_s(key, _countof(key), L"cell%i", i + 1); From 6e855414930d50f5c8c5f2d42a3f132f0abe3d24 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 10 Feb 2022 12:17:10 -0500 Subject: [PATCH 152/175] Added d3d11 hook, better touch naming --- Dockerfile | 1 + dist/mercury/segatools.ini | 2 ++ dist/mercury/start.bat | 13 +++++++++---- gfxhook/dxgi.c | 2 +- gfxhook/util.c | 2 +- mercuryhook/config.c | 2 ++ mercuryhook/config.h | 2 ++ mercuryhook/dllmain.c | 6 ++++++ mercuryhook/meson.build | 1 + mercuryhook/touch.c | 16 ++++++++-------- mercuryhook/touch.h | 2 +- 11 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6aa360d..279c6b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,7 @@ COPY mu3hook mu3hook COPY mu3io mu3io COPY pki pki COPY platform platform +COPY gfxhook gfxhook COPY reg reg COPY spike spike COPY subprojects subprojects diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index 009ce65..f71bfae 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -32,6 +32,8 @@ enable=1 ; that subnet must start with 192.168. subnet=192.168.250.0 +[gfx] +enable=1 [io4] ; Input API selection for JVS input emulator. diff --git a/dist/mercury/start.bat b/dist/mercury/start.bat index 887274f..c00f2ea 100644 --- a/dist/mercury/start.bat +++ b/dist/mercury/start.bat @@ -1,10 +1,15 @@ @echo off pushd %~dp0 + taskkill /f /im amdaemon.exe > nul 2>&1 -:LOOP + +REM USA +REM start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json + +REM JP start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe + taskkill /f /im amdaemon.exe > nul 2>&1 -echo. -echo Game processes have terminated -pause \ No newline at end of file + +echo Game processes have terminated \ No newline at end of file diff --git a/gfxhook/dxgi.c b/gfxhook/dxgi.c index e27e68d..14d3e5f 100644 --- a/gfxhook/dxgi.c +++ b/gfxhook/dxgi.c @@ -312,7 +312,7 @@ static HRESULT STDMETHODCALLTYPE my_IDXGIFactory_CreateSwapChain( ShowWindow(hwnd, SW_RESTORE); if (!gfx_config.framed && width > 0 && height > 0) { - dprintf("DXGI: Resizing window to %ldx%ld\n", width, height); + dprintf("DXGI: Resizing window to %ux%u\n", width, height); SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); diff --git a/gfxhook/util.c b/gfxhook/util.c index 4da45b3..1ee552d 100644 --- a/gfxhook/util.c +++ b/gfxhook/util.c @@ -19,7 +19,7 @@ void gfx_util_borderless_fullscreen_windowed(HWND hwnd, UINT width, UINT height) BOOL ok; HRESULT hr; - dprintf("Gfx: Resizing window to %ldx%ld\n", width, height); + dprintf("Gfx: Resizing window to %ux%u\n", width, height); SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP); SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST); diff --git a/mercuryhook/config.c b/mercuryhook/config.c index a66efa0..f0dbb08 100644 --- a/mercuryhook/config.c +++ b/mercuryhook/config.c @@ -5,6 +5,7 @@ #include "hooklib/config.h" #include "hooklib/dvd.h" +#include "gfxhook/config.h" #include "mercuryhook/config.h" @@ -51,6 +52,7 @@ void mercury_hook_config_load( aime_config_load(&cfg->aime, filename); dvd_config_load(&cfg->dvd, filename); io4_config_load(&cfg->io4, filename); + gfx_config_load(&cfg->gfx, filename); mercury_dll_config_load(&cfg->dll, filename); touch_config_load(&cfg->touch, filename); } diff --git a/mercuryhook/config.h b/mercuryhook/config.h index bbfc4a4..cfeb490 100644 --- a/mercuryhook/config.h +++ b/mercuryhook/config.h @@ -5,6 +5,7 @@ #include "board/config.h" #include "hooklib/dvd.h" +#include "gfxhook/gfx.h" #include "mercuryhook/mercury-dll.h" #include "mercuryhook/touch.h" @@ -16,6 +17,7 @@ struct mercury_hook_config { struct aime_config aime; struct dvd_config dvd; struct io4_config io4; + struct gfx_config gfx; struct mercury_dll_config dll; struct touch_config touch; }; diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c index 9da0b25..611122b 100644 --- a/mercuryhook/dllmain.c +++ b/mercuryhook/dllmain.c @@ -9,6 +9,9 @@ #include "hooklib/serial.h" #include "hooklib/spike.h" +#include "gfxhook/gfx.h" +#include "gfxhook/d3d11.h" + #include "mercuryhook/config.h" #include "mercuryhook/io4.h" #include "mercuryhook/mercury-dll.h" @@ -40,6 +43,9 @@ static DWORD CALLBACK mercury_pre_startup(void) dvd_hook_init(&mercury_hook_cfg.dvd, mercury_hook_mod); serial_hook_init(); + gfx_hook_init(&mercury_hook_cfg.gfx); + gfx_d3d11_hook_init(&mercury_hook_cfg.gfx, mercury_hook_mod); + /* Initialize emulation hooks */ hr = platform_hook_init( diff --git a/mercuryhook/meson.build b/mercuryhook/meson.build index ffa0f88..7a676b2 100644 --- a/mercuryhook/meson.build +++ b/mercuryhook/meson.build @@ -11,6 +11,7 @@ shared_library( ], link_with : [ aimeio_lib, + gfxhook_lib, board_lib, hooklib_lib, mercuryio_lib, diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 2f86645..d8ada37 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -31,7 +31,7 @@ static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf, i static uint8_t calc_checksum(const void *ptr, size_t nbytes); static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req); -static HRESULT touch_handle_startup(const struct touch_req *req); +static HRESULT touch_handle_next_read(const struct touch_req *req); static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req); static HRESULT touch_handle_mystery1(const struct touch_req *req); static HRESULT touch_handle_mystery2(const struct touch_req *req); @@ -204,8 +204,8 @@ static HRESULT touch_req_dispatch(const struct touch_req *req) switch (req->cmd) { case CMD_GET_SYNC_BOARD_VER: return touch_handle_get_sync_board_ver(req); - case CMD_STARTUP: - return touch_handle_startup(req); + case CMD_NEXT_READ: + return touch_handle_next_read(req); case CMD_GET_UNIT_BOARD_VER: return touch_handle_get_unit_board_ver(req); case CMD_MYSTERY1: @@ -252,13 +252,13 @@ static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) } /* TODO: Very ugly please make better before upstreaming */ -static HRESULT touch_handle_startup(const struct touch_req *req) +static HRESULT touch_handle_next_read(const struct touch_req *req) { struct touch_resp_startup resp; HRESULT hr; uint8_t *rev; - dprintf("Wacca Touch%d: Startup %2hx\n", req->side, req->data[2]); + dprintf("Wacca Touch%d: Read section %2hx\n", req->side, req->data[2]); switch (req->data[2]) { @@ -290,7 +290,7 @@ static HRESULT touch_handle_startup(const struct touch_req *req) 0x20, 0x20, 0x20, 0x34 }; break; default: - dprintf("Wacca touch: BAD STARTUP REQUEST %2hx\n", req->data[2]); + dprintf("Wacca touch: BAD READ REQUEST %2hx\n", req->data[2]); return 1; } @@ -342,7 +342,7 @@ static HRESULT touch_handle_mystery1(const struct touch_req *req) struct touch_resp_mystery1 resp; HRESULT hr; - dprintf("Wacca Touch%d: mystery command 1\n", req->side); + dprintf("Wacca Touch%d: Command A2\n", req->side); resp.cmd = 0xa2; resp.data = 0x3f; @@ -363,7 +363,7 @@ static HRESULT touch_handle_mystery2(const struct touch_req *req) struct touch_resp_mystery2 resp; HRESULT hr; - dprintf("Wacca Touch%d: mystery command 2\n", req->side); + dprintf("Wacca Touch%d: Command 94\n", req->side); resp.cmd = 0x94; resp.data = 0; diff --git a/mercuryhook/touch.h b/mercuryhook/touch.h index d98fc0b..cc665c8 100644 --- a/mercuryhook/touch.h +++ b/mercuryhook/touch.h @@ -10,7 +10,7 @@ struct touch_config { enum touch_cmd { CMD_GET_SYNC_BOARD_VER = 0xa0, - CMD_STARTUP = 0x72, + CMD_NEXT_READ = 0x72, CMD_GET_UNIT_BOARD_VER = 0xa8, CMD_MYSTERY1 = 0xa2, CMD_MYSTERY2 = 0x94, From 890cad75e7cf8550b9a42c3e81c2307952fa9177 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 23 Feb 2022 02:44:23 -0500 Subject: [PATCH 153/175] non-working coin insert emulation --- dist/mercury/segatools.ini | 1 + mercuryhook/io4.c | 4 ++++ mercuryio/config.c | 1 + mercuryio/config.h | 1 + mercuryio/mercuryio.c | 4 ++++ mercuryio/mercuryio.h | 1 + 6 files changed, 12 insertions(+) diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index f71bfae..0b9ffdd 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -39,6 +39,7 @@ enable=1 ; Input API selection for JVS input emulator. test=0x2D service=0x2E +coin=0x24 volup=0x26 voldown=0x28 diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index f178ab9..fa146db 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -61,6 +61,10 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) if (opbtn & MERCURY_IO_OPBTN_SERVICE) { state->buttons[0] |= IO4_BUTTON_SERVICE; + } + + if (gamebtn & MERCURY_IO_OPBTN_COIN) { + state->chutes[0] |= 1 << 1; // FIXME: insert coins } if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { diff --git a/mercuryio/config.c b/mercuryio/config.c index 6e67e42..f3c8e54 100644 --- a/mercuryio/config.c +++ b/mercuryio/config.c @@ -29,6 +29,7 @@ void mercury_io_config_load( cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", 0x2D, filename); cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", 0x2E, filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", 0x24, filename); cfg->vk_vol_up = GetPrivateProfileIntW(L"io4", L"volup", 0x26, filename); cfg->vk_vol_down = GetPrivateProfileIntW(L"io4", L"voldown", 0x28, filename); diff --git a/mercuryio/config.h b/mercuryio/config.h index 18ff5de..c925764 100644 --- a/mercuryio/config.h +++ b/mercuryio/config.h @@ -8,6 +8,7 @@ struct mercury_io_config { uint8_t vk_test; uint8_t vk_service; + uint8_t vk_coin; uint8_t vk_vol_up; uint8_t vk_vol_down; uint8_t vk_cell[240]; diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 75ee77d..9ad755b 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -40,6 +40,10 @@ HRESULT mercury_io_poll(void) mercury_opbtn |= MERCURY_IO_OPBTN_SERVICE; } + if (GetAsyncKeyState(mercury_io_cfg.vk_coin)) { + mercury_opbtn |= MERCURY_IO_OPBTN_COIN; + } + if (GetAsyncKeyState(mercury_io_cfg.vk_vol_up)) { mercury_gamebtn |= MERCURY_IO_GAMEBTN_VOL_UP; } diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 4b36dee..2701ec4 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -7,6 +7,7 @@ enum { MERCURY_IO_OPBTN_TEST = 0x01, MERCURY_IO_OPBTN_SERVICE = 0x02, + MERCURY_IO_OPBTN_COIN = 0x04, }; enum { From 5e210b9e0e38df545fa1ab651b38840f255d93b6 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Wed, 18 May 2022 01:33:09 -0400 Subject: [PATCH 154/175] cleaned up touch emu code a bit --- mercuryhook/touch.c | 86 ++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index d8ada37..44aae7e 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -21,6 +21,9 @@ #include "util/dprintf.h" #include "util/dump.h" +const char SYNC_BOARD_VER[6] = "190523"; +const char UNIT_BOARD_VER[6] = "190514"; + static HRESULT touch_handle_irp(struct irp *irp); static HRESULT touch0_handle_irp_locked(struct irp *irp); static HRESULT touch1_handle_irp_locked(struct irp *irp); @@ -230,71 +233,50 @@ static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) { struct touch_resp_get_sync_board_ver resp; HRESULT hr; - uint8_t sync_board_ver[6] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33 }; dprintf("Wacca Touch%d: Get sync board version\n", req->side); resp.cmd = 0xa0; - // TODO: Why does strcpy_s here give a runtime warning and not work???? - //strcpy_s(resp.version, sizeof(resp.version), "190523"); - memcpy(resp.version, sync_board_ver, sizeof(sync_board_ver)); + memcpy(resp.version, SYNC_BOARD_VER, sizeof(SYNC_BOARD_VER)); resp.checksum = 0; resp.checksum = calc_checksum(&resp, sizeof(resp)); - if (req->side == 0) { hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } + return hr; } -/* TODO: Very ugly please make better before upstreaming */ static HRESULT touch_handle_next_read(const struct touch_req *req) { struct touch_resp_startup resp; HRESULT hr; - uint8_t *rev; + char *rev; dprintf("Wacca Touch%d: Read section %2hx\n", req->side, req->data[2]); switch (req->data[2]) { + // These can be found in the config file case 0x30: - rev = (uint8_t[80]) { 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, - 0x20, 0x20, 0x20, 0x31, 0x20, 0x20, 0x20, 0x20, 0x32, 0x20, 0x20, 0x20, 0x20, - 0x33, 0x20, 0x20, 0x20, 0x20, 0x34, 0x20, 0x20, 0x20, 0x20, 0x35, 0x20, 0x20, - 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, - 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x35, 0x20, 0x20, 0x20, - 0x31, 0x35, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, - 0x20, 0x20, 0x31, 0x31 }; + rev = " 0 0 1 2 3 4 5 15 15 15 15 15 15 11 11 11"; break; case 0x31: - rev = (uint8_t[80]) { 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x20, 0x31, 0x31, 0x20, - 0x20, 0x20, 0x31, 0x31, 0x20, 0x20, 0x31, 0x32, 0x38, 0x20, 0x20, 0x31, 0x30, - 0x33, 0x20, 0x20, 0x31, 0x30, 0x33, 0x20, 0x20, 0x31, 0x31, 0x35, 0x20, 0x20, - 0x31, 0x33, 0x38, 0x20, 0x20, 0x31, 0x32, 0x37, 0x20, 0x20, 0x31, 0x30, 0x33, - 0x20, 0x20, 0x31, 0x30, 0x35, 0x20, 0x20, 0x31, 0x31, 0x31, 0x20, 0x20, 0x31, - 0x32, 0x36, 0x20, 0x20, 0x31, 0x31, 0x33, 0x20, 0x20, 0x20, 0x39, 0x35, 0x20, - 0x20, 0x31, 0x30, 0x30 }; + rev = " 11 11 11 128 103 103 115 138 127 103 105 111 126 113 95 100"; break; case 0x33: - rev = (uint8_t[80]) { 0x20, 0x20, 0x31, 0x30, 0x31, 0x20, 0x20, 0x31, 0x31, 0x35, 0x20, - 0x20, 0x20, 0x39, 0x38, 0x20, 0x20, 0x20, 0x38, 0x36, 0x20, 0x20, 0x20, 0x37, - 0x36, 0x20, 0x20, 0x20, 0x36, 0x37, 0x20, 0x20, 0x20, 0x36, 0x38, 0x20, 0x20, - 0x20, 0x34, 0x38, 0x20, 0x20, 0x31, 0x31, 0x37, 0x20, 0x20, 0x20, 0x20, 0x30, - 0x20, 0x20, 0x20, 0x38, 0x32, 0x20, 0x20, 0x31, 0x35, 0x34, 0x20, 0x20, 0x20, - 0x20, 0x30, 0x20, 0x20, 0x20, 0x20, 0x36, 0x20, 0x20, 0x20, 0x33, 0x35, 0x20, - 0x20, 0x20, 0x20, 0x34 }; + rev = " 101 115 98 86 76 67 68 48 117 0 82 154 0 6 35 4"; break; default: dprintf("Wacca touch: BAD READ REQUEST %2hx\n", req->data[2]); return 1; } - memcpy(resp.data, rev, 80 * sizeof(uint8_t)); + memcpy(resp.data, rev, 80 * sizeof(char)); resp.checksum = 0; resp.checksum = calc_checksum(&resp, sizeof(resp)); @@ -311,30 +293,46 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) { struct touch_resp_get_unit_board_ver resp; HRESULT hr; - uint8_t unit_board_ver[43] = { 0x31, 0x39, 0x30, 0x35, 0x32, 0x33, 0x52, 0x31, - 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, - 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, - 0x39, 0x30, 0x35, 0x31, 0x34, 0x31, 0x39, 0x30, 0x35, 0x31, 0x34 }; dprintf("Wacca Touch%d: get unit board version\n", req->side); + memset(resp.version, 0, sizeof(resp.version)); + memcpy(resp.version, SYNC_BOARD_VER, sizeof(SYNC_BOARD_VER)); + + for (int i = 0; i < 6; i++ ) + memcpy(&resp.version[7 + (6 * i)], UNIT_BOARD_VER, sizeof(UNIT_BOARD_VER)); + resp.cmd = 0xa8; + resp.checksum = 0; if (req->side == 0) { - memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); - resp.checksum = 0; + resp.version[6] = 'R'; resp.checksum = calc_checksum(&resp, sizeof(resp)); + + #if 0 + for (int i = 0; i < sizeof(resp.version); i++) { + dprintf("0x%02x ", resp.version[i]); + } + dprintf("\n"); + #endif + hr = iobuf_write(&touch0_uart.readable, &resp, sizeof(resp)); } else { - unit_board_ver[6] = 0x4c; - memcpy(resp.version, unit_board_ver, sizeof(unit_board_ver)); - resp.checksum = 0; + resp.version[6] = 'L'; resp.checksum = calc_checksum(&resp, sizeof(resp)); + + #if 0 + for (int i = 0; i < sizeof(resp.version); i++) { + dprintf("0x%02x ", resp.version[i]); + } + dprintf("\n"); + #endif + hr = iobuf_write(&touch1_uart.readable, &resp, sizeof(resp)); } - return hr; + return hr; } static HRESULT touch_handle_mystery1(const struct touch_req *req) @@ -384,9 +382,17 @@ static HRESULT touch_handle_start_auto_scan(const struct touch_req *req) struct touch_resp_start_auto resp; HRESULT hr; uint8_t data1[24] = { 0 }; + // Unsure what this does. It seems to change every request on a real board, + // but the game doesn't seem to mind that it's the same uint8_t data2[9] = { 0x0d, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 }; - dprintf("Wacca Touch%d: Start Auto\n", req->side); + dprintf("Wacca Touch%d: Start Auto", req->side); + + #if 0 + for (int i = 0; i < req->data_length; i++) + dprintf("0x%02x ", req->data[i]); + #endif + dprintf("\n"); resp.cmd = 0x9c; resp.data = 0; From c51cf52b623abd0cdab4027f286c2a0572898f9b Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 29 May 2022 00:23:24 -0400 Subject: [PATCH 155/175] small logging improvement --- mercuryhook/elisabeth.c | 1 + mercuryhook/touch.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index 7e2f268..71c4a05 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -35,6 +35,7 @@ static const struct hook_symbol win32_hooks[] = { HRESULT elisabeth_hook_init() { dll_hook_insert_hooks(NULL); + dprintf("Elisabeth: Init\n"); return S_OK; } diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 44aae7e..87e757c 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -67,7 +67,7 @@ HRESULT touch_hook_init(const struct touch_config *cfg) InitializeCriticalSection(&touch0_lock); InitializeCriticalSection(&touch1_lock); - dprintf("Wacca touch: init\n"); + dprintf("Wacca touch: Init\n"); uart_init(&touch0_uart, 3); touch0_uart.written.bytes = touch0_written_bytes; From f067066130735cf00f25dccf410e7819ae111f7f Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 5 Jun 2022 03:34:38 -0400 Subject: [PATCH 156/175] added ongeki build --- Package.mk | 17 ++++++++++++ dist/mu3/segatools.ini | 60 ++++++++++++++++++++++++++++++++++++++++++ dist/mu3/start.bat | 15 +++++++++++ 3 files changed, 92 insertions(+) create mode 100644 dist/mu3/segatools.ini create mode 100644 dist/mu3/start.bat diff --git a/Package.mk b/Package.mk index fcac6f9..53fd832 100644 --- a/Package.mk +++ b/Package.mk @@ -43,6 +43,22 @@ $(BUILD_DIR_ZIP)/mercury.zip: $(V)strip $(BUILD_DIR_ZIP)/mercury/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/mercury ; zip -r ../mercury.zip * + +$(BUILD_DIR_ZIP)/mu3.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/mu3 + $(V)mkdir -p $(BUILD_DIR_ZIP)/mu3/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/mu3hook/mu3hook.dll \ + $(DIST_DIR)/mu3/segatools.ini \ + $(DIST_DIR)/mu3/start.bat \ + $(BUILD_DIR_ZIP)/mu3 + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/mu3/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/mu3/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/mu3 ; zip -r ../mu3.zip * + $(BUILD_DIR_ZIP)/doc.zip: \ $(DOC_DIR)/config \ $(DOC_DIR)/chunihook.md \ @@ -56,6 +72,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/doc.zip \ $(BUILD_DIR_ZIP)/idz.zip \ $(BUILD_DIR_ZIP)/mercury.zip \ + $(BUILD_DIR_ZIP)/mu3.zip \ CHANGELOG.md \ README.md \ diff --git a/dist/mu3/segatools.ini b/dist/mu3/segatools.ini new file mode 100644 index 0000000..3279509 --- /dev/null +++ b/dist/mu3/segatools.ini @@ -0,0 +1,60 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs=amfs +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata=appdata +option=option + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[ds] +; Region code on the emulated AMEX board DS EEPROM. +; 1: Japan +; 4: Export (some UI elements in English) +; +; NOTE: Changing this setting causes a factory reset. +region=1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; SEGA games are somewhat picky about their LAN environment, so leaving this +; setting enabled is recommended. +enable=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.250.0 + +[gfx] +enable=1 + +[io4] +; Input API selection for JVS input emulator. +; Set "1" to use a xinput gamepad and set "2" to use keyboard. +mode=2 + +test=0x31 +service=0x32 + +[dinput] +LEFT_A=0x53 +LEFT_B=0x44 +LEFT_C=0x46 +LEFT_MENU=0x51 +LEFT_SIDE=0x52 +RIGHT_A=0x4A +RIGHT_B=0x4B +RIGHT_C=0x4C +RIGHT_MENU=0x50 +RIGHT_SIDE=0x55 +SLIDER_LEFT=0x54 +SLIDER_RIGHT=0x59 +;Change move speed of slider when use dinput +SLIDER_SPEED=1000 \ No newline at end of file diff --git a/dist/mu3/start.bat b/dist/mu3/start.bat new file mode 100644 index 0000000..c00f2ea --- /dev/null +++ b/dist/mu3/start.bat @@ -0,0 +1,15 @@ +@echo off +pushd %~dp0 + +taskkill /f /im amdaemon.exe > nul 2>&1 + +REM USA +REM start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_usa.json + +REM JP +start inject -d -k mercuryhook.dll amdaemon.exe -f -c config.json config_lan_install_client.json config_lan_install_server.json config_video_clone.json config_video_dual.json config_video_clone_flip.json config_video_dual_flip.json config_region_exp.json config_region_chn.json config_region_jpn.json +inject -d -k mercuryhook.dll ../WindowsNoEditor/Mercury/Binaries/Win64/Mercury-Win64-Shipping.exe + +taskkill /f /im amdaemon.exe > nul 2>&1 + +echo Game processes have terminated \ No newline at end of file From 9f0d6104d2652009e518f16620e9d8914815cef9 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 5 Jun 2022 03:42:35 -0400 Subject: [PATCH 157/175] Add Bemani Witch's felica fix --- iccard/felica.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/iccard/felica.c b/iccard/felica.c index ba71232..c170e3d 100644 --- a/iccard/felica.c +++ b/iccard/felica.c @@ -31,9 +31,13 @@ uint64_t felica_get_generic_PMm(void) /* A FeliCa PMm contains low-level protocol timing information for communicating with a particular IC card. The exact values are not particularly important for our purposes, so we'll just return a hard- - coded PMm. This was taken from a SuiCa train pass I bought in Japan. */ + coded PMm. This current value has been taken from an iPhone, emulating + a suica pass via apple wallet, which seems to be one of the few + universally accepted felica types for these games. Certain older + suica passes etc do not seem to be supported anymore. */ + + return 0x01168B868FBECBFFULL; - return 0x053143454682B7FFULL; } HRESULT felica_transact( From 37f3ea566a22c2be128659b8140ef2938ae5191c Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 5 Jun 2022 04:24:34 -0400 Subject: [PATCH 158/175] Wacca: Coins insert, but releaseing the button inserts 15 --- mercuryhook/io4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index fa146db..66c1494 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -63,8 +63,8 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) state->buttons[0] |= IO4_BUTTON_SERVICE; } - if (gamebtn & MERCURY_IO_OPBTN_COIN) { - state->chutes[0] |= 1 << 1; // FIXME: insert coins + if (opbtn & MERCURY_IO_OPBTN_COIN) { + state->chutes[0] |= 1 << 8; // FIXME: Inserts 15 extra coins on release of button } if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { From c3c56ef999ccafa69d30877cda09ef4b01a10495 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Thu, 23 Jun 2022 02:32:10 -0400 Subject: [PATCH 159/175] reflect wacca's actual subnet, game doesn't seem to care either way though --- dist/mercury/segatools.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index 0b9ffdd..fbb238c 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -30,7 +30,7 @@ enable=1 ; The /24 LAN subnet that the emulated keychip will tell the game to expect. ; If you disable netenv then you must set this to your LAN's IP subnet, and ; that subnet must start with 192.168. -subnet=192.168.250.0 +subnet=192.168.174.0 [gfx] enable=1 From 005f010861953e83b71d0726cd97810ffd139265 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Thu, 28 Jul 2022 16:40:44 +0000 Subject: [PATCH 160/175] Wacca: UNTESTED coin addition fix --- mercuryhook/io4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index 66c1494..876a81e 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -61,10 +61,10 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) if (opbtn & MERCURY_IO_OPBTN_SERVICE) { state->buttons[0] |= IO4_BUTTON_SERVICE; - } + } if (opbtn & MERCURY_IO_OPBTN_COIN) { - state->chutes[0] |= 1 << 8; // FIXME: Inserts 15 extra coins on release of button + state->chutes[0] += 256; // TODO: Test } if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { From c28c36dc991c7bc90866aaef0c34575a9f4df825 Mon Sep 17 00:00:00 2001 From: xpeng <1216772231@qq.com> Date: Sun, 31 Jul 2022 18:11:24 +0000 Subject: [PATCH 161/175] Add led support for mercuryio.dll --- Package.mk | 1 + dist/mercury/segatools.ini | 7 +++++++ mercuryhook/elisabeth.c | 12 ++++++++++++ mercuryhook/elisabeth.h | 5 +++++ mercuryhook/mercury-dll.c | 3 +++ mercuryhook/mercury-dll.h | 2 ++ mercuryhook/mercuryhook.def | 1 + mercuryio/mercuryio.c | 6 ++++++ mercuryio/mercuryio.def | 11 +++++++++++ mercuryio/mercuryio.h | 4 ++++ mercuryio/meson.build | 9 +++++++++ 11 files changed, 61 insertions(+) create mode 100644 mercuryio/mercuryio.def diff --git a/Package.mk b/Package.mk index 53fd832..85f6b80 100644 --- a/Package.mk +++ b/Package.mk @@ -34,6 +34,7 @@ $(BUILD_DIR_ZIP)/mercury.zip: $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \ + $(BUILD_DIR_64)/mercuryio/mercuryio.dll \ $(DIST_DIR)/mercury/segatools.ini \ $(DIST_DIR)/mercury/start.bat \ $(BUILD_DIR_ZIP)/mercury diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index fbb238c..9fc5bba 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -43,3 +43,10 @@ coin=0x24 volup=0x26 voldown=0x28 +[touch] +; Enable or disable touch hook +enable=1 + +;[mercuryio] +; Use mercuryio.dll +;path=mercuryio.dll \ No newline at end of file diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index 71c4a05..baac575 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -23,6 +23,7 @@ static void dll_hook_insert_hooks(HMODULE target); static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); static int my_USBIntLED_Init(); +static int my_USBIntLED_set(); static const struct hook_symbol win32_hooks[] = { { @@ -59,6 +60,10 @@ FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) if (strcmp(name, "USBIntLED_Init") == 0) { result = (FARPROC) my_USBIntLED_Init; } + + if (strcmp(name, "USBIntLED_set") == 0) { + result = (FARPROC) my_USBIntLED_set; + } } return result; @@ -70,3 +75,10 @@ static int my_USBIntLED_Init() dprintf("Elisabeth: my_USBIntLED_Init hit!\n"); return 1; } + +static int my_USBIntLED_set(int data1, struct led_data data2) +{ + assert(mercury_dll.set_leds != NULL); + mercury_dll.set_leds(data2); + return 1; +} diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index 9692c34..41066fc 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -1,3 +1,8 @@ #pragma once +struct led_data { + DWORD unitCount; + uint8_t rgba[480 * 4]; +}; + HRESULT elisabeth_hook_init(); diff --git a/mercuryhook/mercury-dll.c b/mercuryhook/mercury-dll.c index 19c183a..47d3eab 100644 --- a/mercuryhook/mercury-dll.c +++ b/mercuryhook/mercury-dll.c @@ -27,6 +27,9 @@ const struct dll_bind_sym mercury_dll_syms[] = { }, { .sym = "mercury_io_touch_start", .off = offsetof(struct mercury_dll, touch_start), + }, { + .sym = "mercury_io_touch_set_leds", + .off = offsetof(struct mercury_dll, set_leds), } }; diff --git a/mercuryhook/mercury-dll.h b/mercuryhook/mercury-dll.h index 086fd92..fa27edb 100644 --- a/mercuryhook/mercury-dll.h +++ b/mercuryhook/mercury-dll.h @@ -3,6 +3,7 @@ #include #include "mercuryio/mercuryio.h" +#include "mercuryhook/elisabeth.h" struct mercury_dll { uint16_t api_version; @@ -12,6 +13,7 @@ struct mercury_dll { void (*get_gamebtns)(uint8_t *gamebtn); HRESULT (*touch_init)(void); void (*touch_start)(mercury_io_touch_callback_t callback); + void (*set_leds)(struct led_data data); }; struct mercury_dll_config { diff --git a/mercuryhook/mercuryhook.def b/mercuryhook/mercuryhook.def index b12bc79..32f33c1 100644 --- a/mercuryhook/mercuryhook.def +++ b/mercuryhook/mercuryhook.def @@ -16,5 +16,6 @@ EXPORTS mercury_io_get_opbtns mercury_io_touch_init mercury_io_touch_start + mercury_io_touch_set_leds mercury_io_init mercury_io_poll diff --git a/mercuryio/mercuryio.c b/mercuryio/mercuryio.c index 9ad755b..6d38ff1 100644 --- a/mercuryio/mercuryio.c +++ b/mercuryio/mercuryio.c @@ -6,6 +6,7 @@ #include "mercuryio/mercuryio.h" #include "mercuryio/config.h" +#include "mercuryhook/elisabeth.h" static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx); @@ -90,6 +91,11 @@ void mercury_io_touch_start(mercury_io_touch_callback_t callback) ); } +void mercury_io_touch_set_leds(struct led_data data) +{ + +} + static unsigned int __stdcall mercury_io_touch_thread_proc(void *ctx) { mercury_io_touch_callback_t callback; diff --git a/mercuryio/mercuryio.def b/mercuryio/mercuryio.def new file mode 100644 index 0000000..167d1cf --- /dev/null +++ b/mercuryio/mercuryio.def @@ -0,0 +1,11 @@ +LIBRARY mercuryio + +EXPORTS + mercury_io_get_api_version + mercury_io_init + mercury_io_poll + mercury_io_get_opbtns + mercury_io_get_gamebtns + mercury_io_touch_init + mercury_io_touch_start + mercury_io_touch_set_leds \ No newline at end of file diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 2701ec4..1031698 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -4,6 +4,8 @@ #include +#include "mercuryhook/elisabeth.h" + enum { MERCURY_IO_OPBTN_TEST = 0x01, MERCURY_IO_OPBTN_SERVICE = 0x02, @@ -64,3 +66,5 @@ void mercury_io_get_gamebtns(uint8_t *gamebtn); HRESULT mercury_io_touch_init(void); void mercury_io_touch_start(mercury_io_touch_callback_t callback); + +void mercury_io_touch_set_leds(struct led_data data); diff --git a/mercuryio/meson.build b/mercuryio/meson.build index 813958a..4e6c0eb 100644 --- a/mercuryio/meson.build +++ b/mercuryio/meson.build @@ -11,3 +11,12 @@ mercuryio_lib = static_library( 'config.h', ], ) + +shared_library( + 'mercuryio', + name_prefix : '', + vs_module_defs : 'mercuryio.def', + link_with : [ + mercuryio_lib, + ], +) \ No newline at end of file From e677b9ed5bebe9ced29e3c615bd8819d013b0182 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Thu, 11 Aug 2022 23:58:24 -0400 Subject: [PATCH 162/175] wacca: io is now part of mercuryhook.dll --- Package.mk | 1 - mercuryhook/io4.c | 2 +- mercuryio/meson.build | 9 --------- platform/misc.c | 10 ++++++++++ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Package.mk b/Package.mk index 85f6b80..53fd832 100644 --- a/Package.mk +++ b/Package.mk @@ -34,7 +34,6 @@ $(BUILD_DIR_ZIP)/mercury.zip: $(V)mkdir -p $(BUILD_DIR_ZIP)/mercury/DEVICE $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ $(BUILD_DIR_64)/mercuryhook/mercuryhook.dll \ - $(BUILD_DIR_64)/mercuryio/mercuryio.dll \ $(DIST_DIR)/mercury/segatools.ini \ $(DIST_DIR)/mercury/start.bat \ $(BUILD_DIR_ZIP)/mercury diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index 876a81e..092a037 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -64,7 +64,7 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) } if (opbtn & MERCURY_IO_OPBTN_COIN) { - state->chutes[0] += 256; // TODO: Test + state->chutes[0] += 1 << 8; // FIXME: Inserts 1 credit on press, then 15 on release... } if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { diff --git a/mercuryio/meson.build b/mercuryio/meson.build index 4e6c0eb..2970fa9 100644 --- a/mercuryio/meson.build +++ b/mercuryio/meson.build @@ -10,13 +10,4 @@ mercuryio_lib = static_library( 'config.c', 'config.h', ], -) - -shared_library( - 'mercuryio', - name_prefix : '', - vs_module_defs : 'mercuryio.def', - link_with : [ - mercuryio_lib, - ], ) \ No newline at end of file diff --git a/platform/misc.c b/platform/misc.c index f4bc87d..040a249 100644 --- a/platform/misc.c +++ b/platform/misc.c @@ -20,6 +20,7 @@ static HRESULT misc_read_app_loader_count(void *bytes, uint32_t *nbytes); static HRESULT misc_read_cpu_temp_error(void *bytes, uint32_t *nbytes); static HRESULT misc_read_cpu_temp_warning(void *bytes, uint32_t *nbytes); static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes); +static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes); static const struct hook_symbol misc_syms[] = { { @@ -65,6 +66,10 @@ static const struct reg_hook_val misc_static_keys[] = { .name = L"PlatformId", .read = misc_read_platform_id, .type = REG_SZ, + }, { + .name = L"PlatformName", + .read = misc_read_platform_name, + .type = REG_SZ, } }; @@ -160,3 +165,8 @@ static HRESULT misc_read_platform_id(void *bytes, uint32_t *nbytes) { return reg_hook_read_wstr(bytes, nbytes, misc_platform_id); } + +static HRESULT misc_read_platform_name(void *bytes, uint32_t *nbytes) +{ + return reg_hook_read_wstr(bytes, nbytes, L"ALLS MX2.1"); // TODO: Dynamic +} From 5a5ffee8193f46e3a0a6a99c53555559eff4e24d Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Fri, 18 Nov 2022 16:37:48 -0500 Subject: [PATCH 163/175] mercury: fix coin counter (thanks Raki!) --- hooklib/reg.c | 94 +++++++++++++++++++++++++++++++++++++++++++ mercuryhook/io4.c | 14 ++++++- mercuryio/mercuryio.h | 1 + 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/hooklib/reg.c b/hooklib/reg.c index 6028e8e..cc3c584 100644 --- a/hooklib/reg.c +++ b/hooklib/reg.c @@ -89,6 +89,16 @@ static LSTATUS WINAPI hook_RegSetValueExW( const void *bytes, uint32_t nbytes); +static LSTATUS WINAPI hook_RegGetValueW( + HKEY hkey, + LPCWSTR lpSubKey, + LPCWSTR lpValue, + uint32_t flags, + uint32_t *type, + void *pData, + uint32_t *numData +); + /* Link pointers */ static LSTATUS (WINAPI *next_RegOpenKeyExW)( @@ -135,6 +145,16 @@ static LSTATUS (WINAPI *next_RegSetValueExW)( const void *bytes, uint32_t nbytes); +static LSTATUS (WINAPI *next_RegGetValueW)( + HKEY hkey, + LPCWSTR lpSubKey, + LPCWSTR lpValue, + uint32_t flags, + uint32_t *type, + void *pData, + uint32_t *numData +); + static const struct hook_symbol reg_hook_syms[] = { { .name = "RegOpenKeyExW", @@ -160,6 +180,10 @@ static const struct hook_symbol reg_hook_syms[] = { .name = "RegSetValueExW", .patch = hook_RegSetValueExW, .link = (void **) &next_RegSetValueExW, + }, { + .name = "RegGetValueW", + .patch = hook_RegGetValueW, + .link = (void **) &next_RegGetValueW, } }; @@ -184,6 +208,12 @@ HRESULT reg_hook_push_key( reg_hook_init(); + /*dprintf("Pushing reg key %ls:\n", name); + + for (int i = 0; i < nvals; i++) { + dprintf("\t%ls\n", vals[i].name); + } */ + EnterCriticalSection(®_hook_lock); new_mem = realloc( @@ -222,6 +252,7 @@ static void reg_hook_init(void) reg_hook_initted = true; InitializeCriticalSection(®_hook_lock); + dprintf("Reg hook init\n"); hook_table_apply( NULL, @@ -727,6 +758,69 @@ static LSTATUS WINAPI hook_RegSetValueExW( return err; } +static LSTATUS WINAPI hook_RegGetValueW( + HKEY handle, + LPCWSTR subkey, + LPCWSTR name, + uint32_t flags, + uint32_t *type, + void *pData, + uint32_t *numData) +{ + struct reg_hook_key *key; + HKEY tmp = NULL; + const struct reg_hook_val *val; + LSTATUS err; + + EnterCriticalSection(®_hook_lock); + //dprintf("Registry: RegGetValueW lookup for %ls\\%ls\n", subkey, name); + + if (subkey == NULL) { + key = reg_hook_match_key_locked(handle); + } else { + err = hook_RegOpenKeyExW(handle, subkey, flags, 1, &tmp); + key = reg_hook_match_key_locked(tmp); + } + + //dprintf("Registry: RegGetValueW key is %ls", key->name); + + if (key == NULL) { + LeaveCriticalSection(®_hook_lock); + dprintf("Registry: RegGetValueW Failed to find %ls\\%ls, passing on\n", subkey, name); + + return next_RegGetValueW( + handle, + subkey, + name, + flags, + type, + pData, + numData); + } + + val = reg_hook_match_val_locked(key, name); + + if (val != NULL) { + //dprintf("Registry: RegGetValueW found %ls\\%ls!\n", subkey, name); + + if (val->read != NULL) { + val->read(pData, numData); + + if (tmp != NULL) { + hook_RegCloseKey(tmp); + } + + LeaveCriticalSection(®_hook_lock); + err = ERROR_SUCCESS; + return err; + } + } + + LeaveCriticalSection(®_hook_lock); + err = ERROR_NOT_FOUND; + return err; +} + HRESULT reg_hook_read_bin( void *bytes, uint32_t *nbytes, diff --git a/mercuryhook/io4.c b/mercuryhook/io4.c index 092a037..df22656 100644 --- a/mercuryhook/io4.c +++ b/mercuryhook/io4.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "board/io4.h" @@ -10,6 +11,9 @@ #include "util/dprintf.h" +bool mercury_io_coin = false; +uint16_t mercury_io_coins = 0; + static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state); static const struct io4_ops mercury_io4_ops = { @@ -64,8 +68,16 @@ static HRESULT mercury_io4_poll(void *ctx, struct io4_state *state) } if (opbtn & MERCURY_IO_OPBTN_COIN) { - state->chutes[0] += 1 << 8; // FIXME: Inserts 1 credit on press, then 15 on release... + if (!mercury_io_coin) { + mercury_io_coin = true; + mercury_io_coins++; + } } + else { + mercury_io_coin = false; + } + + state->chutes[0] = 128 + 256 * mercury_io_coins; if (gamebtn & MERCURY_IO_GAMEBTN_VOL_UP) { state->buttons[0] |= 1 << 1; diff --git a/mercuryio/mercuryio.h b/mercuryio/mercuryio.h index 1031698..4d029ca 100644 --- a/mercuryio/mercuryio.h +++ b/mercuryio/mercuryio.h @@ -3,6 +3,7 @@ #include #include +#include #include "mercuryhook/elisabeth.h" From 59edd71bb22bb6fe364add471d843e073869d273 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sat, 10 Dec 2022 21:50:11 -0500 Subject: [PATCH 164/175] remove image after compile to save space --- docker-build.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-build.bat b/docker-build.bat index 62348bc..eea0e31 100644 --- a/docker-build.bat +++ b/docker-build.bat @@ -25,6 +25,7 @@ mkdir "!BUILD_OUTPUT_PATH!" docker cp %CONTAINER_NAME%:/segatools/build/zip %BUILD_OUTPUT_PATH% docker rm -f %CONTAINER_NAME% > nul +docker image rm -f %IMAGE_NAME% > nul goto success From 8736ec90af3a6942c985e601c4b53af5461b4cd9 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sat, 10 Dec 2022 22:01:52 -0500 Subject: [PATCH 165/175] Add carolhook for Wonderland Wars --- .vscode/settings.json | 27 +++++- Dockerfile | 2 + Package.mk | 32 +++++++ carolhook/carol-dll.c | 112 ++++++++++++++++++++++++ carolhook/carol-dll.h | 22 +++++ carolhook/carolhook.def | 19 +++++ carolhook/config.c | 76 +++++++++++++++++ carolhook/config.h | 33 ++++++++ carolhook/controlbd.c | 178 +++++++++++++++++++++++++++++++++++++++ carolhook/controlbd.h | 21 +++++ carolhook/dllmain.c | 131 ++++++++++++++++++++++++++++ carolhook/jvs.c | 109 ++++++++++++++++++++++++ carolhook/jvs.h | 7 ++ carolhook/meson.build | 38 +++++++++ carolhook/serial.c | 39 +++++++++ carolhook/serial.h | 5 ++ carolhook/touch.c | 118 ++++++++++++++++++++++++++ carolhook/touch.h | 17 ++++ carolio/carolio.c | 79 +++++++++++++++++ carolio/carolio.h | 52 ++++++++++++ carolio/config.c | 20 +++++ carolio/config.h | 16 ++++ carolio/meson.build | 13 +++ dist/carol/segatools.ini | 48 +++++++++++ dist/carol/start.bat | 13 +++ dist/diva/segatools.ini | 54 ++++++++++++ dist/diva/start.bat | 9 ++ meson.build | 2 + platform/amvideo.c | 5 ++ 29 files changed, 1296 insertions(+), 1 deletion(-) create mode 100644 carolhook/carol-dll.c create mode 100644 carolhook/carol-dll.h create mode 100644 carolhook/carolhook.def create mode 100644 carolhook/config.c create mode 100644 carolhook/config.h create mode 100644 carolhook/controlbd.c create mode 100644 carolhook/controlbd.h create mode 100644 carolhook/dllmain.c create mode 100644 carolhook/jvs.c create mode 100644 carolhook/jvs.h create mode 100644 carolhook/meson.build create mode 100644 carolhook/serial.c create mode 100644 carolhook/serial.h create mode 100644 carolhook/touch.c create mode 100644 carolhook/touch.h create mode 100644 carolio/carolio.c create mode 100644 carolio/carolio.h create mode 100644 carolio/config.c create mode 100644 carolio/config.h create mode 100644 carolio/meson.build create mode 100644 dist/carol/segatools.ini create mode 100644 dist/carol/start.bat create mode 100644 dist/diva/segatools.ini create mode 100644 dist/diva/start.bat diff --git a/.vscode/settings.json b/.vscode/settings.json index 7c2feb7..487d6ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,28 @@ { - "editor.formatOnSave": false + "editor.formatOnSave": false, + "files.associations": { + "assert.h": "c", + "config.h": "c", + "backend.h": "c", + "idacio.h": "c", + "idac-dll.h": "c", + "d3d11.h": "c", + "mu3-dll.h": "c", + "printer.h": "c", + "netenv.h": "c", + "stdint.h": "c", + "string.h": "c", + "platform.h": "c", + "jvs.h": "c", + "sg-reader.h": "c", + "carol-dll.h": "c", + "slider.h": "c", + "slider-cmd.h": "c", + "touch.h": "c", + "iobd.h": "c", + "stdbool.h": "c", + "sg-frame.h": "c", + "sg-cmd.h": "c", + "controlbd.h": "c" + } } diff --git a/Dockerfile b/Dockerfile index 279c6b6..5a3ff66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,8 @@ COPY chuniio chuniio COPY dist dist COPY divahook divahook COPY divaio divaio +COPY carolhook carolhook +COPY carolio carolio COPY doc doc COPY hooklib hooklib COPY iccard iccard diff --git a/Package.mk b/Package.mk index 53fd832..4f21e64 100644 --- a/Package.mk +++ b/Package.mk @@ -12,6 +12,36 @@ $(BUILD_DIR_ZIP)/chuni.zip: $(BUILD_DIR_ZIP)/chuni/DEVICE $(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip * + +$(BUILD_DIR_ZIP)/diva.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/diva + $(V)mkdir -p $(BUILD_DIR_ZIP)/diva/DEVICE + $(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_64)/divahook/divahook.dll \ + $(DIST_DIR)/diva/segatools.ini \ + $(DIST_DIR)/diva/start.bat \ + $(BUILD_DIR_ZIP)/diva + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/diva/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/diva/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/diva ; zip -r ../diva.zip * + +$(BUILD_DIR_ZIP)/carol.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/carol + $(V)mkdir -p $(BUILD_DIR_ZIP)/carol/DEVICE + $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_32)/carolhook/carolhook.dll \ + $(DIST_DIR)/carol/segatools.ini \ + $(DIST_DIR)/carol/start.bat \ + $(BUILD_DIR_ZIP)/carol + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/carol/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/carol/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/carol ; zip -r ../carol.zip * $(BUILD_DIR_ZIP)/idz.zip: $(V)echo ... $@ @@ -69,6 +99,8 @@ $(BUILD_DIR_ZIP)/doc.zip: \ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/chuni.zip \ + $(BUILD_DIR_ZIP)/carol.zip \ + $(BUILD_DIR_ZIP)/diva.zip \ $(BUILD_DIR_ZIP)/doc.zip \ $(BUILD_DIR_ZIP)/idz.zip \ $(BUILD_DIR_ZIP)/mercury.zip \ diff --git a/carolhook/carol-dll.c b/carolhook/carol-dll.c new file mode 100644 index 0000000..5cc8e98 --- /dev/null +++ b/carolhook/carol-dll.c @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "carolhook/carol-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym carol_dll_syms[] = { + { + .sym = "carol_io_jvs_init", + .off = offsetof(struct carol_dll, jvs_init), + }, { + .sym = "carol_io_jvs_poll", + .off = offsetof(struct carol_dll, jvs_poll), + }, { + .sym = "carol_io_jvs_read_coin_counter", + .off = offsetof(struct carol_dll, jvs_read_coin_counter), + }, { + .sym = "carol_io_touch_init", + .off = offsetof(struct carol_dll, touch_init), + }, { + .sym = "carol_io_controlbd_init", + .off = offsetof(struct carol_dll, controlbd_init), + } +}; + +struct carol_dll carol_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT carol_dll_init(const struct carol_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("carol IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("carol IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "carol_io_get_api_version"); + + if (get_api_version != NULL) { + carol_dll.api_version = get_api_version(); + } else { + carol_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose carol_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (carol_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("carol IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + carol_dll.api_version); + + goto end; + } + + sym = carol_dll_syms; + hr = dll_bind(&carol_dll, src, &sym, _countof(carol_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("carol IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/carolhook/carol-dll.h b/carolhook/carol-dll.h new file mode 100644 index 0000000..095a1fd --- /dev/null +++ b/carolhook/carol-dll.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "carolio/carolio.h" + +struct carol_dll { + uint16_t api_version; + HRESULT (*jvs_init)(void); + void (*jvs_poll)(uint8_t *opbtn, uint8_t *beams); + void (*jvs_read_coin_counter)(uint16_t *total); + HRESULT (*touch_init)(); + HRESULT (*controlbd_init)(); +}; + +struct carol_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct carol_dll carol_dll; + +HRESULT carol_dll_init(const struct carol_dll_config *cfg, HINSTANCE self); diff --git a/carolhook/carolhook.def b/carolhook/carolhook.def new file mode 100644 index 0000000..3c324c4 --- /dev/null +++ b/carolhook/carolhook.def @@ -0,0 +1,19 @@ +LIBRARY carolhook + +EXPORTS + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 + carol_io_get_api_version + carol_io_jvs_init + carol_io_jvs_poll + carol_io_jvs_read_coin_counter + carol_io_touch_init + carol_io_controlbd_init diff --git a/carolhook/config.c b/carolhook/config.c new file mode 100644 index 0000000..740a162 --- /dev/null +++ b/carolhook/config.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include "amex/amex.h" +#include "amex/config.h" +#include "gfxhook/config.h" + +#include "board/config.h" +#include "board/sg-reader.h" + +#include "carolhook/config.h" + +#include "platform/config.h" +#include "platform/platform.h" + +void carol_dll_config_load( + struct carol_dll_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"carolio", + L"path", + L"", + cfg->path, + _countof(cfg->path), + filename); +} + +void touch_config_load( + struct touch_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW( + L"touch", + L"enable", + 1, + filename); +} + +void controlbd_config_load( + struct controlbd_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW( + L"controlbd", + L"enable", + 1, + filename); +} + + +void carol_hook_config_load( + struct carol_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + platform_config_load(&cfg->platform, filename); + amex_config_load(&cfg->amex, filename); + aime_config_load(&cfg->aime, filename); + carol_dll_config_load(&cfg->dll, filename); + gfx_config_load(&cfg->gfx, filename); + touch_config_load(&cfg->touch, filename); + controlbd_config_load(&cfg->controlbd, filename); +} diff --git a/carolhook/config.h b/carolhook/config.h new file mode 100644 index 0000000..50b7f0b --- /dev/null +++ b/carolhook/config.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" + +#include "carolhook/carol-dll.h" + +#include "platform/platform.h" + +#include "gfxhook/gfx.h" + +#include "carolhook/touch.h" +#include "carolhook/controlbd.h" + +struct carol_hook_config { + struct platform_config platform; + struct amex_config amex; + struct aime_config aime; + struct carol_dll_config dll; + struct gfx_config gfx; + struct touch_config touch; + struct controlbd_config controlbd; +}; + +void carol_dll_config_load( + struct carol_dll_config *cfg, + const wchar_t *filename); +void carol_hook_config_load( + struct carol_hook_config *cfg, + const wchar_t *filename); diff --git a/carolhook/controlbd.c b/carolhook/controlbd.c new file mode 100644 index 0000000..44fa5a6 --- /dev/null +++ b/carolhook/controlbd.c @@ -0,0 +1,178 @@ +#include + +#include +#include +#include +#include + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "carolhook/carol-dll.h" +#include "carolhook/controlbd.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT controlbd_handle_irp(struct irp *irp); +static HRESULT controlbd_handle_irp_locked(struct irp *irp); +static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf); +static HRESULT controlbd_frame_dispatch(struct controlbd_req *dest); + +static HRESULT controlbd_req_nop(uint8_t cmd); + +static CRITICAL_SECTION controlbd_lock; +static struct uart controlbd_uart; +static uint8_t controlbd_written_bytes[520]; +static uint8_t controlbd_readable_bytes[520]; + +HRESULT controlbd_hook_init(const struct controlbd_config *cfg) +{ + if (!cfg->enable) { + return S_OK; + } + + InitializeCriticalSection(&controlbd_lock); + + uart_init(&controlbd_uart, 11); + controlbd_uart.written.bytes = controlbd_written_bytes; + controlbd_uart.written.nbytes = sizeof(controlbd_written_bytes); + controlbd_uart.readable.bytes = controlbd_readable_bytes; + controlbd_uart.readable.nbytes = sizeof(controlbd_readable_bytes); + + dprintf("Control Board: Init\n"); + + return iohook_push_handler(controlbd_handle_irp); +} + +static HRESULT controlbd_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&controlbd_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&controlbd_lock); + hr = controlbd_handle_irp_locked(irp); + LeaveCriticalSection(&controlbd_lock); + + return hr; +} + +static HRESULT controlbd_handle_irp_locked(struct irp *irp) +{ + struct controlbd_req req; + HRESULT hr; + + assert(carol_dll.controlbd_init != NULL); + + if (irp->op == IRP_OP_OPEN) { + dprintf("Control Board: Starting backend DLL\n"); + hr = carol_dll.controlbd_init(); + + if (FAILED(hr)) { + dprintf("Control Board: Backend DLL error: %x\n", (int) hr); + + return hr; + } + } + + hr = uart_handle_irp(&controlbd_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("Control Board: TX Buffer:\n"); + dump_iobuf(&controlbd_uart.written); +#endif + hr = controlbd_frame_decode(&req, &controlbd_uart.written); + + if (FAILED(hr)) { + dprintf("Control Board: Deframe Error: %x\n", (int) hr); + + return hr; + } + + hr = controlbd_frame_dispatch(&req); + if (FAILED(hr)) { + dprintf("Control Board: Dispatch Error: %x\n", (int) hr); + + return hr; + } + + return hr; + } +} + +static HRESULT controlbd_frame_dispatch(struct controlbd_req *req) +{ + switch (req->cmd) { + case CONTROLBD_CMD_UNK_11: + return controlbd_req_nop(req->cmd); + default: + dprintf("Unhandled command %#02x\n", req->cmd); + + return S_OK; + } +} + +static HRESULT controlbd_req_nop(uint8_t cmd) +{ + dprintf("Control Board: No-op cmd %#02x\n", cmd); + + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0xE0; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x11; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x03; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x10; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x01; + controlbd_uart.readable.bytes[controlbd_uart.readable.pos++] = 0x27; + + return S_OK; +} + +/* Decodes the response into a struct that's easier to work with. */ +static HRESULT controlbd_frame_decode(struct controlbd_req *dest, struct iobuf *iobuf) +{ + int initial_pos = iobuf->pos; + uint8_t check = 0; + + dest->sync = iobuf->bytes[0]; + iobuf->pos--; + + dest->cmd = iobuf->bytes[1]; + iobuf->pos--; + check += dest->cmd; + + dest->checksum = iobuf->bytes[initial_pos - 1]; + iobuf->pos--; + + dest->data_length = initial_pos - 3; // sync, cmd, checksum + if (dest->data_length > 0) { + for (int i = 0; i < dest->data_length; i++) { + dest->data[i] = iobuf->bytes[i+2]; + check += dest->data[i]; + } + } + iobuf->pos -= dest->data_length; + + if (dest->sync != 0xe0) { + dprintf("Control Board: Sync error, expected 0xe0, got %x\n", dest->sync); + return E_FAIL; + } + if (dest->checksum != check) { + dprintf("Control Board: Checksum error, expected %x, got %x\n", check, dest->checksum); + return E_FAIL; + } + + return S_OK; +} \ No newline at end of file diff --git a/carolhook/controlbd.h b/carolhook/controlbd.h new file mode 100644 index 0000000..500efd7 --- /dev/null +++ b/carolhook/controlbd.h @@ -0,0 +1,21 @@ +#pragma once +#include + +#include +#include + +struct controlbd_config { + bool enable; +}; +enum controlbd_cmd { + CONTROLBD_CMD_UNK_11 = 0x11 +}; +struct controlbd_req { + uint8_t sync; // First byte is the sync + uint8_t cmd; // Command byte + uint8_t data[256]; // Request body goes here + uint8_t checksum; // Final byte is all bytes added, except the sync + uint8_t data_length; // Size of the data including command byte +}; + +HRESULT controlbd_hook_init(const struct controlbd_config *cfg); \ No newline at end of file diff --git a/carolhook/dllmain.c b/carolhook/dllmain.c new file mode 100644 index 0000000..bb664d1 --- /dev/null +++ b/carolhook/dllmain.c @@ -0,0 +1,131 @@ +#include + +#include + +#include "amex/amex.h" +#include "gfxhook/gfx.h" +#include "gfxhook/d3d9.h" + +#include "board/sg-reader.h" + +#include "carolhook/config.h" +#include "carolhook/carol-dll.h" +#include "carolhook/jvs.h" +#include "carolhook/touch.h" +#include "carolhook/controlbd.h" +#include "carolhook/serial.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE carol_hook_mod; +static process_entry_t carol_startup; +static struct carol_hook_config carol_hook_cfg; + +/* +COM Layout +01:(?) Touchscreen +10: Aime reader +11: Control board +12(?): LED Board +*/ + +static DWORD CALLBACK carol_pre_startup(void) +{ + HRESULT hr; + + dprintf("--- Begin carol_pre_startup ---\n"); + + /* Config load */ + + carol_hook_config_load(&carol_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &carol_hook_cfg.platform, + "SDAP", + "AAV0", + carol_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = carol_dll_init(&carol_hook_cfg.dll, carol_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = amex_hook_init(&carol_hook_cfg.amex, carol_jvs_init); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&carol_hook_cfg.aime, 10, carol_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + gfx_hook_init(&carol_hook_cfg.gfx); + gfx_d3d9_hook_init(&carol_hook_cfg.gfx, carol_hook_mod); + //serial_init(); + + + hr = touch_hook_init(&carol_hook_cfg.touch); + + if (FAILED(hr)) { + goto fail; + } + + hr = controlbd_hook_init(&carol_hook_cfg.controlbd); + + if (FAILED(hr)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End carol_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return carol_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + carol_hook_mod = mod; + + hr = process_hijack_startup(carol_pre_startup, &carol_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/carolhook/jvs.c b/carolhook/jvs.c new file mode 100644 index 0000000..6aeb288 --- /dev/null +++ b/carolhook/jvs.c @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include + +#include "amex/jvs.h" + +#include "board/io3.h" + +#include "carolhook/carol-dll.h" + +#include "jvs/jvs-bus.h" + +#include "util/dprintf.h" + +static void carol_jvs_read_switches(void *ctx, struct io3_switch_state *out); +static void carol_jvs_read_coin_counter( + void *ctx, + uint8_t slot_no, + uint16_t *out); + +static const struct io3_ops carol_jvs_io3_ops = { + .read_switches = carol_jvs_read_switches, + .read_coin_counter = carol_jvs_read_coin_counter, +}; + +static struct io3 carol_jvs_io3; + +HRESULT carol_jvs_init(struct jvs_node **out) +{ + HRESULT hr; + + assert(out != NULL); + assert(carol_dll.jvs_init != NULL); + + dprintf("JVS I/O: Starting carol backend DLL\n"); + hr = carol_dll.jvs_init(); + + if (FAILED(hr)) { + dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr); + + return hr; + } + + io3_init(&carol_jvs_io3, NULL, &carol_jvs_io3_ops, NULL); + *out = io3_to_jvs_node(&carol_jvs_io3); + + return S_OK; +} + +static void carol_jvs_read_switches(void *ctx, struct io3_switch_state *out) +{ + uint8_t opbtn; + uint8_t gamebtn; + + assert(out != NULL); + assert(carol_dll.jvs_poll != NULL); + + opbtn = 0; + gamebtn = 0; + + carol_dll.jvs_poll(&opbtn, &gamebtn); + + if (gamebtn & 0x01) { + out->p1 |= 1 << 6; + } + + if (gamebtn & 0x02) { + out->p1 |= 1 << 7; + } + + if (gamebtn & 0x04) { + out->p1 |= 1 << 8; + } + + if (gamebtn & 0x08) { + out->p1 |= 1 << 9; + } + + if (gamebtn & 0x10) { + out->p1 |= 1 << 15; + } + + if (opbtn & 0x01) { + out->system = 0x80; + } else { + out->system = 0; + } + + if (opbtn & 0x02) { + out->p1 |= 1 << 14; + } +} + +static void carol_jvs_read_coin_counter( + void *ctx, + uint8_t slot_no, + uint16_t *out) +{ + assert(carol_dll.jvs_read_coin_counter != NULL); + + if (slot_no > 0) { + return; + } + + carol_dll.jvs_read_coin_counter(out); +} diff --git a/carolhook/jvs.h b/carolhook/jvs.h new file mode 100644 index 0000000..ef95817 --- /dev/null +++ b/carolhook/jvs.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "jvs/jvs-bus.h" + +HRESULT carol_jvs_init(struct jvs_node **root); diff --git a/carolhook/meson.build b/carolhook/meson.build new file mode 100644 index 0000000..ac4c3cd --- /dev/null +++ b/carolhook/meson.build @@ -0,0 +1,38 @@ +shared_library( + 'carolhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'carolhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + amex_lib, + board_lib, + carolio_lib, + hooklib_lib, + jvs_lib, + platform_lib, + util_lib, + gfxhook_lib, + ], + sources : [ + 'config.c', + 'config.h', + 'carol-dll.c', + 'carol-dll.h', + 'dllmain.c', + 'jvs.c', + 'jvs.h', + 'touch.c', + 'touch.h', + 'controlbd.c', + 'controlbd.h', + 'serial.c', + 'serial.h', + ], +) diff --git a/carolhook/serial.c b/carolhook/serial.c new file mode 100644 index 0000000..3a50907 --- /dev/null +++ b/carolhook/serial.c @@ -0,0 +1,39 @@ +#include +#include + +#include "hook/table.h" + +#include "util/dprintf.h" + +static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB); +static BOOL (WINAPI *next_SetCommState)(HANDLE hFile, LPDCB lpDCB); +static void com_hook_insert_hooks(HMODULE target); + +static const struct hook_symbol win32_hooks[] = { + { + .name = "SetCommState", + .patch = my_SetCommState, + .link = (void **) &next_SetCommState + } +}; + +void serial_init() +{ + com_hook_insert_hooks(NULL); + dprintf("Serial: Spy init\n"); +} + +static void com_hook_insert_hooks(HMODULE target) +{ + hook_table_apply( + target, + "kernel32.dll", + win32_hooks, + _countof(win32_hooks)); +} + +static BOOL WINAPI my_SetCommState(HANDLE hFile, LPDCB lpDCB) +{ + dprintf("Serial: my_SetCommState with baudrate %ld\n", lpDCB->BaudRate); + return next_SetCommState(hFile, lpDCB); +} \ No newline at end of file diff --git a/carolhook/serial.h b/carolhook/serial.h new file mode 100644 index 0000000..8a682ac --- /dev/null +++ b/carolhook/serial.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +void serial_init(); \ No newline at end of file diff --git a/carolhook/touch.c b/carolhook/touch.c new file mode 100644 index 0000000..8cbb8ca --- /dev/null +++ b/carolhook/touch.c @@ -0,0 +1,118 @@ +#include + +#include +#include +#include +#include + +#include "carolhook/carol-dll.h" +#include "carolhook/touch.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT touch_handle_irp(struct irp *irp); +static HRESULT touch_handle_irp_locked(struct irp *irp); +static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf); + +static CRITICAL_SECTION touch_lock; +static struct uart touch_uart; +static uint8_t touch_written_bytes[520]; +static uint8_t touch_readable_bytes[520]; + +HRESULT touch_hook_init(const struct touch_config *cfg) +{ + if (!cfg->enable) { + return S_OK; + } + + InitializeCriticalSection(&touch_lock); + + uart_init(&touch_uart, 1); + touch_uart.written.bytes = touch_written_bytes; + touch_uart.written.nbytes = sizeof(touch_written_bytes); + touch_uart.readable.bytes = touch_readable_bytes; + touch_uart.readable.nbytes = sizeof(touch_readable_bytes); + + dprintf("Touchscreen: Init\n"); + + return iohook_push_handler(touch_handle_irp); + return S_OK; +} + +static HRESULT touch_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + if (!uart_match_irp(&touch_uart, irp)) { + return iohook_invoke_next(irp); + } + + EnterCriticalSection(&touch_lock); + hr = touch_handle_irp_locked(irp); + LeaveCriticalSection(&touch_lock); + + return hr; +} + +static HRESULT touch_handle_irp_locked(struct irp *irp) +{ + struct touch_req req; + HRESULT hr; + + assert(carol_dll.touch_init != NULL); + + if (irp->op == IRP_OP_OPEN) { + dprintf("Touchscreen: Starting backend DLL\n"); + hr = carol_dll.touch_init(); + + if (FAILED(hr)) { + dprintf("Touchscreen: Backend DLL error: %x\n", (int) hr); + + return hr; + } + } + + hr = uart_handle_irp(&touch_uart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 1 + dprintf("Touchscreen: TX Buffer:\n"); + dump_iobuf(&touch_uart.written); +#endif + hr = touch_frame_decode(&req, &touch_uart.written); + + if (FAILED(hr)) { + dprintf("Touchscreen: Deframe Error: %x\n", (int) hr); + + return hr; + } + + return hr; + } +} + +/* Decodes the response into a struct that's easier to work with. */ +static HRESULT touch_frame_decode(struct touch_req *dest, struct iobuf *iobuf) +{ + dest->cmd = iobuf->bytes[0]; + iobuf->pos--; + dest->data_length = iobuf->pos; + + if (dest->data_length > 0) { + for (int i = 1; i < dest->data_length; i++) { + dest->data[i-1] = iobuf->bytes[i]; + } + } + iobuf->pos -= dest->data_length; + + return S_OK; +} \ No newline at end of file diff --git a/carolhook/touch.h b/carolhook/touch.h new file mode 100644 index 0000000..6881b8f --- /dev/null +++ b/carolhook/touch.h @@ -0,0 +1,17 @@ +#pragma once +#include + +#include +#include + +struct touch_config { + bool enable; +}; + +struct touch_req { + uint8_t cmd; // First byte is the command byte + uint8_t data[256]; // rest of the data goes here + uint8_t data_length; // Size of the data including command byte +}; + +HRESULT touch_hook_init(const struct touch_config *cfg); \ No newline at end of file diff --git a/carolio/carolio.c b/carolio/carolio.c new file mode 100644 index 0000000..1ae4817 --- /dev/null +++ b/carolio/carolio.c @@ -0,0 +1,79 @@ +#include + +#include +#include +#include +#include + +#include "carolio/carolio.h" +#include "carolio/config.h" + +static bool carol_io_coin; +static uint16_t carol_io_coins; +static struct carol_io_config carol_io_cfg; + +uint16_t carol_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT carol_io_jvs_init(void) +{ + carol_io_config_load(&carol_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +void carol_io_jvs_poll(uint8_t *opbtn_out, uint8_t *gamebtn_out) +{ + uint8_t opbtn; + uint8_t gamebtn; + size_t i; + + opbtn = 0; + + if (GetAsyncKeyState(carol_io_cfg.vk_test) & 0x8000) { + opbtn |= 1; + } + + if (GetAsyncKeyState(carol_io_cfg.vk_service) & 0x8000) { + opbtn |= 2; + } + + for (i = 0 ; i < _countof(carol_io_cfg.vk_buttons) ; i++) { + if (GetAsyncKeyState(carol_io_cfg.vk_buttons[i]) & 0x8000) { + gamebtn |= 1 << i; + } + } + + *opbtn_out = opbtn; + *gamebtn_out = gamebtn; +} + +void carol_io_jvs_read_coin_counter(uint16_t *out) +{ + if (out == NULL) { + return; + } + + if (GetAsyncKeyState(carol_io_cfg.vk_coin) & 0x8000) { + if (!carol_io_coin) { + carol_io_coin = true; + carol_io_coins++; + } + } else { + carol_io_coin = false; + } + + *out = carol_io_coins; +} + +HRESULT carol_io_touch_init() +{ + return S_OK; +} + +HRESULT carol_io_controlbd_init() +{ + return S_OK; +} \ No newline at end of file diff --git a/carolio/carolio.h b/carolio/carolio.h new file mode 100644 index 0000000..dbe1091 --- /dev/null +++ b/carolio/carolio.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include +#include + +/* Get the version of the Project carol IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0100. */ + +uint16_t carol_io_get_api_version(void); + +/* Initialize JVS-based input. This function will be called before any other + carol_io_jvs_*() function calls. Errors returned from this function will + manifest as a disconnected JVS bus. + + All subsequent calls may originate from arbitrary threads and some may + overlap with each other. Ensuring synchronization inside your IO DLL is + your responsibility. + + Minimum API version: 0x0100 */ + +HRESULT carol_io_jvs_init(void); + +/* Poll JVS input. + + opbtn returns the cabinet test/service state, where bit 0 is Test and Bit 1 + is Service. + + gamebtn bits, from least significant to most significant, are: + + Circle Cross Square Triangle Start UNUSED UNUSED UNUSED + + Minimum API version: 0x0100 */ + +void carol_io_jvs_poll(uint8_t *opbtn, uint8_t *gamebtn); + +/* Read the current state of the coin counter. This value should be incremented + for every coin detected by the coin acceptor mechanism. This count does not + need to persist beyond the lifetime of the process. + + Minimum API Version: 0x0100 */ + +void carol_io_jvs_read_coin_counter(uint16_t *out); + +HRESULT carol_io_touch_init(); + +HRESULT carol_io_controlbd_init(); \ No newline at end of file diff --git a/carolio/config.c b/carolio/config.c new file mode 100644 index 0000000..151e909 --- /dev/null +++ b/carolio/config.c @@ -0,0 +1,20 @@ +#include + +#include +#include +#include +#include + +#include "carolio/config.h" + +void carol_io_config_load( + struct carol_io_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); +} diff --git a/carolio/config.h b/carolio/config.h new file mode 100644 index 0000000..953277f --- /dev/null +++ b/carolio/config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct carol_io_config { + uint8_t vk_buttons[5]; + uint8_t vk_slider[8]; + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_coin; +}; + +void carol_io_config_load( + struct carol_io_config *cfg, + const wchar_t *filename); diff --git a/carolio/meson.build b/carolio/meson.build new file mode 100644 index 0000000..dcb81ab --- /dev/null +++ b/carolio/meson.build @@ -0,0 +1,13 @@ +carolio_lib = static_library( + 'carolio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + sources : [ + 'carolio.c', + 'carolio.h', + 'config.c', + 'config.h', + ], +) diff --git a/dist/carol/segatools.ini b/dist/carol/segatools.ini new file mode 100644 index 0000000..d43cfe0 --- /dev/null +++ b/dist/carol/segatools.ini @@ -0,0 +1,48 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata= + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[netenv] +; Simulate an ideal LAN environment. +enable=1 + +[gpio] +dipsw1=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.126.0 + +[gfx] +; Force the game to run windowed. +windowed=1 +; Add a frame to the game window if running windowed. +framed=1 +; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) +monitor=0 + +[aimeio] +; To use a custom card reader IO DLL enter its path here. +; Leave empty if you want to use Segatools built-in keyboard input. +path= + +[io3] +; 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 diff --git a/dist/carol/start.bat b/dist/carol/start.bat new file mode 100644 index 0000000..0fab950 --- /dev/null +++ b/dist/carol/start.bat @@ -0,0 +1,13 @@ +@echo off + +pushd %~dp0 + +taskkill /f /im aimeReaderHost.exe > nul 2>&1 + +start /min inject -d -k carolhook.dll aimeReaderHost.exe -p 10 +inject -d -k carolhook.dll carol_nu.exe +taskkill /f /im aimeReaderHost.exe > nul 2>&1 + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/dist/diva/segatools.ini b/dist/diva/segatools.ini new file mode 100644 index 0000000..e2608c0 --- /dev/null +++ b/dist/diva/segatools.ini @@ -0,0 +1,54 @@ +[vfs] +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata= + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[netenv] +; Simulate an ideal LAN environment. This may interfere with head-to-head play. +; Chunithm is extremely picky about its LAN environment, so leaving this +; setting enabled is strongly recommended. +enable=1 + +[gpio] +dipsw1=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.150.0 + +[slider] +cell1=0x51 +cell2=0x57 +cell3=0x45 +cell4=0x52 +cell5=0x55 +cell6=0x49 +cell7=0x4F +cell8=0x50 + +[buttons] +key1=0x27 +key2=0x28 +key3=0x25 +key4=0x26 +key5=0x20 + +; Sliders : <- QWER UIOP -> +; Triangle : Up arrow +; Square : Left Arrow +; Cross : Down Arrow +; Circle : Right arrow +; Enter : Space + diff --git a/dist/diva/start.bat b/dist/diva/start.bat new file mode 100644 index 0000000..72934f6 --- /dev/null +++ b/dist/diva/start.bat @@ -0,0 +1,9 @@ +@echo off + +pushd %~dp0 + +inject -d -k divahook.dll diva.exe + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/meson.build b/meson.build index caca31f..acf85ef 100644 --- a/meson.build +++ b/meson.build @@ -55,12 +55,14 @@ subdir('gfxhook') subdir('aimeio') subdir('chuniio') subdir('divaio') +subdir('carolio') subdir('idzio') subdir('mu3io') subdir('mercuryio') subdir('chunihook') subdir('divahook') +subdir('carolhook') subdir('idzhook') subdir('minihook') subdir('mu3hook') diff --git a/platform/amvideo.c b/platform/amvideo.c index ccd818f..001a52a 100644 --- a/platform/amvideo.c +++ b/platform/amvideo.c @@ -28,6 +28,11 @@ static const struct reg_hook_val amvideo_reg_vals[] = { .name = L"name", .read = amvideo_reg_read_name, .type = REG_SZ, + }, + { + .name = L"name_x86", + .read = amvideo_reg_read_name, + .type = REG_SZ, } }; From 8f0d21200acd606d1af680d1b1e9031d22ef7043 Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sat, 10 Dec 2022 22:57:09 -0500 Subject: [PATCH 166/175] fix chuni subnet --- dist/chuni/segatools.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/chuni/segatools.ini b/dist/chuni/segatools.ini index e94c405..7385546 100644 --- a/dist/chuni/segatools.ini +++ b/dist/chuni/segatools.ini @@ -23,7 +23,7 @@ enable=1 ; The /24 LAN subnet that the emulated keychip will tell the game to expect. ; If you disable netenv then you must set this to your LAN's IP subnet, and ; that subnet must start with 192.168. -subnet=192.168.100.0 +subnet=192.168.139.0 [gfx] ; Force the game to run windowed. From dce5bbe66d7c49cc7095ca6ef183a13d749a3e3c Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sat, 10 Dec 2022 23:21:54 -0500 Subject: [PATCH 167/175] platform: make billing type configurable --- doc/config/common.md | 14 ++++++++++++++ platform/config.c | 1 + platform/nusec.c | 5 +++-- platform/nusec.h | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/config/common.md b/doc/config/common.md index 4dcaf43..7dc14c1 100644 --- a/doc/config/common.md +++ b/doc/config/common.md @@ -326,6 +326,20 @@ Bit values are: - 3: EXP: Export (for Asian markets) - 4: CHS: China (Simplified Chinese?) +### `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 +is an issue. Billing types are: + +- 0: No billing? +- 1: Billing type A +- 2: Billing type B1 +- 3: Billing type B2 + ### `systemFlag` Default: `0x64` diff --git a/platform/config.c b/platform/config.c index 848cfbd..715e3c3 100644 --- a/platform/config.c +++ b/platform/config.c @@ -222,6 +222,7 @@ void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename) filename); cfg->region = GetPrivateProfileIntW(L"keychip", L"region", 1, filename); + cfg->region = GetPrivateProfileIntW(L"keychip", L"billingType", 1, filename); cfg->system_flag = GetPrivateProfileIntW( L"keychip", L"systemFlag", diff --git a/platform/nusec.c b/platform/nusec.c index 7f888a4..cc1030b 100644 --- a/platform/nusec.c +++ b/platform/nusec.c @@ -134,8 +134,9 @@ HRESULT nusec_hook_init( if (nusec_cfg.platform_id[0] == '\0') { memcpy(nusec_cfg.platform_id, platform_id, sizeof(nusec_cfg.platform_id)); } - - nusec_nearfull = 0x00010200; + + // High 16 bits is billing type, low is actual playlimit + nusec_nearfull = (nusec_cfg.billing_type << 16) + 512; nusec_play_count = 0; nusec_play_limit = 1024; diff --git a/platform/nusec.h b/platform/nusec.h index 60886b1..5d3dd4f 100644 --- a/platform/nusec.h +++ b/platform/nusec.h @@ -14,6 +14,7 @@ struct nusec_config { uint8_t region; uint8_t system_flag; uint32_t subnet; + uint16_t billing_type; wchar_t billing_ca[MAX_PATH]; wchar_t billing_pub[MAX_PATH]; }; From 31931badf70353115cad836128ed6f54cf65db5d Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 11 Dec 2022 05:48:28 -0500 Subject: [PATCH 168/175] fix billingType --- platform/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/config.c b/platform/config.c index 715e3c3..1c6be62 100644 --- a/platform/config.c +++ b/platform/config.c @@ -222,7 +222,7 @@ void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename) filename); cfg->region = GetPrivateProfileIntW(L"keychip", L"region", 1, filename); - cfg->region = GetPrivateProfileIntW(L"keychip", L"billingType", 1, filename); + cfg->billing_type = GetPrivateProfileIntW(L"keychip", L"billingType", 1, filename); cfg->system_flag = GetPrivateProfileIntW( L"keychip", L"systemFlag", From 9715a788bf37c6641a15c8dc8181d00ec00e8fda Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 11 Dec 2022 06:22:00 -0500 Subject: [PATCH 169/175] add hooklib for external functions loaded via getprocaddress --- hooklib/meson.build | 2 + hooklib/procaddr.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ hooklib/procaddr.h | 18 +++++++ 3 files changed, 145 insertions(+) create mode 100644 hooklib/procaddr.c create mode 100644 hooklib/procaddr.h diff --git a/hooklib/meson.build b/hooklib/meson.build index ce1c96e..d112da6 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -19,6 +19,8 @@ hooklib_lib = static_library( 'fdshark.h', 'path.c', 'path.h', + 'procaddr.c', + 'procaddr.h', 'reg.c', 'reg.h', 'setupapi.c', diff --git a/hooklib/procaddr.c b/hooklib/procaddr.c new file mode 100644 index 0000000..2409f28 --- /dev/null +++ b/hooklib/procaddr.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include "hooklib/procaddr.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + +static struct proc_addr_table *proc_addr_hook_list; +static size_t proc_addr_hook_count; +static CRITICAL_SECTION proc_addr_hook_lock; +static bool proc_addr_hook_initted; + +static FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name); +static FARPROC (WINAPI *next_GetProcAddress)(HMODULE hModule, const char *name); +static void proc_addr_hook_init(void); + +static const struct hook_symbol win32_hooks[] = { + { + .name = "GetProcAddress", + .patch = my_GetProcAddress, + .link = (void **) &next_GetProcAddress + } +}; + +HRESULT proc_addr_table_push( + const char *target, + struct hook_symbol *syms, + size_t nsyms +) +{ + HRESULT hr; + struct proc_addr_table *new_item; + struct proc_addr_table *new_mem; + + proc_addr_hook_init(); + + EnterCriticalSection(&proc_addr_hook_lock); + + new_mem = realloc( + proc_addr_hook_list, + (proc_addr_hook_count + 1) * sizeof(struct proc_addr_table)); + + if (new_mem == NULL) { + hr = E_OUTOFMEMORY; + + LeaveCriticalSection(&proc_addr_hook_lock); + return hr; + } + + new_item = &new_mem[proc_addr_hook_count]; + new_item->name = target; + new_item->nsyms = nsyms; + new_item->syms = syms; + + proc_addr_hook_list = new_mem; + proc_addr_hook_count++; + hr = S_OK; + + LeaveCriticalSection(&proc_addr_hook_lock); + + return hr; +} + +static void proc_addr_hook_init(void) +{ + if (proc_addr_hook_initted) { + return; + } + + dprintf("ProcAddr: Hook init\n"); + proc_addr_hook_initted = true; + + InitializeCriticalSection(&proc_addr_hook_lock); + + hook_table_apply( + NULL, + "kernel32.dll", + win32_hooks, + _countof(win32_hooks)); +} + +FARPROC WINAPI my_GetProcAddress(HMODULE hModule, const char *name) +{ + uintptr_t ordinal = (uintptr_t) name; + char mod_path[PATH_MAX]; + char *mod_name; + const struct hook_symbol *sym; + FARPROC result = next_GetProcAddress(hModule, name); + + GetModuleFileNameA(hModule, mod_path, PATH_MAX); + mod_name = basename(mod_path); + + for (int i = 0; i < proc_addr_hook_count; i++) { + + if (strcmp(proc_addr_hook_list[i].name, mod_name) == 0) { + + for (int j = 0; j < proc_addr_hook_list[i].nsyms; j++) { + sym = &proc_addr_hook_list[i].syms[j]; + + if (ordinal > 0xFFFF) { + + if (strcmp(sym->name, name) == 0) { + + dprintf("ProcAddr: Hooking %s from %s\n", name, mod_name); + result = (FARPROC) sym->patch; + } + } + + else { + if (sym->ordinal == ordinal) { + + dprintf("ProcAddr: Hooking Ord %p from %s\n", (void *)ordinal, mod_name); + result = (FARPROC) sym->patch; + } + } + } + } + } + + return result; +} \ No newline at end of file diff --git a/hooklib/procaddr.h b/hooklib/procaddr.h new file mode 100644 index 0000000..750e154 --- /dev/null +++ b/hooklib/procaddr.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include + +#include "hook/table.h" + +struct proc_addr_table { + const char *name; + size_t nsyms; + struct hook_symbol *syms; +}; + +HRESULT proc_addr_table_push( + const char *target, + struct hook_symbol *syms, + size_t nsyms +); \ No newline at end of file From d565e50b8ac115a1b16f796b90769b97d06fdc7e Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 11 Dec 2022 06:22:33 -0500 Subject: [PATCH 170/175] vscode settings.json cleanup --- .vscode/settings.json | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 487d6ff..49872e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,28 +1,3 @@ { "editor.formatOnSave": false, - "files.associations": { - "assert.h": "c", - "config.h": "c", - "backend.h": "c", - "idacio.h": "c", - "idac-dll.h": "c", - "d3d11.h": "c", - "mu3-dll.h": "c", - "printer.h": "c", - "netenv.h": "c", - "stdint.h": "c", - "string.h": "c", - "platform.h": "c", - "jvs.h": "c", - "sg-reader.h": "c", - "carol-dll.h": "c", - "slider.h": "c", - "slider-cmd.h": "c", - "touch.h": "c", - "iobd.h": "c", - "stdbool.h": "c", - "sg-frame.h": "c", - "sg-cmd.h": "c", - "controlbd.h": "c" - } } From c7fe5189cfe6f93804accc7fa069d8a07d9036dc Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Sun, 11 Dec 2022 17:01:51 -0500 Subject: [PATCH 171/175] add half-working CXB support --- Dockerfile | 2 + Package.mk | 16 +++ aimeio/aimeio.c | 1 + cxbhook/config.c | 61 +++++++++ cxbhook/config.h | 40 ++++++ cxbhook/cxb-dll.c | 120 +++++++++++++++++ cxbhook/cxb-dll.h | 24 ++++ cxbhook/cxbhook.def | 21 +++ cxbhook/dllmain.c | 149 +++++++++++++++++++++ cxbhook/led.c | 93 +++++++++++++ cxbhook/led.h | 11 ++ cxbhook/meson.build | 36 +++++ cxbhook/network.c | 13 ++ cxbhook/network.h | 13 ++ cxbhook/revio.c | 226 ++++++++++++++++++++++++++++++++ cxbhook/revio.h | 17 +++ cxbio/config.c | 22 ++++ cxbio/config.h | 17 +++ cxbio/cxbio.c | 78 +++++++++++ cxbio/cxbio.h | 19 +++ cxbio/meson.build | 13 ++ dist/cxb/resource/segatools.ini | 4 + dist/cxb/segatools.ini | 75 +++++++++++ dist/cxb/start.bat | 9 ++ meson.build | 2 + 25 files changed, 1082 insertions(+) create mode 100644 cxbhook/config.c create mode 100644 cxbhook/config.h create mode 100644 cxbhook/cxb-dll.c create mode 100644 cxbhook/cxb-dll.h create mode 100644 cxbhook/cxbhook.def create mode 100644 cxbhook/dllmain.c create mode 100644 cxbhook/led.c create mode 100644 cxbhook/led.h create mode 100644 cxbhook/meson.build create mode 100644 cxbhook/network.c create mode 100644 cxbhook/network.h create mode 100644 cxbhook/revio.c create mode 100644 cxbhook/revio.h create mode 100644 cxbio/config.c create mode 100644 cxbio/config.h create mode 100644 cxbio/cxbio.c create mode 100644 cxbio/cxbio.h create mode 100644 cxbio/meson.build create mode 100644 dist/cxb/resource/segatools.ini create mode 100644 dist/cxb/segatools.ini create mode 100644 dist/cxb/start.bat diff --git a/Dockerfile b/Dockerfile index 5a3ff66..c52ccf4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,8 @@ COPY amex amex COPY board board COPY chunihook chunihook COPY chuniio chuniio +COPY cxbhook cxbhook +COPY cxbio cxbio COPY dist dist COPY divahook divahook COPY divaio divaio diff --git a/Package.mk b/Package.mk index 4f21e64..850f9ee 100644 --- a/Package.mk +++ b/Package.mk @@ -12,6 +12,21 @@ $(BUILD_DIR_ZIP)/chuni.zip: $(BUILD_DIR_ZIP)/chuni/DEVICE $(V)strip $(BUILD_DIR_ZIP)/chuni/*.{exe,dll} $(V)cd $(BUILD_DIR_ZIP)/chuni ; zip -r ../chuni.zip * + +$(BUILD_DIR_ZIP)/cxb.zip: + $(V)echo ... $@ + $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb + $(V)mkdir -p $(BUILD_DIR_ZIP)/cxb/DEVICE + $(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \ + $(BUILD_DIR_32)/cxbhook/cxbhook.dll \ + $(DIST_DIR)/cxb/segatools.ini \ + $(DIST_DIR)/cxb/start.bat \ + $(BUILD_DIR_ZIP)/cxb + $(V)cp pki/billing.pub \ + pki/ca.crt \ + $(BUILD_DIR_ZIP)/cxb/DEVICE + $(V)strip $(BUILD_DIR_ZIP)/cxb/*.{exe,dll} + $(V)cd $(BUILD_DIR_ZIP)/cxb ; zip -r ../cxb.zip * $(BUILD_DIR_ZIP)/diva.zip: $(V)echo ... $@ @@ -99,6 +114,7 @@ $(BUILD_DIR_ZIP)/doc.zip: \ $(BUILD_DIR_ZIP)/segatools.zip: \ $(BUILD_DIR_ZIP)/chuni.zip \ + $(BUILD_DIR_ZIP)/cxb.zip \ $(BUILD_DIR_ZIP)/carol.zip \ $(BUILD_DIR_ZIP)/diva.zip \ $(BUILD_DIR_ZIP)/doc.zip \ diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c index b09d612..f505aac 100644 --- a/aimeio/aimeio.c +++ b/aimeio/aimeio.c @@ -62,6 +62,7 @@ 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", diff --git a/cxbhook/config.c b/cxbhook/config.c new file mode 100644 index 0000000..9a513d1 --- /dev/null +++ b/cxbhook/config.c @@ -0,0 +1,61 @@ +#include + +#include +#include +#include + +#include "amex/amex.h" +#include "amex/config.h" + +#include "board/config.h" +#include "board/sg-reader.h" + +#include "cxbhook/config.h" + +#include "gfxhook/config.h" + +#include "hooklib/config.h" + +#include "platform/config.h" +#include "platform/platform.h" + +void cxb_dll_config_load( + struct cxb_dll_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 cxb_hook_config_load( + struct cxb_hook_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + memset(cfg, 0, sizeof(*cfg)); + + platform_config_load(&cfg->platform, filename); + amex_config_load(&cfg->amex, filename); + aime_config_load(&cfg->aime, filename); + 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); +} \ No newline at end of file diff --git a/cxbhook/config.h b/cxbhook/config.h new file mode 100644 index 0000000..f2bb8ca --- /dev/null +++ b/cxbhook/config.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" + +#include "cxbhook/cxb-dll.h" +#include "cxbhook/revio.h" +#include "cxbhook/led.h" +#include "cxbhook/network.h" + +#include "gfxhook/gfx.h" + +#include "platform/platform.h" + +struct cxb_hook_config { + struct platform_config platform; + struct amex_config amex; + struct aime_config aime; + struct gfx_config gfx; + struct cxb_dll_config dll; + struct revio_config revio; + struct network_config network; + struct led_config led; +}; + +void cxb_dll_config_load( + struct cxb_dll_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 cxb_hook_config_load( + struct cxb_hook_config *cfg, + const wchar_t *filename); diff --git a/cxbhook/cxb-dll.c b/cxbhook/cxb-dll.c new file mode 100644 index 0000000..93fb83b --- /dev/null +++ b/cxbhook/cxb-dll.c @@ -0,0 +1,120 @@ +#include + +#include +#include + +#include "cxbhook/cxb-dll.h" + +#include "util/dll-bind.h" +#include "util/dprintf.h" + +const struct dll_bind_sym cxb_dll_syms[] = { + { + .sym = "cxb_io_revio_init", + .off = offsetof(struct cxb_dll, revio_init), + }, + { + .sym = "cxb_io_revio_poll", + .off = offsetof(struct cxb_dll, revio_poll), + }, + { + .sym = "cxb_io_revio_get_coins", + .off = offsetof(struct cxb_dll, revio_get_coins), + }, + { + .sym = "cxb_io_revio_set_coins", + .off = offsetof(struct cxb_dll, revio_set_coins), + }, + { + .sym = "cxb_io_led_init", + .off = offsetof(struct cxb_dll, led_init), + }, + { + .sym = "cxb_io_led_update", + .off = offsetof(struct cxb_dll, led_update), + }, +}; + +struct cxb_dll cxb_dll; + +// Copypasta DLL binding and diagnostic message boilerplate. +// Not much of this lends itself to being easily factored out. Also there +// will be a lot of API-specific branching code here eventually as new API +// versions get defined, so even though these functions all look the same +// now this won't remain the case forever. + +HRESULT cxb_dll_init(const struct cxb_dll_config *cfg, HINSTANCE self) +{ + uint16_t (*get_api_version)(void); + const struct dll_bind_sym *sym; + HINSTANCE owned; + HINSTANCE src; + HRESULT hr; + + assert(cfg != NULL); + assert(self != NULL); + + if (cfg->path[0] != L'\0') { + owned = LoadLibraryW(cfg->path); + + if (owned == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("Crossbeats IO: Failed to load IO DLL: %lx: %S\n", + hr, + cfg->path); + + goto end; + } + + dprintf("Crossbeats IO: Using custom IO DLL: %S\n", cfg->path); + src = owned; + } else { + owned = NULL; + src = self; + } + + get_api_version = (void *) GetProcAddress(src, "cxb_io_get_api_version"); + + if (get_api_version != NULL) { + cxb_dll.api_version = get_api_version(); + } else { + cxb_dll.api_version = 0x0100; + dprintf("Custom IO DLL does not expose cxb_io_get_api_version, " + "assuming API version 1.0.\n" + "Please ask the developer to update their DLL.\n"); + } + + if (cxb_dll.api_version >= 0x0200) { + hr = E_NOTIMPL; + dprintf("Crossbeats IO: Custom IO DLL implements an unsupported " + "API version (%#04x). Please update Segatools.\n", + cxb_dll.api_version); + + goto end; + } + + sym = cxb_dll_syms; + hr = dll_bind(&cxb_dll, src, &sym, _countof(cxb_dll_syms)); + + if (FAILED(hr)) { + if (src != self) { + dprintf("Crossbeats IO: Custom IO DLL does not provide function " + "\"%s\". Please contact your IO DLL's developer for " + "further assistance.\n", + sym->sym); + + goto end; + } else { + dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); + } + } + + owned = NULL; + +end: + if (owned != NULL) { + FreeLibrary(owned); + } + + return hr; +} diff --git a/cxbhook/cxb-dll.h b/cxbhook/cxb-dll.h new file mode 100644 index 0000000..de57958 --- /dev/null +++ b/cxbhook/cxb-dll.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "cxbio/cxbio.h" + +struct cxb_dll { + uint16_t api_version; + HRESULT (*revio_init)(void); + void (*revio_poll)(uint16_t *opbtn); + void (*revio_get_coins)(long *coins); + void (*revio_set_coins)(int coins); + HRESULT (*led_init)(void); + void (*led_update)(int id, int color); +}; + +struct cxb_dll_config { + wchar_t path[MAX_PATH]; +}; + +extern struct cxb_dll cxb_dll; + +HRESULT cxb_dll_init(const struct cxb_dll_config *cfg, HINSTANCE self); diff --git a/cxbhook/cxbhook.def b/cxbhook/cxbhook.def new file mode 100644 index 0000000..9efaaf6 --- /dev/null +++ b/cxbhook/cxbhook.def @@ -0,0 +1,21 @@ +LIBRARY cxbhook + +EXPORTS + Direct3DCreate9 + aime_io_get_api_version + aime_io_init + aime_io_led_set_color + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id + aime_io_nfc_poll + amDllVideoClose @2 + amDllVideoGetVBiosVersion @4 + amDllVideoOpen @1 + amDllVideoSetResolution @3 + cxb_io_get_api_version + cxb_io_revio_init + cxb_io_revio_poll + cxb_io_revio_get_coins + cxb_io_revio_set_coins + cxb_io_led_init + cxb_io_led_update diff --git a/cxbhook/dllmain.c b/cxbhook/dllmain.c new file mode 100644 index 0000000..b9c3056 --- /dev/null +++ b/cxbhook/dllmain.c @@ -0,0 +1,149 @@ +#include + +#include + +#include "amex/amex.h" + +#include "board/sg-reader.h" + +#include "cxbhook/config.h" +#include "cxbhook/revio.h" +#include "cxbhook/led.h" +#include "cxbhook/network.h" + +#include "cxbio/cxbio.h" + +#include "gfxhook/gfx.h" +#include "gfxhook/d3d9.h" + +#include "hook/process.h" + +#include "hooklib/serial.h" +#include "hooklib/spike.h" + +#include "platform/platform.h" + +#include "util/dprintf.h" + +static HMODULE cxb_hook_mod; +static process_entry_t cxb_startup; +static struct cxb_hook_config cxb_hook_cfg; + +static DWORD CALLBACK cxb_pre_startup(void) +{ + HMODULE d3dc; + HMODULE dbghelp; + HRESULT hr; + + dprintf("--- Begin cxb_pre_startup ---\n"); + + /* Pin the D3D shader compiler. This makes startup much faster. */ + + d3dc = LoadLibraryW(L"D3DCompiler_43.dll"); + + if (d3dc != NULL) { + dprintf("Pinned shader compiler, hMod=%p\n", d3dc); + } else { + dprintf("Failed to load shader compiler!\n"); + } + + /* Pin dbghelp so the path hooks apply to it. */ + + dbghelp = LoadLibraryW(L"dbghelp.dll"); + + if (dbghelp != NULL) { + dprintf("Pinned debug helper library, hMod=%p\n", dbghelp); + } else { + dprintf("Failed to load debug helper library!\n"); + } + + /* Config load */ + + cxb_hook_config_load(&cxb_hook_cfg, L".\\segatools.ini"); + + /* Hook Win32 APIs */ + + gfx_hook_init(&cxb_hook_cfg.gfx); + gfx_d3d9_hook_init(&cxb_hook_cfg.gfx, cxb_hook_mod); + serial_hook_init(); + + /* Initialize emulation hooks */ + + hr = platform_hook_init( + &cxb_hook_cfg.platform, + "SDCA", + "AAV1", + cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = cxb_dll_init(&cxb_hook_cfg.dll, cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = amex_hook_init(&cxb_hook_cfg.amex, NULL); + + if (FAILED(hr)) { + goto fail; + } + + hr = sg_reader_hook_init(&cxb_hook_cfg.aime, 12, cxb_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + + hr = revio_hook_init(&cxb_hook_cfg.revio); + + if (FAILED(hr)) { + 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)) { + goto fail; + } + + /* Initialize debug helpers */ + + spike_hook_init(L".\\segatools.ini"); + + dprintf("--- End cxb_pre_startup ---\n"); + + /* Jump to EXE start address */ + + return cxb_startup(); + +fail: + ExitProcess(EXIT_FAILURE); +} + +BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) +{ + HRESULT hr; + + if (cause != DLL_PROCESS_ATTACH) { + return TRUE; + } + + cxb_hook_mod = mod; + + hr = process_hijack_startup(cxb_pre_startup, &cxb_startup); + + if (!SUCCEEDED(hr)) { + dprintf("Failed to hijack process startup: %x\n", (int) hr); + } + + return SUCCEEDED(hr); +} diff --git a/cxbhook/led.c b/cxbhook/led.c new file mode 100644 index 0000000..39d6d4f --- /dev/null +++ b/cxbhook/led.c @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "cxbhook/led.h" +#include "cxbhook/cxb-dll.h" + +#include "hooklib/procaddr.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + + +static int my_cCommLamp_Open(char *port); +static void my_cCommLamp_Close(); +static int my_cCommLamp_Setup(int led_id); +static int my_cCommLamp_SetColor(int led_id, int color); +static int my_cCommLamp_Update(); +static int my_cCommLamp_UpdateDelta(float delta); + + +static struct hook_symbol lamp_syms[] = { + { + .name = "cCommLamp_Open", + .patch = my_cCommLamp_Open + }, + { + .name = "cCommLamp_Close", + .patch = my_cCommLamp_Close + }, + { + .name = "cCommLamp_Setup", + .patch = my_cCommLamp_Setup, + }, + { + .name = "cCommLamp_SetColor", + .patch = my_cCommLamp_SetColor + }, + { + .name = "cCommLamp_Update", + .patch = my_cCommLamp_Update + }, + { + .name = "cCommLamp_UpdateDelta", + .patch = my_cCommLamp_UpdateDelta + }, +}; + +HRESULT led_hook_init(struct led_config *cfg) +{ + dprintf("LED: Init\n"); + return proc_addr_table_push("CommLamp.dll", lamp_syms, _countof(lamp_syms)); +} + +static int my_cCommLamp_Open(char *port) +{ + HRESULT hr = cxb_dll.led_init(); + dprintf("LED: Open %s (DLL init result %lx)\n", port, hr); + + if (FAILED(hr)) { + return 0; + } + + return 1; +} + +static void my_cCommLamp_Close() +{ + dprintf("LED: Close\n"); +} + +static int my_cCommLamp_Setup(int led_id) +{ + dprintf("LED: Setup %d\n", led_id); + return 0; +} + +static int my_cCommLamp_SetColor(int led_id, int color) +{ + cxb_dll.led_update(led_id, color); + return 1; +} + +static int my_cCommLamp_Update() +{ + return 0; +} + +static int my_cCommLamp_UpdateDelta(float delta) +{ + return 0; +} \ No newline at end of file diff --git a/cxbhook/led.h b/cxbhook/led.h new file mode 100644 index 0000000..0cbfac7 --- /dev/null +++ b/cxbhook/led.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +struct led_config { + bool enable; +}; + +HRESULT led_hook_init(struct led_config *cfg); \ No newline at end of file diff --git a/cxbhook/meson.build b/cxbhook/meson.build new file mode 100644 index 0000000..1c23a00 --- /dev/null +++ b/cxbhook/meson.build @@ -0,0 +1,36 @@ +shared_library( + 'cxbhook', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + vs_module_defs : 'cxbhook.def', + c_pch : '../precompiled.h', + dependencies : [ + capnhook.get_variable('hook_dep'), + capnhook.get_variable('hooklib_dep'), + ], + link_with : [ + aimeio_lib, + amex_lib, + board_lib, + cxbio_lib, + gfxhook_lib, + hooklib_lib, + jvs_lib, + platform_lib, + util_lib, + ], + sources : [ + 'cxb-dll.c', + 'cxb-dll.h', + 'config.c', + 'config.h', + 'dllmain.c', + 'revio.c', + 'revio.h', + 'led.c', + 'led.h', + 'network.c', + 'network.h', + ], +) diff --git a/cxbhook/network.c b/cxbhook/network.c new file mode 100644 index 0000000..e9a832a --- /dev/null +++ b/cxbhook/network.c @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "cxbhook/network.h" + +#include "util/dprintf.h" + +HRESULT network_hook_init(struct network_config *cfg) +{ + dprintf("Network: Init\n"); + return S_OK; +} \ No newline at end of file diff --git a/cxbhook/network.h b/cxbhook/network.h new file mode 100644 index 0000000..a2ae7ce --- /dev/null +++ b/cxbhook/network.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +struct network_config { + bool enable; + bool disable_ssl; + char title_server[PATH_MAX]; +}; + +HRESULT network_hook_init(struct network_config *cfg); \ No newline at end of file diff --git a/cxbhook/revio.c b/cxbhook/revio.c new file mode 100644 index 0000000..e72158d --- /dev/null +++ b/cxbhook/revio.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +#include "cxbhook/revio.h" +#include "cxbhook/cxb-dll.h" + +#include "hooklib/procaddr.h" + +#include "hook/table.h" + +#include "util/dprintf.h" + +static int my_cCommIo_Open(char *port); +static int my_cCommIo_Close(); +static long my_cCommIo_GetCoin(); +static int my_cCommIo_SetCoin(int coin_ct); +static int my_cCommIo_GetStatus(); +static int my_cCommIo_GetSwitch(); +static int my_cCommIo_GetTrigger(); +static int my_cCommIo_GetRelease(); +static long my_cCommIo_GetVolume(); +static int my_cCommIo_SetAmpVolume(int amp_id, long new_volume); +static int my_cCommIo_GetAmpVolume(int amp_id); +static int my_cCommIo_SetAmpMute(int amp_id, int a2); + +int amp_volume[] = {20, 20, 20}; +int last_triggers = 0; +int last_is_mouse_down = false; + +static struct hook_symbol revio_syms[] = { + { + .name = "cCommIo_Open", + .patch = my_cCommIo_Open + }, + { + .name = "cCommIo_Close", + .patch = my_cCommIo_Close + }, + { + .name = "cCommIo_GetStatus", + .patch = my_cCommIo_GetStatus + }, + { + .name = "cCommIo_GetCoin", + .patch = my_cCommIo_GetCoin + }, + { + .name = "cCommIo_SetCoin", + .patch = my_cCommIo_SetCoin + }, + { + .name = "cCommIo_GetSwitch", + .patch = my_cCommIo_GetSwitch + }, + { + .name = "cCommIo_GetTrigger", + .patch = my_cCommIo_GetTrigger + }, + { + .name = "cCommIo_GetRelease", + .patch = my_cCommIo_GetRelease + }, + { + .name = "cCommIo_GetVolume", + .patch = my_cCommIo_GetVolume + }, + { + .name = "cCommIo_SetAmpVolume", + .patch = my_cCommIo_SetAmpVolume + }, + { + .name = "cCommIo_GetAmpVolume", + .patch = my_cCommIo_GetAmpVolume + }, + { + .name = "cCommIo_SetAmpMute", + .patch = my_cCommIo_SetAmpMute + }, +}; + +HRESULT revio_hook_init(struct revio_config *cfg) +{ + dprintf("Revio: Init\n"); + return proc_addr_table_push("CommIo.dll", revio_syms, _countof(revio_syms)); +} + +static int my_cCommIo_Open(char *port) +{ + dprintf("Revio: Open port %s\n", port); + cxb_dll.revio_init(); + return 1; +} + +static int my_cCommIo_Close() +{ + dprintf("Revio: Close\n"); + return 0; +} + +static int my_cCommIo_GetStatus() +{ + return 1; +} + +static long my_cCommIo_GetCoin() +{ + long coins; + cxb_dll.revio_get_coins(&coins); + + return coins; +} + +static int my_cCommIo_SetCoin(int coin_ct) +{ + // does some weird shit, not sure + //dprintf("Revio: Set coin %d\n", coin_ct); + cxb_dll.revio_set_coins(coin_ct); + return 1; +} + +static int my_cCommIo_GetSwitch() +{ + return 0; +} + +static int my_cCommIo_GetTrigger() +{ + uint16_t btns = 0; + int out = 0; + + cxb_dll.revio_poll(&btns); + + if (btns & 0x01) { + out |= 1 << 4; // test + } + + if (btns & 0x02) { + out |= 1 << 5; // service? + } + + if (btns & 0x04) { + out |= 1 << 1; // up + } + + if (btns & 0x08) { + out |= 1 << 3; // down + } + + if (btns & 0x0F) { + out |= 1 << 2; // cancel + } + + out &= ~last_triggers; + + dprintf("Revio: GetTrigger %X\n", out); + last_triggers = out; + return out; +} + +static int my_cCommIo_GetRelease() +{ + uint16_t btns = 0; + int out = last_triggers; + + cxb_dll.revio_poll(&btns); + + if (btns & 0x01) { + out |= 1 << 4; // test + } + + if (btns & 0x02) { + out |= 1 << 5; // service? + } + + if (btns & 0x04) { + out |= 1 << 1; // up + } + + if (btns & 0x08) { + out |= 1 << 3; // down + } + + if (btns & 0x0F) { + out |= 1 << 2; // cancel + } + + out &= ~btns; + + dprintf("Revio: GetRelease %X\n", out); + last_triggers = btns; + return out; +} + +static long my_cCommIo_GetVolume() +{ + return 0; +} + +static int my_cCommIo_SetAmpVolume(int amp_id, long new_volume) +{ + dprintf("Revio: SetAmpVolume id %d -> vol %ld\n", amp_id, new_volume); + if (amp_id > _countof(amp_volume)) { + return 0; + } + + amp_volume[amp_id] = new_volume; + return 0; +} + +static int my_cCommIo_GetAmpVolume(int amp_id) +{ + dprintf("Revio: GetAmpVolume id %d\n", amp_id); + if (amp_id > _countof(amp_volume)) { + return 0; + } + + return amp_volume[amp_id]; +} + +static int my_cCommIo_SetAmpMute(int amp_id, int a2) +{ + dprintf("Revio: GetAmpVolume id %d unknown %d\n", amp_id, a2); + return 0; +} diff --git a/cxbhook/revio.h b/cxbhook/revio.h new file mode 100644 index 0000000..8150c97 --- /dev/null +++ b/cxbhook/revio.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +struct revio_config { + bool enable; + uint8_t test; + uint8_t service; + uint8_t coin; + uint8_t up; + uint8_t down; + uint8_t cancel; +}; + +HRESULT revio_hook_init(struct revio_config *cfg); \ No newline at end of file diff --git a/cxbio/config.c b/cxbio/config.c new file mode 100644 index 0000000..681c78a --- /dev/null +++ b/cxbio/config.c @@ -0,0 +1,22 @@ +#include + +#include +#include +#include + +#include "cxbio/config.h" + +void cxb_io_config_load( + struct cxb_io_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->test = GetPrivateProfileIntW(L"revio", L"test", '1', filename); + cfg->service = GetPrivateProfileIntW(L"revio", L"service", '2', filename); + cfg->coin = GetPrivateProfileIntW(L"revio", L"coin", '3', filename); + cfg->cancel = GetPrivateProfileIntW(L"revio", L"cancel", '4', filename); + cfg->up = GetPrivateProfileIntW(L"revio", L"up", VK_UP, filename); + cfg->down = GetPrivateProfileIntW(L"revio", L"down", VK_DOWN, filename); +} diff --git a/cxbio/config.h b/cxbio/config.h new file mode 100644 index 0000000..f6e82cb --- /dev/null +++ b/cxbio/config.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include + +struct cxb_io_config { + uint8_t test; + uint8_t service; + uint8_t coin; + uint8_t cancel; + uint8_t up; + uint8_t down; +}; + +void cxb_io_config_load( + struct cxb_io_config *cfg, + const wchar_t *filename); \ No newline at end of file diff --git a/cxbio/cxbio.c b/cxbio/cxbio.c new file mode 100644 index 0000000..d021d3c --- /dev/null +++ b/cxbio/cxbio.c @@ -0,0 +1,78 @@ +#include + +#include +#include +#include + +#include "cxbio/cxbio.h" +#include "cxbio/config.h" + +#include "util/dprintf.h" + +static bool cxb_io_coin; +static int cxb_io_coins; +static struct cxb_io_config cxb_io_cfg; + +uint16_t cxb_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT cxb_io_revio_init(void) +{ + dprintf("CXB IO: REVIO init\n"); + cxb_io_config_load(&cxb_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +void cxb_io_revio_poll(uint16_t *opbtn) +{ + if (GetAsyncKeyState(cxb_io_cfg.test)) { + *opbtn |= 0x01; /* Test */ + } + + if (GetAsyncKeyState(cxb_io_cfg.service)) { + *opbtn |= 0x02; /* Service */ + } + + if (GetAsyncKeyState(cxb_io_cfg.cancel)) { + *opbtn |= 0x04; /* Cancel */ + } + + if (GetAsyncKeyState(cxb_io_cfg.up)) { + *opbtn |= 0x08; /* Up */ + } + + if (GetAsyncKeyState(cxb_io_cfg.down)) { + *opbtn |= 0x10; /* Down */ + } +} + +void cxb_io_revio_get_coins(long *coins) +{ + if (GetAsyncKeyState(cxb_io_cfg.coin)) { + if (!cxb_io_coin) { + cxb_io_coin = true; + cxb_io_coins++; + } + } else { + cxb_io_coin = false; + } + + *coins = cxb_io_coins; +} + +void cxb_io_revio_set_coins(int coins) +{ + cxb_io_coins = coins; +} + +HRESULT cxb_io_led_init(void) +{ + dprintf("CXB IO: LED init\n"); + return S_OK; +} + +void cxb_io_led_update(int id, int color) +{} \ No newline at end of file diff --git a/cxbio/cxbio.h b/cxbio/cxbio.h new file mode 100644 index 0000000..35fccea --- /dev/null +++ b/cxbio/cxbio.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +uint16_t cxb_io_get_api_version(void); + +HRESULT cxb_io_revio_init(void); + +void cxb_io_revio_poll(uint16_t *opbtn); + +void cxb_io_revio_get_coins(long *coins); + +void cxb_io_revio_set_coins(int coins); + +HRESULT cxb_io_led_init(void); + +void cxb_io_led_update(int id, int color); \ No newline at end of file diff --git a/cxbio/meson.build b/cxbio/meson.build new file mode 100644 index 0000000..2ce52d2 --- /dev/null +++ b/cxbio/meson.build @@ -0,0 +1,13 @@ +cxbio_lib = static_library( + 'cxbio', + name_prefix : '', + include_directories : inc, + implicit_include_directories : false, + c_pch : '../precompiled.h', + sources : [ + 'cxbio.c', + 'cxbio.h', + 'config.c', + 'config.h', + ], +) diff --git a/dist/cxb/resource/segatools.ini b/dist/cxb/resource/segatools.ini new file mode 100644 index 0000000..a214976 --- /dev/null +++ b/dist/cxb/resource/segatools.ini @@ -0,0 +1,4 @@ +[aime] +; CXB is stupid, so we have to make the paths go back one +aimePath=../DEVICE/aime.txt +felicaPath=../DEVICE/felica.txt \ No newline at end of file diff --git a/dist/cxb/segatools.ini b/dist/cxb/segatools.ini new file mode 100644 index 0000000..e0491ff --- /dev/null +++ b/dist/cxb/segatools.ini @@ -0,0 +1,75 @@ +[vfs] +; Make sure theses are full paths and not relative or you will have a bad time +; Insert the path to the game AMFS directory here (contains ICF1 and ICF2) +amfs= +; Insert the path to the game Option directory here (contains Axxx directories) +option= +; Create an empty directory somewhere and insert the path here. +; This directory may be shared between multiple SEGA games. +; NOTE: This has nothing to do with Windows %APPDATA%. +appdata= + +[dns] +; Insert the hostname or IP address of the server you wish to use here. +; Note that 127.0.0.1, localhost etc are specifically rejected. +default=127.0.0.1 + +[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 +; setting enabled is strongly recommended. +enable=1 + +[keychip] +; The /24 LAN subnet that the emulated keychip will tell the game to expect. +; If you disable netenv then you must set this to your LAN's IP subnet, and +; that subnet must start with 192.168. +subnet=192.168.100.0 +billingCa=../DEVICE/ca.crt +billingPub=../DEVICE/billing.pub +billingType=2 + +[gfx] +; Force the game to run windowed. +windowed=1 +; Add a frame to the game window if running windowed. +framed=1 +; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) +monitor=0 + +[aime] +; Aime reader emulation +; CXB is stupid, so we have to make the paths go back one +enable=1 +aimePath=../DEVICE/aime.txt +felicaPath=../DEVICE/felica.txt + +[eeprom] +; See above +path=../DEVICE/eeprom.bin + +[sram] +; See above +path=../DEVICE/sram.bin + +[led] +; Emulation for the LED board. Currently it's just dummy responses, +; but if somebody wants to make their keyboard or whatever light +; up with the game they can +enable=1 + +[revio] +; Enable emulation of the rev IO board +enabe=1 +; 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 +; Menu up key. Default is up arrow. +up=0x26 +; Menu down key. Default is down arrow. +down=0x28 +; Menu cancel key. Default is the 4 key. +cancel=0x34 diff --git a/dist/cxb/start.bat b/dist/cxb/start.bat new file mode 100644 index 0000000..af92b2d --- /dev/null +++ b/dist/cxb/start.bat @@ -0,0 +1,9 @@ +@echo off + +pushd %~dp0 + +inject -d -k cxbhook.dll Rev_v11.exe + +echo. +echo Game processes have terminated +pause \ No newline at end of file diff --git a/meson.build b/meson.build index acf85ef..2c6b673 100644 --- a/meson.build +++ b/meson.build @@ -59,6 +59,7 @@ subdir('carolio') subdir('idzio') subdir('mu3io') subdir('mercuryio') +subdir('cxbio') subdir('chunihook') subdir('divahook') @@ -67,3 +68,4 @@ subdir('idzhook') subdir('minihook') subdir('mu3hook') subdir('mercuryhook') +subdir('cxbhook') From b2db1f230bd56e83497101e583a211eaf18821bd Mon Sep 17 00:00:00 2001 From: Kevin Trocolli Date: Thu, 29 Dec 2022 04:41:45 -0500 Subject: [PATCH 172/175] mercuryhook-touch: Zero buffers before writing them to the uart --- mercuryhook/touch.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mercuryhook/touch.c b/mercuryhook/touch.c index 87e757c..87ba6d5 100644 --- a/mercuryhook/touch.c +++ b/mercuryhook/touch.c @@ -233,7 +233,7 @@ static HRESULT touch_handle_get_sync_board_ver(const struct touch_req *req) { struct touch_resp_get_sync_board_ver resp; HRESULT hr; - + memset(&resp, 0, sizeof(resp)); dprintf("Wacca Touch%d: Get sync board version\n", req->side); resp.cmd = 0xa0; @@ -256,7 +256,7 @@ static HRESULT touch_handle_next_read(const struct touch_req *req) struct touch_resp_startup resp; HRESULT hr; char *rev; - + memset(&resp, 0, sizeof(resp)); dprintf("Wacca Touch%d: Read section %2hx\n", req->side, req->data[2]); @@ -293,7 +293,7 @@ static HRESULT touch_handle_get_unit_board_ver(const struct touch_req *req) { struct touch_resp_get_unit_board_ver resp; HRESULT hr; - + memset(&resp, 0, sizeof(resp)); dprintf("Wacca Touch%d: get unit board version\n", req->side); memset(resp.version, 0, sizeof(resp.version)); @@ -339,7 +339,7 @@ static HRESULT touch_handle_mystery1(const struct touch_req *req) { struct touch_resp_mystery1 resp; HRESULT hr; - + memset(&resp, 0, sizeof(resp)); dprintf("Wacca Touch%d: Command A2\n", req->side); resp.cmd = 0xa2; @@ -360,7 +360,7 @@ static HRESULT touch_handle_mystery2(const struct touch_req *req) { struct touch_resp_mystery2 resp; HRESULT hr; - + memset(&resp, 0, sizeof(resp)); dprintf("Wacca Touch%d: Command 94\n", req->side); resp.cmd = 0x94; @@ -423,6 +423,8 @@ static void touch_res_auto_scan(const bool *state) { struct touch_input_frame frame0; struct touch_input_frame frame1; + memset(&frame0, 0, sizeof(frame0)); + memset(&frame1, 0, sizeof(frame1)); uint8_t dataR[24] = { 0 }; uint8_t dataL[24] = { 0 }; // this changes every input on a real board but From 87ecd639a2ea6916bea152be8908c0c6ce15bbea Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sun, 12 Feb 2023 20:40:41 -0500 Subject: [PATCH 173/175] Wacca: Add toggle for LED board hooks --- dist/mercury/segatools.ini | 8 ++++++-- mercuryhook/config.c | 16 ++++++++++++++++ mercuryhook/config.h | 2 ++ mercuryhook/dllmain.c | 2 +- mercuryhook/elisabeth.c | 5 ++++- mercuryhook/elisabeth.h | 7 ++++++- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/dist/mercury/segatools.ini b/dist/mercury/segatools.ini index 9fc5bba..f231c66 100644 --- a/dist/mercury/segatools.ini +++ b/dist/mercury/segatools.ini @@ -43,10 +43,14 @@ coin=0x24 volup=0x26 voldown=0x28 +; Hooks related to the touch boards [touch] -; Enable or disable touch hook +enable=1 + +; Hooks related to the LED board (codenamed Elisabeth) +[elisabeth] enable=1 ;[mercuryio] ; Use mercuryio.dll -;path=mercuryio.dll \ No newline at end of file +;path=mercuryio.dll diff --git a/mercuryhook/config.c b/mercuryhook/config.c index f0dbb08..f087942 100644 --- a/mercuryhook/config.c +++ b/mercuryhook/config.c @@ -41,6 +41,21 @@ void touch_config_load( filename); } +void elisabeth_config_load( + struct elisabeth_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW( + L"elisabeth", + L"enable", + 1, + filename); +} + + void mercury_hook_config_load( struct mercury_hook_config *cfg, const wchar_t *filename) @@ -55,4 +70,5 @@ void mercury_hook_config_load( gfx_config_load(&cfg->gfx, filename); mercury_dll_config_load(&cfg->dll, filename); touch_config_load(&cfg->touch, filename); + elisabeth_config_load(&cfg->elisabeth, filename); } diff --git a/mercuryhook/config.h b/mercuryhook/config.h index cfeb490..d5bf463 100644 --- a/mercuryhook/config.h +++ b/mercuryhook/config.h @@ -9,6 +9,7 @@ #include "mercuryhook/mercury-dll.h" #include "mercuryhook/touch.h" +#include "mercuryhook/elisabeth.h" #include "platform/config.h" @@ -20,6 +21,7 @@ struct mercury_hook_config { struct gfx_config gfx; struct mercury_dll_config dll; struct touch_config touch; + struct elisabeth_config elisabeth; }; void mercury_dll_config_load( diff --git a/mercuryhook/dllmain.c b/mercuryhook/dllmain.c index 611122b..f3df2a7 100644 --- a/mercuryhook/dllmain.c +++ b/mercuryhook/dllmain.c @@ -83,7 +83,7 @@ static DWORD CALLBACK mercury_pre_startup(void) } /* Start elisabeth Hooks for the LED and IO Board DLLs */ - elisabeth_hook_init(); + elisabeth_hook_init(&mercury_hook_cfg.elisabeth); touch_hook_init(&mercury_hook_cfg.touch); diff --git a/mercuryhook/elisabeth.c b/mercuryhook/elisabeth.c index baac575..7078a21 100644 --- a/mercuryhook/elisabeth.c +++ b/mercuryhook/elisabeth.c @@ -33,8 +33,11 @@ static const struct hook_symbol win32_hooks[] = { } }; -HRESULT elisabeth_hook_init() +HRESULT elisabeth_hook_init(struct elisabeth_config *cfg) { + if (!cfg->enable) { + return S_OK; + } dll_hook_insert_hooks(NULL); dprintf("Elisabeth: Init\n"); return S_OK; diff --git a/mercuryhook/elisabeth.h b/mercuryhook/elisabeth.h index 41066fc..5806c99 100644 --- a/mercuryhook/elisabeth.h +++ b/mercuryhook/elisabeth.h @@ -1,8 +1,13 @@ #pragma once +#include struct led_data { DWORD unitCount; uint8_t rgba[480 * 4]; }; -HRESULT elisabeth_hook_init(); +struct elisabeth_config { + bool enable; +}; + +HRESULT elisabeth_hook_init(struct elisabeth_config *cfg); From 9e78d7fbc395fa348eea0f8064893e169354ef77 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Sun, 12 Feb 2023 20:45:13 -0500 Subject: [PATCH 174/175] update docker build system --- Dockerfile | 52 ++++-------------------------------------------- docker-build.bat | 10 ++-------- 2 files changed, 6 insertions(+), 56 deletions(-) diff --git a/Dockerfile b/Dockerfile index c52ccf4..2c5d516 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,12 @@ -FROM fedora:31 +FROM fedora:36 LABEL description="Build environment for segatools" -RUN yum -y install meson -RUN yum -y install ninja-build -RUN yum -y install make -RUN yum -y install zip -RUN yum -y install clang -RUN yum -y install mingw64-gcc.x86_64 -RUN yum -y install mingw32-gcc.x86_64 -RUN yum -y install git +RUN dnf -y install meson ninja-build make zip clang mingw64-gcc.x86_64 mingw32-gcc.x86_64 git RUN mkdir /segatools WORKDIR /segatools -COPY aimeio aimeio -COPY amex amex -COPY board board -COPY chunihook chunihook -COPY chuniio chuniio -COPY cxbhook cxbhook -COPY cxbio cxbio -COPY dist dist -COPY divahook divahook -COPY divaio divaio -COPY carolhook carolhook -COPY carolio carolio -COPY doc doc -COPY hooklib hooklib -COPY iccard iccard -COPY idzhook idzhook -COPY idzio idzio -COPY mercuryhook mercuryhook -COPY mercuryio mercuryio -COPY jvs jvs -COPY minihook minihook -COPY mu3hook mu3hook -COPY mu3io mu3io -COPY pki pki -COPY platform platform -COPY gfxhook gfxhook -COPY reg reg -COPY spike spike -COPY subprojects subprojects -COPY util util -COPY CHANGELOG.md CHANGELOG.md -COPY cross-mingw-32.txt cross-mingw-32.txt -COPY cross-mingw-64.txt cross-mingw-64.txt -COPY Makefile Makefile -COPY meson.build meson.build -COPY Package.mk Package.mk -COPY precompiled.h precompiled.h -COPY README.md README.md +VOLUME [ "/segatools" ] -RUN make dist \ No newline at end of file +ENTRYPOINT [ "make", "dist" ] diff --git a/docker-build.bat b/docker-build.bat index eea0e31..1b0ce12 100644 --- a/docker-build.bat +++ b/docker-build.bat @@ -13,19 +13,13 @@ if ERRORLEVEL 1 ( goto failure ) -docker create --name %CONTAINER_NAME% %IMAGE_NAME% +docker run -it --rm -v %~dp0:/segatools --name %CONTAINER_NAME% %IMAGE_NAME% if ERRORLEVEL 1 ( goto failure ) -rd /s /q "!BUILD_OUTPUT_PATH!" -mkdir "!BUILD_OUTPUT_PATH!" - -docker cp %CONTAINER_NAME%:/segatools/build/zip %BUILD_OUTPUT_PATH% - -docker rm -f %CONTAINER_NAME% > nul -docker image rm -f %IMAGE_NAME% > nul +docker image rm -f %IMAGE_NAME% goto success From e4cf6a9e1b767bbc8ba0f8f58e7ba868a4219367 Mon Sep 17 00:00:00 2001 From: Hay1tsme Date: Mon, 13 Feb 2023 02:53:37 -0500 Subject: [PATCH 175/175] fix zips not updating on build --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a3c4197..f39d291 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,11 @@ build: $(V)ninja -C $(BUILD_DIR_64) .PHONY: dist # Build and create a zip distribution package -dist: build zip +dist: build clean-zip zip + +.PHONY: clean-zip # Remove zip files from build dir before packaging +clean-zip: + $(V)rm -Rf $(BUILD_DIR_ZIP)/*.zip .PHONY: zip # Create a zip distribution pacakge zip: $(BUILD_DIR_ZIP)/segatools.zip