rodecomp/Source/Resource/ResourceMgr.cpp
ragnarok-clanker e168b4e4a6 Restore translation-unit ownership and publish verified source corrections
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.
2026-10-25 15:42:21 +00:00

254 lines
6.3 KiB
C++

#include "File/ResourcePath.h"
#include "Resource/ResourceFailures.h"
#include "Resource/ResourceMgr.h"
#include "Utility/Threading/NSyncObject.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <windows.h>
#include <timeapi.h>
#include <optional>
// OFFSET: 0x00AECEE0
bool ResourceNameLess::operator()(const ResourceName* left, const ResourceName* right) const {
if (left->hash != right->hash) {
return left->hash < right->hash;
}
return std::strcmp(left->text, right->text) < 0;
}
// INLINED
ResourceMgr::ResourceMgr(ResourceFailures& failures) : m_failures(&failures) {
InitializeCriticalSection(&this->m_lock);
}
// OFFSET: 0x00AEE070
// FUNCTION: fn_18169
ResourceMgr::~ResourceMgr() {
this->m_transient.clear();
for (auto& resources : this->m_resources) {
for (auto& [name, resource] : resources) {
resource->Unload();
delete resource;
}
resources.clear();
}
DeleteCriticalSection(&this->m_lock);
}
// OFFSET: 0x00AF04B0
// FUNCTION: fn_18188
void ResourceMgr::Register(const char* extension, const char* prefix, CRes* prototype) {
const int type = static_cast<int>(this->m_extensions.size());
this->m_extensions.emplace(extension, type);
this->m_prototypes.push_back(prototype);
this->m_prefixes.push_back(prefix);
this->m_resources.resize(static_cast<std::size_t>(type) + 1);
}
// OFFSET: 0x00AEE5E0
// FUNCTION: fn_18173
int ResourceMgr::FindType(const char* extension) const {
if (!extension) {
return -1;
}
const auto entry = this->m_extensions.find(extension);
if (entry != this->m_extensions.end()) {
return entry->second;
}
char text[256];
std::sprintf(text, "ResourceError : %s is Undefined Ext \n", extension);
return -1;
}
// OFFSET: 0x00AEE6B0
// FUNCTION: fn_18174
CRes* ResourceMgr::Find(int type, const char* name) const {
ResourceName key;
key.Assign(name);
const auto& resources = this->m_resources[type];
const auto entry = resources.find(&key);
return entry == resources.end() ? nullptr : entry->second;
}
// OFFSET: 0x00AEF7D0
// FUNCTION: fn_18181
bool ResourceMgr::Exists(const char* name) {
if (!name) {
return false;
}
char normalized[260] = {};
strncpy_s(normalized, sizeof(normalized), name, _TRUNCATE);
for (unsigned char* byte = reinterpret_cast<unsigned char*>(normalized); *byte; ++byte) {
if (*byte >= 'A' && *byte <= 'Z') {
*byte += 'a' - 'A';
}
}
const char* dot = std::strrchr(normalized, '.');
const int type = this->FindType(dot ? dot + 1 : nullptr);
if (type < 0) {
return false;
}
const std::string resolved = AddResourcePrefix(this->aliases.Resolve(normalized), this->m_prefixes[type]);
if (CRes* existing = this->Find(type, resolved.c_str())) {
existing->lastUse = timeGetTime();
return true;
}
return ResourceExists(resolved.c_str(), 0);
}
// OFFSET: 0x00AEE770
// FUNCTION: fn_18175
CRes* ResourceMgr::Load(const char* name) {
if (!name) {
return nullptr;
}
std::optional<NSyncObject> lock(std::in_place, &this->m_lock);
char normalized[260] = {};
_snprintf_s(normalized, sizeof(normalized), _TRUNCATE, "%s", name);
for (unsigned char* byte = reinterpret_cast<unsigned char*>(normalized); *byte; ++byte) {
if (*byte >= 'A' && *byte <= 'Z') {
*byte += 'a' - 'A';
}
}
const char* dot = std::strrchr(normalized, '.');
const int type = this->FindType(dot ? dot + 1 : nullptr);
if (type < 0) {
return nullptr;
}
std::string resolved = AddResourcePrefix(this->aliases.Resolve(normalized), this->m_prefixes[type]);
if (CRes* existing = this->Find(type, resolved.c_str())) {
existing->lastUse = timeGetTime();
return existing;
}
CRes* resource = this->m_prototypes[type]->Create();
if (!resource) {
return nullptr;
}
if (resource->Load(resolved.c_str()) != 1) {
delete resource;
lock.reset();
if (resolved.find("\\map\\") == std::string::npos) {
this->m_failures->Report(name, resolved);
}
return nullptr;
}
resource->name.Assign(resolved.c_str());
resource->type = type;
resource->lastUse = timeGetTime();
this->m_resources[type].emplace(&resource->name, resource);
if (resource->IsTransient() == 1) {
this->m_transient.push_back(resource);
}
return resource;
}
// OFFSET: 0x00AF0780
// FUNCTION: fn_18190
void ResourceMgr::Remove(CRes* resource) {
this->EraseTransient(resource);
auto& resources = this->m_resources[resource->type];
const auto entry = resources.find(&resource->name);
entry->second->Unload();
delete entry->second;
resources.erase(entry);
}
// INLINED
void ResourceMgr::EraseTransient(CRes* resource) {
auto entry = std::find(this->m_transient.begin(), this->m_transient.end(), resource);
if (entry != this->m_transient.end()) {
this->m_transient.erase(entry);
}
}
// OFFSET: 0x00AF0A10
// FUNCTION: fn_18192
void ResourceMgr::ClearUnused(const char* extension) {
const int type = this->FindType(extension);
if (type < 0) {
return;
}
auto& resources = this->m_resources[type];
for (auto entry = resources.begin(); entry != resources.end();) {
CRes* resource = entry->second;
if (resource->references != 0) {
++entry;
continue;
}
this->EraseTransient(resource);
resource->Unload();
delete resource;
entry = resources.erase(entry);
}
}
// OFFSET: 0x00AF0AE0
// FUNCTION: fn_18193
void ResourceMgr::ClearUnused() {
timeGetTime();
for (auto& resources : this->m_resources) {
for (auto entry = resources.begin(); entry != resources.end();) {
CRes* resource = entry->second;
if (resource->references != 0) {
++entry;
continue;
}
this->EraseTransient(resource);
resource->Unload();
delete resource;
entry = resources.erase(entry);
}
}
}
// OFFSET: 0x00AF0910
// FUNCTION: fn_18191
void ResourceMgr::ClearExpired(const unsigned& reuseDelay) {
const unsigned threshold = timeGetTime() - reuseDelay;
for (auto& resources : this->m_resources) {
for (auto entry = resources.begin(); entry != resources.end();) {
CRes* resource = entry->second;
if (resource->references != 0 || resource->lastUse >= threshold) {
++entry;
continue;
}
this->EraseTransient(resource);
resource->Unload();
delete resource;
entry = resources.erase(entry);
}
}
}