Mercurial > hg > sv-dependency-builds
comparison src/libid3tag-0.15.1b/tag.c @ 85:545efbb81310
Import initial set of sources
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 18 Mar 2013 14:12:14 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 85:545efbb81310 |
---|---|
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: tag.c,v 1.20 2004/02/17 02:04:10 rob Exp $ | |
20 */ | |
21 | |
22 # ifdef HAVE_CONFIG_H | |
23 # include "config.h" | |
24 # endif | |
25 | |
26 # include "global.h" | |
27 | |
28 # include <string.h> | |
29 # include <stdlib.h> | |
30 | |
31 # ifdef HAVE_ASSERT_H | |
32 # include <assert.h> | |
33 # endif | |
34 | |
35 # include "id3tag.h" | |
36 # include "tag.h" | |
37 # include "frame.h" | |
38 # include "compat.h" | |
39 # include "parse.h" | |
40 # include "render.h" | |
41 # include "latin1.h" | |
42 # include "ucs4.h" | |
43 # include "genre.h" | |
44 # include "crc.h" | |
45 # include "field.h" | |
46 # include "util.h" | |
47 | |
48 /* | |
49 * NAME: tag->new() | |
50 * DESCRIPTION: allocate and return a new, empty tag | |
51 */ | |
52 struct id3_tag *id3_tag_new(void) | |
53 { | |
54 struct id3_tag *tag; | |
55 | |
56 tag = malloc(sizeof(*tag)); | |
57 if (tag) { | |
58 tag->refcount = 0; | |
59 tag->version = ID3_TAG_VERSION; | |
60 tag->flags = 0; | |
61 tag->extendedflags = 0; | |
62 tag->restrictions = 0; | |
63 tag->options = /* ID3_TAG_OPTION_UNSYNCHRONISATION | */ | |
64 ID3_TAG_OPTION_COMPRESSION | ID3_TAG_OPTION_CRC; | |
65 tag->nframes = 0; | |
66 tag->frames = 0; | |
67 tag->paddedsize = 0; | |
68 } | |
69 | |
70 return tag; | |
71 } | |
72 | |
73 /* | |
74 * NAME: tag->delete() | |
75 * DESCRIPTION: destroy a tag and deallocate all associated memory | |
76 */ | |
77 void id3_tag_delete(struct id3_tag *tag) | |
78 { | |
79 assert(tag); | |
80 | |
81 if (tag->refcount == 0) { | |
82 id3_tag_clearframes(tag); | |
83 | |
84 if (tag->frames) | |
85 free(tag->frames); | |
86 | |
87 free(tag); | |
88 } | |
89 } | |
90 | |
91 /* | |
92 * NAME: tag->addref() | |
93 * DESCRIPTION: add an external reference to a tag | |
94 */ | |
95 void id3_tag_addref(struct id3_tag *tag) | |
96 { | |
97 assert(tag); | |
98 | |
99 ++tag->refcount; | |
100 } | |
101 | |
102 /* | |
103 * NAME: tag->delref() | |
104 * DESCRIPTION: remove an external reference to a tag | |
105 */ | |
106 void id3_tag_delref(struct id3_tag *tag) | |
107 { | |
108 assert(tag && tag->refcount > 0); | |
109 | |
110 --tag->refcount; | |
111 } | |
112 | |
113 /* | |
114 * NAME: tag->version() | |
115 * DESCRIPTION: return the tag's original ID3 version number | |
116 */ | |
117 unsigned int id3_tag_version(struct id3_tag const *tag) | |
118 { | |
119 assert(tag); | |
120 | |
121 return tag->version; | |
122 } | |
123 | |
124 /* | |
125 * NAME: tag->options() | |
126 * DESCRIPTION: get or set tag options | |
127 */ | |
128 int id3_tag_options(struct id3_tag *tag, int mask, int values) | |
129 { | |
130 assert(tag); | |
131 | |
132 if (mask) | |
133 tag->options = (tag->options & ~mask) | (values & mask); | |
134 | |
135 return tag->options; | |
136 } | |
137 | |
138 /* | |
139 * NAME: tag->setlength() | |
140 * DESCRIPTION: set the minimum rendered tag size | |
141 */ | |
142 void id3_tag_setlength(struct id3_tag *tag, id3_length_t length) | |
143 { | |
144 assert(tag); | |
145 | |
146 tag->paddedsize = length; | |
147 } | |
148 | |
149 /* | |
150 * NAME: tag->clearframes() | |
151 * DESCRIPTION: detach and delete all frames associated with a tag | |
152 */ | |
153 void id3_tag_clearframes(struct id3_tag *tag) | |
154 { | |
155 unsigned int i; | |
156 | |
157 assert(tag); | |
158 | |
159 for (i = 0; i < tag->nframes; ++i) { | |
160 id3_frame_delref(tag->frames[i]); | |
161 id3_frame_delete(tag->frames[i]); | |
162 } | |
163 | |
164 tag->nframes = 0; | |
165 } | |
166 | |
167 /* | |
168 * NAME: tag->attachframe() | |
169 * DESCRIPTION: attach a frame to a tag | |
170 */ | |
171 int id3_tag_attachframe(struct id3_tag *tag, struct id3_frame *frame) | |
172 { | |
173 struct id3_frame **frames; | |
174 | |
175 assert(tag && frame); | |
176 | |
177 frames = realloc(tag->frames, (tag->nframes + 1) * sizeof(*frames)); | |
178 if (frames == 0) | |
179 return -1; | |
180 | |
181 tag->frames = frames; | |
182 tag->frames[tag->nframes++] = frame; | |
183 | |
184 id3_frame_addref(frame); | |
185 | |
186 return 0; | |
187 } | |
188 | |
189 /* | |
190 * NAME: tag->detachframe() | |
191 * DESCRIPTION: detach (but don't delete) a frame from a tag | |
192 */ | |
193 int id3_tag_detachframe(struct id3_tag *tag, struct id3_frame *frame) | |
194 { | |
195 unsigned int i; | |
196 | |
197 assert(tag && frame); | |
198 | |
199 for (i = 0; i < tag->nframes; ++i) { | |
200 if (tag->frames[i] == frame) | |
201 break; | |
202 } | |
203 | |
204 if (i == tag->nframes) | |
205 return -1; | |
206 | |
207 --tag->nframes; | |
208 while (i++ < tag->nframes) | |
209 tag->frames[i - 1] = tag->frames[i]; | |
210 | |
211 id3_frame_delref(frame); | |
212 | |
213 return 0; | |
214 } | |
215 | |
216 /* | |
217 * NAME: tag->findframe() | |
218 * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID | |
219 */ | |
220 struct id3_frame *id3_tag_findframe(struct id3_tag const *tag, | |
221 char const *id, unsigned int index) | |
222 { | |
223 unsigned int len, i; | |
224 | |
225 assert(tag); | |
226 | |
227 if (id == 0 || *id == 0) | |
228 return (index < tag->nframes) ? tag->frames[index] : 0; | |
229 | |
230 len = strlen(id); | |
231 | |
232 if (len == 4) { | |
233 struct id3_compat const *compat; | |
234 | |
235 compat = id3_compat_lookup(id, len); | |
236 if (compat && compat->equiv && !compat->translate) { | |
237 id = compat->equiv; | |
238 len = strlen(id); | |
239 } | |
240 } | |
241 | |
242 for (i = 0; i < tag->nframes; ++i) { | |
243 if (strncmp(tag->frames[i]->id, id, len) == 0 && index-- == 0) | |
244 return tag->frames[i]; | |
245 } | |
246 | |
247 return 0; | |
248 } | |
249 | |
250 enum tagtype { | |
251 TAGTYPE_NONE = 0, | |
252 TAGTYPE_ID3V1, | |
253 TAGTYPE_ID3V2, | |
254 TAGTYPE_ID3V2_FOOTER | |
255 }; | |
256 | |
257 static | |
258 enum tagtype tagtype(id3_byte_t const *data, id3_length_t length) | |
259 { | |
260 if (length >= 3 && | |
261 data[0] == 'T' && data[1] == 'A' && data[2] == 'G') | |
262 return TAGTYPE_ID3V1; | |
263 | |
264 if (length >= 10 && | |
265 ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') || | |
266 (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) && | |
267 data[3] < 0xff && data[4] < 0xff && | |
268 data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80) | |
269 return data[0] == 'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER; | |
270 | |
271 return TAGTYPE_NONE; | |
272 } | |
273 | |
274 static | |
275 void parse_header(id3_byte_t const **ptr, | |
276 unsigned int *version, int *flags, id3_length_t *size) | |
277 { | |
278 *ptr += 3; | |
279 | |
280 *version = id3_parse_uint(ptr, 2); | |
281 *flags = id3_parse_uint(ptr, 1); | |
282 *size = id3_parse_syncsafe(ptr, 4); | |
283 } | |
284 | |
285 /* | |
286 * NAME: tag->query() | |
287 * DESCRIPTION: if a tag begins at the given location, return its size | |
288 */ | |
289 signed long id3_tag_query(id3_byte_t const *data, id3_length_t length) | |
290 { | |
291 unsigned int version; | |
292 int flags; | |
293 id3_length_t size; | |
294 | |
295 assert(data); | |
296 | |
297 switch (tagtype(data, length)) { | |
298 case TAGTYPE_ID3V1: | |
299 return 128; | |
300 | |
301 case TAGTYPE_ID3V2: | |
302 parse_header(&data, &version, &flags, &size); | |
303 | |
304 if (flags & ID3_TAG_FLAG_FOOTERPRESENT) | |
305 size += 10; | |
306 | |
307 return 10 + size; | |
308 | |
309 case TAGTYPE_ID3V2_FOOTER: | |
310 parse_header(&data, &version, &flags, &size); | |
311 return -size - 10; | |
312 | |
313 case TAGTYPE_NONE: | |
314 break; | |
315 } | |
316 | |
317 return 0; | |
318 } | |
319 | |
320 static | |
321 void trim(char *str) | |
322 { | |
323 char *ptr; | |
324 | |
325 ptr = str + strlen(str); | |
326 while (ptr > str && ptr[-1] == ' ') | |
327 --ptr; | |
328 | |
329 *ptr = 0; | |
330 } | |
331 | |
332 static | |
333 int v1_attachstr(struct id3_tag *tag, char const *id, | |
334 char *text, unsigned long number) | |
335 { | |
336 struct id3_frame *frame; | |
337 id3_ucs4_t ucs4[31]; | |
338 | |
339 if (text) { | |
340 trim(text); | |
341 if (*text == 0) | |
342 return 0; | |
343 } | |
344 | |
345 frame = id3_frame_new(id); | |
346 if (frame == 0) | |
347 return -1; | |
348 | |
349 if (id3_field_settextencoding(&frame->fields[0], | |
350 ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1) | |
351 goto fail; | |
352 | |
353 if (text) | |
354 id3_latin1_decode(text, ucs4); | |
355 else | |
356 id3_ucs4_putnumber(ucs4, number); | |
357 | |
358 if (strcmp(id, ID3_FRAME_COMMENT) == 0) { | |
359 if (id3_field_setlanguage(&frame->fields[1], "XXX") == -1 || | |
360 id3_field_setstring(&frame->fields[2], id3_ucs4_empty) == -1 || | |
361 id3_field_setfullstring(&frame->fields[3], ucs4) == -1) | |
362 goto fail; | |
363 } | |
364 else { | |
365 id3_ucs4_t *ptr = ucs4; | |
366 | |
367 if (id3_field_setstrings(&frame->fields[1], 1, &ptr) == -1) | |
368 goto fail; | |
369 } | |
370 | |
371 if (id3_tag_attachframe(tag, frame) == -1) | |
372 goto fail; | |
373 | |
374 return 0; | |
375 | |
376 fail: | |
377 id3_frame_delete(frame); | |
378 return -1; | |
379 } | |
380 | |
381 static | |
382 struct id3_tag *v1_parse(id3_byte_t const *data) | |
383 { | |
384 struct id3_tag *tag; | |
385 | |
386 tag = id3_tag_new(); | |
387 if (tag) { | |
388 char title[31], artist[31], album[31], year[5], comment[31]; | |
389 unsigned int genre, track; | |
390 | |
391 tag->version = 0x0100; | |
392 | |
393 tag->options |= ID3_TAG_OPTION_ID3V1; | |
394 tag->options &= ~ID3_TAG_OPTION_COMPRESSION; | |
395 | |
396 tag->restrictions = | |
397 ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 | | |
398 ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS; | |
399 | |
400 title[30] = artist[30] = album[30] = year[4] = comment[30] = 0; | |
401 | |
402 memcpy(title, &data[3], 30); | |
403 memcpy(artist, &data[33], 30); | |
404 memcpy(album, &data[63], 30); | |
405 memcpy(year, &data[93], 4); | |
406 memcpy(comment, &data[97], 30); | |
407 | |
408 genre = data[127]; | |
409 | |
410 track = 0; | |
411 if (comment[28] == 0 && comment[29] != 0) { | |
412 track = comment[29]; | |
413 tag->version = 0x0101; | |
414 } | |
415 | |
416 /* populate tag frames */ | |
417 | |
418 if (v1_attachstr(tag, ID3_FRAME_TITLE, title, 0) == -1 || | |
419 v1_attachstr(tag, ID3_FRAME_ARTIST, artist, 0) == -1 || | |
420 v1_attachstr(tag, ID3_FRAME_ALBUM, album, 0) == -1 || | |
421 v1_attachstr(tag, ID3_FRAME_YEAR, year, 0) == -1 || | |
422 (track && v1_attachstr(tag, ID3_FRAME_TRACK, 0, track) == -1) || | |
423 (genre < 0xff && v1_attachstr(tag, ID3_FRAME_GENRE, 0, genre) == -1) || | |
424 v1_attachstr(tag, ID3_FRAME_COMMENT, comment, 0) == -1) { | |
425 id3_tag_delete(tag); | |
426 tag = 0; | |
427 } | |
428 } | |
429 | |
430 return tag; | |
431 } | |
432 | |
433 static | |
434 struct id3_tag *v2_parse(id3_byte_t const *ptr) | |
435 { | |
436 struct id3_tag *tag; | |
437 id3_byte_t *mem = 0; | |
438 | |
439 tag = id3_tag_new(); | |
440 if (tag) { | |
441 id3_byte_t const *end; | |
442 id3_length_t size; | |
443 | |
444 parse_header(&ptr, &tag->version, &tag->flags, &size); | |
445 | |
446 tag->paddedsize = 10 + size; | |
447 | |
448 if ((tag->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) && | |
449 ID3_TAG_VERSION_MAJOR(tag->version) < 4) { | |
450 mem = malloc(size); | |
451 if (mem == 0) | |
452 goto fail; | |
453 | |
454 memcpy(mem, ptr, size); | |
455 | |
456 size = id3_util_deunsynchronise(mem, size); | |
457 ptr = mem; | |
458 } | |
459 | |
460 end = ptr + size; | |
461 | |
462 if (tag->flags & ID3_TAG_FLAG_EXTENDEDHEADER) { | |
463 switch (ID3_TAG_VERSION_MAJOR(tag->version)) { | |
464 case 2: | |
465 goto fail; | |
466 | |
467 case 3: | |
468 { | |
469 id3_byte_t const *ehptr, *ehend; | |
470 id3_length_t ehsize; | |
471 | |
472 enum { | |
473 EH_FLAG_CRC = 0x8000 /* CRC data present */ | |
474 }; | |
475 | |
476 if (end - ptr < 4) | |
477 goto fail; | |
478 | |
479 ehsize = id3_parse_uint(&ptr, 4); | |
480 | |
481 if (ehsize > end - ptr) | |
482 goto fail; | |
483 | |
484 ehptr = ptr; | |
485 ehend = ptr + ehsize; | |
486 | |
487 ptr = ehend; | |
488 | |
489 if (ehend - ehptr >= 6) { | |
490 int ehflags; | |
491 id3_length_t padsize; | |
492 | |
493 ehflags = id3_parse_uint(&ehptr, 2); | |
494 padsize = id3_parse_uint(&ehptr, 4); | |
495 | |
496 if (padsize > end - ptr) | |
497 goto fail; | |
498 | |
499 end -= padsize; | |
500 | |
501 if (ehflags & EH_FLAG_CRC) { | |
502 unsigned long crc; | |
503 | |
504 if (ehend - ehptr < 4) | |
505 goto fail; | |
506 | |
507 crc = id3_parse_uint(&ehptr, 4); | |
508 | |
509 if (crc != id3_crc_compute(ptr, end - ptr)) | |
510 goto fail; | |
511 | |
512 tag->extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; | |
513 } | |
514 } | |
515 } | |
516 break; | |
517 | |
518 case 4: | |
519 { | |
520 id3_byte_t const *ehptr, *ehend; | |
521 id3_length_t ehsize; | |
522 unsigned int bytes; | |
523 | |
524 if (end - ptr < 4) | |
525 goto fail; | |
526 | |
527 ehptr = ptr; | |
528 ehsize = id3_parse_syncsafe(&ptr, 4); | |
529 | |
530 if (ehsize < 6 || ehsize > end - ehptr) | |
531 goto fail; | |
532 | |
533 ehend = ehptr + ehsize; | |
534 | |
535 bytes = id3_parse_uint(&ptr, 1); | |
536 | |
537 if (bytes < 1 || bytes > ehend - ptr) | |
538 goto fail; | |
539 | |
540 ehptr = ptr + bytes; | |
541 | |
542 /* verify extended header size */ | |
543 { | |
544 id3_byte_t const *flagsptr = ptr, *dataptr = ehptr; | |
545 unsigned int datalen; | |
546 int ehflags; | |
547 | |
548 while (bytes--) { | |
549 for (ehflags = id3_parse_uint(&flagsptr, 1); ehflags; | |
550 ehflags = (ehflags << 1) & 0xff) { | |
551 if (ehflags & 0x80) { | |
552 if (dataptr == ehend) | |
553 goto fail; | |
554 datalen = id3_parse_uint(&dataptr, 1); | |
555 if (datalen > 0x7f || datalen > ehend - dataptr) | |
556 goto fail; | |
557 dataptr += datalen; | |
558 } | |
559 } | |
560 } | |
561 } | |
562 | |
563 tag->extendedflags = id3_parse_uint(&ptr, 1); | |
564 | |
565 ptr = ehend; | |
566 | |
567 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) { | |
568 bytes = id3_parse_uint(&ehptr, 1); | |
569 ehptr += bytes; | |
570 } | |
571 | |
572 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) { | |
573 unsigned long crc; | |
574 | |
575 bytes = id3_parse_uint(&ehptr, 1); | |
576 if (bytes < 5) | |
577 goto fail; | |
578 | |
579 crc = id3_parse_syncsafe(&ehptr, 5); | |
580 ehptr += bytes - 5; | |
581 | |
582 if (crc != id3_crc_compute(ptr, end - ptr)) | |
583 goto fail; | |
584 } | |
585 | |
586 if (tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) { | |
587 bytes = id3_parse_uint(&ehptr, 1); | |
588 if (bytes < 1) | |
589 goto fail; | |
590 | |
591 tag->restrictions = id3_parse_uint(&ehptr, 1); | |
592 ehptr += bytes - 1; | |
593 } | |
594 } | |
595 break; | |
596 } | |
597 } | |
598 | |
599 /* frames */ | |
600 | |
601 while (ptr < end) { | |
602 struct id3_frame *frame; | |
603 | |
604 if (*ptr == 0) | |
605 break; /* padding */ | |
606 | |
607 frame = id3_frame_parse(&ptr, end - ptr, tag->version); | |
608 if (frame == 0 || id3_tag_attachframe(tag, frame) == -1) | |
609 goto fail; | |
610 } | |
611 | |
612 if (ID3_TAG_VERSION_MAJOR(tag->version) < 4 && | |
613 id3_compat_fixup(tag) == -1) | |
614 goto fail; | |
615 } | |
616 | |
617 if (0) { | |
618 fail: | |
619 id3_tag_delete(tag); | |
620 tag = 0; | |
621 } | |
622 | |
623 if (mem) | |
624 free(mem); | |
625 | |
626 return tag; | |
627 } | |
628 | |
629 /* | |
630 * NAME: tag->parse() | |
631 * DESCRIPTION: parse a complete ID3 tag | |
632 */ | |
633 struct id3_tag *id3_tag_parse(id3_byte_t const *data, id3_length_t length) | |
634 { | |
635 id3_byte_t const *ptr; | |
636 unsigned int version; | |
637 int flags; | |
638 id3_length_t size; | |
639 | |
640 assert(data); | |
641 | |
642 switch (tagtype(data, length)) { | |
643 case TAGTYPE_ID3V1: | |
644 return (length < 128) ? 0 : v1_parse(data); | |
645 | |
646 case TAGTYPE_ID3V2: | |
647 break; | |
648 | |
649 case TAGTYPE_ID3V2_FOOTER: | |
650 case TAGTYPE_NONE: | |
651 return 0; | |
652 } | |
653 | |
654 /* ID3v2.x */ | |
655 | |
656 ptr = data; | |
657 parse_header(&ptr, &version, &flags, &size); | |
658 | |
659 switch (ID3_TAG_VERSION_MAJOR(version)) { | |
660 case 4: | |
661 if (flags & ID3_TAG_FLAG_FOOTERPRESENT) | |
662 size += 10; | |
663 case 2: | |
664 case 3: | |
665 return (length < 10 + size) ? 0 : v2_parse(data); | |
666 } | |
667 | |
668 return 0; | |
669 } | |
670 | |
671 static | |
672 void v1_renderstr(struct id3_tag const *tag, char const *frameid, | |
673 id3_byte_t **buffer, id3_length_t length) | |
674 { | |
675 struct id3_frame *frame; | |
676 id3_ucs4_t const *string; | |
677 | |
678 frame = id3_tag_findframe(tag, frameid, 0); | |
679 if (frame == 0) | |
680 string = id3_ucs4_empty; | |
681 else { | |
682 if (strcmp(frameid, ID3_FRAME_COMMENT) == 0) | |
683 string = id3_field_getfullstring(&frame->fields[3]); | |
684 else | |
685 string = id3_field_getstrings(&frame->fields[1], 0); | |
686 } | |
687 | |
688 id3_render_paddedstring(buffer, string, length); | |
689 } | |
690 | |
691 /* | |
692 * NAME: v1->render() | |
693 * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag | |
694 */ | |
695 static | |
696 id3_length_t v1_render(struct id3_tag const *tag, id3_byte_t *buffer) | |
697 { | |
698 id3_byte_t data[128], *ptr; | |
699 struct id3_frame *frame; | |
700 unsigned int i; | |
701 int genre = -1; | |
702 | |
703 ptr = data; | |
704 | |
705 id3_render_immediate(&ptr, "TAG", 3); | |
706 | |
707 v1_renderstr(tag, ID3_FRAME_TITLE, &ptr, 30); | |
708 v1_renderstr(tag, ID3_FRAME_ARTIST, &ptr, 30); | |
709 v1_renderstr(tag, ID3_FRAME_ALBUM, &ptr, 30); | |
710 v1_renderstr(tag, ID3_FRAME_YEAR, &ptr, 4); | |
711 v1_renderstr(tag, ID3_FRAME_COMMENT, &ptr, 30); | |
712 | |
713 /* ID3v1.1 track number */ | |
714 | |
715 frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0); | |
716 if (frame) { | |
717 unsigned int track; | |
718 | |
719 track = id3_ucs4_getnumber(id3_field_getstrings(&frame->fields[1], 0)); | |
720 if (track > 0 && track <= 0xff) { | |
721 ptr[-2] = 0; | |
722 ptr[-1] = track; | |
723 } | |
724 } | |
725 | |
726 /* ID3v1 genre number */ | |
727 | |
728 frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0); | |
729 if (frame) { | |
730 unsigned int nstrings; | |
731 | |
732 nstrings = id3_field_getnstrings(&frame->fields[1]); | |
733 | |
734 for (i = 0; i < nstrings; ++i) { | |
735 genre = id3_genre_number(id3_field_getstrings(&frame->fields[1], i)); | |
736 if (genre != -1) | |
737 break; | |
738 } | |
739 | |
740 if (i == nstrings && nstrings > 0) | |
741 genre = ID3_GENRE_OTHER; | |
742 } | |
743 | |
744 id3_render_int(&ptr, genre, 1); | |
745 | |
746 /* make sure the tag is not empty */ | |
747 | |
748 if (genre == -1) { | |
749 for (i = 3; i < 127; ++i) { | |
750 if (data[i] != ' ') | |
751 break; | |
752 } | |
753 | |
754 if (i == 127) | |
755 return 0; | |
756 } | |
757 | |
758 if (buffer) | |
759 memcpy(buffer, data, 128); | |
760 | |
761 return 128; | |
762 } | |
763 | |
764 /* | |
765 * NAME: tag->render() | |
766 * DESCRIPTION: render a complete ID3 tag | |
767 */ | |
768 id3_length_t id3_tag_render(struct id3_tag const *tag, id3_byte_t *buffer) | |
769 { | |
770 id3_length_t size = 0; | |
771 id3_byte_t **ptr, | |
772 *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0; | |
773 int flags, extendedflags; | |
774 unsigned int i; | |
775 | |
776 assert(tag); | |
777 | |
778 if (tag->options & ID3_TAG_OPTION_ID3V1) | |
779 return v1_render(tag, buffer); | |
780 | |
781 /* a tag must contain at least one (renderable) frame */ | |
782 | |
783 for (i = 0; i < tag->nframes; ++i) { | |
784 if (id3_frame_render(tag->frames[i], 0, 0) > 0) | |
785 break; | |
786 } | |
787 | |
788 if (i == tag->nframes) | |
789 return 0; | |
790 | |
791 ptr = buffer ? &buffer : 0; | |
792 | |
793 /* get flags */ | |
794 | |
795 flags = tag->flags & ID3_TAG_FLAG_KNOWNFLAGS; | |
796 extendedflags = tag->extendedflags & ID3_TAG_EXTENDEDFLAG_KNOWNFLAGS; | |
797 | |
798 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; | |
799 if (tag->options & ID3_TAG_OPTION_CRC) | |
800 extendedflags |= ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT; | |
801 | |
802 extendedflags &= ~ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS; | |
803 if (tag->restrictions) | |
804 extendedflags |= ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS; | |
805 | |
806 flags &= ~ID3_TAG_FLAG_UNSYNCHRONISATION; | |
807 if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION) | |
808 flags |= ID3_TAG_FLAG_UNSYNCHRONISATION; | |
809 | |
810 flags &= ~ID3_TAG_FLAG_EXTENDEDHEADER; | |
811 if (extendedflags) | |
812 flags |= ID3_TAG_FLAG_EXTENDEDHEADER; | |
813 | |
814 flags &= ~ID3_TAG_FLAG_FOOTERPRESENT; | |
815 if (tag->options & ID3_TAG_OPTION_APPENDEDTAG) | |
816 flags |= ID3_TAG_FLAG_FOOTERPRESENT; | |
817 | |
818 /* header */ | |
819 | |
820 if (ptr) | |
821 header_ptr = *ptr; | |
822 | |
823 size += id3_render_immediate(ptr, "ID3", 3); | |
824 size += id3_render_int(ptr, ID3_TAG_VERSION, 2); | |
825 size += id3_render_int(ptr, flags, 1); | |
826 | |
827 if (ptr) | |
828 tagsize_ptr = *ptr; | |
829 | |
830 size += id3_render_syncsafe(ptr, 0, 4); | |
831 | |
832 /* extended header */ | |
833 | |
834 if (flags & ID3_TAG_FLAG_EXTENDEDHEADER) { | |
835 id3_length_t ehsize = 0; | |
836 id3_byte_t *ehsize_ptr = 0; | |
837 | |
838 if (ptr) | |
839 ehsize_ptr = *ptr; | |
840 | |
841 ehsize += id3_render_syncsafe(ptr, 0, 4); | |
842 ehsize += id3_render_int(ptr, 1, 1); | |
843 ehsize += id3_render_int(ptr, extendedflags, 1); | |
844 | |
845 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE) | |
846 ehsize += id3_render_int(ptr, 0, 1); | |
847 | |
848 if (extendedflags & ID3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) { | |
849 ehsize += id3_render_int(ptr, 5, 1); | |
850 | |
851 if (ptr) | |
852 crc_ptr = *ptr; | |
853 | |
854 ehsize += id3_render_syncsafe(ptr, 0, 5); | |
855 } | |
856 | |
857 if (extendedflags & ID3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) { | |
858 ehsize += id3_render_int(ptr, 1, 1); | |
859 ehsize += id3_render_int(ptr, tag->restrictions, 1); | |
860 } | |
861 | |
862 if (ehsize_ptr) | |
863 id3_render_syncsafe(&ehsize_ptr, ehsize, 4); | |
864 | |
865 size += ehsize; | |
866 } | |
867 | |
868 /* frames */ | |
869 | |
870 if (ptr) | |
871 frames_ptr = *ptr; | |
872 | |
873 for (i = 0; i < tag->nframes; ++i) | |
874 size += id3_frame_render(tag->frames[i], ptr, tag->options); | |
875 | |
876 /* padding */ | |
877 | |
878 if (!(flags & ID3_TAG_FLAG_FOOTERPRESENT)) { | |
879 if (size < tag->paddedsize) | |
880 size += id3_render_padding(ptr, 0, tag->paddedsize - size); | |
881 else if (tag->options & ID3_TAG_OPTION_UNSYNCHRONISATION) { | |
882 if (ptr == 0) | |
883 size += 1; | |
884 else { | |
885 if ((*ptr)[-1] == 0xff) | |
886 size += id3_render_padding(ptr, 0, 1); | |
887 } | |
888 } | |
889 } | |
890 | |
891 /* patch tag size and CRC */ | |
892 | |
893 if (tagsize_ptr) | |
894 id3_render_syncsafe(&tagsize_ptr, size - 10, 4); | |
895 | |
896 if (crc_ptr) { | |
897 id3_render_syncsafe(&crc_ptr, | |
898 id3_crc_compute(frames_ptr, *ptr - frames_ptr), 5); | |
899 } | |
900 | |
901 /* footer */ | |
902 | |
903 if (flags & ID3_TAG_FLAG_FOOTERPRESENT) { | |
904 size += id3_render_immediate(ptr, "3DI", 3); | |
905 size += id3_render_binary(ptr, header_ptr + 3, 7); | |
906 } | |
907 | |
908 return size; | |
909 } |