47 unsigned long location;
53 enum id3_file_mode mode;
65 ID3_FILE_FLAG_ID3V1 = 0x0001
73signed long query_tag(FILE *iofile)
76 id3_byte_t query[ID3_TAG_QUERYSIZE];
79 if (fgetpos(iofile, &save_position) == -1)
82 size = id3_tag_query(query, fread(query, 1,
sizeof(query), iofile));
84 if (fsetpos(iofile, &save_position) == -1)
95struct id3_tag *read_tag(FILE *iofile, id3_length_t size)
102 if (fread(data, size, 1, iofile) == 1)
103 tag = id3_tag_parse(data, size);
122 if (!(new->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE))
123 id3_tag_clearframes(tag);
126 while ((frame = id3_tag_findframe(
new, 0, i++))) {
127 if (id3_tag_attachframe(tag, frame) == -1)
140int tag_compare(
const void *a,
const void *b)
142 struct filetag const *tag1 = a, *tag2 = b;
144 if (tag1->location < tag2->location)
146 else if (tag1->location > tag2->location)
161 tags = realloc(file->tags, (file->ntags + 1) *
sizeof(*tags));
166 file->tags[file->ntags++] = *
filetag;
171 qsort(file->tags, file->ntags,
sizeof(file->tags[0]), tag_compare);
181void del_filetag(
struct id3_file *file,
unsigned int index)
183 assert(index < file->ntags);
185 while (index < file->ntags - 1) {
186 file->tags[index] = file->tags[index + 1];
205 location = ftell(file->iofile);
211 unsigned long begin1, end1, begin2, end2;
214 end1 = begin1 + length;
216 for (i = 0; i < file->ntags; ++i) {
217 begin2 = file->tags[i].location;
218 end2 = begin2 + file->tags[i].length;
220 if (begin1 == begin2 && end1 == end2)
221 return file->tags[i].tag;
223 if (begin1 < end2 && end1 > begin2)
228 tag = read_tag(file->iofile, length);
234 if (add_filetag(file, &
filetag) == -1 ||
235 update_primary(file->primary, tag) == -1) {
252int search_tags(
struct id3_file *file)
254 fpos_t save_position;
266 if (fgetpos(file->iofile, &save_position) == -1 ||
267 fsetpos(file->iofile, &save_position) == -1)
272 if (fseek(file->iofile, -128, SEEK_END) == 0) {
273 size = query_tag(file->iofile);
277 tag = add_tag(file, size);
281 if (tag && (ID3_TAG_VERSION_MAJOR(id3_tag_version(tag)) == 1))
282 file->flags |= ID3_FILE_FLAG_ID3V1;
288 rewind(file->iofile);
290 size = query_tag(file->iofile);
295 tag = add_tag(file, size);
299 while (tag && (frame = id3_tag_findframe(tag,
"SEEK", 0))) {
302 seek = id3_field_getint(id3_frame_field(frame, 0));
303 if (seek < 0 || fseek(file->iofile, seek, SEEK_CUR) == -1)
306 size = query_tag(file->iofile);
307 tag = (size > 0) ? add_tag(file, size) : 0;
313 if (fseek(file->iofile, ((file->flags & ID3_FILE_FLAG_ID3V1) ? -128 : 0) +
314 -10, SEEK_END) == 0) {
315 size = query_tag(file->iofile);
316 if (size < 0 && fseek(file->iofile, size, SEEK_CUR) == 0) {
317 size = query_tag(file->iofile);
323 clearerr(file->iofile);
327 if (fsetpos(file->iofile, &save_position) == -1)
332 if ((file->ntags > 0 && !(file->flags & ID3_FILE_FLAG_ID3V1)) ||
333 (file->ntags > 1 && (file->flags & ID3_FILE_FLAG_ID3V1))) {
334 if (file->tags[0].location == 0)
335 id3_tag_setlength(file->primary, file->tags[0].length);
337 id3_tag_options(file->primary, ID3_TAG_OPTION_APPENDEDTAG, ~0);
348void finish_file(
struct id3_file *file)
356 id3_tag_delref(file->primary);
357 id3_tag_delete(file->primary);
360 for (i = 0; i < file->ntags; ++i) {
363 tag = file->tags[i].tag;
381struct id3_file *new_file(FILE *iofile,
enum id3_file_mode mode,
386 file = malloc(
sizeof(*file));
390 file->iofile = iofile;
392 file->path = path ? strdup(path) : 0;
399 file->primary = id3_tag_new();
400 if (file->primary == 0)
403 id3_tag_addref(file->primary);
407 if (search_tags(file) == -1)
410 id3_tag_options(file->primary, ID3_TAG_OPTION_ID3V1,
411 (file->flags & ID3_FILE_FLAG_ID3V1) ? ~0 : 0);
428struct id3_file *id3_file_open(
char const *path,
enum id3_file_mode mode)
435 iofile = fopen(path, (mode == ID3_FILE_MODE_READWRITE) ?
"r+b" :
"rb");
439 file = new_file(iofile, mode, path);
450struct id3_file *id3_file_fdopen(
int fd,
enum id3_file_mode mode)
457 iofile = fdopen(fd, (mode == ID3_FILE_MODE_READWRITE) ?
"r+b" :
"rb");
461 file = new_file(iofile, mode, 0);
487int id3_file_close(
struct id3_file *file)
493 if (fclose(file->iofile) == EOF)
509 return file->primary;
518 id3_byte_t
const *data, id3_length_t length)
520 assert(!data || length == 128);
525 if (fseek(file->iofile, (file->flags & ID3_FILE_FLAG_ID3V1) ? -128 : 0,
527 (location = ftell(file->iofile)) == -1 ||
528 fwrite(data, 128, 1, file->iofile) != 1 ||
529 fflush(file->iofile) == EOF)
534 if (!(file->flags & ID3_FILE_FLAG_ID3V1)) {
541 if (add_filetag(file, &
filetag) == -1)
544 file->flags |= ID3_FILE_FLAG_ID3V1;
547# if defined(HAVE_FTRUNCATE)
548 else if (file->flags & ID3_FILE_FLAG_ID3V1) {
551 if (fseek(file->iofile, 0, SEEK_END) == -1)
554 length = ftell(file->iofile);
556 (length >= 0 && length < 128))
564 del_filetag(file, file->ntags - 1);
566 file->flags &= ~ID3_FILE_FLAG_ID3V1;
579 id3_byte_t
const *data, id3_length_t length)
581 assert(!data || length > 0);
584 ((file->ntags == 1 && !(file->flags & ID3_FILE_FLAG_ID3V1)) ||
585 (file->ntags == 2 && (file->flags & ID3_FILE_FLAG_ID3V1))) &&
586 file->tags[0].length == length) {
589 if (fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 ||
590 fwrite(data, length, 1, file->iofile) != 1 ||
591 fflush(file->iofile) == EOF)
609int id3_file_update(
struct id3_file *file)
611 int options, result = 0;
612 id3_length_t v1size = 0, v2size = 0;
613 id3_byte_t id3v1_data[128], *id3v1 = 0, *id3v2 = 0;
617 if (file->mode != ID3_FILE_MODE_READWRITE)
620 options = id3_tag_options(file->primary, 0, 0);
624 if (options & ID3_TAG_OPTION_ID3V1) {
625 v1size = id3_tag_render(file->primary, 0);
627 assert(v1size ==
sizeof(id3v1_data));
629 v1size = id3_tag_render(file->primary, id3v1_data);
631 assert(v1size ==
sizeof(id3v1_data));
639 id3_tag_options(file->primary, ID3_TAG_OPTION_ID3V1, 0);
641 v2size = id3_tag_render(file->primary, 0);
643 id3v2 = malloc(v2size);
647 v2size = id3_tag_render(file->primary, id3v2);
656 if (v2_write(file, id3v2, v2size) == -1 ||
657 v1_write(file, id3v1, v1size) == -1)
660 rewind(file->iofile);
674 id3_tag_options(file->primary, ~0, options);