258 lines
6.1 KiB
C++
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;
|
|
}
|