diff --git a/src/codecs/mp3utils.c b/src/codecs/mp3utils.c index 8740734cc..4c025992a 100644 --- a/src/codecs/mp3utils.c +++ b/src/codecs/mp3utils.c @@ -1173,4 +1173,29 @@ int read_id3v2_from_mem(Mix_MusicMetaTags *out_tags, Uint8 *data, size_t length) } return -1; } + +long get_id3v2_length(SDL_RWops *src) +{ + Uint8 buf[TAGS_INPUT_BUFFER_SIZE]; + size_t readsize; + Sint64 start; + + if(!src) { + return 0; + } + + start = SDL_RWtell(src); + readsize = SDL_RWread(src, buf, TAGS_INPUT_BUFFER_SIZE); + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + + if (!readsize) { + return 0; + } + + if (!is_id3v2(buf, readsize)) { + return 0; + } + + return get_id3v2_len(buf, (long)readsize); +} #endif /* ENABLE_ID3V2_TAG */ diff --git a/src/codecs/mp3utils.h b/src/codecs/mp3utils.h index 0bb3d47fa..de486ddbe 100644 --- a/src/codecs/mp3utils.h +++ b/src/codecs/mp3utils.h @@ -42,6 +42,7 @@ extern int mp3_read_tags(Mix_MusicMetaTags *out_tags, struct mp3file_t *fil, SDL #ifdef ENABLE_ID3V2_TAG extern int read_id3v2_from_mem(Mix_MusicMetaTags *out_tags, Uint8 *data, size_t length); +extern long get_id3v2_length(SDL_RWops *src); #endif #ifdef ENABLE_ALL_MP3_TAGS diff --git a/src/music.c b/src/music.c index c295b7939..7476026a5 100644 --- a/src/music.c +++ b/src/music.c @@ -44,6 +44,7 @@ #include "native_midi/native_midi.h" #include "utils.h" +#include "mp3utils.h" /* Check to make sure we are building with a new enough SDL */ #if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 7) @@ -549,15 +550,93 @@ SDL_bool has_music(Mix_MusicType type) return SDL_FALSE; } +static int detect_mp3(Uint8 *magic, SDL_RWops *src, Sint64 start, Sint64 offset) +{ + const Uint32 null = 0; + Uint8 mp3_magic[4]; + Sint64 end_file_pos = 0; + const int max_search = 10240; + + SDL_memcpy(mp3_magic, magic, 4); + + /* Attempt to quickly detect MP3 file if possible */ + /* see: https://bugzilla.libsdl.org/show_bug.cgi?id=5322 */ + if ((magic[0] == 0xFF) && (magic[1] & 0xE6) == 0xE2) { + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + return 1; + } + + /* If no success, try the deep scan of first 10 kilobytes of the file + * to detect the first valid MP3 frame */ + + SDL_RWseek(src, 0, SDL_RW_SEEK_END); + end_file_pos = SDL_RWtell(src); + SDL_RWseek(src, start + offset, SDL_RW_SEEK_SET); + + /* If first 4 bytes are not zero */ + if (SDL_memcmp(mp3_magic, &null, 4) != 0) { + goto readHeader; + } + +digMoreBytes: + /* Find the nearest 0xFF byte */ + while ((SDL_RWread(src, mp3_magic, 1) == 1) && + (mp3_magic[0] != 0xFF) && + (SDL_RWtell(src) < (start + offset + max_search)) && + (SDL_RWtell(src) < (end_file_pos - 1)) ) + {} + + /* Can't read last 3 bytes of the frame header */ + if (SDL_RWread(src, mp3_magic + 1, 3) != 3) { + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + return 0; + } + + /* Go back to 3 bytes */ + SDL_RWseek(src, -3, SDL_RW_SEEK_CUR); + + /* Got the end of search zone, however, found nothing */ + if (SDL_RWtell(src) >= (start + offset + max_search)) { + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + return 0; + } + + /* Got the end of file, however, found nothing */ + if (SDL_RWtell(src) >= (end_file_pos - 1)) { + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + return 0; + } + +readHeader: + if ( + ((mp3_magic[0] & 0xff) != 0xff) || ((mp3_magic[1] & 0xf0) != 0xf0) || /* No sync bits */ + ((mp3_magic[1] & 0xe6) != 0xe2) || + ((mp3_magic[2] & 0xf0) == 0x00) || /* Bitrate is 0 */ + ((mp3_magic[2] & 0xf0) == 0xf0) || /* Bitrate is 15 */ + ((mp3_magic[2] & 0x0c) == 0x0c) || /* Frequency is 3 */ + ((mp3_magic[1] & 0x06) == 0x00) /* Layer is 4 */ + ) { + /* printf("WRONG BITS\n"); */ + goto digMoreBytes; + } + + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + return 1; +} + Mix_MusicType detect_music_type(SDL_RWops *src) { Uint8 magic[12]; + Sint64 start = SDL_RWtell(src); + Uint8 submagic[4]; + long id3len = 0; + size_t readlen = 0; if (SDL_RWread(src, magic, 12) != 12) { Mix_SetError("Couldn't read first 12 bytes of audio data"); return MUS_NONE; } - SDL_RWseek(src, -12, SDL_RW_SEEK_CUR); + SDL_RWseek(src, start, SDL_RW_SEEK_SET); /* WAVE files have the magic four bytes "RIFF" AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ @@ -597,12 +676,6 @@ Mix_MusicType detect_music_type(SDL_RWops *src) return MUS_MID; } - if (SDL_memcmp(magic, "ID3", 3) == 0 || - /* see: https://bugzilla.libsdl.org/show_bug.cgi?id=5322 */ - (magic[0] == 0xFF && (magic[1] & 0xE6) == 0xE2)) { - return MUS_MP3; - } - /* GME Specific files */ if (SDL_memcmp(magic, "ZXAY", 4) == 0) return MUS_GME; @@ -629,6 +702,27 @@ Mix_MusicType detect_music_type(SDL_RWops *src) if (SDL_memcmp(magic, "\x1f\x8b", 2) == 0) return MUS_GME; + if (SDL_memcmp(magic, "ID3", 3) == 0) { + id3len = get_id3v2_length(src); + + /* Check if there is something not an MP3, however, also has ID3 tag */ + if (id3len > 0) { + SDL_RWseek(src, id3len, SDL_RW_SEEK_CUR); + readlen = SDL_RWread(src, submagic, 4); + SDL_RWseek(src, start, SDL_RW_SEEK_SET); + + if (readlen == 4) { + if (SDL_memcmp(submagic, "fLaC", 4) == 0) + return MUS_FLAC; + } + } + } + + /* Detect MP3 format by frame header [needs scanning of bigger part of the file] */ + if (detect_mp3(submagic, src, start, id3len)) { + return MUS_MP3; + } + /* Assume MOD format. * * Apparently there is no way to check if the file is really a MOD,