2016-04-27 04:27:57 -06:00

283 lines
8.7 KiB
C++

#include "CResCache.h"
#include "Core/Resource/Factory/CAreaLoader.h"
#include "Core/Resource/Factory/CAnimationLoader.h"
#include "Core/Resource/Factory/CAnimSetLoader.h"
#include "Core/Resource/Factory/CCollisionLoader.h"
#include "Core/Resource/Factory/CFontLoader.h"
#include "Core/Resource/Factory/CModelLoader.h"
#include "Core/Resource/Factory/CPoiToWorldLoader.h"
#include "Core/Resource/Factory/CScanLoader.h"
#include "Core/Resource/Factory/CSkeletonLoader.h"
#include "Core/Resource/Factory/CSkinLoader.h"
#include "Core/Resource/Factory/CStringLoader.h"
#include "Core/Resource/Factory/CTextureDecoder.h"
#include "Core/Resource/Factory/CWorldLoader.h"
#include <Common/Log.h>
#include <Common/TString.h>
#include <FileIO/FileIO.h>
#include <iostream>
#include <boost/filesystem.hpp>
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(TString Path)
{
Path.EnsureEndsWith("/");
mResSource.Path = Path;
mResSource.Source = SResSource::eFolder;
Log::Write("Set resource folder: " + Path);
}
void CResCache::SetPak(const TString& rkPath)
{
CFileInStream *pPakFile = new CFileInStream(rkPath.ToStdString(), IOUtil::eBigEndian);
if (!pPakFile->IsValid())
{
Log::Error("Couldn't load pak file: " + rkPath);
delete pPakFile;
return;
}
if (mpPak) delete mpPak;
mpPak = new CPakFile(pPakFile);
mResSource.Path = rkPath;
mResSource.Source = SResSource::ePakFile;
Log::Write("Loaded pak file: " + rkPath);
}
void CResCache::SetResSource(SResSource& rResSource)
{
mResSource = rResSource;
}
SResSource CResCache::GetResSource()
{
return mResSource;
}
TString 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;
TString Source;
// Load from pak
if (mResSource.Source == SResSource::ePakFile)
{
pBuffer = mpPak->Resource(ResID.ToLongLong(), Type);
Source = ResID.ToString() + "." + Type.ToString();
}
// Load from folder
else
{
Source = mResSource.Path + ResID.ToString() + "." + Type.ToString();
CFileInStream File(Source.ToStdString(), IOUtil::eBigEndian);
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::eBigEndian);
Mem.SetSourceString(*Source.GetFileName());
CResource *pRes = nullptr;
bool SupportedFormat = true;
if (Type == "CMDL") pRes = CModelLoader::LoadCMDL(Mem);
else if (Type == "TXTR") pRes = CTextureDecoder::LoadTXTR(Mem);
else if (Type == "ANCS") pRes = CAnimSetLoader::LoadANCS(Mem);
else if (Type == "CHAR") pRes = CAnimSetLoader::LoadCHAR(Mem);
else if (Type == "MREA") pRes = CAreaLoader::LoadMREA(Mem);
else if (Type == "MLVL") pRes = CWorldLoader::LoadMLVL(Mem);
else if (Type == "STRG") pRes = CStringLoader::LoadSTRG(Mem);
else if (Type == "FONT") pRes = CFontLoader::LoadFONT(Mem);
else if (Type == "SCAN") pRes = CScanLoader::LoadSCAN(Mem);
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(Mem);
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(Mem);
else if (Type == "CSKR") pRes = CSkinLoader::LoadCSKR(Mem);
else SupportedFormat = false;
// Log errors
if (!SupportedFormat)
Log::Write("Unsupported format; unable to load " + Type.ToString() + " " + ResID.ToString());
if (!pRes) pRes = new CResource(); // Default for invalid resource or unsupported format
// Add to cache and cleanup
pRes->mID = ResID;
pRes->mResSource = Source;
mResourceCache[ResID.ToLongLong()] = pRes;
delete pBuffer;
return pRes;
}
CResource* CResCache::GetResource(const TString& rkResPath)
{
// Since this function takes a string argument it always loads directly from a file - no pak
CUniqueID ResID = rkResPath.Hash64();
auto Got = mResourceCache.find(ResID.ToLongLong());
if (Got != mResourceCache.end())
return Got->second;
CFileInStream File(rkResPath.ToStdString(), IOUtil::eBigEndian);
if (!File.IsValid())
{
Log::Error("Couldn't open resource: " + rkResPath);
return nullptr;
}
// Save old ResSource to restore later
const SResSource OldSource = mResSource;
mResSource.Source = SResSource::eFolder;
mResSource.Path = rkResPath.GetFileDirectory();
// Load resource
CResource *pRes = nullptr;
CFourCC Type = rkResPath.GetFileExtension().ToUpper();
bool SupportedFormat = true;
if (Type == "CMDL") pRes = CModelLoader::LoadCMDL(File);
else if (Type == "TXTR") pRes = CTextureDecoder::LoadTXTR(File);
else if (Type == "ANCS") pRes = CAnimSetLoader::LoadANCS(File);
else if (Type == "CHAR") pRes = CAnimSetLoader::LoadCHAR(File);
else if (Type == "MREA") pRes = CAreaLoader::LoadMREA(File);
else if (Type == "MLVL") pRes = CWorldLoader::LoadMLVL(File);
else if (Type == "STRG") pRes = CStringLoader::LoadSTRG(File);
else if (Type == "FONT") pRes = CFontLoader::LoadFONT(File);
else if (Type == "SCAN") pRes = CScanLoader::LoadSCAN(File);
else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(File);
else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File);
else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File);
else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(File);
else if (Type == "CSKR") pRes = CSkinLoader::LoadCSKR(File);
else SupportedFormat = false;
if (!pRes) pRes = new CResource(); // Default for unsupported formats
// Add to cache and cleanup
pRes->mID = *rkResPath;
pRes->mResSource = rkResPath;
mResourceCache[ResID.ToLongLong()] = pRes;
mResSource = OldSource;
return pRes;
}
CFourCC CResCache::FindResourceType(CUniqueID ResID, const TStringList& rkPossibleTypes)
{
// If we only have one type then there's only one possibility.
if (rkPossibleTypes.size() == 1)
return CFourCC(rkPossibleTypes.front());
// Determine extension from pak
if (mResSource.Source == SResSource::ePakFile)
{
for (auto it = rkPossibleTypes.begin(); it != rkPossibleTypes.end(); it++)
{
SResInfo ResInfo = mpPak->ResourceInfo(ResID.ToLongLong(), CFourCC(*it));
if (ResInfo.Type != "NULL")
return CFourCC(*it);
}
}
// Determine extension from filesystem - try every extension until we find one that works
else
{
TString PathBase = mResSource.Path + ResID.ToString() + ".";
for (auto it = rkPossibleTypes.begin(); it != rkPossibleTypes.end(); it++)
{
TString NewPath = PathBase + *it;
if (boost::filesystem::exists(NewPath.ToStdString()))
return CFourCC(*it);
}
}
return "UNKN";
}
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;