comparison src/libid3tag-0.15.1b/tag.c @ 85:545efbb81310

Import initial set of sources
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 18 Mar 2013 14:12:14 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 85:545efbb81310
1 /*
2 * libid3tag - ID3 tag manipulation library
3 * Copyright (C) 2000-2004 Underbit Technologies, Inc.
4 *
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.
9 *
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.
14 *
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
18 *
19 * $Id: tag.c,v 1.20 2004/02/17 02:04:10 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <string.h>
29 # include <stdlib.h>
30
31 # ifdef HAVE_ASSERT_H
32 # include <assert.h>
33 # endif
34
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"
47
48 /*
49 * NAME: tag->new()
50 * DESCRIPTION: allocate and return a new, empty tag
51 */
52 struct id3_tag *id3_tag_new(void)
53 {
54 struct id3_tag *tag;
55
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;
68 }
69
70 return tag;
71 }
72
73 /*
74 * NAME: tag->delete()
75 * DESCRIPTION: destroy a tag and deallocate all associated memory
76 */
77 void id3_tag_delete(struct id3_tag *tag)
78 {
79 assert(tag);
80
81 if (tag->refcount == 0) {
82 id3_tag_clearframes(tag);
83
84 if (tag->frames)
85 free(tag->frames);
86
87 free(tag);
88 }
89 }
90
91 /*
92 * NAME: tag->addref()
93 * DESCRIPTION: add an external reference to a tag
94 */
95 void id3_tag_addref(struct id3_tag *tag)
96 {
97 assert(tag);
98
99 ++tag->refcount;
100 }
101
102 /*
103 * NAME: tag->delref()
104 * DESCRIPTION: remove an external reference to a tag
105 */
106 void id3_tag_delref(struct id3_tag *tag)
107 {
108 assert(tag && tag->refcount > 0);
109
110 --tag->refcount;
111 }
112
113 /*
114 * NAME: tag->version()
115 * DESCRIPTION: return the tag's original ID3 version number
116 */
117 unsigned int id3_tag_version(struct id3_tag const *tag)
118 {
119 assert(tag);
120
121 return tag->version;
122 }
123
124 /*
125 * NAME: tag->options()
126 * DESCRIPTION: get or set tag options
127 */
128 int id3_tag_options(struct id3_tag *tag, int mask, int values)
129 {
130 assert(tag);
131
132 if (mask)
133 tag->options = (tag->options & ~mask) | (values & mask);
134
135 return tag->options;
136 }
137
138 /*
139 * NAME: tag->setlength()
140 * DESCRIPTION: set the minimum rendered tag size
141 */
142 void id3_tag_setlength(struct id3_tag *tag, id3_length_t length)
143 {
144 assert(tag);
145
146 tag->paddedsize = length;
147 }
148
149 /*
150 * NAME: tag->clearframes()
151 * DESCRIPTION: detach and delete all frames associated with a tag
152 */
153 void id3_tag_clearframes(struct id3_tag *tag)
154 {
155 unsigned int i;
156
157 assert(tag);
158
159 for (i = 0; i < tag->nframes; ++i) {
160 id3_frame_delref(tag->frames[i]);
161 id3_frame_delete(tag->frames[i]);
162 }
163
164 tag->nframes = 0;
165 }
166
167 /*
168 * NAME: tag->attachframe()
169 * DESCRIPTION: attach a frame to a tag
170 */
171 int id3_tag_attachframe(struct id3_tag *tag, struct id3_frame *frame)
172 {
173 struct id3_frame **frames;
174
175 assert(tag && frame);
176
177 frames = realloc(tag->frames, (tag->nframes + 1) * sizeof(*frames));
178 if (frames == 0)
179 return -1;
180
181 tag->frames = frames;
182 tag->frames[tag->nframes++] = frame;
183
184 id3_frame_addref(frame);
185
186 return 0;
187 }
188
189 /*
190 * NAME: tag->detachframe()
191 * DESCRIPTION: detach (but don't delete) a frame from a tag
192 */
193 int id3_tag_detachframe(struct id3_tag *tag, struct id3_frame *frame)
194 {
195 unsigned int i;
196
197 assert(tag && frame);
198
199 for (i = 0; i < tag->nframes; ++i) {
200 if (tag->frames[i] == frame)
201 break;
202 }
203
204 if (i == tag->nframes)
205 return -1;
206
207 --tag->nframes;
208 while (i++ < tag->nframes)
209 tag->frames[i - 1] = tag->frames[i];
210
211 id3_frame_delref(frame);
212
213 return 0;
214 }
215
216 /*
217 * NAME: tag->findframe()
218 * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID
219 */
220 struct id3_frame *id3_tag_findframe(struct id3_tag const *tag,
221 char const *id, unsigned int index)
222 {
223 unsigned int len, i;
224
225 assert(tag);
226
227 if (id == 0 || *id == 0)
228 return (index < tag->nframes) ? tag->frames[index] : 0;
229
230 len = strlen(id);
231
232 if (len == 4) {
233 struct id3_compat const *compat;
234
235 compat = id3_compat_lookup(id, len);
236 if (compat && compat->equiv && !compat->translate) {
237 id = compat->equiv;
238 len = strlen(id);
239 }
240 }
241
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];
245 }
246
247 return 0;
248 }
249
250 enum tagtype {
251 TAGTYPE_NONE = 0,
252 TAGTYPE_ID3V1,
253 TAGTYPE_ID3V2,
254 TAGTYPE_ID3V2_FOOTER
255 };
256
257 static
258 enum tagtype tagtype(id3_byte_t const *data, id3_length_t length)
259 {
260 if (length >= 3 &&
261 data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
262 return TAGTYPE_ID3V1;
263
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;
270
271 return TAGTYPE_NONE;
272 }
273
274 static
275 void parse_header(id3_byte_t const **ptr,
276 unsigned int *version, int *flags, id3_length_t *size)
277 {
278 *ptr += 3;
279
280 *version = id3_parse_uint(ptr, 2);
281 *flags = id3_parse_uint(ptr, 1);
282 *size = id3_parse_syncsafe(ptr, 4);
283 }
284
285 /*
286 * NAME: tag->query()
287 * DESCRIPTION: if a tag begins at the given location, return its size
288 */
289 signed long id3_tag_query(id3_byte_t const *data, id3_length_t length)
290 {
291 unsigned int version;
292 int flags;
293 id3_length_t size;
294
295 assert(data);
296
297 switch (tagtype(data, length)) {
298 case TAGTYPE_ID3V1:
299 return 128;
300
301 case TAGTYPE_ID3V2:
302 parse_header(&data, &version, &flags, &size);
303
304 if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
305 size += 10;
306
307 return 10 + size;
308
309 case TAGTYPE_ID3V2_FOOTER:
310 parse_header(&data, &version, &flags, &size);
311 return -size - 10;
312
313 case TAGTYPE_NONE:
314 break;
315 }
316
317 return 0;
318 }
319
320 static
321 void trim(char *str)
322 {
323 char *ptr;
324
325 ptr = str + strlen(str);
326 while (ptr > str && ptr[-1] == ' ')
327 --ptr;
328
329 *ptr = 0;
330 }
331
332 static
333 int v1_attachstr(struct id3_tag *tag, char const *id,
334 char *text, unsigned long number)
335 {
336 struct id3_frame *frame;
337 id3_ucs4_t ucs4[31];
338
339 if (text) {
340 trim(text);
341 if (*text == 0)
342 return 0;
343 }
344
345 frame = id3_frame_new(id);
346 if (frame == 0)
347 return -1;
348
349 if (id3_field_settextencoding(&frame->fields[0],
350 ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
351 goto fail;
352
353 if (text)
354 id3_latin1_decode(text, ucs4);
355 else
356 id3_ucs4_putnumber(ucs4, number);
357
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;
363 }
364 else {
365 id3_ucs4_t *ptr = ucs4;
366
367 if (id3_field_setstrings(&frame->fields[1], 1, &ptr) == -1)
368 goto fail;
369 }
370
371 if (id3_tag_attachframe(tag, frame) == -1)
372 goto fail;
373
374 return 0;
375
376 fail:
377 id3_frame_delete(frame);
378 return -1;
379 }
380
381 static
382 struct id3_tag *v1_parse(id3_byte_t const *data)
383 {
384 struct id3_tag *tag;
385
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;
390
391 tag->version = 0x0100;
392
393 tag->options |= ID3_TAG_OPTION_ID3V1;
394 tag->options &= ~ID3_TAG_OPTION_COMPRESSION;
395
396 tag->restrictions =
397 ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 |
398 ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS;
399
400 title[30] = artist[30] = album[30] = year[4] = comment[30] = 0;
401
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);
407
408 genre = data[127];
409
410 track = 0;
411 if (comment[28] == 0 && comment[29] != 0) {
412 track = comment[29];
413 tag->version = 0x0101;
414 }
415
416 /* populate tag frames */
417
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;
427 }
428 }
429
430 return tag;
431 }
432
433 static
434 struct id3_tag *v2_parse(id3_byte_t const *ptr)
435 {
436 struct id3_tag *tag;
437 id3_byte_t *mem = 0;
438
439 tag = id3_tag_new();
440 if (tag) {
441 id3_byte_t const *end;
442 id3_length_t size;
443
444 parse_header(&ptr, &tag->version, &tag->flags, &size);
445
446 tag->paddedsize = 10 + size;
447
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;
453
454 memcpy(mem, ptr, size);
455
456 size = id3_util_deunsynchronise(mem, size);
457 ptr = mem;
458 }
459
460 end = ptr + size;
461
462 if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
463 switch (ID3_TAG_VERSION_MAJOR(tag->version)) {
464 case 2:
465 goto fail;
466
467 case 3:
468 {
469 id3_byte_t const *ehptr, *ehend;
470 id3_length_t ehsize;
471
472 enum {
473 EH_FLAG_CRC = 0x8000 /* CRC data present */
474 };
475
476 if (end - ptr < 4)
477 goto fail;
478
479 ehsize = id3_parse_uint(&ptr, 4);
480
481 if (ehsize > end - ptr)
482 goto fail;
483
484 ehptr = ptr;
485 ehend = ptr + ehsize;
486
487 ptr = ehend;
488
489 if (ehend - ehptr >= 6) {
490 int ehflags;
491 id3_length_t padsize;
492
493 ehflags = id3_parse_uint(&ehptr, 2);
494 padsize = id3_parse_uint(&ehptr, 4);
495
496 if (padsize > end - ptr)
497 goto fail;
498
499 end -= padsize;
500
501 if (ehflags & EH_FLAG_CRC) {
502 unsigned long crc;
503
504 if (ehend - ehptr < 4)
505 goto fail;
506
507 crc = id3_parse_uint(&ehptr, 4);
508
509 if (crc != id3_crc_compute(ptr, end - ptr))
510 goto fail;
511
512 tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
513 }
514 }
515 }
516 break;
517
518 case 4:
519 {
520 id3_byte_t const *ehptr, *ehend;
521 id3_length_t ehsize;
522 unsigned int bytes;
523
524 if (end - ptr < 4)
525 goto fail;
526
527 ehptr = ptr;
528 ehsize = id3_parse_syncsafe(&ptr, 4);
529
530 if (ehsize < 6 || ehsize > end - ehptr)
531 goto fail;
532
533 ehend = ehptr + ehsize;
534
535 bytes = id3_parse_uint(&ptr, 1);
536
537 if (bytes < 1 || bytes > ehend - ptr)
538 goto fail;
539
540 ehptr = ptr + bytes;
541
542 /* verify extended header size */
543 {
544 id3_byte_t const *flagsptr = ptr, *dataptr = ehptr;
545 unsigned int datalen;
546 int ehflags;
547
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;
558 }
559 }
560 }
561 }
562
563 tag->extendedflags = id3_parse_uint(&ptr, 1);
564
565 ptr = ehend;
566
567 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) {
568 bytes = id3_parse_uint(&ehptr, 1);
569 ehptr += bytes;
570 }
571
572 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
573 unsigned long crc;
574
575 bytes = id3_parse_uint(&ehptr, 1);
576 if (bytes < 5)
577 goto fail;
578
579 crc = id3_parse_syncsafe(&ehptr, 5);
580 ehptr += bytes - 5;
581
582 if (crc != id3_crc_compute(ptr, end - ptr))
583 goto fail;
584 }
585
586 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
587 bytes = id3_parse_uint(&ehptr, 1);
588 if (bytes < 1)
589 goto fail;
590
591 tag->restrictions = id3_parse_uint(&ehptr, 1);
592 ehptr += bytes - 1;
593 }
594 }
595 break;
596 }
597 }
598
599 /* frames */
600
601 while (ptr < end) {
602 struct id3_frame *frame;
603
604 if (*ptr == 0)
605 break; /* padding */
606
607 frame = id3_frame_parse(&ptr, end - ptr, tag->version);
608 if (frame == 0 || id3_tag_attachframe(tag, frame) == -1)
609 goto fail;
610 }
611
612 if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 &&
613 id3_compat_fixup(tag) == -1)
614 goto fail;
615 }
616
617 if (0) {
618 fail:
619 id3_tag_delete(tag);
620 tag = 0;
621 }
622
623 if (mem)
624 free(mem);
625
626 return tag;
627 }
628
629 /*
630 * NAME: tag->parse()
631 * DESCRIPTION: parse a complete ID3 tag
632 */
633 struct id3_tag *id3_tag_parse(id3_byte_t const *data, id3_length_t length)
634 {
635 id3_byte_t const *ptr;
636 unsigned int version;
637 int flags;
638 id3_length_t size;
639
640 assert(data);
641
642 switch (tagtype(data, length)) {
643 case TAGTYPE_ID3V1:
644 return (length < 128) ? 0 : v1_parse(data);
645
646 case TAGTYPE_ID3V2:
647 break;
648
649 case TAGTYPE_ID3V2_FOOTER:
650 case TAGTYPE_NONE:
651 return 0;
652 }
653
654 /* ID3v2.x */
655
656 ptr = data;
657 parse_header(&ptr, &version, &flags, &size);
658
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);
666 }
667
668 return 0;
669 }
670
671 static
672 void v1_renderstr(struct id3_tag const *tag, char const *frameid,
673 id3_byte_t **buffer, id3_length_t length)
674 {
675 struct id3_frame *frame;
676 id3_ucs4_t const *string;
677
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);
686 }
687
688 id3_render_paddedstring(buffer, string, length);
689 }
690
691 /*
692 * NAME: v1->render()
693 * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag
694 */
695 static
696 id3_length_t v1_render(struct id3_tag const *tag, id3_byte_t *buffer)
697 {
698 id3_byte_t data[128], *ptr;
699 struct id3_frame *frame;
700 unsigned int i;
701 int genre = -1;
702
703 ptr = data;
704
705 id3_render_immediate(&ptr, "TAG", 3);
706
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);
712
713 /* ID3v1.1 track number */
714
715 frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0);
716 if (frame) {
717 unsigned int track;
718
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;
723 }
724 }
725
726 /* ID3v1 genre number */
727
728 frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0);
729 if (frame) {
730 unsigned int nstrings;
731
732 nstrings = id3_field_getnstrings(&frame->fields[1]);
733
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;
738 }
739
740 if (i == nstrings && nstrings > 0)
741 genre = ID3_GENRE_OTHER;
742 }
743
744 id3_render_int(&ptr, genre, 1);
745
746 /* make sure the tag is not empty */
747
748 if (genre == -1) {
749 for (i = 3; i < 127; ++i) {
750 if (data[i] != ' ')
751 break;
752 }
753
754 if (i == 127)
755 return 0;
756 }
757
758 if (buffer)
759 memcpy(buffer, data, 128);
760
761 return 128;
762 }
763
764 /*
765 * NAME: tag->render()
766 * DESCRIPTION: render a complete ID3 tag
767 */
768 id3_length_t id3_tag_render(struct id3_tag const *tag, id3_byte_t *buffer)
769 {
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;
775
776 assert(tag);
777
778 if (tag->options & ID3_TAG_OPTION_ID3V1)
779 return v1_render(tag, buffer);
780
781 /* a tag must contain at least one (renderable) frame */
782
783 for (i = 0; i < tag->nframes; ++i) {
784 if (id3_frame_render(tag->frames[i], 0, 0) > 0)
785 break;
786 }
787
788 if (i == tag->nframes)
789 return 0;
790
791 ptr = buffer ? &buffer : 0;
792
793 /* get flags */
794
795 flags = tag->flags & ID3_TAG_FLAG_KNOWNFLAGS;
796 extendedflags = tag->extendedflags & ID3_TAG_EXTENDEDFLAG_KNOWNFLAGS;
797
798 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
799 if (tag->options & ID3_TAG_OPTION_CRC)
800 extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
801
802 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
803 if (tag->restrictions)
804 extendedflags |= ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
805
806 flags &= ~ID3_TAG_FLAG_UNSYNCHRONISATION;
807 if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION)
808 flags |= ID3_TAG_FLAG_UNSYNCHRONISATION;
809
810 flags &= ~ID3_TAG_FLAG_EXTENDEDHEADER;
811 if (extendedflags)
812 flags |= ID3_TAG_FLAG_EXTENDEDHEADER;
813
814 flags &= ~ID3_TAG_FLAG_FOOTERPRESENT;
815 if (tag->options & ID3_TAG_OPTION_APPENDEDTAG)
816 flags |= ID3_TAG_FLAG_FOOTERPRESENT;
817
818 /* header */
819
820 if (ptr)
821 header_ptr = *ptr;
822
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);
826
827 if (ptr)
828 tagsize_ptr = *ptr;
829
830 size += id3_render_syncsafe(ptr, 0, 4);
831
832 /* extended header */
833
834 if (flags & ID3_TAG_FLAG_EXTENDEDHEADER) {
835 id3_length_t ehsize = 0;
836 id3_byte_t *ehsize_ptr = 0;
837
838 if (ptr)
839 ehsize_ptr = *ptr;
840
841 ehsize += id3_render_syncsafe(ptr, 0, 4);
842 ehsize += id3_render_int(ptr, 1, 1);
843 ehsize += id3_render_int(ptr, extendedflags, 1);
844
845 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)
846 ehsize += id3_render_int(ptr, 0, 1);
847
848 if (extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
849 ehsize += id3_render_int(ptr, 5, 1);
850
851 if (ptr)
852 crc_ptr = *ptr;
853
854 ehsize += id3_render_syncsafe(ptr, 0, 5);
855 }
856
857 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
858 ehsize += id3_render_int(ptr, 1, 1);
859 ehsize += id3_render_int(ptr, tag->restrictions, 1);
860 }
861
862 if (ehsize_ptr)
863 id3_render_syncsafe(&ehsize_ptr, ehsize, 4);
864
865 size += ehsize;
866 }
867
868 /* frames */
869
870 if (ptr)
871 frames_ptr = *ptr;
872
873 for (i = 0; i < tag->nframes; ++i)
874 size += id3_frame_render(tag->frames[i], ptr, tag->options);
875
876 /* padding */
877
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);
887 }
888 }
889 }
890
891 /* patch tag size and CRC */
892
893 if (tagsize_ptr)
894 id3_render_syncsafe(&tagsize_ptr, size - 10, 4);
895
896 if (crc_ptr) {
897 id3_render_syncsafe(&crc_ptr,
898 id3_crc_compute(frames_ptr, *ptr - frames_ptr), 5);
899 }
900
901 /* footer */
902
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);
906 }
907
908 return size;
909 }