Phase 3: x86 (32-bit) game support via injector helper

Drive a nested Win32 sub-build (CMake ExternalProject, re-entrant via
COOP_X86_HELPER_BUILD) from the normal x64 build to produce coop_hook_x86.dll and
a 32-bit coop_inject_x86.exe, staged next to the x64 binaries. The host detects a
WOW64 target with IsWow64Process2 and spawns the helper to load the x86 DLL, since
a 64-bit process can't cleanly inject a 32-bit one. The shared-memory IPC is
fixed-width / bitness-stable, so the x64 host and x86 hook interoperate.

Validated end-to-end against Slaps and Beans (32-bit D3D11): all 15 hooks
installed, heartbeat advancing, the Present hook engaged (shared a 1920x1080
backbuffer -- the real-game video-hook proof Phantom Brave's D3D9 couldn't give),
and status/audio/video/log IPC all crossed the x64<->x86 boundary. coop_audio_probe
now also delegates to the helper for WOW64 targets. All 5 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 11:42:42 +02:00
parent 36b861d167
commit b1f8783321
8 changed files with 261 additions and 15 deletions

View File

@@ -37,8 +37,12 @@ XInput game becomes Remote-Play-Together-able.
co-op/local-multiplayer titles without active anti-cheat.
- **XInput only:** the game must read controllers via XInput (the common case).
DirectInput-only / RawInput-only games are not handled.
- **x64 only:** the host and hook DLL must match the game's bitness, and only x64
is built today. 32-bit games need the x86 hook + injector (see Roadmap).
- **32-bit games supported via a helper:** the host is x64, but the build also
produces an x86 hook DLL (`coop_hook_x86.dll`) and a 32-bit injector helper
(`coop_inject_x86.exe`). When the target is a 32-bit (WOW64) process the host
detects it (`IsWow64Process2`) and shells out to the helper to load the x86 DLL
(a 64-bit process can't cleanly inject a 32-bit one). The shared-memory IPC is
fixed-width / bitness-stable, so the x64 host and x86 hook interoperate.
- **Local audio echo (fixed via the hook; falls back otherwise):** when the
render-hook is active it silences the game's local playback while mirroring it,
so there is no echo. If the hook can't attach or the game uses an
@@ -143,14 +147,18 @@ Done:
(drives a real D3D11 swapchain end-to-end and reads the pixels back through the
shared texture).
- **x86 (32-bit) game support. ✅** A nested Win32 sub-build (CMake
`ExternalProject`, driven from the normal x64 build) produces `coop_hook_x86.dll`
and a 32-bit `coop_inject_x86.exe`, staged next to the x64 binaries. The host
detects a WOW64 target with `IsWow64Process2` and spawns the helper to inject
the x86 DLL. Validated end-to-end against Slaps and Beans (a 32-bit D3D11 game):
all subsystems hooked, and the IPC channels (status, audio ring, video share,
log) all flow across the x64↔x86 boundary.
Future work, roughly in priority order:
- **Steam Input:** consume guest input through the Steam Input API directly
rather than XInput.
- **x86 support:** add an x86 build of `coop_hook.dll` plus an x86 injector
helper the x64 host spawns, so 32-bit games work (a 64-bit process can't
cleanly inject a 32-bit one). Detect target bitness with `IsWow64Process2`.
The shared-memory IPC layout is already fixed-width / bitness-stable.
## Building
@@ -166,6 +174,11 @@ cmake --build build --config Debug
# output: bin/Debug/coop_host.exe (+ coop_hook.dll, test exes)
```
The x64 build also drives a nested Win32 sub-build (CMake `ExternalProject`,
configured into `build/x86/`) that produces `coop_hook_x86.dll` and
`coop_inject_x86.exe` for 32-bit games, staged next to the x64 binaries. Disable
it with `-DCOOP_BUILD_X86_HELPER=OFF` if you don't need 32-bit support.
Third-party dependencies (Dear ImGui, SafetyHook) are git submodules under
`third_party/`; the Steamworks SDK is vendored manually there when wired. No
vcpkg / package manager is used.
@@ -259,8 +272,9 @@ Useful checks while developing without RPT: tick **Forward synthetic test input*
in the Injection panel to make the game move on its own (proving forwarding is the
source), and click away from the game to confirm focus spoofing keeps it running.
> Injection access error → run the host as administrator. "Target is 32-bit" →
> that game needs the x86 hook (see Roadmap).
> Injection access error → run the host as administrator. A 32-bit (WOW64) target
> is injected automatically via `coop_inject_x86.exe` + `coop_hook_x86.dll`; if
> those aren't next to the host, rebuild (the x86 sub-build stages them there).
## Lessons learned