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:
62
hook/src/ipc_client.hpp
Normal file
62
hook/src/ipc_client.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Hook-side view of the shared-memory IPC channel. The host creates the section
|
||||
// (named by the target game's pid); we open it from inside the game and read the
|
||||
// forwarded pad state the host publishes each frame.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "coop/protocol.hpp"
|
||||
#include "coop/shared_memory.hpp"
|
||||
|
||||
namespace coop::hook
|
||||
{
|
||||
|
||||
class IpcClient
|
||||
{
|
||||
public:
|
||||
// Tries to open the section a few times: the host may inject us slightly
|
||||
// before (or after) it creates the mapping. Returns true once connected.
|
||||
bool connect(int attempts, int delay_ms)
|
||||
{
|
||||
const std::wstring name = shared_memory_name(GetCurrentProcessId());
|
||||
for (int i = 0; i < attempts; ++i)
|
||||
{
|
||||
if (shm_.open(name, sizeof(SharedBlock)))
|
||||
{
|
||||
auto* block = shm_.as<SharedBlock>();
|
||||
if (block->magic == kProtocolMagic && block->version == kProtocolVersion)
|
||||
{
|
||||
block_ = block;
|
||||
return true;
|
||||
}
|
||||
shm_.reset(); // present but not a contract we understand; retry
|
||||
}
|
||||
Sleep(static_cast<DWORD>(delay_ms));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool connected() const
|
||||
{
|
||||
return block_ != nullptr;
|
||||
}
|
||||
|
||||
// Copies a torn-free snapshot of all slots. Returns false only if the host
|
||||
// was mid-write for the whole spin window (caller should reuse its cache).
|
||||
bool snapshot(CoopPadState (&out)[kMaxPads], std::uint32_t& count) const
|
||||
{
|
||||
if (block_ == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return read_pads(*block_, out, count);
|
||||
}
|
||||
|
||||
private:
|
||||
SharedMemory shm_;
|
||||
SharedBlock* block_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace coop::hook
|
||||
Reference in New Issue
Block a user