Skip to content

Commit

Permalink
mp3_skiptags: let it consume all the tags at file end (bug #4907):
Browse files Browse the repository at this point in the history
[backport from default/2.0 commit 4e1c2282e6f1]

We do not know the order of ape, or lyrics3, or musicmatch tags,
so we loop until we consume all, scanning for each tag type once.
I don't yet care about freaky broken mp3 files with double tags.

<rant> MP3 standard has no metadata format, so everyone invented
their own thing, even with extensions, until ID3v2 became dominant:
Hence the impossible mess there.</rant>

Also remove inline directive from a few detection procedures there.
  • Loading branch information
sezero committed Dec 20, 2019
1 parent 8ac7990 commit 49e70ec
Showing 1 changed file with 112 additions and 61 deletions.
173 changes: 112 additions & 61 deletions mp3utils.c
@@ -1,6 +1,6 @@
/*
SDL_mixer: An audio mixer library based on the SDL library
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
Expand Down Expand Up @@ -68,7 +68,7 @@ static __inline__ SDL_bool is_id3v1(const unsigned char *data, int length)
}
return SDL_TRUE;
}
static __inline__ SDL_bool is_id3v2(const unsigned char *data, int length)
static SDL_bool is_id3v2(const unsigned char *data, int length)
{
/* ID3v2 header is 10 bytes: http://id3.org/id3v2.4.0-structure */
/* bytes 0-2: "ID3" identifier */
Expand All @@ -87,7 +87,7 @@ static __inline__ SDL_bool is_id3v2(const unsigned char *data, int length)
}
return SDL_TRUE;
}
static __inline__ int get_id3v2_len(const unsigned char *data, int length)
static int get_id3v2_len(const unsigned char *data, int length)
{
/* size is a 'synchsafe' integer (see above) */
int size = (int)((data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9]);
Expand All @@ -104,7 +104,7 @@ static __inline__ int get_id3v2_len(const unsigned char *data, int length)
}
return size;
}
static __inline__ SDL_bool is_apetag(const unsigned char *data, int length)
static SDL_bool is_apetag(const unsigned char *data, int length)
{
/* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification
* Header/footer is 32 bytes: bytes 0-7 ident, bytes 8-11 version,
Expand All @@ -124,7 +124,7 @@ static __inline__ SDL_bool is_apetag(const unsigned char *data, int length)
}
return SDL_TRUE;
}
static __inline__ int get_ape_len(const unsigned char *data)
static int get_ape_len(const unsigned char *data)
{
Uint32 flags, version;
int size = (int)((data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12]);
Expand All @@ -141,7 +141,7 @@ static __inline__ int is_lyrics3tag(const unsigned char *data, int length) {
if (SDL_memcmp(data+6,"LYRICSEND",9) == 0) return 1; /* v1 */
return 0;
}
static __inline__ int get_lyrics3v1_len(struct mp3file_t *m) {
static int get_lyrics3v1_len(struct mp3file_t *m) {
const char *p; int i, len;
char buf[5104];
/* needs manual search: http://id3.org/Lyrics3 */
Expand All @@ -163,13 +163,13 @@ static __inline__ int get_lyrics3v2_len(const unsigned char *data, int length) {
if (length != 6) return 0;
return SDL_strtol((const char *)data, NULL, 10) + 15;
}
static __inline__ SDL_bool verify_lyrics3v2(const unsigned char *data, int length) {
static SDL_bool verify_lyrics3v2(const unsigned char *data, int length) {
if (length < 11) return SDL_FALSE;
if (SDL_memcmp(data,"LYRICSBEGIN",11) == 0) return SDL_TRUE;
return SDL_FALSE;
}
#define MMTAG_PARANOID
static __inline__ SDL_bool is_musicmatch(const unsigned char *data, int length) {
static SDL_bool is_musicmatch(const unsigned char *data, int length) {
/* From docs/musicmatch.txt in id3lib: https://sourceforge.net/projects/id3lib/
Overall tag structure:
Expand Down Expand Up @@ -212,7 +212,7 @@ static __inline__ SDL_bool is_musicmatch(const unsigned char *data, int length)
#endif
return SDL_TRUE;
}
static __inline__ int get_musicmatch_len(struct mp3file_t *m) {
static int get_musicmatch_len(struct mp3file_t *m) {
const Sint32 metasizes[4] = { 7868, 7936, 8004, 8132 };
const unsigned char syncstr[10] = {'1','8','2','7','3','6','4','5',0,0};
unsigned char buf[256];
Expand Down Expand Up @@ -279,96 +279,147 @@ static __inline__ int get_musicmatch_len(struct mp3file_t *m) {
return len + 256; /* header is present. */
}

int mp3_skiptags(struct mp3file_t *fil)
{
unsigned char buf[128];
int len, readsize;
int rc = -1;

readsize = MP3_RWread(fil, buf, 1, 128);
if (readsize <= 0) goto fail;

/* ID3v2 tag is at the start */
if (is_id3v2(buf, readsize)) {
len = get_id3v2_len(buf, readsize);
if (len >= fil->length) goto fail;
fil->start += len;
fil->length -= len;
}
/* APE tag _might_ be at the start (discouraged
* but not forbidden, either.) read the header. */
else if (is_apetag(buf, readsize)) {
len = get_ape_len(buf);
if (len >= fil->length) goto fail;
fil->start += len;
fil->length -= len;
}

/* ID3v1 tag is at the end */
static int probe_id3v1(struct mp3file_t *fil, unsigned char *buf) {
if (fil->length >= 128) {
MP3_RWseek(fil, -128, RW_SEEK_END);
readsize = MP3_RWread(fil, buf, 1, 128);
if (readsize != 128) goto fail;
if (MP3_RWread(fil, buf, 1, 128) != 128)
return -1;
if (is_id3v1(buf, 128)) {
fil->length -= 128;
return 1;
/* FIXME: handle possible double-ID3v1 tags?? */
}
}

/* do we know whether ape or lyrics3 is the first?
* well, we don't: we need to handle that later... */

/* check for the _old_ MusicMatch tag at end. */
return 0;
}
static int probe_mmtag(struct mp3file_t *fil, unsigned char *buf) {
int len;
if (fil->length >= 68) {
MP3_RWseek(fil, -48, RW_SEEK_END);
readsize = MP3_RWread(fil, buf, 1, 48);
if (readsize != 48) goto fail;
if (MP3_RWread(fil, buf, 1, 48) != 48)
return -1;
if (is_musicmatch(buf, 48)) {
len = get_musicmatch_len(fil);
if (len < 0) goto fail;
if (len >= fil->length) goto fail;
if (len < 0) return -1;
if (len >= fil->length) return -1;
fil->length -= len;
return 1;
}
}

/* APE tag may be at the end: read the footer */
return 0;
}
static int probe_apetag(struct mp3file_t *fil, unsigned char *buf) {
int len;
if (fil->length >= 32) {
MP3_RWseek(fil, -32, RW_SEEK_END);
readsize = MP3_RWread(fil, buf, 1, 32);
if (readsize != 32) goto fail;
if (MP3_RWread(fil, buf, 1, 32) != 32)
return -1;
if (is_apetag(buf, 32)) {
len = get_ape_len(buf);
if (len >= fil->length) goto fail;
if (len >= fil->length) return -1;
fil->length -= len;
return 1;
}
}

return 0;
}
static int probe_lyrics3(struct mp3file_t *fil, unsigned char *buf) {
int len;
if (fil->length >= 15) {
MP3_RWseek(fil, -15, RW_SEEK_END);
readsize = MP3_RWread(fil, buf, 1, 15);
if (readsize != 15) goto fail;
if (MP3_RWread(fil, buf, 1, 15) != 15)
return -1;
len = is_lyrics3tag(buf, 15);
if (len == 2) {
len = get_lyrics3v2_len(buf, 6);
if (len >= fil->length) goto fail;
if (len < 15) goto fail;
if (len >= fil->length) return -1;
if (len < 15) return -1;
MP3_RWseek(fil, -len, RW_SEEK_END);
readsize = MP3_RWread(fil, buf, 1, 11);
if (readsize != 11) goto fail;
if (!verify_lyrics3v2(buf, 11)) goto fail;
if (MP3_RWread(fil, buf, 1, 11)!= 11)
return -1;
if (!verify_lyrics3v2(buf, 11)) return -1;
fil->length -= len;
return 1;
}
else if (len == 1) {
len = get_lyrics3v1_len(fil);
if (len < 0) goto fail;
if (len < 0) return -1;
fil->length -= len;
return 1;
}
}
return 0;
}

int mp3_skiptags(struct mp3file_t *fil)
{
unsigned char buf[128];
int len, readsize;
int c_id3, c_ape, c_lyr, c_mm;
int rc = -1;

/* MP3 standard has no metadata format, so everyone invented
* their own thing, even with extensions, until ID3v2 became
* dominant: Hence the impossible mess here.
*/

readsize = MP3_RWread(fil, buf, 1, 128);
if (readsize <= 0) goto fail;

/* ID3v2 tag is at the start */
if (is_id3v2(buf, readsize)) {
len = get_id3v2_len(buf, readsize);
if (len >= fil->length) goto fail;
fil->start += len;
fil->length -= len;
}
/* APE tag _might_ be at the start (discouraged
* but not forbidden, either.) read the header. */
else if (is_apetag(buf, readsize)) {
len = get_ape_len(buf);
if (len >= fil->length) goto fail;
fil->start += len;
fil->length -= len;
}

/* it's not impossible that _old_ MusicMatch tag
* placing itself after ID3v1. */
if ((c_mm = probe_mmtag(fil, buf)) < 0) {
goto fail;
}
/* ID3v1 tag is at the end */
if ((c_id3 = probe_id3v1(fil, buf)) < 0) {
goto fail;
}
/* we do not know the order of ape or lyrics3
* or musicmatch tags, hence the loop here.. */
c_ape = 0;
c_lyr = 0;
for (;;) {
if (!c_lyr) {
/* care about mp3s with double Lyrics3 tags? */
if ((c_lyr = probe_lyrics3(fil, buf)) < 0)
goto fail;
if (c_lyr) continue;
}
if (!c_mm) {
if ((c_mm = probe_mmtag(fil, buf)) < 0)
goto fail;
if (c_mm) continue;
}
if (!c_ape) {
if ((c_ape = probe_apetag(fil, buf)) < 0)
goto fail;
if (c_ape) continue;
}
break;
} /* for (;;) */

rc = (fil->length > 0)? 0 : -1;
fail:
MP3_RWseek(fil, 0, RW_SEEK_SET);
return rc;
}

#endif /* MP3_???_MUSIC */

/* vi: set ts=4 sw=4 expandtab: */

0 comments on commit 49e70ec

Please sign in to comment.