|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
Commandos.cpp
Abstract:
A hack for Commandos (EIDOS). The game caches a pointer to the ddraw primary surface. On NT, after a mode change, the memory can be mapped into a different location - so when they try to write to it, it access violates.
We know from debugging the app where they keep the cached pointer, so when they restore the surface, we relock it, and patch the new pointer into their store.
Notes:
This is an app specific hack.
History:
10/29/1999 linstev Created
--*/
#include "precomp.h"
IMPLEMENT_SHIM_BEGIN(Commandos) #include "ShimHookMacro.h"
APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY_DIRECTX_COMSERVER() APIHOOK_ENUM_END
IMPLEMENT_DIRECTX_COMSERVER_HOOKS()
static LPVOID pLastPrimary = NULL; static LPDWORD pAppPrimary = NULL;
/*++
Hook create surface so we can be sure we're being called.
--*/
HRESULT COMHOOK(IDirectDraw, CreateSurface)( PVOID pThis, LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE* lplpDDSurface, IUnknown* pUnkOuter ) { HRESULT hReturn; _pfn_IDirectDraw_CreateSurface pfnOld = ORIGINAL_COM(IDirectDraw, CreateSurface, pThis);
if (SUCCEEDED(hReturn = (*pfnOld)( pThis, lpDDSurfaceDesc, lplpDDSurface, pUnkOuter))) { HookObject( NULL, IID_IDirectDrawSurface, (PVOID*)lplpDDSurface, NULL, FALSE); }
return hReturn; }
/*++
Find out where they store the pointer.
--*/
HRESULT COMHOOK(IDirectDrawSurface, Lock)( LPDIRECTDRAWSURFACE lpDDSurface, LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent ) { DDSURFACEDESC ddsd = {sizeof(ddsd)}; HRESULT hReturn, hr;
// Retrieve the old function
_pfn_IDirectDrawSurface_Lock pfnOld = ORIGINAL_COM(IDirectDrawSurface, Lock, lpDDSurface); // Call the old API
if (FAILED(hReturn = (*pfnOld)( lpDDSurface, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent))) { return hReturn; }
// Make sure it's a primary
hr = lpDDSurface->GetSurfaceDesc(&ddsd); if (SUCCEEDED(hr) && (ddsd.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE))) {
// We know:
// 1. They cache the primary address in [esi+0x20]
// 2. They lock the primary more than once
//
// We assume:
// 1. When they lock the primary, esi+0x20 is a valid pointer
if ((pLastPrimary) && (!pAppPrimary)) { __asm { pop edi pop esi mov eax,pLastPrimary cmp [esi+0x20],eax jne WrongESI
// [esi+0x20] does contain the cached pointer
lea eax,[esi+0x20] mov pAppPrimary,eax WrongESI:
push esi push edi } }
pLastPrimary = lpDDSurfaceDesc->lpSurface; } return hReturn; }
/*++
Patch the new pointer directly into their data segment.
--*/
HRESULT COMHOOK(IDirectDrawSurface, Restore)( LPDIRECTDRAWSURFACE lpDDSurface ) { DDSURFACEDESC ddsd = {sizeof(ddsd)}; HRESULT hReturn, hr, hrt; // Retrieve the old function
_pfn_IDirectDrawSurface_Restore pfnOld = ORIGINAL_COM(IDirectDrawSurface, Restore, lpDDSurface);
// Call the old API
if (FAILED(hReturn = (*pfnOld)(lpDDSurface))) { return hReturn; }
// Make sure it's a primary
hr = lpDDSurface->GetSurfaceDesc(&ddsd); if (SUCCEEDED(hr) && (ddsd.ddsCaps.dwCaps & (DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE))) { // Check if we've been set up
if (!((pLastPrimary) && (pAppPrimary))) { return hReturn; }
// We must get a pointer here, so keep trying
do { hr = lpDDSurface->Lock( NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
if (hr == DDERR_SURFACELOST) { // Don't care about result
(*pfnOld)(lpDDSurface); } } while (hr == DDERR_SURFACELOST);
// Patch the new pointer into their memory
pLastPrimary = ddsd.lpSurface; if ((pLastPrimary) && (pAppPrimary)) { *pAppPrimary = (DWORD_PTR)pLastPrimary; }
// Unlock the surface
lpDDSurface->Unlock(NULL); }
return hReturn; }
/*++
Register hooked functions
--*/
HOOK_BEGIN
APIHOOK_ENTRY_DIRECTX_COMSERVER() COMHOOK_ENTRY(DirectDraw, IDirectDraw, CreateSurface, 6) COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, Lock, 25) COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, Restore, 27)
HOOK_END
IMPLEMENT_SHIM_END
|