235 lines
5 KiB
C++
235 lines
5 KiB
C++
/*++
|
|
|
|
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
|
|
|