SDL_mixer: vorbis: Explicitly reject .XM files.

From bdadaa0371ed8f7c4b4783cb67e3925310882acb Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 19 May 2026 10:32:38 -0400
Subject: [PATCH] vorbis: Explicitly reject .XM files.

Otherwise .oxm files (XM mod files with Ogg Vorbis-compressed samples) might
get incorrectly claimed by the Vorbis decoder. Since libvorbis reads into the
file a little looking for the "OggS" magic--in case we came in mid-stream and
it can find a starting point to anchor to--it might catch the first Vorbis
sample's magic and think the whole file is Ogg Vorbis.

Our stb_vorbis decoder is more aggressive, and will reject anything that
doesn't start with a valid Ogg Vorbis header, but letting the actual libvorbis
decoder be more "robust" seems reasonable for now, so we're checking for this
special case where it's _too_ eager to make things work.

Fixes #834.
---
 src/decoder_vorbis.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/decoder_vorbis.c b/src/decoder_vorbis.c
index 9f764a89..b622c38f 100644
--- a/src/decoder_vorbis.c
+++ b/src/decoder_vorbis.c
@@ -159,8 +159,20 @@ static const ov_callbacks VORBIS_IoCallbacks = { VORBIS_IoRead, VORBIS_IoSeek, V
 
 static bool SDLCALL VORBIS_init_audio(SDL_IOStream *io, SDL_AudioSpec *spec, SDL_PropertiesID props, Sint64 *duration_frames, void **audio_userdata)
 {
+    Uint8 buffer[17];
+    if (SDL_ReadIO(io, buffer, sizeof (buffer)) != sizeof (buffer)) {
+        return false;
+    } else if (SDL_memcmp(buffer, "Extended Module: ", 17) == 0) {  // this is an XM file that _might_ be OXM and we would incorrectly claim it. Drop out now.
+        return SDL_SetError("Not an Ogg Vorbis audio stream");
+    }
+
+    // Go back and let ov_test_callbacks() take a run at it, too.
+    if (SDL_SeekIO(io, 0, SDL_IO_SEEK_SET) < 0) {
+        return false;
+    }
+
     // just load the bare minimum from the IOStream to verify it's an Ogg Vorbis file.
-    // !!! FIXME: is ov_open_callbacks going to return more slowly if this isn't an Opus file? It's probably better to just do the full open.
+    // !!! FIXME: is ov_open_callbacks going to return more slowly if this isn't a Vorbis file? It's probably better to just do the full open.
     OggVorbis_File vf;
     if (vorbis.ov_test_callbacks(io, &vf, NULL, 0, VORBIS_IoCallbacks) < 0) {
         return SDL_SetError("Not an Ogg Vorbis audio stream");