mp3utils.c: initial support for skipping the old MusicMatch tags
authorOzkan Sezer
Wed, 11 Dec 2019 17:50:10 +0300
changeset 1074c7c4d55152af
parent 1073 8450c02199e4
child 1075 915b41920c70
mp3utils.c: initial support for skipping the old MusicMatch tags
src/codecs/mp3utils.c
     1.1 --- a/src/codecs/mp3utils.c	Wed Dec 11 01:22:50 2019 +0300
     1.2 +++ b/src/codecs/mp3utils.c	Wed Dec 11 17:50:10 2019 +0300
     1.3 @@ -168,6 +168,85 @@
     1.4      if (SDL_memcmp(data,"LYRICSBEGIN",11) == 0) return SDL_TRUE;
     1.5      return SDL_FALSE;
     1.6  }
     1.7 +static SDL_INLINE SDL_bool is_musicmatch(const unsigned char *data, long length) {
     1.8 +  /* From docs/musicmatch.txt in id3lib: https://sourceforge.net/projects/id3lib/
     1.9 +     Overall tag structure:
    1.10 +
    1.11 +      +-----------------------------+
    1.12 +      |           Header            |
    1.13 +      |    (256 bytes, OPTIONAL)    |
    1.14 +      +-----------------------------+
    1.15 +      |  Image extension (4 bytes)  |
    1.16 +      +-----------------------------+
    1.17 +      |        Image binary         |
    1.18 +      |  (var. length >= 4 bytes)   |
    1.19 +      +-----------------------------+
    1.20 +      |      Unused (4 bytes)       |
    1.21 +      +-----------------------------+
    1.22 +      |  Version info (256 bytes)   |
    1.23 +      +-----------------------------+
    1.24 +      |       Audio meta-data       |
    1.25 +      | (var. length >= 7868 bytes) |
    1.26 +      +-----------------------------+
    1.27 +      |   Data offsets (20 bytes)   |
    1.28 +      +-----------------------------+
    1.29 +      |      Footer (48 bytes)      |
    1.30 +      +-----------------------------+
    1.31 +     */
    1.32 +    if (length < 48) return 0;
    1.33 +    /* sig: 19 bytes company name + 13 bytes space */
    1.34 +    if (SDL_memcmp(data,"Brava Software Inc.             ",32) != 0) {
    1.35 +        return SDL_FALSE;
    1.36 +    }
    1.37 +    /* 4 bytes version: x.xx */
    1.38 +    if (!SDL_isdigit(data[32]) || data[33] != '.' ||
    1.39 +        !SDL_isdigit(data[34]) ||!SDL_isdigit(data[35])) {
    1.40 +        return SDL_FALSE;
    1.41 +    }
    1.42 +    /* [36..47]: 12 bytes trailing space */
    1.43 +    return SDL_TRUE;
    1.44 +}
    1.45 +static SDL_INLINE long get_musicmatch_len(struct mp3file_t *m) {
    1.46 +    const Sint32 metasizes[4] = { 7868, 7936, 8004, 8132 };
    1.47 +    const unsigned char syncstr[10] = {'1','8','2','7','3','6','4','5',0,0};
    1.48 +    unsigned char buf[20];
    1.49 +    Sint32 i, imgext_ofs, version_ofs;
    1.50 +    long len;
    1.51 +
    1.52 +    /* calc. the image extension section ofs */
    1.53 +    MP3_RWseek(m, -68, RW_SEEK_END);
    1.54 +    MP3_RWread(m, buf, 1, 20);
    1.55 +    imgext_ofs  = (Sint32)((buf[3] <<24) | (buf[2] <<16) | (buf[1] <<8) | buf[0] );
    1.56 +    version_ofs = (Sint32)((buf[15]<<24) | (buf[14]<<16) | (buf[13]<<8) | buf[12]);
    1.57 +    /* Try finding the version info section:
    1.58 +     * Because metadata section comes after it, and because metadata section
    1.59 +     * has different sizes across versions (format ver. <= 3.00: always 7868
    1.60 +     * bytes), we can _not_ directly calculate using deltas from the offsets
    1.61 +     * section. */
    1.62 +    for (i = 0; i < 4; ++i) {
    1.63 +    /* 48: footer, 20: offsets, 256: version info */
    1.64 +        len = metasizes[i] + 48 + 20 + 256;
    1.65 +        if (m->length < len) return -1;
    1.66 +        MP3_RWseek(m, -len, RW_SEEK_END);
    1.67 +        MP3_RWread(m, buf, 1, 10);
    1.68 +        /* [0..9]: sync string, [30..255]: 0x20 */
    1.69 +        if (SDL_memcmp(buf, syncstr, 10) == 0) {
    1.70 +            break;
    1.71 +        }
    1.72 +    }
    1.73 +    if (i == 4) return -1; /* no luck. */
    1.74 +    len += (version_ofs - imgext_ofs);
    1.75 +    if (m->length < len) return -1;
    1.76 +    if (m->length < len + 256) return len;
    1.77 +    /* try finding the optional header */
    1.78 +    MP3_RWseek(m, -(len + 256), RW_SEEK_END);
    1.79 +    MP3_RWread(m, buf, 1, 10);
    1.80 +    /* [0..9]: sync string, [30..255]: 0x20 */
    1.81 +    if (SDL_memcmp(buf, syncstr, 10) != 0) {
    1.82 +        return len;
    1.83 +    }
    1.84 +    return len + 256; /* header is present. */
    1.85 +}
    1.86  
    1.87  int mp3_skiptags(struct mp3file_t *fil)
    1.88  {
    1.89 @@ -208,6 +287,19 @@
    1.90      /* do we know whether ape or lyrics3 is the first?
    1.91       * well, we don't: we need to handle that later... */
    1.92  
    1.93 +    /* check for the _old_ MusicMatch tag at end. */
    1.94 +    if (fil->length >= 68) {
    1.95 +        MP3_RWseek(fil, -48, RW_SEEK_END);
    1.96 +        readsize = MP3_RWread(fil, buf, 1, 48);
    1.97 +        if (readsize != 48) goto fail;
    1.98 +        if (is_musicmatch(buf, 48)) {
    1.99 +            len = get_musicmatch_len(fil);
   1.100 +            if (len < 0) goto fail;
   1.101 +            if (len >= fil->length) goto fail;
   1.102 +            fil->length -= len;
   1.103 +        }
   1.104 +    }
   1.105 +
   1.106      /* APE tag may be at the end: read the footer */
   1.107      if (fil->length >= 32) {
   1.108          MP3_RWseek(fil, -32, RW_SEEK_END);