static struct id3_tag *v1_parse(id3_byte_t const *data) { struct id3_tag *tag; tag = id3_tag_new(); if (tag) { char title[31], artist[31], album[31], year[5], comment[31]; unsigned int genre, track; tag->version = 0x0100; tag->options |= ID3_TAG_OPTION_ID3V1; tag->options &= ~ID3_TAG_OPTION_COMPRESSION; tag->restrictions = ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 | ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS; title[30] = artist[30] = album[30] = year[4] = comment[30] = 0; memcpy(title, &data[3], 30); memcpy(artist, &data[33], 30); memcpy(album, &data[63], 30); memcpy(year, &data[93], 4); memcpy(comment, &data[97], 30); genre = data[127]; track = 0; if (comment[28] == 0 && comment[29] != 0) { track = comment[29]; tag->version = 0x0101; } /* populate tag frames */ if (v1_attachstr(tag, ID3_FRAME_TITLE, title, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ARTIST, artist, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ALBUM, album, 0) == -1 || v1_attachstr(tag, ID3_FRAME_YEAR, year, 0) == -1 || (track && v1_attachstr(tag, ID3_FRAME_TRACK, 0, track) == -1) || (genre < 0xff && v1_attachstr(tag, ID3_FRAME_GENRE, 0, genre) == -1) || v1_attachstr(tag, ID3_FRAME_COMMENT, comment, 0) == -1) { id3_tag_delete(tag); tag = 0; } } return tag; }
// returns buffer len; caller frees int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), char **buffer, bool *endOfFile, const Tags *tags) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); for (const auto &pair : tags->GetRange()) { const auto &n = pair.first; const auto &v = pair.second; const char *name = "TXXX"; if (n.CmpNoCase(TAG_TITLE) == 0) { name = ID3_FRAME_TITLE; } else if (n.CmpNoCase(TAG_ARTIST) == 0) { name = ID3_FRAME_ARTIST; } else if (n.CmpNoCase(TAG_ALBUM) == 0) { name = ID3_FRAME_ALBUM; } else if (n.CmpNoCase(TAG_YEAR) == 0) { // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR), // so we add old one as well. AddFrame(tp, n, v, "TYER"); name = ID3_FRAME_YEAR; } else if (n.CmpNoCase(TAG_GENRE) == 0) { name = ID3_FRAME_GENRE; } else if (n.CmpNoCase(TAG_COMMENTS) == 0) { name = ID3_FRAME_COMMENT; } else if (n.CmpNoCase(TAG_TRACK) == 0) { name = ID3_FRAME_TRACK; } AddFrame(tp, n, v, name); } tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }
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; }
static int _BarFlyTagID3Write(BarFly_t const* fly, uint8_t const* cover_art, size_t cover_size, BarSettings_t const* settings) { int const BUFFER_SIZE = 5; int const TAG_PADDED_SIZE = 1024; char const BAR_FLY_ID3_FRAME_DISC[] = "TPOS"; int exit_status = 0; int status; struct id3_tag* tag; char buffer[BUFFER_SIZE]; assert(fly != NULL); assert(fly->audio_file_path != NULL); assert(settings != NULL); /* * Set the minimum size for the tag. The tag will use CRC and compression. * FIXME - figure out if the padded size is really needed. */ tag = id3_tag_new(); if (tag == NULL) { BarUiMsg(settings, MSG_ERR, "Failed to create new tag.\n"); goto error; } id3_tag_setlength(tag, TAG_PADDED_SIZE); id3_tag_options(tag, ID3_TAG_OPTION_UNSYNCHRONISATION | ID3_TAG_OPTION_APPENDEDTAG | ID3_TAG_OPTION_CRC | ID3_TAG_OPTION_COMPRESSION, 0); /* * Add the data to the tag. */ status = BarFlyID3AddFrame(tag, ID3_FRAME_ARTIST, fly->artist, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write artist to tag.\n"); goto error; } status = BarFlyID3AddFrame(tag, ID3_FRAME_ALBUM, fly->album, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write album to tag.\n"); goto error; } status = BarFlyID3AddFrame(tag, ID3_FRAME_TITLE, fly->title, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write title to tag.\n"); goto error; } if (fly->year != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->year); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, ID3_FRAME_YEAR, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write year to tag.\n"); goto error; } } if (fly->track != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->track); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, ID3_FRAME_TRACK, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write track number to tag.\n"); goto error; } } if (fly->disc != 0) { snprintf(buffer, BUFFER_SIZE, "%hu", fly->disc); buffer[BUFFER_SIZE - 1] = '\0'; status = BarFlyID3AddFrame(tag, BAR_FLY_ID3_FRAME_DISC, buffer, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write disc number to tag.\n"); goto error; } } if (cover_art != NULL) { status = BarFlyID3AddCover(tag, cover_art, cover_size, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write cover to tag.\n"); goto error; } } /* * Write the tag to the file. */ status = BarFlyID3WriteFile(fly->audio_file_path, tag, settings); if (status != 0) { BarUiMsg(settings, MSG_ERR, "Failed to write the tag.\n"); goto error; } goto end; error: exit_status = -1; end: if (tag != NULL) { id3_tag_delete(tag); } return exit_status; }
static context * context_create(const char *filename) { context *node = (context *) malloc(sizeof(context)); context *ptr, *last; int last_id = INT_MAX; node->refcount = 1; { struct id3_file *file; struct id3_tag *tag; unsigned int i; file = id3_file_open(filename, ID3_FILE_MODE_READONLY); if (!file) { fprintf(stderr, "Unable to open tagged file %s: %s\n", filename, strerror(errno)); goto fail_free; } tag = id3_file_tag(file); if (!tag) { fprintf(stderr, "Unable to find ID3v2 tags in file %s\n", filename); id3_file_close(file); goto fail_free; } node->tag = id3_tag_new(); for (i = 0; i < id3_tag_get_numframes(tag); i++) if (!strcmp(id3_frame_id(id3_tag_get_frame(tag, i)), "APIC")) id3_tag_attachframe(node->tag, id3_tag_get_frame(tag, i)); id3_file_close(file); } node->filename = strdup(filename); if (!id3_ctxs) { node->id = 1; node->next = NULL; id3_ctxs = node; return node; } ptr = id3_ctxs; last = NULL; while (UNLIKELY(ptr && (ptr->id + 1) >= last_id)) { last_id = ptr->id; last = ptr; ptr = ptr->next; } /* Paranoid! this can occur only if there are INT_MAX contexts :) */ if (UNLIKELY(!ptr)) { fprintf(stderr, "Too many open ID3 contexts\n"); goto fail_close; } node->id = ptr->id + 1; if (UNLIKELY(! !last)) { node->next = last->next; last->next = node; } else { node->next = id3_ctxs; id3_ctxs = node; } return node; fail_close: free(node->filename); id3_tag_delete(node->tag); fail_free: free(node); return NULL; }
// returns buffer len; caller frees int Tags::ExportID3(char **buffer, bool *endOfFile) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); if (mTitle != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TITLE, mTitle.mb_str())); if (mArtist != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ARTIST, mArtist.mb_str())); if (mAlbum != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ALBUM, mAlbum.mb_str())); if (mYear != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_YEAR, mYear.mb_str())); if (mComments != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_COMMENT, mComments.mb_str())); if (mTrackNum >= 0) { wxString trackNumStr; trackNumStr.Printf(wxT("%d"), mTrackNum); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TRACK, trackNumStr.mb_str())); } if (mGenre >= 0) { if (mID3V2) { wxString genreStr = GetGenreNum(mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } else { wxString genreStr; genreStr.Printf(wxT("%d"), mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } } if (mID3V2) { tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; } else { tp->options |= ID3_TAG_OPTION_ID3V1; *endOfFile = true; } id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }