Fix libmikmod 3.1.10 check
[cmus.git] / id3.c
blob668a4069d9d7365b5f2a379238e4c53719e9611c
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 "replaygain_track_gain",
228 "replaygain_track_peak",
229 "replaygain_album_gain",
230 "replaygain_album_peak"
233 static int utf16_is_special(const uchar uch)
235 if (UTF16_IS_HSURROGATE(uch) || UTF16_IS_LSURROGATE(uch) || UTF16_IS_BOM(uch))
236 return -1;
237 return 0;
240 static char *utf16_to_utf8(const unsigned char *buf, int buf_size)
242 char *out;
243 int i, idx;
245 out = xnew(char, (buf_size / 2) * 4 + 1);
246 i = idx = 0;
247 while (buf_size - i >= 2) {
248 uchar u;
250 u = buf[i] + (buf[i + 1] << 8);
251 if (u_is_unicode(u)) {
252 if (utf16_is_special(u) == 0)
253 u_set_char(out, &idx, u);
254 } else {
255 free(out);
256 return NULL;
258 if (u == 0)
259 return out;
260 i += 2;
262 u_set_char(out, &idx, 0);
263 return out;
266 static char *utf16be_to_utf8(const unsigned char *buf, int buf_size)
268 char *out;
269 int i, idx;
271 out = xnew(char, (buf_size / 2) * 4 + 1);
272 i = 0;
273 idx = 0;
274 while (buf_size - i >= 2) {
275 uchar u;
277 u = buf[i + 1] + (buf[i] << 8);
278 if (u_is_unicode(u)) {
279 if (utf16_is_special(u) == 0)
280 u_set_char(out, &idx, u);
281 } else {
282 free(out);
283 return NULL;
285 if (u == 0)
286 return out;
287 i += 2;
289 u_set_char(out, &idx, 0);
290 return out;
293 static int is_v1(const char *buf)
295 return buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G';
298 static int u32_unsync(const unsigned char *buf, uint32_t *up)
300 uint32_t b, u = 0;
301 int i;
303 for (i = 0; i < 4; i++) {
304 b = buf[i];
305 if (b >= 0x80)
306 return 0;
307 u <<= 7;
308 u |= b;
310 *up = u;
311 return 1;
314 static void get_u32(const unsigned char *buf, uint32_t *up)
316 uint32_t b, u = 0;
317 int i;
319 for (i = 0; i < 4; i++) {
320 b = buf[i];
321 u <<= 8;
322 u |= b;
324 *up = u;
327 static void get_u24(const unsigned char *buf, uint32_t *up)
329 uint32_t b, u = 0;
330 int i;
332 for (i = 0; i < 3; i++) {
333 b = buf[i];
334 u <<= 8;
335 u |= b;
337 *up = u;
340 static int v2_header_footer_parse(struct v2_header *header, const char *buf)
342 const unsigned char *b = (const unsigned char *)buf;
344 header->ver_major = b[3];
345 header->ver_minor = b[4];
346 header->flags = b[5];
347 if (header->ver_major == 0xff || header->ver_minor == 0xff)
348 return 0;
349 return u32_unsync(b + 6, &header->size);
352 static int v2_header_parse(struct v2_header *header, const char *buf)
354 if (buf[0] != 'I' || buf[1] != 'D' || buf[2] != '3')
355 return 0;
356 return v2_header_footer_parse(header, buf);
359 static int v2_footer_parse(struct v2_header *header, const char *buf)
361 if (buf[0] != '3' || buf[1] != 'D' || buf[2] != 'I')
362 return 0;
363 return v2_header_footer_parse(header, buf);
366 static int v2_extended_header_parse(struct v2_extended_header *header, const char *buf)
368 return u32_unsync((const unsigned char *)buf, &header->size);
371 static int is_frame_id_char(char ch)
373 return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
376 /* XXXYYY
378 * X = [A-Z0-9]
379 * Y = byte
381 * XXX is frame
382 * YYY is frame size excluding this 6 byte header
384 static int v2_2_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
386 int i;
388 for (i = 0; i < 3; i++) {
389 if (!is_frame_id_char(buf[i]))
390 return 0;
391 header->id[i] = buf[i];
393 header->id[3] = 0;
394 get_u24((const unsigned char *)(buf + 3), &header->size);
395 header->flags = 0;
396 if (header->size == 0)
397 return 0;
398 id3_debug("%c%c%c %d\n", header->id[0], header->id[1], header->id[2], header->size);
399 return 1;
402 /* XXXXYYYYZZ
404 * X = [A-Z0-9]
405 * Y = byte
406 * Z = byte
408 * XXXX is frame
409 * YYYY is frame size excluding this 10 byte header
410 * ZZ is flags
412 static int v2_3_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
414 int i;
416 for (i = 0; i < 4; i++) {
417 if (!is_frame_id_char(buf[i]))
418 return 0;
419 header->id[i] = buf[i];
421 get_u32((const unsigned char *)(buf + 4), &header->size);
422 header->flags = (buf[8] << 8) | buf[9];
423 if (header->size == 0)
424 return 0;
425 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
426 header->id[3], header->size);
427 return 1;
430 /* same as 2.3 but header size is sync safe */
431 static int v2_4_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
433 int i;
435 for (i = 0; i < 4; i++) {
436 if (!is_frame_id_char(buf[i]))
437 return 0;
438 header->id[i] = buf[i];
440 if (!u32_unsync((const unsigned char *)(buf + 4), &header->size))
441 return 0;
442 header->flags = (buf[8] << 8) | buf[9];
443 if (header->size == 0)
444 return 0;
445 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
446 header->id[3], header->size);
447 return 1;
450 static int read_all(int fd, char *buf, size_t size)
452 size_t pos = 0;
454 while (pos < size) {
455 int rc = read(fd, buf + pos, size - pos);
457 if (rc == -1) {
458 if (errno == EINTR || errno == EAGAIN)
459 continue;
460 return -1;
462 pos += rc;
464 return 0;
467 static char *parse_genre(const char *str)
469 int parenthesis = 0;
470 long int idx;
471 char *end;
473 if (strncasecmp(str, "(RX", 3) == 0)
474 return xstrdup("Remix");
476 if (strncasecmp(str, "(CR", 3) == 0)
477 return xstrdup("Cover");
479 if (*str == '(') {
480 parenthesis = 1;
481 str++;
484 idx = strtol(str, &end, 10);
485 if (str != end) {
486 /* Number parsed but there may be some crap after the number.
487 * I don't care, ID3v2 by definition contains crap.
489 if (idx >= 0 && idx < NR_GENRES)
490 return xstrdup(genres[idx]);
493 if (parenthesis) {
494 const char *ptr = strchr(str, ')');
496 if (ptr && ptr[1]) {
497 /* genre name after random crap in parenthesis,
498 * return the genre name */
499 return xstrdup(ptr + 1);
501 str--;
504 /* random crap, just return it and wait for a bug report */
505 return xstrdup(str);
508 /* http://www.id3.org/id3v2.4.0-structure.txt */
509 static struct {
510 const char name[8];
511 enum id3_key key;
512 } frame_tab[] = {
513 /* 2.4.0 */
514 { "TDRC", ID3_DATE }, // recording date
515 { "TDRL", ID3_DATE }, // release date
516 { "TDOR", ID3_DATE }, // original release date
517 { "TSOP", ID3_ARTISTSORT },
519 /* >= 2.3.0 */
520 { "TPE1", ID3_ARTIST },
521 { "TALB", ID3_ALBUM },
522 { "TIT2", ID3_TITLE },
523 { "TYER", ID3_DATE },
524 { "TCON", ID3_GENRE },
525 { "TPOS", ID3_DISC },
526 { "TRCK", ID3_TRACK },
527 { "TPE2", ID3_ALBUMARTIST },
528 { "XSOP", ID3_ARTISTSORT }, // obsolete
530 /* obsolete frames (2.2.0) */
531 { "TP1", ID3_ARTIST },
532 { "TAL", ID3_ALBUM },
533 { "TT2", ID3_TITLE },
534 { "TYE", ID3_DATE },
535 { "TCO", ID3_GENRE },
536 { "TPA", ID3_DISC },
537 { "TRK", ID3_TRACK },
539 { "", -1 }
542 static int frame_tab_index(const char *id)
544 int i;
546 for (i = 0; frame_tab[i].key != -1; i++) {
547 if (!strncmp(id, frame_tab[i].name, 4))
548 return i;
550 return -1;
553 static void fix_date(char *buf)
555 const char *ptr = buf;
556 int ch, len = 0;
558 do {
559 ch = *ptr++;
560 if (ch >= '0' && ch <= '9') {
561 len++;
562 continue;
564 if (len == 4) {
565 // number which length is 4, must be year
566 memmove(buf, ptr - 5, 4);
567 buf[4] = 0;
568 return;
570 len = 0;
571 } while (ch);
572 *buf = 0;
575 static char *decode_str(const char *buf, int len, int encoding)
577 char *in, *out = NULL;
578 int rc = 0;
580 switch (encoding) {
581 case 0x00: /* ISO-8859-1 */
582 in = xstrndup(buf, len);
583 rc = utf8_encode(in, id3_default_charset, &out);
584 free(in);
585 break;
586 case 0x03: /* UTF-8 */
587 in = xstrndup(buf, len);
588 if (u_is_valid(in)) {
589 out = in;
590 } else {
591 rc = utf8_encode(in, id3_default_charset, &out);
592 free(in);
594 break;
595 case 0x01: /* UTF-16 */
596 out = utf16_to_utf8((const unsigned char *)buf, len);
597 break;
598 case 0x02: /* UTF-16BE */
599 out = utf16be_to_utf8((const unsigned char *)buf, len);
600 break;
602 return out;
605 static void v2_add_frame(struct id3tag *id3, struct v2_frame_header *fh, const char *buf)
607 int idx, encoding = *buf++, len = fh->size - 1;
608 enum id3_key key = NUM_ID3_KEYS;
609 char *out;
611 if (encoding > 3)
612 return;
614 idx = frame_tab_index(fh->id);
615 if (idx >= 0) {
616 key = frame_tab[idx].key;
617 out = decode_str(buf, len, encoding);
618 if (!out)
619 return;
621 if (key == ID3_GENRE) {
622 char *tmp;
624 id3_debug("genre before: '%s'\n", out);
625 tmp = parse_genre(out);
626 free(out);
627 out = tmp;
629 if (key == ID3_DATE) {
630 id3_debug("date before: '%s'\n", out);
631 fix_date(out);
632 if (!*out) {
633 id3_debug("date parsing failed\n");
634 free(out);
635 return;
639 id3_debug("%s '%s'\n", frame_tab[idx].name, out);
640 } else if (!strncmp(fh->id, "TXXX", 4)) {
641 int size;
643 id3_debug("TXXX\n");
645 /* TXXX<len><encoding><key><val> */
646 out = decode_str(buf, len, encoding);
647 if (!out)
648 return;
650 id3_debug("TXXX, key = '%s'\n", out);
651 if (!strcasecmp(out, "replaygain_track_gain"))
652 key = ID3_RG_TRACK_GAIN;
653 if (!strcasecmp(out, "replaygain_track_peak"))
654 key = ID3_RG_TRACK_PEAK;
655 if (!strcasecmp(out, "replaygain_album_gain"))
656 key = ID3_RG_ALBUM_GAIN;
657 if (!strcasecmp(out, "replaygain_album_peak"))
658 key = ID3_RG_ALBUM_PEAK;
659 if (!strcasecmp(out, "album artist"))
660 key = ID3_ALBUMARTIST;
661 if (!strcasecmp(out, "albumartistsort"))
662 key = ID3_ALBUMARTISTSORT;
664 size = strlen(out) + 1;
665 free(out);
667 if (key == NUM_ID3_KEYS)
668 return;
670 buf += size;
671 len -= size;
672 if (len <= 0)
673 return;
675 out = decode_str(buf, len, encoding);
676 if (!out)
677 return;
679 id3_debug("TXXX, val = '%s'\n", out);
680 } else {
681 return;
684 free(id3->v2[key]);
685 id3->v2[key] = out;
686 id3->has_v2 = 1;
689 static void unsync(unsigned char *buf, int *lenp)
691 int len = *lenp;
692 int s, d;
694 s = d = 0;
695 while (s < len - 1) {
696 if (buf[s] == 0xff && buf[s + 1] == 0x00) {
697 /* 0xff 0x00 -> 0xff */
698 buf[d++] = 0xff;
699 s += 2;
701 if (s < len - 2 && buf[s] == 0x00) {
702 /* 0xff 0x00 0x00 -> 0xff 0x00 */
703 buf[d++] = 0x00;
704 s++;
706 continue;
708 buf[d++] = buf[s++];
710 if (s < len)
711 buf[d++] = buf[s++];
713 d_print("unsyncronization removed %d bytes\n", s - d);
714 *lenp = d;
717 static int v2_read(struct id3tag *id3, int fd, const struct v2_header *header)
719 char *buf;
720 int rc, buf_size;
721 int frame_start, i;
722 int frame_header_size;
724 buf_size = header->size;
725 buf = xnew(char, buf_size);
726 rc = read_all(fd, buf, buf_size);
727 if (rc) {
728 free(buf);
729 return rc;
732 frame_start = 0;
733 if (header->flags & V2_HEADER_EXTENDED) {
734 struct v2_extended_header ext;
736 v2_extended_header_parse(&ext, buf);
737 if (ext.size > buf_size) {
738 id3_debug("extended header corrupted\n");
739 free(buf);
740 return -2;
742 frame_start = ext.size;
743 /* should check if update flag is set */
746 if (header->flags & V2_HEADER_UNSYNC) {
747 int len = buf_size - frame_start;
749 unsync((unsigned char *)(buf + frame_start), &len);
750 buf_size = len + frame_start;
753 frame_header_size = 10;
754 if (header->ver_major == 2)
755 frame_header_size = 6;
757 i = frame_start;
758 while (i < buf_size - frame_header_size) {
759 struct v2_frame_header fh;
760 int len;
762 if (header->ver_major == 2) {
763 if (!v2_2_0_frame_header_parse(&fh, buf + i))
764 break;
765 } else if (header->ver_major == 3) {
766 if (!v2_3_0_frame_header_parse(&fh, buf + i))
767 break;
768 } else {
769 /* assume v2.4 */
770 if (!v2_4_0_frame_header_parse(&fh, buf + i))
771 break;
774 i += frame_header_size;
775 if (fh.size > buf_size - i) {
776 id3_debug("frame too big\n");
777 break;
780 len = fh.size;
781 if (fh.flags & V2_FRAME_UNSYNC) {
782 int tmp = len;
784 unsync((unsigned char *)(buf + i), &tmp);
785 fh.size = tmp;
787 v2_add_frame(id3, &fh, buf + i);
788 i += len;
791 free(buf);
792 return 0;
795 int id3_tag_size(const char *buf, int buf_size)
797 struct v2_header header;
799 if (buf_size < 10)
800 return 0;
801 if (v2_header_parse(&header, buf)) {
802 if (header.flags & V2_HEADER_FOOTER) {
803 /* header + data + footer */
804 id3_debug("v2.%d.%d with footer\n", header.ver_major, header.ver_minor);
805 return 10 + header.size + 10;
807 /* header */
808 id3_debug("v2.%d.%d\n", header.ver_major, header.ver_minor);
809 return 10 + header.size;
811 if (buf_size >= 3 && is_v1(buf)) {
812 id3_debug("v1\n");
813 return 128;
815 return 0;
818 void id3_free(struct id3tag *id3)
820 int i;
822 for (i = 0; i < NUM_ID3_KEYS; i++)
823 free(id3->v2[i]);
826 int id3_read_tags(struct id3tag *id3, int fd, unsigned int flags)
828 off_t off;
829 int rc;
831 if (flags & ID3_V2) {
832 struct v2_header header;
833 char buf[138];
835 rc = read_all(fd, buf, 10);
836 if (rc)
837 goto rc_error;
838 if (v2_header_parse(&header, buf)) {
839 rc = v2_read(id3, fd, &header);
840 if (rc)
841 goto rc_error;
842 /* get v1 if needed */
843 } else {
844 /* get v2 from end and optionally v1 */
846 off = lseek(fd, -138, SEEK_END);
847 if (off == -1)
848 goto error;
849 rc = read_all(fd, buf, 138);
850 if (rc)
851 goto rc_error;
853 if (is_v1(buf + 10)) {
854 if (flags & ID3_V1) {
855 memcpy(id3->v1, buf + 10, 128);
856 id3->has_v1 = 1;
858 if (v2_footer_parse(&header, buf)) {
859 /* footer at end of file - 128 */
860 off = lseek(fd, -(header.size + 138), SEEK_END);
861 if (off == -1)
862 goto error;
863 rc = v2_read(id3, fd, &header);
864 if (rc)
865 goto rc_error;
867 } else if (v2_footer_parse(&header, buf + 128)) {
868 /* footer at end of file */
869 off = lseek(fd, -(header.size + 10), SEEK_END);
870 if (off == -1)
871 goto error;
872 rc = v2_read(id3, fd, &header);
873 if (rc)
874 goto rc_error;
876 return 0;
879 if (flags & ID3_V1) {
880 off = lseek(fd, -128, SEEK_END);
881 if (off == -1)
882 goto error;
883 rc = read_all(fd, id3->v1, 128);
884 if (rc)
885 goto rc_error;
886 id3->has_v1 = is_v1(id3->v1);
888 return 0;
889 error:
890 rc = -1;
891 rc_error:
892 return rc;
895 static char *v1_get_str(const char *buf, int len)
897 char in[32];
898 char *out;
899 int i;
901 for (i = len - 1; i >= 0; i--) {
902 if (buf[i] != 0 && buf[i] != ' ')
903 break;
905 if (i == -1)
906 return NULL;
907 memcpy(in, buf, i + 1);
908 in[i + 1] = 0;
909 if (u_is_valid(in))
910 return xstrdup(in);
911 if (utf8_encode(in, id3_default_charset, &out))
912 return NULL;
913 return out;
916 char *id3_get_comment(struct id3tag *id3, enum id3_key key)
918 if (id3->has_v2) {
919 if (id3->v2[key])
920 return xstrdup(id3->v2[key]);
922 if (id3->has_v1) {
923 switch (key) {
924 case ID3_ARTIST:
925 return v1_get_str(id3->v1 + 33, 30);
926 case ID3_ALBUM:
927 return v1_get_str(id3->v1 + 63, 30);
928 case ID3_TITLE:
929 return v1_get_str(id3->v1 + 3, 30);
930 case ID3_DATE:
931 return v1_get_str(id3->v1 + 93, 4);
932 case ID3_GENRE:
934 unsigned char idx = id3->v1[127];
936 if (idx >= NR_GENRES)
937 return NULL;
938 return xstrdup(genres[idx]);
940 case ID3_TRACK:
942 char *t;
944 if (id3->v1[125] != 0)
945 return NULL;
946 t = xnew(char, 4);
947 snprintf(t, 4, "%d", ((unsigned char *)id3->v1)[126]);
948 return t;
950 default:
951 return NULL;
954 return NULL;