diff --git a/data/uking_functions.csv b/data/uking_functions.csv index 8a5bd0a1..dd871ee9 100644 --- a/data/uking_functions.csv +++ b/data/uking_functions.csv @@ -84776,7 +84776,7 @@ 0x0000007101049af8,sub_7101049AF8,360, 0x0000007101049c60,sub_7101049C60,140, 0x0000007101049cec,sub_7101049CEC,436, -0x0000007101049ea0,_ZN4sead15FixedSafeStringILi135EEaSERKNS_14SafeStringBaseIcEE,372, +0x0000007101049ea0,xxx,372, 0x000000710104a014,sub_710104A014,68, 0x000000710104a058,sub_710104A058,64, 0x000000710104a098,sub_710104A098,60, @@ -89624,8 +89624,8 @@ 0x00000071011c0168,StringMap::forEach,116, 0x00000071011c01dc,sub_71011C01DC,48, 0x00000071011c020c,sub_71011C020C,92, -0x00000071011c0268,sub_71011C0268,8, -0x00000071011c0270,sub_71011C0270,8, +0x00000071011c0268,sub_71011C0268,8,_ZNK4sead10IDelegate1IPN4ksys4util14StrTreeMapNodeEE9isNoDummyEv +0x00000071011c0270,sub_71011C0270,8,_ZNK4sead10IDelegate1IPN4ksys4util14StrTreeMapNodeEE5cloneEPNS_4HeapE 0x00000071011c0278,sub_71011C0278,32, 0x00000071011c0298,ActorJobLink::ctor,32,_ZN4ksys3act15BaseProcJobLinkC1EPNS0_8BaseProcEh 0x00000071011c02b8,ActorJobListsForPriority::getJobFromFront,52, @@ -90832,25 +90832,26 @@ 0x0000007101202f2c,ResourceLoadArgBase::dtor,20,_ZN4ksys3res12ILoadRequestD1Ev 0x0000007101202f40,ResourceLoadArg3::dtorDelete,4,_ZN4ksys3res23SimplePackedLoadRequestD0Ev 0x0000007101202f44,sub_7101202F44,140,_ZNK4sead15RuntimeTypeInfo6DeriveIN4ksys3res12ILoadRequestEE9isDerivedEPKNS0_9InterfaceE -0x0000007101202fd0,sub_7101202FD0,32, +0x0000007101202fd0,sub_7101202FD0,32,_ZN4ksys3res6detail5cache18ForEachContextData6invokeERKNS_4util13StrTreeMapKeyERPNS0_12ResourceUnitE 0x0000007101202ff0,Struct18::ctor,32,_ZN4ksys3res5CacheC1Ev 0x0000007101203010,nullsub_4696,4,_ZN4ksys3res5CacheD2Ev 0x0000007101203014,j__ZdlPv_1236,4,_ZN4ksys3res5CacheD0Ev 0x0000007101203018,nullsub_4697,4,_ZN4ksys3res5Cache4initEv 0x000000710120301c,Struct18::getBinder,288,_ZNK4ksys3res5Cache8findUnitERKNS_4util13StrTreeMapKeyE -0x000000710120313c,Struct18::doLoadOnThread,1008, +0x000000710120313c,Struct18::doLoadOnThread,1008,_ZN4ksys3res5Cache12loadResourceERKNS0_15ControlTaskDataE 0x000000710120352c,Struct18::eraseResFromCache,108,_ZN4ksys3res5Cache9eraseUnitEPNS0_12ResourceUnitE -0x0000007101203598,sub_7101203598,100, -0x0000007101203694,nullsub_4698,4, -0x0000007101203698,res::lockResourceCacheCS,12, -0x00000071012036a4,res::unlockResourceCacheCS,12, -0x00000071012036b0,j__ZdlPv_1237,4, -0x00000071012036b4,j__ZdlPv_1238,4, -0x00000071012036b8,_ZN4sead15FixedSafeStringILi26EEaSERKNS_14SafeStringBaseIcEE,240, -0x00000071012037a8,sub_71012037A8,28, -0x00000071012037c4,sub_71012037C4,116, -0x0000007101203838,sub_7101203838,48, -0x0000007101203868,sub_7101203868,92, +0x0000007101203598,sub_7101203598,100,_ZN4ksys3res5Cache24removeUnitAndClearCache_EPNS0_12ResourceUnitE +0x00000071012035fc,_ZN4ksys3res5Cache10eraseUnitsEv,0x98,_ZN4ksys3res5Cache10eraseUnitsEv +0x0000007101203694,nullsub_4698,4,_ZN4ksys3res6detail5cache18ForEachContextDataD2Ev +0x0000007101203698,res::lockResourceCacheCS,12,_ZN4ksys3res24lockCacheCriticalSectionEv +0x00000071012036a4,res::unlockResourceCacheCS,12,_ZN4ksys3res26unlockCacheCriticalSectionEv +0x00000071012036b0,j__ZdlPv_1237,4,_ZN4ksys3res6detail5cache18ForEachContextDataD0Ev +0x00000071012036b4,j__ZdlPv_1238,4,_ZN4sead15FixedSafeStringILi135EED0Ev +0x00000071012036b8,_ZN4sead15FixedSafeStringILi135EEaSERKNS_14SafeStringBaseIcEE,240,_ZN4sead15FixedSafeStringILi135EEaSERKNS_14SafeStringBaseIcEE +0x00000071012037a8,sub_71012037A8,28,_ZN4ksys3res6detail5cache14ForEachContext10deleteUnitEPNS_4util14StrTreeMapNodeE +0x00000071012037c4,sub_71012037C4,116,_ZN4sead11TreeMapImplIN4ksys4util13StrTreeMapKeyEE7forEachIZNKS_16IntrusiveTreeMapIS3_NS1_3res19ResourceUnitMapNodeEE7forEachINS_9Delegate1INS7_6detail5cache14ForEachContextEPNS2_14StrTreeMapNodeEEEEEvRKT_EUlPSI_E_EEvPNS_11TreeMapNodeIS3_EESK_ +0x0000007101203838,sub_7101203838,48,_ZN4sead9Delegate1IN4ksys3res6detail5cache14ForEachContextEPNS1_4util14StrTreeMapNodeEE6invokeES8_ +0x0000007101203868,sub_7101203868,92,_ZNK4sead9Delegate1IN4ksys3res6detail5cache14ForEachContextEPNS1_4util14StrTreeMapNodeEE5cloneEPNS_4HeapE 0x00000071012038c4,sub_71012038C4,76,_GLOBAL__sub_I_resCacheCriticalSection.cpp 0x0000007101203910,nullsub_5571,4, 0x0000007101203914,sub_7101203914,228, diff --git a/lib/sead b/lib/sead index 8ca05884..776e3177 160000 --- a/lib/sead +++ b/lib/sead @@ -1 +1 @@ -Subproject commit 8ca05884c751b7211d051ec8c0700ae202a54860 +Subproject commit 776e3177769a7f0156829c4dba529ded92cc01b1 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 #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(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 delegate{&context, + &ForEachContext::deleteUnit}; + mMap.forEach(delegate); +} + } // namespace ksys::res diff --git a/src/KingSystem/Resource/resCache.h b/src/KingSystem/Resource/resCache.h index a0a2da91..3f01b5d9 100644 --- a/src/KingSystem/Resource/resCache.h +++ b/src/KingSystem/Resource/resCache.h @@ -1,13 +1,14 @@ #pragma once #include +#include "KingSystem/Resource/resHandle.h" #include "KingSystem/Resource/resUnit.h" #include "KingSystem/Utils/StrTreeMap.h" #include "KingSystem/Utils/Types.h" namespace ksys::res { -struct LoadContext; +class ControlTaskData; class Cache : public sead::hostio::Node { public: @@ -15,14 +16,16 @@ public: virtual ~Cache() = default; void init(); - ResourceUnit* findUnit(const ResourceUnitMapNode::KeyType& key) const; - void eraseUnit(ResourceUnit* unit); + Handle::Status loadResource(const ControlTaskData& data); - // FIXME: return type - s32 loadResource(const LoadContext& context); + void eraseUnit(ResourceUnit* unit); + void eraseUnits(); private: + void removeUnitAndClearCache_(ResourceUnit* unit); + + // This seems to be unused. [[maybe_unused]] u8 _8 = 2; util::StrTreeMap mMap; }; diff --git a/src/KingSystem/Resource/resCacheCriticalSection.cpp b/src/KingSystem/Resource/resCacheCriticalSection.cpp index eb2a12db..e6bf0138 100644 --- a/src/KingSystem/Resource/resCacheCriticalSection.cpp +++ b/src/KingSystem/Resource/resCacheCriticalSection.cpp @@ -6,4 +6,12 @@ namespace ksys::res { [[maybe_unused]] static util::InitTimeInfo sInitTimeInfo; sead::CriticalSection gCacheCriticalSection; +void lockCacheCriticalSection() { + gCacheCriticalSection.lock(); +} + +void unlockCacheCriticalSection() { + gCacheCriticalSection.unlock(); +} + } // namespace ksys::res diff --git a/src/KingSystem/Resource/resCacheCriticalSection.h b/src/KingSystem/Resource/resCacheCriticalSection.h index 339a34be..89f37497 100644 --- a/src/KingSystem/Resource/resCacheCriticalSection.h +++ b/src/KingSystem/Resource/resCacheCriticalSection.h @@ -6,4 +6,7 @@ namespace ksys::res { extern sead::CriticalSection gCacheCriticalSection; +void lockCacheCriticalSection(); +void unlockCacheCriticalSection(); + } // namespace ksys::res diff --git a/src/KingSystem/Resource/resHandle.h b/src/KingSystem/Resource/resHandle.h index f31dcb67..c6747b7e 100644 --- a/src/KingSystem/Resource/resHandle.h +++ b/src/KingSystem/Resource/resHandle.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -21,6 +22,14 @@ class Handle { public: enum class Status { _0 = 0, + _1 = 1, + _2 = 2, + _3 = 3, + _4 = 4, + _5 = 5, + _6 = 6, + _7 = 7, + _8 = 8, }; Handle(); @@ -35,6 +44,10 @@ public: void setUnit(ResourceUnit* unit) { mUnit = unit; } + bool isLinked() const { return mListNode.isLinked(); } + + static size_t getListNodeOffset() { return offsetof(Handle, mListNode); } + private: enum class Flag : u8 { _1 = 0x1, @@ -44,8 +57,7 @@ private: Status mStatus = Status::_0; ResourceUnit* mUnit = nullptr; util::ManagedTaskHandle mTaskHandle; - void* _40 = nullptr; - void* _48 = nullptr; + sead::ListNode mListNode; }; KSYS_CHECK_SIZE_NX150(Handle, 0x50); diff --git a/src/KingSystem/Resource/resResourceMgrTask.h b/src/KingSystem/Resource/resResourceMgrTask.h index 18ea07f0..08051522 100644 --- a/src/KingSystem/Resource/resResourceMgrTask.h +++ b/src/KingSystem/Resource/resResourceMgrTask.h @@ -1,5 +1,7 @@ #pragma once +#include "KingSystem/Resource/resUnit.h" + namespace sead { class Heap; } @@ -24,6 +26,23 @@ public: util::TaskThread* makeResourceLoadingThread(sead::Heap* heap, bool use_game_task_thread); + struct GetUnitArg { + const ResourceUnit::InitArg* unit_init_arg; + OverlayArena* arena; + }; + ResourceUnit* clearCachesAndGetUnit(const GetUnitArg& arg); + + void eraseUnit(ResourceUnit* unit); + + struct ClearCacheArg { + ResourceUnit* unit; + }; + void clearCache(ClearCacheArg& arg, void* x = nullptr); + + bool dropSFromExtensionIfNeeded(const sead::SafeString& path, + sead::BufferedSafeString& new_path, s32 dot_idx, + const sead::SafeString& extension); + private: static ResourceMgrTask* sInstance; }; diff --git a/src/KingSystem/Resource/resUnit.cpp b/src/KingSystem/Resource/resUnit.cpp index 8ddc4f34..1c2f0e1c 100644 --- a/src/KingSystem/Resource/resUnit.cpp +++ b/src/KingSystem/Resource/resUnit.cpp @@ -78,7 +78,13 @@ bool ResourceUnit::init(const ResourceUnit::InitArg& arg) { mFlags.change(Flag::_2, arg.set_flag_2); mFlags.change(Flag::_4, arg.set_flag_4); +#ifdef MATCHING_HACK_NX_CLANG + mStatusFlags.change(StatusFlag::_20000, + arg.load_req_field_26 && + !*static_cast(&arg.load_req_field_28)); +#else mStatusFlags.change(StatusFlag::_20000, arg.load_req_field_26 && !arg.load_req_field_28); +#endif mStatusFlags.change(StatusFlag::_40000, arg.load_req->_27); mStatusFlags.change(StatusFlag::HasHeap, arg.heap != nullptr); mStatusFlags.change(StatusFlag::_80000, arg.load_req_field_28); diff --git a/src/KingSystem/Resource/resUnit.h b/src/KingSystem/Resource/resUnit.h index 4f81f919..67a615f2 100644 --- a/src/KingSystem/Resource/resUnit.h +++ b/src/KingSystem/Resource/resUnit.h @@ -18,12 +18,15 @@ class DirectResource; class FileDevice; } // namespace sead +namespace ksys { +class OverlayArena; +} + namespace ksys::res { class Cache; class Handle; class LoadRequest; -class OverlayArena; class ResourceUnit; class ResourceUnitMapNode : public util::StrTreeMapNode { @@ -32,6 +35,7 @@ public: ~ResourceUnitMapNode() override { ; } void erase_() override { util::StrTreeMapNode::erase_(); } + ResourceUnit*& getUnit() { return mUnit; } ResourceUnit* getUnit() const { return mUnit; } util::StrTreeMapKey& key() { return mKey; } const util::StrTreeMapKey& key() const { return mKey; } @@ -63,22 +67,38 @@ public: }; struct InitArg { - bool set_flag_1; - bool set_flag_2; - bool set_flag_4; - sead::Atomic load_req_field_26; - sead::Atomic load_req_field_28; - Handle* handle; - Cache* cache; - void* _18; - sead::Heap* heap; - LoadRequest* load_req; - OverlayArena* arena; - u32 alloc_size; + InitArg() = default; + InitArg(bool set_flag_1, bool set_flag_2, bool set_flag_4, bool load_req_field_26, + bool load_req_field_28, Handle* handle, Cache* cache, void* _18, sead::Heap* heap, + const LoadRequest* load_req, OverlayArena* arena, u32 alloc_size, + sead::SafeString path) + : set_flag_1(set_flag_1), set_flag_2(set_flag_2), set_flag_4(set_flag_4), + load_req_field_26(load_req_field_26), load_req_field_28(load_req_field_28), + handle(handle), cache(cache), _18(_18), heap(heap), load_req(load_req), arena(arena), + alloc_size(alloc_size), path(path) {} + + bool set_flag_1 = false; + bool set_flag_2 = false; + bool set_flag_4 = false; + bool load_req_field_26 = false; + bool load_req_field_28 = false; + Handle* handle = nullptr; + Cache* cache = nullptr; + void* _18 = nullptr; + sead::Heap* heap = nullptr; + const LoadRequest* load_req = nullptr; + OverlayArena* arena = nullptr; + u32 alloc_size = 0; sead::SafeString path; }; KSYS_CHECK_SIZE_NX150(InitArg, 0x50); + struct RequestLoadArg { + u8 lane_id; + bool has_handle; + }; + KSYS_CHECK_SIZE_NX150(RequestLoadArg, 0x2); + explicit ResourceUnit(const InitArg& arg); ResourceUnit(); virtual ~ResourceUnit(); @@ -107,11 +127,16 @@ public: bool isLinkedToCache() const; void setIsLinkedToCache(bool linked); + Cache* getCache() const { return mCache; } const auto& getCacheKey() const { return mMapNode.key(); } void removeFromCache(); bool removeTask3FromQueue(); + void requestLoad(const RequestLoadArg& arg); + + bool isStatusFlag1000Set() const; + static size_t getArenaUnitListNodeOffset() { return offsetof(ResourceUnit, mArenaUnitListNode); } @@ -125,6 +150,7 @@ public: } private: + friend class Cache; friend class Handle; enum class CacheFlag : u8 { @@ -142,6 +168,7 @@ private: BufferSizeIsNonZero = 0x10, LoadFromArchive = 0x20, LoadReqField24IsTrue = 0x40, + _80 = 0x80, FailedMaybe = 0x100, FileSizeIsZero = 0x200, FileSizeExceedsAllocSize = 0x800, diff --git a/src/KingSystem/Utils/StrTreeMap.h b/src/KingSystem/Utils/StrTreeMap.h index 62c4dd09..52f72f4d 100644 --- a/src/KingSystem/Utils/StrTreeMap.h +++ b/src/KingSystem/Utils/StrTreeMap.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace ksys::util { @@ -10,7 +11,7 @@ class StrTreeMapKey { public: StrTreeMapKey() = default; StrTreeMapKey(u32 key_hash, const sead::SafeString& key) : mKeyHash(key_hash), mKey(key) {} - explicit StrTreeMapKey(const sead::SafeString& key) + StrTreeMapKey(const sead::SafeString& key) : StrTreeMapKey(sead::HashCRC32::calcStringHash(key.cstr()), key) {} const sead::SafeString& key() const { return mKey; }