diff --git a/UnleashedRecomp/mod/mod_loader.cpp b/UnleashedRecomp/mod/mod_loader.cpp index 7c33b1a3..dd33c56d 100644 --- a/UnleashedRecomp/mod/mod_loader.cpp +++ b/UnleashedRecomp/mod/mod_loader.cpp @@ -562,14 +562,68 @@ PPC_FUNC(sub_82E0D3E8) g_userHeap.Free(newArlFileData); } +// Load elements have an unused "pretty name" field. We will use this field to store the archive file path, +// prefixed with a magic string. When the first load detects this string, it will load append archives +// and then clear the field to prevent remaining splits from loading the append archives again. +// We cannot rely on .ar.00 being the first split to be loaded, so this approach is necessary. +static thread_local uint32_t g_prefixedArFilePath = NULL; + +// Hedgehog::Database::CDatabaseLoader::LoadArchives +PPC_FUNC_IMPL(__imp__sub_82E0CC38); +PPC_FUNC(sub_82E0CC38) +{ + if (g_mods.empty()) + { + __imp__sub_82E0CC38(ctx, base); + return; + } + + auto r3 = ctx.r3; + auto r4 = ctx.r4; + auto r5 = ctx.r5; + auto r6 = ctx.r6; + auto r7 = ctx.r7; + auto r8 = ctx.r8; + + const char* arFilePath = reinterpret_cast(base + PPC_LOAD_U32(r5.u32)); + + // __HH_ALLOC + ctx.r3.u32 = 22 + strlen(arFilePath); + sub_822C0988(ctx, base); + char* prefixedArFilePath = reinterpret_cast(base + ctx.r3.u32); + + *reinterpret_cast*>(prefixedArFilePath) = 1; + strcpy(prefixedArFilePath + 0x4, "/UnleashedRecomp/"); + strcpy(prefixedArFilePath + 0x15, arFilePath); + + ctx.r1.u32 -= 0x10; + uint32_t stackSpace = ctx.r1.u32; + PPC_STORE_U32(stackSpace, static_cast(reinterpret_cast(prefixedArFilePath) - base) + 0x4); + g_prefixedArFilePath = stackSpace; + + ctx.r3 = r3; + ctx.r4 = r4; + ctx.r5 = r5; + ctx.r6 = r6; + ctx.r7 = r7; + ctx.r8 = r8; + __imp__sub_82E0CC38(ctx, base); + + // Hedgehog::Base::CSharedString::~CSharedString + ctx.r3.u32 = stackSpace; + sub_82DFB148(ctx, base); + + g_prefixedArFilePath = NULL; + ctx.r1.u32 += 0x10; +} + // Hedgehog::Database::SLoadElement::SLoadElement PPC_FUNC_IMPL(__imp__sub_82E140D8); PPC_FUNC(sub_82E140D8) { - // Store archive name as the pretty name to use it later for append archive loading. - // This is always set to an empty string for archives, so it should be safe to replace. - if (!g_mods.empty() && PPC_LOAD_U32(ctx.r5.u32) == 0x8200A621) - ctx.r5.u32 = ctx.r6.u32; + // Store the prefixed archive file path as the pretty name. It's unused for archives we want to append to. + if (!g_mods.empty() && PPC_LOAD_U32(ctx.r5.u32) == 0x8200A621 && g_prefixedArFilePath != NULL) + ctx.r5.u32 = g_prefixedArFilePath; __imp__sub_82E140D8(ctx, base); } @@ -584,25 +638,17 @@ PPC_FUNC(sub_82E0B500) return; } - std::u8string_view arFilePathU8(reinterpret_cast(base + PPC_LOAD_U32(ctx.r5.u32))); - size_t index = arFilePathU8.find(u8".ar.00"); - if (index == (arFilePathU8.size() - 6)) + uint32_t prefixedArFilePath = PPC_LOAD_U32(ctx.r5.u32); + std::u8string_view arFilePathU8(reinterpret_cast(base + prefixedArFilePath)); + if (!arFilePathU8.starts_with(u8"/UnleashedRecomp/")) { - arFilePathU8.remove_suffix(3); + __imp__sub_82E0B500(ctx, base); + return; } - else - { - index = arFilePathU8.find(u8".ar"); - if (index != (arFilePathU8.size() - 3) || - arFilePathU8.starts_with(u8"tg-") || - arFilePathU8.starts_with(u8"gia-") || - arFilePathU8.starts_with(u8"gi-texture-")) - { - __imp__sub_82E0B500(ctx, base); - return; - } - } + // Immediately clear the string, so the remaining splits don't load append archives again. + PPC_STORE_U8(prefixedArFilePath, 0x00); + arFilePathU8.remove_prefix(0x11); auto r3 = ctx.r3; // Callback auto r4 = ctx.r4; // Database