From cb3404cdde56d8ef7bb7034e8b2ad5dc7a02e935 Mon Sep 17 00:00:00 2001 From: Egor Date: Mon, 17 Jul 2017 02:58:21 +0300 Subject: [PATCH] devicelost: fix using more generic approach Apparently my original fix only worked for me, because there are multiple sets of Reset calls, and I've only patched one of them. To avoid going through all the executables again, I decided to just detour the Reset function. Idealy we would need to do something like nmlgc/ProxyDLLs, but this should work for now. I tried copying the vtable, but it turned out to be a bad idea, because the size of vtable is unknown. The only other way, besides creating a proxy object, is editing the vtable directly. --- thcrap_tsa/src/devicelost.c | 75 ++++++++++++++++++++++++++++++------- thcrap_tsa/thcrap_tsa.def | 3 +- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/thcrap_tsa/src/devicelost.c b/thcrap_tsa/src/devicelost.c index 365c0f8d..0cec31a3 100644 --- a/thcrap_tsa/src/devicelost.c +++ b/thcrap_tsa/src/devicelost.c @@ -10,32 +10,34 @@ #include #include "thcrap_tsa.h" +// HRESULT values from d3d9.h #define FACILITY_D3D 0x88760000 #define D3D_OK 0 #define D3DERR_DRIVERINTERNALERROR (FACILITY_D3D|2087) #define D3DERR_DEVICELOST (FACILITY_D3D|2152) #define D3DERR_DEVICENOTRESET (FACILITY_D3D|2153) -int BP_devicelost(x86_reg_t *regs, json_t *bp_info) { - // Parameters - // ---------- - void ***d3dd9 = *(void ****)json_object_get_register(bp_info, regs, "d3dd9"); - void *presParams = (void *)json_object_get_immediate(bp_info, regs, "pres_params"); - // ---------- +// vtable indeices +#define D3DD9_TESTCOOPERATIVELEVEL 3 +#define D3DD9_RESET 16 +#define D3D9_CREATEDEVICE 16 - // function no. 16 - Reset - int(__stdcall*d3dd9_Reset)(void***,void*) = (*d3dd9)[16]; - // function no. 3 - TestCooperativeLevel - int(__stdcall*d3dd9_TestCooperativeLevel)(void***) = (*d3dd9)[3]; +// original memeber function pointers +static int(__stdcall*orig_d3dd9_Reset)(void***, void*) = NULL; +static int(__stdcall*orig_d3d9_CreateDevice)(void***, UINT, int, void*, DWORD, void*, void****) = NULL; +static void***(__stdcall*orig_Direct3DCreate9)(UINT SDKVersion) = NULL; - for(;;){ - switch (d3dd9_TestCooperativeLevel(d3dd9)) { +int __stdcall my_d3dd9_Reset(void*** that, void* pPresentationParameters) { + int(__stdcall*d3dd9_TestCooperativeLevel)(void***) = (*that)[D3DD9_TESTCOOPERATIVELEVEL]; + int rv = D3D_OK; + for (;;) { + switch (d3dd9_TestCooperativeLevel(that)) { case D3DERR_DEVICELOST: // wait for a little MsgWaitForMultipleObjects(0, NULL, FALSE, 10, QS_ALLINPUT); break; case D3DERR_DEVICENOTRESET: - d3dd9_Reset(d3dd9, presParams); + rv = orig_d3dd9_Reset(that, pPresentationParameters); break; default: case D3DERR_DRIVERINTERNALERROR: @@ -43,7 +45,52 @@ int BP_devicelost(x86_reg_t *regs, json_t *bp_info) { // panic and return (will probably result in crash) // [[fallthrough]] case D3D_OK: - return 0; + return rv; } } } + +int __stdcall my_d3d9_CreateDevice(void*** that, UINT Adapter, int DeviceType, void* hFocusWindow, DWORD BehaviourFlags, void* pPresentationParameters, void**** ppReturnedDeviceInterface) { + int rv = orig_d3d9_CreateDevice(that, Adapter, DeviceType, hFocusWindow, BehaviourFlags, pPresentationParameters, ppReturnedDeviceInterface); + if (rv == D3D_OK && ppReturnedDeviceInterface && *ppReturnedDeviceInterface) { + if (!orig_d3dd9_Reset) { + void **d3dd9_vtable = **ppReturnedDeviceInterface; + orig_d3dd9_Reset = d3dd9_vtable[D3DD9_RESET]; + + DWORD oldProt; + VirtualProtect(&d3dd9_vtable[D3DD9_RESET], sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + d3dd9_vtable[D3DD9_RESET] = my_d3dd9_Reset; + VirtualProtect(&d3dd9_vtable[D3DD9_RESET], sizeof(void*), oldProt, &oldProt); + } + } + return rv; +} + +void*** __stdcall my_Direct3DCreate9(UINT SDKVersion) { + if (!orig_Direct3DCreate9) return NULL; + void*** rv = orig_Direct3DCreate9(SDKVersion); + if (rv) { + if (!orig_d3d9_CreateDevice) { + void **d3d9_vtable = *rv; + orig_d3d9_CreateDevice = d3d9_vtable[D3D9_CREATEDEVICE]; + + DWORD oldProt; + VirtualProtect(&d3d9_vtable[D3D9_CREATEDEVICE], sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + d3d9_vtable[D3D9_CREATEDEVICE] = my_d3d9_CreateDevice; + VirtualProtect(&d3d9_vtable[D3D9_CREATEDEVICE], sizeof(void*), oldProt, &oldProt); + } + } + return rv; +} + +void devicelost_mod_detour(void) +{ + HMODULE hModule = GetModuleHandle(L"d3d9.dll"); + if (hModule) { + orig_Direct3DCreate9 = GetProcAddress(hModule, "Direct3DCreate9"); + detour_chain("d3d9.dll", 1, + "Direct3DCreate9", my_Direct3DCreate9, &orig_Direct3DCreate9, + NULL + ); + } +} \ No newline at end of file diff --git a/thcrap_tsa/thcrap_tsa.def b/thcrap_tsa/thcrap_tsa.def index 2c7613be..4d088b0e 100644 --- a/thcrap_tsa/thcrap_tsa.def +++ b/thcrap_tsa/thcrap_tsa.def @@ -36,8 +36,6 @@ EXPORTS BP_th06_file_load BP_th06_file_loaded - BP_devicelost - ; Bounds ; ------ fn_for_bounds @@ -110,3 +108,4 @@ EXPORTS ; TSA-specific detours ; -------------------- tsa_mod_detour + devicelost_mod_detour