annotate src/libid3tag-0.15.1b/tag.c @ 23:619f715526df sv_v2.1

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