/libtunepimp-0.5.3/plugins/mp3/id3tag-2.3/tag.c
C | 973 lines | 646 code | 221 blank | 106 comment | 208 complexity | 4646c351d24b1ed090a96eb4210fb846 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, GPL-2.0, LGPL-2.0
1/*
2 * libid3tag - ID3 tag manipulation library
3 * Copyright (C) 2000-2003 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 8460 2006-09-03 18:37:43Z luks $
20 */
21
22# ifdef HAVE_CONFIG_H
23# include "config.h"
24# endif
25
26#ifdef WIN32
27# include "../../../config_win32.h"
28#endif
29
30# include "global.h"
31
32# include <string.h>
33# include <stdlib.h>
34# include <stdio.h>
35
36# ifdef HAVE_ASSERT_H
37# include <assert.h>
38# endif
39
40# include "id3tag.h"
41# include "tag.h"
42# include "frame.h"
43# include "compat.h"
44# include "parse.h"
45# include "render.h"
46# include "latin1.h"
47# include "ucs4.h"
48# include "genre.h"
49# include "crc.h"
50# include "field.h"
51# include "util.h"
52
53/* If a v2 tag is too small to fit and the file needs to be rewritten, add at least
54 * TAG_PADDING number of bytes to hopefully prevent having to rewrite the file
55 * in the future.
56 */
57#define TAG_PADDING 256
58
59/*
60 * NAME: tag->new()
61 * DESCRIPTION: allocate and return a new, empty tag
62 */
63struct id3_2_3_tag *id3_2_3_tag_new(void)
64{
65 struct id3_2_3_tag *tag;
66
67 tag = malloc(sizeof(*tag));
68 if (tag) {
69 tag->refcount = 0;
70 tag->version = ID3_2_3_TAG_VERSION;
71 tag->flags = 0;
72 tag->extendedflags = 0;
73 tag->restrictions = 0;
74 tag->options = /* ID3_2_3_TAG_OPTION_UNSYNCHRONISATION | */
75 ID3_2_3_TAG_OPTION_COMPRESSION | ID3_2_3_TAG_OPTION_CRC;
76 tag->nframes = 0;
77 tag->frames = 0;
78 tag->paddedsize = 0;
79 }
80
81 return tag;
82}
83
84/*
85 * NAME: tag->delete()
86 * DESCRIPTION: destroy a tag and deallocate all associated memory
87 */
88void id3_2_3_tag_delete(struct id3_2_3_tag *tag)
89{
90 assert(tag);
91
92 if (tag->refcount == 0) {
93 id3_2_3_tag_clearframes(tag);
94
95 if (tag->frames)
96 free(tag->frames);
97
98 free(tag);
99 }
100}
101
102/*
103 * NAME: tag->addref()
104 * DESCRIPTION: add an external reference to a tag
105 */
106void id3_2_3_tag_addref(struct id3_2_3_tag *tag)
107{
108 assert(tag);
109
110 ++tag->refcount;
111}
112
113/*
114 * NAME: tag->delref()
115 * DESCRIPTION: remove an external reference to a tag
116 */
117void id3_2_3_tag_delref(struct id3_2_3_tag *tag)
118{
119 assert(tag && tag->refcount > 0);
120
121 --tag->refcount;
122}
123
124/*
125 * NAME: tag->version()
126 * DESCRIPTION: return the tag's original ID3 version number
127 */
128unsigned int id3_2_3_tag_version(struct id3_2_3_tag const *tag)
129{
130 assert(tag);
131
132 return tag->version;
133}
134
135/*
136 * NAME: tag->options()
137 * DESCRIPTION: get or set tag options
138 */
139int id3_2_3_tag_options(struct id3_2_3_tag *tag, int mask, int values)
140{
141 assert(tag);
142
143 if (mask)
144 tag->options = (tag->options & ~mask) | (values & mask);
145
146 return tag->options;
147}
148
149/*
150 * NAME: tag->setlength()
151 * DESCRIPTION: set the minimum rendered tag size
152 */
153void id3_2_3_tag_setlength(struct id3_2_3_tag *tag, id3_2_3_length_t length)
154{
155 assert(tag);
156
157 tag->paddedsize = length;
158}
159
160/*
161 * NAME: tag->clearframes()
162 * DESCRIPTION: detach and delete all frames associated with a tag
163 */
164void id3_2_3_tag_clearframes(struct id3_2_3_tag *tag)
165{
166 unsigned int i;
167
168 assert(tag);
169
170 for (i = 0; i < tag->nframes; ++i) {
171 id3_2_3_frame_delref(tag->frames[i]);
172 id3_2_3_frame_delete(tag->frames[i]);
173 }
174
175 tag->nframes = 0;
176}
177
178/*
179 * NAME: tag->sanitycheckframe()
180 * DESCRIPTION: Check to see if the frames are valid. If not return 0.
181 */
182int id3_2_3_tag_sanitycheckframe(struct id3_2_3_frame *frame)
183{
184 union id3_2_3_field *field;
185 int i;
186 id3_2_3_latin1_t *str;
187 const id3_2_3_ucs4_t *unicode;
188
189 if (strcmp(frame->id, "TCON"))
190 return 1;
191
192 for(i = 0;; i++)
193 {
194 field = id3_2_3_frame_field(frame, i);
195 if (!field)
196 break;
197
198 if (id3_2_3_field_type(field) == ID3_2_3_FIELD_TYPE_STRINGLIST)
199 {
200 unicode = id3_2_3_field_getstrings(field, 0);
201 if (unicode)
202 {
203 str = id3_2_3_ucs4_latin1duplicate(unicode);
204 if (strcasecmp(str, "other") == 0)
205 {
206 free(str);
207 return 0;
208 }
209 free(str);
210 }
211 }
212 }
213
214 return 1;
215}
216
217/*
218 * NAME: tag->attachframe()
219 * DESCRIPTION: attach a frame to a tag
220 */
221int id3_2_3_tag_attachframe(struct id3_2_3_tag *tag, struct id3_2_3_frame *frame)
222{
223 struct id3_2_3_frame **frames;
224
225 assert(tag && frame);
226
227 if (!id3_2_3_tag_sanitycheckframe(frame))
228 return 0;
229
230 frames = realloc(tag->frames, (tag->nframes + 1) * sizeof(*frames));
231 if (frames == 0)
232 return -1;
233
234 tag->frames = frames;
235 tag->frames[tag->nframes++] = frame;
236
237 id3_2_3_frame_addref(frame);
238
239 return 0;
240}
241
242/*
243 * NAME: tag->detachframe()
244 * DESCRIPTION: detach (but don't delete) a frame from a tag
245 */
246int id3_2_3_tag_detachframe(struct id3_2_3_tag *tag, struct id3_2_3_frame *frame)
247{
248 unsigned int i;
249
250 assert(tag && frame);
251
252 for (i = 0; i < tag->nframes; ++i) {
253 if (tag->frames[i] == frame)
254 break;
255 }
256
257 if (i == tag->nframes)
258 return -1;
259
260 --tag->nframes;
261 while (i++ < tag->nframes)
262 tag->frames[i - 1] = tag->frames[i];
263
264 id3_2_3_frame_delref(frame);
265
266 return 0;
267}
268
269/*
270 * NAME: tag->findframe()
271 * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID
272 */
273struct id3_2_3_frame *id3_2_3_tag_findframe(struct id3_2_3_tag const *tag,
274 char const *id, unsigned int index)
275{
276 unsigned int len, i;
277
278 assert(tag);
279
280 if (id == 0 || *id == 0)
281 return (index < tag->nframes) ? tag->frames[index] : 0;
282
283 len = strlen(id);
284
285 if (len == 4) {
286 struct id3_2_3_compat const *compat;
287
288 compat = id3_2_3_compat_lookup(id, len);
289 if (compat && compat->equiv && !compat->translate) {
290 id = compat->equiv;
291 len = strlen(id);
292 }
293 }
294
295 for (i = 0; i < tag->nframes; ++i) {
296 if (strncmp(tag->frames[i]->id, id, len) == 0 && index-- == 0)
297 return tag->frames[i];
298 }
299
300 return 0;
301}
302
303enum tagtype {
304 TAGTYPE_NONE = 0,
305 TAGTYPE_ID3V1,
306 TAGTYPE_ID3V2,
307 TAGTYPE_ID3V2_FOOTER
308};
309
310static
311enum tagtype tagtype(id3_2_3_byte_t const *data, id3_2_3_length_t length)
312{
313 if (length >= 3 &&
314 data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
315 return TAGTYPE_ID3V1;
316
317 if (length >= 10 &&
318 ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') ||
319 (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) &&
320 data[3] < 0xff && data[4] < 0xff &&
321 data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
322 return data[0] == 'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER;
323
324 return TAGTYPE_NONE;
325}
326
327static
328void parse_header(id3_2_3_byte_t const **ptr,
329 unsigned int *version, int *flags, id3_2_3_length_t *size)
330{
331 *ptr += 3;
332
333 *version = id3_2_3_parse_uint(ptr, 2);
334 *flags = id3_2_3_parse_uint(ptr, 1);
335 *size = id3_2_3_parse_syncsafe(ptr, 4);
336}
337
338/*
339 * NAME: tag->query()
340 * DESCRIPTION: if a tag begins at the given location, return its size
341 */
342signed long id3_2_3_tag_query(id3_2_3_byte_t const *data, id3_2_3_length_t length)
343{
344 unsigned int version;
345 int flags;
346 id3_2_3_length_t size;
347
348 assert(data);
349
350 switch (tagtype(data, length)) {
351 case TAGTYPE_ID3V1:
352 return 128;
353
354 case TAGTYPE_ID3V2:
355 parse_header(&data, &version, &flags, &size);
356
357 if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)
358 size += 10;
359
360 return 10 + size;
361
362 case TAGTYPE_ID3V2_FOOTER:
363 parse_header(&data, &version, &flags, &size);
364 return -size - 10;
365
366 case TAGTYPE_NONE:
367 break;
368 }
369
370 return 0;
371}
372
373static
374void trim(char *str)
375{
376 char *ptr;
377
378 ptr = str + strlen(str);
379 while (ptr > str && ptr[-1] == ' ')
380 --ptr;
381
382 *ptr = 0;
383}
384
385
386static
387int v1_attachstr(struct id3_2_3_tag *tag, char const *id,
388 char *text, unsigned long number)
389{
390 struct id3_2_3_frame *frame;
391 id3_2_3_ucs4_t ucs4[31];
392
393 if (text) {
394 trim(text);
395 if (*text == 0)
396 return 0;
397 }
398
399 frame = id3_2_3_frame_new(id);
400 if (frame == 0)
401 return -1;
402
403 if (id3_2_3_field_settextencoding(&frame->fields[0],
404 ID3_2_3_FIELD_TEXTENCODING_ISO_8859_1) == -1)
405 goto fail;
406
407 if (text)
408 id3_2_3_latin1_decode(text, ucs4);
409 else
410 id3_2_3_ucs4_putnumber(ucs4, number);
411
412 if (strcmp(id, ID3_2_3_FRAME_COMMENT) == 0) {
413 if (id3_2_3_field_setlanguage(&frame->fields[1], "XXX") == -1 ||
414 id3_2_3_field_setstring(&frame->fields[2], id3_2_3_ucs4_empty) == -1 ||
415 id3_2_3_field_setfullstring(&frame->fields[3], ucs4) == -1)
416 goto fail;
417 }
418 else {
419 id3_2_3_ucs4_t *ptr = ucs4;
420 if (id3_2_3_field_setstrings(&frame->fields[1], 1, &ptr) == -1)
421 goto fail;
422 }
423
424 if (id3_2_3_tag_attachframe(tag, frame) == -1)
425 goto fail;
426
427 return 0;
428
429 fail:
430 id3_2_3_frame_delete(frame);
431 return -1;
432}
433
434static
435struct id3_2_3_tag *v1_parse(id3_2_3_byte_t const *data)
436{
437 struct id3_2_3_tag *tag;
438
439 tag = id3_2_3_tag_new();
440 if (tag) {
441 char title[31], artist[31], album[31], year[5], comment[31], genreStr[255];
442 unsigned int genre, track;
443
444 tag->version = 0x0100;
445
446 tag->options |= ID3_2_3_TAG_OPTION_ID3V1;
447 tag->options &= ~ID3_2_3_TAG_OPTION_COMPRESSION;
448
449 tag->restrictions =
450 ID3_2_3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 |
451 ID3_2_3_TAG_RESTRICTION_TEXTSIZE_30_CHARS;
452
453 title[30] = artist[30] = album[30] = year[4] = comment[30] = 0;
454
455 memcpy(title, &data[3], 30);
456 memcpy(artist, &data[33], 30);
457 memcpy(album, &data[63], 30);
458 memcpy(year, &data[93], 4);
459 memcpy(comment, &data[97], 30);
460
461 genre = data[127];
462
463 track = 0;
464 if (comment[28] == 0 && comment[29] != 0) {
465 track = comment[29];
466 tag->version = 0x0101;
467 }
468
469 /* populate tag frames */
470
471 sprintf(genreStr, "(%d)", genre);
472 if (v1_attachstr(tag, ID3_2_3_FRAME_TITLE, title, 0) == -1 ||
473 v1_attachstr(tag, ID3_2_3_FRAME_ARTIST, artist, 0) == -1 ||
474 v1_attachstr(tag, ID3_2_3_FRAME_ALBUM, album, 0) == -1 ||
475 v1_attachstr(tag, ID3_2_3_FRAME_YEAR, year, 0) == -1 ||
476 (track && v1_attachstr(tag, ID3_2_3_FRAME_TRACK, 0, track) == -1) ||
477 (genre < 0xff && v1_attachstr(tag, ID3_2_3_FRAME_GENRE, genreStr, 0) == -1) ||
478 v1_attachstr(tag, ID3_2_3_FRAME_COMMENT, comment, 0) == -1) {
479 id3_2_3_tag_delete(tag);
480 tag = 0;
481 }
482 }
483
484 return tag;
485}
486
487static
488struct id3_2_3_tag *v2_parse(id3_2_3_byte_t const *ptr)
489{
490 struct id3_2_3_tag *tag;
491 id3_2_3_byte_t *mem = 0;
492
493 tag = id3_2_3_tag_new();
494 if (tag) {
495 id3_2_3_byte_t const *end;
496 id3_2_3_length_t size;
497
498 parse_header(&ptr, &tag->version, &tag->flags, &size);
499
500 tag->paddedsize = 10 + size;
501
502 if ((tag->flags & ID3_2_3_TAG_FLAG_UNSYNCHRONISATION) &&
503 ID3_2_3_TAG_VERSION_MAJOR(tag->version) < 4) {
504 mem = malloc(size);
505 if (mem == 0)
506 goto fail;
507
508 memcpy(mem, ptr, size);
509
510 size = id3_2_3_util_deunsynchronise(mem, size);
511 ptr = mem;
512 }
513
514 end = ptr + size;
515
516 if (tag->flags & ID3_2_3_TAG_FLAG_EXTENDEDHEADER) {
517 switch (ID3_2_3_TAG_VERSION_MAJOR(tag->version)) {
518 case 2:
519 goto fail;
520
521 case 3:
522 {
523 id3_2_3_byte_t const *ehptr, *ehend;
524 id3_2_3_length_t ehsize;
525
526 enum {
527 EH_FLAG_CRC = 0x8000 /* CRC data present */
528 };
529
530 if (end - ptr < 4)
531 goto fail;
532
533 ehsize = id3_2_3_parse_uint(&ptr, 4);
534
535 if (ehsize > end - ptr)
536 goto fail;
537
538 ehptr = ptr;
539 ehend = ptr + ehsize;
540
541 ptr = ehend;
542
543 if (ehend - ehptr >= 6) {
544 int ehflags;
545 id3_2_3_length_t padsize;
546
547 ehflags = id3_2_3_parse_uint(&ehptr, 2);
548 padsize = id3_2_3_parse_uint(&ehptr, 4);
549
550 if (padsize > end - ptr)
551 goto fail;
552
553 end -= padsize;
554
555 if (ehflags & EH_FLAG_CRC) {
556 unsigned long crc;
557
558 if (ehend - ehptr < 4)
559 goto fail;
560
561 crc = id3_2_3_parse_uint(&ehptr, 4);
562
563 if (crc != id3_2_3_crc_calculate(ptr, end - ptr))
564 goto fail;
565
566 tag->extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
567 }
568 }
569 }
570 break;
571
572 case 4:
573 {
574 id3_2_3_byte_t const *ehptr, *ehend;
575 id3_2_3_length_t ehsize;
576 unsigned int bytes;
577
578 if (end - ptr < 4)
579 goto fail;
580
581 ehptr = ptr;
582 ehsize = id3_2_3_parse_syncsafe(&ptr, 4);
583
584 if (ehsize < 6 || ehsize > end - ehptr)
585 goto fail;
586
587 ehend = ehptr + ehsize;
588
589 bytes = id3_2_3_parse_uint(&ptr, 1);
590
591 if (bytes < 1 || bytes > ehend - ptr)
592 goto fail;
593
594 ehptr = ptr + bytes;
595
596 /* verify extended header size */
597 {
598 id3_2_3_byte_t const *flagsptr = ptr, *dataptr = ehptr;
599 unsigned int datalen;
600 int ehflags;
601
602 while (bytes--) {
603 for (ehflags = id3_2_3_parse_uint(&flagsptr, 1); ehflags;
604 ehflags = (ehflags << 1) & 0xff) {
605 if (ehflags & 0x80) {
606 if (dataptr == ehend)
607 goto fail;
608 datalen = id3_2_3_parse_uint(&dataptr, 1);
609 if (datalen > 0x7f || datalen > ehend - dataptr)
610 goto fail;
611 dataptr += datalen;
612 }
613 }
614 }
615 }
616
617 tag->extendedflags = id3_2_3_parse_uint(&ptr, 1);
618
619 ptr = ehend;
620
621 if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGISANUPDATE) {
622 bytes = id3_2_3_parse_uint(&ehptr, 1);
623 ehptr += bytes;
624 }
625
626 if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
627 unsigned long crc;
628
629 bytes = id3_2_3_parse_uint(&ehptr, 1);
630 if (bytes < 5)
631 goto fail;
632
633 crc = id3_2_3_parse_syncsafe(&ehptr, 5);
634 ehptr += bytes - 5;
635
636 if (crc != id3_2_3_crc_calculate(ptr, end - ptr))
637 goto fail;
638 }
639
640 if (tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
641 bytes = id3_2_3_parse_uint(&ehptr, 1);
642 if (bytes < 1)
643 goto fail;
644
645 tag->restrictions = id3_2_3_parse_uint(&ehptr, 1);
646 ehptr += bytes - 1;
647 }
648 }
649 break;
650 }
651 }
652
653 /* frames */
654
655 while (ptr < end) {
656 struct id3_2_3_frame *frame;
657
658 if (*ptr == 0)
659 break; /* padding */
660
661 if (memcmp(ptr, "MP3ext", end - ptr < 6 ? end - ptr : 6) == 0)
662 break; /* non-standard 'MP3ext' padding */
663
664 frame = id3_2_3_frame_parse(&ptr, end - ptr, tag->version);
665 if (frame == 0 || id3_2_3_tag_attachframe(tag, frame) == -1)
666 goto fail;
667 }
668
669 if (ID3_2_3_TAG_VERSION_MAJOR(tag->version) < 4 &&
670 ID3_2_3_TAG_VERSION == 4 &&
671 id3_2_3_compat_fixup(tag) == -1)
672 goto fail;
673 }
674
675 if (0) {
676 fail:
677 id3_2_3_tag_delete(tag);
678 tag = 0;
679 }
680
681 if (mem)
682 free(mem);
683
684 return tag;
685}
686
687/*
688 * NAME: tag->parse()
689 * DESCRIPTION: parse a complete ID3 tag
690 */
691struct id3_2_3_tag *id3_2_3_tag_parse(id3_2_3_byte_t const *data, id3_2_3_length_t length)
692{
693 id3_2_3_byte_t const *ptr;
694 unsigned int version;
695 int flags;
696 id3_2_3_length_t size;
697
698 assert(data);
699
700 switch (tagtype(data, length)) {
701 case TAGTYPE_ID3V1:
702 return (length < 128) ? 0 : v1_parse(data);
703
704 case TAGTYPE_ID3V2:
705 break;
706
707 case TAGTYPE_ID3V2_FOOTER:
708 case TAGTYPE_NONE:
709 return 0;
710 }
711
712 /* ID3v2.x */
713
714 ptr = data;
715 parse_header(&ptr, &version, &flags, &size);
716
717 switch (ID3_2_3_TAG_VERSION_MAJOR(version)) {
718 case 4:
719 if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)
720 size += 10;
721 case 2:
722 case 3:
723 return (length < 10 + size) ? 0 : v2_parse(data);
724 }
725
726 return 0;
727}
728
729static
730void v1_renderstr(struct id3_2_3_tag const *tag, char const *frameid,
731 id3_2_3_byte_t **buffer, id3_2_3_length_t length)
732{
733 struct id3_2_3_frame *frame;
734 id3_2_3_ucs4_t const *string;
735
736 frame = id3_2_3_tag_findframe(tag, frameid, 0);
737 if (frame == 0)
738 string = id3_2_3_ucs4_empty;
739 else {
740 if (strcmp(frameid, ID3_2_3_FRAME_COMMENT) == 0)
741 string = id3_2_3_field_getfullstring(&frame->fields[3]);
742 else
743 string = id3_2_3_field_getstrings(&frame->fields[1], 0);
744 }
745
746 id3_2_3_render_paddedstring(buffer, string, length);
747}
748
749/*
750 * NAME: v1->render()
751 * DESCRIPTION: render an ID3v1 (or ID3v1.1) tag
752 */
753static
754id3_2_3_length_t v1_render(struct id3_2_3_tag const *tag, id3_2_3_byte_t *buffer)
755{
756 id3_2_3_byte_t data[128], *ptr;
757 struct id3_2_3_frame *frame;
758 unsigned int i;
759 int genre = -1;
760
761 ptr = data;
762
763 id3_2_3_render_immediate(&ptr, "TAG", 3);
764
765 v1_renderstr(tag, ID3_2_3_FRAME_TITLE, &ptr, 30);
766 v1_renderstr(tag, ID3_2_3_FRAME_ARTIST, &ptr, 30);
767 v1_renderstr(tag, ID3_2_3_FRAME_ALBUM, &ptr, 30);
768 v1_renderstr(tag, ID3_2_3_FRAME_YEAR, &ptr, 4);
769 v1_renderstr(tag, ID3_2_3_FRAME_COMMENT, &ptr, 30);
770
771 /* ID3v1.1 track number */
772
773 frame = id3_2_3_tag_findframe(tag, ID3_2_3_FRAME_TRACK, 0);
774 if (frame) {
775 unsigned int track;
776
777 track = id3_2_3_ucs4_getnumber(id3_2_3_field_getstrings(&frame->fields[1], 0));
778 if (track > 0 && track <= 0xff) {
779 ptr[-2] = 0;
780 ptr[-1] = track;
781 }
782 }
783
784 /* ID3v1 genre number */
785
786 frame = id3_2_3_tag_findframe(tag, ID3_2_3_FRAME_GENRE, 0);
787 if (frame) {
788 unsigned int nstrings;
789
790 nstrings = id3_2_3_field_getnstrings(&frame->fields[1]);
791
792 for (i = 0; i < nstrings; ++i) {
793 genre = id3_2_3_genre_number(id3_2_3_field_getstrings(&frame->fields[1], i));
794 if (genre != -1)
795 break;
796 }
797
798 if (i == nstrings && nstrings > 0)
799 genre = ID3_2_3_GENRE_OTHER;
800 }
801
802 id3_2_3_render_int(&ptr, genre, 1);
803
804 /* make sure the tag is not empty */
805
806 if (genre == -1) {
807 for (i = 3; i < 127; ++i) {
808 if (data[i] != ' ')
809 break;
810 }
811
812 if (i == 127)
813 return 0;
814 }
815
816 if (buffer)
817 memcpy(buffer, data, 128);
818
819 return 128;
820}
821
822/*
823 * NAME: tag->render()
824 * DESCRIPTION: render a complete ID3 tag
825 */
826/* RAK: Note: This file used to be const, but since the padding might be
827 * adjusted by this function, we can't do that anymore.
828 */
829id3_2_3_length_t id3_2_3_tag_render(struct id3_2_3_tag *tag, id3_2_3_byte_t *buffer)
830{
831 id3_2_3_length_t size = 0;
832 id3_2_3_byte_t **ptr,
833 *header_ptr = 0, *tagsize_ptr = 0, *crc_ptr = 0, *frames_ptr = 0;
834 int flags, extendedflags;
835 unsigned int i;
836
837 assert(tag);
838
839 if (tag->options & ID3_2_3_TAG_OPTION_ID3V1)
840 return v1_render(tag, buffer);
841
842 /* a tag must contain at least one (renderable) frame */
843
844 for (i = 0; i < tag->nframes; ++i) {
845 if (id3_2_3_frame_render(tag->frames[i], 0, 0) > 0)
846 break;
847 }
848
849 if (i == tag->nframes)
850 return 0;
851
852 ptr = buffer ? &buffer : 0;
853
854 /* get flags */
855
856 flags = tag->flags & ID3_2_3_TAG_FLAG_KNOWNFLAGS;
857 extendedflags = tag->extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_KNOWNFLAGS;
858
859 extendedflags &= ~ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
860 if (tag->options & ID3_2_3_TAG_OPTION_CRC)
861 extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT;
862
863 extendedflags &= ~ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
864 if (tag->restrictions)
865 extendedflags |= ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS;
866
867 flags &= ~ID3_2_3_TAG_FLAG_UNSYNCHRONISATION;
868 if (tag->options & ID3_2_3_TAG_OPTION_UNSYNCHRONISATION)
869 flags |= ID3_2_3_TAG_FLAG_UNSYNCHRONISATION;
870
871 flags &= ~ID3_2_3_TAG_FLAG_EXTENDEDHEADER;
872 if (extendedflags)
873 flags |= ID3_2_3_TAG_FLAG_EXTENDEDHEADER;
874
875 flags &= ~ID3_2_3_TAG_FLAG_FOOTERPRESENT;
876 if (tag->options & ID3_2_3_TAG_OPTION_APPENDEDTAG)
877 flags |= ID3_2_3_TAG_FLAG_FOOTERPRESENT;
878
879 /* header */
880
881 if (ptr)
882 header_ptr = *ptr;
883
884 size += id3_2_3_render_immediate(ptr, "ID3", 3);
885 size += id3_2_3_render_int(ptr, ID3_2_3_TAG_VERSION, 2);
886 size += id3_2_3_render_int(ptr, flags, 1);
887
888 if (ptr)
889 tagsize_ptr = *ptr;
890
891 size += id3_2_3_render_syncsafe(ptr, 0, 4);
892
893 /* extended header */
894
895 if (flags & ID3_2_3_TAG_FLAG_EXTENDEDHEADER) {
896 id3_2_3_length_t ehsize = 0;
897 id3_2_3_byte_t *ehsize_ptr = 0;
898
899 if (ptr)
900 ehsize_ptr = *ptr;
901
902 ehsize += id3_2_3_render_syncsafe(ptr, 0, 4);
903 ehsize += id3_2_3_render_int(ptr, 1, 1);
904 ehsize += id3_2_3_render_int(ptr, extendedflags, 1);
905
906 if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGISANUPDATE)
907 ehsize += id3_2_3_render_int(ptr, 0, 1);
908
909 if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_CRCDATAPRESENT) {
910 ehsize += id3_2_3_render_int(ptr, 5, 1);
911
912 if (ptr)
913 crc_ptr = *ptr;
914
915 ehsize += id3_2_3_render_syncsafe(ptr, 0, 5);
916 }
917
918 if (extendedflags & ID3_2_3_TAG_EXTENDEDFLAG_TAGRESTRICTIONS) {
919 ehsize += id3_2_3_render_int(ptr, 1, 1);
920 ehsize += id3_2_3_render_int(ptr, tag->restrictions, 1);
921 }
922
923 if (ehsize_ptr)
924 id3_2_3_render_syncsafe(&ehsize_ptr, ehsize, 4);
925
926 size += ehsize;
927 }
928
929 /* frames */
930
931 if (ptr)
932 frames_ptr = *ptr;
933
934 for (i = 0; i < tag->nframes; ++i)
935 size += id3_2_3_frame_render(tag->frames[i], ptr, tag->options);
936
937 /* padding */
938
939 if (!(flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT)) {
940 if (size > tag->paddedsize)
941 tag->paddedsize += TAG_PADDING + size;
942
943 if (size < tag->paddedsize)
944 size += id3_2_3_render_padding(ptr, 0, tag->paddedsize - size);
945 else if (tag->options & ID3_2_3_TAG_OPTION_UNSYNCHRONISATION) {
946 if (ptr == 0)
947 size += 1;
948 else {
949 if ((*ptr)[-1] == 0xff)
950 size += id3_2_3_render_padding(ptr, 0, 1);
951 }
952 }
953 }
954
955 /* patch tag size and CRC */
956
957 if (tagsize_ptr)
958 id3_2_3_render_syncsafe(&tagsize_ptr, size - 10, 4);
959
960 if (crc_ptr) {
961 id3_2_3_render_syncsafe(&crc_ptr,
962 id3_2_3_crc_calculate(frames_ptr, *ptr - frames_ptr), 5);
963 }
964
965 /* footer */
966
967 if (flags & ID3_2_3_TAG_FLAG_FOOTERPRESENT) {
968 size += id3_2_3_render_immediate(ptr, "3DI", 3);
969 size += id3_2_3_render_binary(ptr, header_ptr + 3, 7);
970 }
971
972 return size;
973}