annotate src/libid3tag-0.15.1b/tag.c @ 169:223a55898ab9 tip default

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