Mercurial > audlegacy
diff Plugins/Input/mpg123/id3_frame.c @ 61:fa848bd484d8 trunk
[svn] Move plugins to Plugins/
author | nenolod |
---|---|
date | Fri, 28 Oct 2005 22:58:11 -0700 |
parents | |
children | d539e5c5f730 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/mpg123/id3_frame.c Fri Oct 28 22:58:11 2005 -0700 @@ -0,0 +1,765 @@ +/********************************************************************* + * + * Copyright (C) 1999-2000, 2001, Espen Skoglund + * Department of Computer Science, University of Tromsų + * + * Filename: id3_frame.c + * Description: Code for handling ID3 frames. + * Author: Espen Skoglund <espensk@stud.cs.uit.no> + * Created at: Fri Feb 5 23:47:08 1999 + * + * $Id: id3_frame.c,v 1.5 2004/07/20 21:47:22 descender Exp $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ********************************************************************/ +#include "config.h" + +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif +#include <glib.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "xmms-id3.h" +#include "id3_header.h" + +static void *id3_frame_get_dataptr(struct id3_frame *frame); +static int id3_frame_get_size(struct id3_frame *frame); +static int id3_read_frame_v22(struct id3_tag *id3); + + +/* + * Description of all valid ID3v2 frames. + */ +static struct id3_framedesc framedesc[] = { + {ID3_AENC, "AENC", "Audio encryption"}, + {ID3_APIC, "APIC", "Attached picture"}, + {ID3_ASPI, "ASPI", "Audio seek point index"}, /* v4 only */ + + {ID3_COMM, "COMM", "Comments"}, + {ID3_COMR, "COMR", "Commercial frame"}, + + {ID3_ENCR, "ENCR", "Encryption method registration"}, + {ID3_EQUA, "EQUA", "Equalization"}, /* v3 only */ + {ID3_EQU2, "EQU2", "Equalization (2)"}, /* v4 only */ + {ID3_ETCO, "ETCO", "Event timing codes"}, + + {ID3_GEOB, "GEOB", "General encapsulated object"}, + {ID3_GRID, "GRID", "Group identification registration"}, + + {ID3_IPLS, "IPLS", "Involved people list"}, /* v3 only */ + + {ID3_LINK, "LINK", "Linked information"}, + + {ID3_MCDI, "MCDI", "Music CD identifier"}, + {ID3_MLLT, "MLLT", "MPEG location lookup table"}, + + {ID3_OWNE, "OWNE", "Ownership frame"}, + + {ID3_PRIV, "PRIV", "Private frame"}, + {ID3_PCNT, "PCNT", "Play counter"}, + {ID3_POPM, "POPM", "Popularimeter"}, + {ID3_POSS, "POSS", "Position synchronisation frame"}, + + {ID3_RBUF, "RBUF", "Recommended buffer size"}, + {ID3_RVAD, "RVAD", "Relative volume adjustment"}, /* v3 only */ + {ID3_RVA2, "RVA2", "RVA2 Relative volume adjustment (2)"}, /* v4 only */ + {ID3_RVRB, "RVRB", "Reverb"}, + + {ID3_SEEK, "SEEK", "Seek frame"}, /* v4 only */ + {ID3_SIGN, "SIGN", "Signature frame"}, /* v4 only */ + {ID3_SYLT, "SYLT", "Synchronized lyric/text"}, + {ID3_SYTC, "SYTC", "Synchronized tempo codes"}, + + {ID3_TALB, "TALB", "Album/Movie/Show title"}, + {ID3_TBPM, "TBPM", "BPM (beats per minute)"}, + {ID3_TCOM, "TCOM", "Composer"}, + {ID3_TCON, "TCON", "Content type"}, + {ID3_TCOP, "TCOP", "Copyright message"}, + {ID3_TDAT, "TDAT", "Date"}, /* v3 only */ + {ID3_TDEN, "TDEN", "Encoding time"}, /* v4 only */ + {ID3_TDLY, "TDLY", "Playlist delay"}, + {ID3_TDOR, "TDOR", "Original release time"}, /* v4 only */ + {ID3_TDRC, "TDRC", "Recording time"}, /* v4 only */ + {ID3_TDRL, "TDRL", "Release time"}, /* v4 only */ + {ID3_TDTG, "TDTG", "Tagging time"}, /* v4 only */ + + {ID3_TENC, "TENC", "Encoded by"}, + {ID3_TEXT, "TEXT", "Lyricist/Text writer"}, + {ID3_TFLT, "TFLT", "File type"}, + {ID3_TIME, "TIME", "Time"}, /* v3 only */ + {ID3_TIPL, "TIPL", "Involved people list"}, /* v4 only */ + {ID3_TIT1, "TIT1", "Content group description"}, + {ID3_TIT2, "TIT2", "Title/songname/content description"}, + {ID3_TIT3, "TIT3", "Subtitle/Description refinement"}, + {ID3_TKEY, "TKEY", "Initial key"}, + {ID3_TLAN, "TLAN", "Language(s)"}, + {ID3_TLEN, "TLEN", "Length"}, + {ID3_TMCL, "TMCL", "Musician credits list"}, /* v4 only */ + {ID3_TMOO, "TMOO", "Mood"}, /* v4 only */ + {ID3_TMED, "TMED", "Media type"}, + {ID3_TOAL, "TOAL", "Original album/movie/show title"}, + {ID3_TOFN, "TOFN", "Original filename"}, + {ID3_TOLY, "TOLY", "Original lyricist(s)/text writer(s)"}, + {ID3_TOPE, "TOPE", "Original artist(s)/performer(s)"}, + {ID3_TORY, "TORY", "Original release year"}, /* v3 only */ + {ID3_TOWN, "TOWN", "File owner/licensee"}, + {ID3_TPE1, "TPE1", "Lead performer(s)/Soloist(s)"}, + {ID3_TPE2, "TPE2", "Band/orchestra/accompaniment"}, + {ID3_TPE3, "TPE3", "Conductor/performer refinement"}, + {ID3_TPE4, "TPE4", "Interpreted, remixed, or otherwise modified by"}, + {ID3_TPOS, "TPOS", "Part of a set"}, + {ID3_TPRO, "TPRO", "Produced notice"}, /* v4 only */ + {ID3_TPUB, "TPUB", "Publisher"}, + {ID3_TRCK, "TRCK", "Track number/Position in set"}, + {ID3_TRDA, "TRDA", "Recording dates"}, /* v3 only */ + {ID3_TRSN, "TRSN", "Internet radio station name"}, + {ID3_TRSO, "TRSO", "Internet radio station owner"}, + {ID3_TSIZ, "TSIZ", "Size"}, /* v3 only */ + {ID3_TSOA, "TSOA", "Album sort order"}, /* v4 only */ + {ID3_TSOP, "TSOP", "Performer sort order"}, /* v4 only */ + {ID3_TSOT, "TSOT", "Title sort order"}, /* v4 only */ + + {ID3_TSRC, "TSRC", "ISRC (international standard recording code)"}, + {ID3_TSSE, "TSSE", "Software/Hardware and settings used for encoding"}, + {ID3_TSST, "TSST", "Set subtitle"}, /* v4 only */ + {ID3_TYER, "TYER", "Year"}, /* v3 only */ + {ID3_TXXX, "TXXX", "User defined text information frame"}, + + {ID3_UFID, "UFID", "Unique file identifier"}, + {ID3_USER, "USER", "Terms of use"}, + {ID3_USLT, "USLT", "Unsychronized lyric/text transcription"}, + + {ID3_WCOM, "WCOM", "Commercial information"}, + {ID3_WCOP, "WCOP", "Copyright/Legal information"}, + {ID3_WOAF, "WOAF", "Official audio file webpage"}, + {ID3_WOAR, "WOAR", "Official artist/performer webpage"}, + {ID3_WOAS, "WOAS", "Official audio source webpage"}, + {ID3_WORS, "WORS", "Official internet radio station homepage"}, + {ID3_WPAY, "WPAY", "Payment"}, + {ID3_WPUB, "WPUB", "Publishers official webpage"}, + {ID3_WXXX, "WXXX", "User defined URL link frame"}, +}; + +struct id3_framedesc22 { + guint32 fd_v22, fd_v24; +}; + +static struct id3_framedesc22 framedesc22[] = { + {ID3_BUF, ID3_RBUF}, /* Recommended buffer size */ + + {ID3_CNT, ID3_PCNT}, /* Play counter */ + {ID3_COM, ID3_COMM}, /* Comments */ + {ID3_CRA, ID3_AENC}, /* Audio encryption */ + {ID3_CRM, 0}, /* Encrypted meta frame */ + + {ID3_ETC, ID3_ETCO}, /* Event timing codes */ + /* Could be converted to EQU2 */ + {ID3_EQU, 0}, /* Equalization */ + + {ID3_GEO, ID3_GEOB}, /* General encapsulated object */ + + /* Would need conversion to TIPL */ + {ID3_IPL, 0}, /* Involved people list */ + + /* This is so fragile it's not worth trying to save */ + {ID3_LNK, 0}, /* Linked information */ + + {ID3_MCI, ID3_MCDI}, /* Music CD Identifier */ + {ID3_MLL, ID3_MLLT}, /* MPEG location lookup table */ + + /* Would need to convert header for APIC */ + {ID3_PIC, 0}, /* Attached picture */ + {ID3_POP, ID3_POPM}, /* Popularimeter */ + + {ID3_REV, ID3_RVRB}, /* Reverb */ + /* Could be converted to RVA2 */ + {ID3_RVA, 0}, /* Relative volume adjustment */ + + {ID3_SLT, ID3_SYLT}, /* Synchronized lyric/text */ + {ID3_STC, ID3_SYTC}, /* Synced tempo codes */ + + {ID3_TAL, ID3_TALB}, /* Album/Movie/Show title */ + {ID3_TBP, ID3_TBPM}, /* BPM (Beats Per Minute) */ + {ID3_TCM, ID3_TCOM}, /* Composer */ + {ID3_TCO, ID3_TCON}, /* Content type */ + {ID3_TCR, ID3_TCOP}, /* Copyright message */ + /* This could be incorporated into TDRC */ + {ID3_TDA, 0}, /* Date */ + {ID3_TDY, ID3_TDLY}, /* Playlist delay */ + {ID3_TEN, ID3_TENC}, /* Encoded by */ + {ID3_TFT, ID3_TFLT}, /* File type */ + /* This could be incorporated into TDRC */ + {ID3_TIM, 0}, /* Time */ + {ID3_TKE, ID3_TKEY}, /* Initial key */ + {ID3_TLA, ID3_TLAN}, /* Language(s) */ + {ID3_TLE, ID3_TLEN}, /* Length */ + {ID3_TMT, ID3_TMED}, /* Media type */ + {ID3_TOA, ID3_TOPE}, /* Original artist(s)/performer(s) */ + {ID3_TOF, ID3_TOFN}, /* Original filename */ + {ID3_TOL, ID3_TOLY}, /* Original Lyricist(s)/text writer(s) */ + /* + * The docs says that original release year should be in + * milliseconds! Hopefully that is a typo. + */ + {ID3_TOR, ID3_TDOR}, /* Original release year */ + {ID3_TOT, ID3_TOAL}, /* Original album/Movie/Show title */ + {ID3_TP1, ID3_TPE1}, /* Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group */ + {ID3_TP2, ID3_TPE2}, /* Band/Orchestra/Accompaniment */ + {ID3_TP3, ID3_TPE3}, /* Conductor/Performer refinement */ + {ID3_TP4, ID3_TPE4}, /* Interpreted, remixed, or otherwise modified by */ + {ID3_TPA, ID3_TPOS}, /* Part of a set */ + {ID3_TPB, ID3_TPUB}, /* Publisher */ + {ID3_TRC, ID3_TSRC}, /* ISRC (International Standard Recording Code) */ + {ID3_TRD, 0}, /* Recording dates */ + {ID3_TRK, ID3_TRCK}, /* Track number/Position in set */ + {ID3_TSI, 0}, /* Size */ + {ID3_TSS, ID3_TSSE}, /* Software/hardware and settings used for encoding */ + {ID3_TT1, ID3_TIT1}, /* Content group description */ + {ID3_TT2, ID3_TIT2}, /* Title/Songname/Content description */ + {ID3_TT3, ID3_TIT3}, /* Subtitle/Description refinement */ + {ID3_TXT, ID3_TEXT}, /* Lyricist/text writer */ + {ID3_TXX, ID3_TXXX}, /* User defined text information frame */ + {ID3_TYE, ID3_TDRC}, /* Year */ + + {ID3_UFI, ID3_UFID}, /* Unique file identifier */ + {ID3_ULT, ID3_USLT}, /* Unsychronized lyric/text transcription */ + + {ID3_WAF, ID3_WOAF}, /* Official audio file webpage */ + {ID3_WAR, ID3_WOAR}, /* Official artist/performer webpage */ + {ID3_WAS, ID3_WOAS}, /* Official audio source webpage */ + {ID3_WCM, ID3_WCOM}, /* Commercial information */ + {ID3_WCP, ID3_WCOP}, /* Copyright/Legal information */ + {ID3_WPB, ID3_WPUB}, /* Publishers official webpage */ + {ID3_WXX, ID3_WXXX}, /* User defined URL link frame */ +}; + +static struct id3_framedesc * +find_frame_description(guint32 id) +{ + int i; + for (i = 0; i < sizeof(framedesc) / sizeof(struct id3_framedesc); i++) + if (framedesc[i].fd_id == id) + return &framedesc[i]; + return NULL; +} + + +/* + * Function id3_read_frame (id3) + * + * Read next frame from the indicated ID3 tag. Return 0 upon + * success, or -1 if an error occured. + * + */ +int +id3_read_frame(struct id3_tag *id3) +{ + struct id3_frame *frame; + guint32 id; + char *buf; + + if (id3->id3_version == 2) + return id3_read_frame_v22(id3); + + /* + * Read frame header. + */ + buf = id3->id3_read(id3, NULL, ID3_FRAMEHDR_SIZE); + if (buf == NULL) + return -1; + + /* + * If we encounter an invalid frame id, we assume that there is + * some padding in the header. We just skip the rest of the ID3 + * tag. + */ + if (!((buf[0] >= '0' && buf[0] <= '9') + || (buf[0] >= 'A' && buf[0] <= 'Z'))) { + id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); + return 0; + } + id = ID3_FRAME_ID(buf[0], buf[1], buf[2], buf[3]); + + /* + * Allocate frame. + */ + frame = g_malloc0(sizeof(*frame)); + + frame->fr_owner = id3; + /* FIXME v2.4.0 */ + frame->fr_raw_size = buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7]; + if (frame->fr_raw_size < 0 || frame->fr_raw_size > 1000000) { + g_free(frame); + return -1; + } + frame->fr_flags = buf[8] << 8 | buf[9]; + + /* + * Determine the type of the frame. + */ + + frame->fr_desc = find_frame_description(id); + + /* + * Check if frame had a valid id. + */ + if (frame->fr_desc == NULL) { + /* + * No. Ignore the frame. + */ + if (id3->id3_seek(id3, frame->fr_raw_size) < 0) { + g_free(frame); + return -1; + } + return 0; + } + + /* + * Initialize frame. + */ + + /* + * We allocate 2 extra bytes. This simplifies retrieval of + * text strings. + */ + frame->fr_raw_data = g_malloc0(frame->fr_raw_size + 2); + if (id3->id3_read(id3, frame->fr_raw_data, frame->fr_raw_size) == NULL) { + g_free(frame->fr_raw_data); + g_free(frame); + return -1; + } + + /* + * Insert frame into linked list. + */ + id3->id3_frame = g_list_append(id3->id3_frame, frame); + + /* + * Check if frame is compressed using zlib. + */ + if (frame->fr_flags & ID3_FHFLAG_COMPRESS) + return 0; + + frame->fr_data = id3_frame_get_dataptr(frame); + frame->fr_size = id3_frame_get_size(frame); + + return 0; +} + + +/* + * Function id3_get_frame (id3, type, num) + * + * Search in the list of frames for the ID3-tag, and return a frame + * of the indicated type. If tag contains several frames of the + * indicated type, the third argument tells which of the frames to + * return. + * + */ +struct id3_frame * +id3_get_frame(struct id3_tag *id3, guint32 type, int num) +{ + GList *node; + + for (node = id3->id3_frame; node != NULL; node = node->next) { + struct id3_frame *fr = node->data; + if (fr->fr_desc && fr->fr_desc->fd_id == type) { + if (--num <= 0) + return fr; + } + } + return NULL; +} + +/* + * Function decompress_frame(frame) + * + * Uncompress the indicated frame. Return 0 upon success, or -1 if + * an error occured. + * + */ +static int +decompress_frame(struct id3_frame *frame) +{ +#ifdef HAVE_LIBZ + z_stream z; + int r; + + /* + * Fetch the size of the decompressed data. + */ + frame->fr_size_z = g_ntohl(*((guint32 *) frame->fr_raw_data)); + if (frame->fr_size_z < 0 || frame->fr_size_z > 1000000) + return -1; + + /* + * Allocate memory to hold uncompressed frame. + */ + frame->fr_data_z = g_malloc(frame->fr_size_z + + (id3_frame_is_text(frame) ? 2 : 0)); + + /* + * Initialize zlib. + */ + z.next_in = id3_frame_get_dataptr(frame); + z.avail_in = id3_frame_get_size(frame); + z.zalloc = NULL; + z.zfree = NULL; + z.opaque = NULL; + + r = inflateInit(&z); + switch (r) { + case Z_OK: + break; + case Z_MEM_ERROR: + id3_error(frame->fr_owner, "zlib - no memory"); + goto Error_init; + case Z_VERSION_ERROR: + id3_error(frame->fr_owner, "zlib - invalid version"); + goto Error_init; + default: + id3_error(frame->fr_owner, "zlib - unknown error"); + goto Error_init; + } + + /* + * Decompress frame. + */ + z.next_out = frame->fr_data_z; + z.avail_out = frame->fr_size_z; + r = inflate(&z, Z_SYNC_FLUSH); + switch (r) { + case Z_STREAM_END: + break; + case Z_OK: + if (z.avail_in == 0) + /* + * This should not be possible with a correct stream. + * We will be nice however, and try to go on. + */ + break; + id3_error(frame->fr_owner, "zlib - buffer exhausted"); + goto Error_inflate; + default: + id3_error(frame->fr_owner, "zlib - unknown error"); + goto Error_inflate; + } + + r = inflateEnd(&z); + if (r != Z_OK) + id3_error(frame->fr_owner, "zlib - inflateEnd error"); + + /* + * Null-terminate text frames. + */ + if (id3_frame_is_text(frame)) { + ((char *) frame->fr_data_z)[frame->fr_size_z] = 0; + ((char *) frame->fr_data_z)[frame->fr_size_z + 1] = 0; + } + frame->fr_data = frame->fr_data_z; + frame->fr_size = frame->fr_size_z + (id3_frame_is_text(frame) ? 2 : 0); + + return 0; + + /* + * Cleanup code. + */ + Error_inflate: + r = inflateEnd(&z); + Error_init: + g_free(frame->fr_data_z); + frame->fr_data_z = NULL; +#endif + return -1; +} + +/* + * Function id3_decompress_frame(frame) + * + * Check if frame is compressed, and uncompress if necessary. + * Return 0 upon success, or -1 if an error occured. + * + */ +int +id3_decompress_frame(struct id3_frame *frame) +{ + if (!(frame->fr_flags & ID3_FHFLAG_COMPRESS)) + /* Frame not compressed */ + return 0; + if (frame->fr_data_z) + /* Frame already decompressed */ + return 0; + /* Do decompression */ + return decompress_frame(frame); +} + + +/* + * Function id3_delete_frame (frame) + * + * Remove frame from ID3 tag and release memory ocupied by it. + * + */ +int +id3_delete_frame(struct id3_frame *frame) +{ + GList *list = frame->fr_owner->id3_frame; + int ret; + + /* + * Search for frame in list. + */ + + if (g_list_find(list, frame) != NULL) { + /* + * Frame does not exist in frame list. + */ + ret = -1; + + } + else { + /* + * Remove frame from frame list. + */ + list = g_list_remove(list, frame); + frame->fr_owner->id3_altered = 1; + ret = 0; + } + + /* + * Release memory occupied by frame. + */ + if (frame->fr_raw_data) + g_free(frame->fr_raw_data); + if (frame->fr_data_z) + g_free(frame->fr_data_z); + g_free(frame); + + return ret; +} + + +/* + * Function id3_add_frame (id3, type) + * + * Add a new frame to the ID3 tag. Return a pointer to the new + * frame, or NULL if an error occured. + * + */ +struct id3_frame * +id3_add_frame(struct id3_tag *id3, guint32 type) +{ + struct id3_frame *frame; + int i; + + /* + * Allocate frame. + */ + frame = g_malloc0(sizeof(*frame)); + + /* + * Initialize frame + */ + frame->fr_owner = id3; + + /* + * Try finding the correct frame descriptor. + */ + for (i = 0; i < sizeof(framedesc) / sizeof(struct id3_framedesc); i++) { + if (framedesc[i].fd_id == type) { + frame->fr_desc = &framedesc[i]; + break; + } + } + + /* + * Insert frame into linked list. + */ + id3->id3_frame = g_list_append(id3->id3_frame, frame); + id3->id3_altered = 1; + + return frame; +} + + +/* + * Destroy all frames in an id3 tag, and free all data + */ +void +id3_destroy_frames(struct id3_tag *id) +{ + GList *node; + + for (node = id->id3_frame; node != NULL; node = node->next) { + struct id3_frame *frame = node->data; + /* + * Release memory occupied by frame. + */ + if (frame->fr_raw_data) + g_free(frame->fr_raw_data); + if (frame->fr_data_z) + g_free(frame->fr_data_z); + g_free(frame); + } + g_list_free(id->id3_frame); + id->id3_frame = NULL; +} + +static int +id3_frame_extra_headers(struct id3_frame *frame) +{ + int retv = 0; + /* + * If frame is encrypted, we have four extra bytes in the + * header. + */ + if (frame->fr_flags & ID3_FHFLAG_COMPRESS) + retv += 4; + /* + * If frame is encrypted, we have one extra byte in the + * header. + */ + if (frame->fr_flags & ID3_FHFLAG_ENCRYPT) + retv += 1; + + /* + * If frame has grouping identity, we have one extra byte in + * the header. + */ + if (frame->fr_flags & ID3_FHFLAG_GROUP) + retv += 1; + + return retv; +} + +static void * +id3_frame_get_dataptr(struct id3_frame *frame) +{ + char *ptr = frame->fr_raw_data; + + ptr += id3_frame_extra_headers(frame); + + return ptr; +} + +static int +id3_frame_get_size(struct id3_frame *frame) +{ + return frame->fr_raw_size - id3_frame_extra_headers(frame); +} + +void +id3_frame_clear_data(struct id3_frame *frame) +{ + if (frame->fr_raw_data) + g_free(frame->fr_raw_data); + if (frame->fr_data_z) + g_free(frame->fr_data_z); + frame->fr_raw_data = NULL; + frame->fr_raw_size = 0; + frame->fr_data = NULL; + frame->fr_size = 0; + frame->fr_data_z = NULL; + frame->fr_size_z = 0; +} + +static guint32 +find_v24_id(guint32 v22) +{ + int i; + for (i = 0; i < sizeof(framedesc22) / sizeof(framedesc22[0]); i++) + if (framedesc22[i].fd_v22 == v22) + return framedesc22[i].fd_v24; + + return 0; +} + +static int +id3_read_frame_v22(struct id3_tag *id3) +{ + struct id3_frame *frame; + guint32 id, idv24; + char *buf; + int size; + + /* + * Read frame header. + */ + buf = id3->id3_read(id3, NULL, ID3_FRAMEHDR_SIZE_22); + if (buf == NULL) + return -1; + + /* + * If we encounter an invalid frame id, we assume that there + * is some. We just skip the rest of the ID3 tag. + */ + if (!((buf[0] >= '0' && buf[0] <= '9') + || (buf[0] >= 'A' && buf[0] <= 'Z'))) { + id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); + return 0; + } + + id = ID3_FRAME_ID_22(buf[0], buf[1], buf[2]); + size = buf[3] << 16 | buf[4] << 8 | buf[5]; + + if ((idv24 = find_v24_id(id)) == 0) { + if (id3->id3_seek(id3, size) < 0) + return -1; + return 0; + } + + /* + * Allocate frame. + */ + frame = g_malloc0(sizeof(*frame)); + + frame->fr_owner = id3; + frame->fr_raw_size = size; + if (frame->fr_raw_size < 0 || frame->fr_raw_size > 1000000) { + g_free(frame); + return -1; + } + + /* + * Initialize frame. + */ + frame->fr_desc = find_frame_description(idv24); + + /* + * We allocate 2 extra bytes. This simplifies retrieval of + * text strings. + */ + frame->fr_raw_data = g_malloc0(frame->fr_raw_size + 2); + if (id3->id3_read(id3, frame->fr_raw_data, frame->fr_raw_size) == NULL) { + g_free(frame->fr_raw_data); + g_free(frame); + return -1; + } + + /* + * Insert frame into linked list. + */ + id3->id3_frame = g_list_append(id3->id3_frame, frame); + + frame->fr_data = frame->fr_raw_data; + frame->fr_size = frame->fr_raw_size; + + return 0; +}