1 /* Copyright (C) 2001--2005 Chris Vaill
2 This file is part of nid3lib.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*/
17
18 #define _POSIX_C_SOURCE 2
19
20 #include "config.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include "nid3P.h"
30
31 /*
32 * Version conversion data
33 */
34
35 /*
36 * struct frame_convert holds all information for converting a frame
37 * between different versions of tags.
38 *
39 * The from field holds the frame ID as used in the earlier version;
40 * the to field has the equivalent frame ID for the newer version.
41 *
42 * If the converter field is non-NULL, it contains a pointer to a
43 * function which will convert the frame data.
44 *
45 * The converter's arguments are the frame, the two frame ID's from
46 * and to, and an int which, if non-zero, indicates that the
47 * conversion should be done backwards. If the converter field for a
48 * frame convert is NULL, the data left unchanged (only the frame ID
49 * is changed).
50 *
51 * A converter function should return 0 normally, 1 if the frame
52 * should be deleted instead of converted, or -1 on error.
53 */
54
55 struct frame_convert {
56 const char *from;
57 const char *to;
58 int (*converter)(id3_frame_t, const char *, const char *, int);
59 };
60
61 static int convert_link(id3_frame_t, const char *, const char *, int);
62 static int convert_apic(id3_frame_t, const char *, const char *, int);
63 static int convert_time(id3_frame_t, const char *, const char *, int);
64 static int convert_tcon(id3_frame_t, const char *, const char *, int);
65 static int convert_rva(id3_frame_t, const char *, const char *, int);
66
67 static const struct frame_convert _convert_map_v2to3[] = {
68 { "BUF", "RBUF", NULL },
69 { "CNT", "PCNT", NULL },
70 { "COM", "COMM", NULL },
71 { "CRA", "AENC", NULL },
72 { "CRM", NULL, NULL },
73 { "ETC", "ETCO", NULL },
74 { "EQU", "EQUA", NULL },
75 { "GEO", "GEOB", NULL },
76 { "IPL", "IPLS", NULL },
77 { "LNK", "LINK", convert_link },
78 { "MCI", "MCDI", NULL },
79 { "MLL", "MLLT", NULL },
80 { "PIC", "APIC", convert_apic },
81 { "POP", "POPM", NULL },
82 { "REV", "RVRB", NULL },
83 { "RVA", "RVAD", NULL },
84 { "SLT", "SYLT", NULL },
85 { "STC", "SYTC", NULL },
86 { "TAL", "TALB", NULL },
87 { "TBP", "TBPM", NULL },
88 { "TCM", "TCOM", NULL },
89 { "TCO", "TCON", NULL },
90 { "TCR", "TCOP", NULL },
91 { "TDA", "TDAT", NULL },
92 { "TDY", "TDLY", NULL },
93 { "TEN", "TENC", NULL },
94 { "TFT", "TFLT", NULL },
95 { "TIM", "TIME", NULL },
96 { "TKE", "TKEY", NULL },
97 { "TLA", "TLAN", NULL },
98 { "TLE", "TLEN", NULL },
99 { "TMT", "TMED", NULL },
100 { "TOA", "TOPE", NULL },
101 { "TOF", "TOFN", NULL },
102 { "TOL", "TOLY", NULL },
103 { "TOR", "TORY", NULL },
104 { "TOT", "TOAL", NULL },
105 { "TP1", "TPE1", NULL },
106 { "TP2", "TPE2", NULL },
107 { "TP3", "TPE3", NULL },
108 { "TP4", "TPE4", NULL },
109 { "TPA", "TPOS", NULL },
110 { "TPB", "TPUB", NULL },
111 { "TRC", "TSRC", NULL },
112 { "TRD", "TRDA", NULL },
113 { "TRK", "TRCK", NULL },
114 { "TSI", "TSIZ", NULL },
115 { "TSS", "TSSE", NULL },
116 { "TT1", "TIT1", NULL },
117 { "TT2", "TIT2", NULL },
118 { "TT3", "TIT3", NULL },
119 { "TXT", "TEXT", NULL },
120 { "TXX", "TXXX", NULL },
121 { "TYE", "TYER", NULL },
122 { "UFI", "UFID", NULL },
123 { "ULT", "USLT", NULL },
124 { "WAF", "WOAF", NULL },
125 { "WAR", "WOAR", NULL },
126 { "WAS", "WOAS", NULL },
127 { "WCM", "WCOM", NULL },
128 { "WCP", "WCOP", NULL },
129 { "WPB", "WPUB", NULL },
130 { "WXX", "WXXX", NULL },
131
132 { "XRV", "XRVA", convert_rva },
133
134 { NULL, NULL, NULL }
135 };
136
137 static const struct frame_convert _convert_map_v3to4[] = {
138
139 /* removed and changed frames in 2.4 */
140 { "EQUA", NULL, NULL },
141 { "IPLS", "TIPL", NULL },
142 { "RVAD", NULL, NULL },
143 { "TDAT", "TDRC", convert_time },
144 { "TIME", "TDRC", convert_time },
145 { "TORY", "TDOR", NULL },
146 /* TRDA is free-form, so we can't convert it to TDRC automatically */
147 { "TRDA", NULL, NULL },
148 { "TSIZ", NULL, NULL },
149 { "TYER", "TDRC", convert_time },
150
151 { "XRVA", "RVA2", convert_rva },
152
153 /* new frames in 2.4 */
154 { NULL, "ASPI", NULL },
155 { NULL, "EQU2", NULL },
156 { NULL, "RVA2", NULL },
157 { NULL, "SEEK", NULL },
158 { NULL, "SIGN", NULL },
159 { NULL, "TDEN", NULL },
160 { NULL, "TDOR", NULL },
161 { NULL, "TDRC", NULL },
162 { NULL, "TDRL", NULL },
163 { NULL, "TDTG", NULL },
164 { NULL, "TIPL", NULL },
165 { NULL, "TMCL", NULL },
166 { NULL, "TMOO", NULL },
167 { NULL, "TPRO", NULL },
168 { NULL, "TSOA", NULL },
169 { NULL, "TSOP", NULL },
170 { NULL, "TSOT", NULL },
171 { NULL, "TSST", NULL },
172
173 /* frames whose format changed */
174 { "TCON", "TCON", convert_tcon },
175
176 { NULL, NULL, NULL }
177 };
178
179 /*** Start of converter routines ***/
180
181 static int
convert_link(id3_frame_t f,const char * from,const char * to,int backward)182 convert_link(id3_frame_t f, const char *from, const char *to, int backward)
183 {
184 /* FIXME: implement, maybe */
185 return 0;
186 }
187
188 struct imgtype_mimetype_struct {
189 const char *imgtype;
190 const char *mimetype;
191 };
192
193 static const struct imgtype_mimetype_struct imgtype_mimetype_map[] = {
194 { "jpg", "jpeg" },
195 { "tif", "tiff" },
196 { "xbm", "x-xbitmap" },
197 { "xpm", "x-xpixmap" },
198 { "xwd", "x-xwindowdump" },
199 { "ras", "x-cmu-raster" },
200 { "pnm", "x-portable-anymap" },
201 { "pbm", "x-portable-bitmap" },
202 { "pgm", "x-portable-graymap" },
203 { "rgb", "x-rgb" },
204
205 { NULL, NULL }
206 };
207
208 /* convert v2.2 PIC to v2.3 APIC */
209 static int
_convert_apic_forward(id3_frame_t f)210 _convert_apic_forward(id3_frame_t f)
211 {
212 char imgtype[4];
213 char mimetype[32]; /* large enough for "image/" + largest string in map */
214 const struct imgtype_mimetype_struct *im_map;
215 unsigned char *data;
216 int i, newsz;
217
218 data = id3_frame_get_raw(f);
219 if (data == NULL)
220 return 0;
221 memcpy(imgtype, data + 1, 3);
222 imgtype[3] = '\0';
223 for (i = 0; i < 3; i++)
224 imgtype[i] = tolower(imgtype[i]);
225
226 /* use the map to get the corresponding mime type */
227 strcpy(mimetype, "image/");
228 im_map = imgtype_mimetype_map;
229 while (im_map->imgtype) {
230 if (strcmp(imgtype, im_map->imgtype) == 0) {
231 strcat(mimetype, im_map->mimetype);
232 break;
233 }
234 im_map++;
235 }
236
237 /* just use image/<imgtype> if we didn't find it in the map */
238 if (im_map->imgtype == NULL)
239 strcat(mimetype, imgtype);
240
241 i = strlen(mimetype);
242 newsz = f->sz - 3 + i + 1;
243 f->data = (unsigned char *)malloc(newsz);
244 if (f->data == NULL) {
245 f->data = data;
246 return -1;
247 }
248 f->data[0] = data[0];
249 strcpy((char *)f->data + 1, mimetype);
250 memcpy(f->data + 1 + i + 1, data + 4, f->sz - 4);
251 f->sz = newsz;
252 free(data);
253
254 return 0;
255 }
256
257 /* convert v2.3 APIC to v2.2 PIC */
258 static int
_convert_apic_backward(id3_frame_t f)259 _convert_apic_backward(id3_frame_t f)
260 {
261 char imgtype[4], *mimetype = NULL;
262 const struct imgtype_mimetype_struct *im_map;
263 unsigned char *data;
264 int i, newsz, len, bad_mimetype = 0;
265
266 data = id3_frame_get_raw(f);
267 if (data == NULL)
268 return 0;
269
270 /* make sure the mime type is terminated */
271 for (i = 1; i < f->sz; i++)
272 if (data[i] == '\0')
273 break;
274
275 if (data[i] != '\0' || i < 8)
276 bad_mimetype = 1;
277
278 if (!bad_mimetype) {
279 mimetype = (char *)data + 1;
280 len = strlen(mimetype);
281 for (i = 0; i < len; i++)
282 mimetype[i] = tolower(mimetype[i]);
283 if (strncmp(mimetype, "image/", 6) != 0)
284 bad_mimetype = 1;
285 }
286
287 if (!bad_mimetype) {
288 mimetype += 6; /* skip "image/" */
289 /* use the map to get the corresponding image type */
290 im_map = imgtype_mimetype_map;
291 while (im_map->mimetype) {
292 if (strcmp(mimetype, im_map->mimetype) == 0) {
293 strcpy(imgtype, im_map->imgtype);
294 break;
295 }
296 im_map++;
297 }
298
299 /* just use the first three chars after "image/"
300 if we didn't find it in the map */
301 if (im_map->mimetype == NULL)
302 strncpy(imgtype, mimetype, 3);
303 imgtype[3] = '\0';
304
305 } else {
306 /* can't make head or tail of the mime type */
307 return 0;
308 }
309
310 newsz = f->sz - len - 1 + 3;
311 memcpy(f->data + 1, imgtype, 3);
312 memmove(f->data + 4, f->data + 4 + len + 1, f->sz - 4 - len - 1);
313 f->sz = newsz;
314
315 return 0;
316 }
317
318 static int
convert_apic(id3_frame_t f,const char * from,const char * to,int backward)319 convert_apic(id3_frame_t f, const char *from, const char *to, int backward)
320 {
321 if (backward)
322 return _convert_apic_backward(f);
323 else
324 return _convert_apic_forward(f);
325 }
326
327 /* used by the following convert_time() converter */
328 static int
_convert_time_backward(id3_frame_t f)329 _convert_time_backward(id3_frame_t f)
330 {
331 id3_t tag = f->id3;
332 id3_frame_t f2;
333 unsigned char *old_data;
334 unsigned char buf[32];
335 int len;
336
337 /* split TDRC frame into TDAT, TIME, and TYER frames */
338 old_data = id3_frame_get_raw(f);
339 if (old_data == NULL)
340 return 0;
341 len = strlen((char *)old_data + 1);
342
343 if (len >= 4) {
344 /* form TYER frame */
345 f2 = id3_frame_add(tag, "TYER");
346 if (f2 == NULL)
347 return -1;
348 if (id3_frame_set_raw(f2, old_data, 5) == -1)
349 return -1;
350 }
351
352 if (len >= 10) {
353 /* form TDAT frame */
354 f2 = id3_frame_add(tag, "TDAT");
355 if (f2 == NULL)
356 return -1;
357 buf[0] = '\0';
358 buf[1] = old_data[9];
359 buf[2] = old_data[10];
360 buf[3] = old_data[6];
361 buf[4] = old_data[7];
362 if (id3_frame_set_raw(f2, buf, 5) == -1)
363 return -1;
364 }
365
366 if (len >= 16) {
367 /* form TIME frame */
368 f2 = id3_frame_add(tag, "TIME");
369 if (f2 == NULL)
370 return -1;
371 buf[0] = '\0';
372 buf[1] = old_data[12];
373 buf[2] = old_data[13];
374 buf[3] = old_data[15];
375 buf[4] = old_data[16];
376 if (id3_frame_set_raw(f2, buf, 5) == -1)
377 return -1;
378 }
379
380 return 1;
381 }
382
383 /* used by the following convert_time() converter */
384 static int
_convert_time_forward(id3_frame_t f)385 _convert_time_forward(id3_frame_t f)
386 {
387 id3_t tag = f->id3;
388 id3_frame_t tdrc_f;
389 unsigned char *old_data;
390
391 tdrc_f = id3_get_frame_by_id(tag, "TDRC");
392 if (tdrc_f == NULL) {
393 /* convert this frame to TDRC */
394 old_data = id3_frame_get_raw(f);
395 if (strcmp(f->id, "TDAT") == 0) {
396
397 /* make sure the TIME frame is in the form DDMM */
398 if (strlen((char *)old_data + 1) != 4) {
399 /* non-standard TDAT frame, so drop it */
400 return 1;
401 }
402 f->sz = 11;
403 f->data = (unsigned char *)calloc(f->sz + 2, 1);
404 /* leave space for the year if we find a TYER later */
405 /* (string is split to avoid trigraphs) */
406 sprintf((char *)f->data + 1, "????" "-%c%c-%c%c",
407 old_data[3], old_data[4], old_data[1], old_data[2]);
408 free(old_data);
409
410 } else if (strcmp(f->id, "TIME") == 0) {
411
412 /* make sure the TIME frame is in the form HHmm */
413 if (strlen((char *)old_data + 1) != 4) {
414 /* non-standard TIME frame, so drop it */
415 return 1;
416 }
417 f->sz = 17;
418 f->data = (unsigned char *)calloc(f->sz + 2, 1);
419 /* leave space for the year and date for later TYER and TDAT */
420 /* (string is split to avoid trigraphs) */
421 sprintf((char *)f->data + 1, "????" "-??" "-??T%c%c:%c%c",
422 old_data[1], old_data[2], old_data[3], old_data[4]);
423 free(old_data);
424
425 } else if (strcmp(f->id, "TYER") == 0) {
426 /* leave as it is */
427 } else {
428 /* unrecognized from field */
429 return -1;
430 }
431
432 } else {
433
434 /* There's already a TDRC frame, so we just add to it */
435 id3_frame_get_raw(f); /* make sure the data is read into f->data */
436 if (strcmp(f->id, "TDAT") == 0) {
437
438 /* make sure the TIME frame is in the form DDMM */
439 if (strlen((char *)f->data + 1) != 4) {
440 /* non-standard TDAT frame, so drop it */
441 return 1;
442 }
443 /* make sure the TDRC frame can hold 11 bytes */
444 if (tdrc_f->sz < 11) {
445 /* We need to make it bigger */
446 old_data = id3_frame_get_raw(tdrc_f);
447 tdrc_f->sz = 11;
448 tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
449 if (tdrc_f->data == NULL) {
450 tdrc_f->data = old_data;
451 return -1;
452 }
453 tdrc_f->data[11] = tdrc_f->data[12] = '\0'; /* ensure termination */
454 }
455 /* data will now be YYYY-MM-DD, so splice in the -MM-DD */
456 sprintf((char *)tdrc_f->data + 5, "-%c%c-%c%c",
457 f->data[3], f->data[4], f->data[1], f->data[2]);
458
459 } else if (strcmp(f->id, "TIME") == 0) {
460
461 /* make sure the TIME frame is in the form HHmm */
462 if (strlen((char *)f->data + 1) != 4) {
463 /* non-standard TIME frame, so drop it */
464 return 1;
465 }
466 /* make sure the TDRC frame can hold 17 bytes */
467 if (tdrc_f->sz < 17) {
468 /* We need to make it bigger */
469 old_data = id3_frame_get_raw(tdrc_f);
470 tdrc_f->sz = 17;
471 tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
472 if (tdrc_f->data == NULL) {
473 tdrc_f->data = old_data;
474 return -1;
475 }
476 tdrc_f->data[17] = tdrc_f->data[18] = '\0'; /* ensure termination */
477 }
478 /* data will now be YYYY-MM-DDTHH:mm, so splice in the THH:mm */
479 sprintf((char *)tdrc_f->data + 11, "T%c%c:%c%c",
480 f->data[1], f->data[2], f->data[3], f->data[4]);
481
482 } else if (strcmp(f->id, "TYER") == 0) {
483
484 /* make sure the TYER frame is in the form YYYY */
485 if (strlen((char *)f->data + 1) != 4) {
486 /* non-standard TYER frame, so drop it */
487 return 1;
488 }
489 /* make sure the TDRC frame can hold 5 bytes */
490 if (tdrc_f->sz < 5) {
491 /* We need to make it bigger */
492 old_data = id3_frame_get_raw(tdrc_f);
493 tdrc_f->sz = 5;
494 tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
495 if (tdrc_f->data == NULL) {
496 tdrc_f->data = old_data;
497 return -1;
498 }
499 tdrc_f->data[5] = tdrc_f->data[6] = '\0'; /* ensure termination */
500 }
501 /* splice the YYYY into the beginning of the data */
502 memcpy(tdrc_f->data + 1, f->data + 1, 4);
503
504 } else {
505 /* unrecognized from field */
506 return -1;
507 }
508
509 return 1;
510 }
511
512 return 0;
513 }
514
515 /* This converter changes the v2.3 frames TDAT, TIME, and TYER
516 * to the v2.4 frame TDRC. */
517 static int
convert_time(id3_frame_t f,const char * from,const char * to,int backward)518 convert_time(id3_frame_t f, const char *from, const char *to, int backward)
519 {
520 if (backward)
521 return _convert_time_backward(f);
522 else
523 return _convert_time_forward(f);
524 }
525
526 /* This converter changes between v2.3 and v2.4 TCON frame formats. */
527 static int
convert_tcon(id3_frame_t f,const char * from,const char * to,int backward)528 convert_tcon(id3_frame_t f, const char *from, const char *to, int backward)
529 {
530 char *src, *dest, *rparen;
531 unsigned char *data;
532 char *endptr;
533 int i, newsz, len, last_was_nonnumeric;
534 long gnum;
535
536 data = id3_frame_get_raw(f);
537
538 if (f->sz < 1)
539 return 0;
540
541 /* FIXME: need to test backwards -- fix mp3tag.c */
542 if (backward) {
543
544 /* "129" 0x00 "9" 0x00 ==> "(129)(9)" */
545 /* "129" 0x00 "(I think)" 0x00 ==> "(129)((I think)" */
546
547 /* frame can grow, so allocate new data pointer */
548 newsz = f->sz + 2;
549 for (i = 0; i < f->sz; i++) {
550 if (data[i] == '(' || data[i] == '\0')
551 newsz++;
552 }
553 f->data = (unsigned char *)calloc(newsz, 1);
554 if (f->data == NULL) {
555 f->data = data;
556 return -1;
557 }
558
559 src = (char *)data;
560 dest = (char *)f->data;
561 *dest++ = *src++; /* text encoding byte */
562 last_was_nonnumeric = 0;
563 while (src - (char *)data < f->sz) {
564
565 /* Check for a numeric string between 0 and 255, by itself */
566 if (src[0] >= '0' && src[0] <= '9') {
567 gnum = strtol(src, &endptr, 10);
568 if (*endptr == '\0' && gnum >= 0 && gnum <= 255) {
569 dest += sprintf(dest, "(%d)", (int)gnum);
570 src = endptr + 1;
571 last_was_nonnumeric = 0;
572 continue;
573 }
574 }
575
576 /* It's a non-numeric string, so escape the lparens and copy */
577 if (last_was_nonnumeric) {
578 /* separate consecutive non-numeric strings by '/' */
579 *dest++ = '/';
580 }
581 while (*src != '\0') {
582 if (*src == '(')
583 *dest++ = '(';
584 *dest++ = *src++;
585 }
586 src++; /* skip the '\0' */
587 last_was_nonnumeric = 1;
588 }
589 newsz = dest - (char *)f->data;
590 free(data);
591
592 } else {
593
594 /* "(129)(9)" ==> "129" 0x00 "9" 0x00 */
595 /* "(129)((I think)" ==> "129" 0x00 "(I think)" 0x00 */
596
597 src = dest = (char *)data + 1;
598 while (src - (char *)data < f->sz) {
599
600 if (src[0] == '(') {
601
602 rparen = strchr(src, ')');
603 if (src[1] == '(') {
604 /* if "((" is encountered, drop one '(' */
605 src++;
606 if (rparen)
607 len = rparen - src + 1;
608 else
609 len = f->sz - (src - (char *)data);
610 memmove(dest, src, len);
611 src += len;
612 dest += len;
613 } else {
614 /* found start of a field */
615 if (rparen == NULL)
616 break;
617 *rparen = '\0';
618 if (dest[-1] == '\0')
619 src++;
620 else
621 *src = '\0';
622 len = rparen - src;
623 memmove(dest, src, len);
624 src += len;
625 dest += len;
626 }
627
628 } else {
629 *dest++ = *src++;
630 }
631 }
632
633 newsz = dest - (char *)data;
634 }
635
636 f->sz = newsz;
637 f->data[f->sz] = '\0';
638
639 return 0;
640 }
641
642 /*
643 * We use experimental tags XRV and XRVA in v2.2 and v2.3 tags, since
644 * they don't have useful native relative volume adjust frames like
645 * RVA2. Their format is the same as RVA2, so convert_rva() doesn't
646 * actually do any conversion; it's just to ensure that the format is
647 * correct, since someone else could also be putting XRVA frames with
648 * entirely different data in tags. If we don't recogize the format,
649 * we just drop the tag.
650 */
651 static int
convert_rva(id3_frame_t f,const char * from,const char * to,int backward)652 convert_rva(id3_frame_t f, const char *from, const char *to, int backward)
653 {
654 int i, peakbytes;
655 unsigned char *data;
656
657 data = id3_frame_get_raw(f);
658
659 /* check identification string */
660 for (i = 0; i < f->sz; i++)
661 if (data[i] == '\0')
662 break;
663 if (data[i] != '\0')
664 return 1;
665
666 i++;
667 while (1) {
668 /* check channel type */
669 if (i >= f->sz || data[i] > 0x08)
670 return 1;
671
672 /* check peak data */
673 i += 3;
674 if (i >= f->sz)
675 return 1;
676 peakbytes = data[i] / 8;
677 i += peakbytes;
678 if (i >= f->sz)
679 return 1;
680
681 /* end of field; it's okay to end here */
682 i++;
683 if (i >= f->sz)
684 break;
685 }
686
687 return 0;
688 }
689
690 /*** End of converter routines ***/
691
692 static const struct frame_convert *
find_converter(const struct frame_convert * map,const char * from,int backw)693 find_converter(const struct frame_convert *map, const char *from, int backw)
694 {
695 const char *s;
696
697 while (map->from || map->to) {
698 s = backw ? map->to : map->from;
699 if (s && memcmp(from, s, 4) == 0)
700 return map;
701 map++;
702 }
703 return NULL;
704 }
705
706 static int
convert_frame(id3_frame_t f,int from_vers,int to_vers)707 convert_frame(id3_frame_t f, int from_vers, int to_vers)
708 {
709 const struct frame_convert *fc_map = NULL;
710 int ret, backward = 0;
711 const char *newid;
712
713 if (from_vers == to_vers)
714 return 0;
715
716 if (from_vers - to_vers > 1) {
717 ret = convert_frame(f, from_vers, to_vers + 1);
718 if (ret == -1)
719 return -1;
720 from_vers = to_vers + 1;
721 } else if (to_vers - from_vers > 1) {
722 ret = convert_frame(f, from_vers, to_vers - 1);
723 if (ret == -1)
724 return -1;
725 from_vers = to_vers - 1;
726 }
727
728 /* to and from are always one version apart at this point */
729 switch (from_vers) {
730 case 2:
731 /* we can only be converting to v3 */
732 fc_map = _convert_map_v2to3;
733 backward = 0;
734 break;
735 case 3:
736 switch (to_vers) {
737 case 2:
738 fc_map = _convert_map_v2to3;
739 backward = 1;
740 break;
741 case 4:
742 fc_map = _convert_map_v3to4;
743 backward = 0;
744 break;
745 }
746 break;
747 case 4:
748 /* we can only be converting to v3 */
749 fc_map = _convert_map_v3to4;
750 backward = 1;
751 break;
752 }
753
754 fc_map = find_converter(fc_map, f->id, backward);
755 if (fc_map) {
756 if (fc_map->converter) {
757 ret = fc_map->converter(f, fc_map->from, fc_map->to, backward);
758 if (ret == -1)
759 return -1;
760 if (ret == 1) {
761 id3_frame_delete(f);
762 return 0;
763 }
764 }
765 newid = backward ? fc_map->from : fc_map->to;
766 if (newid) {
767 strcpy(f->id, newid);
768 } else {
769 /* newid is NULL, so the frame should just be dropped */
770 id3_frame_delete(f);
771 }
772 }
773
774 return 0;
775 }
776
777 int
id3_set_version(id3_t tag,enum id3_version ver)778 id3_set_version(id3_t tag, enum id3_version ver)
779 {
780 int oldversion;
781 id3_frame_t fr, nextfr;
782
783 /* make sure headers have been read */
784 if (id3_frame_count(tag) == -1)
785 return -1;
786
787 /* set version field */
788 oldversion = tag->version;
789 switch (ver) {
790 case ID3_VERSION_2_3: tag->version = 3; break;
791 case ID3_VERSION_2_4: tag->version = 4; break;
792 case ID3_VERSION_2_2:
793 /* we choose not to write ID3v2.2 tags, so fall through */
794 default:
795 errno = EINVAL;
796 return -1;
797 }
798
799 if (tag->version == oldversion)
800 return 0; /* nothing to do */
801 if (oldversion < 2 || oldversion > 4) {
802 /* we only know how to convert from v2.2, v2.3, and v2.4 */
803 errno = EINVAL;
804 return -1;
805 }
806
807 /* convert frames */
808 fr = tag->frame_hd;
809 while (fr) {
810 /* fr may get removed in the conversion process, so grab the next
811 frame before converting */
812 nextfr = fr->next;
813 if (convert_frame(fr, oldversion, tag->version) == -1)
814 return -1;
815 fr = nextfr;
816 }
817
818 return 0;
819 }
820
821 enum id3_version
id3_get_version(id3_t tag)822 id3_get_version(id3_t tag)
823 {
824 if (id3_get_size(tag) == -1)
825 return ID3_VERSION_NONE;
826 switch (tag->version) {
827 case 2: return ID3_VERSION_2_2;
828 case 3: return ID3_VERSION_2_3;
829 case 4: return ID3_VERSION_2_4;
830 }
831 return ID3_VERSION_NONE;
832 }
833