untitled paste

unlisted ⁨1⁩ ⁨file⁩ 2023-05-07 11:11:20 UTC

sub_531C30.cpp

Raw
#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;
}