Handle streams separately in tree_add_track()
[cmus.git] / id3.c
blobbd1bdbad1de2e6cd027ed8f85d3dfc3beba7580f
1 /*
2 * Copyright 2005 Timo Hirvonen
3 */
5 #include "id3.h"
6 #include "comment.h"
7 #include "xmalloc.h"
8 #include "utf8_encode.h"
9 #include "uchar.h"
10 #include "options.h"
11 #include "debug.h"
13 #include <unistd.h>
14 #include <inttypes.h>
15 #include <errno.h>
16 #include <stdio.h>
19 * position:
21 * 0 "ID3"
22 * -10 "3DI"
23 * -128 "TAG"
24 * -138 "3DI"
26 * if v2 is at beginning _and_ at end then there must be a seek tag at beginning
29 struct v2_header {
30 unsigned char ver_major;
31 unsigned char ver_minor;
32 unsigned char flags;
33 uint32_t size;
36 struct v2_extended_header {
37 uint32_t size;
40 struct v2_frame_header {
41 char id[4];
42 uint32_t size;
43 uint16_t flags;
46 #define V2_HEADER_UNSYNC (1 << 7)
47 #define V2_HEADER_EXTENDED (1 << 6)
48 #define V2_HEADER_EXPERIMENTAL (1 << 5)
49 #define V2_HEADER_FOOTER (1 << 4)
51 #define V2_FRAME_COMPRESSED (1 << 3) /* great idea!!1 */
52 #define V2_FRAME_ENCRYPTHED (1 << 2) /* wow, this is very neat! */
53 #define V2_FRAME_UNSYNC (1 << 1)
54 #define V2_FRAME_LEN_INDICATOR (1 << 0)
56 #define NR_GENRES 148
57 /* genres {{{ */
58 static const char *genres[NR_GENRES] = {
59 "Blues",
60 "Classic Rock",
61 "Country",
62 "Dance",
63 "Disco",
64 "Funk",
65 "Grunge",
66 "Hip-Hop",
67 "Jazz",
68 "Metal",
69 "New Age",
70 "Oldies",
71 "Other",
72 "Pop",
73 "R&B",
74 "Rap",
75 "Reggae",
76 "Rock",
77 "Techno",
78 "Industrial",
79 "Alternative",
80 "Ska",
81 "Death Metal",
82 "Pranks",
83 "Soundtrack",
84 "Euro-Techno",
85 "Ambient",
86 "Trip-Hop",
87 "Vocal",
88 "Jazz+Funk",
89 "Fusion",
90 "Trance",
91 "Classical",
92 "Instrumental",
93 "Acid",
94 "House",
95 "Game",
96 "Sound Clip",
97 "Gospel",
98 "Noise",
99 "Alt",
100 "Bass",
101 "Soul",
102 "Punk",
103 "Space",
104 "Meditative",
105 "Instrumental Pop",
106 "Instrumental Rock",
107 "Ethnic",
108 "Gothic",
109 "Darkwave",
110 "Techno-Industrial",
111 "Electronic",
112 "Pop-Folk",
113 "Eurodance",
114 "Dream",
115 "Southern Rock",
116 "Comedy",
117 "Cult",
118 "Gangsta Rap",
119 "Top 40",
120 "Christian Rap",
121 "Pop/Funk",
122 "Jungle",
123 "Native American",
124 "Cabaret",
125 "New Wave",
126 "Psychedelic",
127 "Rave",
128 "Showtunes",
129 "Trailer",
130 "Lo-Fi",
131 "Tribal",
132 "Acid Punk",
133 "Acid Jazz",
134 "Polka",
135 "Retro",
136 "Musical",
137 "Rock & Roll",
138 "Hard Rock",
139 "Folk",
140 "Folk/Rock",
141 "National Folk",
142 "Swing",
143 "Fast-Fusion",
144 "Bebob",
145 "Latin",
146 "Revival",
147 "Celtic",
148 "Bluegrass",
149 "Avantgarde",
150 "Gothic Rock",
151 "Progressive Rock",
152 "Psychedelic Rock",
153 "Symphonic Rock",
154 "Slow Rock",
155 "Big Band",
156 "Chorus",
157 "Easy Listening",
158 "Acoustic",
159 "Humour",
160 "Speech",
161 "Chanson",
162 "Opera",
163 "Chamber Music",
164 "Sonata",
165 "Symphony",
166 "Booty Bass",
167 "Primus",
168 "Porn Groove",
169 "Satire",
170 "Slow Jam",
171 "Club",
172 "Tango",
173 "Samba",
174 "Folklore",
175 "Ballad",
176 "Power Ballad",
177 "Rhythmic Soul",
178 "Freestyle",
179 "Duet",
180 "Punk Rock",
181 "Drum Solo",
182 "A Cappella",
183 "Euro-House",
184 "Dance Hall",
185 "Goa",
186 "Drum & Bass",
187 "Club-House",
188 "Hardcore",
189 "Terror",
190 "Indie",
191 "BritPop",
192 "Negerpunk",
193 "Polsk Punk",
194 "Beat",
195 "Christian Gangsta Rap",
196 "Heavy Metal",
197 "Black Metal",
198 "Crossover",
199 "Contemporary Christian",
200 "Christian Rock",
201 "Merengue",
202 "Salsa",
203 "Thrash Metal",
204 "Anime",
205 "JPop",
206 "Synthpop"
208 /* }}} */
210 #if 1
211 #define id3_debug(...) d_print(__VA_ARGS__)
212 #else
213 #define id3_debug(...) do { } while (0)
214 #endif
216 const char * const id3_key_names[NUM_ID3_KEYS] = {
217 "artist",
218 "album",
219 "title",
220 "date",
221 "genre",
222 "discnumber",
223 "tracknumber",
224 "albumartist",
225 "artistsort",
226 "albumartistsort",
227 "compilation",
228 "replaygain_track_gain",
229 "replaygain_track_peak",
230 "replaygain_album_gain",
231 "replaygain_album_peak",
232 "comment",
235 static int utf16_is_special(const uchar uch)
237 if (UTF16_IS_HSURROGATE(uch) || UTF16_IS_LSURROGATE(uch) || UTF16_IS_BOM(uch))
238 return -1;
239 return 0;
242 static char *utf16_to_utf8(const unsigned char *buf, int buf_size)
244 char *out;
245 int i, idx;
247 out = xnew(char, (buf_size / 2) * 4 + 1);
248 i = idx = 0;
249 while (buf_size - i >= 2) {
250 uchar u;
252 u = buf[i] + (buf[i + 1] << 8);
253 if (u_is_unicode(u)) {
254 if (utf16_is_special(u) == 0)
255 u_set_char(out, &idx, u);
256 } else {
257 free(out);
258 return NULL;
260 if (u == 0)
261 return out;
262 i += 2;
264 u_set_char(out, &idx, 0);
265 return out;
268 static char *utf16be_to_utf8(const unsigned char *buf, int buf_size)
270 char *out;
271 int i, idx;
273 out = xnew(char, (buf_size / 2) * 4 + 1);
274 i = 0;
275 idx = 0;
276 while (buf_size - i >= 2) {
277 uchar u;
279 u = buf[i + 1] + (buf[i] << 8);
280 if (u_is_unicode(u)) {
281 if (utf16_is_special(u) == 0)
282 u_set_char(out, &idx, u);
283 } else {
284 free(out);
285 return NULL;
287 if (u == 0)
288 return out;
289 i += 2;
291 u_set_char(out, &idx, 0);
292 return out;
295 static int is_v1(const char *buf)
297 return buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G';
300 static int u32_unsync(const unsigned char *buf, uint32_t *up)
302 uint32_t b, u = 0;
303 int i;
305 for (i = 0; i < 4; i++) {
306 b = buf[i];
307 if (b >= 0x80)
308 return 0;
309 u <<= 7;
310 u |= b;
312 *up = u;
313 return 1;
316 static void get_u32(const unsigned char *buf, uint32_t *up)
318 uint32_t b, u = 0;
319 int i;
321 for (i = 0; i < 4; i++) {
322 b = buf[i];
323 u <<= 8;
324 u |= b;
326 *up = u;
329 static void get_u24(const unsigned char *buf, uint32_t *up)
331 uint32_t b, u = 0;
332 int i;
334 for (i = 0; i < 3; i++) {
335 b = buf[i];
336 u <<= 8;
337 u |= b;
339 *up = u;
342 static int v2_header_footer_parse(struct v2_header *header, const char *buf)
344 const unsigned char *b = (const unsigned char *)buf;
346 header->ver_major = b[3];
347 header->ver_minor = b[4];
348 header->flags = b[5];
349 if (header->ver_major == 0xff || header->ver_minor == 0xff)
350 return 0;
351 return u32_unsync(b + 6, &header->size);
354 static int v2_header_parse(struct v2_header *header, const char *buf)
356 if (buf[0] != 'I' || buf[1] != 'D' || buf[2] != '3')
357 return 0;
358 return v2_header_footer_parse(header, buf);
361 static int v2_footer_parse(struct v2_header *header, const char *buf)
363 if (buf[0] != '3' || buf[1] != 'D' || buf[2] != 'I')
364 return 0;
365 return v2_header_footer_parse(header, buf);
368 static int v2_extended_header_parse(struct v2_extended_header *header, const char *buf)
370 return u32_unsync((const unsigned char *)buf, &header->size);
373 static int is_frame_id_char(char ch)
375 return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
378 /* XXXYYY
380 * X = [A-Z0-9]
381 * Y = byte
383 * XXX is frame
384 * YYY is frame size excluding this 6 byte header
386 static int v2_2_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
388 int i;
390 for (i = 0; i < 3; i++) {
391 if (!is_frame_id_char(buf[i]))
392 return 0;
393 header->id[i] = buf[i];
395 header->id[3] = 0;
396 get_u24((const unsigned char *)(buf + 3), &header->size);
397 header->flags = 0;
398 if (header->size == 0)
399 return 0;
400 id3_debug("%c%c%c %d\n", header->id[0], header->id[1], header->id[2], header->size);
401 return 1;
404 /* XXXXYYYYZZ
406 * X = [A-Z0-9]
407 * Y = byte
408 * Z = byte
410 * XXXX is frame
411 * YYYY is frame size excluding this 10 byte header
412 * ZZ is flags
414 static int v2_3_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
416 int i;
418 for (i = 0; i < 4; i++) {
419 if (!is_frame_id_char(buf[i]))
420 return 0;
421 header->id[i] = buf[i];
423 get_u32((const unsigned char *)(buf + 4), &header->size);
424 header->flags = (buf[8] << 8) | buf[9];
425 if (header->size == 0)
426 return 0;
427 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
428 header->id[3], header->size);
429 return 1;
432 /* same as 2.3 but header size is sync safe */
433 static int v2_4_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
435 int i;
437 for (i = 0; i < 4; i++) {
438 if (!is_frame_id_char(buf[i]))
439 return 0;
440 header->id[i] = buf[i];
442 if (!u32_unsync((const unsigned char *)(buf + 4), &header->size))
443 return 0;
444 header->flags = (buf[8] << 8) | buf[9];
445 if (header->size == 0)
446 return 0;
447 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
448 header->id[3], header->size);
449 return 1;
452 static int read_all(int fd, char *buf, size_t size)
454 size_t pos = 0;
456 while (pos < size) {
457 int rc = read(fd, buf + pos, size - pos);
459 if (rc == -1) {
460 if (errno == EINTR || errno == EAGAIN)
461 continue;
462 return -1;
464 pos += rc;
466 return 0;
469 static char *parse_genre(const char *str)
471 int parenthesis = 0;
472 long int idx;
473 char *end;
475 if (strncasecmp(str, "(RX", 3) == 0)
476 return xstrdup("Remix");
478 if (strncasecmp(str, "(CR", 3) == 0)
479 return xstrdup("Cover");
481 if (*str == '(') {
482 parenthesis = 1;
483 str++;
486 idx = strtol(str, &end, 10);
487 if (str != end) {
488 /* Number parsed but there may be some crap after the number.
489 * I don't care, ID3v2 by definition contains crap.
491 if (idx >= 0 && idx < NR_GENRES)
492 return xstrdup(genres[idx]);
495 if (parenthesis) {
496 const char *ptr = strchr(str, ')');
498 if (ptr && ptr[1]) {
499 /* genre name after random crap in parenthesis,
500 * return the genre name */
501 return xstrdup(ptr + 1);
503 str--;
506 /* random crap, just return it and wait for a bug report */
507 return xstrdup(str);
510 /* http://www.id3.org/id3v2.4.0-structure.txt */
511 static struct {
512 const char name[8];
513 enum id3_key key;
514 } frame_tab[] = {
515 /* 2.4.0 */
516 { "TDRC", ID3_DATE }, // recording date
517 { "TDRL", ID3_DATE }, // release date
518 { "TDOR", ID3_DATE }, // original release date
519 { "TSOP", ID3_ARTISTSORT },
521 /* >= 2.3.0 */
522 { "TPE1", ID3_ARTIST },
523 { "TALB", ID3_ALBUM },
524 { "TIT2", ID3_TITLE },
525 { "TYER", ID3_DATE },
526 { "TCON", ID3_GENRE },
527 { "TPOS", ID3_DISC },
528 { "TRCK", ID3_TRACK },
529 { "TPE2", ID3_ALBUMARTIST },
530 { "XSOP", ID3_ARTISTSORT }, // obsolete
531 { "TCMP", ID3_COMPILATION },
533 /* obsolete frames (2.2.0) */
534 { "TP1", ID3_ARTIST },
535 { "TAL", ID3_ALBUM },
536 { "TT2", ID3_TITLE },
537 { "TYE", ID3_DATE },
538 { "TCO", ID3_GENRE },
539 { "TPA", ID3_DISC },
540 { "TRK", ID3_TRACK },
542 { "", -1 }
545 static int frame_tab_index(const char *id)
547 int i;
549 for (i = 0; frame_tab[i].key != -1; i++) {
550 if (!strncmp(id, frame_tab[i].name, 4))
551 return i;
553 return -1;
556 static void fix_date(char *buf)
558 const char *ptr = buf;
559 int ch, len = 0;
561 do {
562 ch = *ptr++;
563 if (ch >= '0' && ch <= '9') {
564 len++;
565 continue;
567 if (len == 4) {
568 // number which length is 4, must be year
569 memmove(buf, ptr - 5, 4);
570 buf[4] = 0;
571 return;
573 len = 0;
574 } while (ch);
575 *buf = 0;
578 static char *decode_str(const char *buf, int len, int encoding)
580 char *in, *out = NULL;
581 int rc = 0;
583 switch (encoding) {
584 case 0x00: /* ISO-8859-1 */
585 in = xstrndup(buf, len);
586 rc = utf8_encode(in, id3_default_charset, &out);
587 free(in);
588 break;
589 case 0x03: /* UTF-8 */
590 in = xstrndup(buf, len);
591 if (u_is_valid(in)) {
592 out = in;
593 } else {
594 rc = utf8_encode(in, id3_default_charset, &out);
595 free(in);
597 break;
598 case 0x01: /* UTF-16 */
599 out = utf16_to_utf8((const unsigned char *)buf, len);
600 break;
601 case 0x02: /* UTF-16BE */
602 out = utf16be_to_utf8((const unsigned char *)buf, len);
603 break;
605 return out;
608 static void add_v2(struct id3tag *id3, enum id3_key key, char *value)
610 free(id3->v2[key]);
611 id3->v2[key] = value;
612 id3->has_v2 = 1;
615 static void decode_normal(struct id3tag *id3, const char *buf, int len, int encoding, enum id3_key key)
617 char *out = decode_str(buf, len, encoding);
619 if (!out)
620 return;
622 if (key == ID3_GENRE) {
623 char *tmp;
625 id3_debug("genre before: '%s'\n", out);
626 tmp = parse_genre(out);
627 free(out);
628 out = tmp;
630 if (key == ID3_DATE) {
631 id3_debug("date before: '%s'\n", out);
632 fix_date(out);
633 if (!*out) {
634 id3_debug("date parsing failed\n");
635 free(out);
636 return;
639 add_v2(id3, key, out);
642 static void decode_txxx(struct id3tag *id3, const char *buf, int len, int encoding)
644 enum id3_key key = NUM_ID3_KEYS;
645 int size;
646 char *out;
648 out = decode_str(buf, len, encoding);
649 if (!out)
650 return;
652 id3_debug("TXXX, key = '%s'\n", out);
653 if (!strcasecmp(out, "replaygain_track_gain"))
654 key = ID3_RG_TRACK_GAIN;
655 if (!strcasecmp(out, "replaygain_track_peak"))
656 key = ID3_RG_TRACK_PEAK;
657 if (!strcasecmp(out, "replaygain_album_gain"))
658 key = ID3_RG_ALBUM_GAIN;
659 if (!strcasecmp(out, "replaygain_album_peak"))
660 key = ID3_RG_ALBUM_PEAK;
661 if (!strcasecmp(out, "album artist"))
662 key = ID3_ALBUMARTIST;
663 if (!strcasecmp(out, "albumartistsort"))
664 key = ID3_ALBUMARTISTSORT;
665 if (!strcasecmp(out, "compilation"))
666 key = ID3_COMPILATION;
668 size = strlen(out) + 1;
669 free(out);
671 if (key == NUM_ID3_KEYS)
672 return;
674 buf += size;
675 len -= size;
676 if (len <= 0)
677 return;
679 out = decode_str(buf, len, encoding);
680 if (!out)
681 return;
683 add_v2(id3, key, out);
686 static void decode_comment(struct id3tag *id3, const char *buf, int len, int encoding)
688 char *out;
690 if (len <= 4)
691 return;
693 /* skip language */
694 buf += 4;
695 len -= 4;
697 out = decode_str(buf, len, encoding);
698 if (!out)
699 return;
701 add_v2(id3, ID3_COMMENT, out);
704 static void v2_add_frame(struct id3tag *id3, struct v2_frame_header *fh, const char *buf)
706 int encoding = *buf++;
707 int len = fh->size - 1;
708 int idx;
710 if (encoding > 3)
711 return;
713 idx = frame_tab_index(fh->id);
714 if (idx >= 0) {
715 decode_normal(id3, buf, len, encoding, frame_tab[idx].key);
716 } else if (!strncmp(fh->id, "TXXX", 4)) {
717 decode_txxx(id3, buf, len, encoding);
718 } else if (!strncmp(fh->id, "COMM", 4)) {
719 decode_comment(id3, buf, len, encoding);
720 } else if (!strncmp(fh->id, "COM", 4)) {
721 decode_comment(id3, buf, len, encoding);
725 static void unsync(unsigned char *buf, int *lenp)
727 int len = *lenp;
728 int s, d;
730 s = d = 0;
731 while (s < len - 1) {
732 if (buf[s] == 0xff && buf[s + 1] == 0x00) {
733 /* 0xff 0x00 -> 0xff */
734 buf[d++] = 0xff;
735 s += 2;
737 if (s < len - 2 && buf[s] == 0x00) {
738 /* 0xff 0x00 0x00 -> 0xff 0x00 */
739 buf[d++] = 0x00;
740 s++;
742 continue;
744 buf[d++] = buf[s++];
746 if (s < len)
747 buf[d++] = buf[s++];
749 d_print("unsyncronization removed %d bytes\n", s - d);
750 *lenp = d;
753 static int v2_read(struct id3tag *id3, int fd, const struct v2_header *header)
755 char *buf;
756 int rc, buf_size;
757 int frame_start, i;
758 int frame_header_size;
760 buf_size = header->size;
761 buf = xnew(char, buf_size);
762 rc = read_all(fd, buf, buf_size);
763 if (rc) {
764 free(buf);
765 return rc;
768 frame_start = 0;
769 if (header->flags & V2_HEADER_EXTENDED) {
770 struct v2_extended_header ext;
772 if (!v2_extended_header_parse(&ext, buf) || ext.size > buf_size) {
773 id3_debug("extended header corrupted\n");
774 free(buf);
775 return -2;
777 frame_start = ext.size;
778 /* should check if update flag is set */
781 if (header->flags & V2_HEADER_UNSYNC) {
782 int len = buf_size - frame_start;
784 unsync((unsigned char *)(buf + frame_start), &len);
785 buf_size = len + frame_start;
788 frame_header_size = 10;
789 if (header->ver_major == 2)
790 frame_header_size = 6;
792 i = frame_start;
793 while (i < buf_size - frame_header_size) {
794 struct v2_frame_header fh;
795 int len;
797 if (header->ver_major == 2) {
798 if (!v2_2_0_frame_header_parse(&fh, buf + i))
799 break;
800 } else if (header->ver_major == 3) {
801 if (!v2_3_0_frame_header_parse(&fh, buf + i))
802 break;
803 } else {
804 /* assume v2.4 */
805 if (!v2_4_0_frame_header_parse(&fh, buf + i))
806 break;
809 i += frame_header_size;
810 if (fh.size > buf_size - i) {
811 id3_debug("frame too big\n");
812 break;
815 len = fh.size;
816 if (fh.flags & V2_FRAME_UNSYNC) {
817 int tmp = len;
819 unsync((unsigned char *)(buf + i), &tmp);
820 fh.size = tmp;
822 v2_add_frame(id3, &fh, buf + i);
823 i += len;
826 free(buf);
827 return 0;
830 int id3_tag_size(const char *buf, int buf_size)
832 struct v2_header header;
834 if (buf_size < 10)
835 return 0;
836 if (v2_header_parse(&header, buf)) {
837 if (header.flags & V2_HEADER_FOOTER) {
838 /* header + data + footer */
839 id3_debug("v2.%d.%d with footer\n", header.ver_major, header.ver_minor);
840 return 10 + header.size + 10;
842 /* header */
843 id3_debug("v2.%d.%d\n", header.ver_major, header.ver_minor);
844 return 10 + header.size;
846 if (buf_size >= 3 && is_v1(buf)) {
847 id3_debug("v1\n");
848 return 128;
850 return 0;
853 void id3_free(struct id3tag *id3)
855 int i;
857 for (i = 0; i < NUM_ID3_KEYS; i++)
858 free(id3->v2[i]);
861 int id3_read_tags(struct id3tag *id3, int fd, unsigned int flags)
863 off_t off;
864 int rc;
866 if (flags & ID3_V2) {
867 struct v2_header header;
868 char buf[138];
870 rc = read_all(fd, buf, 10);
871 if (rc)
872 goto rc_error;
873 if (v2_header_parse(&header, buf)) {
874 rc = v2_read(id3, fd, &header);
875 if (rc)
876 goto rc_error;
877 /* get v1 if needed */
878 } else {
879 /* get v2 from end and optionally v1 */
881 off = lseek(fd, -138, SEEK_END);
882 if (off == -1)
883 goto error;
884 rc = read_all(fd, buf, 138);
885 if (rc)
886 goto rc_error;
888 if (is_v1(buf + 10)) {
889 if (flags & ID3_V1) {
890 memcpy(id3->v1, buf + 10, 128);
891 id3->has_v1 = 1;
893 if (v2_footer_parse(&header, buf)) {
894 /* footer at end of file - 128 */
895 off = lseek(fd, -(header.size + 138), SEEK_END);
896 if (off == -1)
897 goto error;
898 rc = v2_read(id3, fd, &header);
899 if (rc)
900 goto rc_error;
902 } else if (v2_footer_parse(&header, buf + 128)) {
903 /* footer at end of file */
904 off = lseek(fd, -(header.size + 10), SEEK_END);
905 if (off == -1)
906 goto error;
907 rc = v2_read(id3, fd, &header);
908 if (rc)
909 goto rc_error;
911 return 0;
914 if (flags & ID3_V1) {
915 off = lseek(fd, -128, SEEK_END);
916 if (off == -1)
917 goto error;
918 rc = read_all(fd, id3->v1, 128);
919 if (rc)
920 goto rc_error;
921 id3->has_v1 = is_v1(id3->v1);
923 return 0;
924 error:
925 rc = -1;
926 rc_error:
927 return rc;
930 static char *v1_get_str(const char *buf, int len)
932 char in[32];
933 char *out;
934 int i;
936 for (i = len - 1; i >= 0; i--) {
937 if (buf[i] != 0 && buf[i] != ' ')
938 break;
940 if (i == -1)
941 return NULL;
942 memcpy(in, buf, i + 1);
943 in[i + 1] = 0;
944 if (u_is_valid(in))
945 return xstrdup(in);
946 if (utf8_encode(in, id3_default_charset, &out))
947 return NULL;
948 return out;
951 char *id3_get_comment(struct id3tag *id3, enum id3_key key)
953 if (id3->has_v2) {
954 if (id3->v2[key])
955 return xstrdup(id3->v2[key]);
957 if (id3->has_v1) {
958 switch (key) {
959 case ID3_ARTIST:
960 return v1_get_str(id3->v1 + 33, 30);
961 case ID3_ALBUM:
962 return v1_get_str(id3->v1 + 63, 30);
963 case ID3_TITLE:
964 return v1_get_str(id3->v1 + 3, 30);
965 case ID3_DATE:
966 return v1_get_str(id3->v1 + 93, 4);
967 case ID3_GENRE:
969 unsigned char idx = id3->v1[127];
971 if (idx >= NR_GENRES)
972 return NULL;
973 return xstrdup(genres[idx]);
975 case ID3_TRACK:
977 char *t;
979 if (id3->v1[125] != 0)
980 return NULL;
981 t = xnew(char, 4);
982 snprintf(t, 4, "%d", ((unsigned char *)id3->v1)[126]);
983 return t;
985 default:
986 return NULL;
989 return NULL;