Mercurial > audlegacy
comparison src/libid3tag/file.c @ 2503:10692383c103 trunk
[svn] first try for libid3tag integration. this improved libid3tag supports vfs operations and is capable of adding id3v2 tag to files which doesn't have id3v2 tag ever.
author | yaz |
---|---|
date | Sun, 11 Feb 2007 05:19:07 -0800 |
parents | |
children | 1e6d2d719876 |
comparison
equal
deleted
inserted
replaced
2502:b7be0af74307 | 2503:10692383c103 |
---|---|
1 /* | |
2 * libid3tag - ID3 tag manipulation library | |
3 * Copyright (C) 2000-2004 Underbit Technologies, Inc. | |
4 * | |
5 * This program is free software; you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License as published by | |
7 * the Free Software Foundation; either version 2 of the License, or | |
8 * (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 * | |
19 * $Id: file.c,v 1.21 2004/01/23 09:41:32 rob Exp $ | |
20 */ | |
21 | |
22 # ifdef HAVE_CONFIG_H | |
23 # include "config.h" | |
24 # endif | |
25 | |
26 # include "global.h" | |
27 | |
28 # include <stdio.h> | |
29 # include <stdlib.h> | |
30 # include <string.h> | |
31 | |
32 # ifdef HAVE_UNISTD_H | |
33 # include <unistd.h> | |
34 # endif | |
35 | |
36 # ifdef HAVE_ASSERT_H | |
37 # include <assert.h> | |
38 # endif | |
39 | |
40 # include "id3tag.h" | |
41 # include "file.h" | |
42 # include "tag.h" | |
43 # include "field.h" | |
44 | |
45 #define AUDACIOUS 1 | |
46 //#undef AUDACIOUS | |
47 | |
48 #ifdef AUDACIOUS | |
49 #undef G_BEGIN_DECLS | |
50 #undef G_END_DECLS | |
51 #include <audacious/vfs.h> | |
52 #else | |
53 #define VFSFile FILE | |
54 #define vfs_fopen fopen | |
55 #define vfs_fclose fclose | |
56 #define vfs_fseek fseek | |
57 #define vfs_ftell ftell | |
58 #define vfs_rewind rewind | |
59 #define vfs_fread fread | |
60 #define vfs_fwrite fwrite | |
61 #define vfs_truncate(x, y) ftruncate((fileno(x)), (y)) | |
62 #endif | |
63 | |
64 struct filetag { | |
65 struct id3_tag *tag; | |
66 unsigned long location; | |
67 id3_length_t length; | |
68 }; | |
69 | |
70 struct id3_file { | |
71 VFSFile *iofile; | |
72 enum id3_file_mode mode; | |
73 char *path; | |
74 | |
75 int flags; | |
76 | |
77 struct id3_tag *primary; | |
78 | |
79 unsigned int ntags; | |
80 struct filetag *tags; | |
81 }; | |
82 | |
83 enum { | |
84 ID3_FILE_FLAG_ID3V1 = 0x0001 | |
85 }; | |
86 | |
87 /* | |
88 * NAME: query_tag() | |
89 * DESCRIPTION: check for a tag at a file's current position | |
90 */ | |
91 static | |
92 signed long query_tag(VFSFile *iofile) | |
93 { | |
94 int save_position; | |
95 id3_byte_t query[ID3_TAG_QUERYSIZE]; | |
96 signed long size; | |
97 | |
98 save_position = vfs_ftell(iofile); | |
99 if (save_position == -1) | |
100 return 0; | |
101 | |
102 size = id3_tag_query(query, vfs_fread(query, 1, sizeof(query), iofile)); | |
103 | |
104 if(vfs_fseek(iofile, save_position, SEEK_SET) == -1) | |
105 return 0; | |
106 | |
107 return size; | |
108 } | |
109 | |
110 /* | |
111 * NAME: read_tag() | |
112 * DESCRIPTION: read and parse a tag at a file's current position | |
113 */ | |
114 static | |
115 struct id3_tag *read_tag(VFSFile *iofile, id3_length_t size) | |
116 { | |
117 id3_byte_t *data; | |
118 struct id3_tag *tag = 0; | |
119 | |
120 data = malloc(size); | |
121 if (data) { | |
122 if (vfs_fread(data, size, 1, iofile) == 1) | |
123 tag = id3_tag_parse(data, size); | |
124 | |
125 free(data); | |
126 } | |
127 | |
128 return tag; | |
129 } | |
130 | |
131 /* | |
132 * NAME: update_primary() | |
133 * DESCRIPTION: update the primary tag with data from a new tag | |
134 */ | |
135 static | |
136 int update_primary(struct id3_tag *tag, struct id3_tag const *new) | |
137 { | |
138 unsigned int i; | |
139 struct id3_frame *frame; | |
140 | |
141 if (new) { | |
142 if (!(new->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)) | |
143 id3_tag_clearframes(tag); | |
144 | |
145 i = 0; | |
146 while ((frame = id3_tag_findframe(new, 0, i++))) { | |
147 if (id3_tag_attachframe(tag, frame) == -1) | |
148 return -1; | |
149 } | |
150 } | |
151 | |
152 return 0; | |
153 } | |
154 | |
155 /* | |
156 * NAME: tag_compare() | |
157 * DESCRIPTION: tag sort function for qsort() | |
158 */ | |
159 static | |
160 int tag_compare(const void *a, const void *b) | |
161 { | |
162 struct filetag const *tag1 = a, *tag2 = b; | |
163 | |
164 if (tag1->location < tag2->location) | |
165 return -1; | |
166 else if (tag1->location > tag2->location) | |
167 return +1; | |
168 | |
169 return 0; | |
170 } | |
171 | |
172 /* | |
173 * NAME: add_filetag() | |
174 * DESCRIPTION: add a new file tag entry | |
175 */ | |
176 static | |
177 int add_filetag(struct id3_file *file, struct filetag const *filetag) | |
178 { | |
179 struct filetag *tags; | |
180 | |
181 tags = realloc(file->tags, (file->ntags + 1) * sizeof(*tags)); | |
182 if (tags == 0) | |
183 return -1; | |
184 | |
185 file->tags = tags; | |
186 file->tags[file->ntags++] = *filetag; | |
187 | |
188 /* sort tags by location */ | |
189 | |
190 if (file->ntags > 1) | |
191 qsort(file->tags, file->ntags, sizeof(file->tags[0]), tag_compare); | |
192 | |
193 return 0; | |
194 } | |
195 | |
196 /* | |
197 * NAME: del_filetag() | |
198 * DESCRIPTION: delete a file tag entry | |
199 */ | |
200 static | |
201 void del_filetag(struct id3_file *file, unsigned int index) | |
202 { | |
203 assert(index < file->ntags); | |
204 | |
205 while (index < file->ntags - 1) { | |
206 file->tags[index] = file->tags[index + 1]; | |
207 ++index; | |
208 } | |
209 | |
210 --file->ntags; | |
211 } | |
212 | |
213 /* | |
214 * NAME: add_tag() | |
215 * DESCRIPTION: read, parse, and add a tag to a file structure | |
216 */ | |
217 static | |
218 struct id3_tag *add_tag(struct id3_file *file, id3_length_t length) | |
219 { | |
220 long location; | |
221 unsigned int i; | |
222 struct filetag filetag; | |
223 struct id3_tag *tag; | |
224 | |
225 location = vfs_ftell(file->iofile); | |
226 if (location == -1) | |
227 return 0; | |
228 | |
229 /* check for duplication/overlap */ | |
230 { | |
231 unsigned long begin1, end1, begin2, end2; | |
232 | |
233 begin1 = location; | |
234 end1 = begin1 + length; | |
235 | |
236 for (i = 0; i < file->ntags; ++i) { | |
237 begin2 = file->tags[i].location; | |
238 end2 = begin2 + file->tags[i].length; | |
239 | |
240 if (begin1 == begin2 && end1 == end2) | |
241 return file->tags[i].tag; /* duplicate */ | |
242 | |
243 if (begin1 < end2 && end1 > begin2) | |
244 return 0; /* overlap */ | |
245 } | |
246 } | |
247 | |
248 tag = read_tag(file->iofile, length); | |
249 | |
250 filetag.tag = tag; | |
251 filetag.location = location; | |
252 filetag.length = length; | |
253 | |
254 if (add_filetag(file, &filetag) == -1 || | |
255 update_primary(file->primary, tag) == -1) { | |
256 if (tag) | |
257 id3_tag_delete(tag); | |
258 return 0; | |
259 } | |
260 | |
261 if (tag) | |
262 id3_tag_addref(tag); | |
263 | |
264 return tag; | |
265 } | |
266 | |
267 /* | |
268 * NAME: search_tags() | |
269 * DESCRIPTION: search for tags in a file | |
270 */ | |
271 //static | |
272 int search_tags(struct id3_file *file) | |
273 { | |
274 int save_position; | |
275 signed long size; | |
276 | |
277 /* | |
278 * save the current seek position | |
279 * | |
280 * We also verify the stream is seekable by calling fsetpos(), since | |
281 * fgetpos() alone is not reliable enough for this purpose. | |
282 * | |
283 * [Apparently not even fsetpos() is sufficient under Win32.] | |
284 */ | |
285 | |
286 // if (fgetpos(file->iofile, &save_position) == -1 || | |
287 // fsetpos(file->iofile, &save_position) == -1) | |
288 // if (save_position = vfs_ftell(file->iofile) == -1 || | |
289 // vfs_fseek(file->iofile, save_position, SEEK_SET) == -1) | |
290 if((save_position = vfs_ftell(file->iofile)) == -1) | |
291 return -1; | |
292 | |
293 /* look for an ID3v1 tag */ | |
294 | |
295 if (vfs_fseek(file->iofile, -128, SEEK_END) == 0) { | |
296 size = query_tag(file->iofile); | |
297 if (size > 0) { | |
298 struct id3_tag const *tag; | |
299 | |
300 tag = add_tag(file, size); | |
301 | |
302 /* if this is indeed an ID3v1 tag, mark the file so */ | |
303 | |
304 if (tag && (ID3_TAG_VERSION_MAJOR(id3_tag_version(tag)) == 1)) | |
305 file->flags |= ID3_FILE_FLAG_ID3V1; | |
306 } | |
307 } | |
308 | |
309 /* look for a tag at the beginning of the file */ | |
310 | |
311 vfs_rewind(file->iofile); | |
312 | |
313 size = query_tag(file->iofile); | |
314 if (size > 0) { | |
315 struct id3_tag const *tag; | |
316 struct id3_frame const *frame; | |
317 | |
318 tag = add_tag(file, size); | |
319 | |
320 /* locate tags indicated by SEEK frames */ | |
321 | |
322 while (tag && (frame = id3_tag_findframe(tag, "SEEK", 0))) { | |
323 long seek; | |
324 | |
325 seek = id3_field_getint(id3_frame_field(frame, 0)); | |
326 if (seek < 0 || vfs_fseek(file->iofile, seek, SEEK_CUR) == -1) | |
327 break; | |
328 | |
329 size = query_tag(file->iofile); | |
330 tag = (size > 0) ? add_tag(file, size) : 0; | |
331 } | |
332 } | |
333 | |
334 /* look for a tag at the end of the file (before any ID3v1 tag) */ | |
335 | |
336 if (vfs_fseek(file->iofile, ((file->flags & ID3_FILE_FLAG_ID3V1) ? -128 : 0) + | |
337 -10, SEEK_END) == 0) { | |
338 size = query_tag(file->iofile); | |
339 if (size < 0 && vfs_fseek(file->iofile, size, SEEK_CUR) == 0) { | |
340 size = query_tag(file->iofile); | |
341 if (size > 0) | |
342 add_tag(file, size); | |
343 } | |
344 } | |
345 | |
346 #ifndef AUDACIOUS | |
347 clearerr(file->iofile); | |
348 #endif | |
349 | |
350 /* restore seek position */ | |
351 | |
352 // if (fsetpos(file->iofile, &save_position) == -1) | |
353 if (vfs_fseek(file->iofile, save_position, SEEK_SET) == -1) | |
354 return -1; | |
355 | |
356 /* set primary tag options and target padded length for convenience */ | |
357 | |
358 if ((file->ntags > 0 && !(file->flags & ID3_FILE_FLAG_ID3V1)) || | |
359 (file->ntags > 1 && (file->flags & ID3_FILE_FLAG_ID3V1))) { | |
360 if (file->tags[0].location == 0) | |
361 id3_tag_setlength(file->primary, file->tags[0].length); | |
362 else | |
363 id3_tag_options(file->primary, ID3_TAG_OPTION_APPENDEDTAG, ~0); | |
364 } | |
365 | |
366 return 0; | |
367 } | |
368 | |
369 /* | |
370 * NAME: finish_file() | |
371 * DESCRIPTION: release memory associated with a file | |
372 */ | |
373 static | |
374 void finish_file(struct id3_file *file) | |
375 { | |
376 unsigned int i; | |
377 | |
378 if (file->path) | |
379 free(file->path); | |
380 | |
381 if (file->primary) { | |
382 id3_tag_delref(file->primary); | |
383 id3_tag_delete(file->primary); | |
384 } | |
385 | |
386 for (i = 0; i < file->ntags; ++i) { | |
387 struct id3_tag *tag; | |
388 | |
389 tag = file->tags[i].tag; | |
390 if (tag) { | |
391 id3_tag_delref(tag); | |
392 id3_tag_delete(tag); | |
393 } | |
394 } | |
395 | |
396 if (file->tags) | |
397 free(file->tags); | |
398 | |
399 free(file); | |
400 } | |
401 | |
402 /* | |
403 * NAME: new_file() | |
404 * DESCRIPTION: create a new file structure and load tags | |
405 */ | |
406 static | |
407 struct id3_file *new_file(VFSFile *iofile, enum id3_file_mode mode, | |
408 char const *path) | |
409 { | |
410 struct id3_file *file; | |
411 | |
412 file = malloc(sizeof(*file)); | |
413 if (file == 0) | |
414 goto fail; | |
415 | |
416 file->iofile = iofile; | |
417 file->mode = mode; | |
418 file->path = path ? strdup(path) : 0; | |
419 | |
420 file->flags = 0; | |
421 | |
422 file->ntags = 0; | |
423 file->tags = 0; | |
424 | |
425 file->primary = id3_tag_new(); | |
426 if (file->primary == 0) | |
427 goto fail; | |
428 | |
429 id3_tag_addref(file->primary); | |
430 | |
431 /* load tags from the file */ | |
432 | |
433 if (search_tags(file) == -1) | |
434 goto fail; | |
435 | |
436 id3_tag_options(file->primary, ID3_TAG_OPTION_ID3V1, | |
437 (file->flags & ID3_FILE_FLAG_ID3V1) ? ~0 : 0); | |
438 | |
439 if (0) { | |
440 fail: | |
441 if (file) { | |
442 finish_file(file); | |
443 file = 0; | |
444 } | |
445 } | |
446 | |
447 return file; | |
448 } | |
449 | |
450 | |
451 /* | |
452 * NAME: file->open() | |
453 * DESCRIPTION: open a file given its pathname | |
454 */ | |
455 struct id3_file *id3_file_open(char const *path, enum id3_file_mode mode) | |
456 { | |
457 VFSFile *iofile; | |
458 struct id3_file *file; | |
459 | |
460 assert(path); | |
461 | |
462 iofile = vfs_fopen(path, (mode == ID3_FILE_MODE_READWRITE) ? "r+b" : "rb"); | |
463 if (iofile == 0){ | |
464 printf("id3_file_open: iofile failed\n"); | |
465 return 0; | |
466 } | |
467 file = new_file(iofile, mode, path); | |
468 if (file == 0){ | |
469 printf("id3_file_open: file failed\n"); | |
470 vfs_fclose(iofile); | |
471 } | |
472 | |
473 return file; | |
474 } | |
475 | |
476 /* | |
477 * NAME: file->fdopen() | |
478 * DESCRIPTION: open a file using an existing file descriptor | |
479 */ | |
480 #ifndef AUDACIOUS | |
481 struct id3_file *id3_file_fdopen(int fd, enum id3_file_mode mode) | |
482 { | |
483 # if 1 || defined(HAVE_UNISTD_H) | |
484 VFSFile *iofile; | |
485 struct id3_file *file; | |
486 | |
487 iofile = fdopen(fd, (mode == ID3_FILE_MODE_READWRITE) ? "r+b" : "rb"); | |
488 if (iofile == 0) | |
489 return 0; | |
490 | |
491 file = new_file(iofile, mode, 0); | |
492 if (file == 0) { | |
493 int save_fd; | |
494 | |
495 /* close iofile without closing fd */ | |
496 | |
497 save_fd = dup(fd); | |
498 | |
499 fclose(iofile); | |
500 | |
501 dup2(save_fd, fd); | |
502 close(save_fd); | |
503 } | |
504 | |
505 return file; | |
506 # else | |
507 return 0; | |
508 # endif | |
509 } | |
510 #endif | |
511 | |
512 /* | |
513 * NAME: file->close() | |
514 * DESCRIPTION: close a file and delete its associated tags | |
515 */ | |
516 int id3_file_close(struct id3_file *file) | |
517 { | |
518 int result = 0; | |
519 | |
520 assert(file); | |
521 | |
522 if (vfs_fclose(file->iofile) == EOF) | |
523 result = -1; | |
524 | |
525 finish_file(file); | |
526 | |
527 return result; | |
528 } | |
529 | |
530 /* | |
531 * NAME: file->tag() | |
532 * DESCRIPTION: return the primary tag structure for a file | |
533 */ | |
534 struct id3_tag *id3_file_tag(struct id3_file const *file) | |
535 { | |
536 assert(file); | |
537 | |
538 return file->primary; | |
539 } | |
540 | |
541 /* | |
542 * NAME: v1_write() | |
543 * DESCRIPTION: write ID3v1 tag modifications to a file | |
544 */ | |
545 static | |
546 int v1_write(struct id3_file *file, | |
547 id3_byte_t const *data, id3_length_t length) | |
548 { | |
549 assert(!data || length == 128); | |
550 | |
551 if (data) { | |
552 long location; | |
553 | |
554 if (vfs_fseek(file->iofile, (file->flags & ID3_FILE_FLAG_ID3V1) ? -128 : 0, | |
555 SEEK_END) == -1 || | |
556 (location = vfs_ftell(file->iofile)) == -1 || | |
557 #ifdef AUDACIOUS | |
558 vfs_fwrite(data, 128, 1, file->iofile) != 1 ) | |
559 #else | |
560 vfs_fwrite(data, 128, 1, file->iofile) != 1 || | |
561 fflush(file->iofile) == EOF) //XXX | |
562 #endif | |
563 return -1; | |
564 | |
565 /* add file tag reference */ | |
566 | |
567 if (!(file->flags & ID3_FILE_FLAG_ID3V1)) { | |
568 struct filetag filetag; | |
569 | |
570 filetag.tag = 0; | |
571 filetag.location = location; | |
572 filetag.length = 128; | |
573 | |
574 if (add_filetag(file, &filetag) == -1) | |
575 return -1; | |
576 | |
577 file->flags |= ID3_FILE_FLAG_ID3V1; | |
578 } | |
579 } | |
580 # if defined(HAVE_FTRUNCATE) | |
581 else if (file->flags & ID3_FILE_FLAG_ID3V1) { | |
582 long length; | |
583 | |
584 if (vfs_fseek(file->iofile, 0, SEEK_END) == -1) | |
585 return -1; | |
586 | |
587 length = vfs_ftell(file->iofile); | |
588 if (length == -1 || | |
589 (length >= 0 && length < 128)) | |
590 return -1; | |
591 | |
592 // if (ftruncate(fileno(file->iofile), length - 128) == -1) //XXX | |
593 if (vfs_truncate(file->iofile, length - 128) == -1) | |
594 return -1; | |
595 | |
596 /* delete file tag reference */ | |
597 | |
598 del_filetag(file, file->ntags - 1); | |
599 | |
600 file->flags &= ~ID3_FILE_FLAG_ID3V1; | |
601 } | |
602 # endif | |
603 | |
604 return 0; | |
605 } | |
606 | |
607 /* | |
608 * NAME: v2_write() | |
609 * DESCRIPTION: write ID3v2 tag modifications to a file | |
610 */ | |
611 static | |
612 int v2_write(struct id3_file *file, | |
613 id3_byte_t const *data, id3_length_t length) | |
614 { | |
615 assert(!data || length > 0); | |
616 | |
617 // append a new id3v2 tag to the file which doesn't have any tag or only have v1tag. | |
618 if(data && | |
619 ((file->ntags == 0) || // no tag | |
620 (file->ntags == 1 && (file->flags & ID3_FILE_FLAG_ID3V1))) ) { // only v1 tag exists | |
621 | |
622 struct filetag filetag; | |
623 | |
624 printf("append v2tag\n"); | |
625 | |
626 filetag.tag = 0; | |
627 filetag.location = 0; // begining of the file. | |
628 filetag.length = 0; | |
629 | |
630 if(add_filetag(file, &filetag) == -1) | |
631 return -1; | |
632 | |
633 if(file->ntags == 1) | |
634 file->flags = 0; | |
635 if(file->ntags == 2) | |
636 file->flags |= ID3_FILE_FLAG_ID3V1; | |
637 } | |
638 | |
639 if (!data | |
640 || (!(file->ntags == 1 && !(file->flags & ID3_FILE_FLAG_ID3V1)) && | |
641 !(file->ntags == 2 && (file->flags & ID3_FILE_FLAG_ID3V1)))) { | |
642 /* no v2 tag. nothing to do */ | |
643 goto done; | |
644 } | |
645 | |
646 if (file->tags[0].length == length) { | |
647 /* easy special case: rewrite existing tag in-place */ | |
648 | |
649 if (vfs_fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 || | |
650 #ifdef AUDACIOUS | |
651 vfs_fwrite(data, length, 1, file->iofile) != 1) | |
652 #else | |
653 vfs_fwrite(data, length, 1, file->iofile) != 1 || | |
654 fflush(file->iofile) == EOF) | |
655 #endif | |
656 return -1; | |
657 | |
658 goto done; | |
659 } else { | |
660 /* the new tag has a different size */ | |
661 int file_size; | |
662 int remainder_size; | |
663 char *remainder; | |
664 | |
665 /* read in the remainder of the file */ | |
666 vfs_fseek(file->iofile, 0, SEEK_END); | |
667 file_size = vfs_ftell(file->iofile); | |
668 remainder_size = file_size - file->tags[0].location - file->tags[0].length; | |
669 remainder = (char*)malloc(remainder_size); | |
670 if (vfs_fseek(file->iofile, file->tags[0].location + file->tags[0].length, SEEK_SET) == -1 || | |
671 vfs_fread(remainder, remainder_size, 1, file->iofile) != 1) { | |
672 free(remainder); | |
673 return -1; | |
674 } | |
675 | |
676 /* write the tag where the old one was */ | |
677 if (vfs_fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 || | |
678 vfs_fwrite(data, length, 1, file->iofile) != 1) { | |
679 free(remainder); | |
680 return -1; | |
681 } | |
682 | |
683 /* write the reaminder */ | |
684 if (vfs_fwrite(remainder, remainder_size, 1, file->iofile) != 1) { | |
685 free(remainder); | |
686 return -1; | |
687 } | |
688 | |
689 free(remainder); | |
690 | |
691 /* flush the FILE */ | |
692 #ifndef AUDACIOUS | |
693 if (fflush(file->iofile) == EOF) | |
694 return -1; | |
695 #endif | |
696 /* truncate if required */ | |
697 if (vfs_ftell(file->iofile) < file_size) | |
698 vfs_truncate(file->iofile, vfs_ftell(file->iofile)); | |
699 } | |
700 | |
701 done: | |
702 return 0; | |
703 } | |
704 | |
705 /* | |
706 * NAME: file->update() | |
707 * DESCRIPTION: rewrite tag(s) to a file | |
708 */ | |
709 int id3_file_update(struct id3_file *file) | |
710 { | |
711 int options, result = 0; | |
712 id3_length_t v1size = 0, v2size = 0; | |
713 id3_byte_t id3v1_data[128], *id3v1 = 0, *id3v2 = 0; | |
714 | |
715 assert(file); | |
716 | |
717 if (file->mode != ID3_FILE_MODE_READWRITE) | |
718 return -1; | |
719 | |
720 options = id3_tag_options(file->primary, 0, 0); | |
721 | |
722 /* render ID3v1 */ | |
723 | |
724 if (options & ID3_TAG_OPTION_ID3V1) { | |
725 v1size = id3_tag_render(file->primary, 0); | |
726 if (v1size) { | |
727 assert(v1size == sizeof(id3v1_data)); | |
728 | |
729 v1size = id3_tag_render(file->primary, id3v1_data); | |
730 if (v1size) { | |
731 assert(v1size == sizeof(id3v1_data)); | |
732 id3v1 = id3v1_data; | |
733 } | |
734 } | |
735 } | |
736 | |
737 /* render ID3v2 */ | |
738 | |
739 id3_tag_options(file->primary, ID3_TAG_OPTION_ID3V1, 0); | |
740 | |
741 v2size = id3_tag_render(file->primary, 0); | |
742 if (v2size) { | |
743 id3v2 = malloc(v2size); | |
744 if (id3v2 == 0) | |
745 goto fail; | |
746 | |
747 v2size = id3_tag_render(file->primary, id3v2); | |
748 if (v2size == 0) { | |
749 free(id3v2); | |
750 id3v2 = 0; | |
751 } | |
752 } | |
753 | |
754 /* write tags */ | |
755 | |
756 if (v2_write(file, id3v2, v2size) == -1 || | |
757 v1_write(file, id3v1, v1size) == -1) | |
758 goto fail; | |
759 | |
760 vfs_rewind(file->iofile); | |
761 | |
762 /* update file tags array? ... */ | |
763 | |
764 if (0) { | |
765 fail: | |
766 result = -1; | |
767 } | |
768 | |
769 /* clean up; restore tag options */ | |
770 | |
771 if (id3v2) | |
772 free(id3v2); | |
773 | |
774 id3_tag_options(file->primary, ~0, options); | |
775 | |
776 return result; | |
777 } |