518 lines
11 KiB
C++
518 lines
11 KiB
C++
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
TurkeyHunter.cpp
|
|
|
|
Abstract:
|
|
|
|
In Win9x, IDirectDraw::GetDC simply locks the surface and creates a DC
|
|
around it via internal GDI calls. On NT, GDI supports DCs obtained from
|
|
DirectDraw surfaces.
|
|
|
|
Some games, like Turkey Hunter, use Surface::Unlock to get usage of the
|
|
surface back, instead of Surface::ReleaseDC. Ordinarily we could simply
|
|
make the unlock call the DirectDraw ReleaseDC, except that they continue
|
|
using the DC after they've unlocked the surface.
|
|
|
|
Notes:
|
|
|
|
This is a general purpose hack.
|
|
|
|
History:
|
|
|
|
01/20/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(TurkeyHunter)
|
|
#include "ShimHookMacro.h"
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
APIHOOK_ENUM_ENTRY_DIRECTX_COMSERVER()
|
|
APIHOOK_ENUM_END
|
|
|
|
IMPLEMENT_DIRECTX_COMSERVER_HOOKS()
|
|
|
|
// Link list of open DCs
|
|
struct DC
|
|
{
|
|
DC *next;
|
|
HDC hdc;
|
|
HBITMAP hbmp;
|
|
DWORD dwWidth, dwHeight;
|
|
LPDIRECTDRAWSURFACE lpDDSurface;
|
|
BOOL bBad;
|
|
};
|
|
DC *g_DCList = NULL;
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDrawSurface, ReleaseDC)(
|
|
LPDIRECTDRAWSURFACE lpDDSurface,
|
|
HDC hDC);
|
|
|
|
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
/*++
|
|
|
|
Hook create surface so we can be sure we're being called.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDraw2, CreateSurface)(
|
|
PVOID pThis,
|
|
LPDDSURFACEDESC lpDDSurfaceDesc,
|
|
LPDIRECTDRAWSURFACE* lplpDDSurface,
|
|
IUnknown* pUnkOuter
|
|
)
|
|
{
|
|
HRESULT hReturn;
|
|
|
|
_pfn_IDirectDraw2_CreateSurface pfnOld =
|
|
ORIGINAL_COM(IDirectDraw2, CreateSurface, pThis);
|
|
|
|
if (SUCCEEDED(hReturn = (*pfnOld)(
|
|
pThis,
|
|
lpDDSurfaceDesc,
|
|
lplpDDSurface,
|
|
pUnkOuter)))
|
|
{
|
|
HookObject(
|
|
NULL,
|
|
IID_IDirectDrawSurface,
|
|
(PVOID*)lplpDDSurface,
|
|
NULL,
|
|
FALSE);
|
|
}
|
|
|
|
return hReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Fake a DC - or rather produce a normal GDI DC that doesn't have the surface
|
|
memory backing it.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDrawSurface, GetDC)(
|
|
LPDIRECTDRAWSURFACE lpDDSurface,
|
|
HDC FAR *lphDC
|
|
)
|
|
{
|
|
HRESULT hReturn = DDERR_GENERIC;
|
|
_pfn_IDirectDrawSurface_ReleaseDC pfnOldReleaseDC = NULL;
|
|
_pfn_IDirectDrawSurface_GetDC pfnOld = NULL;
|
|
DDSURFACEDESC ddsd = {sizeof(DDSURFACEDESC)};
|
|
HDC hdc = 0;
|
|
HBITMAP hbmp = 0;
|
|
HGDIOBJ hOld = 0;
|
|
DC *pdc = NULL;
|
|
|
|
if (!lphDC || !lpDDSurface)
|
|
{
|
|
DPFN( eDbgLevelError, "Invalid parameters");
|
|
goto Exit;
|
|
}
|
|
|
|
// Original GetDC
|
|
pfnOld = ORIGINAL_COM(IDirectDrawSurface, GetDC, (LPVOID) lpDDSurface);
|
|
|
|
if (!pfnOld)
|
|
{
|
|
DPFN( eDbgLevelError, "Old GetDC not found");
|
|
goto Exit;
|
|
}
|
|
|
|
if (FAILED(hReturn = (*pfnOld)(
|
|
lpDDSurface,
|
|
lphDC)))
|
|
{
|
|
DPFN( eDbgLevelError, "IDirectDraw::GetDC Failed");
|
|
goto Exit;
|
|
}
|
|
|
|
// We need the surface desc for the surface width and height
|
|
lpDDSurface->GetSurfaceDesc(&ddsd);
|
|
|
|
// Create a DC to be used by the app
|
|
hdc = CreateCompatibleDC(0);
|
|
if (!hdc)
|
|
{
|
|
DPFN( eDbgLevelError, "CreateDC failed");
|
|
goto Exit;
|
|
}
|
|
|
|
// Create the DIB Section
|
|
BITMAPINFO bmi;
|
|
ZeroMemory(&bmi, sizeof(bmi));
|
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 24;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
bmi.bmiHeader.biWidth = ddsd.dwWidth;
|
|
bmi.bmiHeader.biHeight = ddsd.dwHeight;
|
|
hbmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, NULL, NULL, 0);
|
|
|
|
if (!hbmp)
|
|
{
|
|
DPFN( eDbgLevelError, "CreateDIBSection failed");
|
|
goto Exit;
|
|
}
|
|
|
|
// Select the DIB Section into the DC
|
|
hOld = SelectObject(hdc, hbmp);
|
|
BitBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, *lphDC, 0, 0, SRCCOPY);
|
|
|
|
// Original ReleaseDC
|
|
pfnOldReleaseDC =
|
|
ORIGINAL_COM(IDirectDrawSurface, ReleaseDC, (LPVOID) lpDDSurface);
|
|
|
|
if (!pfnOldReleaseDC)
|
|
{
|
|
DPFN( eDbgLevelError, "Old ReleaseDC not found");
|
|
goto Exit;
|
|
}
|
|
// Release the DirectDraw DC
|
|
(*pfnOldReleaseDC)(lpDDSurface, *lphDC);
|
|
|
|
// Return the DC we just created
|
|
*lphDC = hdc;
|
|
|
|
// Add this to our DC list
|
|
pdc = (DC *) malloc(sizeof DC);
|
|
if (pdc)
|
|
{
|
|
pdc->next = g_DCList;
|
|
g_DCList = pdc;
|
|
pdc->hdc = hdc;
|
|
pdc->lpDDSurface = lpDDSurface;
|
|
pdc->hbmp = hbmp;
|
|
pdc->dwHeight = ddsd.dwHeight;
|
|
pdc->dwWidth = ddsd.dwWidth;
|
|
pdc->bBad = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, "Out of memory");
|
|
goto Exit;
|
|
}
|
|
|
|
hReturn = DD_OK;
|
|
|
|
Exit:
|
|
if (hReturn != DD_OK)
|
|
{
|
|
if (hOld && hdc)
|
|
{
|
|
SelectObject(hdc, hOld);
|
|
}
|
|
|
|
if (hbmp)
|
|
{
|
|
DeleteObject(hbmp);
|
|
}
|
|
|
|
if (hdc)
|
|
{
|
|
DeleteDC(hdc);
|
|
}
|
|
}
|
|
|
|
return DD_OK;
|
|
}
|
|
|
|
/*++
|
|
|
|
ReleaseDC has to copy the data back into the surface.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDrawSurface, ReleaseDC)(
|
|
LPDIRECTDRAWSURFACE lpDDSurface,
|
|
HDC hDC
|
|
)
|
|
{
|
|
HRESULT hReturn = DDERR_GENERIC;
|
|
|
|
// Original ReleaseDC
|
|
_pfn_IDirectDrawSurface_ReleaseDC pfnOld =
|
|
ORIGINAL_COM(IDirectDrawSurface, ReleaseDC, (LPVOID) lpDDSurface);
|
|
|
|
|
|
// Run the list to see if we need to do anything
|
|
DC *pdc = g_DCList, *last = NULL;
|
|
while (pdc)
|
|
{
|
|
if ((pdc->lpDDSurface == lpDDSurface) &&
|
|
(pdc->hdc == hDC))
|
|
{
|
|
// Remove it from the list
|
|
if (last)
|
|
{
|
|
last->next = pdc->next;
|
|
}
|
|
else
|
|
{
|
|
g_DCList = pdc->next;
|
|
}
|
|
break;
|
|
}
|
|
last = pdc;
|
|
pdc = pdc->next;
|
|
}
|
|
|
|
// We were in the list and someone used Unlock.
|
|
if (pdc && (pdc->bBad))
|
|
{
|
|
// Original GetDC
|
|
_pfn_IDirectDrawSurface_GetDC pfnOldGetDC =
|
|
ORIGINAL_COM(IDirectDrawSurface, GetDC, (LPVOID)pdc->lpDDSurface);
|
|
|
|
// Original ReleaseDC
|
|
_pfn_IDirectDrawSurface_ReleaseDC pfnOldReleaseDC =
|
|
ORIGINAL_COM(IDirectDrawSurface, ReleaseDC, (LPVOID)pdc->lpDDSurface);
|
|
|
|
if (pfnOldGetDC && pfnOldReleaseDC)
|
|
{
|
|
// Copy everything back onto the surface
|
|
HDC hTempDC;
|
|
HGDIOBJ hOld = SelectObject(hDC, pdc->hbmp);
|
|
if (SUCCEEDED((*pfnOldGetDC)(pdc->lpDDSurface, &hTempDC)))
|
|
{
|
|
BitBlt(hTempDC, 0, 0, pdc->dwWidth, pdc->dwHeight, hDC, 0, 0, SRCCOPY);
|
|
(*pfnOldReleaseDC)(pdc->lpDDSurface, hTempDC);
|
|
}
|
|
SelectObject(hDC, hOld);
|
|
|
|
// Delete the DIB Section
|
|
DeleteObject(pdc->hbmp);
|
|
|
|
// Delete the DC
|
|
DeleteDC(hDC);
|
|
|
|
hReturn = DD_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pfnOld)
|
|
{
|
|
// Didn't need to fake
|
|
hReturn = (*pfnOld)(lpDDSurface, hDC);
|
|
}
|
|
}
|
|
|
|
// Free the list item
|
|
if (pdc)
|
|
{
|
|
free(pdc);
|
|
}
|
|
|
|
return hReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
This is where we detect if Surface::Unlock was called after a Surface::GetDC.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDrawSurface, Unlock)(
|
|
LPDIRECTDRAWSURFACE lpDDSurface,
|
|
LPVOID lpSurfaceData
|
|
)
|
|
{
|
|
HRESULT hRet = DDERR_GENERIC;
|
|
|
|
// Walk the list to see if we're in it.
|
|
DC *pdc = g_DCList;
|
|
while (pdc)
|
|
{
|
|
if (pdc->lpDDSurface == lpDDSurface)
|
|
{
|
|
pdc->bBad = TRUE;
|
|
break;
|
|
}
|
|
pdc = pdc->next;
|
|
}
|
|
|
|
if (!pdc)
|
|
{
|
|
// Original Unlock
|
|
_pfn_IDirectDrawSurface_Unlock pfnOld =
|
|
ORIGINAL_COM(IDirectDrawSurface, Unlock, (LPVOID)lpDDSurface);
|
|
|
|
if (pfnOld)
|
|
{
|
|
// This is just a normal unlock
|
|
hRet = (*pfnOld)(lpDDSurface, lpSurfaceData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We never really locked in the first place, so no harm done.
|
|
hRet = DD_OK;
|
|
}
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
This is a problem case where they Blt after the Surface::Unlock, but before
|
|
the Surface::ReleaseDC.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
COMHOOK(IDirectDrawSurface, Blt)(
|
|
LPDIRECTDRAWSURFACE lpDDDestSurface,
|
|
LPRECT lpDestRect,
|
|
LPDIRECTDRAWSURFACE lpDDSrcSurface,
|
|
LPRECT lpSrcRect,
|
|
DWORD dwFlags,
|
|
LPDDBLTFX lpDDBltFX
|
|
)
|
|
{
|
|
HRESULT hRet = DDERR_GENERIC;
|
|
|
|
// Original Blt
|
|
_pfn_IDirectDrawSurface_Blt pfnOld =
|
|
ORIGINAL_COM(IDirectDrawSurface, Blt, (LPVOID) lpDDDestSurface);
|
|
|
|
if (!pfnOld)
|
|
{
|
|
return hRet;
|
|
}
|
|
|
|
// Are we in the bad state
|
|
DC *pdc = g_DCList;
|
|
while (pdc)
|
|
{
|
|
if (pdc->lpDDSurface == lpDDDestSurface)
|
|
{
|
|
break;
|
|
}
|
|
pdc = pdc->next;
|
|
}
|
|
|
|
if (!pdc)
|
|
{
|
|
return (*pfnOld)(
|
|
lpDDDestSurface,
|
|
lpDestRect,
|
|
lpDDSrcSurface,
|
|
lpSrcRect,
|
|
dwFlags,
|
|
lpDDBltFX);
|
|
}
|
|
|
|
// To get here, there must be an outstanding DC on this surface
|
|
|
|
// Original GetDC
|
|
_pfn_IDirectDrawSurface_GetDC pfnOldGetDC =
|
|
ORIGINAL_COM(IDirectDrawSurface, GetDC, (LPVOID) lpDDDestSurface);
|
|
|
|
|
|
// Original ReleaseDC
|
|
_pfn_IDirectDrawSurface_ReleaseDC pfnOldReleaseDC =
|
|
ORIGINAL_COM(IDirectDrawSurface, ReleaseDC, (LPVOID) lpDDDestSurface);
|
|
|
|
if (!pfnOldGetDC || !pfnOldReleaseDC)
|
|
{
|
|
return hRet;
|
|
}
|
|
|
|
// Copy the DC contents to the surface
|
|
|
|
HDC hTempDC;
|
|
HGDIOBJ hOld = SelectObject(pdc->hdc, pdc->hbmp);
|
|
if (SUCCEEDED((*pfnOldGetDC)(lpDDDestSurface, &hTempDC)))
|
|
{
|
|
BitBlt(hTempDC, 0, 0, pdc->dwWidth, pdc->dwHeight, pdc->hdc, 0, 0, SRCCOPY);
|
|
(*pfnOldReleaseDC)(lpDDDestSurface, hTempDC);
|
|
}
|
|
|
|
// Do the ddraw Blt
|
|
hRet = (*pfnOld)(
|
|
lpDDDestSurface,
|
|
lpDestRect,
|
|
lpDDSrcSurface,
|
|
lpSrcRect,
|
|
dwFlags,
|
|
lpDDBltFX);
|
|
|
|
// Copy stuff back to the DC
|
|
if (SUCCEEDED((*pfnOldGetDC)(lpDDDestSurface, &hTempDC)))
|
|
{
|
|
BitBlt(pdc->hdc, 0, 0, pdc->dwWidth, pdc->dwHeight, hTempDC, 0, 0, SRCCOPY);
|
|
(*pfnOldReleaseDC)(lpDDDestSurface, hTempDC);
|
|
}
|
|
|
|
SelectObject(pdc->hdc, hOld);
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
HOOK_BEGIN
|
|
APIHOOK_ENTRY_DIRECTX_COMSERVER()
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDraw, CreateSurface, 6)
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDraw2, CreateSurface, 6)
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, GetDC, 17)
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, ReleaseDC, 26)
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, Unlock, 32)
|
|
COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, Blt, 5)
|
|
HOOK_END
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|