// thunks.cpp — runtime replacements called by AC into our DLL #include "thunks.h" #include "ac_addrs.h" // ===== v5 — replacement PurgeResource for RenderSurface / RenderTexture ===== // // Vtable slots use thiscall (ECX = this). MSVC __fastcall(arg1, arg2) // receives arg1 in ECX and arg2 in EDX. EDX is scratch from the caller // and isn't used, so we make it an unused parameter. // // Effect: instead of the no-op stub `mov al,1; ret`, we now actually // call Destroy() on the resource (frees its D3D handle + heap state) // then return 1 so PurgeOldResources marks it cleanly purged. typedef void (__fastcall *destroy_fn_t)(void* self, void* edx_unused); extern "C" int __fastcall purge_rendersurface_thunk(void* self, void* /*edx*/) { ((destroy_fn_t)ac::V5_RS_DESTROY_VA)(self, 0); return 1; } extern "C" int __fastcall purge_rendertexture_thunk(void* self, void* /*edx*/) { ((destroy_fn_t)ac::V5_RT_DESTROY_VA)(self, 0); return 1; } // ===== v14 — CEnvCell::Destroy ClipPlaneList cleanup ===== // // EoR's CEnvCell::Destroy contains an 18-byte cleanup block at // 0x0052E661 that only zeros cplane_num without freeing the underlying // ClipPlaneList object. We replace those 18 bytes with a 5-byte // JMP rel32 into the naked thunk below + 13 NOPs. // // Register context at entry (preserved from caller): // esi = `this` (CEnvCell) // ebx = 0 (cleared earlier in Destroy — relied on by the original // buggy `mov [eax], ebx`) // edi/ebp = live in surrounding loop // // On exit, we JMP to V14_RESUME_VA (the instruction immediately after // the 18-byte block). extern "C" __declspec(naked) void v14_clipplane_cleanup_thunk() { __asm { pushad ; preserve everything mov edi, [esi + 0xDC] ; outer ClipPlaneList wrapper ptr test edi, edi jz done mov ecx, [edi] ; inner ClipPlaneList ptr test ecx, ecx jz free_outer // Free the inner ClipPlaneList properly push ecx mov eax, ac::V14_CLIPPLANELIST_DTOR call eax ; ClipPlaneList::~ClipPlaneList (thiscall) pop ecx push ecx mov eax, ac::V14_OPERATOR_DELETE call eax ; operator delete(inner) add esp, 4 free_outer: push edi mov eax, ac::V14_OPERATOR_DELETE_ARR call eax ; operator delete[](outer) add esp, 4 mov dword ptr [esi + 0xDC], 0 ; clear back-pointer done: popad push ac::V14_RESUME_VA ; jmp to resume point ret } }