PageRenderTime 44ms CodeModel.GetById 12ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/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}