Decode Yaz0 through guarded host buffers

Ported from chrissotraidis/wiicompiled (KartPad) 97cdc9a3609b6d3ad43009f76be17361b1a9e93a.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rLZ24cFcqTNBmb4w2dpSq
This commit is contained in:
Chris SotraidisandClaude Opus 5.5 authored and Claude committed 2026-10-05 15:54:42 +00:00
1 parent 03db602a05
commit 8e9e905b85
1 file changed
+68 -13
+68 -13
View File
@@ -1,20 +1,19 @@
#include "hle_stubs.h" #include "hle_stubs.h"
#include <cstdint> #include <cstdint>
#include <limits>
#include "memory.h" #include "memory.h"
#include "recomp_mod_loader.h"
#include "runtime_log.h" #include "runtime_log.h"
extern "C" void GxNotifyGuestRamDmaWrite(uint32_t addr, uint32_t size);
// Native because a crafted Yaz0 run writes past the caller's buffer // Native because a crafted Yaz0 run writes past the caller's buffer
// (github.com/vabold/szsHaxx) // (github.com/vabold/szsHaxx)
// https://github.com/vabold/Kinoko/blob/main/source/egg/core/Decomp.cc // https://github.com/vabold/Kinoko/blob/main/source/egg/core/Decomp.cc
extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst) template <typename Access>
{ static uint32_t DecodeSZS(uint32_t src, uint32_t expandSize, Access& access) {
const uint32_t expandSize = (static_cast<uint32_t>(MemoryInline::FlatRead8(src + 4)) << 24) |
(static_cast<uint32_t>(MemoryInline::FlatRead8(src + 5)) << 16) |
(static_cast<uint32_t>(MemoryInline::FlatRead8(src + 6)) << 8) |
static_cast<uint32_t>(MemoryInline::FlatRead8(src + 7));
uint32_t srcIdx = 16; uint32_t srcIdx = 16;
uint32_t dstIdx = 0; uint32_t dstIdx = 0;
uint32_t mask = 0; uint32_t mask = 0;
@@ -22,15 +21,15 @@ extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst)
while (static_cast<int32_t>(dstIdx) < static_cast<int32_t>(expandSize)) { while (static_cast<int32_t>(dstIdx) < static_cast<int32_t>(expandSize)) {
if (mask == 0) { if (mask == 0) {
flags = MemoryInline::FlatRead8(src + srcIdx++); flags = access.ReadSource(srcIdx++);
mask = 0x80; mask = 0x80;
} }
if ((flags & mask) != 0) { if ((flags & mask) != 0) {
MemoryInline::FlatWrite8(dst + dstIdx++, MemoryInline::FlatRead8(src + srcIdx++)); access.WriteOutput(dstIdx++, access.ReadSource(srcIdx++));
} else { } else {
const uint32_t high = MemoryInline::FlatRead8(src + srcIdx); const uint32_t high = access.ReadSource(srcIdx);
const uint32_t low = MemoryInline::FlatRead8(src + srcIdx + 1); const uint32_t low = access.ReadSource(srcIdx + 1);
srcIdx += 2; srcIdx += 2;
const uint32_t rep = (high << 8) | low; const uint32_t rep = (high << 8) | low;
@@ -48,7 +47,7 @@ extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst)
uint32_t count = rep >> 12; uint32_t count = rep >> 12;
count = count != 0 count = count != 0
? count + 2 ? count + 2
: static_cast<uint32_t>(MemoryInline::FlatRead8(src + srcIdx++)) + 18; : static_cast<uint32_t>(access.ReadSource(srcIdx++)) + 18;
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
if (dstIdx >= expandSize) { if (dstIdx >= expandSize) {
@@ -59,7 +58,7 @@ extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst)
"The game stopped decoding a malformed Yaz0 file."); "The game stopped decoding a malformed Yaz0 file.");
std::abort(); std::abort();
} }
MemoryInline::FlatWrite8(dst + dstIdx++, MemoryInline::FlatRead8(dst + copyIdx++)); access.WriteOutput(dstIdx++, access.ReadOutput(copyIdx++));
} }
} }
@@ -69,5 +68,61 @@ extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst)
return expandSize; return expandSize;
} }
struct GuestSZSAccess {
uint32_t src;
uint32_t dst;
uint8_t ReadSource(uint32_t offset) const { return MemoryInline::FlatRead8(src + offset); }
uint8_t ReadOutput(uint32_t offset) const { return MemoryInline::FlatRead8(dst + offset); }
void WriteOutput(uint32_t offset, uint8_t value) const {
MemoryInline::FlatWrite8(dst + offset, value);
}
};
struct HostSZSAccess {
const uint8_t* src;
uint8_t* dst;
uint8_t ReadSource(uint32_t offset) const { return src[offset]; }
uint8_t ReadOutput(uint32_t offset) const { return dst[offset]; }
void WriteOutput(uint32_t offset, uint8_t value) const { dst[offset] = value; }
};
static bool HasDeferredReadPages(uint32_t address, size_t length) {
const uint32_t first = address >> MemoryInline::kPageShift;
const uint32_t last = static_cast<uint32_t>(
(static_cast<uint64_t>(address) + length - 1) >> MemoryInline::kPageShift);
for (uint32_t page = first; page <= last; ++page) {
if (MemoryInline::g_deferredReadCoveredPages[page] != 0) return true;
}
return false;
}
extern "C" uint32_t EGG_Decomp_decodeSZS_80218c2c(uint32_t src, uint32_t dst)
{
const uint32_t expandSize = (static_cast<uint32_t>(MemoryInline::FlatRead8(src + 4)) << 24) |
(static_cast<uint32_t>(MemoryInline::FlatRead8(src + 5)) << 16) |
(static_cast<uint32_t>(MemoryInline::FlatRead8(src + 6)) << 8) |
static_cast<uint32_t>(MemoryInline::FlatRead8(src + 7));
// A token produces at least one byte; all-literal data is the largest
// valid input (one flag per eight output bytes). Three extra bytes cover
// the final malformed run before its output-overrun check aborts.
const size_t maxSourceBytes = 16ull + expandSize + (static_cast<uint64_t>(expandSize) + 7) / 8 + 3;
if (expandSize != 0 && expandSize <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) &&
!GuestFlat::RequiresCheckedAccess() &&
Memory::Contains(src, maxSourceBytes) && Memory::Contains(dst, expandSize) &&
!HasDeferredReadPages(src, maxSourceBytes) && !HasDeferredReadPages(dst, expandSize) &&
!RecompMod::ExecutableWriteGuardMayHit(dst, expandSize)) {
HostSZSAccess access{Memory::GetPointer(src, maxSourceBytes), Memory::GetPointer(dst, expandSize)};
const uint32_t decoded = DecodeSZS(src, expandSize, access);
GxNotifyGuestRamDmaWrite(dst, decoded);
return decoded;
}
GuestSZSAccess access{src, dst};
return DecodeSZS(src, expandSize, access);
}
PPC_NATIVE_OVERRIDE(80218C2C, EGG_Decomp_decodeSZS_80218c2c, uint32_t, PPC_NATIVE_OVERRIDE(80218C2C, EGG_Decomp_decodeSZS_80218c2c, uint32_t,
(uint32_t src, uint32_t dst), (src, dst)); (uint32_t src, uint32_t dst), (src, dst));