Rob Leslie wrote:
On Tuesday, September 2, 2003, at 11:13 PM, Dominic Mazzoni wrote:
...
- Any plans to support writing older, but more compatible, versions of ID3 tags for better compatibility with other programs?
Not currently. Although ID3 is fairly miserable in its lack of forward compatibility, the decision to write ID3v2.4 tags exclusively was intended to encourage its adoption and to simplify the implementation and programmer view.
Fortunately it may not be very difficult to modify libid3tag to support writing tags in multiple version formats. It will require some additional translations, but seems possible. I suppose it may happen if there is enough demand for it.
Luckily ID3v2.3 is quite similar to ID3v2.4, so I tried patching libid3tag to output ID3v2.3 tags as an option. I think I took care of the main differences, but there are a small number of specific tags that changed between the versions, and I didn't handle those differences. I'm using this in Audacity, though, and it seem to be pretty successful - the ID3v2.3 tags it outputs seem to be supported by many more programs.
Patch attached. I don't really expect you to check this in, since it makes it possible to output bad tags if you're not careful, but maybe someone else on the list will be interested in this as a workaround, too, or maybe it will save you some time later if you decide there's more demand.
Cheers, Dominic
diff -r libid3tag-0.15.0b/frame.c libid3tag-0.15.0b-patch/frame.c 542,543c542,547 < if (size_ptr) < id3_render_syncsafe(&size_ptr, size - 10, 4); ---
if (size_ptr) { if (options & ID3_TAG_OPTION_ID3V2_3) id3_render_int(&size_ptr, size - 10, 4); else id3_render_syncsafe(&size_ptr, size - 10, 4); }
560c564 < if (flags & ID3_FRAME_FLAG_GROUPINGIDENTITY) ---
if (flags & ID3_FRAME_FLAG_GROUPINGIDENTITY) {
562c566,568 < if (flags & ID3_FRAME_FLAG_ENCRYPTION) ---
}
if (flags & ID3_FRAME_FLAG_ENCRYPTION) {
563a570,571
}
620,621c628,633 < if (size_ptr) < id3_render_syncsafe(&size_ptr, size - 10, 4); ---
if (size_ptr) { if (options & ID3_TAG_OPTION_ID3V2_3) id3_render_int(&size_ptr, size - 10, 4); else id3_render_syncsafe(&size_ptr, size - 10, 4); }
diff -r libid3tag-0.15.0b/id3tag.h libid3tag-0.15.0b-patch/id3tag.h 142c142,143 < ID3_TAG_OPTION_ID3V1 = 0x0100 /* render ID3v1/ID3v1.1 tag */ ---
ID3_TAG_OPTION_ID3V1 = 0x0100,/* render ID3v1/ID3v1.1 tag */ ID3_TAG_OPTION_ID3V2_3 = 0x0200 /* render ID3v2.3 tag */
diff -r libid3tag-0.15.0b/tag.c libid3tag-0.15.0b-patch/tag.c 764a765,913
- NAME: v2_3_render()
- DESCRIPTION: render a v2.3 ID3 tag for compatibility
- AUTHOR: Dominic Mazzoni
*/
#define ID3V2_3_TAG_VERSION 0x0300 #define ID3V2_3_TAG_FLAG_KNOWNFLAGS 0xc0
id3_length_t v2_3_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);
/* 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 & ID3V2_3_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;
extendedflags = 0;
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;
/* header */
if (ptr) header_ptr = *ptr;
size += id3_render_immediate(ptr, "ID3", 3); size += id3_render_int(ptr, ID3V2_3_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_calculate(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; }
/*
780a930,932
if (tag->options & ID3_TAG_OPTION_ID3V2_3) return v2_3_render(tag, buffer);
852c1004 < crc_ptr = *ptr; ---
crc_ptr = *ptr;