#include "gx/Texture.hpp" #include "gx/Device.hpp" #include "gx/Gx.hpp" #include "gx/texture/CBLPFile.hpp" #include "util/Filesystem.hpp" #include "util/SFile.hpp" #include #include #include #include #include #include namespace Texture { int32_t s_createBlpAsync; // Invented name MipBits* s_mipBits; int32_t s_mipBitsValid; TSHashTable s_textureCache; EGxTexFormat s_pixelFormatToGxTexFormat[10] = { GxTex_Dxt1, // PIXEL_DXT1 GxTex_Dxt3, // PIXEL_DXT3 GxTex_Argb8888, // PIXEL_ARGB8888 GxTex_Argb1555, // PIXEL_ARGB1555 GxTex_Argb4444, // PIXEL_ARGB4444 GxTex_Rgb565, // PIXEL_RGB565 GxTex_Unknown, // PIXEL_A8 GxTex_Dxt5, // PIXEL_DXT5 GxTex_Unknown, // PIXEL_UNSPECIFIED GxTex_Unknown // PIXEL_ARGB2565 }; } static CImVector CRAPPY_GREEN = { 0x00, 0xFF, 0x00, 0xFF }; void AsyncTextureWait(CTexture* texture) { // TODO } uint32_t CalcLevelCount(uint32_t width, uint32_t height) { uint32_t v2 = width; uint32_t v3 = height; uint32_t v4 = 1; uint32_t v5; uint32_t v6; if (width == 6 * height) { v2 = width / 6; } while (v2 > 1 || v3 > 1) { v5 = v2 >> 1; ++v4; v6 = v2 >> 1 < 1; v2 = 1; if (!v6) { v2 = v5; } if ( v3 >> 1 >= 1 ) { v3 >>= 1; } else { v3 = 1; } } return v4; } uint32_t CalcLevelOffset(uint32_t level, uint32_t width, uint32_t height, uint32_t fourCC) { uint32_t offset = 0; for (int32_t i = 0; i < level; i++) { offset += CalcLevelSize(i, width, height, fourCC); } return offset; } uint32_t CalcLevelSize(uint32_t level, uint32_t width, uint32_t height, uint32_t fourCC) { uint32_t v4 = std::max(width >> level, 1u); uint32_t v5 = std::max(height >> level, 1u); if (fourCC == 0 || fourCC == 1 || fourCC == 7) { if (v4 == 6 * v5) { if (v5 <= 4) { v5 = 4; } v4 = 6 * v5; } else { if (v4 <= 4) { v4 = 4; } if (v5 <= 4) { v5 = 4; } } } uint32_t size; if (fourCC == 9) { uint32_t v6 = v5 * v4; uint32_t v7 = v5 * v4 >> 2; if (v7 < 1) { v7 = 1; } size = v7 + 2 * v6; } else { uint32_t v9 = GetBitDepth(fourCC); size = (v4 * v5 * v9) >> 3; } return size; } void FillInSolidTexture(const CImVector& color, CTexture* texture) { // Treat the value of color as a nonsense pointer to ensure the value remains available // when GxuUpdateSingleColorTexture is called void* userArg = reinterpret_cast(color.value); CGxTexFlags gxTexFlags = CGxTexFlags(GxTex_Linear, 0, 0, 0, 0, 0, 1); texture->gxTex = TextureAllocGxTex( GxTex_2d, 8, 8, 0, GxTex_Argb8888, gxTexFlags, userArg, GxuUpdateSingleColorTexture, GxTex_Argb8888 ); if (color.a < 0xFE) { texture->flags &= ~0x1; } else { texture->flags |= 0x1; } texture->dataFormat = GxTex_Argb8888; texture->gxTexFormat = GxTex_Argb8888; texture->gxTexTarget = GxTex_2d; texture->gxWidth = 8; texture->gxHeight = 8; texture->gxTexFlags = CGxTexFlags(GxTex_Linear, 0, 0, 0, 0, 0, 1); SStrCopy(texture->filename, "SolidTexture", STORM_MAX_PATH); } uint32_t GetBitDepth(uint32_t fourCC) { switch (fourCC) { case 0: return 4; case 1: case 6: case 7: return 8; case 2: return 32; case 3: case 4: case 5: return 16; default: return 0; } } uint32_t GxCalcTexelStrideInBytes(EGxTexFormat format, uint32_t width) { static uint16_t word9F103C[] = { 0, // GxTex_Unknown 32, // GxTex_Abgr8888 32, // GxTex_Argb8888 16, // GxTex_Argb4444 16, // GxTex_Argb1555 16, // GxTex_Rgb565 4, // GxTex_Dxt1 8, // GxTex_Dxt3 8, // GxTex_Dxt5 16, // GxTex_Uv88 32, // GxTex_Gr1616F 32, // GxTex_R32F 32, // GxTex_D24X8 0 // GxTexFormats_Last }; static uint16_t word9F1058[] = { 0, // GxTex_Unknown 0, // GxTex_Abgr8888 0, // GxTex_Argb8888 0, // GxTex_Argb4444 0, // GxTex_Argb1555 0, // GxTex_Rgb565 8, // GxTex_Dxt1 16, // GxTex_Dxt3 16, // GxTex_Dxt5 0, // GxTex_Uv88 0, // GxTex_Gr1616F 0, // GxTex_R32F 0, // GxTex_D24X8 0 // GxTexFormats_Last }; if (format == GxTex_Dxt1 || format == GxTex_Dxt3 || format == GxTex_Dxt5) { uint32_t v11 = (width >> 2) * word9F1058[format]; return std::max(v11, static_cast(word9F1058[format])); } else { return width * word9F103C[format] >> 3; } } int32_t GxTexCreate(const CGxTexParms& parms, CGxTex*& texId) { return GxTexCreate( parms.target, parms.width, parms.height, parms.depth, parms.format, parms.dataFormat, parms.flags, parms.userArg, parms.userFunc, "", texId ); } int32_t GxTexCreate(EGxTexTarget target, uint32_t width, uint32_t height, uint32_t depth, EGxTexFormat format, EGxTexFormat dataFormat, CGxTexFlags flags, void* userArg, TEXTURE_CALLBACK* userFunc, const char* name, CGxTex*& texId) { texId = nullptr; STORM_ASSERT(target <= GxTexTargets_Last); STORM_ASSERT(GxCaps().m_texTarget[target] == 1); STORM_ASSERT(width >= 8); STORM_ASSERT(height >= 8); STORM_ASSERT(width <= GxCaps().m_texMaxSize[target]); STORM_ASSERT(height <= GxCaps().m_texMaxSize[target]); STORM_ASSERT((target != GxTex_Rectangle && target != GxTex_NonPow2) ? (width & (width - 1)) == 0 : 1); STORM_ASSERT((target != GxTex_Rectangle && target != GxTex_NonPow2) ? (height & (height - 1)) == 0 : 1); STORM_ASSERT((target == GxTex_Rectangle) ? flags.m_filter <= GxTex_Linear : 1); STORM_ASSERT(format <= GxTexFormats_Last); STORM_ASSERT((flags.m_generateMipMaps) ? (GxCaps().m_generateMipMaps && !(format >= GxTex_Dxt1 && format <= GxTex_Dxt5)) : 1); STORM_ASSERT((flags.m_filter == GxTex_Anisotropic) ? GxCaps().m_texFilterAnisotropic : 1); STORM_ASSERT(dataFormat <= GxTexFormats_Last); STORM_ASSERT(userFunc != nullptr); return g_theGxDevicePtr->TexCreate( target, width, height, depth, format, dataFormat, flags, userArg, userFunc, name, texId ); } void GxTexDestroy(CGxTex* texId) { g_theGxDevicePtr->TexDestroy(texId); } void GxTexParameters(const CGxTex* texId, CGxTexParms& parms) { // TODO } bool GxTexReusable(CGxTexParms& parms) { // TODO return false; } void GxTexSetWrap(CGxTex* texId, EGxTexWrapMode wrapU, EGxTexWrapMode wrapV) { g_theGxDevicePtr->TexSetWrap(texId, wrapU, wrapV); } int32_t ReloadMips(const char* filename, uint32_t a2, MipBits*& mipBits) { // TODO return 0; } void TextureFreeGxTex(CGxTex* texId) { STORM_ASSERT(texId); CGxTexParms gxTexParms; GxTexParameters(texId, gxTexParms); if (GxTexReusable(gxTexParms)) { // TODO return; } GxTexDestroy(texId); } CGxTex* TextureAllocGxTex(EGxTexTarget target, uint32_t width, uint32_t height, uint32_t depth, EGxTexFormat format, CGxTexFlags flags, void* userArg, TEXTURE_CALLBACK* userFunc, EGxTexFormat dataFormat) { CGxTexParms gxTexParms; gxTexParms.height = height; gxTexParms.depth = depth; gxTexParms.target = target; gxTexParms.dataFormat = dataFormat; gxTexParms.userArg = userArg; gxTexParms.format = format; gxTexParms.width = width; gxTexParms.userFunc = userFunc; gxTexParms.flags = flags; gxTexParms.flags.m_generateMipMaps = 0; CGxTexParms gxTexParms2; if (!GxTexReusable(gxTexParms) || width > 512 || height > 512) { CGxTex* texture = nullptr; memcpy(&gxTexParms2, &gxTexParms, sizeof(gxTexParms2)); GxTexCreate(gxTexParms2, texture); return texture; } uint32_t v9 = width >> 5; uint32_t v10 = height >> 5; for (int32_t i = 0; !(v9 & 1); i++) { v9 >>= 1; } for (int32_t j = 0; !(v10 & 1); j++) { v10 >>= 1; } // TODO return nullptr; } void GxTexUpdate(CGxTex* texId, int32_t minX, int32_t minY, int32_t maxX, int32_t maxY, int32_t immediate) { CiRect rect = { minY, minX, maxY, maxX }; GxTexUpdate(texId, rect, immediate); } void GxTexUpdate(CGxTex* texId, CiRect& updateRect, int32_t immediate) { g_theGxDevicePtr->TexMarkForUpdate(texId, updateRect, immediate); } void GxuUpdateSingleColorTexture(EGxTexCommand cmd, uint32_t w, uint32_t h, uint32_t d, uint32_t mipLevel, void* userArg, uint32_t& texelStrideInBytes, const void*& texels) { static uint8_t image[256] = { 0 }; switch (cmd) { case GxTex_Lock: { // Treat the userArg pointer as the literal color to use while filling the texture uint32_t color = reinterpret_cast(userArg); for (int32_t i = 0; i < sizeof(image) / sizeof(color); i++) { reinterpret_cast(image)[i] = color; } return; } case GxTex_Latch: { texelStrideInBytes = 4 * w; texels = image; return; } default: return; } } void GetDefaultTexture(uint32_t height, uint32_t width) { // TODO } void GetTextureFormats(PIXEL_FORMAT* pixFormat, EGxTexFormat* gxTexFormat, PIXEL_FORMAT preferredFormat, int32_t alphaBits) { switch (preferredFormat) { case PIXEL_DXT1: if (GxCaps().m_texFmt[GxTex_Dxt1]) { *gxTexFormat = GxTex_Dxt1; *pixFormat = PIXEL_DXT1; } else if (alphaBits) { *gxTexFormat = GxTex_Argb1555; *pixFormat = PIXEL_ARGB1555;; } else { *gxTexFormat = GxTex_Rgb565; *pixFormat = PIXEL_RGB565; } break; case PIXEL_DXT3: if (GxCaps().m_texFmt[GxTex_Dxt3]) { *gxTexFormat = GxTex_Dxt3; *pixFormat = PIXEL_DXT3; } else { *gxTexFormat = GxTex_Argb4444; *pixFormat = PIXEL_ARGB4444; } break; case PIXEL_ARGB8888: *gxTexFormat = GxTex_Argb8888; *pixFormat = PIXEL_ARGB8888; break; case PIXEL_ARGB1555: *gxTexFormat = GxTex_Argb1555; *pixFormat = PIXEL_ARGB8888; break; case PIXEL_ARGB4444: *gxTexFormat = GxTex_Argb4444; *pixFormat = PIXEL_ARGB8888; break; case PIXEL_RGB565: *gxTexFormat = GxTex_Rgb565; *pixFormat = PIXEL_ARGB8888; break; case PIXEL_DXT5: if (GxCaps().m_texFmt[GxTex_Dxt5]) { *gxTexFormat = GxTex_Dxt5; *pixFormat = PIXEL_DXT5; } else { *gxTexFormat = GxTex_Argb4444; *pixFormat = PIXEL_ARGB4444; } break; case PIXEL_UNSPECIFIED: if (alphaBits > 0) { if (alphaBits == 1) { *gxTexFormat = GxTex_Argb1555; *pixFormat = PIXEL_ARGB8888; } else if (alphaBits == 4) { *gxTexFormat = GxTex_Argb4444; *pixFormat = PIXEL_ARGB8888; } else { *gxTexFormat = GxTex_Argb8888; *pixFormat = PIXEL_ARGB8888; } } else { *gxTexFormat = GxTex_Rgb565; *pixFormat = PIXEL_ARGB8888; } break; default: break; } } MipBits* MippedImgAllocA(uint32_t fourCC, uint32_t width, uint32_t height, const char* fileName, int32_t lineNumber) { uint32_t levelCount = CalcLevelCount(width, height); uint32_t levelDataSize = CalcLevelOffset(levelCount, width, height, fourCC); MipBits* images = reinterpret_cast(SMemAlloc(levelDataSize + sizeof(void*) * levelCount + 16, fileName, lineNumber, 0)); uintptr_t v10 = (reinterpret_cast(images) + sizeof(void*) * levelCount + 15) & static_cast(-sizeof(void*)); uintptr_t offset = v10 - reinterpret_cast(images); MipBits** ptr = reinterpret_cast(images); for (int32_t level = 0; level < levelCount; level++) { ptr[level] = images + offset; offset += CalcLevelSize(level, width, height, fourCC); } return images; } uint32_t MippedImgCalcSize(uint32_t fourCC, uint32_t width, uint32_t height) { uint32_t levelCount = CalcLevelCount(width, height); uint32_t levelDataSize = CalcLevelOffset(levelCount, width, height, fourCC); uint32_t imgSize = levelDataSize + (sizeof(void*) * levelCount); return imgSize; } // TODO // - order: width, height or height, width? void RequestImageDimensions(uint32_t* width, uint32_t* height, uint32_t* bestMip) { CGxCaps systemCaps; memcpy(&systemCaps, &GxCaps(), sizeof(systemCaps)); auto maxTextureSize = systemCaps.m_texMaxSize[GxTex_2d]; if (maxTextureSize) { while (*height > maxTextureSize || *width > maxTextureSize) { *height >>= 1; *width >>= 1; ++*bestMip; if (!*height) { *height = 1; } if (!*width) { *width = 1; } } } else { // TODO // SErrSetLastError(0x57u); } } void UpdateBlpTextureAsync(EGxTexCommand cmd, uint32_t w, uint32_t h, uint32_t d, uint32_t mipLevel, void* userArg, uint32_t& texelStrideInBytes, const void*& texels) { CTexture* texture = static_cast(userArg); switch (cmd) { case GxTex_Lock: if (Texture::s_mipBitsValid) { return; } if (!ReloadMips(texture->filename, texture->flags & 0x2, Texture::s_mipBits)) { GetDefaultTexture(h, w); // TODO // GetGlobalStatusObj()->Add( // STATUS_ERROR, // "Texture %s not loaded -- replaced with default.\n", // texture->filename // ); } return; case GxTex_Latch: texelStrideInBytes = GxCalcTexelStrideInBytes(texture->dataFormat, w); if (texture->gxTexTarget == GxTex_CubeMap) { // TODO } else { texels = reinterpret_cast(Texture::s_mipBits)[mipLevel]; } return; default: return; } } int32_t PumpBlpTextureAsync(CTexture* texture, void* buf) { CBLPFile image; if (!image.Source(buf)) { texture->loadStatus.Add( STATUS_FATAL, "BLP Texture failure: \"%s\" invalid file version\n", texture->filename ); image.Close(); return 0; } if (texture->flags & 0x4 && !(image.m_header.hasMips & 0x10)) { texture->flags &= 0xFFFB; } texture->alphaBits = image.m_header.alphaSize; if (image.m_header.alphaSize == 0) { texture->flags |= 0x1; } uint32_t width = image.m_header.width; uint32_t height = image.m_header.height; uint32_t bestMip = 0; RequestImageDimensions(&width, &height, &bestMip); texture->bestMip = bestMip; PIXEL_FORMAT pixFormat; EGxTexFormat gxTexFormat; PIXEL_FORMAT preferredFormat = static_cast(image.m_header.preferredFormat); int32_t alphaSize = image.m_header.alphaSize; GetTextureFormats(&pixFormat, &gxTexFormat, preferredFormat, alphaSize); int32_t mipLevel = texture->bestMip; Texture::s_mipBitsValid = 1; if (!image.LockChain2(texture->filename, pixFormat, Texture::s_mipBits, mipLevel, 1)) { Texture::s_mipBitsValid = 0; texture->loadStatus.Add( STATUS_FATAL, "BLP Texture failure: \"%s\" decompression failed.\n", texture->filename ); image.Close(); return 0; } uint32_t gxHeight = height; uint32_t gxWidth = width; EGxTexTarget gxTexTarget = GxTex_2d; // Check if texture dimensions indicate cube mapping if (width == 6 * height) { gxHeight = height; gxWidth = width / 6u; gxTexTarget = GxTex_CubeMap; } texture->gxHeight = gxHeight; texture->gxWidth = gxWidth; texture->gxTexTarget = gxTexTarget; EGxTexFormat dataFormat = Texture::s_pixelFormatToGxTexFormat[pixFormat]; texture->dataFormat = dataFormat; texture->gxTexFormat = gxTexFormat; if (gxWidth < 256 && image.m_numLevels == 1) { if (!texture->gxTexFlags.m_generateMipMaps) { texture->gxTexFlags.m_filter = 0; } } if (texture->flags & 0x4) { // TODO // CTextureAtlas* atlas = CTextureAtlas::Get(v2); // texture->atlas = atlas; // if (atlas) { // sub_4B50D0(atlas, v2); // } else { // texture->flags &= 0xFFFBu; // } } if (!texture->atlas) { if (texture->gxTex) { TextureFreeGxTex(texture->gxTex); texture->gxTex = nullptr; } CGxTex* gxTex = TextureAllocGxTex( texture->gxTexTarget, texture->gxWidth, texture->gxHeight, 0, texture->gxTexFormat, texture->gxTexFlags, texture, &UpdateBlpTextureAsync, texture->dataFormat ); texture->gxTex = gxTex; if (!gxTex) { Texture::s_mipBitsValid = 0; texture->loadStatus.Add( STATUS_FATAL, "BLP Texture failure: \"%s\" allocating %dx%d texture failed.\n", texture->filename, gxWidth, gxHeight ); image.Close(); return 0; } GxTexUpdate(gxTex, 0, 0, gxWidth, gxHeight, 1); } Texture::s_mipBitsValid = 0; image.Close(); return 1; } int32_t FindSubstitution(const char* a1, char* a2) { // TODO return 0; } CTexture* CreateBlpAsync(char* fileExt, char* fileName, int32_t createFlags, CGxTexFlags texFlags) { // TODO return nullptr; } CTexture* CreateBlpSync(int32_t createFlags, char* fileName, char* fileExt, CGxTexFlags texFlags) { SFile* file = nullptr; // TODO // SErrSetLastError(0); if (!SFile::OpenEx(nullptr, fileName, (createFlags >> 1) & 1, &file)) { // TODO // if (!sub_7717E0()) { // SErrSetLastError(2u); // } return nullptr; } if (!file) { return nullptr; } auto m = SMemAlloc(sizeof(CTexture), "HTEXTURE", -2, 0x0); auto texture = new (m) CTexture(); texture->gxTexFlags = texFlags; if (createFlags & 0x2) { texture->flags |= 0x2; } // TODO // if (createFlags & 0x4 && dword_B49C84) { // texture->flags |= 0x4; // } if (fileExt) { *fileExt = 0; } SStrCopy(texture->filename, fileName, 0x7FFFFFFF); size_t fileSize = SFile::GetFileSize(file, 0); void* buf = SMemAlloc(fileSize, __FILE__, __LINE__, 0); if (!SFile::Read(file, buf, fileSize, nullptr, nullptr, nullptr)) { // nullsub_3(); } if (!PumpBlpTextureAsync(texture, buf)) { FillInSolidTexture(CRAPPY_GREEN, texture); } SFile::Close(file); SMemFree(buf, __FILE__, __LINE__, 0); return texture; } HTEXTURE CreateBlpTexture(char* fileExt, char* fileName, int32_t createFlags, CGxTexFlags texFlags) { if (fileExt) { SStrCopy(fileExt, ".blp", 0x7FFFFFFF); } char* fileExtFinal = fileExt; char* fileNameFinal = fileName; char fileNameSub[260]; if (FindSubstitution(fileNameSub, fileName)) { fileNameFinal = fileNameSub; fileExtFinal = OsPathFindExtensionWithDot(fileNameSub); } CTexture* texture; if (Texture::s_createBlpAsync) { texture = CreateBlpAsync(fileExtFinal, fileNameFinal, createFlags, texFlags); } else { texture = CreateBlpSync(createFlags, fileNameFinal, fileExtFinal, texFlags); } HTEXTURE handle = texture ? HandleCreate(texture) : nullptr; return handle; } HTEXTURE CreateTgaTexture(const char* fileName, const char* fileExt, int32_t a3, CGxTexFlags texFlags, CStatus* status) { // TODO return nullptr; } HTEXTURE TextureCacheGetTexture(char* fileName, char* fileExt, CGxTexFlags texFlags) { if (fileExt) { *fileExt = '\0'; } auto hashval = SStrHashHT(fileName); HASHKEY_TEXTUREFILE key = { fileName, texFlags }; auto texture = Texture::s_textureCache.Ptr(hashval, key); if (fileExt) { *fileExt = '.'; } if (texture) { return HandleCreate(texture); } return nullptr; } HTEXTURE TextureCacheGetTexture(const CImVector& color) { // TODO return nullptr; } void TextureCacheNewTexture(CTexture* texture, CGxTexFlags texFlags) { auto hashval = SStrHashHT(texture->filename); HASHKEY_TEXTUREFILE key = { texture->filename, texFlags }; Texture::s_textureCache.Insert(texture, hashval, key); } void TextureCacheNewTexture(CTexture* texture, const CImVector& color) { // TODO } HTEXTURE TextureCreate(const char* fileName, CGxTexFlags texFlags, CStatus* status, int32_t createFlags) { STORM_ASSERT(fileName); STORM_ASSERT(*fileName); STORM_ASSERT(status); if (createFlags & 0x1) { texFlags.m_filter = CTexture::s_filterMode; } if (texFlags.m_filter == 5) { texFlags.m_maxAnisotropy = CTexture::s_maxAnisotropy; } else { texFlags.m_maxAnisotropy = 1; } int32_t v16 = 2; int32_t v8 = 1; char tmpFileName[260]; SStrCopy(tmpFileName, fileName, 260); char* fileExt = OsPathFindExtensionWithDot(tmpFileName); char* v10 = fileExt; if (createFlags & 0x8) { if (fileExt) { v16 = 1; v8 = SStrCmpI(fileExt, ".blp", 0x7FFFFFFFu) == 0; v10 = 0; } } HTEXTURE texture = TextureCacheGetTexture(tmpFileName, v10, texFlags); if (texture) { return texture; } for (int32_t i = 0; i < v16; ++i) { if (v8) { if (v8 != 1) { v8 = (v8 + 1) % 2; continue; } texture = CreateBlpTexture(v10, tmpFileName, createFlags, texFlags); } else { texture = CreateTgaTexture(tmpFileName, v10, createFlags & 0x2, texFlags, status); } if (texture) { TextureCacheNewTexture(TextureGetTexturePtr(texture), texFlags); return texture; } v8 = (v8 + 1) % 2; } // TODO // FileError(status, "texture", fileName); return TextureCreateSolid(CRAPPY_GREEN); return nullptr; } HTEXTURE TextureCreate(uint32_t width, uint32_t height, EGxTexFormat format, EGxTexFormat dataFormat, CGxTexFlags texFlags, void* userArg, TEXTURE_CALLBACK* userFunc, const char* a8, int32_t a9) { return TextureCreate( GxTex_2d, width, height, 0, format, dataFormat, texFlags, userArg, userFunc, a8, a9 ); } HTEXTURE TextureCreate(EGxTexTarget target, uint32_t width, uint32_t height, uint32_t depth, EGxTexFormat format, EGxTexFormat dataFormat, CGxTexFlags texFlags, void* userArg, TEXTURE_CALLBACK* userFunc, const char* a10, int32_t a11) { auto m = SMemAlloc(sizeof(CTexture), __FILE__, __LINE__, 0x0); auto texture = new (m) CTexture(); if (a11) { texFlags.m_filter = CTexture::s_filterMode; } texFlags.m_maxAnisotropy = texFlags.m_filter == GxTex_Anisotropic ? CTexture::s_maxAnisotropy : 1; texture->gxTex = TextureAllocGxTex(target, width, height, depth, format, texFlags, userArg, userFunc, dataFormat); texture->dataFormat = dataFormat; texture->gxWidth = width; texture->gxHeight = height; texture->gxTexFormat = format; texture->gxTexTarget = target; texture->gxTexFlags = texFlags; texture->asyncObject = nullptr; const char* filename = a10 ? a10 : "UniqueTexture"; SStrCopy(texture->filename, filename, STORM_MAX_PATH); return HandleCreate(texture); } HTEXTURE TextureCreateSolid(const CImVector& color) { HTEXTURE textureHandle = TextureCacheGetTexture(color); if (textureHandle) { return textureHandle; } auto m = SMemAlloc(sizeof(CTexture), __FILE__, __LINE__, 0x0); auto texture = new (m) CTexture(); FillInSolidTexture(color, texture); textureHandle = HandleCreate(texture); TextureCacheNewTexture(texture, color); return textureHandle; } int32_t TextureGetDimensions(CTexture* texture, uint32_t* width, uint32_t* height, int32_t force) { if (texture->asyncObject) { if (!force) { return 0; } // TODO } if (width) { *width = texture->gxWidth; } if (height) { *height = texture->gxHeight; } return 1; } int32_t TextureGetDimensions(HTEXTURE textureHandle, uint32_t* width, uint32_t* height, int32_t force) { return TextureGetDimensions(TextureGetTexturePtr(textureHandle), width, height, force); } CGxTex* TextureGetGxTex(CTexture* texture, int32_t a2, CStatus* status) { STORM_ASSERT(texture); if (texture->flags & 0x4) { if (texture->asyncObject) { if (a2 != 1 && (a2 != 2 || texture->asyncObject->char24)) { TextureIncreasePriority(texture); return nullptr; } AsyncTextureWait(texture); if (status) { status->Add(texture->loadStatus); } } if (texture->atlas) { // TODO // atlas->Reload(); // TODO // - pull texture out of atlas return nullptr; } } if (texture->asyncObject) { TextureIncreasePriority(texture); } if (!texture->gxTex) { if (a2 != 1 && (a2 != 2 || texture->asyncObject->char24)) { return nullptr; } AsyncTextureWait(texture); if (status) { status->Add(texture->loadStatus); } } return texture->gxTex; } CGxTex* TextureGetGxTex(HTEXTURE handle, int32_t a2, CStatus* status) { return TextureGetGxTex(reinterpret_cast(handle), a2, status); } CTexture* TextureGetTexturePtr(HTEXTURE handle) { return reinterpret_cast(handle); } void TextureIncreasePriority(CTexture* texture) { // TODO } void TextureInitialize() { uint32_t v0 = MippedImgCalcSize(2, 1024, 1024); Texture::s_mipBits = reinterpret_cast(SMemAlloc(v0, __FILE__, __LINE__, 0)); // TODO // - rest of function } int32_t TextureIsSame(HTEXTURE textureHandle, const char* fileName) { char buf[STORM_MAX_PATH]; uint32_t len = SStrCopy(buf, fileName, sizeof(buf)); if (len >= 4 && buf[len - 4] == '.') { len -= 4; } auto v3 = &buf[len]; if (*v3 == '.') { SStrLower(v3 + 1); *v3 = '\0'; } STORM_ASSERT(textureHandle); return SStrCmpI(buf, TextureGetTexturePtr(textureHandle)->filename, sizeof(buf)) == 0; }