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 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-11-08 14:42:58 +01:00
1 parent 77fed58257
commit 41a02ffcb3
1 file changed
+15 -4
+15 -4
View File
@@ -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);