diff --git a/UnleashedRecomp/kernel/memory.h b/UnleashedRecomp/kernel/memory.h index 98349a44..2411b196 100644 --- a/UnleashedRecomp/kernel/memory.h +++ b/UnleashedRecomp/kernel/memory.h @@ -19,6 +19,11 @@ public: void* Commit(size_t offset, size_t size); void* Reserve(size_t offset, size_t size); + bool IsInMemoryRange(const void* host) const noexcept + { + return host >= base && host < (base + size); + } + void* Translate(size_t offset) const noexcept { if (offset) @@ -27,12 +32,12 @@ public: return base + offset; } - uint32_t MapVirtual(void* host) const noexcept + uint32_t MapVirtual(const void* host) const noexcept { if (host) - assert(host >= base && host < (base + size)); + assert(IsInMemoryRange(host)); - return static_cast(static_cast(host) - base); + return static_cast(static_cast(host) - base); } }; diff --git a/UnleashedRecomp/mod/mod_loader.cpp b/UnleashedRecomp/mod/mod_loader.cpp index 93a15531..1054b7f1 100644 --- a/UnleashedRecomp/mod/mod_loader.cpp +++ b/UnleashedRecomp/mod/mod_loader.cpp @@ -145,6 +145,66 @@ void ModLoader::Init() } } +static constexpr uint32_t LZX_SIGNATURE = 0xFF512EE; + +static std::span decompressLzx(PPCContext& ctx, uint8_t* base, const uint8_t* compressedData, size_t compressedDataSize, be* scratchSpace) +{ + assert(g_memory.IsInMemoryRange(compressedData)); + + bool shouldFreeScratchSpace = false; + if (scratchSpace == nullptr) + { + scratchSpace = reinterpret_cast*>(g_userHeap.Alloc(sizeof(uint32_t) * 2)); + shouldFreeScratchSpace = true; + } + + // Initialize decompressor + ctx.r3.u32 = 1; + ctx.r4.u32 = uint32_t((compressedData + 0xC) - base); + ctx.r5.u32 = *reinterpret_cast*>(compressedData + 0x8); + ctx.r6.u32 = uint32_t(reinterpret_cast(scratchSpace) - base); + sub_831CE1A0(ctx, base); + + uint64_t decompressedDataSize = *reinterpret_cast*>(compressedData + 0x18); + uint8_t* decompressedData = reinterpret_cast(g_userHeap.Alloc(decompressedDataSize)); + + uint32_t blockSize = *reinterpret_cast*>(compressedData + 0x28); + size_t decompressedDataOffset = 0; + size_t compressedDataOffset = 0x30; + + while (decompressedDataOffset < decompressedDataSize) + { + size_t decompressedBlockSize = decompressedDataSize - decompressedDataOffset; + + if (decompressedBlockSize > blockSize) + decompressedBlockSize = blockSize; + + *(scratchSpace + 1) = decompressedBlockSize; + + uint32_t compressedBlockSize = *reinterpret_cast*>(compressedData + compressedDataOffset); + + // Decompress + ctx.r3.u32 = *scratchSpace; + ctx.r4.u32 = uint32_t((decompressedData + decompressedDataOffset) - base); + ctx.r5.u32 = uint32_t(reinterpret_cast(scratchSpace + 1) - base); + ctx.r6.u32 = uint32_t((compressedData + compressedDataOffset + 0x4) - base); + ctx.r7.u32 = compressedBlockSize; + sub_831CE0D0(ctx, base); + + decompressedDataOffset += *(scratchSpace + 1); + compressedDataOffset += 0x4 + compressedBlockSize; + } + + // Deinitialize decompressor + ctx.r3.u32 = *scratchSpace; + sub_831CE150(ctx, base); + + if (shouldFreeScratchSpace) + g_userHeap.Free(scratchSpace); + + return { decompressedData, decompressedDataSize }; +} + // Hedgehog::Database::CDatabaseLoader::ReadArchiveList PPC_FUNC_IMPL(__imp__sub_82E0D3E8); PPC_FUNC(sub_82E0D3E8) @@ -210,6 +270,11 @@ PPC_FUNC(sub_82E0D3E8) std::filesystem::path arFilePath; std::filesystem::path appendArlFilePath; + auto r3 = ctx.r3; + auto r4 = ctx.r4; + auto r5 = ctx.r5; + auto r6 = ctx.r6; + for (auto& mod : g_mods) { for (auto& includeDir : mod.includeDirs) @@ -222,14 +287,34 @@ PPC_FUNC(sub_82E0D3E8) std::ifstream stream(includeDir / filePath, std::ios::binary); if (stream.good()) { + be signature{}; + stream.read(reinterpret_cast(&signature), sizeof(signature)); + stream.seekg(0, std::ios::end); size_t arlFileSize = stream.tellg(); stream.seekg(0, std::ios::beg); - s_fileData.resize(arlFileSize); - stream.read(reinterpret_cast(s_fileData.data()), arlFileSize); - stream.close(); - function(s_fileData.data(), arlFileSize); + if (signature == LZX_SIGNATURE) + { + void* compressedFileData = g_userHeap.Alloc(arlFileSize); + stream.read(reinterpret_cast(compressedFileData), arlFileSize); + + auto fileData = decompressLzx(ctx, base, reinterpret_cast(compressedFileData), arlFileSize, nullptr); + + g_userHeap.Free(compressedFileData); + + function(fileData.data(), fileData.size()); + + g_userHeap.Free(fileData.data()); + } + else + { + s_fileData.resize(arlFileSize); + stream.read(reinterpret_cast(s_fileData.data()), arlFileSize); + stream.close(); + + function(s_fileData.data(), arlFileSize); + } return true; } @@ -286,6 +371,11 @@ PPC_FUNC(sub_82E0D3E8) } } + ctx.r3 = r3; + ctx.r4 = r4; + ctx.r5 = r5; + ctx.r6 = r6; + if (s_fileNames.empty()) { __imp__sub_82E0D3E8(ctx, base); @@ -397,6 +487,17 @@ PPC_FUNC(sub_82E0B500) stream.close(); auto arFileDataHolder = reinterpret_cast*>(g_userHeap.Alloc(sizeof(uint32_t) * 2)); + + if (*reinterpret_cast*>(arFileData) == LZX_SIGNATURE) + { + auto fileData = decompressLzx(ctx, base, reinterpret_cast(arFileData), arFileSize, arFileDataHolder); + + g_userHeap.Free(arFileData); + + arFileData = fileData.data(); + arFileSize = fileData.size(); + } + arFileDataHolder[0] = g_memory.MapVirtual(arFileData); arFileDataHolder[1] = NULL; @@ -430,10 +531,33 @@ PPC_FUNC(sub_82E0B500) if (stream.good()) { // TODO: Should cache this instead of re-opening the file. + be signature{}; uint32_t splitCount{}; - stream.seekg(4, std::ios::beg); - stream.read(reinterpret_cast(&splitCount), sizeof(splitCount)); - stream.close(); + stream.read(reinterpret_cast(&signature), sizeof(signature)); + + if (signature == LZX_SIGNATURE) + { + stream.seekg(0, std::ios::end); + size_t arlFileSize = stream.tellg(); + stream.seekg(0, std::ios::beg); + + void* compressedFileData = g_userHeap.Alloc(arlFileSize); + stream.read(reinterpret_cast(compressedFileData), arlFileSize); + stream.close(); + + auto fileData = decompressLzx(ctx, base, reinterpret_cast(compressedFileData), arlFileSize, nullptr); + + g_userHeap.Free(compressedFileData); + + splitCount = *reinterpret_cast(fileData.data() + 0x4); + + g_userHeap.Free(fileData.data()); + } + else + { + stream.read(reinterpret_cast(&splitCount), sizeof(splitCount)); + stream.close(); + } if (splitCount == 0) {