src/codecs/mp3utils.c
author Ozkan Sezer
Sun, 22 Dec 2019 01:56:51 +0300
changeset 1111 e56a612a3daa
parent 1103 4e1c2282e6f1
child 1123 d40dacdd4ab1
permissions -rw-r--r--
mp3utils.c: very minor clean-up.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #include "SDL_stdinc.h"
    23 #include "SDL_rwops.h"
    24 
    25 #include "mp3utils.h"
    26 
    27 #if defined(MUSIC_MP3_MAD) || defined(MUSIC_MP3_MPG123)
    28 
    29 /*********************** SDL_RW WITH BOOKKEEPING ************************/
    30 
    31 size_t MP3_RWread(struct mp3file_t *fil, void *ptr, size_t size, size_t maxnum) {
    32     size_t remaining = (size_t)(fil->length - fil->pos);
    33     size_t ret;
    34     maxnum *= size;
    35     if (maxnum > remaining) maxnum = remaining;
    36     ret = SDL_RWread(fil->src, ptr, 1, maxnum);
    37     fil->pos += (Sint64)ret;
    38     return ret;
    39 }
    40 
    41 Sint64 MP3_RWseek(struct mp3file_t *fil, Sint64 offset, int whence) {
    42     Sint64 ret;
    43     switch (whence) {
    44     case RW_SEEK_CUR:
    45         offset += fil->pos;
    46         break;
    47     case RW_SEEK_END:
    48         offset += fil->length;
    49         break;
    50     }
    51     if (offset < 0) return -1;
    52     if (offset > fil->length)
    53         offset = fil->length;
    54     ret = SDL_RWseek(fil->src, fil->start + offset, RW_SEEK_SET);
    55     if (ret < 0) return ret;
    56     fil->pos = offset;
    57     return (fil->pos - fil->start);
    58 }
    59 
    60 
    61 /*************************** TAG HANDLING: ******************************/
    62 
    63 static SDL_INLINE SDL_bool is_id3v1(const unsigned char *data, long length) {
    64     /* http://id3.org/ID3v1 :  3 bytes "TAG" identifier and 125 bytes tag data */
    65     if (length < 3 || SDL_memcmp(data,"TAG",3) != 0) {
    66         return SDL_FALSE;
    67     }
    68     return SDL_TRUE;
    69 }
    70 static SDL_bool is_id3v2(const unsigned char *data, size_t length) {
    71     /* ID3v2 header is 10 bytes:  http://id3.org/id3v2.4.0-structure */
    72     /* bytes 0-2: "ID3" identifier */
    73     if (length < 10 || SDL_memcmp(data,"ID3",3) != 0) {
    74         return SDL_FALSE;
    75     }
    76     /* bytes 3-4: version num (major,revision), each byte always less than 0xff. */
    77     if (data[3] == 0xff || data[4] == 0xff) {
    78         return SDL_FALSE;
    79     }
    80     /* bytes 6-9 are the ID3v2 tag size: a 32 bit 'synchsafe' integer, i.e. the
    81      * highest bit 7 in each byte zeroed.  i.e.: 7 bit information in each byte ->
    82      * effectively a 28 bit value.  */
    83     if (data[6] >= 0x80 || data[7] >= 0x80 || data[8] >= 0x80 || data[9] >= 0x80) {
    84         return SDL_FALSE;
    85     }
    86     return SDL_TRUE;
    87 }
    88 static long get_id3v2_len(const unsigned char *data, long length) {
    89     /* size is a 'synchsafe' integer (see above) */
    90     long size = (long)((data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9]);
    91     size += 10; /* header size */
    92     /* ID3v2 header[5] is flags (bits 4-7 only, 0-3 are zero).
    93      * bit 4 set: footer is present (a copy of the header but
    94      * with "3DI" as ident.)  */
    95     if (data[5] & 0x10) {
    96         size += 10; /* footer size */
    97     }
    98     /* optional padding (always zeroes) */
    99     while (size < length && data[size] == 0) {
   100         ++size;
   101     }
   102     return size;
   103 }
   104 static SDL_bool is_apetag(const unsigned char *data, size_t length) {
   105    /* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification
   106     * Header/footer is 32 bytes: bytes 0-7 ident, bytes 8-11 version,
   107     * bytes 12-17 size. bytes 24-31 are reserved: must be all zeroes. */
   108     Uint32 v;
   109 
   110     if (length < 32 || SDL_memcmp(data,"APETAGEX",8) != 0) {
   111         return SDL_FALSE;
   112     }
   113     v = (Uint32)((data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]); /* version */
   114     if (v != 2000U && v != 1000U) {
   115         return SDL_FALSE;
   116     }
   117     v = 0; /* reserved bits : */
   118     if (SDL_memcmp(&data[24],&v,4) != 0 || SDL_memcmp(&data[28],&v,4) != 0) {
   119         return SDL_FALSE;
   120     }
   121     return SDL_TRUE;
   122 }
   123 static long get_ape_len(const unsigned char *data) {
   124     Uint32 flags, version;
   125     long size = (long)((data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12]);
   126     version = (Uint32)((data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]);
   127     flags = (Uint32)((data[23]<<24) | (data[22]<<16) | (data[21]<<8) | data[20]);
   128     if (version == 2000U && (flags & (1U<<31))) size += 32; /* header present. */
   129     return size;
   130 }
   131 static SDL_INLINE int is_lyrics3tag(const unsigned char *data, long length) {
   132     /* http://id3.org/Lyrics3
   133      * http://id3.org/Lyrics3v2 */
   134     if (length < 15) return 0;
   135     if (SDL_memcmp(data+6,"LYRICS200",9) == 0) return 2; /* v2 */
   136     if (SDL_memcmp(data+6,"LYRICSEND",9) == 0) return 1; /* v1 */
   137     return 0;
   138 }
   139 static long get_lyrics3v1_len(struct mp3file_t *m) {
   140     const char *p; long i, len;
   141     char buf[5104];
   142     /* needs manual search:  http://id3.org/Lyrics3 */
   143     if (m->length < 20) return -1;
   144     len = (m->length > 5109)? 5109 : (long)m->length;
   145     MP3_RWseek(m, -len, RW_SEEK_END);
   146     MP3_RWread(m, buf, 1, (len -= 9)); /* exclude footer */
   147     /* strstr() won't work here. */
   148     for (i = len - 11, p = buf; i >= 0; --i, ++p) {
   149         if (SDL_memcmp(p, "LYRICSBEGIN", 11) == 0)
   150             break;
   151     }
   152     if (i < 0) return -1;
   153     return len - (long)(p - buf) + 9 /* footer */;
   154 }
   155 static SDL_INLINE long get_lyrics3v2_len(const unsigned char *data, long length) {
   156     /* 6 bytes before the end marker is size in decimal format -
   157      * does not include the 9 bytes end marker and size field. */
   158     if (length != 6) return 0;
   159     return SDL_strtol((const char *)data, NULL, 10) + 15;
   160 }
   161 static SDL_INLINE SDL_bool verify_lyrics3v2(const unsigned char *data, long length) {
   162     if (length < 11) return SDL_FALSE;
   163     if (SDL_memcmp(data,"LYRICSBEGIN",11) == 0) return SDL_TRUE;
   164     return SDL_FALSE;
   165 }
   166 #define MMTAG_PARANOID
   167 static SDL_bool is_musicmatch(const unsigned char *data, long length) {
   168   /* From docs/musicmatch.txt in id3lib: https://sourceforge.net/projects/id3lib/
   169      Overall tag structure:
   170 
   171       +-----------------------------+
   172       |           Header            |
   173       |    (256 bytes, OPTIONAL)    |
   174       +-----------------------------+
   175       |  Image extension (4 bytes)  |
   176       +-----------------------------+
   177       |        Image binary         |
   178       |  (var. length >= 4 bytes)   |
   179       +-----------------------------+
   180       |      Unused (4 bytes)       |
   181       +-----------------------------+
   182       |  Version info (256 bytes)   |
   183       +-----------------------------+
   184       |       Audio meta-data       |
   185       | (var. length >= 7868 bytes) |
   186       +-----------------------------+
   187       |   Data offsets (20 bytes)   |
   188       +-----------------------------+
   189       |      Footer (48 bytes)      |
   190       +-----------------------------+
   191      */
   192     if (length < 48) return SDL_FALSE;
   193     /* sig: 19 bytes company name + 13 bytes space */
   194     if (SDL_memcmp(data,"Brava Software Inc.             ",32) != 0) {
   195         return SDL_FALSE;
   196     }
   197     /* 4 bytes version: x.xx */
   198     if (!SDL_isdigit(data[32]) || data[33] != '.' ||
   199         !SDL_isdigit(data[34]) ||!SDL_isdigit(data[35])) {
   200         return SDL_FALSE;
   201     }
   202     #ifdef MMTAG_PARANOID
   203     /* [36..47]: 12 bytes trailing space */
   204     for (length = 36; length < 48; ++length) {
   205         if (data[length] != ' ') return SDL_FALSE;
   206     }
   207     #endif
   208     return SDL_TRUE;
   209 }
   210 static long get_musicmatch_len(struct mp3file_t *m) {
   211     const Sint32 metasizes[4] = { 7868, 7936, 8004, 8132 };
   212     const unsigned char syncstr[10] = {'1','8','2','7','3','6','4','5',0,0};
   213     unsigned char buf[256];
   214     Sint32 i, j, imgext_ofs, version_ofs;
   215     long len;
   216 
   217     MP3_RWseek(m, -68, RW_SEEK_END);
   218     MP3_RWread(m, buf, 1, 20);
   219     imgext_ofs  = (Sint32)((buf[3] <<24) | (buf[2] <<16) | (buf[1] <<8) | buf[0] );
   220     version_ofs = (Sint32)((buf[15]<<24) | (buf[14]<<16) | (buf[13]<<8) | buf[12]);
   221     if (version_ofs <= imgext_ofs) return -1;
   222     if (version_ofs <= 0 || imgext_ofs <= 0) return -1;
   223     /* Try finding the version info section:
   224      * Because metadata section comes after it, and because metadata section
   225      * has different sizes across versions (format ver. <= 3.00: always 7868
   226      * bytes), we can _not_ directly calculate using deltas from the offsets
   227      * section. */
   228     for (i = 0; i < 4; ++i) {
   229     /* 48: footer, 20: offsets, 256: version info */
   230         len = metasizes[i] + 48 + 20 + 256;
   231         if (m->length < len) return -1;
   232         MP3_RWseek(m, -len, RW_SEEK_END);
   233         MP3_RWread(m, buf, 1, 256);
   234         /* [0..9]: sync string, [30..255]: 0x20 */
   235         #ifdef MMTAG_PARANOID
   236         for (j = 30; j < 256; ++j) {
   237             if (buf[j] != ' ') break;
   238         }
   239         if (j < 256) continue;
   240         #endif
   241         if (SDL_memcmp(buf, syncstr, 10) == 0) {
   242             break;
   243         }
   244     }
   245     if (i == 4) return -1; /* no luck. */
   246     #ifdef MMTAG_PARANOID
   247     /* unused section: (4 bytes of 0x00) */
   248     MP3_RWseek(m, -(len + 4), RW_SEEK_END);
   249     MP3_RWread(m, buf, 1, 4); j = 0;
   250     if (SDL_memcmp(buf, &j, 4) != 0) return -1;
   251     #endif
   252     len += (version_ofs - imgext_ofs);
   253     if (m->length < len) return -1;
   254     MP3_RWseek(m, -len, RW_SEEK_END);
   255     MP3_RWread(m, buf, 1, 8);
   256     j = (Sint32)((buf[7] <<24) | (buf[6] <<16) | (buf[5] <<8) | buf[4]);
   257     if (j < 0) return -1;
   258     /* verify image size: */
   259     /* without this, we may land at a wrong place. */
   260     if (j + 12 != version_ofs - imgext_ofs) return -1;
   261     /* try finding the optional header */
   262     if (m->length < len + 256) return len;
   263     MP3_RWseek(m, -(len + 256), RW_SEEK_END);
   264     MP3_RWread(m, buf, 1, 256);
   265     /* [0..9]: sync string, [30..255]: 0x20 */
   266     if (SDL_memcmp(buf, syncstr, 10) != 0) {
   267         return len;
   268     }
   269     #ifdef MMTAG_PARANOID
   270     for (j = 30; j < 256; ++j) {
   271         if (buf[j] != ' ') return len;
   272     }
   273     #endif
   274     return len + 256; /* header is present. */
   275 }
   276 
   277 static int probe_id3v1(struct mp3file_t *fil, unsigned char *buf) {
   278     if (fil->length >= 128) {
   279         MP3_RWseek(fil, -128, RW_SEEK_END);
   280         if (MP3_RWread(fil, buf, 1, 128) != 128)
   281             return -1;
   282         if (is_id3v1(buf, 128)) {
   283             fil->length -= 128;
   284             return 1;
   285             /* FIXME: handle possible double-ID3v1 tags?? */
   286         }
   287     }
   288     return 0;
   289 }
   290 static int probe_mmtag(struct mp3file_t *fil, unsigned char *buf) {
   291     long len;
   292     if (fil->length >= 68) {
   293         MP3_RWseek(fil, -48, RW_SEEK_END);
   294         if (MP3_RWread(fil, buf, 1, 48) != 48)
   295             return -1;
   296         if (is_musicmatch(buf, 48)) {
   297             len = get_musicmatch_len(fil);
   298             if (len < 0) return -1;
   299             if (len >= fil->length) return -1;
   300             fil->length -= len;
   301             return 1;
   302         }
   303     }
   304     return 0;
   305 }
   306 static int probe_apetag(struct mp3file_t *fil, unsigned char *buf) {
   307     long len;
   308     if (fil->length >= 32) {
   309         MP3_RWseek(fil, -32, RW_SEEK_END);
   310         if (MP3_RWread(fil, buf, 1, 32) != 32)
   311             return -1;
   312         if (is_apetag(buf, 32)) {
   313             len = get_ape_len(buf);
   314             if (len >= fil->length) return -1;
   315             fil->length -= len;
   316             return 1;
   317         }
   318     }
   319     return 0;
   320 }
   321 static int probe_lyrics3(struct mp3file_t *fil, unsigned char *buf) {
   322     long len;
   323     if (fil->length >= 15) {
   324         MP3_RWseek(fil, -15, RW_SEEK_END);
   325         if (MP3_RWread(fil, buf, 1, 15) != 15)
   326             return -1;
   327         len = is_lyrics3tag(buf, 15);
   328         if (len == 2) {
   329             len = get_lyrics3v2_len(buf, 6);
   330             if (len >= fil->length) return -1;
   331             if (len < 15) return -1;
   332             MP3_RWseek(fil, -len, RW_SEEK_END);
   333             if (MP3_RWread(fil, buf, 1, 11) != 11)
   334                 return -1;
   335             if (!verify_lyrics3v2(buf, 11)) return -1;
   336             fil->length -= len;
   337             return 1;
   338         }
   339         else if (len == 1) {
   340             len = get_lyrics3v1_len(fil);
   341             if (len < 0) return -1;
   342             fil->length -= len;
   343             return 1;
   344         }
   345     }
   346     return 0;
   347 }
   348 
   349 int mp3_skiptags(struct mp3file_t *fil, SDL_bool keep_id3v2)
   350 {
   351     unsigned char buf[128];
   352     long len; size_t readsize;
   353     int c_id3, c_ape, c_lyr, c_mm;
   354     int rc = -1;
   355 
   356     /* MP3 standard has no metadata format, so everyone invented
   357      * their own thing, even with extensions, until ID3v2 became
   358      * dominant: Hence the impossible mess here.
   359      *
   360      * Note: I don't yet care about freaky broken mp3 files with
   361      * double tags. -- O.S.
   362      */
   363 
   364     readsize = MP3_RWread(fil, buf, 1, 128);
   365     if (!readsize) goto fail;
   366 
   367     /* ID3v2 tag is at the start */
   368     if (is_id3v2(buf, readsize)) {
   369         len = get_id3v2_len(buf, (long)readsize);
   370         if (len >= fil->length) goto fail;
   371         if (! keep_id3v2) {
   372             fil->start  += len;
   373             fil->length -= len;
   374         }
   375     }
   376     /* APE tag _might_ be at the start (discouraged
   377      * but not forbidden, either.)  read the header. */
   378     else if (is_apetag(buf, readsize)) {
   379         len = get_ape_len(buf);
   380         if (len >= fil->length) goto fail;
   381         fil->start += len;
   382         fil->length -= len;
   383     }
   384 
   385     /* it's not impossible that _old_ MusicMatch tag
   386      * placing itself after ID3v1. */
   387     if ((c_mm = probe_mmtag(fil, buf)) < 0) {
   388         goto fail;
   389     }
   390     /* ID3v1 tag is at the end */
   391     if ((c_id3 = probe_id3v1(fil, buf)) < 0) {
   392         goto fail;
   393     }
   394     /* we do not know the order of ape or lyrics3
   395      * or musicmatch tags, hence the loop here.. */
   396     c_ape = 0;
   397     c_lyr = 0;
   398     for (;;) {
   399         if (!c_lyr) {
   400         /* care about mp3s with double Lyrics3 tags? */
   401             if ((c_lyr = probe_lyrics3(fil, buf)) < 0)
   402                 goto fail;
   403             if (c_lyr) continue;
   404         }
   405         if (!c_mm) {
   406             if ((c_mm = probe_mmtag(fil, buf)) < 0)
   407                 goto fail;
   408             if (c_mm) continue;
   409         }
   410         if (!c_ape) {
   411             if ((c_ape = probe_apetag(fil, buf)) < 0)
   412                 goto fail;
   413             if (c_ape) continue;
   414         }
   415         break;
   416     } /* for (;;) */
   417 
   418     rc = (fil->length > 0)? 0 : -1;
   419     fail:
   420     MP3_RWseek(fil, 0, RW_SEEK_SET);
   421     return rc;
   422 }
   423 #endif /* MUSIC_MP3_??? */
   424 
   425 /* vi: set ts=4 sw=4 expandtab: */