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:
41
README.md
41
README.md
@@ -42,14 +42,19 @@ See [`docs`](docs) and the in-repo plan for the full design.
|
||||
|
||||
## Status
|
||||
|
||||
**Phase 0 — donor spike (current).** `coop_host.exe` is a borderless D3D11 window
|
||||
with an ImGui overlay that lists every controller it can see. It exists to
|
||||
validate the riskiest assumption before anything else is built: *can Steam RPT
|
||||
stream an arbitrary window launched under a donor appid, and route a guest's
|
||||
gamepad into it?*
|
||||
**Phase 0 — donor spike. ✅ Validated.** `coop_host.exe` is a borderless D3D11
|
||||
window with an ImGui overlay listing every controller it sees. Confirmed
|
||||
end-to-end: Steam RPT streams the window under a donor appid, and guest gamepads
|
||||
arrive (with correct slot assignment) as XInput.
|
||||
|
||||
Later phases (capture, injection, audio) are scoped in the plan and gated on
|
||||
Phase 0 passing.
|
||||
**Phase 1a — input forwarding (current).** The host can inject `coop_hook.dll`
|
||||
into a running game; the DLL hooks XInput (via SafetyHook) so the game reads the
|
||||
controller state the host forwards over shared memory — and *only* that state, so
|
||||
physical/other controllers are hidden from the game. The in-process
|
||||
`hook_selftest` validates the IPC + hook core without needing a game.
|
||||
|
||||
Still ahead (scoped in the plan): video mirror (WGC, then a `Present` hook),
|
||||
audio (WASAPI process loopback), and x86 support.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -107,3 +112,25 @@ a donor game that supports Remote Play Together.
|
||||
**If steps 2 and 4 both work, the core premise holds** and we proceed to Phase 1
|
||||
(window capture + input injection). If not, we revisit the donor-attribution
|
||||
approach before building further.
|
||||
|
||||
## Phase 1a: testing input forwarding locally
|
||||
|
||||
This needs no RPT, donor, or second account — just the host, the hook, a
|
||||
controller, and a target game. `coop_host.exe` and `coop_hook.dll` must sit in
|
||||
the same folder (the build places both in `bin/<Config>/`).
|
||||
|
||||
1. Start a DRM-free, **non-anti-cheat**, XInput game (e.g. a small controller
|
||||
sample or a permissive indie title) and get to a screen that reads the pad.
|
||||
2. Run `bin\Debug\coop_host.exe`. In the **Injection** panel, filter for the
|
||||
game's `.exe`, select it, and click **Inject & Connect**. The status line
|
||||
should turn green ("Injected … / Forwarding input to pid …").
|
||||
3. Press buttons on your physical controller. The game should respond — its
|
||||
XInput now comes from the host's forwarded state, not the device directly.
|
||||
Unplug-test: other controllers/slots are hidden from the game.
|
||||
4. Click **Stop forwarding** (or quit the host) to tear down the channel.
|
||||
|
||||
> If injection fails with an access error, run the host as administrator. If it
|
||||
> reports "target is 32-bit", that game needs the x86 hook (a later phase).
|
||||
|
||||
For a quick sanity check of the forwarding core without a game, run
|
||||
`bin\Debug\hook_selftest.exe` — it should print `SELFTEST PASS`.
|
||||
|
||||
Reference in New Issue
Block a user