#include "Render/RenderSystemBase.h" #include "Render/Texture.h" #include "Render/TextureCache.h" #include "Resource/BitmapResource.h" #include "Resource/ResourceMgr.h" #include #include // Original key comparison is unsigned-byte, case-sensitive string order. bool TextureNameLess::operator()(const char* left, const char* right) const { return std::strcmp(left, right) < 0; } // OFFSET: 0x004074F5 // FUNCTION: fn_00152 TextureCache::TextureCache( ResourceMgr& resources, CRenderSystemBase*& system, TextureSources& sources, const int& reduction, const unsigned& reuseDelay ) : m_resources(resources), m_system(system), m_sources(sources), m_reduction(reduction), m_reuseDelay(reuseDelay) { } // OFFSET: 0x005713C0 // FUNCTION: fn_02447 TextureCache::~TextureCache() { this->Clear(); } // OFFSET: 0x00571790 // FUNCTION: fn_02453 CTexture* TextureCache::Default() { return this->m_system ? this->m_system->BackBufferTexture() : nullptr; } // OFFSET: 0x005715E0 // FUNCTION: fn_02449 CTexture* TextureCache::Upload( int width, int height, const std::uint32_t* pixels, int format, int option, bool fullSize, bool dynamic, bool renderTarget ) { const int outputWidth = fullSize ? width : width / this->m_reduction; const int outputHeight = fullSize ? height : height / this->m_reduction; CTexture* texture = this->m_system ? this->m_system->CreateTexture(outputWidth, outputHeight, format, dynamic, renderTarget) : nullptr; if (texture) { texture->Upload(0, 0, width, height, pixels, option, 0); texture->unknown_0008 = (texture->unknown_0008 & 0xFF00u) | fullSize; } return texture; } // OFFSET: 0x00571930 // FUNCTION: fn_02455 CTexture* TextureCache::Create(const char* name, int option, bool fullSize, bool dynamic, bool renderTarget) { if (std::strstr(name, ".bik")) { int width; int height; if (this->m_sources.MovieSize(name, width, height)) { CTexture* texture = this->m_system ? this->m_system->CreateTexture(width, height, 4, false, false) : nullptr; if (texture) { return texture; } } } else { auto* bitmap = static_cast(this->m_resources.Load(this->m_sources.Resolve(name))); if (bitmap) { const auto& image = bitmap->image; CTexture* texture = this->Upload( image.width, image.height, image.pixels, image.alpha != 0 ? 1 : 0, option, fullSize, dynamic, renderTarget ); this->m_resources.Remove(bitmap); return texture; } } // Original explicitly creates a transparent 1x1 substitute on a missing // bitmap/movie, rather than returning the renderer's shared default here. const int extent = 1 / this->m_reduction; CTexture* texture = this->m_system ? this->m_system->CreateTexture(extent, extent, 0, false, false) : nullptr; if (texture) { const std::uint32_t transparent = 0; texture->Upload(0, 0, 1, 1, &transparent, option, 0); texture->unknown_0008 &= 0xFF00u; } return texture; } // OFFSET: 0x005717A0 // FUNCTION: fn_02454 CTexture* TextureCache::Load(const char* name, int option, bool fullSize, bool dynamic, bool renderTarget) { const auto found = this->m_textures.find(name); if (found != this->m_textures.end() && found->second) { found->second->unknown_0020 = timeGetTime(); return found->second; } CTexture* texture = this->Create(name, option, fullSize, dynamic, renderTarget); if (!texture) { return this->Default(); } strncpy_s(texture->m_name, sizeof(texture->m_name), name, _TRUNCATE); texture->unknown_0020 = timeGetTime(); this->m_textures.emplace(texture->m_name, texture); return texture; } // OFFSET: 0x005716F0 // FUNCTION: fn_02452 void TextureCache::Remove(CTexture* texture) { if (texture->unknown_001C == 0 && texture != this->Default()) { const auto found = this->m_textures.find(texture->m_name); if (found != this->m_textures.end()) { delete found->second; this->m_textures.erase(found); } } } // OFFSET: 0x00571670 // FUNCTION: fn_02451 void TextureCache::Clear() { for (auto& [name, texture] : this->m_textures) { delete texture; } this->m_textures.clear(); } // OFFSET: 0x00571C50 // FUNCTION: fn_02457 void TextureCache::ClearUnused() { const unsigned threshold = timeGetTime() - this->m_reuseDelay; for (auto entry = this->m_textures.begin(); entry != this->m_textures.end();) { CTexture* texture = entry->second; if (texture->unknown_001C != 0 || texture->unknown_0020 >= threshold) { ++entry; } else { delete texture; entry = this->m_textures.erase(entry); } } HeapCompact(GetProcessHeap(), 0); this->m_sources.CleanupTextures(); }