Phase 1a: input forwarding via DLL injection + XInput hook

The host can now inject coop_hook.dll into a running game and forward
controller state to it over shared memory, so the game reads the host's
(eventually the guest's) input and nothing else.

- hook/: coop_hook.dll. DllMain spawns a worker that opens the shared-memory
  channel (named by the game's pid) and installs SafetyHook inline hooks on
  XInputGetState/GetStateEx/GetCapabilities/SetState. Detours synthesize state
  from shared memory; unmanaged slots report disconnected, hiding physical pads.
- host/: process picker (Toolhelp32), CreateRemoteThread(LoadLibraryW) injector
  with an IsWow64Process2 bitness guard, IPC server publishing pads each frame,
  and an ImGui Injection panel wiring it together.
- tests/: hook_selftest exercises the IPC seqlock + hook detours in-process
  (no game/controller needed); passes.

Build: SafetyHook wired in (COOP_BUILD_HOOK=ON), Zydis via FetchContent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 23:21:20 +02:00
parent cf058aecfa
commit e370c8dcc5
19 changed files with 1038 additions and 11 deletions

View File

@@ -28,9 +28,16 @@ add_subdirectory(third_party)
add_subdirectory(common)
add_subdirectory(host)
# The injected hook DLL pulls in SafetyHook (+ Zydis). It is built in Phase 1;
# enable once the dependency is wired so Phase 0 stays minimal.
option(COOP_BUILD_HOOK "Build the injected game-side hook DLL" OFF)
# The injected hook DLL pulls in SafetyHook, which fetches Zydis via FetchContent
# at configure time (allowed for transitive deps).
option(COOP_BUILD_HOOK "Build the injected game-side hook DLL" ON)
if(COOP_BUILD_HOOK)
# SafetyHook's own tests/examples/docs are noise for us.
set(SAFETYHOOK_BUILD_TEST OFF CACHE BOOL "" FORCE)
set(SAFETYHOOK_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(SAFETYHOOK_BUILD_DOCS OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/safetyhook)
add_subdirectory(hook)
enable_testing()
add_subdirectory(tests)
endif()