Files
wc3re/ResourceProvider.cc

258 lines
6.1 KiB
C++

#include <algorithm>
#include "Resource.hh"
#include "exceptions.hh"
#include "util.hh"
#include "IffFile.hh"
//#include "TreFile.hh"
#include "ResourceProvider.hh"
template<>
std::unique_ptr<ResourceProvider> Singleton<ResourceProvider>::instance{nullptr};
template<>
std::once_flag Singleton<ResourceProvider>::instance_flag{};
using std::tie;
using std::ignore;
using std::make_unique;
ResourceProvider::ResourceProvider()
: cacheSize_(0), cacheLimit_(64*1024*1024)
{
}
ResourceProvider::~ResourceProvider()
{
}
void ResourceProvider::dumpCache_() const
{
for (auto& iff: iffs_) {
printf("IFF: %s: %u users, %zu size\n", iff.first.c_str(), iff.second->getRef(),
iff.second->footprint());
}
for (auto& treObj: treObjs_) {
printf("TRE-Obj: %s: %u users, %zu size\n", treObj.first.c_str(), treObj.second->getRef(),
treObj.second->footprint());
}
for (auto& tre: tres_) {
printf("TRE: %s: %u users, %zu size\n", tre.first.c_str(), tre.second->getRef(),
tre.second->footprint());
}
for (auto& mmap: files_) {
printf("File: %s: %u users, %zu size\n", mmap.first.c_str(), mmap.second->getRef(),
mmap.second->footprint());
}
}
void ResourceProvider::gc_()
{
printf("ResourceProvider::gc_()\nBefore:\n");
dumpCache_();
if (cacheSize_ <= cacheLimit_) {
printf("No GC necessary\n");
return;
}
{
auto it = iffs_.begin();
while (it != iffs_.end() && cacheSize_ > cacheLimit_) {
if (it->second->getRef() == 0) {
auto del = it++;
cacheSize_ -= del->second->footprint();
iffs_.erase(del);
} else
++it;
}
}
{
auto it = treObjs_.begin();
while (it != treObjs_.end() && cacheSize_ > cacheLimit_) {
if (it->second->getRef() == 0) {
auto del = it++;
cacheSize_ -= del->second->footprint();
treObjs_.erase(del);
} else
++it;
}
}
{
auto it = tres_.begin();
while (it != tres_.end() && cacheSize_ > cacheLimit_) {
if (it->second->getRef() == 0) {
auto del = it++;
cacheSize_ -= del->second->footprint();
tres_.erase(del);
} else
++it;
}
}
{
auto it = files_.begin();
while (it != files_.end() && cacheSize_ > cacheLimit_) {
if (it->second->getRef() == 0) {
auto del = it++;
cacheSize_ -= del->second->footprint();
files_.erase(del);
} else
++it;
}
}
printf("After:\n");
dumpCache_();
}
MmapFile& ResourceProvider::getMmap_(std::string const& ospath)
{
auto it = files_.find(ospath);
if (it == files_.end()) {
tie(it, ignore) = files_.emplace(ospath, make_unique<MmapFile>(ospath));
cacheSize_ += it->second->footprint();
}
return *it->second;
}
TreFile& ResourceProvider::getTre_(std::string const& ospath)
{
auto it = tres_.find(ospath);
if (it == tres_.end()) {
auto& mmap = getMmap_(ospath);
tie(it, ignore) = tres_.emplace(ospath, make_unique<TreFile>(mmap));
cacheSize_ += it->second->footprint();
}
return *it->second;
}
TreFile::Object& ResourceProvider::getTreObj_(std::string const& path)
{
auto sepPos = path.find(':');
if (sepPos == std::string::npos)
throw PathException{"Incomplete TRE object path"};
auto& tre = getTre_(path.substr(0, sepPos));
auto normName = tre.normalizeName(path.substr(sepPos+1));
auto it = treObjs_.find(path.substr(0, sepPos) + ':' + normName);
if (it == treObjs_.end()) {
tie(it, ignore) = treObjs_.emplace(path.substr(0, sepPos) + ':' + normName,
tre.getObject(normName));
cacheSize_ += it->second->footprint();
}
return *it->second;
}
IffFile& ResourceProvider::getIff_(std::string const& path)
{
std::string normPath;
auto sepPos = path.find(':');
if (sepPos == std::string::npos) {
normPath = path;
} else {
auto& tre = getTre_(path.substr(0, sepPos));
normPath = path.substr(0, sepPos) + ':' + tre.normalizeName(path.substr(sepPos+1));
}
auto it = iffs_.find(normPath);
if (it == iffs_.end()) {
if (sepPos == std::string::npos) {
auto& mmap = getMmap_(normPath);
tie(it, ignore) = iffs_.emplace(normPath, make_unique<IffFile>(mmap));
} else {
auto& treObj = getTreObj_(normPath);
tie(it, ignore) = iffs_.emplace(normPath, make_unique<IffFile>(treObj));
}
cacheSize_ += it->second->footprint();
}
return *it->second;
}
IffFile::Object& ResourceProvider::getIffObj_(IffFile& iff, std::string const& nodepath)
{
size_t pos = 0, sepPos = nodepath.find('/');
auto obj = &iff.getObject(nodepath.substr(0, sepPos));
pos = sepPos;
while (pos != std::string::npos) {
sepPos = nodepath.find('/', pos+1);
auto form = dynamic_cast<IffFile::Form*>(obj);
if (!form)
throw PathException{"Non-form " + nodepath.substr(0, pos) +
"with leftover path elements: " + nodepath.substr(pos+1)};
obj = &form->getObject(nodepath.substr(pos+1, sepPos-pos-1));
pos = sepPos;
}
return *obj;
}
Resource::Handle ResourceProvider::getResource(std::string const& path)
{
std::string normPath;
auto sepPos = path.find(':');
Resource::Handle ret;
if (sepPos == std::string::npos) {
// No path seperator, requested resource is a plain OS file
ret = getMmap_(path);
} else {
try {
// Assume root resource is TRE...
auto sepPos2 = path.find('/', sepPos);
if (sepPos2 == std::string::npos) {
// Only one path component, requested resource is TRE object
ret = getTreObj_(path);
} else {
// Requested resource is probably IFF node inside TRE
auto& iff = getIff_(path.substr(0, sepPos2));
ret = getIffObj_(iff, path.substr(sepPos2+1));
}
} catch (FormatException& ex) {
}
if (!ret) {
try {
// Hmm, maybe root resource is a naked IFF (no TRE)?
auto& iff = getIff_(path.substr(0, sepPos));
ret = getIffObj_(iff, path.substr(sepPos+1));
} catch (FormatException& ex) {
}
}
if (!ret) {
// No dice
throw PathException{"Could not load requested resource: " + path};
}
}
gc_();
return ret;
}
IffFile::Handle ResourceProvider::getIff(std::string const& path)
{
IffFile::Handle ret(getIff_(path));
gc_();
return ret;
}
TreFile::Handle ResourceProvider::getTre(std::string const& path)
{
TreFile::Handle ret(getTre_(path));
gc_();
return ret;
}