MinHook flushes only the 5 patched bytes; under the x86-on-ARM64
emulator (xtajit) that can leave a stale JIT translation of the
modified prologue and trap with a non-continuable breakpoint. Flush the
whole prologue and the trampoline on install, and the restored prologue
on teardown, so the emulator re-translates them. No-op on native x86.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep the o_* trampoline pointers valid on RemoveAllD3D9Hooks and add a
g_unhooked flag each detour checks after calling the original, so a
surviving vtable patch invoked during/after teardown just returns the
real result instead of touching freed TextureClient state. Tear down
off the loader lock via a new Shutdown export and skip teardown on
process exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The D3DPOOL_DEFAULT multisample branch aliased pSurfaceLevel_orig onto
pResolvedSurface, leaking the GetSurfaceLevel reference and releasing the
resolved surface twice (use-after-free). Track the read source in a separate
non-owning pointer so each surface is released exactly once on every path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 1.9
* Release replacement textures when a client is torn down
TextureClient::~TextureClient deleted the per-texture side-state but never
released the replacement IDirect3DTexture9 objects gMod created. That's
fine when the client dies because the device hit refcount 0 (a texture
refs its device, so by then every fake is already released and the maps
are empty). But when gMod is unloaded via FreeLibrary while the game's
device is still alive (late-injected/SetDevice integrations), the maps
are full of textures gMod still owns and they all leak - and ExitInstance
never deleted the clients, so the destructor didn't even run.
- ~TextureClient now releases each replacement it still owns (a fake is
still owned while it has a partner; an orphaned fake was already
released and is left alone). No-op in the device-release path.
- Add DestroyAllTextureClients() to delete the per-device clients.
- ExitInstance calls it, gated on a genuine FreeLibrary
(lpReserved == nullptr) so it's skipped during process termination
where the device/d3d9 may already be gone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Jon <>
Co-authored-by: Marc <m@pyc.ac>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>