#include <windows.h>
#include <D3DX8.h>
#include <ddraw.h>
#include <d3d8.h>
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;
}