diff options
| author | Léo Lam <leo@leolam.fr> | 2020-09-27 16:11:56 +0200 |
|---|---|---|
| committer | Léo Lam <leo@leolam.fr> | 2020-10-04 02:00:38 +0200 |
| commit | 12e6979b6c175d7b9d8b871cb50bbbe9b545a74e (patch) | |
| tree | 23c778e41c0c96eeabaa5e60341ee2f127cfc59f /src/KingSystem/Resource/resCache.cpp | |
| parent | f8db98617c3ccdac22276a2f0e7e9c033b4b62f2 (diff) | |
ksys/res: Implement more Cache functions
Diffstat (limited to 'src/KingSystem/Resource/resCache.cpp')
| -rw-r--r-- | src/KingSystem/Resource/resCache.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/src/KingSystem/Resource/resCache.cpp b/src/KingSystem/Resource/resCache.cpp index 4f7abb62..55535d65 100644 --- a/src/KingSystem/Resource/resCache.cpp +++ b/src/KingSystem/Resource/resCache.cpp @@ -1,6 +1,10 @@ #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 { @@ -14,6 +18,125 @@ ResourceUnit* Cache::findUnit(const util::StrTreeMapNode::KeyType& key) const { 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->isStatusFlag1000Set()) { + 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::RequestLoadArg load_arg; + load_arg.lane_id = lane_id; + load_arg.has_handle = data.mResLoadReq._8; + result->requestLoad(load_arg); + return Handle::Status::_7; +} + void Cache::eraseUnit(ResourceUnit* unit) { auto lock = sead::makeScopedLock(gCacheCriticalSection); if (unit->isLinkedToCache()) { @@ -22,4 +145,48 @@ void Cache::eraseUnit(ResourceUnit* unit) { } } +void Cache::removeUnitAndClearCache_(ResourceUnit* unit) { + ResourceMgrTask::ClearCacheArg arg{unit}; + unit->mStatusFlags.reset(ResourceUnit::StatusFlag::_20000); + if (unit->isStatusFlag8000Set()) { + ResourceMgrTask::instance()->eraseUnit(unit); + ResourceMgrTask::instance()->clearCache(arg); + } +} + +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 |
