#include "Runtime/CResLoader.hpp" #include "Runtime/CPakFile.hpp" namespace metaforce { static logvisor::Module Log("CResLoader"); CResLoader::CResLoader() { x48_curPak = x18_pakLoadedList.end(); } const std::vector* CResLoader::GetTagListForFile(std::string_view name) const { const std::string namePak = std::string(name).append(".pak"); for (const std::unique_ptr& pak : x18_pakLoadedList) { if (CStringExtras::CompareCaseInsensitive(namePak, pak->x18_path)) { return &pak->GetDepList(); } } return nullptr; } void CResLoader::AddPakFileAsync(std::string_view name, bool buildDepList, bool worldPak, bool override) { const std::string namePak = std::string(name).append(".pak"); if (CDvdFile::FileExists(namePak)) { x30_pakLoadingList.emplace_back(std::make_unique(namePak, buildDepList, worldPak, override)); ++x44_pakLoadingCount; } } void CResLoader::AddPakFile(std::string_view name, bool samusPak, bool worldPak, bool override) { AddPakFileAsync(name, samusPak, worldPak, override); WaitForPakFileLoadingComplete(); } void CResLoader::WaitForPakFileLoadingComplete() { while (x44_pakLoadingCount) AsyncIdlePakLoading(); } std::unique_ptr CResLoader::LoadNewResourcePartSync(const SObjectTag& tag, u32 length, u32 offset, void* extBuf) { void* buf = extBuf; if (buf == nullptr) { buf = new u8[length]; } CPakFile* const file = FindResourceForLoad(tag); file->SyncSeekRead(buf, length, ESeekOrigin::Begin, x50_cachedResInfo->GetOffset() + offset); return std::make_unique( buf, length, extBuf == nullptr ? CMemoryInStream::EOwnerShip::Owned : CMemoryInStream::EOwnerShip::NotOwned); } void CResLoader::LoadMemResourceSync(const SObjectTag& tag, std::unique_ptr& bufOut, int* sizeOut) { if (CPakFile* file = FindResourceForLoad(tag)) { bufOut = std::unique_ptr(new u8[x50_cachedResInfo->GetSize()]); file->SyncSeekRead(bufOut.get(), x50_cachedResInfo->GetSize(), ESeekOrigin::Begin, x50_cachedResInfo->GetOffset()); *sizeOut = x50_cachedResInfo->GetSize(); } } std::unique_ptr CResLoader::LoadResourceFromMemorySync(const SObjectTag& tag, const void* buf) { FindResourceForLoad(tag); std::unique_ptr newStrm = std::make_unique(buf, x50_cachedResInfo->GetSize(), CMemoryInStream::EOwnerShip::NotOwned); if (x50_cachedResInfo->IsCompressed()) { newStrm->ReadLong(); newStrm = std::make_unique(std::move(newStrm)); } return newStrm; } std::unique_ptr CResLoader::LoadNewResourceSync(const SObjectTag& tag, void* extBuf) { if (CPakFile* const file = FindResourceForLoad(tag)) { const size_t resSz = ROUND_UP_32(x50_cachedResInfo->GetSize()); void* buf = extBuf; if (buf == nullptr) { buf = new u8[resSz]; } file->SyncSeekRead(buf, resSz, ESeekOrigin::Begin, x50_cachedResInfo->GetOffset()); const bool takeOwnership = extBuf == nullptr; std::unique_ptr newStrm = std::make_unique( buf, resSz, takeOwnership ? CMemoryInStream::EOwnerShip::Owned : CMemoryInStream::EOwnerShip::NotOwned); if (x50_cachedResInfo->IsCompressed()) { newStrm->ReadLong(); newStrm = std::make_unique(std::move(newStrm)); } return newStrm; } return nullptr; } std::shared_ptr CResLoader::LoadResourcePartAsync(const SObjectTag& tag, u32 off, u32 size, void* buf) { CPakFile* file = FindResourceForLoad(tag.id); return file->AsyncSeekRead(buf, size, ESeekOrigin::Begin, x50_cachedResInfo->GetOffset() + off); } std::shared_ptr CResLoader::LoadResourceAsync(const SObjectTag& tag, void* buf) { CPakFile* file = FindResourceForLoad(tag.id); return file->AsyncSeekRead(buf, ROUND_UP_32(x50_cachedResInfo->GetSize()), ESeekOrigin::Begin, x50_cachedResInfo->GetOffset()); } std::unique_ptr CResLoader::LoadResourceSync(const metaforce::SObjectTag& tag) { CPakFile* file = FindResourceForLoad(tag.id); u32 size = ROUND_UP_32(x50_cachedResInfo->GetSize()); std::unique_ptr ret(new u8[size]); file->SyncSeekRead(ret.get(), size, ESeekOrigin::Begin, x50_cachedResInfo->GetOffset()); return ret; } std::unique_ptr CResLoader::LoadNewResourcePartSync(const metaforce::SObjectTag& tag, u32 off, u32 size) { CPakFile* file = FindResourceForLoad(tag.id); std::unique_ptr ret(new u8[size]); file->SyncSeekRead(ret.get(), size, ESeekOrigin::Begin, x50_cachedResInfo->GetOffset() + off); return ret; } void CResLoader::GetTagListForFile(const char* pakName, std::vector& out) const { std::string path = std::string(pakName) + ".pak"; for (const std::unique_ptr& file : m_overridePakList) { if (_GetTagListForFile(out, path, file)) return; } for (const std::unique_ptr& file : x18_pakLoadedList) { if (_GetTagListForFile(out, path, file)) return; } } bool CResLoader::_GetTagListForFile(std::vector& out, const std::string& path, const std::unique_ptr& file) const { if (CStringExtras::CompareCaseInsensitive(file->GetPath(), path)) { const auto& depList = file->GetDepList(); out.reserve(depList.size()); for (const auto& dep : depList) { const auto* const resInfo = file->GetResInfo(dep); out.emplace_back(resInfo->GetType(), dep); } return true; } return false; } bool CResLoader::GetResourceCompression(const SObjectTag& tag) const { if (FindResource(tag.id)) return x50_cachedResInfo->IsCompressed(); return false; } u32 CResLoader::ResourceSize(const SObjectTag& tag) const { if (FindResource(tag.id)) return x50_cachedResInfo->GetSize(); return 0; } bool CResLoader::ResourceExists(const SObjectTag& tag) const { return FindResource(tag.id); } FourCC CResLoader::GetResourceTypeById(CAssetId id) const { if (id.IsValid() && FindResource(id)) return x50_cachedResInfo->GetType(); return {}; } const SObjectTag* CResLoader::GetResourceIdByName(std::string_view name) const { for (const std::unique_ptr& file : m_overridePakList) if (const SObjectTag* id = file->GetResIdByName(name)) return id; for (const std::unique_ptr& file : x18_pakLoadedList) if (const SObjectTag* id = file->GetResIdByName(name)) return id; return nullptr; } bool CResLoader::AreAllPaksLoaded() const { return x44_pakLoadingCount == 0; } void CResLoader::AsyncIdlePakLoading() { for (auto it = x30_pakLoadingList.begin(); it != x30_pakLoadingList.end();) { (*it)->AsyncIdle(); if ((*it)->x2c_asyncLoadPhase == CPakFile::EAsyncPhase::Loaded) { MoveToCorrectLoadedList(std::move(*it)); it = x30_pakLoadingList.erase(it); --x44_pakLoadingCount; continue; } ++it; } } bool CResLoader::FindResource(CAssetId id) const { if (x4c_cachedResId == id) return true; for (auto it = m_overridePakList.begin(); it != m_overridePakList.end(); ++it) { if (CacheFromPak(**it, id)) { return true; } } if (x48_curPak != x18_pakLoadedList.end()) if (CacheFromPak(**x48_curPak, id)) return true; for (auto it = x18_pakLoadedList.begin(); it != x18_pakLoadedList.end(); ++it) { if (it == x48_curPak) continue; if (CacheFromPak(**it, id)) return true; } Log.report(logvisor::Warning, FMT_STRING("Unable to find asset {}"), id); return false; } CPakFile* CResLoader::FindResourceForLoad(CAssetId id) { for (auto it = m_overridePakList.begin(); it != m_overridePakList.end(); ++it) { if (CacheFromPakForLoad(**it, id)) { return &**it; } } if (x48_curPak != x18_pakLoadedList.end()) if (CacheFromPakForLoad(**x48_curPak, id)) return &**x48_curPak; for (auto it = x18_pakLoadedList.begin(); it != x18_pakLoadedList.end(); ++it) { if (it == x48_curPak) continue; if (CacheFromPakForLoad(**it, id)) { x48_curPak = it; return &**it; } } Log.report(logvisor::Error, FMT_STRING("Unable to find asset {}"), id); return nullptr; } CPakFile* CResLoader::FindResourceForLoad(const SObjectTag& tag) { return FindResourceForLoad(tag.id); } bool CResLoader::CacheFromPakForLoad(CPakFile& file, CAssetId id) { const CPakFile::SResInfo* info; if (x54_forwardSeek) { info = file.GetResInfoForLoadPreferForward(id); x54_forwardSeek = false; } else { info = file.GetResInfoForLoadDirectionless(id); } if (info) { x4c_cachedResId = id; x50_cachedResInfo = info; return true; } return false; } bool CResLoader::CacheFromPak(const CPakFile& file, CAssetId id) const { const CPakFile::SResInfo* info = file.GetResInfo(id); if (info) { x4c_cachedResId = id; x50_cachedResInfo = info; return true; } return false; } void CResLoader::MoveToCorrectLoadedList(std::unique_ptr&& file) { if (file->IsOverridePak()) m_overridePakList.push_back(std::move(file)); else x18_pakLoadedList.push_back(std::move(file)); } std::vector> CResLoader::GetResourceIdToNameList() const { std::vector> ret; for (auto it = x18_pakLoadedList.begin(); it != x18_pakLoadedList.end(); ++it) for (const auto& name : (*it)->GetNameList()) ret.push_back(name); return ret; } void CResLoader::EnumerateResources(const std::function& lambda) const { for (auto it = m_overridePakList.begin(); it != m_overridePakList.end(); ++it) { for (const CAssetId& id : (*it)->GetDepList()) { SObjectTag fcc(GetResourceTypeById(id), id); if (!lambda(fcc)) return; } } for (auto it = x18_pakLoadedList.begin(); it != x18_pakLoadedList.end(); ++it) { for (const CAssetId& id : (*it)->GetDepList()) { SObjectTag fcc(GetResourceTypeById(id), id); if (!lambda(fcc)) return; } } } void CResLoader::EnumerateNamedResources(const std::function& lambda) const { for (auto it = x18_pakLoadedList.begin(); it != x18_pakLoadedList.end(); ++it) for (const auto& name : (*it)->GetNameList()) if (!lambda(name.first, name.second)) return; } } // namespace metaforce