Updates to Tomato RAF including NGINX && PHP
[tomato.git] / release / src / router / libid3tag / tag.c
blobbe4e8e777e2ea81ad409fa0c93b662c4a5c121ac
1 /*
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 $
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
26 # include "global.h"
28 # include <string.h>
29 # include <stdlib.h>
31 # ifdef HAVE_ASSERT_H
32 # include <assert.h>
33 # endif
35 # include "id3tag.h"
36 # include "tag.h"
37 # include "frame.h"
38 # include "compat.h"
39 # include "parse.h"
40 # include "render.h"
41 # include "latin1.h"
42 # include "ucs4.h"
43 # include "genre.h"
44 # include "crc.h"
45 # include "field.h"
46 # include "util.h"
49 * NAME: tag->new()
50 * DESCRIPTION: allocate and return a new, empty tag
52 struct id3_tag *id3_tag_new(void)
54 struct id3_tag *tag;
56 tag = malloc(sizeof(*tag));
57 if (tag) {
58 tag->refcount = 0;
59 tag->version = ID3_TAG_VERSION;
60 tag->flags = 0;
61 tag->extendedflags = 0;
62 tag->restrictions = 0;
63 tag->options = /* ID3_TAG_OPTION_UNSYNCHRONISATION | */
64 ID3_TAG_OPTION_COMPRESSION | ID3_TAG_OPTION_CRC;
65 tag->nframes = 0;
66 tag->frames = 0;
67 tag->paddedsize = 0;
70 return tag;
74 * NAME: tag->delete()
75 * DESCRIPTION: destroy a tag and deallocate all associated memory
77 void id3_tag_delete(struct id3_tag *tag)
79 assert(tag);
81 if (tag->refcount == 0) {
82 id3_tag_clearframes(tag);
84 if (tag->frames)
85 free(tag->frames);
87 free(tag);
92 * NAME: tag->addref()
93 * DESCRIPTION: add an external reference to a tag
95 void id3_tag_addref(struct id3_tag *tag)
97 assert(tag);
99 ++tag->refcount;
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);
110 --tag->refcount;
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)
119 assert(tag);
121 return tag->version;
125 * NAME: tag->options()
126 * DESCRIPTION: get or set tag options
128 int id3_tag_options(struct id3_tag *tag, int mask, int values)
130 assert(tag);
132 if (mask)
133 tag->options = (tag->options & ~mask) | (values & mask);
135 return tag->options;
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)
144 assert(tag);
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)
155 unsigned int i;
157 assert(tag);
159 for (i = 0; i < tag->nframes; ++i) {
160 id3_frame_delref(tag->frames[i]);
161 id3_frame_delete(tag->frames[i]);
164 tag->nframes = 0;
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));
178 if (frames == 0)
179 return -1;
181 tag->frames = frames;
182 tag->frames[tag->nframes++] = frame;
184 id3_frame_addref(frame);
186 return 0;
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)
195 unsigned int i;
197 assert(tag && frame);
199 for (i = 0; i < tag->nframes; ++i) {
200 if (tag->frames[i] == frame)
201 break;
204 if (i == tag->nframes)
205 return -1;
207 --tag->nframes;
208 while (i++ < tag->nframes)
209 tag->frames[i - 1] = tag->frames[i];
211 id3_frame_delref(frame);
213 return 0;
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)
223 unsigned int len, i;
225 assert(tag);
227 if (id == 0 || *id == 0)
228 return (index < tag->nframes) ? tag->frames[index] : 0;
230 len = strlen(id);
232 if (len == 4) {
233 struct id3_compat const *compat;
235 compat = id3_compat_lookup(id, len);
236 if (compat && compat->equiv && !compat->translate) {
237 id = compat->equiv;
238 len = strlen(id);
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];
247 return 0;
250 enum tagtype {
251 TAGTYPE_NONE = 0,
252 TAGTYPE_ID3V1,
253 TAGTYPE_ID3V2,
254 TAGTYPE_ID3V2_FOOTER
257 static
258 enum tagtype tagtype(id3_byte_t const *data, id3_length_t length)
260 if (length >= 3 &&
261 data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
262 return TAGTYPE_ID3V1;
264 if (length >= 10 &&
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;
271 return TAGTYPE_NONE;
274 static
275 void parse_header(id3_byte_t const **ptr,
276 unsigned int *version, int *flags, id3_length_t *size)
278 *ptr += 3;
280 *version = id3_parse_uint(ptr, 2);
281 *flags = id3_parse_uint(ptr, 1);
282 *size = id3_parse_syncsafe(ptr, 4);
286 * NAME: tag->query()
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;
292 int flags;
293 id3_length_t size;
295 assert(data);
297 switch (tagtype(data, length)) {
298 case TAGTYPE_ID3V1:
299 return 128;
301 case TAGTYPE_ID3V2:
302 parse_header(&data, &version, &flags, &size);
304 if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
305 size += 10;
307 return 10 + size;
309 case TAGTYPE_ID3V2_FOOTER:
310 parse_header(&data, &version, &flags, &size);
311 return -size - 10;
313 case TAGTYPE_NONE:
314 break;
317 return 0;
320 static
321 void trim(char *str)
323 char *ptr;
325 ptr = str + strlen(str);
326 while (ptr > str && ptr[-1] == ' ')
327 --ptr;
329 *ptr = 0;
332 static
333 int v1_attachstr(struct id3_tag *tag, char const *id,
334 char *text, unsigned long number)
336 struct id3_frame *frame;
337 id3_ucs4_t ucs4[31];
339 if (text) {
340 trim(text);
341 if (*text == 0)
342 return 0;
345 frame = id3_frame_new(id);
346 if (frame == 0)
347 return -1;
349 if (id3_field_settextencoding(&frame->fields[0],
350 ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
351 goto fail;
353 if (text)
354 id3_latin1_decode(text, ucs4);
355 else
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)
362 goto fail;
364 else {
365 id3_ucs4_t *ptr = ucs4;
367 if (id3_field_setstrings(&frame->fields[1], 1, &ptr) == -1)
368 goto fail;
371 if (id3_tag_attachframe(tag, frame) == -1)
372 goto fail;
374 return 0;
376 fail:
377 id3_frame_delete(frame);
378 return -1;
381 static
382 struct id3_tag *v1_parse(id3_byte_t const *data)
384 struct id3_tag *tag;
386 tag = id3_tag_new();
387 if (tag) {
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;
396 tag->restrictions =
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);
408 genre = data[127];
410 track = 0;
411 if (comment[28] == 0 && comment[29] != 0) {
412 track = comment[29];
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) {
425 id3_tag_delete(tag);
426 tag = 0;
430 return tag;
433 static
434 struct id3_tag *v2_parse(id3_byte_t const *ptr)
436 struct id3_tag *tag;
437 id3_byte_t *mem = 0;
439 tag = id3_tag_new();
440 if (tag) {
441 id3_byte_t const *end;
442 id3_length_t size;
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) {
450 mem = malloc(size);
451 if (mem == 0)
452 goto fail;
454 memcpy(mem, ptr, size);
456 size = id3_util_deunsynchronise(mem, size);
457 ptr = mem;
460 end = ptr + size;
462 if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
463 switch (ID3_TAG_VERSION_MAJOR(tag->version)) {
464 case 2:
465 goto fail;
467 case 3:
469 id3_byte_t const *ehptr, *ehend;
470 id3_length_t ehsize;
472 enum {
473 EH_FLAG_CRC = 0x8000 /* CRC data present */
476 if (end - ptr < 4)
477 goto fail;
479 ehsize = id3_parse_uint(&ptr, 4);
481 if (ehsize > end - ptr)
482 goto fail;
484 ehptr = ptr;
485 ehend = ptr + ehsize;
487 ptr = ehend;
489 if (ehend - ehptr >= 6) {
490 int ehflags;
491 id3_length_t padsize;
493 ehflags = id3_parse_uint(&ehptr, 2);
494 padsize = id3_parse_uint(&ehptr, 4);
496 if (padsize > end - ptr)
497 goto fail;
499 end -= padsize;
501 if (ehflags & EH_FLAG_CRC) {
502 unsigned long crc;
504 if (ehend - ehptr < 4)
505 goto fail;
507 crc = id3_parse_uint(&ehptr, 4);
509 if (crc != id3_crc_compute(ptr, end - ptr))
510 goto fail;
512 tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
516 break;
518 case 4:
520 id3_byte_t const *ehptr, *ehend;
521 id3_length_t ehsize;
522 unsigned int bytes;
524 if (end - ptr < 4)
525 goto fail;
527 ehptr = ptr;
528 ehsize = id3_parse_syncsafe(&ptr, 4);
530 if (ehsize < 6 || ehsize > end - ehptr)
531 goto fail;
533 ehend = ehptr + ehsize;
535 bytes = id3_parse_uint(&ptr, 1);
537 if (bytes < 1 || bytes > ehend - ptr)
538 goto fail;
540 ehptr = ptr + bytes;
542 /* verify extended header size */
544 id3_byte_t const *flagsptr = ptr, *dataptr = ehptr;
545 unsigned int datalen;
546 int ehflags;
548 while (bytes--) {
549 for (ehflags = id3_parse_uint(&flagsptr, 1); ehflags;
550 ehflags = (ehflags << 1) & 0xff) {
551 if (ehflags & 0x80) {
552 if (dataptr == ehend)
553 goto fail;
554 datalen = id3_parse_uint(&dataptr, 1);
555 if (datalen > 0x7f || datalen > ehend - dataptr)
556 goto fail;
557 dataptr += datalen;
563 tag->extendedflags = id3_parse_uint(&ptr, 1);
565 ptr = ehend;
567 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) {
568 bytes = id3_parse_uint(&ehptr, 1);
569 ehptr += bytes;
572 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
573 unsigned long crc;
575 bytes = id3_parse_uint(&ehptr, 1);
576 if (bytes < 5)
577 goto fail;
579 crc = id3_parse_syncsafe(&ehptr, 5);
580 ehptr += bytes - 5;
582 if (crc != id3_crc_compute(ptr, end - ptr))
583 goto fail;
586 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
587 bytes = id3_parse_uint(&ehptr, 1);
588 if (bytes < 1)
589 goto fail;
591 tag->restrictions = id3_parse_uint(&ehptr, 1);
592 ehptr += bytes - 1;
595 break;
599 /* frames */
601 while (ptr < end) {
602 struct id3_frame *frame;
604 if (*ptr == 0)
605 break; /* padding */
607 frame = id3_frame_parse(&ptr, end - ptr, tag->version);
608 if (frame == 0 || id3_tag_attachframe(tag, frame) == -1)
609 goto fail;
612 if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 &&
613 id3_compat_fixup(tag) == -1)
614 goto fail;
617 if (0) {
618 fail:
619 id3_tag_delete(tag);
620 tag = 0;
623 if (mem)
624 free(mem);
626 return tag;
630 * NAME: tag->parse()
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;
637 int flags;
638 id3_length_t size;
640 assert(data);
642 switch (tagtype(data, length)) {
643 case TAGTYPE_ID3V1:
644 return (length < 128) ? 0 : v1_parse(data);
646 case TAGTYPE_ID3V2:
647 break;
649 case TAGTYPE_ID3V2_FOOTER:
650 case TAGTYPE_NONE:
651 return 0;
654 /* ID3v2.x */
656 ptr = data;
657 parse_header(&ptr, &version, &flags, &size);
659 switch (ID3_TAG_VERSION_MAJOR(version)) {
660 case 4:
661 if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
662 size += 10;
663 case 2:
664 case 3:
665 return (length < 10 + size) ? 0 : v2_parse(data);
668 return 0;
671 static
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);
679 if (frame == 0)
680 string = id3_ucs4_empty;
681 else {
682 if (strcmp(frameid, ID3_FRAME_COMMENT) == 0)
683 string = id3_field_getfullstring(&frame->fields[3]);
684 else
685 string = id3_field_getstrings(&frame->fields[1], 0);
688 id3_render_paddedstring(buffer, string, length);
692 * NAME: v1->render()
693 * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag
695 static
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;
700 unsigned int i;
701 int genre = -1;
703 ptr = data;
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);
716 if (frame) {
717 unsigned int track;
719 track = id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0));
720 if (track > 0 && track <= 0xff) {
721 ptr[-2] = 0;
722 ptr[-1] = track;
726 /* ID3v1 genre number */
728 frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0);
729 if (frame) {
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));
736 if (genre != -1)
737 break;
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 */
748 if (genre == -1) {
749 for (i = 3; i < 127; ++i) {
750 if (data[i] != ' ')
751 break;
754 if (i == 127)
755 return 0;
758 if (buffer)
759 memcpy(buffer, data, 128);
761 return 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;
771 id3_byte_t **ptr,
772 *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0;
773 int flags, extendedflags;
774 unsigned int i;
776 assert(tag);
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)
785 break;
788 if (i == tag->nframes)
789 return 0;
791 ptr = buffer ? &buffer : 0;
793 /* get flags */
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;
811 if (extendedflags)
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;
818 /* header */
820 if (ptr)
821 header_ptr = *ptr;
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);
827 if (ptr)
828 tagsize_ptr = *ptr;
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;
838 if (ptr)
839 ehsize_ptr = *ptr;
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);
851 if (ptr)
852 crc_ptr = *ptr;
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);
862 if (ehsize_ptr)
863 id3_render_syncsafe(&ehsize_ptr, ehsize, 4);
865 size += ehsize;
868 /* frames */
870 if (ptr)
871 frames_ptr = *ptr;
873 for (i = 0; i < tag->nframes; ++i)
874 size += id3_frame_render(tag->frames[i], ptr, tag->options);
876 /* padding */
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) {
882 if (ptr == 0)
883 size += 1;
884 else {
885 if ((*ptr)[-1] == 0xff)
886 size += id3_render_padding(ptr, 0, 1);
891 /* patch tag size and CRC */
893 if (tagsize_ptr)
894 id3_render_syncsafe(&tagsize_ptr, size - 10, 4);
896 if (crc_ptr) {
897 id3_render_syncsafe(&crc_ptr,
898 id3_crc_compute(frames_ptr, *ptr - frames_ptr), 5);
901 /* footer */
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);
908 return size;