Owner-authorized source migration: 31 helper-only translation units were merged into their callers or existing inline headers; both build manifests updated. Includes the preserved source payload, Lua configuration matching wowdecomp, and exact malloca expansion without diagnostic pragmas. 449 native units compiled locally and 449 staged units compiled by the pinned server toolchain. No previously byte-matched function regressed. Compiler mapping diagnostics remain recorded as needs_cleanup, not accepted or peer-confirmed. Existing independent review records are preserved. README.md is intentionally unchanged.
480 lines
15 KiB
C++
480 lines
15 KiB
C++
#include "Render/DX9/D3DX9Runtime.h"
|
|
#include "Render/Primitives/RenderPrimitivePool.h"
|
|
#include "Render/Primitives/RenderQueues.h"
|
|
#include "Render/Textures/SpritePixels.h"
|
|
#include "Render/DX9/TextureDX9.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
namespace {
|
|
// INLINED
|
|
std::uint16_t Convert16(std::uint32_t color, D3DFORMAT format) {
|
|
switch (format) {
|
|
case D3DFMT_R5G6B5:
|
|
return ((color & 255) >> 3) | ((color >> 5) & 0x7E0) | ((color >> 8) & 0xF800);
|
|
case D3DFMT_A1R5G5B5:
|
|
return ((color >> 16) & 0x8000) | ((color & 255) >> 3) | ((color >> 6) & 0x3E0) | ((color >> 9) & 0x7C00);
|
|
case D3DFMT_A4R4G4B4:
|
|
return ((color & 255) >> 4) | ((color >> 8) & 0xF0) | ((color >> 12) & 0xF00) | ((color >> 16) & 0xF000);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// INLINED
|
|
UINT RoundUpPowerOfTwo(UINT value) {
|
|
--value;
|
|
value |= value >> 1;
|
|
value |= value >> 2;
|
|
value |= value >> 4;
|
|
value |= value >> 8;
|
|
value |= value >> 16;
|
|
|
|
return value + 1 + (value == 0xFFFFFFFFu);
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00572500
|
|
// FUNCTION: fn_02474
|
|
CTextureDX9::CTextureDX9(TextureDX9Context& context)
|
|
: m_context(&context), m_renderTargetUsage(false), m_dynamic(false), m_allowDynamic(false), m_renderTarget(false), m_texture(nullptr), m_surface(nullptr),
|
|
m_description{} {
|
|
this->m_textureWidth = this->m_textureHeight = this->m_width = this->m_height = 1;
|
|
this->m_format = TextureFormat::A1R5G5B5;
|
|
}
|
|
|
|
// OFFSET: 0x005725C0
|
|
// FUNCTION: fn_02475
|
|
CTextureDX9::CTextureDX9(TextureDX9Context& context, int width, int height, int format, bool dynamic, bool renderTarget) : CTextureDX9(context) {
|
|
this->m_allowDynamic = dynamic;
|
|
this->m_renderTarget = renderTarget;
|
|
this->Create(width, height, format);
|
|
this->SetImageSize(width, height);
|
|
}
|
|
|
|
// OFFSET: 0x00572650
|
|
// FUNCTION: fn_02476
|
|
CTextureDX9::CTextureDX9(TextureDX9Context& context, IDirect3DTexture9* texture) : CTextureDX9(context) {
|
|
this->m_texture = texture;
|
|
if (SUCCEEDED(texture->GetSurfaceLevel(0, &this->m_surface)) && SUCCEEDED(this->m_surface->GetDesc(&this->m_description))) {
|
|
this->m_renderTargetUsage = (this->m_description.Usage & D3DUSAGE_RENDERTARGET) != 0;
|
|
this->m_dynamic = (this->m_description.Usage & D3DUSAGE_DYNAMIC) != 0;
|
|
this->m_textureWidth = this->m_description.Width;
|
|
this->m_textureHeight = this->m_description.Height;
|
|
|
|
switch (this->m_description.Format) {
|
|
case D3DFMT_A8R8G8B8:
|
|
this->m_format = TextureFormat::A8R8G8B8;
|
|
break;
|
|
case D3DFMT_R5G6B5:
|
|
this->m_format = TextureFormat::R5G6B5;
|
|
break;
|
|
case D3DFMT_X1R5G5B5:
|
|
this->m_format = TextureFormat::X1R5G5B5;
|
|
break;
|
|
case D3DFMT_A1R5G5B5:
|
|
this->m_format = TextureFormat::A1R5G5B5;
|
|
break;
|
|
case D3DFMT_A4R4G4B4:
|
|
this->m_format = TextureFormat::A4R4G4B4;
|
|
break;
|
|
default:
|
|
this->m_format = TextureFormat::UnknownTextureFormat;
|
|
break;
|
|
}
|
|
|
|
this->m_width = this->m_height = this->m_description.Width;
|
|
}
|
|
}
|
|
|
|
// INLINED
|
|
CTextureDX9::~CTextureDX9() {
|
|
if (this->m_surface) {
|
|
this->m_surface->Release();
|
|
this->m_surface = nullptr;
|
|
}
|
|
|
|
if (this->m_texture) {
|
|
this->m_texture->Release();
|
|
this->m_texture = nullptr;
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00572AC0
|
|
// FUNCTION: fn_02480
|
|
bool CTextureDX9::Create(int width, int height, int format) {
|
|
if (this->m_surface) {
|
|
this->m_surface->Release();
|
|
this->m_surface = nullptr;
|
|
}
|
|
|
|
if (this->m_texture) {
|
|
this->m_texture->Release();
|
|
this->m_texture = nullptr;
|
|
}
|
|
|
|
D3DFORMAT nativeFormat = D3DFMT_UNKNOWN;
|
|
|
|
switch (format) {
|
|
case TextureFormat::A1R5G5B5:
|
|
nativeFormat = D3DFMT_A1R5G5B5;
|
|
break;
|
|
case TextureFormat::A4R4G4B4:
|
|
nativeFormat = D3DFMT_A4R4G4B4;
|
|
break;
|
|
case TextureFormat::R5G6B5:
|
|
nativeFormat = D3DFMT_R5G6B5;
|
|
break;
|
|
case TextureFormat::X1R5G5B5:
|
|
nativeFormat = D3DFMT_X1R5G5B5;
|
|
break;
|
|
case TextureFormat::A8R8G8B8:
|
|
nativeFormat = D3DFMT_A8R8G8B8;
|
|
break;
|
|
}
|
|
|
|
UINT w = std::min<UINT>(RoundUpPowerOfTwo(width), this->m_context->caps.MaxTextureWidth);
|
|
UINT h = std::min<UINT>(RoundUpPowerOfTwo(height), this->m_context->caps.MaxTextureHeight);
|
|
UINT ratio = this->m_context->caps.MaxTextureAspectRatio;
|
|
|
|
if (ratio) {
|
|
if (w / h > ratio) {
|
|
h = w / ratio;
|
|
}
|
|
|
|
if (h / w > ratio) {
|
|
w = h / ratio;
|
|
}
|
|
}
|
|
|
|
this->m_textureWidth = w;
|
|
this->m_textureHeight = h;
|
|
this->m_format = format;
|
|
DWORD usage = this->m_renderTarget ? 0 : D3DUSAGE_AUTOGENMIPMAP;
|
|
D3DPOOL pool = D3DPOOL_MANAGED;
|
|
this->m_dynamic = false;
|
|
|
|
if (this->m_allowDynamic && this->m_context->dynamicTextures) {
|
|
this->m_dynamic = this->m_context->direct3D && (this->m_context->caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) &&
|
|
this->m_context->direct3D->CheckDeviceFormat(
|
|
this->m_context->adapter, D3DDEVTYPE_HAL, this->m_context->displayFormat, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, nativeFormat
|
|
) == D3D_OK;
|
|
pool = D3DPOOL_DEFAULT;
|
|
if (this->m_dynamic) {
|
|
usage = D3DUSAGE_DYNAMIC | (this->m_renderTarget ? 0 : D3DUSAGE_AUTOGENMIPMAP);
|
|
}
|
|
}
|
|
|
|
if (FAILED(this->m_context->device->CreateTexture(w, h, 1, usage, nativeFormat, pool, &this->m_texture, nullptr)) ||
|
|
FAILED(this->m_texture->GetSurfaceLevel(0, &this->m_surface)) || FAILED(this->m_surface->GetDesc(&this->m_description))) {
|
|
return false;
|
|
}
|
|
|
|
if (!this->m_renderTarget) {
|
|
for (UINT level = 0; level < this->m_texture->GetLevelCount(); ++level) {
|
|
IDirect3DSurface9* surface = nullptr;
|
|
if (FAILED(this->m_texture->GetSurfaceLevel(level, &surface))) {
|
|
break;
|
|
}
|
|
|
|
this->ClearSurface(surface, 0);
|
|
surface->Release();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// OFFSET: 0x00572D30
|
|
// FUNCTION: fn_02481
|
|
bool CTextureDX9::Resize(int width, int height) {
|
|
return this->Create(width, height, this->m_format);
|
|
}
|
|
|
|
// OFFSET: 0x00573050
|
|
// FUNCTION: fn_02485
|
|
SurfaceLock CTextureDX9::Lock() {
|
|
SurfaceLock output{};
|
|
D3DLOCKED_RECT locked;
|
|
if (this->m_surface && SUCCEEDED(this->m_surface->LockRect(&locked, nullptr, 0))) {
|
|
output = {true, locked.pBits, locked.Pitch, this->m_description.Width, this->m_description.Height, 0};
|
|
if (this->m_description.Format == D3DFMT_A8R8G8B8 || this->m_description.Format == D3DFMT_X8R8G8B8) {
|
|
output.bitsPerPixel = 32;
|
|
} else if (this->m_description.Format >= D3DFMT_R5G6B5 && this->m_description.Format <= D3DFMT_A4R4G4B4) {
|
|
output.bitsPerPixel = 16;
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
// OFFSET: 0x00572D50
|
|
// OFFSET: 0x005737A0
|
|
// FUNCTION: fn_02482
|
|
void CTextureDX9::Filter() {
|
|
if (!this->m_dynamic && !this->m_renderTarget && this->m_texture && this->m_texture->GetLevelCount() > 1) {
|
|
this->m_context->d3dx->filterTexture(this->m_texture, nullptr, UINT(-1), DWORD(-1));
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x005731E0
|
|
// FUNCTION: fn_02489
|
|
void CTextureDX9::Unlock() {
|
|
if (this->m_surface && SUCCEEDED(this->m_surface->UnlockRect())) {
|
|
this->Filter();
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00572D90
|
|
// FUNCTION: fn_02483
|
|
bool CTextureDX9::GetDC(HDC* output) {
|
|
return this->m_surface && SUCCEEDED(this->m_surface->GetDC(output));
|
|
}
|
|
|
|
// OFFSET: 0x00573110
|
|
// FUNCTION: fn_02486
|
|
void CTextureDX9::ReleaseDC(HDC dc) {
|
|
if (this->m_surface) {
|
|
this->m_surface->ReleaseDC(dc);
|
|
this->Filter();
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x005737A0
|
|
// FUNCTION: fn_02492
|
|
void CTextureDX9::Update(const RECT*) {
|
|
this->Filter();
|
|
}
|
|
|
|
// OFFSET: 0x00572940
|
|
// FUNCTION: fn_02478
|
|
void CTextureDX9::Fill(const RECT* rectangle, std::uint32_t color) {
|
|
if (this->m_surface) {
|
|
const RECT pixel{0, 0, 1, 1};
|
|
if (SUCCEEDED(this->m_context->d3dx->loadSurfaceFromMemory(this->m_surface, nullptr, rectangle, &color, D3DFMT_A8R8G8B8, 4, nullptr, &pixel, 2, 0))) {
|
|
this->Filter();
|
|
}
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x00572DC0
|
|
// FUNCTION: fn_02484
|
|
void CTextureDX9::ClearSurface(IDirect3DSurface9* surface, std::uint32_t color) {
|
|
D3DSURFACE_DESC description;
|
|
D3DLOCKED_RECT locked;
|
|
if (FAILED(surface->GetDesc(&description)) || FAILED(surface->LockRect(&locked, nullptr, 0))) {
|
|
return;
|
|
}
|
|
|
|
for (UINT y = 0; y < description.Height; ++y) {
|
|
auto* row = static_cast<unsigned char*>(locked.pBits) + y * locked.Pitch;
|
|
if (this->m_description.Format == D3DFMT_A8R8G8B8) {
|
|
for (UINT x = 0; x < description.Width; ++x) {
|
|
std::memcpy(row + x * sizeof(color), &color, sizeof(color));
|
|
}
|
|
} else if (this->m_description.Format == D3DFMT_R5G6B5 || this->m_description.Format == D3DFMT_A1R5G5B5 || this->m_description.Format == D3DFMT_A4R4G4B4) {
|
|
const std::uint16_t value = Convert16(color, this->m_description.Format);
|
|
|
|
for (UINT x = 0; x < description.Width; ++x) {
|
|
std::memcpy(row + x * sizeof(value), &value, sizeof(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
surface->UnlockRect();
|
|
}
|
|
|
|
// OFFSET: 0x00573240
|
|
// FUNCTION: fn_02490
|
|
bool CTextureDX9::UploadSurface(
|
|
IDirect3DSurface9* surface, int x, int y, int width, int height, const std::uint32_t* pixels, bool blackTransparent, int sourceWidth
|
|
) {
|
|
D3DSURFACE_DESC description;
|
|
if (FAILED(surface->GetDesc(&description))) {
|
|
return false;
|
|
}
|
|
|
|
const int stride = sourceWidth ? sourceWidth : width;
|
|
int stepX = 1, stepY = 1;
|
|
int extentX = x + width;
|
|
int extentY = y + height;
|
|
|
|
if (static_cast<UINT>(extentX) > this->m_width) {
|
|
do {
|
|
++stepX;
|
|
extentX /= stepX;
|
|
} while (static_cast<UINT>(extentX) > this->m_width);
|
|
}
|
|
|
|
if (static_cast<UINT>(extentY) > this->m_height) {
|
|
do {
|
|
++stepY;
|
|
extentY /= stepY;
|
|
} while (static_cast<UINT>(extentY) > this->m_height);
|
|
}
|
|
|
|
const int w = width / stepX, h = height / stepY;
|
|
if (!w || !h) {
|
|
return false;
|
|
}
|
|
|
|
const RECT rectangle{x / stepX, y / stepY, x / stepX + w, y / stepY + h};
|
|
D3DLOCKED_RECT locked;
|
|
if (FAILED(surface->LockRect(&locked, &rectangle, 0))) {
|
|
return false;
|
|
}
|
|
|
|
for (int row = 0; row < h; ++row) {
|
|
auto* output = static_cast<unsigned char*>(locked.pBits) + row * locked.Pitch;
|
|
|
|
for (int column = 0; column < w; ++column) {
|
|
const std::uint32_t color = pixels[row * stepY * stride + column * stepX];
|
|
if (description.Format == D3DFMT_A8R8G8B8) {
|
|
std::memcpy(output + column * sizeof(color), &color, sizeof(color));
|
|
} else if (description.Format == D3DFMT_R5G6B5) {
|
|
const std::uint16_t converted = Convert16(color, description.Format);
|
|
std::memcpy(output + column * sizeof(converted), &converted, sizeof(converted));
|
|
} else if (description.Format == D3DFMT_A1R5G5B5 || description.Format == D3DFMT_A4R4G4B4) {
|
|
std::uint16_t converted = Convert16(color, description.Format);
|
|
const std::uint16_t rgb = description.Format == D3DFMT_A1R5G5B5 ? converted & 0x7FFF : converted & 0x0FFF;
|
|
if (rgb == (description.Format == D3DFMT_A1R5G5B5 ? 0x7C1F : 0xF0F) || (blackTransparent && !rgb)) {
|
|
converted = 0;
|
|
}
|
|
|
|
std::memcpy(output + column * sizeof(converted), &converted, sizeof(converted));
|
|
}
|
|
}
|
|
}
|
|
|
|
return SUCCEEDED(surface->UnlockRect());
|
|
}
|
|
|
|
// OFFSET: 0x00573600
|
|
// FUNCTION: fn_02491
|
|
void CTextureDX9::Upload(int x, int y, int width, int height, const std::uint32_t* pixels, int option, int sourceWidth) {
|
|
this->SetImageSize(width, height);
|
|
this->unknown_0008 = (this->unknown_0008 & 255) | (static_cast<unsigned char>(option) << 8);
|
|
if (!pixels) {
|
|
return;
|
|
}
|
|
|
|
IDirect3DTexture9* temporary = nullptr;
|
|
IDirect3DSurface9* surface = nullptr;
|
|
if (!this->m_renderTarget) {
|
|
surface = this->m_surface;
|
|
if (surface) {
|
|
surface->AddRef();
|
|
}
|
|
} else if (SUCCEEDED(this->m_context->device->CreateTexture(
|
|
this->m_textureWidth, this->m_textureHeight, !this->m_context->dynamicTextures, 0, this->m_description.Format, D3DPOOL_SYSTEMMEM, &temporary,
|
|
nullptr
|
|
)) &&
|
|
SUCCEEDED(temporary->GetSurfaceLevel(0, &surface))) {
|
|
this->ClearSurface(surface, 0);
|
|
}
|
|
|
|
if (surface) {
|
|
this->UploadSurface(surface, x, y, width, height, pixels, option != 0, sourceWidth);
|
|
if (this->m_renderTarget && temporary) {
|
|
if (temporary->GetLevelCount() > 1) {
|
|
this->m_context->d3dx->filterTexture(temporary, nullptr, UINT(-1), DWORD(-1));
|
|
}
|
|
|
|
this->m_context->device->UpdateTexture(temporary, this->m_texture);
|
|
} else {
|
|
this->Filter();
|
|
}
|
|
}
|
|
|
|
if (surface) {
|
|
surface->Release();
|
|
}
|
|
|
|
if (temporary) {
|
|
temporary->Release();
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x005737E0
|
|
// FUNCTION: fn_02493
|
|
void CTextureDX9::UploadSprite(int x, int y, int width, int height, const SpriteRleImage* rawImage, const std::uint32_t* palette) {
|
|
if (!this->m_surface) {
|
|
return;
|
|
}
|
|
|
|
RECT rectangle{x, y, x + width, y + height};
|
|
D3DLOCKED_RECT locked;
|
|
if (FAILED(this->m_surface->LockRect(&locked, &rectangle, 0))) {
|
|
return;
|
|
}
|
|
|
|
if (this->m_description.Format == D3DFMT_A1R5G5B5 || this->m_description.Format == D3DFMT_A8R8G8B8) {
|
|
const int bytes = this->m_description.Format == D3DFMT_A1R5G5B5 ? 2 : 4;
|
|
|
|
for (int row = 0; row < height; ++row) {
|
|
std::memset(static_cast<unsigned char*>(locked.pBits) + row * locked.Pitch, 0, width * bytes);
|
|
}
|
|
}
|
|
|
|
const SpriteRleImage& image = *rawImage;
|
|
const std::uint8_t* input = image.runs;
|
|
int remaining = 0;
|
|
|
|
for (int row = 0; row < image.height; ++row) {
|
|
auto* output = static_cast<unsigned char*>(locked.pBits) + row * locked.Pitch;
|
|
|
|
for (int column = 0; column < image.width; ++column) {
|
|
if (remaining) {
|
|
--remaining;
|
|
const std::uint16_t color = 0;
|
|
std::memcpy(output + column * sizeof(color), &color, sizeof(color));
|
|
} else {
|
|
const unsigned value = *input++;
|
|
if (value) {
|
|
const std::uint16_t color = palette[value];
|
|
std::memcpy(output + column * sizeof(color), &color, sizeof(color));
|
|
} else {
|
|
const std::uint16_t color = 0;
|
|
std::memcpy(output + column * sizeof(color), &color, sizeof(color));
|
|
remaining = *input++ - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(this->m_surface->UnlockRect())) {
|
|
this->Filter();
|
|
}
|
|
}
|
|
|
|
// OFFSET: 0x005729E0
|
|
// FUNCTION: fn_02479
|
|
bool CTextureDX9::CopyTexture(CTexture* source, int sourceX, int sourceY, int sourceWidth, int sourceHeight, int x, int y, int width, int height) {
|
|
RECT src{sourceX, sourceY, sourceX + sourceWidth, sourceY + sourceHeight};
|
|
RECT dst{x, y, x + width, y + height};
|
|
if (FAILED(
|
|
this->m_context->d3dx->loadSurfaceFromSurface(this->m_surface, nullptr, &dst, static_cast<CTextureDX9*>(source)->m_surface, nullptr, &src, 3, 0)
|
|
)) {
|
|
return false;
|
|
}
|
|
|
|
this->Filter();
|
|
return true;
|
|
}
|
|
|
|
// OFFSET: 0x00544410
|
|
void CTextureDX9::Draw(int x, int y, int width, int height, std::uint32_t color) {
|
|
this->DrawTo(
|
|
*this->m_context->primitives, *this->m_context->queues, x, y, width, height, color, this->m_context->depthAlpha, this->m_context->depthStart,
|
|
this->m_context->depthEnd
|
|
);
|
|
}
|
|
|
|
// OFFSET: 0x00544610
|
|
void CTextureDX9::Stretch(int x, int y, int width, int height) {
|
|
this->StretchTo(
|
|
*this->m_context->primitives, *this->m_context->queues, x, y, width, height, this->m_context->depthAlpha, this->m_context->depthStart,
|
|
this->m_context->depthEnd
|
|
);
|
|
}
|