Run clang-format (the repo's .clang-format: LLVM base, 120 cols, tabs,
Allman functions) over every source file so the tree is formatter-clean.
Whitespace only -- no behavior change; full x64 + x86 suites pass.
Also set SortIncludes: false in .clang-format. Windows include order is
load-bearing (windows.h must precede tlhelp32.h / mmreg.h / xinput.h /
dinput.h; winsock2.h must precede windows.h), and the default
alphabetical sort reorders tlhelp32.h ahead of windows.h -- a build
break. Leaving order alone keeps the manual, correct grouping.
On an explicit Disconnect and on graceful tool exit, the host now asks the
injected DLL to remove every subsystem so the game runs exactly as if it was
never hooked (each hook restores its original bytes). The DLL stays injected but
dormant, ready for a later reconnect -- we never eject it.
Before, both paths just dropped the IPC channel (IpcServer::stop) without telling
the DLL, leaving the hooks active with frozen forwarded state until the game
exited.
- IpcServer::request_unhook_all() sets every subsystem_disabled flag (the DLL
reconciles to fully unhooked on its next tick); all_hooks_removed() reads the
hook registry back so the host can confirm the game is vanilla.
- InjectionPanel::disconnect_graceful() requests the unhook, waits (bounded) for
the registry to clear, then stops. Wired into the Disconnect button (700ms) and
the destructor (300ms). The flags persist in the section the DLL keeps alive, so
the unhook completes even if the host exits before confirming.
Tests (failing first):
- ipc_server_test: request_unhook_all() disables all subsystems; all_hooks_removed()
tracks the registry. Deterministic, no game.
- mock_game_test test_graceful_disconnect: inject -> hooks installed -> request
unhook-all -> every hook removed (game vanilla) while the DLL stays alive
(heartbeat advancing). Full suite still passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>