static struct id3_tag *v2_parse(id3_byte_t const *ptr) { struct id3_tag *tag; id3_byte_t *mem = 0; tag = id3_tag_new(); if (tag) { id3_byte_t const *end; id3_length_t size; parse_header(&ptr, &tag->version, &tag->flags, &size); tag->paddedsize = 10 + size; if ((tag->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) && ID3_TAG_VERSION_MAJOR(tag->version) < 4) { mem = malloc(size); if (mem == 0) goto fail; memcpy(mem, ptr, size); size = id3_util_deunsynchronise(mem, size); ptr = mem; } end = ptr + size; if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) { switch (ID3_TAG_VERSION_MAJOR(tag->version)) { case 2: goto fail; case 3: { id3_byte_t const *ehptr, *ehend; id3_length_t ehsize; enum { EH_FLAG_CRC = 0x8000 /* CRC data present */ }; if (end - ptr < 4) goto fail; ehsize = id3_parse_uint(&ptr, 4); if (ehsize > end - ptr) goto fail; ehptr = ptr; ehend = ptr + ehsize; ptr = ehend; if (ehend - ehptr >= 6) { int ehflags; id3_length_t padsize; ehflags = id3_parse_uint(&ehptr, 2); padsize = id3_parse_uint(&ehptr, 4); if (padsize > end - ptr) goto fail; end -= padsize; if (ehflags & EH_FLAG_CRC) { unsigned long crc; if (ehend - ehptr < 4) goto fail; crc = id3_parse_uint(&ehptr, 4); if (crc != id3_crc_compute(ptr, end - ptr)) goto fail; tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; } } } break; case 4: { id3_byte_t const *ehptr, *ehend; id3_length_t ehsize; unsigned int bytes; if (end - ptr < 4) goto fail; ehptr = ptr; ehsize = id3_parse_syncsafe(&ptr, 4); if (ehsize < 6 || ehsize > end - ehptr) goto fail; ehend = ehptr + ehsize; bytes = id3_parse_uint(&ptr, 1); if (bytes < 1 || bytes > ehend - ptr) goto fail; ehptr = ptr + bytes; /* verify extended header size */ { id3_byte_t const *flagsptr = ptr, *dataptr = ehptr; unsigned int datalen; int ehflags; while (bytes--) { for (ehflags = id3_parse_uint(&flagsptr, 1); ehflags; ehflags = (ehflags << 1) & 0xff) { if (ehflags & 0x80) { if (dataptr == ehend) goto fail; datalen = id3_parse_uint(&dataptr, 1); if (datalen > 0x7f || datalen > ehend - dataptr) goto fail; dataptr += datalen; } } } } tag->extendedflags = id3_parse_uint(&ptr, 1); ptr = ehend; if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) { bytes = id3_parse_uint(&ehptr, 1); ehptr += bytes; } if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) { unsigned long crc; bytes = id3_parse_uint(&ehptr, 1); if (bytes < 5) goto fail; crc = id3_parse_syncsafe(&ehptr, 5); ehptr += bytes - 5; if (crc != id3_crc_compute(ptr, end - ptr)) goto fail; } if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) { bytes = id3_parse_uint(&ehptr, 1); if (bytes < 1) goto fail; tag->restrictions = id3_parse_uint(&ehptr, 1); ehptr += bytes - 1; } } break; } } /* frames */ while (ptr < end) { struct id3_frame *frame; if (*ptr == 0) break; /* padding */ frame = id3_frame_parse(&ptr, end - ptr, tag->version); if (frame == 0 || id3_tag_attachframe(tag, frame) == -1) goto fail; } if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 && id3_compat_fixup(tag) == -1) goto fail; } if (0) { fail: id3_tag_delete(tag); tag = 0; } if (mem) free(mem); return tag; }
/* * NAME: tag->render() * DESCRIPTION: render a complete ID3 tag */ id3_length_t id3_tag_render(struct id3_tag const *tag, id3_byte_t *buffer) { id3_length_t size = 0; id3_byte_t **ptr, *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0; int flags, extendedflags; unsigned int i; assert(tag); if (tag->options & ID3_TAG_OPTION_ID3V1) return v1_render(tag, buffer); /* a tag must contain at least one (renderable) frame */ for (i = 0; i < tag->nframes; ++i) { if (id3_frame_render(tag->frames[i], 0, 0) > 0) break; } if (i == tag->nframes) return 0; ptr = buffer ? &buffer : 0; /* get flags */ flags = tag->flags & ID3_TAG_FLAG_KNOWNFLAGS; extendedflags = tag->extendedflags & ID3_TAG_EXTENDEDFLAG_KNOWNFLAGS; extendedflags &= ~ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; if (tag->options & ID3_TAG_OPTION_CRC) extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; extendedflags &= ~ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS; if (tag->restrictions) extendedflags |= ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS; flags &= ~ID3_TAG_FLAG_UNSYNCHRONISATION; if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION) flags |= ID3_TAG_FLAG_UNSYNCHRONISATION; flags &= ~ID3_TAG_FLAG_EXTENDEDHEADER; if (extendedflags) flags |= ID3_TAG_FLAG_EXTENDEDHEADER; flags &= ~ID3_TAG_FLAG_FOOTERPRESENT; if (tag->options & ID3_TAG_OPTION_APPENDEDTAG) flags |= ID3_TAG_FLAG_FOOTERPRESENT; /* header */ if (ptr) header_ptr = *ptr; size += id3_render_immediate(ptr, "ID3", 3); size += id3_render_int(ptr, ID3_TAG_VERSION, 2); size += id3_render_int(ptr, flags, 1); if (ptr) tagsize_ptr = *ptr; size += id3_render_syncsafe(ptr, 0, 4); /* extended header */ if (flags & ID3_TAG_FLAG_EXTENDEDHEADER) { id3_length_t ehsize = 0; id3_byte_t *ehsize_ptr = 0; if (ptr) ehsize_ptr = *ptr; ehsize += id3_render_syncsafe(ptr, 0, 4); ehsize += id3_render_int(ptr, 1, 1); ehsize += id3_render_int(ptr, extendedflags, 1); if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) ehsize += id3_render_int(ptr, 0, 1); if (extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) { ehsize += id3_render_int(ptr, 5, 1); if (ptr) crc_ptr = *ptr; ehsize += id3_render_syncsafe(ptr, 0, 5); } if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) { ehsize += id3_render_int(ptr, 1, 1); ehsize += id3_render_int(ptr, tag->restrictions, 1); } if (ehsize_ptr) id3_render_syncsafe(&ehsize_ptr, ehsize, 4); size += ehsize; } /* frames */ if (ptr) frames_ptr = *ptr; for (i = 0; i < tag->nframes; ++i) size += id3_frame_render(tag->frames[i], ptr, tag->options); /* padding */ if (!(flags & ID3_TAG_FLAG_FOOTERPRESENT)) { if (size < tag->paddedsize) size += id3_render_padding(ptr, 0, tag->paddedsize - size); else if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION) { if (ptr == 0) size += 1; else { if ((*ptr)[-1] == 0xff) size += id3_render_padding(ptr, 0, 1); } } } /* patch tag size and CRC */ if (tagsize_ptr) id3_render_syncsafe(&tagsize_ptr, size - 10, 4); if (crc_ptr) { id3_render_syncsafe(&crc_ptr, id3_crc_compute(frames_ptr, *ptr - frames_ptr), 5); } /* footer */ if (flags & ID3_TAG_FLAG_FOOTERPRESENT) { size += id3_render_immediate(ptr, "3DI", 3); size += id3_render_binary(ptr, header_ptr + 3, 7); } return size; }