Implement decompression.

This commit is contained in:
Skyth 2024-12-29 19:18:54 +03:00
parent 2a2437789d
commit 0267b0045c
2 changed files with 139 additions and 10 deletions

View file

@ -19,6 +19,11 @@ public:
void* Commit(size_t offset, size_t size); void* Commit(size_t offset, size_t size);
void* Reserve(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 void* Translate(size_t offset) const noexcept
{ {
if (offset) if (offset)
@ -27,12 +32,12 @@ public:
return base + offset; return base + offset;
} }
uint32_t MapVirtual(void* host) const noexcept uint32_t MapVirtual(const void* host) const noexcept
{ {
if (host) if (host)
assert(host >= base && host < (base + size)); assert(IsInMemoryRange(host));
return static_cast<uint32_t>(static_cast<char*>(host) - base); return static_cast<uint32_t>(static_cast<const char*>(host) - base);
} }
}; };

View file

@ -145,6 +145,66 @@ void ModLoader::Init()
} }
} }
static constexpr uint32_t LZX_SIGNATURE = 0xFF512EE;
static std::span<uint8_t> decompressLzx(PPCContext& ctx, uint8_t* base, const uint8_t* compressedData, size_t compressedDataSize, be<uint32_t>* scratchSpace)
{
assert(g_memory.IsInMemoryRange(compressedData));
bool shouldFreeScratchSpace = false;
if (scratchSpace == nullptr)
{
scratchSpace = reinterpret_cast<be<uint32_t>*>(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<const be<uint32_t>*>(compressedData + 0x8);
ctx.r6.u32 = uint32_t(reinterpret_cast<uint8_t*>(scratchSpace) - base);
sub_831CE1A0(ctx, base);
uint64_t decompressedDataSize = *reinterpret_cast<const be<uint64_t>*>(compressedData + 0x18);
uint8_t* decompressedData = reinterpret_cast<uint8_t*>(g_userHeap.Alloc(decompressedDataSize));
uint32_t blockSize = *reinterpret_cast<const be<uint32_t>*>(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<const be<uint32_t>*>(compressedData + compressedDataOffset);
// Decompress
ctx.r3.u32 = *scratchSpace;
ctx.r4.u32 = uint32_t((decompressedData + decompressedDataOffset) - base);
ctx.r5.u32 = uint32_t(reinterpret_cast<uint8_t*>(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 // Hedgehog::Database::CDatabaseLoader::ReadArchiveList
PPC_FUNC_IMPL(__imp__sub_82E0D3E8); PPC_FUNC_IMPL(__imp__sub_82E0D3E8);
PPC_FUNC(sub_82E0D3E8) PPC_FUNC(sub_82E0D3E8)
@ -210,6 +270,11 @@ PPC_FUNC(sub_82E0D3E8)
std::filesystem::path arFilePath; std::filesystem::path arFilePath;
std::filesystem::path appendArlFilePath; 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& mod : g_mods)
{ {
for (auto& includeDir : mod.includeDirs) for (auto& includeDir : mod.includeDirs)
@ -222,14 +287,34 @@ PPC_FUNC(sub_82E0D3E8)
std::ifstream stream(includeDir / filePath, std::ios::binary); std::ifstream stream(includeDir / filePath, std::ios::binary);
if (stream.good()) if (stream.good())
{ {
be<uint32_t> signature{};
stream.read(reinterpret_cast<char*>(&signature), sizeof(signature));
stream.seekg(0, std::ios::end); stream.seekg(0, std::ios::end);
size_t arlFileSize = stream.tellg(); size_t arlFileSize = stream.tellg();
stream.seekg(0, std::ios::beg); stream.seekg(0, std::ios::beg);
if (signature == LZX_SIGNATURE)
{
void* compressedFileData = g_userHeap.Alloc(arlFileSize);
stream.read(reinterpret_cast<char*>(compressedFileData), arlFileSize);
auto fileData = decompressLzx(ctx, base, reinterpret_cast<uint8_t*>(compressedFileData), arlFileSize, nullptr);
g_userHeap.Free(compressedFileData);
function(fileData.data(), fileData.size());
g_userHeap.Free(fileData.data());
}
else
{
s_fileData.resize(arlFileSize); s_fileData.resize(arlFileSize);
stream.read(reinterpret_cast<char*>(s_fileData.data()), arlFileSize); stream.read(reinterpret_cast<char*>(s_fileData.data()), arlFileSize);
stream.close(); stream.close();
function(s_fileData.data(), arlFileSize); function(s_fileData.data(), arlFileSize);
}
return true; 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()) if (s_fileNames.empty())
{ {
__imp__sub_82E0D3E8(ctx, base); __imp__sub_82E0D3E8(ctx, base);
@ -397,6 +487,17 @@ PPC_FUNC(sub_82E0B500)
stream.close(); stream.close();
auto arFileDataHolder = reinterpret_cast<be<uint32_t>*>(g_userHeap.Alloc(sizeof(uint32_t) * 2)); auto arFileDataHolder = reinterpret_cast<be<uint32_t>*>(g_userHeap.Alloc(sizeof(uint32_t) * 2));
if (*reinterpret_cast<be<uint32_t>*>(arFileData) == LZX_SIGNATURE)
{
auto fileData = decompressLzx(ctx, base, reinterpret_cast<uint8_t*>(arFileData), arFileSize, arFileDataHolder);
g_userHeap.Free(arFileData);
arFileData = fileData.data();
arFileSize = fileData.size();
}
arFileDataHolder[0] = g_memory.MapVirtual(arFileData); arFileDataHolder[0] = g_memory.MapVirtual(arFileData);
arFileDataHolder[1] = NULL; arFileDataHolder[1] = NULL;
@ -430,10 +531,33 @@ PPC_FUNC(sub_82E0B500)
if (stream.good()) if (stream.good())
{ {
// TODO: Should cache this instead of re-opening the file. // TODO: Should cache this instead of re-opening the file.
be<uint32_t> signature{};
uint32_t splitCount{}; uint32_t splitCount{};
stream.seekg(4, std::ios::beg); stream.read(reinterpret_cast<char*>(&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<char*>(compressedFileData), arlFileSize);
stream.close();
auto fileData = decompressLzx(ctx, base, reinterpret_cast<uint8_t*>(compressedFileData), arlFileSize, nullptr);
g_userHeap.Free(compressedFileData);
splitCount = *reinterpret_cast<uint32_t*>(fileData.data() + 0x4);
g_userHeap.Free(fileData.data());
}
else
{
stream.read(reinterpret_cast<char*>(&splitCount), sizeof(splitCount)); stream.read(reinterpret_cast<char*>(&splitCount), sizeof(splitCount));
stream.close(); stream.close();
}
if (splitCount == 0) if (splitCount == 0)
{ {