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 */ 52struct 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 */ 77void 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 */ 95void 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 */ 106void 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 */ 117unsigned 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 */ 128int 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 */ 142void 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 */ 153void 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 */ 171int 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 */ 193int 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 */ 220struct 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 250enum tagtype { 251 TAGTYPE_NONE = 0, 252 TAGTYPE_ID3V1, 253 TAGTYPE_ID3V2, 254 TAGTYPE_ID3V2_FOOTER 255}; 256 257static 258enum 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 274static 275void 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 */ 289signed 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 320static 321void 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 332static 333int 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 381static 382struct 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 433static 434struct 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 */ 633struct 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 671static 672void 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 */ 695static 696id3_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 */ 768id3_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} 910