mirror of
https://github.com/zeldaret/botw
synced 2026-06-11 13:10:05 -04:00
192 lines
6.3 KiB
C++
192 lines
6.3 KiB
C++
#include "KingSystem/Resource/resCache.h"
|
|
#include <prim/seadScopedLock.h>
|
|
#include "KingSystem/Resource/resCacheCriticalSection.h"
|
|
#include "KingSystem/Resource/resControlTask.h"
|
|
#include "KingSystem/Resource/resResourceMgrTask.h"
|
|
#include "KingSystem/Resource/resSystem.h"
|
|
#include "KingSystem/Utils/Debug.h"
|
|
|
|
namespace ksys::res {
|
|
|
|
Cache::Cache() = default;
|
|
|
|
void Cache::init() {}
|
|
|
|
ResourceUnit* Cache::findUnit(const util::StrTreeMapNode::KeyType& key) const {
|
|
auto lock = sead::makeScopedLock(gCacheCriticalSection);
|
|
ResourceUnitMapNode* node = mMap.find(key);
|
|
return node ? node->getUnit() : nullptr;
|
|
}
|
|
|
|
Handle::Status Cache::loadResource(const ControlTaskData& data) {
|
|
auto* handle = data.mResHandle;
|
|
if (handle->isLinked()) {
|
|
stubbedLogFunction();
|
|
return Handle::Status::_8;
|
|
}
|
|
|
|
{
|
|
const auto path = data.mResLoadReq.mPath;
|
|
ResourceUnit* unit = data.mPackResUnit;
|
|
auto lock = sead::makeScopedLock(gCacheCriticalSection);
|
|
bool remove_from_cache_if_needed = true;
|
|
if (!unit) {
|
|
unit = findUnit(path);
|
|
remove_from_cache_if_needed = [&] {
|
|
if (!unit)
|
|
return false;
|
|
|
|
if (unit->mStatusFlags.isOn(ResourceUnit::StatusFlag::_80)) {
|
|
unit->removeFromCache();
|
|
if (returnFalse())
|
|
stubbedLogFunction();
|
|
return false;
|
|
}
|
|
|
|
if (unit->isStatusFlag10000Set()) {
|
|
unit->removeFromCache();
|
|
return false;
|
|
}
|
|
|
|
if (unit->isStatus0()) {
|
|
sead::FormatFixedSafeString<256> message("↓↓↓\nリソース名 : %s\n↑↑↑\n",
|
|
path.cstr());
|
|
util::PrintDebug(message);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}();
|
|
}
|
|
|
|
if (remove_from_cache_if_needed) {
|
|
const bool removed = unit->removeTask3FromQueue();
|
|
if (unit->isLinkedToResourceMgr()) {
|
|
unit->removeFromCache();
|
|
} else if (removed) {
|
|
unit->updateStatus();
|
|
unit->attachHandle(handle);
|
|
return unit->getRefCount() == 1 ? Handle::Status::_6 : Handle::Status::_5;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.mHasResLoadReq)
|
|
return Handle::Status::_8;
|
|
|
|
ResourceUnit* result;
|
|
|
|
{
|
|
sead::FixedSafeString<128 + 7> new_path;
|
|
sead::SafeString path = data.mResLoadReq.mPath;
|
|
|
|
bool set_flag_4 = false;
|
|
if (data.mResLoadReq.mLoadCompressed) {
|
|
auto it = data.mResLoadReq.mPath.rfindIterator(".");
|
|
if (it.getIndex() == -1)
|
|
return Handle::Status::_1;
|
|
|
|
++it;
|
|
sead::SafeString extension;
|
|
extension = &*it;
|
|
set_flag_4 = ResourceMgrTask::instance()->dropSFromExtensionIfNeeded(
|
|
data.mResLoadReq.mPath, new_path, it.getIndex() - 1, extension);
|
|
}
|
|
|
|
if (!new_path.isEmpty())
|
|
path = new_path;
|
|
|
|
ResourceUnit::InitArg init_arg(false, false, set_flag_4, data.mResLoadReq._26,
|
|
data.mResLoadReq._28, handle, this, nullptr, nullptr,
|
|
&data.mResLoadReq, data.mResLoadReq.mArena,
|
|
data.mResLoadReq.mBufferSize, path);
|
|
|
|
ResourceMgrTask::GetUnitArg arg;
|
|
arg.unit_init_arg = &init_arg;
|
|
arg.arena = data.mResLoadReq.mArena;
|
|
|
|
result = ResourceMgrTask::instance()->clearCachesAndGetUnit(arg);
|
|
}
|
|
|
|
if (!result)
|
|
return Handle::Status::_3;
|
|
|
|
{
|
|
auto lock = sead::makeScopedLock(gCacheCriticalSection);
|
|
mMap.insert(&result->mMapNode);
|
|
result->setIsLinkedToCache(true);
|
|
}
|
|
|
|
u8 lane_id = 0xff;
|
|
if (data.mResLoadReq._c <= 2) {
|
|
const bool x = result->mStatusFlags.isOn(ResourceUnit::StatusFlag::LoadFromArchive);
|
|
#ifdef MATCHING_HACK_NX_CLANG
|
|
// This makes absolutely no sense at all, but this prevents InstCombine from
|
|
// turning (x & 0x20) >> 5 into (x >> 5) & 1 by adding a fake-use.
|
|
// LLVM (4.0.1 at least) doesn't do anything with this piece of information
|
|
// so the conditional still works fine.
|
|
__builtin_assume(x);
|
|
#endif
|
|
lane_id = 2 * data.mResLoadReq._c + (x ? 1 : 2);
|
|
}
|
|
|
|
ResourceUnit::RequestInitLoadArg load_arg;
|
|
load_arg.lane_id = lane_id;
|
|
load_arg.has_handle = data.mResLoadReq._8;
|
|
result->requestInitLoad(load_arg);
|
|
return Handle::Status::_7;
|
|
}
|
|
|
|
void Cache::eraseUnit(ResourceUnit* unit) {
|
|
auto lock = sead::makeScopedLock(gCacheCriticalSection);
|
|
if (unit->isLinkedToCache()) {
|
|
mMap.erase(unit->getCacheKey());
|
|
unit->setIsLinkedToCache(false);
|
|
}
|
|
}
|
|
|
|
void Cache::removeUnitAndClearCache_(ResourceUnit* unit) {
|
|
unit->mStatusFlags.reset(ResourceUnit::StatusFlag::_20000);
|
|
if (unit->isStatusFlag8000Set()) {
|
|
ResourceMgrTask::instance()->deregisterUnit(unit);
|
|
ResourceMgrTask::instance()->requestClearCache(&unit);
|
|
}
|
|
}
|
|
|
|
namespace detail::cache {
|
|
struct ForEachContextData {
|
|
ForEachContextData() = default;
|
|
virtual ~ForEachContextData() = default;
|
|
virtual void invoke(const util::StrTreeMapKey&, ResourceUnit*& value) {
|
|
(value->getCache()->*fn)(value);
|
|
}
|
|
|
|
void (Cache::*fn0)(ResourceUnit* unit);
|
|
void (Cache::*fn)(ResourceUnit* unit);
|
|
};
|
|
KSYS_CHECK_SIZE_NX150(ForEachContextData, 0x28);
|
|
|
|
struct ForEachContext {
|
|
void deleteUnit(util::StrTreeMapNode* node_) {
|
|
auto* node = static_cast<ResourceUnitMapNode*>(node_);
|
|
data->invoke(node->key(), node->getUnit());
|
|
}
|
|
|
|
ForEachContextData* data;
|
|
};
|
|
KSYS_CHECK_SIZE_NX150(ForEachContext, 0x8);
|
|
} // namespace detail::cache
|
|
|
|
void Cache::eraseUnits() {
|
|
using namespace detail::cache;
|
|
ForEachContextData data{};
|
|
data.fn = &Cache::removeUnitAndClearCache_;
|
|
auto lock = sead::makeScopedLock(gCacheCriticalSection);
|
|
ForEachContext context{&data};
|
|
sead::Delegate1<ForEachContext, util::StrTreeMapNode*> delegate{&context,
|
|
&ForEachContext::deleteUnit};
|
|
mMap.forEach(delegate);
|
|
}
|
|
|
|
} // namespace ksys::res
|