/*++ Copyright (c) 1999 Microsoft Corporation Module Name: UltimateSoccerManager.cpp Abstract: A hack for Ultimate Soccer Manager (Sierra Sports). The game caches a pointer to a ddraw system memory surface. It later uses that pointer even after the surface has been freed. This worked on Win9x by blind luck: when they re-create a new surface, it happened to end up in the same system memory as before. Notes: This is an app specific shim. History: 01/07/2000 linstev Created --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(UltimateSoccerManager) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY_DIRECTX_COMSERVER() APIHOOK_ENUM_END IMPLEMENT_DIRECTX_COMSERVER_HOOKS() // Keep a list of cached surfaces struct SLIST { struct SLIST *next; DDSURFACEDESC ddsd; LPDIRECTDRAWSURFACE lpDDSurface; }; SLIST *g_SList = NULL; /*++ Hook create surface so we can return the cached surface if possible. --*/ HRESULT COMHOOK(IDirectDraw, CreateSurface)( PVOID pThis, LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE* lplpDDSurface, IUnknown* pUnkOuter ) { HRESULT hReturn; // Retrieve the old function _pfn_IDirectDraw_CreateSurface pfnOld = ORIGINAL_COM(IDirectDraw, CreateSurface, pThis); SLIST *surf = g_SList, *last = NULL; while (surf) { // Check for the same kind of surface. if ((lpDDSurfaceDesc->ddsCaps.dwCaps == surf->ddsd.ddsCaps.dwCaps) && (lpDDSurfaceDesc->dwWidth == surf->ddsd.dwWidth) && (lpDDSurfaceDesc->dwHeight == surf->ddsd.dwHeight)) { *lplpDDSurface = surf->lpDDSurface; if (last) { last->next = surf->next; } else { g_SList = surf->next; } free(surf); DPFN( eDbgLevelInfo, "Returning cached surface %08lx\n", *lplpDDSurface); return DD_OK; } last = surf; surf = surf->next; } if (SUCCEEDED(hReturn = (*pfnOld)( pThis, lpDDSurfaceDesc, lplpDDSurface, pUnkOuter))) { HookObject( NULL, IID_IDirectDrawSurface, (PVOID*)lplpDDSurface, NULL, FALSE); } return hReturn; } /*++ If it's a system memory surface, go ahead and cache it if we're about to release it anyway. --*/ ULONG COMHOOK(IDirectDrawSurface, Release)( LPDIRECTDRAWSURFACE lpDDSurface ) { lpDDSurface->AddRef(); // Retrieve the old function _pfn_IDirectDrawSurface_Release pfnOld = ORIGINAL_COM(IDirectDrawSurface, Release, (LPVOID) lpDDSurface); ULONG uRet = (*pfnOld)(lpDDSurface); if (uRet == 1) { DDSURFACEDESC ddsd = {sizeof(ddsd)}; if (SUCCEEDED(lpDDSurface->GetSurfaceDesc(&ddsd)) && (ddsd.ddsCaps.dwCaps == (DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY))) { SLIST *surf = (SLIST *) malloc(sizeof(SLIST)); surf->next = g_SList; MoveMemory(&surf->ddsd, &ddsd, sizeof(ddsd)); surf->lpDDSurface = lpDDSurface; g_SList = surf; DPFN( eDbgLevelInfo, "Surface %08lx is being cached\n", lpDDSurface); return 0; } } return (*pfnOld)(lpDDSurface); } /*++ Register hooked functions --*/ HOOK_BEGIN APIHOOK_ENTRY_DIRECTX_COMSERVER() COMHOOK_ENTRY(DirectDraw, IDirectDraw, CreateSurface, 6) COMHOOK_ENTRY(DirectDraw, IDirectDrawSurface, Release, 2) HOOK_END IMPLEMENT_SHIM_END