sub_531C30.cpp
@@ -0,0 +1,154 @@
+#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;
+}
\ No newline at end of file