From 5a4e6f3254b7b463a4667cc2796c6e617a6b8a8d Mon Sep 17 00:00:00 2001 From: jdflyer Date: Wed, 19 Aug 2026 23:47:57 -0700 Subject: [PATCH] Mod SDK: Arc Overlays (#2280) * Mod SDK: Arc Overlays * Use DVD Functions for Arc overlays * Re-add Fetchcontent for json.hpp * Fix build for actions * Make sure buffer usues aligned length * Add include so msvc compiles * Arc Overlays fixes: remove _arc suffix, handle getting overlayed file size from arcs, copy data correctly, etc. * Reload overlayed data during loading screens * Rename sync function * Free overlayed files on load instead of re-loading them * Rework arc overlays & load overlay files into host memory --------- Co-authored-by: Luke Street --- docs/modding.md | 14 +- extern/aurora | 2 +- .../include/JSystem/JKernel/JKRArchive.h | 54 +++- libs/JSystem/src/JAudio2/JASResArcLoader.cpp | 9 +- libs/JSystem/src/JKernel/JKRAramArchive.cpp | 20 ++ libs/JSystem/src/JKernel/JKRArchivePri.cpp | 268 ++++++++++++++++++ libs/JSystem/src/JKernel/JKRArchivePub.cpp | 22 ++ libs/JSystem/src/JKernel/JKRCompArchive.cpp | 27 ++ libs/JSystem/src/JKernel/JKRDvdArchive.cpp | 20 ++ libs/JSystem/src/JKernel/JKRMemArchive.cpp | 28 ++ src/d/d_lib.cpp | 5 + src/d/d_resorce.cpp | 4 +- src/dusk/mods/svc/overlay.cpp | 30 +- 13 files changed, 474 insertions(+), 29 deletions(-) diff --git a/docs/modding.md b/docs/modding.md index b4508d46d4..d9726e3dc4 100644 --- a/docs/modding.md +++ b/docs/modding.md @@ -262,14 +262,16 @@ Installs hooks on game functions and resolves symbols by name. You'll rarely cal ### OverlayService (`mods/svc/overlay.h`) Registers DVD file overlays at runtime: the dynamic counterpart to the static `overlay/` directory (see -[Asset Overlays](#asset-overlays)). Overlay a disc path with a file from your bundle, or with a caller-owned buffer +[Asset Overlays](#asset-overlays)). Overlay a disc path with a file from your bundle, a file within an archive, +or with a caller-owned buffer (copied on registration): ```cpp IMPORT_SERVICE(OverlayService, svc_overlay); OverlayHandle handle = 0; -svc_overlay->add_file(mod_ctx, "/res/Msgus.arc", "res/replacement.arc", &handle); +svc_overlay->add_file(mod_ctx, "/Movie/demo_movie98_00.thp", "res/replacement.thp", &handle); // Replaces the demo movie +svc_overlay->add_file(mod_ctx, "/res/Object/Kmdl/archive/bmwr/al.bmd", "res/link_model.bmd", &handle); // Replaces link's model svc_overlay->add_buffer(mod_ctx, "/generated.txt", data, size, nullptr); svc_overlay->remove(mod_ctx, handle); ``` @@ -278,6 +280,9 @@ svc_overlay->remove(mod_ctx, handle); on the disc are added as new files. Changes are applied at the next frame boundary, and data the game already read stays in memory until the file is re-read: sometimes a scene reload, and in the worst case, a full restart. +Dusklight reloads core archive files during scene transitions so modifications to Link, Midna or other globally-loaded +data get refreshed without a full restart. + See [Asset Overlays](#asset-overlays) for priority and conflict handling. ### TextureService (`mods/svc/texture.h`) @@ -882,6 +887,11 @@ For reference parameters (e.g. `const cXyz& pos`), `arg_ref` yields a dire Files placed under `overlay/` in the `.dusk` archive override game files at the corresponding path, equivalent to replacing files in the .iso. This requires no code: an archive with just `mod.json` and `overlay/` is a complete mod. +To replace a file within an `.arc` archive, replace the archive suffix with a directory and place the replacement at +its path within the archive. + +- `overlay/Audiores/Stream/menu_select.ast` replaces the main title's audio stream. +- `overlay/res/Layout/main2D/main2d/timg/midona64.bti` replaces Midna's UI icon inside `main2D.arc`. Files placed under `textures/` register as texture replacements, and act just like the user's general `texture_replacements/` directory: Dolphin-style naming, matched by texture hash diff --git a/extern/aurora b/extern/aurora index cf3ffc9886..0f15fb65d1 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit cf3ffc98865ab209b02cb3eefe778a3f057c6db4 +Subproject commit 0f15fb65d13e4a7145667934e0291bad09806bfd diff --git a/libs/JSystem/include/JSystem/JKernel/JKRArchive.h b/libs/JSystem/include/JSystem/JKernel/JKRArchive.h index 4ac5de7a46..bcedc7834e 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRArchive.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRArchive.h @@ -6,11 +6,17 @@ #include "global.h" #include "helpers/endian.h" +#if TARGET_PC +#include +#include +#include +#endif + class JKRHeap; /** * @ingroup jsystem-jkernel - * + * */ struct SArcHeader { /* 0x00 */ BE(u32) signature; @@ -25,7 +31,7 @@ struct SArcHeader { /** * @ingroup jsystem-jkernel - * + * */ struct SArcDataInfo { /* 0x00 */ BE(u32) num_nodes; @@ -59,7 +65,7 @@ extern u32 sCurrentDirID__10JKRArchive; // JKRArchive::sCurrentDirID /** * @ingroup jsystem-jkernel - * + * */ class JKRArchive : public JKRFileLoader { public: @@ -192,9 +198,7 @@ public: u32 countFile() const { return mArcInfoBlock->num_file_entries; } s32 countDirectory() const { return mArcInfoBlock->num_nodes; } u8 getMountMode() const { return mMountMode; } - bool isFileEntry(u32 param_0) const { - return getFileAttribute(param_0) & 1; - } + bool isFileEntry(u32 param_0) const { return getFileAttribute(param_0) & 1; } public: /* 0x00 */ // vtable @@ -210,7 +214,36 @@ public: /* 0x54 */ const char* mStringTable; #if TARGET_PC + u32 getFileSize(SDIFileEntry* entry) const; + void* getOverlayData(SDIFileEntry* entry, u32* outSize); + bool getOverlayFileSize(SDIFileEntry* entry, u32* outSize) const; + static void notifyOverlayFilesChanged(); + +protected: void** mFileData; + + struct ArcOverlayResource { + u32 entryIndex; + u32 size; + u64 generation; + }; + + // Resource pointers remain valid until removed through the JKR resource APIs. + // That way, any pointers to old overlay data remain valid even when the overlay changed. + std::unordered_map mArcOverlayResources; + mutable std::unordered_map mActiveArcOverlayResources; + mutable std::string mArcOverlaysPath; + mutable std::unordered_map mIdxToPathMap; + mutable bool mArcOverlaysPathResolved = false; + + bool buildArcOverlaysPath() const; + void buildIndexToPathMap(u32 dirIndex, const std::string& currentPath) const; + bool getOverlayPath(SDIFileEntry* entry, std::string& path) const; + void* getActiveOverlayData(SDIFileEntry* entry, u32* outSize) const; + bool copyOverlayData(void* buffer, u32 bufferSize, SDIFileEntry* entry, u32* outSize); + bool getOverlayResourceSize(const void* data, u32* outSize) const; + bool removeOverlayResource(void* resource, bool freeResource); + void removeAllOverlayResources(); #endif protected: @@ -234,7 +267,7 @@ public: } else if (attr & JKRARCHIVE_ATTR_YAZ0) { return COMPRESSION_YAZ0; } else { - return COMPRESSION_YAY0; + return COMPRESSION_YAY0; } } @@ -243,6 +276,9 @@ public: protected: static DUSK_GAME_DATA u32 sCurrentDirID; +#if TARGET_PC + static std::atomic sArcOverlayGeneration; +#endif }; inline JKRCompression JKRConvertAttrToCompressionType(int attr) { @@ -261,8 +297,8 @@ inline bool JKRRemoveResource(void* resource, JKRFileLoader* fileLoader) { return JKRFileLoader::removeResource(resource, fileLoader); } -inline JKRArchive* JKRMountArchive(void* ptr, JKRHeap* heap, - JKRArchive::EMountDirection mountDirection) { +inline JKRArchive* JKRMountArchive( + void* ptr, JKRHeap* heap, JKRArchive::EMountDirection mountDirection) { return JKRArchive::mount(ptr, heap, mountDirection); } diff --git a/libs/JSystem/src/JAudio2/JASResArcLoader.cpp b/libs/JSystem/src/JAudio2/JASResArcLoader.cpp index a55c73469c..57e6742c51 100644 --- a/libs/JSystem/src/JAudio2/JASResArcLoader.cpp +++ b/libs/JSystem/src/JAudio2/JASResArcLoader.cpp @@ -17,7 +17,7 @@ size_t JASResArcLoader::getResSize(JKRArchive const* i_archiveP, u16 i_resourceI return 0; } - return file->data_size; + return DUSK_IF_ELSE(i_archiveP->getFileSize(file), file->data_size); } size_t JASResArcLoader::getResMaxSize(JKRArchive const* i_archiveP) { @@ -27,9 +27,16 @@ size_t JASResArcLoader::getResMaxSize(JKRArchive const* i_archiveP) { for (index = 0; index < fileEntries; index++) { JKRArchive::SDIFileEntry* file = i_archiveP->findIdxResource(index); if (file) { +#if TARGET_PC + const u32 fileSize = i_archiveP->getFileSize(file); + if (maxSize < fileSize) { + maxSize = fileSize; + } +#else if (maxSize < file->data_size) { maxSize = file->data_size; } +#endif } } diff --git a/libs/JSystem/src/JKernel/JKRAramArchive.cpp b/libs/JSystem/src/JKernel/JKRAramArchive.cpp index d6923ee06b..fcb54271e4 100644 --- a/libs/JSystem/src/JKernel/JKRAramArchive.cpp +++ b/libs/JSystem/src/JKernel/JKRAramArchive.cpp @@ -196,6 +196,13 @@ cleanup: void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) { JUT_ASSERT(442, isMounted()); + +#if TARGET_PC + if (void* data = getOverlayData(pEntry, pOutSize); data != nullptr) { + return data; + } +#endif + u32 outSize; u8* outBuf; if (pOutSize == NULL) { @@ -231,6 +238,13 @@ void* JKRAramArchive::fetchResource(SDIFileEntry* pEntry, u32* pOutSize) { void* JKRAramArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* pEntry, u32* resourceSize) { JUT_ASSERT(515, isMounted()); + +#if TARGET_PC + if (copyOverlayData(buffer, bufferSize, pEntry, resourceSize)) { + return buffer; + } +#endif + u32 size = pEntry->data_size; if (size > bufferSize) { size = bufferSize; @@ -337,6 +351,12 @@ u32 JKRAramArchive::getExpandedResSize(const void* ptr) const { return this->getResSize(ptr); } +#if TARGET_PC + if (u32 size; getOverlayResourceSize(ptr, &size)) { + return size; + } +#endif + JKRArchive::SDIFileEntry* entry = this->findPtrResource(ptr); if (entry == NULL) { return 0xFFFFFFFF; diff --git a/libs/JSystem/src/JKernel/JKRArchivePri.cpp b/libs/JSystem/src/JKernel/JKRArchivePri.cpp index a14ab763b0..eed317a357 100644 --- a/libs/JSystem/src/JKernel/JKRArchivePri.cpp +++ b/libs/JSystem/src/JKernel/JKRArchivePri.cpp @@ -7,6 +7,37 @@ #if TARGET_PC #include +#include +#include +#include +#include +#include +#include "JSystem/JKernel/JKRDvdRipper.h" +#if _WIN32 +#include +#endif + +std::atomic JKRArchive::sArcOverlayGeneration{0}; + +namespace { + +void* alloc_overlay_buffer(u32 size) { +#if _WIN32 + return _aligned_malloc(size, alignof(std::max_align_t)); +#else + return std::malloc(size); +#endif +} + +void free_overlay_buffer(void* data) { +#if _WIN32 + _aligned_free(data); +#else + std::free(data); +#endif +} + +} // namespace #endif DUSK_GAME_DATA u32 JKRArchive::sCurrentDirID; @@ -42,6 +73,7 @@ JKRArchive::JKRArchive(s32 entryNumber, JKRArchive::EMountMode mountMode) { JKRArchive::~JKRArchive() { #if TARGET_PC + removeAllOverlayResources(); if (mFileData != nullptr) { JKRHeap::getSystemHeap()->free(mFileData); mFileData = nullptr; @@ -270,4 +302,240 @@ void JKRArchive::initFileDataPointers() { mFiles[i].index = i; } } + +void JKRArchive::notifyOverlayFilesChanged() { + sArcOverlayGeneration.fetch_add(1, std::memory_order_release); +} + +bool JKRArchive::buildArcOverlaysPath() const { + if (mArcOverlaysPathResolved) { + return !mArcOverlaysPath.empty(); + } + mArcOverlaysPathResolved = true; + + if (mEntryNum < 0) { + return false; + } + + char pathBuffer[1024]; + if (!DVDConvertEntrynumToPath(mEntryNum, pathBuffer, sizeof(pathBuffer))) { + return false; + } + + std::string path{pathBuffer}; + constexpr std::string_view extension{".arc"}; + if (path.size() < extension.size() || + path.compare(path.size() - extension.size(), extension.size(), extension) != 0) + { + return false; + } + path.resize(path.size() - extension.size()); + path.push_back('/'); + + mArcOverlaysPath = std::move(path); + return true; +} + +void JKRArchive::buildIndexToPathMap(u32 dirIndex, const std::string& currentPath) const { + const SDIDirEntry& dir = mNodes[dirIndex]; + for (int i = 0; i < dir.num_entries; i++) { + const SDIFileEntry& entry = mFiles[dir.first_file_index + i]; + std::string entryName{&mStringTable[entry.getNameOffset()]}; + if (entryName == "." || entryName == "..") { + continue; + } + if (entry.isDirectory()) { + buildIndexToPathMap(entry.data_offset, currentPath + entryName + "/"); + } else { + mIdxToPathMap[entry.index] = currentPath + entryName; + } + } +} + +bool JKRArchive::getOverlayPath(SDIFileEntry* entry, std::string& path) const { + if (entry == nullptr || !buildArcOverlaysPath()) { + return false; + } + + if (mIdxToPathMap.empty()) { + buildIndexToPathMap(0, std::string{&mStringTable[mNodes[0].name_offset]} + "/"); + } + + const auto pathIt = mIdxToPathMap.find(entry->index); + if (pathIt == mIdxToPathMap.end()) { + return false; + } + + path = mArcOverlaysPath + pathIt->second; + return true; +} + +void* JKRArchive::getActiveOverlayData(SDIFileEntry* entry, u32* outSize) const { + const auto activeIt = mActiveArcOverlayResources.find(entry->index); + if (activeIt == mActiveArcOverlayResources.end()) { + return nullptr; + } + + const auto resourceIt = mArcOverlayResources.find(activeIt->second); + const u64 generation = sArcOverlayGeneration.load(std::memory_order_acquire); + if (resourceIt == mArcOverlayResources.end() || resourceIt->second.generation != generation) { + // Keep the allocation owned so raw pointers returned by earlier fetches remain valid. + mActiveArcOverlayResources.erase(activeIt); + return nullptr; + } + + if (outSize != nullptr) { + *outSize = resourceIt->second.size; + } + return resourceIt->first; +} + +void* JKRArchive::getOverlayData(SDIFileEntry* entry, u32* outSize) { + if (entry == nullptr) { + return nullptr; + } + + if (void* data = getActiveOverlayData(entry, outSize)) { + return data; + } + + std::string path; + if (!getOverlayPath(entry, path)) { + return nullptr; + } + + constexpr u32 alignmentMask = 0x1f; + const u64 generation = sArcOverlayGeneration.load(std::memory_order_acquire); + DVDFileInfo fileInfo{}; + if (!DVDOpen(path.c_str(), &fileInfo)) { + return nullptr; + } + + const u32 logicalSize = fileInfo.length; + if (logicalSize > static_cast(std::numeric_limits::max()) - alignmentMask) { + DVDClose(&fileInfo); + return nullptr; + } + + const u32 readSize = ALIGN_NEXT(logicalSize, 0x20); + const u32 allocationSize = readSize == 0 ? 1 : readSize; + void* data = alloc_overlay_buffer(allocationSize); + if (data == nullptr) { + DVDClose(&fileInfo); + return nullptr; + } + + const s32 status = DVDReadPrio(&fileInfo, data, readSize, 0, 2); + DVDClose(&fileInfo); + if (status < DVD_RESULT_GOOD || static_cast(status) != logicalSize) { + free_overlay_buffer(data); + return nullptr; + } + + mArcOverlayResources.emplace(data, ArcOverlayResource{ + .entryIndex = entry->index, + .size = logicalSize, + .generation = generation, + }); + mActiveArcOverlayResources[entry->index] = data; + + if (outSize != nullptr) { + *outSize = logicalSize; + } + return data; +} + +bool JKRArchive::copyOverlayData(void* buffer, u32 bufferSize, SDIFileEntry* entry, u32* outSize) { + u32 overlaySize; + const void* overlayData = getOverlayData(entry, &overlaySize); + if (overlayData == nullptr) { + return false; + } + + const u32 copySize = overlaySize < bufferSize ? overlaySize : bufferSize; + if (copySize != 0) { + memcpy(buffer, overlayData, copySize); + } + if (outSize != nullptr) { + *outSize = copySize; + } + return true; +} + +bool JKRArchive::getOverlayResourceSize(const void* data, u32* outSize) const { + const auto resourceIt = mArcOverlayResources.find(const_cast(data)); + if (resourceIt == mArcOverlayResources.end()) { + return false; + } + + if (outSize != nullptr) { + *outSize = resourceIt->second.size; + } + return true; +} + +bool JKRArchive::getOverlayFileSize(SDIFileEntry* entry, u32* outSize) const { + if (entry == nullptr) { + return false; + } + + u32 activeSize; + if (getActiveOverlayData(entry, &activeSize) != nullptr) { + if (outSize != nullptr) { + *outSize = activeSize; + } + return true; + } + + std::string path; + if (!getOverlayPath(entry, path)) { + return false; + } + + DVDFileInfo fileInfo{}; + if (!DVDOpen(path.c_str(), &fileInfo)) { + return false; + } + + if (outSize != nullptr) { + *outSize = fileInfo.length; + } + DVDClose(&fileInfo); + return true; +} + +u32 JKRArchive::getFileSize(SDIFileEntry* entry) const { + u32 size; + if (getOverlayFileSize(entry, &size)) { + return size; + } + return entry != nullptr ? entry->getSize() : 0; +} + +bool JKRArchive::removeOverlayResource(void* resource, bool freeResource) { + const auto resourceIt = mArcOverlayResources.find(resource); + if (resourceIt == mArcOverlayResources.end()) { + return false; + } + + const auto activeIt = mActiveArcOverlayResources.find(resourceIt->second.entryIndex); + if (activeIt != mActiveArcOverlayResources.end() && activeIt->second == resource) { + mActiveArcOverlayResources.erase(activeIt); + } + + if (freeResource) { + free_overlay_buffer(resource); + } + mArcOverlayResources.erase(resourceIt); + return true; +} + +void JKRArchive::removeAllOverlayResources() { + mActiveArcOverlayResources.clear(); + for (const auto& key : mArcOverlayResources | std::views::keys) { + free_overlay_buffer(key); + } + mArcOverlayResources.clear(); +} + #endif diff --git a/libs/JSystem/src/JKernel/JKRArchivePub.cpp b/libs/JSystem/src/JKernel/JKRArchivePub.cpp index 731a049457..97306c9033 100644 --- a/libs/JSystem/src/JKernel/JKRArchivePub.cpp +++ b/libs/JSystem/src/JKernel/JKRArchivePub.cpp @@ -246,6 +246,7 @@ u32 JKRArchive::readResource(void* buffer, u32 bufferSize, u16 id) { void JKRArchive::removeResourceAll() { if (mArcInfoBlock && mMountMode != MOUNT_MEM) { + IF_DUSK(removeAllOverlayResources();) SDIFileEntry* fileEntry = mFiles; for (int i = 0; i < mArcInfoBlock->num_file_entries; i++) { if (JKAR_DATA(fileEntry)) { @@ -259,6 +260,13 @@ void JKRArchive::removeResourceAll() { bool JKRArchive::removeResource(void* resource) { JUT_ASSERT(678, resource != NULL); + +#if TARGET_PC + if (removeOverlayResource(resource, true)) { + return true; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (fileEntry == NULL) return false; @@ -270,6 +278,13 @@ bool JKRArchive::removeResource(void* resource) { bool JKRArchive::detachResource(void* resource) { JUT_ASSERT(707, resource != NULL); + +#if TARGET_PC + if (removeOverlayResource(resource, false)) { + return true; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (fileEntry == NULL) return false; @@ -280,6 +295,13 @@ bool JKRArchive::detachResource(void* resource) { u32 JKRArchive::getResSize(const void* resource) const { JUT_ASSERT(732, resource != NULL); + +#if TARGET_PC + if (u32 size; getOverlayResourceSize(resource, &size)) { + return size; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (fileEntry == NULL) return -1; diff --git a/libs/JSystem/src/JKernel/JKRCompArchive.cpp b/libs/JSystem/src/JKernel/JKRCompArchive.cpp index 716aaa2838..acac590ec0 100644 --- a/libs/JSystem/src/JKernel/JKRCompArchive.cpp +++ b/libs/JSystem/src/JKernel/JKRCompArchive.cpp @@ -226,6 +226,13 @@ bool JKRCompArchive::open(s32 entryNum) { void* JKRCompArchive::fetchResource(SDIFileEntry *fileEntry, u32 *pSize) { JUT_ASSERT(597, isMounted()); + +#if TARGET_PC + if (void* data = getOverlayData(fileEntry, pSize); data != nullptr) { + return data; + } +#endif + u32 ptrSize; u32 size = fileEntry->data_size; int compression = JKRConvertAttrToCompressionType(u8(fileEntry->type_flags_and_name_offset >> 0x18)); @@ -274,6 +281,13 @@ void *JKRCompArchive::fetchResource(void *data, u32 compressedSize, SDIFileEntry { u32 size = 0; JUT_ASSERT(708, isMounted()); + +#if TARGET_PC + if (copyOverlayData(data, compressedSize, fileEntry, pSize)) { + return data; + } +#endif + u32 fileSize = fileEntry->data_size; u32 alignedSize = ALIGN_NEXT(fileSize, 32); u32 fileFlag = fileEntry->type_flags_and_name_offset >> 0x18; @@ -319,6 +333,7 @@ void *JKRCompArchive::fetchResource(void *data, u32 compressedSize, SDIFileEntry void JKRCompArchive::removeResourceAll() { if (mArcInfoBlock != NULL && mMountMode != MOUNT_MEM) { + IF_DUSK(removeAllOverlayResources();) SDIFileEntry* fileEntry = mFiles; for (int i = 0; i < mArcInfoBlock->num_file_entries; i++) { int tmp = fileEntry->type_flags_and_name_offset >> 0x18; @@ -336,6 +351,12 @@ void JKRCompArchive::removeResourceAll() { } bool JKRCompArchive::removeResource(void* resource) { +#if TARGET_PC + if (removeOverlayResource(resource, true)) { + return true; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (!fileEntry) return false; @@ -356,6 +377,12 @@ u32 JKRCompArchive::getExpandedResSize(const void *resource) const return getResSize(resource); } +#if TARGET_PC + if (u32 size; getOverlayResourceSize(resource, &size)) { + return size; + } +#endif + SDIFileEntry *fileEntry = findPtrResource(resource); if(!fileEntry) { return 0xffffffff; diff --git a/libs/JSystem/src/JKernel/JKRDvdArchive.cpp b/libs/JSystem/src/JKernel/JKRDvdArchive.cpp index b82302c900..d599879f8f 100644 --- a/libs/JSystem/src/JKernel/JKRDvdArchive.cpp +++ b/libs/JSystem/src/JKernel/JKRDvdArchive.cpp @@ -147,6 +147,13 @@ cleanup: void* JKRDvdArchive::fetchResource(SDIFileEntry* fileEntry, u32* returnSize) { JUT_ASSERT(428, isMounted()); + +#if TARGET_PC + if (void* data = getOverlayData(fileEntry, returnSize); data != nullptr) { + return data; + } +#endif + u32 tempReturnSize; if (returnSize == NULL) { returnSize = &tempReturnSize; @@ -181,6 +188,13 @@ void* JKRDvdArchive::fetchResource(SDIFileEntry* fileEntry, u32* returnSize) { void* JKRDvdArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* fileEntry, u32* returnSize) { JUT_ASSERT(504, isMounted()); + +#if TARGET_PC + if (copyOverlayData(buffer, bufferSize, fileEntry, returnSize)) { + return buffer; + } +#endif + u32 size = fileEntry->data_size; JKRCompression fileCompression = JKRConvertAttrToCompressionType(u8(fileEntry->type_flags_and_name_offset >> 24)); @@ -344,6 +358,12 @@ u32 JKRDvdArchive::getExpandedResSize(const void* resource) const { return getResSize(resource); } +#if TARGET_PC + if (u32 size; getOverlayResourceSize(resource, &size)) { + return size; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (!fileEntry) { return -1; diff --git a/libs/JSystem/src/JKernel/JKRMemArchive.cpp b/libs/JSystem/src/JKernel/JKRMemArchive.cpp index d3b17d8a63..f0feabe3be 100644 --- a/libs/JSystem/src/JKernel/JKRMemArchive.cpp +++ b/libs/JSystem/src/JKernel/JKRMemArchive.cpp @@ -135,6 +135,13 @@ bool JKRMemArchive::open(void* buffer, u32 bufferSize, JKRMemBreakFlag flag) { void* JKRMemArchive::fetchResource(SDIFileEntry* fileEntry, u32* resourceSize) { JUT_ASSERT(555, isMounted()); + +#if TARGET_PC + if (void* data = getOverlayData(fileEntry, resourceSize); data != nullptr) { + return data; + } +#endif + if (!JKAR_DATA(fileEntry)) { JKAR_DATA(fileEntry) = mArchiveData + fileEntry->data_offset; } @@ -149,6 +156,13 @@ void* JKRMemArchive::fetchResource(SDIFileEntry* fileEntry, u32* resourceSize) { void* JKRMemArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* fileEntry, u32* resourceSize) { JUT_ASSERT(595, isMounted()); + +#if TARGET_PC + if (copyOverlayData(buffer, bufferSize, fileEntry, resourceSize)) { + return buffer; + } +#endif + u32 srcLength = fileEntry->data_size; if (srcLength > bufferSize) { srcLength = bufferSize; @@ -173,6 +187,8 @@ void* JKRMemArchive::fetchResource(void* buffer, u32 bufferSize, SDIFileEntry* f void JKRMemArchive::removeResourceAll(void) { JUT_ASSERT(642, isMounted()); + IF_DUSK(removeAllOverlayResources();) + if (mArcInfoBlock == NULL) return; if (mMountMode == MOUNT_MEM) @@ -192,6 +208,12 @@ void JKRMemArchive::removeResourceAll(void) { bool JKRMemArchive::removeResource(void* resource) { JUT_ASSERT(673, isMounted()); +#if TARGET_PC + if (removeOverlayResource(resource, true)) { + return true; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (!fileEntry) return false; @@ -231,6 +253,12 @@ u32 JKRMemArchive::fetchResource_subroutine(u8* src, u32 srcLength, u8* dst, u32 } u32 JKRMemArchive::getExpandedResSize(const void* resource) const { +#if TARGET_PC + if (u32 overlaySize; getOverlayResourceSize(resource, &overlaySize)) { + return overlaySize; + } +#endif + SDIFileEntry* fileEntry = findPtrResource(resource); if (fileEntry == NULL) return -1; diff --git a/src/d/d_lib.cpp b/src/d/d_lib.cpp index d83d4f62c0..902e6d4b24 100644 --- a/src/d/d_lib.cpp +++ b/src/d/d_lib.cpp @@ -311,6 +311,11 @@ u32 dLib_getExpandSizeFromAramArchive(JKRAramArchive* i_aramArchive, char const* JUT_ASSERT(1260, readAddress == header); JKRArchive::SDIFileEntry* entry = i_aramArchive->findFsResource(param_2, 0); JUT_ASSERT(1263, entry != NULL); +#if TARGET_PC + if (u32 size; i_aramArchive->getOverlayFileSize(entry, &size)) { + return ALIGN_NEXT(size, 32); + } +#endif u32 uVar1 = ALIGN_NEXT(JKRDecompExpandSize(header), 32); u32 uVar5 = ALIGN_NEXT(entry->data_size, 32); return uVar1 > uVar5 ? uVar1 : uVar5; diff --git a/src/d/d_resorce.cpp b/src/d/d_resorce.cpp index b224343983..97a230b2e0 100644 --- a/src/d/d_resorce.cpp +++ b/src/d/d_resorce.cpp @@ -335,7 +335,7 @@ int dRes_info_c::loadResource() { #endif void* res = mArchive->getIdxResource(fileIndex); #if TARGET_PC - u32 size = mArchive->findIdxResource(fileIndex)->data_size; + u32 size = mArchive->getFileSize(mArchive->findIdxResource(fileIndex)); std::string fileName = mArchive->mStringTable + (mArchive->findIdxResource(fileIndex)->type_flags_and_name_offset & 0xFFFFFF); DuskLog.debug("Loading Resource: {} (Size: {})", fileName, size); @@ -369,7 +369,7 @@ int dRes_info_c::loadResource() { parentHeap = NULL; } - int rt = dComIfG_setObjectRes(arcName, res, entry->data_size, parentHeap); + int rt = dComIfG_setObjectRes(arcName, res, DUSK_IF_ELSE(mArchive->getFileSize(entry),entry->data_size), parentHeap); JUT_ASSERT(788, rt); } else if (nodeType == 'BMDP') { #if DEBUG diff --git a/src/dusk/mods/svc/overlay.cpp b/src/dusk/mods/svc/overlay.cpp index 0bd79caff3..f4ad2ff6a6 100644 --- a/src/dusk/mods/svc/overlay.cpp +++ b/src/dusk/mods/svc/overlay.cpp @@ -1,8 +1,9 @@ #include "registry.hpp" #include "slot_map.hpp" -#include "aurora/dvd.h" #include +#include "JSystem/JKernel/JKRArchive.h" +#include "aurora/dvd.h" #include "dusk/mods/loader/loader.hpp" #include "mods/svc/overlay.h" @@ -24,7 +25,7 @@ constexpr borealis::Log Log{"dusk::mods::overlay"}; struct OverlayFileData { std::string bundlePath; std::shared_ptr bundle; - std::shared_ptr > buffer; + std::shared_ptr> buffer; }; // Keyed by the id passed to Aurora as per-file userdata. Guarded by s_overlayMutex: Aurora may @@ -98,6 +99,7 @@ void append_runtime_overlays(std::vector& files, LoadedMod& m for (const auto* slot : slots) { const auto id = s_nextOverlayId++; + if (slot->buffer != nullptr) { s_overlayFiles.emplace(id, OverlayFileData{{}, nullptr, slot->buffer}); } else { @@ -110,7 +112,7 @@ void append_runtime_overlays(std::vector& files, LoadedMod& m struct OpenOverlayFile { std::vector ownedData; - std::shared_ptr > shared; + std::shared_ptr> shared; size_t pos = 0; [[nodiscard]] const std::vector& data() const { @@ -200,6 +202,7 @@ void overlay_sync_files() { Log.debug("Registering {} overlay file(s).", files.size()); aurora_dvd_overlay_files(files.data(), files.size(), nullptr); + JKRArchive::notifyOverlayFilesChanged(); for (const auto& file : files) { std::free(const_cast(file.fileName)); @@ -220,13 +223,13 @@ uint64_t overlay_add_file( uint64_t overlay_add_buffer(LoadedMod& mod, std::string discPath, std::vector data) { const auto size = data.size(); - const auto handle = s_runtimeOverlays.emplace(mod, - RuntimeOverlaySlot{ - .discPath = std::move(discPath), - .buffer = std::make_shared>(std::move(data)), - .size = size, - .order = s_nextRuntimeOrder++, - }); + const auto handle = s_runtimeOverlays.emplace( + mod, RuntimeOverlaySlot{ + .discPath = std::move(discPath), + .buffer = std::make_shared>(std::move(data)), + .size = size, + .order = s_nextRuntimeOrder++, + }); s_overlaysDirty = true; return handle; } @@ -275,13 +278,12 @@ ModResult overlay_add_file( try { size = mod->bundle->getFileSize(bundlePath); } catch (const std::exception& e) { - Log.error( - "[{}] overlay add_file '{}' failed: {}", mod->metadata.id, bundlePath, e.what()); + Log.error("[{}] overlay add_file '{}' failed: {}", mod->metadata.id, bundlePath, e.what()); return MOD_UNAVAILABLE; } if (size > kMaxOverlayFileSize) { - Log.error("[{}] overlay add_file '{}' failed: file too large ({} bytes)", - mod->metadata.id, bundlePath, size); + Log.error("[{}] overlay add_file '{}' failed: file too large ({} bytes)", mod->metadata.id, + bundlePath, size); return MOD_INVALID_ARGUMENT; }