SDL_mixer: update dr_flac and dr_mp3 from mainstream (Sep.01, 2026)

From df8870a0ac9b2dd56bf22e63627a66cc743030bb Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Tue, 15 Sep 2026 21:41:20 +0300
Subject: [PATCH] update dr_flac and dr_mp3 from mainstream (Sep.01, 2026)

---
 src/dr_libs/dr_flac.h | 74 +++++++++++++++++++++++++++++++------------
 src/dr_libs/dr_mp3.h  | 20 +++++++-----
 2 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/src/dr_libs/dr_flac.h b/src/dr_libs/dr_flac.h
index 97ade626b..051f37c66 100644
--- a/src/dr_libs/dr_flac.h
+++ b/src/dr_libs/dr_flac.h
@@ -5561,6 +5561,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac)
 #endif
 
     /* This function should be called while the stream is sitting on the first byte after the frame header. */
+    pFlac->currentFLACFrame.pcmFramesRemaining = 0;
     DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
 
     /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */
@@ -5608,6 +5609,7 @@ static drflac_result drflac__decode_flac_frame(drflac* pFlac)
 
 static drflac_result drflac__seek_flac_frame(drflac* pFlac)
 {
+    drflac_result result;
     int channelCount;
     int i;
     drflac_uint16 desiredCRC16;
@@ -5615,16 +5617,20 @@ static drflac_result drflac__seek_flac_frame(drflac* pFlac)
     drflac_uint16 actualCRC16;
 #endif
 
+    pFlac->currentFLACFrame.pcmFramesRemaining = 0;
+
     channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
     for (i = 0; i < channelCount; ++i) {
         if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
-            return DRFLAC_ERROR;
+            result = DRFLAC_ERROR;
+            goto error;
         }
     }
 
     /* Padding. */
     if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
-        return DRFLAC_ERROR;
+        result = DRFLAC_ERROR;
+        goto error;
     }
 
     /* CRC. */
@@ -5632,16 +5638,22 @@ static drflac_result drflac__seek_flac_frame(drflac* pFlac)
     actualCRC16 = drflac__flush_crc16(&pFlac->bs);
 #endif
     if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
-        return DRFLAC_AT_END;
+        result = DRFLAC_AT_END;
+        goto error;
     }
 
 #ifndef DR_FLAC_NO_CRC
     if (actualCRC16 != desiredCRC16) {
-        return DRFLAC_CRC_MISMATCH;    /* CRC mismatch. */
+        result = DRFLAC_CRC_MISMATCH;   /* CRC mismatch. */
+        goto error;
     }
 #endif
 
     return DRFLAC_SUCCESS;
+
+error:
+    DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
+    return result;
 }
 
 static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
@@ -9875,6 +9887,23 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(
 }
 
 
+static drflac_bool32 drflac__is_current_flac_frame_valid(drflac* pFlac)
+{
+    drflac_uint32 iChannel;
+
+    if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames || pFlac->currentFLACFrame.pcmFramesRemaining > pFlac->currentFLACFrame.header.blockSizeInPCMFrames) {
+        return DRFLAC_FALSE;
+    }
+
+    for (iChannel = 0; iChannel < pFlac->channels; iChannel += 1) {
+        if (pFlac->currentFLACFrame.subframes[iChannel].pSamplesS32 == NULL) {
+            return DRFLAC_FALSE;
+        }
+    }
+
+    return DRFLAC_TRUE;
+}
+
 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
 {
     drflac_uint64 framesRead;
@@ -11784,23 +11813,25 @@ DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 p
         }
 
         /* If the target sample and the current sample are in the same frame we just move the position forward. */
-        if (pcmFrameIndex > pFlac->currentPCMFrame) {
-            /* Forward. */
-            drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
-            if (pFlac->currentFLACFrame.pcmFramesRemaining >  offset) {
-                pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
-                pFlac->currentPCMFrame = pcmFrameIndex;
-                return DRFLAC_TRUE;
-            }
-        } else {
-            /* Backward. */
-            drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
-            drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
-            drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
-            if (currentFLACFramePCMFramesConsumed > offsetAbs) {
-                pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
-                pFlac->currentPCMFrame = pcmFrameIndex;
-                return DRFLAC_TRUE;
+        if (drflac__is_current_flac_frame_valid(pFlac)) {
+            if (pcmFrameIndex > pFlac->currentPCMFrame) {
+                /* Forward. */
+                drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
+                if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
+                    pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
+                    pFlac->currentPCMFrame = pcmFrameIndex;
+                    return DRFLAC_TRUE;
+                }
+            } else {
+                /* Backward. */
+                drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
+                drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
+                drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
+                if (currentFLACFramePCMFramesConsumed > offsetAbs) {
+                    pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
+                    pFlac->currentPCMFrame = pcmFrameIndex;
+                    return DRFLAC_TRUE;
+                }
             }
         }
 
@@ -12236,6 +12267,7 @@ v0.13.4 - TBD
   - Fix a possible overflow error when parsing picture metadata.
   - Fix an error with seek point parsing.
   - Fix a possible deadlock when seeking.
+  - Fix an error where the decoder can be put into a bad state when seeking fails which then results in a crash when reading and seeking.
 
 v0.13.3 - 2026-01-17
   - Fix a compiler compatibility issue with some inlined assembly.
diff --git a/src/dr_libs/dr_mp3.h b/src/dr_libs/dr_mp3.h
index 01a0f6e15..a631ae40f 100644
--- a/src/dr_libs/dr_mp3.h
+++ b/src/dr_libs/dr_mp3.h
@@ -3200,9 +3200,9 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
                     pDataBufferEnd = pMP3->pData + pMP3->dataCapacity;
                 }
 
-                frameBytes = DRMP3_MIN((size_t)firstFrameInfo.frame_bytes, (size_t)((drmp3_uint8*)pDataBufferEnd - pTagDataBeg));
+                frameBytes = DRMP3_MIN((size_t)firstFrameInfo.frame_bytes, (size_t)((drmp3_uint8*)pDataBufferEnd - pFirstFrameData));
 
-                if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 8) {
+                if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 8) {
                     goto done_xing_info;    /* Frame too small for a Xing/Info tag. */
                 }
 
@@ -3217,7 +3217,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
                     pTagData += 8;  /* Skip past the ID and flags. */
 
                     if (flags & 0x01) { /* FRAMES flag. */
-                        if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) {
+                        if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 4) {
                             goto done_xing_info;    /* Invalid Xing/Info tag. */
                         }
 
@@ -3226,7 +3226,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
                     }
 
                     if (flags & 0x02) { /* BYTES flag. */
-                        if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) {
+                        if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 4) {
                             goto done_xing_info;    /* Invalid Xing/Info tag. */
                         }
 
@@ -3236,7 +3236,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
                     }
 
                     if (flags & 0x04) { /* TOC flag. */
-                        if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 100) {
+                        if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 100) {
                             goto done_xing_info;    /* Invalid Xing/Info tag. */
                         }
 
@@ -3245,19 +3245,23 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm
                     }
 
                     if (flags & 0x08) { /* SCALE flag. */
-                        if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) {
+                        if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 4) {
                             goto done_xing_info;    /* Invalid Xing/Info tag. */
                         }
-                        
+
                         pTagData += 4;
                     }
 
                     /* At this point we're done with the Xing/Info header. Now we can look at the LAME data. */
+                    if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 1) {
+                        goto done_xing_info;    /* Not enough data left to check for a LAME header. */
+                    }
+
                     if (pTagData[0]) {
                         int delayInPCMFrames;
                         int paddingInPCMFrames;
 
-                        if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 36) {
+                        if (frameBytes < (size_t)(pTagData - pFirstFrameData) + 36) {
                             goto done_xing_info;    /* Invalid Xing/Info tag. */
                         }