#include #include #include #include extern IDirect3DDevice8 *g_pD3DDevice8_8E4768; extern HWND g_hWnd; /** * Try to fill up the video memory first with textures, then with surfaces. * Based on how many of these could be created, return the maximum amount of VRAM available in MiB. **/ DWORD sub_531C30(UnknownSLC2002GameObject *this) { unsigned int numOfCreatedTextures; // edi IDirect3DTexture8 **ppCurrentTexture; // esi int colorDepthSizeReqMultiplier; // ebp IDirect3DDevice8 *pDirect3DDevice8; // eax HRESULT createResult; // eax MAPDST DWORD usableVramBytes; // ebp unsigned int i; // esi IDirect3DTexture8 *pTextureToRelease; // eax IDirectDrawSurface7 **ppCurrentSurface; // esi DWORD redBitMask; // eax DWORD greenBitMask; // ecx DWORD blueBitMask; // edx unsigned int numOfCreatedSurfaces; // edi unsigned int j; // esi IDirectDrawSurface7 *pSurfaceToRelease; // eax DWORD usableVramMiB; // eax D3DFORMAT d3dSurfaceFormat; // [esp+30h] [ebp-10A8h] IDirectDraw7 *pDirectDraw7; // [esp+50h] [ebp-1088h] MAPDST BYREF IDirectDraw *lpIDirectDraw; // [esp+54h] [ebp-1084h] BYREF DWORD _unused; // [esp+58h] [ebp-1080h] DDSURFACEDESC2 surfaceDescriptor; // [esp+5Ch] [ebp-107Ch] BYREF IDirect3DTexture8 *dummyTextures[512]; // [esp+D8h] [ebp-1000h] BYREF IDirectDrawSurface7 *dummySurfaces[512]; // [esp+8D8h] [ebp-800h] BYREF numOfCreatedTextures = 0; ppCurrentTexture = dummyTextures; // This can only be either D3DFMT_X8R8G8B8 or D3DFMT_R5G6B5, depending on whether 32-bit or 16-bit color depth was chosen in the game launcher dialog, respectively. // D3DFMT_R5G6B5: 16-bit RGB pixel format with 5 bits for red, 6 bits for green, and 5 bits for blue. // D3DFMT_X8R8G8B8: 32-bit RGB pixel format, where 8 bits are reserved for each color. d3dSurfaceFormat = this[752]; // 32-bit colors: x2, 16-bit colors: x1 colorDepthSizeReqMultiplier = (d3dSurfaceFormat != D3DFMT_R5G6B5) + 1; // create the textures do { pDirect3DDevice8 = g_pD3DDevice8_8E4768; *ppCurrentTexture = NULL; createResult = pDirect3DDevice8->CreateTexture( 256, // width 256, // height 1, // levels D3DUSAGE_RENDERTARGET, // usage d3dSurfaceFormat, // format D3DPOOL_DEFAULT, // pool ppCurrentTexture); // ppTexture if ( SUCCEEDED(createResult) ) { ++numOfCreatedTextures; ++ppCurrentTexture; } } while ( numOfCreatedTextures <= 512 && SUCCEEDED(createResult) ); // each texture's size in bytes: 128K if 16-bit, 256K if 32-bit if ( createResult == D3DERR_OUTOFVIDEOMEMORY && numOfCreatedTextures ) // Fun fact: the last multiplication was decompiled to x << 17 originally, which is equivalent to x * 0x20000 (131072, 128K) usableVramBytes = (numOfCreatedTextures * colorDepthSizeReqMultiplier) * 0x20000; else usableVramBytes = 0; // clean up the textures for ( i = 0; i < numOfCreatedTextures; ++i ) { pTextureToRelease = dummyTextures[i]; if ( pTextureToRelease ) pTextureToRelease->Release(); dummyTextures[i] = NULL; } // first get a generic IDirectDraw 1.0 interface, then from that retrieve a newer, version 7.0 one if ( FAILED(DirectDrawCreateEx(NULL, (void**)&lpIDirectDraw, IID_IDirectDraw7, NULL)) || FAILED(lpIDirectDraw->QueryInterface(IID_IDirectDraw7, (void **)&pDirectDraw7)) ) { return 0; } // describe what kind of surfaces to create pDirectDraw7->SetCooperativeLevel(g_hWnd, DDSCL_NORMAL); memset(&surfaceDescriptor, 0, sizeof(surfaceDescriptor)); surfaceDescriptor.dwWidth = 256; surfaceDescriptor.dwHeight = 256; surfaceDescriptor.dwSize = 124; surfaceDescriptor.dwFlags = DDSD_PIXELFORMAT|DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS; surfaceDescriptor.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY|DDSCAPS_OFFSCREENPLAIN; memset(&surfaceDescriptor.ddpfPixelFormat, 0, sizeof(surfaceDescriptor.ddpfPixelFormat)); redBitMask = this[389]; // seems to always be 0xF800 greenBitMask = this[390]; // seems to always be 0x7E0 blueBitMask = this[391]; // seems to always be 0x1F surfaceDescriptor.ddpfPixelFormat.dwSize = 32; surfaceDescriptor.ddpfPixelFormat.dwFlags = DDPF_RGB; surfaceDescriptor.ddpfPixelFormat.dwRGBBitCount = 16; surfaceDescriptor.ddpfPixelFormat.dwRBitMask = redBitMask; surfaceDescriptor.ddpfPixelFormat.dwGBitMask = greenBitMask; surfaceDescriptor.ddpfPixelFormat.dwBBitMask = blueBitMask; surfaceDescriptor.ddpfPixelFormat.dwRGBAlphaBitMask = 0x00000000; numOfCreatedSurfaces = 0; ppCurrentSurface = &dummySurfaces[0]; // create the surfaces do { *ppCurrentSurface = NULL; createResult = pDirectDraw7->CreateSurface(&surfaceDescriptor, ppCurrentSurface, NULL); if ( !createResult ) { ++numOfCreatedSurfaces; ++ppCurrentSurface; } } while ( numOfCreatedSurfaces <= 512 && SUCCEEDED(createResult) ); // each surface's size in bytes: always 128K, regardless of color depth set in launcher if ( createResult == D3DERR_OUTOFVIDEOMEMORY && numOfCreatedSurfaces && (numOfCreatedSurfaces * 0x20000) < usableVramBytes ) // if less surfaces could be created than textures, reduce the usable/available VRAM size accordingly usableVramBytes = numOfCreatedSurfaces * 0x20000; // clean up the surfaces for ( j = 0; j < numOfCreatedSurfaces; ++j ) { pSurfaceToRelease = dummySurfaces[j]; if ( pSurfaceToRelease ) pSurfaceToRelease->Release(); dummySurfaces[j] = NULL; } pDirectDraw7->Release(); // 1 byte = 0.00000095367432 MiB; this basically converts usableVramBytes from bytes to Mibibytes. // The long conversion uses MSVC's _ftol behind the scenes. usableVramMiB = (long)((double)usableVramBytes * 0.00000095367432); return usableVramMiB; }