SDL_mixer: flac-in-ogg handling (#566)

From 2f0dba0f20fbcd6bb3c75c475e952154e2ef44dc Mon Sep 17 00:00:00 2001
From: Ankith <[EMAIL REDACTED]>
Date: Sun, 22 Oct 2023 21:40:02 +0530
Subject: [PATCH] flac-in-ogg handling (#566)

- Fix flac-in-ogg detection
- Fix flac-in-ogg handling in libflac backend
---
 src/codecs/music_flac.c | 56 +++++++++++++++++++++++++++++++++++------
 src/music.c             |  3 +++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/src/codecs/music_flac.c b/src/codecs/music_flac.c
index 87f7c5f9..22ebdfd2 100644
--- a/src/codecs/music_flac.c
+++ b/src/codecs/music_flac.c
@@ -49,6 +49,17 @@ typedef struct {
                         FLAC__StreamDecoderMetadataCallback metadata_callback,
                         FLAC__StreamDecoderErrorCallback error_callback,
                         void *client_data);
+    FLAC__StreamDecoderInitStatus (*FLAC__stream_decoder_init_ogg_stream)(
+                        FLAC__StreamDecoder *decoder,
+                        FLAC__StreamDecoderReadCallback read_callback,
+                        FLAC__StreamDecoderSeekCallback seek_callback,
+                        FLAC__StreamDecoderTellCallback tell_callback,
+                        FLAC__StreamDecoderLengthCallback length_callback,
+                        FLAC__StreamDecoderEofCallback eof_callback,
+                        FLAC__StreamDecoderWriteCallback write_callback,
+                        FLAC__StreamDecoderMetadataCallback metadata_callback,
+                        FLAC__StreamDecoderErrorCallback error_callback,
+                        void *client_data);
     FLAC__bool (*FLAC__stream_decoder_finish)(FLAC__StreamDecoder *decoder);
     FLAC__bool (*FLAC__stream_decoder_flush)(FLAC__StreamDecoder *decoder);
     FLAC__bool (*FLAC__stream_decoder_process_single)(
@@ -104,6 +115,17 @@ static int FLAC_Load(void)
                         FLAC__StreamDecoderMetadataCallback,
                         FLAC__StreamDecoderErrorCallback,
                         void *))
+        FUNCTION_LOADER(FLAC__stream_decoder_init_ogg_stream, FLAC__StreamDecoderInitStatus (*)(
+                        FLAC__StreamDecoder *,
+                        FLAC__StreamDecoderReadCallback,
+                        FLAC__StreamDecoderSeekCallback,
+                        FLAC__StreamDecoderTellCallback,
+                        FLAC__StreamDecoderLengthCallback,
+                        FLAC__StreamDecoderEofCallback,
+                        FLAC__StreamDecoderWriteCallback,
+                        FLAC__StreamDecoderMetadataCallback,
+                        FLAC__StreamDecoderErrorCallback,
+                        void *))
         FUNCTION_LOADER(FLAC__stream_decoder_finish, FLAC__bool (*)(FLAC__StreamDecoder *))
         FUNCTION_LOADER(FLAC__stream_decoder_flush, FLAC__bool (*)(FLAC__StreamDecoder *))
         FUNCTION_LOADER(FLAC__stream_decoder_process_single, FLAC__bool (*)(FLAC__StreamDecoder *))
@@ -489,6 +511,14 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, SDL_bool freesrc)
     int init_stage = 0;
     int was_error = 1;
     FLAC__int64 full_length;
+    int is_ogg_flac;
+    Uint8 magic[4];
+    if (SDL_RWread(src, magic, 4) != 4) {
+        SDL_SetError("Couldn't read first 4 bytes of audio data");
+        return NULL;
+    }
+    SDL_RWseek(src, -4, SDL_RW_SEEK_CUR);
+    is_ogg_flac = (SDL_memcmp(magic, "OggS", 4) == 0);
 
     music = (FLAC_Music *)SDL_calloc(1, sizeof(*music));
     if (!music) {
@@ -500,17 +530,29 @@ static void *FLAC_CreateFromRW(SDL_RWops *src, SDL_bool freesrc)
 
     music->flac_decoder = flac.FLAC__stream_decoder_new();
     if (music->flac_decoder) {
+        FLAC__StreamDecoderInitStatus ret;
         init_stage++; /* stage 1! */
         flac.FLAC__stream_decoder_set_metadata_respond(music->flac_decoder,
                     FLAC__METADATA_TYPE_VORBIS_COMMENT);
 
-        if (flac.FLAC__stream_decoder_init_stream(
-                    music->flac_decoder,
-                    flac_read_music_cb, flac_seek_music_cb,
-                    flac_tell_music_cb, flac_length_music_cb,
-                    flac_eof_music_cb, flac_write_music_cb,
-                    flac_metadata_music_cb, flac_error_music_cb,
-                    music) == FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+        if (is_ogg_flac) {
+            ret = flac.FLAC__stream_decoder_init_ogg_stream(
+                music->flac_decoder,
+                flac_read_music_cb, flac_seek_music_cb,
+                flac_tell_music_cb, flac_length_music_cb,
+                flac_eof_music_cb, flac_write_music_cb,
+                flac_metadata_music_cb, flac_error_music_cb,
+                music);
+        } else {
+            ret = flac.FLAC__stream_decoder_init_stream(
+                music->flac_decoder,
+                flac_read_music_cb, flac_seek_music_cb,
+                flac_tell_music_cb, flac_length_music_cb,
+                flac_eof_music_cb, flac_write_music_cb,
+                flac_metadata_music_cb, flac_error_music_cb, 
+                music);
+        }
+        if (ret == FLAC__STREAM_DECODER_INIT_STATUS_OK) {
             init_stage++; /* stage 2! */
 
             if (flac.FLAC__stream_decoder_process_until_end_of_metadata(music->flac_decoder)) {
diff --git a/src/music.c b/src/music.c
index c295b793..68e92799 100644
--- a/src/music.c
+++ b/src/music.c
@@ -574,6 +574,9 @@ Mix_MusicType detect_music_type(SDL_RWops *src)
         if (SDL_memcmp(magic, "OpusHead", 8) == 0) {
             return MUS_OPUS;
         }
+        if (magic[0] == 0x7F && SDL_memcmp(magic + 1, "FLAC", 4) == 0) {
+            return MUS_FLAC;
+        }
         return MUS_OGG;
     }