%{ /* * libid3tag - ID3 tag manipulation library * Copyright (C) 2000-2004 Underbit Technologies, Inc. * * 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 * * $Id: compat.gperf,v 1.11 2004/01/23 09:41:32 rob Exp $ */ # ifdef HAVE_CONFIG_H # include "config.h" # endif # include "global.h" # include # include # include # ifdef HAVE_ASSERT_H # include # endif # include "id3tag.h" # include "compat.h" # include "frame.h" # include "field.h" # include "parse.h" # include "ucs4.h" # define EQ(id) #id, 0 # define OBSOLETE 0, 0 # define TX(id) #id, translate_##id static id3_compat_func_t translate_TCON, translate_APIC; %} struct id3_compat; %% # # ID3v2.2 and ID3v2.3 frames # # Only obsolete frames or frames with an equivalent ID3v2.4 frame ID are # listed here. If a frame ID is not listed, it is assumed that the same # frame ID is itself the equivalent ID3v2.4 frame ID. # # This list may also include frames with new content interpretations; the # translation function will rewrite the contents to comply with ID3v2.4. # BUF, EQ(RBUF) /* Recommended buffer size */ CNT, EQ(PCNT) /* Play counter */ COM, EQ(COMM) /* Comments */ CRA, EQ(AENC) /* Audio encryption */ CRM, OBSOLETE /* Encrypted meta frame [obsolete] */ EQU, OBSOLETE /* Equalization [obsolete] */ EQUA, OBSOLETE /* Equalization [obsolete] */ ETC, EQ(ETCO) /* Event timing codes */ GEO, EQ(GEOB) /* General encapsulated object */ IPL, EQ(TIPL) /* Involved people list */ IPLS, EQ(TIPL) /* Involved people list */ LNK, EQ(LINK) /* Linked information */ MCI, EQ(MCDI) /* Music CD identifier */ MLL, EQ(MLLT) /* MPEG location lookup table */ PIC, TX(APIC) /* Attached picture */ POP, EQ(POPM) /* Popularimeter */ REV, EQ(RVRB) /* Reverb */ RVA, OBSOLETE /* Relative volume adjustment [obsolete] */ RVAD, OBSOLETE /* Relative volume adjustment [obsolete] */ SLT, EQ(SYLT) /* Synchronised lyric/text */ STC, EQ(SYTC) /* Synchronised tempo codes */ TAL, EQ(TALB) /* Album/movie/show title */ TBP, EQ(TBPM) /* BPM (beats per minute) */ TCM, EQ(TCOM) /* Composer */ TCO, TX(TCON) /* Content type */ TCON, TX(TCON) /* Content type */ TCR, EQ(TCOP) /* Copyright message */ TDA, OBSOLETE /* Date [obsolete] */ TDAT, OBSOLETE /* Date [obsolete] */ TDY, EQ(TDLY) /* Playlist delay */ TEN, EQ(TENC) /* Encoded by */ TFT, EQ(TFLT) /* File type */ TIM, OBSOLETE /* Time [obsolete] */ TIME, OBSOLETE /* Time [obsolete] */ TKE, EQ(TKEY) /* Initial key */ TLA, EQ(TLAN) /* Language(s) */ TLE, EQ(TLEN) /* Length */ TMT, EQ(TMED) /* Media type */ TOA, EQ(TOPE) /* Original artist(s)/performer(s) */ TOF, EQ(TOFN) /* Original filename */ TOL, EQ(TOLY) /* Original lyricist(s)/text writer(s) */ TOR, EQ(TDOR) /* Original release year [obsolete] */ TORY, EQ(TDOR) /* Original release year [obsolete] */ TOT, EQ(TOAL) /* Original album/movie/show title */ TP1, EQ(TPE1) /* Lead performer(s)/soloist(s) */ TP2, EQ(TPE2) /* Band/orchestra/accompaniment */ TP3, EQ(TPE3) /* Conductor/performer refinement */ TP4, EQ(TPE4) /* Interpreted, remixed, or otherwise modified by */ TPA, EQ(TPOS) /* Part of a set */ TPB, EQ(TPUB) /* Publisher */ TRC, EQ(TSRC) /* ISRC (international standard recording code) */ TRD, OBSOLETE /* Recording dates [obsolete] */ TRDA, OBSOLETE /* Recording dates [obsolete] */ TRK, EQ(TRCK) /* Track number/position in set */ TSI, OBSOLETE /* Size [obsolete] */ TSIZ, OBSOLETE /* Size [obsolete] */ TSS, EQ(TSSE) /* Software/hardware and settings used for encoding */ TT1, EQ(TIT1) /* Content group description */ TT2, EQ(TIT2) /* Title/songname/content description */ TT3, EQ(TIT3) /* Subtitle/description refinement */ TXT, EQ(TEXT) /* Lyricist/text writer */ TXX, EQ(TXXX) /* User defined text information frame */ TYE, OBSOLETE /* Year [obsolete] */ TYER, OBSOLETE /* Year [obsolete] */ UFI, EQ(UFID) /* Unique file identifier */ ULT, EQ(USLT) /* Unsynchronised lyric/text transcription */ WAF, EQ(WOAF) /* Official audio file webpage */ WAR, EQ(WOAR) /* Official artist/performer webpage */ WAS, EQ(WOAS) /* Official audio source webpage */ WCM, EQ(WCOM) /* Commercial information */ WCP, EQ(WCOP) /* Copyright/legal information */ WPB, EQ(WPUB) /* Publishers official webpage */ WXX, EQ(WXXX) /* User defined URL link frame */ %% static int translate_TCON(struct id3_frame *frame, char const *oldid, id3_byte_t const *data, id3_length_t length) { id3_byte_t const *end; enum id3_field_textencoding encoding; id3_ucs4_t *string = 0, *ptr, *endptr; int result = 0; /* translate old TCON syntax into multiple strings */ assert(frame->nfields == 2); encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1; end = data + length; if (id3_field_parse(&frame->fields[0], &data, end - data, &encoding) == -1) goto fail; string = id3_parse_string(&data, end - data, encoding, 0); if (string == 0) goto fail; ptr = string; while (*ptr == '(') { if (*++ptr == '(') break; endptr = ptr; while (*endptr && *endptr != ')') ++endptr; if (*endptr) *endptr++ = 0; if (id3_field_addstring(&frame->fields[1], ptr) == -1) goto fail; ptr = endptr; } if (*ptr && id3_field_addstring(&frame->fields[1], ptr) == -1) goto fail; if (0) { fail: result = -1; } if (string) free(string); return result; } static const char * translate_subtype(const id3_byte_t *in) { static char lcin[4]; static struct inout { char *in; char *out; } subtype_trans[] = { { "jpg", "jpeg" }, { NULL } }; struct inout *p; lcin[0] = tolower(in[0]); lcin[1] = tolower(in[1]); lcin[2] = tolower(in[2]); lcin[3] = 0; for (p = subtype_trans; p->in; p++) if (memcmp(p->in, in, 3) == 0) return p->out; return lcin; } static id3_latin1_t * str_to_mime_type(const id3_byte_t *str) { const char *subtype = translate_subtype(str); static const char prefix[] = "image/"; char *ret; ret = malloc(sizeof(prefix) + strlen(subtype)); if (ret) strcat(strcpy(ret, prefix), subtype); return (id3_latin1_t *) ret; } static int translate_APIC(struct id3_frame *frame, char const *oldid, id3_byte_t const *data, id3_length_t length) { id3_byte_t const *end; enum id3_field_textencoding encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1; int rc = 0; /* translate old PIC into APIC */ assert(frame->nfields == 5); end = data + length; do { /* Encoding */ if ((rc = id3_field_parse(&frame->fields[0], &data, end - data, &encoding)) == -1) break; /* MIME type: a 3-byte string in v2.2, as pointed out in https://bugs.kde.org/show_bug.cgi?id=167786#c2 */ if ((end - data) < 3) { rc = -1; break; } id3_field_finish(&frame->fields[1]); frame->fields[1].latin1.ptr = str_to_mime_type(data); if (!frame->fields[1].latin1.ptr) { rc = -1; break; } data += 3; /* Picture type */ if ((rc = id3_field_parse(&frame->fields[2], &data, end - data, &encoding)) == -1) break; /* Description */ if ((rc = id3_field_parse(&frame->fields[3], &data, end - data, &encoding)) == -1) break; /* Picture data */ if ((rc = id3_field_parse(&frame->fields[4], &data, end - data, &encoding)) == -1) break; } while (0); if (rc) { /* FIXME: cleanup */ ; } return rc; } /* * NAME: compat->fixup() * DESCRIPTION: finish compatibility translations */ int id3_compat_fixup(struct id3_tag *tag) { struct id3_frame *frame; unsigned int index; id3_ucs4_t timestamp[17] = { 0 }; int result = 0; /* create a TDRC frame from obsolete TYER/TDAT/TIME frames */ /* * TYE/TYER: YYYY * TDA/TDAT: DDMM * TIM/TIME: HHMM * * TDRC: yyyy-MM-ddTHH:mm */ index = 0; while ((frame = id3_tag_findframe(tag, ID3_FRAME_OBSOLETE, index++))) { char const *id; id3_byte_t const *data, *end; id3_length_t length; enum id3_field_textencoding encoding; id3_ucs4_t *string; id = id3_field_getframeid(&frame->fields[0]); assert(id); if (strcmp(id, "TYER") != 0 && strcmp(id, "YTYE") != 0 && strcmp(id, "TDAT") != 0 && strcmp(id, "YTDA") != 0 && strcmp(id, "TIME") != 0 && strcmp(id, "YTIM") != 0) continue; data = id3_field_getbinarydata(&frame->fields[1], &length); assert(data); if (length < 1) continue; end = data + length; encoding = id3_parse_uint(&data, 1); string = id3_parse_string(&data, end - data, encoding, 0); if (id3_ucs4_length(string) < 4) { free(string); continue; } if (strcmp(id, "TYER") == 0 || strcmp(id, "YTYE") == 0) { timestamp[0] = string[0]; timestamp[1] = string[1]; timestamp[2] = string[2]; timestamp[3] = string[3]; } else if (strcmp(id, "TDAT") == 0 || strcmp(id, "YTDA") == 0) { timestamp[4] = '-'; timestamp[5] = string[2]; timestamp[6] = string[3]; timestamp[7] = '-'; timestamp[8] = string[0]; timestamp[9] = string[1]; } else { /* TIME or YTIM */ timestamp[10] = 'T'; timestamp[11] = string[0]; timestamp[12] = string[1]; timestamp[13] = ':'; timestamp[14] = string[2]; timestamp[15] = string[3]; } free(string); } if (timestamp[0]) { id3_ucs4_t *strings; frame = id3_frame_new("TDRC"); if (frame == 0) goto fail; strings = timestamp; if (id3_field_settextencoding(&frame->fields[0], ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1 || id3_field_setstrings(&frame->fields[1], 1, &strings) == -1 || id3_tag_attachframe(tag, frame) == -1) { id3_frame_delete(frame); goto fail; } } if (0) { fail: result = -1; } return result; }