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