From b309b6c6adb803cf31410c7799afdb028a828603 Mon Sep 17 00:00:00 2001 From: Max Roncace Date: Sun, 29 Mar 2026 01:12:40 -0400 Subject: [PATCH] Use separate heap for DVD decompression This fixes crashes when first entering North Faaron cave and Renado's Sanctuary due to system heap exhaustion resulting from multiple threads allocating on the heap at once. There's some sort of weird contention going on - for some reason the game gobbles up seemingly any amount of memory you throw at it when the two threads are competing for heap space. Using a separate heap in the async thread sidesteps the issue. --- libs/JSystem/include/JSystem/JKernel/JKRDvdRipper.h | 9 +++++++++ libs/JSystem/src/JKernel/JKRDvdRipper.cpp | 12 ++++++++++++ src/m_Do/m_Do_machine.cpp | 9 +++++++++ 3 files changed, 30 insertions(+) diff --git a/libs/JSystem/include/JSystem/JKernel/JKRDvdRipper.h b/libs/JSystem/include/JSystem/JKernel/JKRDvdRipper.h index 82b9c21a2e..0720605efb 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRDvdRipper.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRDvdRipper.h @@ -36,6 +36,9 @@ public: static JSUList sDvdAsyncList; static u32 sSZSBufferSize; static bool errorRetry; +#if TARGET_PC + static JKRHeap* sHeap; +#endif enum EAllocDirection { UNKNOWN_EALLOC_DIRECTION = 0, @@ -54,6 +57,12 @@ public: static bool isErrorRetry(void) { return errorRetry; } inline static u32 getSZSBufferSize() { return sSZSBufferSize; } + +#if TARGET_PC + static inline JKRHeap* getHeap() { return sHeap; } + static inline void setHeap(JKRHeap* i_heap) { sHeap = i_heap; } + +#endif }; // void JKRDecompressFromDVD(JKRDvdFile*, void*, u32, u32, u32, u32, u32*); diff --git a/libs/JSystem/src/JKernel/JKRDvdRipper.cpp b/libs/JSystem/src/JKernel/JKRDvdRipper.cpp index ae644ee9e1..ae65f42ba3 100644 --- a/libs/JSystem/src/JKernel/JKRDvdRipper.cpp +++ b/libs/JSystem/src/JKernel/JKRDvdRipper.cpp @@ -13,6 +13,8 @@ #include #include +#include "JSystem/JKernel/JKRExpHeap.h" + static int JKRDecompressFromDVD(JKRDvdFile*, void*, u32, u32, u32, u32, u32*); static int decompSZS_subroutine(u8*, u8*); static u8* firstSrcData(); @@ -240,6 +242,8 @@ static OSMutex decompMutex; u32 JKRDvdRipper::sSZSBufferSize = 0x00000400; +JKRHeap* JKRDvdRipper::sHeap = NULL; + static u8* szpBuf; static u8* szpEnd; @@ -282,12 +286,20 @@ static int JKRDecompressFromDVD(JKRDvdFile* dvdFile, void* dst, u32 fileSize, u3 OSLockMutex(&decompMutex); u32 result = 0; u32 szsBufferSize = JKRDvdRipper::getSZSBufferSize(); +#if TARGET_PC + szpBuf = (u8 *)JKRAllocFromHeap(JKRDvdRipper::getHeap(), szsBufferSize, -0x20); +#else szpBuf = (u8 *)JKRAllocFromSysHeap(szsBufferSize, -0x20); +#endif JUT_ASSERT(909, szpBuf != NULL); szpEnd = szpBuf + szsBufferSize; if (inFileOffset != 0) { +#if TARGET_PC + refBuf = (u8 *)JKRAllocFromHeap(JKRDvdRipper::getHeap(), 0x1120, -4); +#else refBuf = (u8 *)JKRAllocFromSysHeap(0x1120, -4); +#endif JUT_ASSERT(918, refBuf != NULL); refEnd = refBuf + 0x1120; refCurrent = refBuf; diff --git a/src/m_Do/m_Do_machine.cpp b/src/m_Do/m_Do_machine.cpp index f50c8a6fdb..d5258b6f98 100644 --- a/src/m_Do/m_Do_machine.cpp +++ b/src/m_Do/m_Do_machine.cpp @@ -29,6 +29,10 @@ #include "DynamicLink.h" #include "os_report.h" +#if TARGET_PC +#include +#endif + #if !PLATFORM_GCN #include #include @@ -991,6 +995,11 @@ int mDoMch_Create() { GXSetVerifyCallback((GXVerifyCallback)&myGXVerifyCallback); #endif JKRDvdRipper::setSZSBufferSize(0x4000); +#if TARGET_PC + JKRHeap* dvdHeap = JKRCreateExpHeap(0x10000, NULL, false); + assert(dvdHeap != NULL); + JKRDvdRipper::setHeap(dvdHeap); +#endif JKRDvdAramRipper::setSZSBufferSize(0x4000); JKRAram::setSZSBufferSize(0x2000); mDoDvdThd::create(OSGetThreadPriority(OSGetCurrentThread()) - 2);