2 * libid3tag - ID3 tag manipulation library
3 * Copyright (C) 2000-2004 Underbit Technologies, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * $Id: tag.c,v 1.20 2004/02/17 02:04:10 rob Exp $
50 * DESCRIPTION: allocate and return a new, empty tag
52 struct 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;
63 tag
->options
= /* ID3_TAG_OPTION_UNSYNCHRONISATION | */
64 ID3_TAG_OPTION_COMPRESSION
| ID3_TAG_OPTION_CRC
;
75 * DESCRIPTION: destroy a tag and deallocate all associated memory
77 void id3_tag_delete(struct id3_tag
*tag
)
81 if (tag
->refcount
== 0) {
82 id3_tag_clearframes(tag
);
93 * DESCRIPTION: add an external reference to a tag
95 void id3_tag_addref(struct id3_tag
*tag
)
103 * NAME: tag->delref()
104 * DESCRIPTION: remove an external reference to a tag
106 void id3_tag_delref(struct id3_tag
*tag
)
108 assert(tag
&& tag
->refcount
> 0);
114 * NAME: tag->version()
115 * DESCRIPTION: return the tag's original ID3 version number
117 unsigned int id3_tag_version(struct id3_tag
const *tag
)
125 * NAME: tag->options()
126 * DESCRIPTION: get or set tag options
128 int id3_tag_options(struct id3_tag
*tag
, int mask
, int values
)
133 tag
->options
= (tag
->options
& ~mask
) | (values
& mask
);
139 * NAME: tag->setlength()
140 * DESCRIPTION: set the minimum rendered tag size
142 void id3_tag_setlength(struct id3_tag
*tag
, id3_length_t length
)
146 tag
->paddedsize
= length
;
150 * NAME: tag->clearframes()
151 * DESCRIPTION: detach and delete all frames associated with a tag
153 void 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
]);
168 * NAME: tag->attachframe()
169 * DESCRIPTION: attach a frame to a tag
171 int id3_tag_attachframe(struct id3_tag
*tag
, struct id3_frame
*frame
)
173 struct id3_frame
**frames
;
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
);
190 * NAME: tag->detachframe()
191 * DESCRIPTION: detach (but don't delete) a frame from a tag
193 int id3_tag_detachframe(struct id3_tag
*tag
, struct id3_frame
*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
);
217 * NAME: tag->findframe()
218 * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID
220 struct id3_frame
*id3_tag_findframe(struct id3_tag
const *tag
,
221 char const *id
, unsigned int index
)
227 if (id
== 0 || *id
== 0)
228 return (index
< tag
->nframes
) ? tag
->frames
[index
] : 0;
233 struct id3_compat
const *compat
;
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
];
258 enum 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
;
275 void 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);
287 * DESCRIPTION: if a tag begins at the given location, return its size
289 signed 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] == ' ')
333 int v1_attachstr(struct id3_tag
*tag
, char const *id
,
334 char *text
, unsigned long number
)
336 struct id3_frame
*frame
;
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(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
);
382 struct 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;
416 /* populate tag frames */
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) {
434 struct 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
;
473 EH_FLAG_CRC
= 0x8000 /* CRC data present */
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
)
542 /* verify extended header size */
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);
602 struct id3_frame
*frame
;
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)
631 * DESCRIPTION: parse a complete ID3 tag
633 struct 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
);
672 void v1_renderstr(struct id3_tag
const *tag
, char const *frameid
,
673 id3_byte_t
**buffer
, id3_length_t length
)
675 struct id3_frame
*frame
;
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
);
693 * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag
696 id3_length_t
v1_render(struct id3_tag
const *tag
, id3_byte_t
*buffer
)
698 id3_byte_t data
[128], *ptr
;
699 struct id3_frame
*frame
;
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);
713 /* ID3v1.1 track number */
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) {
726 /* ID3v1 genre number */
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);
746 /* make sure the tag is not empty */
749 for (i
= 3; i
< 127; ++i
) {
759 memcpy(buffer
, data
, 128);
765 * NAME: tag->render()
766 * DESCRIPTION: render a complete ID3 tag
768 id3_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
);
781 /* a tag must contain at least one (renderable) frame */
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);
832 /* extended header */
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);
891 /* patch tag size and CRC */
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);