/* * NAME: v1->render() * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag */ static id3_length_t v1_render(struct id3_tag const *tag, id3_byte_t *buffer) { struct id3_frame *frame; unsigned int number, i; if (buffer == 0) return 128; id3_render_immediate(&buffer, "TAG", 3); v1_renderstr(tag, ID3_FRAME_TITLE, &buffer, 30); v1_renderstr(tag, ID3_FRAME_ARTIST, &buffer, 30); v1_renderstr(tag, ID3_FRAME_ALBUM, &buffer, 30); v1_renderstr(tag, ID3_FRAME_YEAR, &buffer, 4); v1_renderstr(tag, ID3_FRAME_COMMENT, &buffer, 30); frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0); if (frame) { number = id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0)); if (number & 0xff) { buffer[-2] = 0; buffer[-1] = number; } } frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0); number = frame ? id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0)) : 0xff; id3_render_int(&buffer, number, 1); /* make sure the tag is not empty */ buffer -= 128; for (i = 3; i < 127; ++i) { if (buffer[i] != ' ') break; } return (i == 127 && buffer[127] == 0xff) ? 0 : 128; }
/* * 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; }
/* * NAME: v1->render() * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag */ static id3_length_t v1_render(struct id3_tag const *tag, id3_byte_t *buffer) { id3_byte_t data[128], *ptr; struct id3_frame *frame; unsigned int i; int genre = -1; ptr = data; id3_render_immediate(&ptr, "TAG", 3); v1_renderstr(tag, ID3_FRAME_TITLE, &ptr, 30); v1_renderstr(tag, ID3_FRAME_ARTIST, &ptr, 30); v1_renderstr(tag, ID3_FRAME_ALBUM, &ptr, 30); v1_renderstr(tag, ID3_FRAME_YEAR, &ptr, 4); v1_renderstr(tag, ID3_FRAME_COMMENT, &ptr, 30); /* ID3v1.1 track number */ frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0); if (frame) { unsigned int track; track = id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0)); if (track > 0 && track <= 0xff) { ptr[-2] = 0; ptr[-1] = track; } } /* ID3v1 genre number */ frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0); if (frame) { unsigned int nstrings; nstrings = id3_field_getnstrings(&frame->fields[1]); for (i = 0; i < nstrings; ++i) { genre = id3_genre_number(id3_field_getstrings(&frame->fields[1], i)); if (genre != -1) break; } if (i == nstrings && nstrings > 0) genre = ID3_GENRE_OTHER; } id3_render_int(&ptr, genre, 1); /* make sure the tag is not empty */ if (genre == -1) { for (i = 3; i < 127; ++i) { if (data[i] != ' ') break; } if (i == 127) return 0; } if (buffer) memcpy(buffer, data, 128); return 128; }
/* * NAME: field->render() * DESCRIPTION: render a field value */ id3_length_t id3_field_render(union id3_field const *field, id3_byte_t **ptr, enum id3_field_textencoding *encoding, int terminate) { id3_length_t size; unsigned int i; assert(field && encoding); switch (field->type) { case ID3_FIELD_TYPE_INT32: return id3_render_int(ptr, field->number.value, 4); case ID3_FIELD_TYPE_INT24: return id3_render_int(ptr, field->number.value, 3); case ID3_FIELD_TYPE_INT16: return id3_render_int(ptr, field->number.value, 2); case ID3_FIELD_TYPE_TEXTENCODING: *encoding = field->number.value; case ID3_FIELD_TYPE_INT8: return id3_render_int(ptr, field->number.value, 1); case ID3_FIELD_TYPE_LATIN1: case ID3_FIELD_TYPE_LATIN1FULL: return id3_render_latin1(ptr, field->latin1.ptr, terminate); case ID3_FIELD_TYPE_LATIN1LIST: size = 0; for (i = 0; i < field->latin1list.nstrings; ++i) { size += id3_render_latin1(ptr, field->latin1list.strings[i], (i < field->latin1list.nstrings - 1) || terminate); } return size; case ID3_FIELD_TYPE_STRING: case ID3_FIELD_TYPE_STRINGFULL: return id3_render_string(ptr, field->string.ptr, *encoding, terminate); case ID3_FIELD_TYPE_STRINGLIST: size = 0; for (i = 0; i < field->stringlist.nstrings; ++i) { size += id3_render_string(ptr, field->stringlist.strings[i], *encoding, (i < field->stringlist.nstrings - 1) || terminate); } return size; case ID3_FIELD_TYPE_LANGUAGE: return id3_render_immediate(ptr, field->immediate.value, 3); case ID3_FIELD_TYPE_FRAMEID: return id3_render_immediate(ptr, field->immediate.value, 4); case ID3_FIELD_TYPE_DATE: return id3_render_immediate(ptr, field->immediate.value, 8); case ID3_FIELD_TYPE_INT32PLUS: case ID3_FIELD_TYPE_BINARYDATA: return id3_render_binary(ptr, field->binary.data, field->binary.length); } return 0; }