annotate src/libid3tag-0.15.1b/tag.c @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
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 }