wavpack: Pass correct sample count to format_samples()
[cmus.git] / id3.c
blobbe0a417c8bb08cbcf8c606b1dc0f64fe6734d0e1
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 ID3 {
30 char v1[128];
31 char *v2[NUM_ID3_KEYS];
33 unsigned int has_v1 : 1;
34 unsigned int has_v2 : 1;
37 struct v2_header {
38 unsigned char ver_major;
39 unsigned char ver_minor;
40 unsigned char flags;
41 uint32_t size;
44 struct v2_extended_header {
45 uint32_t size;
48 struct v2_frame_header {
49 char id[4];
50 uint32_t size;
51 uint16_t flags;
54 #define V2_HEADER_UNSYNC (1 << 7)
55 #define V2_HEADER_EXTENDED (1 << 6)
56 #define V2_HEADER_EXPERIMENTAL (1 << 5)
57 #define V2_HEADER_FOOTER (1 << 4)
59 #define V2_FRAME_COMPRESSED (1 << 3) /* great idea!!1 */
60 #define V2_FRAME_ENCRYPTHED (1 << 2) /* wow, this is very neat! */
61 #define V2_FRAME_UNSYNC (1 << 1)
62 #define V2_FRAME_LEN_INDICATOR (1 << 0)
64 #define NR_GENRES 148
65 /* genres {{{ */
66 static const char *genres[NR_GENRES] = {
67 "Blues",
68 "Classic Rock",
69 "Country",
70 "Dance",
71 "Disco",
72 "Funk",
73 "Grunge",
74 "Hip-Hop",
75 "Jazz",
76 "Metal",
77 "New Age",
78 "Oldies",
79 "Other",
80 "Pop",
81 "R&B",
82 "Rap",
83 "Reggae",
84 "Rock",
85 "Techno",
86 "Industrial",
87 "Alternative",
88 "Ska",
89 "Death Metal",
90 "Pranks",
91 "Soundtrack",
92 "Euro-Techno",
93 "Ambient",
94 "Trip-Hop",
95 "Vocal",
96 "Jazz+Funk",
97 "Fusion",
98 "Trance",
99 "Classical",
100 "Instrumental",
101 "Acid",
102 "House",
103 "Game",
104 "Sound Clip",
105 "Gospel",
106 "Noise",
107 "Alt",
108 "Bass",
109 "Soul",
110 "Punk",
111 "Space",
112 "Meditative",
113 "Instrumental Pop",
114 "Instrumental Rock",
115 "Ethnic",
116 "Gothic",
117 "Darkwave",
118 "Techno-Industrial",
119 "Electronic",
120 "Pop-Folk",
121 "Eurodance",
122 "Dream",
123 "Southern Rock",
124 "Comedy",
125 "Cult",
126 "Gangsta Rap",
127 "Top 40",
128 "Christian Rap",
129 "Pop/Funk",
130 "Jungle",
131 "Native American",
132 "Cabaret",
133 "New Wave",
134 "Psychedelic",
135 "Rave",
136 "Showtunes",
137 "Trailer",
138 "Lo-Fi",
139 "Tribal",
140 "Acid Punk",
141 "Acid Jazz",
142 "Polka",
143 "Retro",
144 "Musical",
145 "Rock & Roll",
146 "Hard Rock",
147 "Folk",
148 "Folk/Rock",
149 "National Folk",
150 "Swing",
151 "Fast-Fusion",
152 "Bebob",
153 "Latin",
154 "Revival",
155 "Celtic",
156 "Bluegrass",
157 "Avantgarde",
158 "Gothic Rock",
159 "Progressive Rock",
160 "Psychedelic Rock",
161 "Symphonic Rock",
162 "Slow Rock",
163 "Big Band",
164 "Chorus",
165 "Easy Listening",
166 "Acoustic",
167 "Humour",
168 "Speech",
169 "Chanson",
170 "Opera",
171 "Chamber Music",
172 "Sonata",
173 "Symphony",
174 "Booty Bass",
175 "Primus",
176 "Porn Groove",
177 "Satire",
178 "Slow Jam",
179 "Club",
180 "Tango",
181 "Samba",
182 "Folklore",
183 "Ballad",
184 "Power Ballad",
185 "Rhythmic Soul",
186 "Freestyle",
187 "Duet",
188 "Punk Rock",
189 "Drum Solo",
190 "A Cappella",
191 "Euro-House",
192 "Dance Hall",
193 "Goa",
194 "Drum & Bass",
195 "Club-House",
196 "Hardcore",
197 "Terror",
198 "Indie",
199 "BritPop",
200 "Negerpunk",
201 "Polsk Punk",
202 "Beat",
203 "Christian Gangsta Rap",
204 "Heavy Metal",
205 "Black Metal",
206 "Crossover",
207 "Contemporary Christian",
208 "Christian Rock",
209 "Merengue",
210 "Salsa",
211 "Thrash Metal",
212 "Anime",
213 "JPop",
214 "Synthpop"
216 /* }}} */
218 #if 1
219 #define id3_debug(...) d_print(__VA_ARGS__)
220 #else
221 #define id3_debug(...) do { } while (0)
222 #endif
224 const char * const id3_key_names[NUM_ID3_KEYS] = {
225 "artist",
226 "album",
227 "title",
228 "date",
229 "genre",
230 "discnumber",
231 "tracknumber",
232 "albumartist",
233 "artistsort",
234 "albumartistsort",
235 "replaygain_track_gain",
236 "replaygain_track_peak",
237 "replaygain_album_gain",
238 "replaygain_album_peak"
241 static int utf16_is_special(const uchar uch)
243 if (UTF16_IS_HSURROGATE(uch) || UTF16_IS_LSURROGATE(uch) || UTF16_IS_BOM(uch))
244 return -1;
245 return 0;
248 static char *utf16_to_utf8(const unsigned char *buf, int buf_size)
250 char *out;
251 int i, idx;
253 out = xnew(char, (buf_size / 2) * 4 + 1);
254 i = idx = 0;
255 while (buf_size - i >= 2) {
256 uchar u;
258 u = buf[i] + (buf[i + 1] << 8);
259 if (u_is_unicode(u)) {
260 if (utf16_is_special(u) == 0)
261 u_set_char(out, &idx, u);
262 } else {
263 free(out);
264 return NULL;
266 if (u == 0)
267 return out;
268 i += 2;
270 u_set_char(out, &idx, 0);
271 return out;
274 static char *utf16be_to_utf8(const unsigned char *buf, int buf_size)
276 char *out;
277 int i, idx;
279 out = xnew(char, (buf_size / 2) * 4 + 1);
280 i = 0;
281 idx = 0;
282 while (buf_size - i >= 2) {
283 uchar u;
285 u = buf[i + 1] + (buf[i] << 8);
286 if (u_is_unicode(u)) {
287 if (utf16_is_special(u) == 0)
288 u_set_char(out, &idx, u);
289 } else {
290 free(out);
291 return NULL;
293 if (u == 0)
294 return out;
295 i += 2;
297 u_set_char(out, &idx, 0);
298 return out;
301 static int is_v1(const char *buf)
303 return buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G';
306 static int u32_unsync(const unsigned char *buf, uint32_t *up)
308 uint32_t b, u = 0;
309 int i;
311 for (i = 0; i < 4; i++) {
312 b = buf[i];
313 if (b >= 0x80)
314 return 0;
315 u <<= 7;
316 u |= b;
318 *up = u;
319 return 1;
322 static void get_u32(const unsigned char *buf, uint32_t *up)
324 uint32_t b, u = 0;
325 int i;
327 for (i = 0; i < 4; i++) {
328 b = buf[i];
329 u <<= 8;
330 u |= b;
332 *up = u;
335 static void get_u24(const unsigned char *buf, uint32_t *up)
337 uint32_t b, u = 0;
338 int i;
340 for (i = 0; i < 3; i++) {
341 b = buf[i];
342 u <<= 8;
343 u |= b;
345 *up = u;
348 static int v2_header_footer_parse(struct v2_header *header, const char *buf)
350 const unsigned char *b = (const unsigned char *)buf;
352 header->ver_major = b[3];
353 header->ver_minor = b[4];
354 header->flags = b[5];
355 if (header->ver_major == 0xff || header->ver_minor == 0xff)
356 return 0;
357 return u32_unsync(b + 6, &header->size);
360 static int v2_header_parse(struct v2_header *header, const char *buf)
362 if (buf[0] != 'I' || buf[1] != 'D' || buf[2] != '3')
363 return 0;
364 return v2_header_footer_parse(header, buf);
367 static int v2_footer_parse(struct v2_header *header, const char *buf)
369 if (buf[0] != '3' || buf[1] != 'D' || buf[2] != 'I')
370 return 0;
371 return v2_header_footer_parse(header, buf);
374 static int v2_extended_header_parse(struct v2_extended_header *header, const char *buf)
376 return u32_unsync((const unsigned char *)buf, &header->size);
379 static int is_frame_id_char(char ch)
381 return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
384 /* XXXYYY
386 * X = [A-Z0-9]
387 * Y = byte
389 * XXX is frame
390 * YYY is frame size excluding this 6 byte header
392 static int v2_2_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
394 int i;
396 for (i = 0; i < 3; i++) {
397 if (!is_frame_id_char(buf[i]))
398 return 0;
399 header->id[i] = buf[i];
401 header->id[3] = 0;
402 get_u24((const unsigned char *)(buf + 3), &header->size);
403 header->flags = 0;
404 if (header->size == 0)
405 return 0;
406 id3_debug("%c%c%c %d\n", header->id[0], header->id[1], header->id[2], header->size);
407 return 1;
410 /* XXXXYYYYZZ
412 * X = [A-Z0-9]
413 * Y = byte
414 * Z = byte
416 * XXXX is frame
417 * YYYY is frame size excluding this 10 byte header
418 * ZZ is flags
420 static int v2_3_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
422 int i;
424 for (i = 0; i < 4; i++) {
425 if (!is_frame_id_char(buf[i]))
426 return 0;
427 header->id[i] = buf[i];
429 get_u32((const unsigned char *)(buf + 4), &header->size);
430 header->flags = (buf[8] << 8) | buf[9];
431 if (header->size == 0)
432 return 0;
433 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
434 header->id[3], header->size);
435 return 1;
438 /* same as 2.3 but header size is sync safe */
439 static int v2_4_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
441 int i;
443 for (i = 0; i < 4; i++) {
444 if (!is_frame_id_char(buf[i]))
445 return 0;
446 header->id[i] = buf[i];
448 if (!u32_unsync((const unsigned char *)(buf + 4), &header->size))
449 return 0;
450 header->flags = (buf[8] << 8) | buf[9];
451 if (header->size == 0)
452 return 0;
453 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
454 header->id[3], header->size);
455 return 1;
458 static int read_all(int fd, char *buf, size_t size)
460 size_t pos = 0;
462 while (pos < size) {
463 int rc = read(fd, buf + pos, size - pos);
465 if (rc == -1) {
466 if (errno == EINTR || errno == EAGAIN)
467 continue;
468 return -1;
470 pos += rc;
472 return 0;
475 static char *parse_genre(const char *str)
477 int parenthesis = 0;
478 long int idx;
479 char *end;
481 if (strncasecmp(str, "(RX", 3) == 0)
482 return xstrdup("Remix");
484 if (strncasecmp(str, "(CR", 3) == 0)
485 return xstrdup("Cover");
487 if (*str == '(') {
488 parenthesis = 1;
489 str++;
492 idx = strtol(str, &end, 10);
493 if (str != end) {
494 /* Number parsed but there may be some crap after the number.
495 * I don't care, ID3v2 by definition contains crap.
497 if (idx >= 0 && idx < NR_GENRES)
498 return xstrdup(genres[idx]);
501 if (parenthesis) {
502 const char *ptr = strchr(str, ')');
504 if (ptr && ptr[1]) {
505 /* genre name after random crap in parenthesis,
506 * return the genre name */
507 return xstrdup(ptr + 1);
509 str--;
512 /* random crap, just return it and wait for a bug report */
513 return xstrdup(str);
516 /* http://www.id3.org/id3v2.4.0-structure.txt */
517 static struct {
518 const char name[8];
519 enum id3_key key;
520 } frame_tab[] = {
521 /* 2.4.0 */
522 { "TDRC", ID3_DATE }, // recording date
523 { "TDRL", ID3_DATE }, // release date
524 { "TDOR", ID3_DATE }, // original release date
525 { "TSOP", ID3_ARTISTSORT },
527 /* >= 2.3.0 */
528 { "TPE1", ID3_ARTIST },
529 { "TALB", ID3_ALBUM },
530 { "TIT2", ID3_TITLE },
531 { "TYER", ID3_DATE },
532 { "TCON", ID3_GENRE },
533 { "TPOS", ID3_DISC },
534 { "TRCK", ID3_TRACK },
535 { "TPE2", ID3_ALBUMARTIST },
536 { "XSOP", ID3_ARTISTSORT }, // obsolete
538 /* obsolete frames (2.2.0) */
539 { "TP1", ID3_ARTIST },
540 { "TAL", ID3_ALBUM },
541 { "TT2", ID3_TITLE },
542 { "TYE", ID3_DATE },
543 { "TCO", ID3_GENRE },
544 { "TPA", ID3_DISC },
545 { "TRK", ID3_TRACK },
547 { "", -1 }
550 static int frame_tab_index(const char *id)
552 int i;
554 for (i = 0; frame_tab[i].key != -1; i++) {
555 if (!strncmp(id, frame_tab[i].name, 4))
556 return i;
558 return -1;
561 static void fix_date(char *buf)
563 const char *ptr = buf;
564 int ch, len = 0;
566 do {
567 ch = *ptr++;
568 if (ch >= '0' && ch <= '9') {
569 len++;
570 continue;
572 if (len == 4) {
573 // number which length is 4, must be year
574 memmove(buf, ptr - 5, 4);
575 buf[4] = 0;
576 return;
578 len = 0;
579 } while (ch);
580 *buf = 0;
583 static char *decode_str(const char *buf, int len, int encoding)
585 char *in, *out = NULL;
586 int rc = 0;
588 switch (encoding) {
589 case 0x00: /* ISO-8859-1 */
590 in = xstrndup(buf, len);
591 rc = utf8_encode(in, id3_default_charset, &out);
592 free(in);
593 break;
594 case 0x03: /* UTF-8 */
595 in = xstrndup(buf, len);
596 if (u_is_valid(in)) {
597 out = in;
598 } else {
599 rc = utf8_encode(in, id3_default_charset, &out);
600 free(in);
602 break;
603 case 0x01: /* UTF-16 */
604 out = utf16_to_utf8((const unsigned char *)buf, len);
605 break;
606 case 0x02: /* UTF-16BE */
607 out = utf16be_to_utf8((const unsigned char *)buf, len);
608 break;
610 return out;
613 static void v2_add_frame(ID3 *id3, struct v2_frame_header *fh, const char *buf)
615 int idx, encoding = *buf++, len = fh->size - 1;
616 enum id3_key key = NUM_ID3_KEYS;
617 char *out;
619 if (encoding > 3)
620 return;
622 idx = frame_tab_index(fh->id);
623 if (idx >= 0) {
624 key = frame_tab[idx].key;
625 out = decode_str(buf, len, encoding);
626 if (!out)
627 return;
629 if (key == ID3_GENRE) {
630 char *tmp;
632 id3_debug("genre before: '%s'\n", out);
633 tmp = parse_genre(out);
634 free(out);
635 out = tmp;
637 if (key == ID3_DATE) {
638 id3_debug("date before: '%s'\n", out);
639 fix_date(out);
640 if (!*out) {
641 id3_debug("date parsing failed\n");
642 free(out);
643 return;
647 id3_debug("%s '%s'\n", frame_tab[idx].name, out);
648 } else if (!strncmp(fh->id, "TXXX", 4)) {
649 int size;
651 id3_debug("TXXX\n");
653 /* TXXX<len><encoding><key><val> */
654 out = decode_str(buf, len, encoding);
655 if (!out)
656 return;
658 id3_debug("TXXX, key = '%s'\n", out);
659 if (!strcasecmp(out, "replaygain_track_gain"))
660 key = ID3_RG_TRACK_GAIN;
661 if (!strcasecmp(out, "replaygain_track_peak"))
662 key = ID3_RG_TRACK_PEAK;
663 if (!strcasecmp(out, "replaygain_album_gain"))
664 key = ID3_RG_ALBUM_GAIN;
665 if (!strcasecmp(out, "replaygain_album_peak"))
666 key = ID3_RG_ALBUM_PEAK;
667 if (!strcasecmp(out, "album artist"))
668 key = ID3_ALBUMARTIST;
669 if (!strcasecmp(out, "albumartistsort"))
670 key = ID3_ALBUMARTISTSORT;
672 size = strlen(out) + 1;
673 free(out);
675 if (key == NUM_ID3_KEYS)
676 return;
678 buf += size;
679 len -= size;
680 if (len <= 0)
681 return;
683 out = decode_str(buf, len, encoding);
684 if (!out)
685 return;
687 id3_debug("TXXX, val = '%s'\n", out);
688 } else {
689 return;
692 free(id3->v2[key]);
693 id3->v2[key] = out;
694 id3->has_v2 = 1;
697 static void unsync(unsigned char *buf, int *lenp)
699 int len = *lenp;
700 int s, d;
702 s = d = 0;
703 while (s < len - 1) {
704 if (buf[s] == 0xff && buf[s + 1] == 0x00) {
705 /* 0xff 0x00 -> 0xff */
706 buf[d++] = 0xff;
707 s += 2;
709 if (s < len - 2 && buf[s] == 0x00) {
710 /* 0xff 0x00 0x00 -> 0xff 0x00 */
711 buf[d++] = 0x00;
712 s++;
714 continue;
716 buf[d++] = buf[s++];
718 if (s < len)
719 buf[d++] = buf[s++];
721 d_print("unsyncronization removed %d bytes\n", s - d);
722 *lenp = d;
725 static int v2_read(ID3 *id3, int fd, const struct v2_header *header)
727 char *buf;
728 int rc, buf_size;
729 int frame_start, i;
730 int frame_header_size;
732 buf_size = header->size;
733 buf = xnew(char, buf_size);
734 rc = read_all(fd, buf, buf_size);
735 if (rc) {
736 free(buf);
737 return rc;
740 frame_start = 0;
741 if (header->flags & V2_HEADER_EXTENDED) {
742 struct v2_extended_header ext;
744 v2_extended_header_parse(&ext, buf);
745 if (ext.size > buf_size) {
746 id3_debug("extended header corrupted\n");
747 free(buf);
748 return -2;
750 frame_start = ext.size;
751 /* should check if update flag is set */
754 if (header->flags & V2_HEADER_UNSYNC) {
755 int len = buf_size - frame_start;
757 unsync((unsigned char *)(buf + frame_start), &len);
758 buf_size = len + frame_start;
761 frame_header_size = 10;
762 if (header->ver_major == 2)
763 frame_header_size = 6;
765 i = frame_start;
766 while (i < buf_size - frame_header_size) {
767 struct v2_frame_header fh;
768 int len;
770 if (header->ver_major == 2) {
771 if (!v2_2_0_frame_header_parse(&fh, buf + i))
772 break;
773 } else if (header->ver_major == 3) {
774 if (!v2_3_0_frame_header_parse(&fh, buf + i))
775 break;
776 } else {
777 /* assume v2.4 */
778 if (!v2_4_0_frame_header_parse(&fh, buf + i))
779 break;
782 i += frame_header_size;
783 if (fh.size > buf_size - i) {
784 id3_debug("frame too big\n");
785 break;
788 len = fh.size;
789 if (fh.flags & V2_FRAME_UNSYNC) {
790 int tmp = len;
792 unsync((unsigned char *)(buf + i), &tmp);
793 fh.size = tmp;
795 v2_add_frame(id3, &fh, buf + i);
796 i += len;
799 free(buf);
800 return 0;
803 int id3_tag_size(const char *buf, int buf_size)
805 struct v2_header header;
807 if (buf_size < 10)
808 return 0;
809 if (v2_header_parse(&header, buf)) {
810 if (header.flags & V2_HEADER_FOOTER) {
811 /* header + data + footer */
812 id3_debug("v2.%d.%d with footer\n", header.ver_major, header.ver_minor);
813 return 10 + header.size + 10;
815 /* header */
816 id3_debug("v2.%d.%d\n", header.ver_major, header.ver_minor);
817 return 10 + header.size;
819 if (buf_size >= 3 && is_v1(buf)) {
820 id3_debug("v1\n");
821 return 128;
823 return 0;
826 ID3 *id3_new(void)
828 return xnew0(ID3, 1);
831 void id3_free(ID3 *id3)
833 int i;
835 for (i = 0; i < NUM_ID3_KEYS; i++)
836 free(id3->v2[i]);
837 free(id3);
840 int id3_read_tags(ID3 *id3, int fd, unsigned int flags)
842 off_t off;
843 int rc;
845 if (flags & ID3_V2) {
846 struct v2_header header;
847 char buf[138];
849 rc = read_all(fd, buf, 10);
850 if (rc)
851 goto rc_error;
852 if (v2_header_parse(&header, buf)) {
853 rc = v2_read(id3, fd, &header);
854 if (rc)
855 goto rc_error;
856 /* get v1 if needed */
857 } else {
858 /* get v2 from end and optionally v1 */
860 off = lseek(fd, -138, SEEK_END);
861 if (off == -1)
862 goto error;
863 rc = read_all(fd, buf, 138);
864 if (rc)
865 goto rc_error;
867 if (is_v1(buf + 10)) {
868 if (flags & ID3_V1) {
869 memcpy(id3->v1, buf + 10, 128);
870 id3->has_v1 = 1;
872 if (v2_footer_parse(&header, buf)) {
873 /* footer at end of file - 128 */
874 off = lseek(fd, -(header.size + 138), SEEK_END);
875 if (off == -1)
876 goto error;
877 rc = v2_read(id3, fd, &header);
878 if (rc)
879 goto rc_error;
881 } else if (v2_footer_parse(&header, buf + 128)) {
882 /* footer at end of file */
883 off = lseek(fd, -(header.size + 10), SEEK_END);
884 if (off == -1)
885 goto error;
886 rc = v2_read(id3, fd, &header);
887 if (rc)
888 goto rc_error;
890 return 0;
893 if (flags & ID3_V1) {
894 off = lseek(fd, -128, SEEK_END);
895 if (off == -1)
896 goto error;
897 rc = read_all(fd, id3->v1, 128);
898 if (rc)
899 goto rc_error;
900 id3->has_v1 = is_v1(id3->v1);
902 return 0;
903 error:
904 rc = -1;
905 rc_error:
906 return rc;
909 static char *v1_get_str(const char *buf, int len)
911 char in[32];
912 char *out;
913 int i;
915 for (i = len - 1; i >= 0; i--) {
916 if (buf[i] != 0 && buf[i] != ' ')
917 break;
919 if (i == -1)
920 return NULL;
921 memcpy(in, buf, i + 1);
922 in[i + 1] = 0;
923 if (u_is_valid(in))
924 return xstrdup(in);
925 if (utf8_encode(in, id3_default_charset, &out))
926 return NULL;
927 return out;
930 char *id3_get_comment(ID3 *id3, enum id3_key key)
932 if (id3->has_v2) {
933 if (id3->v2[key])
934 return xstrdup(id3->v2[key]);
936 if (id3->has_v1) {
937 switch (key) {
938 case ID3_ARTIST:
939 return v1_get_str(id3->v1 + 33, 30);
940 case ID3_ALBUM:
941 return v1_get_str(id3->v1 + 63, 30);
942 case ID3_TITLE:
943 return v1_get_str(id3->v1 + 3, 30);
944 case ID3_DATE:
945 return v1_get_str(id3->v1 + 93, 4);
946 case ID3_GENRE:
948 unsigned char idx = id3->v1[127];
950 if (idx >= NR_GENRES)
951 return NULL;
952 return xstrdup(genres[idx]);
954 case ID3_TRACK:
956 char *t;
958 if (id3->v1[125] != 0)
959 return NULL;
960 t = xnew(char, 4);
961 snprintf(t, 4, "%d", ((unsigned char *)id3->v1)[126]);
962 return t;
964 default:
965 return NULL;
968 return NULL;