mad: Simplify tag reading somewhat
[cmus.git] / id3.c
blobc1edb72f73bf2f8409a5f89e5a07da5dc3e30a65
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 "replaygain_track_gain",
234 "replaygain_track_peak",
235 "replaygain_album_gain",
236 "replaygain_album_peak"
239 static int utf16_is_special(const uchar uch)
241 if (UTF16_IS_HSURROGATE(uch) || UTF16_IS_LSURROGATE(uch) || UTF16_IS_BOM(uch))
242 return -1;
243 return 0;
246 static char *utf16_to_utf8(const unsigned char *buf, int buf_size)
248 char *out;
249 int i, idx;
251 out = xnew(char, (buf_size / 2) * 4 + 1);
252 i = idx = 0;
253 while (buf_size - i >= 2) {
254 uchar u;
256 u = buf[i] + (buf[i + 1] << 8);
257 if (u_is_unicode(u)) {
258 if (utf16_is_special(u) == 0)
259 u_set_char(out, &idx, u);
260 } else {
261 free(out);
262 return NULL;
264 if (u == 0)
265 return out;
266 i += 2;
268 u_set_char(out, &idx, 0);
269 return out;
272 static char *utf16be_to_utf8(const unsigned char *buf, int buf_size)
274 char *out;
275 int i, idx;
277 out = xnew(char, (buf_size / 2) * 4 + 1);
278 i = 0;
279 idx = 0;
280 while (buf_size - i >= 2) {
281 uchar u;
283 u = buf[i + 1] + (buf[i] << 8);
284 if (u_is_unicode(u)) {
285 if (utf16_is_special(u) == 0)
286 u_set_char(out, &idx, u);
287 } else {
288 free(out);
289 return NULL;
291 if (u == 0)
292 return out;
293 i += 2;
295 u_set_char(out, &idx, 0);
296 return out;
299 static int is_v1(const char *buf)
301 return buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G';
304 static int u32_unsync(const unsigned char *buf, uint32_t *up)
306 uint32_t b, u = 0;
307 int i;
309 for (i = 0; i < 4; i++) {
310 b = buf[i];
311 if (b >= 0x80)
312 return 0;
313 u <<= 7;
314 u |= b;
316 *up = u;
317 return 1;
320 static void get_u32(const unsigned char *buf, uint32_t *up)
322 uint32_t b, u = 0;
323 int i;
325 for (i = 0; i < 4; i++) {
326 b = buf[i];
327 u <<= 8;
328 u |= b;
330 *up = u;
333 static void get_u24(const unsigned char *buf, uint32_t *up)
335 uint32_t b, u = 0;
336 int i;
338 for (i = 0; i < 3; i++) {
339 b = buf[i];
340 u <<= 8;
341 u |= b;
343 *up = u;
346 static int v2_header_footer_parse(struct v2_header *header, const char *buf)
348 const unsigned char *b = (const unsigned char *)buf;
350 header->ver_major = b[3];
351 header->ver_minor = b[4];
352 header->flags = b[5];
353 if (header->ver_major == 0xff || header->ver_minor == 0xff)
354 return 0;
355 return u32_unsync(b + 6, &header->size);
358 static int v2_header_parse(struct v2_header *header, const char *buf)
360 if (buf[0] != 'I' || buf[1] != 'D' || buf[2] != '3')
361 return 0;
362 return v2_header_footer_parse(header, buf);
365 static int v2_footer_parse(struct v2_header *header, const char *buf)
367 if (buf[0] != '3' || buf[1] != 'D' || buf[2] != 'I')
368 return 0;
369 return v2_header_footer_parse(header, buf);
372 static int v2_extended_header_parse(struct v2_extended_header *header, const char *buf)
374 return u32_unsync((const unsigned char *)buf, &header->size);
377 static int is_frame_id_char(char ch)
379 return (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
382 /* XXXYYY
384 * X = [A-Z0-9]
385 * Y = byte
387 * XXX is frame
388 * YYY is frame size excluding this 6 byte header
390 static int v2_2_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
392 int i;
394 for (i = 0; i < 3; i++) {
395 if (!is_frame_id_char(buf[i]))
396 return 0;
397 header->id[i] = buf[i];
399 header->id[3] = 0;
400 get_u24((const unsigned char *)(buf + 3), &header->size);
401 header->flags = 0;
402 if (header->size == 0)
403 return 0;
404 id3_debug("%c%c%c %d\n", header->id[0], header->id[1], header->id[2], header->size);
405 return 1;
408 /* XXXXYYYYZZ
410 * X = [A-Z0-9]
411 * Y = byte
412 * Z = byte
414 * XXXX is frame
415 * YYYY is frame size excluding this 10 byte header
416 * ZZ is flags
418 static int v2_3_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
420 int i;
422 for (i = 0; i < 4; i++) {
423 if (!is_frame_id_char(buf[i]))
424 return 0;
425 header->id[i] = buf[i];
427 get_u32((const unsigned char *)(buf + 4), &header->size);
428 header->flags = (buf[8] << 8) | buf[9];
429 if (header->size == 0)
430 return 0;
431 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
432 header->id[3], header->size);
433 return 1;
436 /* same as 2.3 but header size is sync safe */
437 static int v2_4_0_frame_header_parse(struct v2_frame_header *header, const char *buf)
439 int i;
441 for (i = 0; i < 4; i++) {
442 if (!is_frame_id_char(buf[i]))
443 return 0;
444 header->id[i] = buf[i];
446 if (!u32_unsync((const unsigned char *)(buf + 4), &header->size))
447 return 0;
448 header->flags = (buf[8] << 8) | buf[9];
449 if (header->size == 0)
450 return 0;
451 id3_debug("%c%c%c%c %d\n", header->id[0], header->id[1], header->id[2],
452 header->id[3], header->size);
453 return 1;
456 static int read_all(int fd, char *buf, size_t size)
458 size_t pos = 0;
460 while (pos < size) {
461 int rc = read(fd, buf + pos, size - pos);
463 if (rc == -1) {
464 if (errno == EINTR || errno == EAGAIN)
465 continue;
466 return -1;
468 pos += rc;
470 return 0;
473 static char *parse_genre(const char *str)
475 int parenthesis = 0;
476 long int idx;
477 char *end;
479 if (strncasecmp(str, "(RX", 3) == 0)
480 return xstrdup("Remix");
482 if (strncasecmp(str, "(CR", 3) == 0)
483 return xstrdup("Cover");
485 if (*str == '(') {
486 parenthesis = 1;
487 str++;
490 idx = strtol(str, &end, 10);
491 if (str != end) {
492 /* Number parsed but there may be some crap after the number.
493 * I don't care, ID3v2 by definition contains crap.
495 if (idx >= 0 && idx < NR_GENRES)
496 return xstrdup(genres[idx]);
499 if (parenthesis) {
500 const char *ptr = strchr(str, ')');
502 if (ptr && ptr[1]) {
503 /* genre name after random crap in parenthesis,
504 * return the genre name */
505 return xstrdup(ptr + 1);
507 str--;
510 /* random crap, just return it and wait for a bug report */
511 return xstrdup(str);
514 /* http://www.id3.org/id3v2.4.0-structure.txt */
515 static struct {
516 const char name[8];
517 enum id3_key key;
518 } frame_tab[] = {
519 /* 2.4.0 */
520 { "TDRC", ID3_DATE }, // recording date
521 { "TDRL", ID3_DATE }, // release date
522 { "TDOR", ID3_DATE }, // original release date
524 /* >= 2.3.0 */
525 { "TPE1", ID3_ARTIST },
526 { "TALB", ID3_ALBUM },
527 { "TIT2", ID3_TITLE },
528 { "TYER", ID3_DATE },
529 { "TCON", ID3_GENRE },
530 { "TPOS", ID3_DISC },
531 { "TRCK", ID3_TRACK },
532 { "TPE2", ID3_ALBUMARTIST },
534 /* obsolete frames (2.2.0) */
535 { "TP1", ID3_ARTIST },
536 { "TAL", ID3_ALBUM },
537 { "TT2", ID3_TITLE },
538 { "TYE", ID3_DATE },
539 { "TCO", ID3_GENRE },
540 { "TPA", ID3_DISC },
541 { "TRK", ID3_TRACK },
543 { "", -1 }
546 static int frame_tab_index(const char *id)
548 int i;
550 for (i = 0; frame_tab[i].key != -1; i++) {
551 if (!strncmp(id, frame_tab[i].name, 4))
552 return i;
554 return -1;
557 static void fix_date(char *buf)
559 const char *ptr = buf;
560 int ch, len = 0;
562 do {
563 ch = *ptr++;
564 if (ch >= '0' && ch <= '9') {
565 len++;
566 continue;
568 if (len == 4) {
569 // number which length is 4, must be year
570 memmove(buf, ptr - 5, 4);
571 buf[4] = 0;
572 return;
574 len = 0;
575 } while (ch);
576 *buf = 0;
579 static char *decode_str(const char *buf, int len, int encoding)
581 char *in, *out = NULL;
582 int rc = 0;
584 switch (encoding) {
585 case 0x00: /* ISO-8859-1 */
586 in = xstrndup(buf, len);
587 rc = utf8_encode(in, id3_default_charset, &out);
588 free(in);
589 break;
590 case 0x03: /* UTF-8 */
591 in = xstrndup(buf, len);
592 if (u_is_valid(in)) {
593 out = in;
594 } else {
595 rc = utf8_encode(in, id3_default_charset, &out);
596 free(in);
598 break;
599 case 0x01: /* UTF-16 */
600 out = utf16_to_utf8((const unsigned char *)buf, len);
601 break;
602 case 0x02: /* UTF-16BE */
603 out = utf16be_to_utf8((const unsigned char *)buf, len);
604 break;
606 return out;
609 static void v2_add_frame(ID3 *id3, struct v2_frame_header *fh, const char *buf)
611 int idx, encoding = *buf++, len = fh->size - 1;
612 enum id3_key key = NUM_ID3_KEYS;
613 char *out;
615 if (encoding > 3)
616 return;
618 idx = frame_tab_index(fh->id);
619 if (idx >= 0) {
620 key = frame_tab[idx].key;
621 out = decode_str(buf, len, encoding);
622 if (!out)
623 return;
625 if (key == ID3_TRACK || key == ID3_DISC)
626 fix_track_or_disc(out);
627 if (key == ID3_GENRE) {
628 char *tmp;
630 id3_debug("genre before: '%s'\n", out);
631 tmp = parse_genre(out);
632 free(out);
633 out = tmp;
635 if (key == ID3_DATE) {
636 id3_debug("date before: '%s'\n", out);
637 fix_date(out);
638 if (!*out) {
639 id3_debug("date parsing failed\n");
640 free(out);
641 return;
645 id3_debug("%s '%s'\n", frame_tab[idx].name, out);
646 } else if (!strncmp(fh->id, "TXXX", 4)) {
647 int size;
649 id3_debug("TXXX\n");
651 /* TXXX<len><encoding><key><val> */
652 out = decode_str(buf, len, encoding);
653 if (!out)
654 return;
656 id3_debug("TXXX, key = '%s'\n", out);
657 if (!strcasecmp(out, "replaygain_track_gain"))
658 key = ID3_RG_TRACK_GAIN;
659 if (!strcasecmp(out, "replaygain_track_peak"))
660 key = ID3_RG_TRACK_PEAK;
661 if (!strcasecmp(out, "replaygain_album_gain"))
662 key = ID3_RG_ALBUM_GAIN;
663 if (!strcasecmp(out, "replaygain_album_peak"))
664 key = ID3_RG_ALBUM_PEAK;
666 size = strlen(out) + 1;
667 free(out);
669 if (key == NUM_ID3_KEYS)
670 return;
672 buf += size;
673 len -= size;
674 if (len <= 0)
675 return;
677 out = decode_str(buf, len, encoding);
678 if (!out)
679 return;
681 id3_debug("TXXX, val = '%s'\n", out);
682 } else {
683 return;
686 free(id3->v2[key]);
687 id3->v2[key] = out;
688 id3->has_v2 = 1;
691 static void unsync(unsigned char *buf, int *lenp)
693 int len = *lenp;
694 int s, d;
696 s = d = 0;
697 while (s < len - 1) {
698 if (buf[s] == 0xff && buf[s + 1] == 0x00) {
699 /* 0xff 0x00 -> 0xff */
700 buf[d++] = 0xff;
701 s += 2;
703 if (s < len - 2 && buf[s] == 0x00) {
704 /* 0xff 0x00 0x00 -> 0xff 0x00 */
705 buf[d++] = 0x00;
706 s++;
708 continue;
710 buf[d++] = buf[s++];
712 if (s < len)
713 buf[d++] = buf[s++];
715 d_print("unsyncronization removed %d bytes\n", s - d);
716 *lenp = d;
719 static int v2_read(ID3 *id3, int fd, const struct v2_header *header)
721 char *buf;
722 int rc, buf_size;
723 int frame_start, i;
724 int frame_header_size;
726 buf_size = header->size;
727 buf = xnew(char, buf_size);
728 rc = read_all(fd, buf, buf_size);
729 if (rc) {
730 free(buf);
731 return rc;
734 frame_start = 0;
735 if (header->flags & V2_HEADER_EXTENDED) {
736 struct v2_extended_header ext;
738 v2_extended_header_parse(&ext, buf);
739 if (ext.size > buf_size) {
740 id3_debug("extended header corrupted\n");
741 free(buf);
742 return -2;
744 frame_start = ext.size;
745 /* should check if update flag is set */
748 if (header->flags & V2_HEADER_UNSYNC) {
749 int len = buf_size - frame_start;
751 unsync((unsigned char *)(buf + frame_start), &len);
752 buf_size = len + frame_start;
755 frame_header_size = 10;
756 if (header->ver_major == 2)
757 frame_header_size = 6;
759 i = frame_start;
760 while (i < buf_size - frame_header_size) {
761 struct v2_frame_header fh;
762 int len;
764 if (header->ver_major == 2) {
765 if (!v2_2_0_frame_header_parse(&fh, buf + i))
766 break;
767 } else if (header->ver_major == 3) {
768 if (!v2_3_0_frame_header_parse(&fh, buf + i))
769 break;
770 } else {
771 /* assume v2.4 */
772 if (!v2_4_0_frame_header_parse(&fh, buf + i))
773 break;
776 i += frame_header_size;
777 if (fh.size > buf_size - i) {
778 id3_debug("frame too big\n");
779 break;
782 len = fh.size;
783 if (fh.flags & V2_FRAME_UNSYNC) {
784 int tmp = len;
786 unsync((unsigned char *)(buf + i), &tmp);
787 fh.size = tmp;
789 v2_add_frame(id3, &fh, buf + i);
790 i += len;
793 free(buf);
794 return 0;
797 int id3_tag_size(const char *buf, int buf_size)
799 struct v2_header header;
801 if (buf_size < 10)
802 return 0;
803 if (v2_header_parse(&header, buf)) {
804 if (header.flags & V2_HEADER_FOOTER) {
805 /* header + data + footer */
806 id3_debug("v2.%d.%d with footer\n", header.ver_major, header.ver_minor);
807 return 10 + header.size + 10;
809 /* header */
810 id3_debug("v2.%d.%d\n", header.ver_major, header.ver_minor);
811 return 10 + header.size;
813 if (buf_size >= 3 && is_v1(buf)) {
814 id3_debug("v1\n");
815 return 128;
817 return 0;
820 ID3 *id3_new(void)
822 return xnew0(ID3, 1);
825 void id3_free(ID3 *id3)
827 int i;
829 for (i = 0; i < NUM_ID3_KEYS; i++)
830 free(id3->v2[i]);
831 free(id3);
834 int id3_read_tags(ID3 *id3, int fd, unsigned int flags)
836 off_t off;
837 int rc;
839 if (flags & ID3_V2) {
840 struct v2_header header;
841 char buf[138];
843 rc = read_all(fd, buf, 10);
844 if (rc)
845 goto rc_error;
846 if (v2_header_parse(&header, buf)) {
847 rc = v2_read(id3, fd, &header);
848 if (rc)
849 goto rc_error;
850 /* get v1 if needed */
851 } else {
852 /* get v2 from end and optionally v1 */
854 off = lseek(fd, -138, SEEK_END);
855 if (off == -1)
856 goto error;
857 rc = read_all(fd, buf, 138);
858 if (rc)
859 goto rc_error;
861 if (is_v1(buf + 10)) {
862 if (flags & ID3_V1) {
863 memcpy(id3->v1, buf + 10, 128);
864 id3->has_v1 = 1;
866 if (v2_footer_parse(&header, buf)) {
867 /* footer at end of file - 128 */
868 off = lseek(fd, -(header.size + 138), SEEK_END);
869 if (off == -1)
870 goto error;
871 rc = v2_read(id3, fd, &header);
872 if (rc)
873 goto rc_error;
875 } else if (v2_footer_parse(&header, buf + 128)) {
876 /* footer at end of file */
877 off = lseek(fd, -(header.size + 10), SEEK_END);
878 if (off == -1)
879 goto error;
880 rc = v2_read(id3, fd, &header);
881 if (rc)
882 goto rc_error;
884 return 0;
887 if (flags & ID3_V1) {
888 off = lseek(fd, -128, SEEK_END);
889 if (off == -1)
890 goto error;
891 rc = read_all(fd, id3->v1, 128);
892 if (rc)
893 goto rc_error;
894 id3->has_v1 = is_v1(id3->v1);
896 return 0;
897 error:
898 rc = -1;
899 rc_error:
900 return rc;
903 static char *v1_get_str(const char *buf, int len)
905 char in[32];
906 char *out;
907 int i;
909 for (i = len - 1; i >= 0; i--) {
910 if (buf[i] != 0 && buf[i] != ' ')
911 break;
913 if (i == -1)
914 return NULL;
915 memcpy(in, buf, i + 1);
916 in[i + 1] = 0;
917 if (u_is_valid(in))
918 return xstrdup(in);
919 if (utf8_encode(in, id3_default_charset, &out))
920 return NULL;
921 return out;
924 char *id3_get_comment(ID3 *id3, enum id3_key key)
926 if (id3->has_v2) {
927 if (id3->v2[key])
928 return xstrdup(id3->v2[key]);
930 if (id3->has_v1) {
931 switch (key) {
932 case ID3_ARTIST:
933 return v1_get_str(id3->v1 + 33, 30);
934 case ID3_ALBUM:
935 return v1_get_str(id3->v1 + 63, 30);
936 case ID3_TITLE:
937 return v1_get_str(id3->v1 + 3, 30);
938 case ID3_DATE:
939 return v1_get_str(id3->v1 + 93, 4);
940 case ID3_GENRE:
942 unsigned char idx = id3->v1[127];
944 if (idx >= NR_GENRES)
945 return NULL;
946 return xstrdup(genres[idx]);
948 case ID3_TRACK:
950 char *t;
952 if (id3->v1[125] != 0)
953 return NULL;
954 t = xnew(char, 4);
955 snprintf(t, 4, "%d", ((unsigned char *)id3->v1)[126]);
956 return t;
958 default:
959 return NULL;
962 return NULL;