52struct id3_tag *id3_tag_new(
void)
56 tag = malloc(
sizeof(*tag));
59 tag->version = ID3_TAG_VERSION;
61 tag->extendedflags = 0;
62 tag->restrictions = 0;
64 ID3_TAG_OPTION_COMPRESSION | ID3_TAG_OPTION_CRC;
77void id3_tag_delete(
struct id3_tag *tag)
81 if (tag->refcount == 0) {
82 id3_tag_clearframes(tag);
95void id3_tag_addref(
struct id3_tag *tag)
106void id3_tag_delref(
struct id3_tag *tag)
108 assert(tag && tag->refcount > 0);
117unsigned int id3_tag_version(
struct id3_tag const *tag)
128int id3_tag_options(
struct id3_tag *tag,
int mask,
int values)
133 tag->options = (tag->options & ~mask) | (values & mask);
142void id3_tag_setlength(
struct id3_tag *tag, id3_length_t length)
146 tag->paddedsize = length;
153void id3_tag_clearframes(
struct id3_tag *tag)
159 for (i = 0; i < tag->nframes; ++i) {
160 id3_frame_delref(tag->frames[i]);
161 id3_frame_delete(tag->frames[i]);
175 assert(tag && frame);
177 frames = realloc(tag->frames, (tag->nframes + 1) *
sizeof(*frames));
181 tag->frames = frames;
182 tag->frames[tag->nframes++] = frame;
184 id3_frame_addref(frame);
197 assert(tag && frame);
199 for (i = 0; i < tag->nframes; ++i) {
200 if (tag->frames[i] == frame)
204 if (i == tag->nframes)
208 while (i++ < tag->nframes)
209 tag->frames[i - 1] = tag->frames[i];
211 id3_frame_delref(frame);
221 char const *
id,
unsigned int index)
227 if (
id == 0 || *
id == 0)
228 return (index < tag->nframes) ? tag->frames[index] : 0;
235 compat = id3_compat_lookup(
id, len);
236 if (compat && compat->equiv && !compat->translate) {
242 for (i = 0; i < tag->nframes; ++i) {
243 if (strncmp(tag->frames[i]->id,
id, len) == 0 && index-- == 0)
244 return tag->frames[i];
258enum tagtype tagtype(id3_byte_t
const *data, id3_length_t length)
261 data[0] ==
'T' && data[1] ==
'A' && data[2] ==
'G')
262 return TAGTYPE_ID3V1;
265 ((data[0] ==
'I' && data[1] ==
'D' && data[2] ==
'3') ||
266 (data[0] ==
'3' && data[1] ==
'D' && data[2] ==
'I')) &&
267 data[3] < 0xff && data[4] < 0xff &&
268 data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
269 return data[0] ==
'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER;
275void parse_header(id3_byte_t
const **ptr,
276 unsigned int *version,
int *flags, id3_length_t *size)
280 *version = id3_parse_uint(ptr, 2);
281 *flags = id3_parse_uint(ptr, 1);
282 *size = id3_parse_syncsafe(ptr, 4);
289signed long id3_tag_query(id3_byte_t
const *data, id3_length_t length)
291 unsigned int version;
297 switch (tagtype(data, length)) {
302 parse_header(&data, &version, &flags, &size);
304 if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
309 case TAGTYPE_ID3V2_FOOTER:
310 parse_header(&data, &version, &flags, &size);
325 ptr = str + strlen(str);
326 while (ptr > str && ptr[-1] ==
' ')
333int v1_attachstr(
struct id3_tag *tag,
char const *
id,
334 char *text,
unsigned long number)
345 frame = id3_frame_new(
id);
349 if (id3_field_settextencoding(&frame->fields[0],
350 ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
354 id3_latin1_decode((
const id3_latin1_t *)text, ucs4);
356 id3_ucs4_putnumber(ucs4, number);
358 if (strcmp(
id, ID3_FRAME_COMMENT) == 0) {
359 if (id3_field_setlanguage(&frame->fields[1],
"XXX") == -1 ||
360 id3_field_setstring(&frame->fields[2], id3_ucs4_empty) == -1 ||
361 id3_field_setfullstring(&frame->fields[3], ucs4) == -1)
365 id3_ucs4_t *ptr = ucs4;
367 if (id3_field_setstrings(&frame->fields[1], 1, &ptr) == -1)
371 if (id3_tag_attachframe(tag, frame) == -1)
377 id3_frame_delete(frame);
382struct id3_tag *v1_parse(id3_byte_t
const *data)
388 char title[31], artist[31], album[31], year[5], comment[31];
389 unsigned int genre, track;
391 tag->version = 0x0100;
393 tag->options |= ID3_TAG_OPTION_ID3V1;
394 tag->options &= ~ID3_TAG_OPTION_COMPRESSION;
397 ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 |
398 ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS;
400 title[30] = artist[30] = album[30] = year[4] = comment[30] = 0;
402 memcpy(title, &data[3], 30);
403 memcpy(artist, &data[33], 30);
404 memcpy(album, &data[63], 30);
405 memcpy(year, &data[93], 4);
406 memcpy(comment, &data[97], 30);
411 if (comment[28] == 0 && comment[29] != 0) {
413 tag->version = 0x0101;
418 if (v1_attachstr(tag, ID3_FRAME_TITLE, title, 0) == -1 ||
419 v1_attachstr(tag, ID3_FRAME_ARTIST, artist, 0) == -1 ||
420 v1_attachstr(tag, ID3_FRAME_ALBUM, album, 0) == -1 ||
421 v1_attachstr(tag, ID3_FRAME_YEAR, year, 0) == -1 ||
422 (track && v1_attachstr(tag, ID3_FRAME_TRACK, 0, track) == -1) ||
423 (genre < 0xff && v1_attachstr(tag, ID3_FRAME_GENRE, 0, genre) == -1) ||
424 v1_attachstr(tag, ID3_FRAME_COMMENT, comment, 0) == -1) {
434struct id3_tag *v2_parse(id3_byte_t
const *ptr)
441 id3_byte_t
const *end;
444 parse_header(&ptr, &tag->version, &tag->flags, &size);
446 tag->paddedsize = 10 + size;
448 if ((tag->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) &&
449 ID3_TAG_VERSION_MAJOR(tag->version) < 4) {
454 memcpy(mem, ptr, size);
456 size = id3_util_deunsynchronise(mem, size);
462 if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
463 switch (ID3_TAG_VERSION_MAJOR(tag->version)) {
469 id3_byte_t
const *ehptr, *ehend;
479 ehsize = id3_parse_uint(&ptr, 4);
481 if (ehsize > end - ptr)
485 ehend = ptr + ehsize;
489 if (ehend - ehptr >= 6) {
491 id3_length_t padsize;
493 ehflags = id3_parse_uint(&ehptr, 2);
494 padsize = id3_parse_uint(&ehptr, 4);
496 if (padsize > end - ptr)
501 if (ehflags & EH_FLAG_CRC) {
504 if (ehend - ehptr < 4)
507 crc = id3_parse_uint(&ehptr, 4);
509 if (crc != id3_crc_compute(ptr, end - ptr))
512 tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
520 id3_byte_t
const *ehptr, *ehend;
528 ehsize = id3_parse_syncsafe(&ptr, 4);
530 if (ehsize < 6 || ehsize > end - ehptr)
533 ehend = ehptr + ehsize;
535 bytes = id3_parse_uint(&ptr, 1);
537 if (bytes < 1 || bytes > ehend - ptr)
544 id3_byte_t
const *flagsptr = ptr, *dataptr = ehptr;
545 unsigned int datalen;
549 for (ehflags = id3_parse_uint(&flagsptr, 1); ehflags;
550 ehflags = (ehflags << 1) & 0xff) {
551 if (ehflags & 0x80) {
552 if (dataptr == ehend)
554 datalen = id3_parse_uint(&dataptr, 1);
555 if (datalen > 0x7f || datalen > ehend - dataptr)
563 tag->extendedflags = id3_parse_uint(&ptr, 1);
567 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) {
568 bytes = id3_parse_uint(&ehptr, 1);
572 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
575 bytes = id3_parse_uint(&ehptr, 1);
579 crc = id3_parse_syncsafe(&ehptr, 5);
582 if (crc != id3_crc_compute(ptr, end - ptr))
586 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
587 bytes = id3_parse_uint(&ehptr, 1);
591 tag->restrictions = id3_parse_uint(&ehptr, 1);
607 frame = id3_frame_parse(&ptr, end - ptr, tag->version);
608 if (frame == 0 || id3_tag_attachframe(tag, frame) == -1)
612 if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 &&
613 id3_compat_fixup(tag) == -1)
633struct id3_tag *id3_tag_parse(id3_byte_t
const *data, id3_length_t length)
635 id3_byte_t
const *ptr;
636 unsigned int version;
642 switch (tagtype(data, length)) {
644 return (length < 128) ? 0 : v1_parse(data);
649 case TAGTYPE_ID3V2_FOOTER:
657 parse_header(&ptr, &version, &flags, &size);
659 switch (ID3_TAG_VERSION_MAJOR(version)) {
661 if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
665 return (length < 10 + size) ? 0 : v2_parse(data);
672void v1_renderstr(
struct id3_tag const *tag,
char const *frameid,
673 id3_byte_t **buffer, id3_length_t length)
676 id3_ucs4_t
const *string;
678 frame = id3_tag_findframe(tag, frameid, 0);
680 string = id3_ucs4_empty;
682 if (strcmp(frameid, ID3_FRAME_COMMENT) == 0)
683 string = id3_field_getfullstring(&frame->fields[3]);
685 string = id3_field_getstrings(&frame->fields[1], 0);
688 id3_render_paddedstring(buffer,
string, length);
696id3_length_t v1_render(
struct id3_tag const *tag, id3_byte_t *buffer)
698 id3_byte_t data[128], *ptr;
705 id3_render_immediate(&ptr,
"TAG", 3);
707 v1_renderstr(tag, ID3_FRAME_TITLE, &ptr, 30);
708 v1_renderstr(tag, ID3_FRAME_ARTIST, &ptr, 30);
709 v1_renderstr(tag, ID3_FRAME_ALBUM, &ptr, 30);
710 v1_renderstr(tag, ID3_FRAME_YEAR, &ptr, 4);
711 v1_renderstr(tag, ID3_FRAME_COMMENT, &ptr, 30);
715 frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0);
719 track = id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0));
720 if (track > 0 && track <= 0xff) {
728 frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0);
730 unsigned int nstrings;
732 nstrings = id3_field_getnstrings(&frame->fields[1]);
734 for (i = 0; i < nstrings; ++i) {
735 genre = id3_genre_number(id3_field_getstrings(&frame->fields[1], i));
740 if (i == nstrings && nstrings > 0)
741 genre = ID3_GENRE_OTHER;
744 id3_render_int(&ptr, genre, 1);
749 for (i = 3; i < 127; ++i) {
759 memcpy(buffer, data, 128);
768id3_length_t id3_tag_render(
struct id3_tag const *tag, id3_byte_t *buffer)
770 id3_length_t size = 0;
772 *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0;
773 int flags, extendedflags;
778 if (tag->options & ID3_TAG_OPTION_ID3V1)
779 return v1_render(tag, buffer);
783 for (i = 0; i < tag->nframes; ++i) {
784 if (id3_frame_render(tag->frames[i], 0, 0) > 0)
788 if (i == tag->nframes)
791 ptr = buffer ? &buffer : 0;
795 flags = tag->flags & ID3_TAG_FLAG_KNOWNFLAGS;
796 extendedflags = tag->extendedflags & ID3_TAG_EXTENDEDFLAG_KNOWNFLAGS;
798 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
799 if (tag->options & ID3_TAG_OPTION_CRC)
800 extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
802 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
803 if (tag->restrictions)
804 extendedflags |= ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
806 flags &= ~ID3_TAG_FLAG_UNSYNCHRONISATION;
807 if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION)
808 flags |= ID3_TAG_FLAG_UNSYNCHRONISATION;
810 flags &= ~ID3_TAG_FLAG_EXTENDEDHEADER;
812 flags |= ID3_TAG_FLAG_EXTENDEDHEADER;
814 flags &= ~ID3_TAG_FLAG_FOOTERPRESENT;
815 if (tag->options & ID3_TAG_OPTION_APPENDEDTAG)
816 flags |= ID3_TAG_FLAG_FOOTERPRESENT;
823 size += id3_render_immediate(ptr,
"ID3", 3);
824 size += id3_render_int(ptr, ID3_TAG_VERSION, 2);
825 size += id3_render_int(ptr, flags, 1);
830 size += id3_render_syncsafe(ptr, 0, 4);
834 if (flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
835 id3_length_t ehsize = 0;
836 id3_byte_t *ehsize_ptr = 0;
841 ehsize += id3_render_syncsafe(ptr, 0, 4);
842 ehsize += id3_render_int(ptr, 1, 1);
843 ehsize += id3_render_int(ptr, extendedflags, 1);
845 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)
846 ehsize += id3_render_int(ptr, 0, 1);
848 if (extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
849 ehsize += id3_render_int(ptr, 5, 1);
854 ehsize += id3_render_syncsafe(ptr, 0, 5);
857 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
858 ehsize += id3_render_int(ptr, 1, 1);
859 ehsize += id3_render_int(ptr, tag->restrictions, 1);
863 id3_render_syncsafe(&ehsize_ptr, ehsize, 4);
873 for (i = 0; i < tag->nframes; ++i)
874 size += id3_frame_render(tag->frames[i], ptr, tag->options);
878 if (!(flags & ID3_TAG_FLAG_FOOTERPRESENT)) {
879 if (size < tag->paddedsize)
880 size += id3_render_padding(ptr, 0, tag->paddedsize - size);
881 else if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION) {
885 if ((*ptr)[-1] == 0xff)
886 size += id3_render_padding(ptr, 0, 1);
894 id3_render_syncsafe(&tagsize_ptr, size - 10, 4);
897 id3_render_syncsafe(&crc_ptr,
898 id3_crc_compute(frames_ptr, *ptr - frames_ptr), 5);
903 if (flags & ID3_TAG_FLAG_FOOTERPRESENT) {
904 size += id3_render_immediate(ptr,
"3DI", 3);
905 size += id3_render_binary(ptr, header_ptr + 3, 7);