237 lines
6.9 KiB
C++
237 lines
6.9 KiB
C++
|
#include "CResCache.h"
|
||
|
#include "Log.h"
|
||
|
#include <Common/StringUtil.h>
|
||
|
#include <FileIO/FileIO.h>
|
||
|
#include <iostream>
|
||
|
#include <string>
|
||
|
|
||
|
// Loaders
|
||
|
#include <Resource/factory/CAreaLoader.h>
|
||
|
#include <Resource/factory/CAnimSetLoader.h>
|
||
|
#include <Resource/factory/CCollisionLoader.h>
|
||
|
#include <Resource/factory/CFontLoader.h>
|
||
|
#include <Resource/factory/CModelLoader.h>
|
||
|
#include <Resource/factory/CScanLoader.h>
|
||
|
#include <Resource/factory/CStringLoader.h>
|
||
|
#include <Resource/factory/CTextureDecoder.h>
|
||
|
#include <Resource/factory/CWorldLoader.h>
|
||
|
|
||
|
CResCache::CResCache()
|
||
|
{
|
||
|
mpPak = nullptr;
|
||
|
}
|
||
|
|
||
|
CResCache::~CResCache()
|
||
|
{
|
||
|
Clean();
|
||
|
}
|
||
|
|
||
|
void CResCache::Clean()
|
||
|
{
|
||
|
if (mResourceCache.empty()) return;
|
||
|
Log::Write("Cleaning unused resources");
|
||
|
|
||
|
// I couldn't get this to work properly using reverse iterators, lol.
|
||
|
// Resources get cached after their dependencies, which is why I go backwards
|
||
|
// while loop is to ensure -all- unused resources are cleaned. Not sure of a better way to do it.
|
||
|
int numResourcesCleaned = 1;
|
||
|
|
||
|
while (numResourcesCleaned)
|
||
|
{
|
||
|
numResourcesCleaned = 0;
|
||
|
|
||
|
for (auto it = mResourceCache.end(); it != mResourceCache.begin();)
|
||
|
{
|
||
|
it--;
|
||
|
if (it->second->mRefCount <= 0)
|
||
|
{
|
||
|
delete it->second;
|
||
|
it = mResourceCache.erase(it);
|
||
|
numResourcesCleaned++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Log::Write(std::to_string(mResourceCache.size()) + " resources loaded");
|
||
|
}
|
||
|
|
||
|
void CResCache::SetFolder(std::string path)
|
||
|
{
|
||
|
StringUtil::AppendSlash(path);
|
||
|
mResSource.Path = path;
|
||
|
mResSource.Source = SResSource::Folder;
|
||
|
Log::Write("Set resource folder: " + path);
|
||
|
}
|
||
|
|
||
|
void CResCache::SetPak(std::string path)
|
||
|
{
|
||
|
CFileInStream *pakfile = new CFileInStream(path, IOUtil::BigEndian);
|
||
|
if (!pakfile->IsValid())
|
||
|
{
|
||
|
Log::Error("Couldn't load pak file: " + path);
|
||
|
delete pakfile;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mpPak) delete mpPak;
|
||
|
mpPak = new CPakFile(pakfile);
|
||
|
mResSource.Path = path;
|
||
|
mResSource.Source = SResSource::PakFile;
|
||
|
Log::Write("Loaded pak file: " + path);
|
||
|
}
|
||
|
|
||
|
void CResCache::SetResSource(SResSource& ResSource)
|
||
|
{
|
||
|
mResSource = ResSource;
|
||
|
}
|
||
|
|
||
|
SResSource CResCache::GetResSource()
|
||
|
{
|
||
|
return mResSource;
|
||
|
}
|
||
|
|
||
|
std::string CResCache::GetSourcePath()
|
||
|
{
|
||
|
return mResSource.Path;
|
||
|
}
|
||
|
|
||
|
CResource* CResCache::GetResource(CUniqueID ResID, CFourCC type)
|
||
|
{
|
||
|
if (!ResID.IsValid()) return nullptr;
|
||
|
|
||
|
auto got = mResourceCache.find(ResID.ToLongLong());
|
||
|
|
||
|
if (got != mResourceCache.end())
|
||
|
return got->second;
|
||
|
|
||
|
std::vector<u8> *pBuffer = nullptr;
|
||
|
std::string Source;
|
||
|
|
||
|
// Load from pak
|
||
|
if (mResSource.Source == SResSource::PakFile)
|
||
|
{
|
||
|
pBuffer = mpPak->getResource(ResID.ToLongLong(), type);
|
||
|
Source = ResID.ToString() + "." + type.ToString();
|
||
|
}
|
||
|
|
||
|
// Load from folder
|
||
|
else
|
||
|
{
|
||
|
Source = mResSource.Path + StringUtil::ResToStr(ResID.ToLong()) + "." + type.ToString();
|
||
|
CFileInStream file(Source, IOUtil::BigEndian);
|
||
|
if (!file.IsValid())
|
||
|
{
|
||
|
Source = mResSource.Path + StringUtil::ResToStr(ResID.ToLongLong()) + "." + type.ToString();
|
||
|
file.Open(Source, IOUtil::BigEndian);
|
||
|
if (!file.IsValid())
|
||
|
{
|
||
|
Log::Error("Couldn't open resource: " + ResID.ToString() + "." + type.ToString());
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pBuffer = new std::vector<u8>;
|
||
|
pBuffer->resize(file.Size());
|
||
|
file.ReadBytes(pBuffer->data(), pBuffer->size());
|
||
|
}
|
||
|
if (!pBuffer) return nullptr;
|
||
|
|
||
|
// Load resource
|
||
|
CMemoryInStream mem(pBuffer->data(), pBuffer->size(), IOUtil::BigEndian);
|
||
|
mem.SetSourceString(StringUtil::GetFileNameWithExtension(Source));
|
||
|
CResource *Res = nullptr;
|
||
|
bool SupportedFormat = true;
|
||
|
|
||
|
if (type == "CMDL") Res = CModelLoader::LoadCMDL(mem);
|
||
|
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(mem);
|
||
|
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(mem);
|
||
|
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(mem);
|
||
|
else if (type == "MREA") Res = CAreaLoader::LoadMREA(mem);
|
||
|
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(mem);
|
||
|
else if (type == "STRG") Res = CStringLoader::LoadSTRG(mem);
|
||
|
else if (type == "FONT") Res = CFontLoader::LoadFONT(mem);
|
||
|
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(mem);
|
||
|
else SupportedFormat = false;
|
||
|
|
||
|
// Log errors
|
||
|
if (!SupportedFormat)
|
||
|
Log::Write("Unsupported format; unable to load " + type.ToString() + " " + ResID.ToString());
|
||
|
|
||
|
if (!Res) Res = new CResource(); // Default for invalid resource or unsupported format
|
||
|
|
||
|
// Add to cache and cleanup
|
||
|
Res->mID = ResID;
|
||
|
Res->mResSource = Source;
|
||
|
mResourceCache[ResID.ToLongLong()] = Res;
|
||
|
delete pBuffer;
|
||
|
return Res;
|
||
|
}
|
||
|
|
||
|
CResource* CResCache::GetResource(std::string ResPath)
|
||
|
{
|
||
|
// Since this function takes a string argument it always loads directly from a file - no pak
|
||
|
CUniqueID ResID = StringUtil::Hash64(ResPath);
|
||
|
|
||
|
auto got = mResourceCache.find(ResID.ToLongLong());
|
||
|
|
||
|
if (got != mResourceCache.end())
|
||
|
return got->second;
|
||
|
|
||
|
CFileInStream file(ResPath, IOUtil::BigEndian);
|
||
|
if (!file.IsValid())
|
||
|
{
|
||
|
Log::Error("Couldn't open resource: " + ResPath);
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
// Save old ResSource to restore later
|
||
|
const SResSource OldSource = mResSource;
|
||
|
mResSource.Source = SResSource::Folder;
|
||
|
mResSource.Path = StringUtil::GetFileDirectory(ResPath);
|
||
|
|
||
|
// Load resource
|
||
|
CResource *Res = nullptr;
|
||
|
CFourCC type = StringUtil::ToUpper( StringUtil::GetExtension(ResPath) ).c_str();
|
||
|
bool SupportedFormat = true;
|
||
|
|
||
|
if (type == "CMDL") Res = CModelLoader::LoadCMDL(file);
|
||
|
else if (type == "TXTR") Res = CTextureDecoder::LoadTXTR(file);
|
||
|
else if (type == "ANCS") Res = CAnimSetLoader::LoadANCS(file);
|
||
|
else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file);
|
||
|
else if (type == "MREA") Res = CAreaLoader::LoadMREA(file);
|
||
|
else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file);
|
||
|
else if (type == "FONT") Res = CFontLoader::LoadFONT(file);
|
||
|
else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file);
|
||
|
else SupportedFormat = false;
|
||
|
|
||
|
if (!Res) Res = new CResource(); // Default for unsupported formats
|
||
|
|
||
|
// Add to cache and cleanup
|
||
|
Res->mID = ResPath.c_str();
|
||
|
Res->mResSource = ResPath;
|
||
|
mResourceCache[ResID.ToLongLong()] = Res;
|
||
|
mResSource = OldSource;
|
||
|
return Res;
|
||
|
}
|
||
|
|
||
|
void CResCache::CacheResource(CResource *pRes)
|
||
|
{
|
||
|
u64 ID = pRes->ResID().ToLongLong();
|
||
|
auto got = mResourceCache.find(ID);
|
||
|
|
||
|
if (got != mResourceCache.end())
|
||
|
mResourceCache[ID] = pRes;
|
||
|
}
|
||
|
|
||
|
void CResCache::DeleteResource(CUniqueID ResID)
|
||
|
{
|
||
|
auto got = mResourceCache.find(ResID.ToLongLong());
|
||
|
|
||
|
if (got != mResourceCache.end())
|
||
|
{
|
||
|
delete got->second;
|
||
|
mResourceCache.erase(got, got);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CResCache gResCache;
|