From 41a02ffcb34d444fba26aa101b6ab1a9656fd2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 8 Nov 2026 13:54:41 +0100 Subject: [PATCH] sceMp3LowLevelDecode: Don't require room for 1152 samples Since b1ffcde9fc the output buffer had to hold 1152 stereo samples, but an MPEG-2 or 2.5 frame makes 576, so a buffer near the end of memory that fits the frame failed. When the buffer can't take the biggest frame, decode aside and copy only what the frame made. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceMp3.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index b180b0bd11..e56661b71b 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -759,20 +759,31 @@ static u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumed return hleLogError(Log::ME, -1, "invalid address in args"); } - // A frame is at most 1152 stereo samples, and the decoder is handed up to 4096 bytes. - if (!Memory::IsValidRange(samplesAddr, 1152 * 2 * 2) || !Memory::IsValidRange(sourceBytesConsumedAddr, 4) || !Memory::IsValidRange(sampleBytesAddr, 4)) { + if (!Memory::IsValidRange(sourceBytesConsumedAddr, 4) || !Memory::IsValidRange(sampleBytesAddr, 4)) { return hleLogError(Log::ME, -1, "invalid address in args"); } const u8 *inbuff = Memory::GetPointerWriteUnchecked(sourceAddr); int16_t *outbuf = (int16_t *)Memory::GetPointerWriteUnchecked(samplesAddr); + // The decoder is handed up to 4096 bytes. const int inBytes = (int)Memory::ClampValidSizeAt(sourceAddr, 4096); - + + // A frame is at most 1152 stereo samples, but an MPEG-2 or 2.5 one is 576. When the game's buffer + // can't take the biggest, decode aside and copy only what this frame made. + constexpr u32 maxFrameBytes = 1152 * 2 * 2; + const u32 outRoom = Memory::ClampValidSizeAt(samplesAddr, maxFrameBytes); + int16_t aside[1152 * 2]; int outSamples = 0; int inbytesConsumed = 0; - if (!ctx->decoder->Decode(inbuff, inBytes, &inbytesConsumed, 2, outbuf, &outSamples)) { + if (!ctx->decoder->Decode(inbuff, inBytes, &inbytesConsumed, 2, outRoom == maxFrameBytes ? outbuf : aside, &outSamples)) { WARN_LOG(Log::ME, "sceMp3LowLevelDecode: Decode failed"); } int outBytes = outSamples * sizeof(int16_t) * 2; + if (outRoom != maxFrameBytes) { + if ((u32)outBytes > outRoom) { + return hleLogError(Log::ME, -1, "output buffer too small for the frame"); + } + memcpy(outbuf, aside, outBytes); + } NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, outBytes, "Mp3LowLevelDecode"); Memory::WriteOrException_U32(inbytesConsumed, sourceBytesConsumedAddr);