mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-10-31 14:13:50 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			635 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			635 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * libid3tag - ID3 tag manipulation library
 | |
|  * Copyright (C) 2000-2004 Underbit Technologies, Inc.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
|  *
 | |
|  * $Id: frame.c,v 1.4 2007-04-29 05:10:24 llucius Exp $
 | |
|  */
 | |
| 
 | |
| # ifdef HAVE_CONFIG_H
 | |
| #  include "config.h"
 | |
| # endif
 | |
| 
 | |
| # include "global.h"
 | |
| 
 | |
| # include <stdlib.h>
 | |
| # include <string.h>
 | |
| 
 | |
| # ifdef HAVE_ASSERT_H
 | |
| #  include <assert.h>
 | |
| # endif
 | |
| 
 | |
| # include "id3tag.h"
 | |
| # include "frame.h"
 | |
| # include "frametype.h"
 | |
| # include "compat.h"
 | |
| # include "field.h"
 | |
| # include "render.h"
 | |
| # include "parse.h"
 | |
| # include "util.h"
 | |
| 
 | |
| static
 | |
| int valid_idchar(char c)
 | |
| {
 | |
|   return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->validid()
 | |
|  * DESCRIPTION:	return true if the parameter string is a legal frame ID
 | |
|  */
 | |
| int id3_frame_validid(char const *id)
 | |
| {
 | |
|   return id &&
 | |
|     valid_idchar(id[0]) &&
 | |
|     valid_idchar(id[1]) &&
 | |
|     valid_idchar(id[2]) &&
 | |
|     valid_idchar(id[3]);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->new()
 | |
|  * DESCRIPTION:	allocate and return a new frame
 | |
|  */
 | |
| struct id3_frame *id3_frame_new(char const *id)
 | |
| {
 | |
|   struct id3_frametype const *frametype;
 | |
|   struct id3_frame *frame;
 | |
|   unsigned int i;
 | |
| 
 | |
|   if (!id3_frame_validid(id))
 | |
|     return 0;
 | |
| 
 | |
|   frametype = id3_frametype_lookup(id, 4);
 | |
|   if (frametype == 0) {
 | |
|     switch (id[0]) {
 | |
|     case 'T':
 | |
|       frametype = &id3_frametype_text;
 | |
|       break;
 | |
| 
 | |
|     case 'W':
 | |
|       frametype = &id3_frametype_url;
 | |
|       break;
 | |
| 
 | |
|     case 'X':
 | |
|     case 'Y':
 | |
|     case 'Z':
 | |
|       frametype = &id3_frametype_experimental;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       frametype = &id3_frametype_unknown;
 | |
|       if (id3_compat_lookup(id, 4))
 | |
| 	frametype = &id3_frametype_obsolete;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   frame = malloc(sizeof(*frame) + frametype->nfields * sizeof(*frame->fields));
 | |
|   if (frame) {
 | |
|     frame->id[0] = id[0];
 | |
|     frame->id[1] = id[1];
 | |
|     frame->id[2] = id[2];
 | |
|     frame->id[3] = id[3];
 | |
|     frame->id[4] = 0;
 | |
| 
 | |
|     frame->description       = frametype->description;
 | |
|     frame->refcount          = 0;
 | |
|     frame->flags             = frametype->defaultflags;
 | |
|     frame->group_id          = 0;
 | |
|     frame->encryption_method = 0;
 | |
|     frame->encoded           = 0;
 | |
|     frame->encoded_length    = 0;
 | |
|     frame->decoded_length    = 0;
 | |
|     frame->nfields           = frametype->nfields;
 | |
|     frame->fields            = (union id3_field *) &frame[1];
 | |
| 
 | |
|     for (i = 0; i < frame->nfields; ++i)
 | |
|       id3_field_init(&frame->fields[i], frametype->fields[i]);
 | |
|   }
 | |
| 
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| void id3_frame_delete(struct id3_frame *frame)
 | |
| {
 | |
|   assert(frame);
 | |
| 
 | |
|   if (frame->refcount == 0) {
 | |
|     unsigned int i;
 | |
| 
 | |
|     for (i = 0; i < frame->nfields; ++i)
 | |
|       id3_field_finish(&frame->fields[i]);
 | |
| 
 | |
|     if (frame->encoded)
 | |
|       free(frame->encoded);
 | |
| 
 | |
|     free(frame);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->addref()
 | |
|  * DESCRIPTION:	add an external reference to a frame
 | |
|  */
 | |
| void id3_frame_addref(struct id3_frame *frame)
 | |
| {
 | |
|   assert(frame);
 | |
| 
 | |
|   ++frame->refcount;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->delref()
 | |
|  * DESCRIPTION:	remove an external reference to a frame
 | |
|  */
 | |
| void id3_frame_delref(struct id3_frame *frame)
 | |
| {
 | |
|   assert(frame && frame->refcount > 0);
 | |
| 
 | |
|   --frame->refcount;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->field()
 | |
|  * DESCRIPTION:	return a pointer to a field in a frame
 | |
|  */
 | |
| union id3_field *id3_frame_field(struct id3_frame const *frame,
 | |
| 				 unsigned int index)
 | |
| {
 | |
|   assert(frame);
 | |
| 
 | |
|   return (index < frame->nfields) ? &frame->fields[index] : 0;
 | |
| }
 | |
| 
 | |
| static
 | |
| struct id3_frame *obsolete(char const *id, id3_byte_t const *data,
 | |
| 			   id3_length_t length)
 | |
| {
 | |
|   struct id3_frame *frame;
 | |
| 
 | |
|   frame = id3_frame_new(ID3_FRAME_OBSOLETE);
 | |
|   if (frame) {
 | |
|     if (id3_field_setframeid(&frame->fields[0], id) == -1 ||
 | |
| 	id3_field_setbinarydata(&frame->fields[1], data, length) == -1)
 | |
|       goto fail;
 | |
|   }
 | |
| 
 | |
|   if (0) {
 | |
|   fail:
 | |
|     if (frame) {
 | |
|       id3_frame_delete(frame);
 | |
|       frame = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| static
 | |
| struct id3_frame *unparseable(char const *id, id3_byte_t const **ptr,
 | |
| 			      id3_length_t length, int flags,
 | |
| 			      int group_id, int encryption_method,
 | |
| 			      id3_length_t decoded_length)
 | |
| {
 | |
|   struct id3_frame *frame = 0;
 | |
|   id3_byte_t *mem;
 | |
| 
 | |
|   mem = malloc(length ? length : 1);
 | |
|   if (mem == 0)
 | |
|     goto fail;
 | |
| 
 | |
|   frame = id3_frame_new(id);
 | |
|   if (frame == 0)
 | |
|     free(mem);
 | |
|   else {
 | |
|     memcpy(mem, *ptr, length);
 | |
| 
 | |
|     frame->flags             = flags;
 | |
|     frame->group_id          = group_id;
 | |
|     frame->encryption_method = encryption_method;
 | |
|     frame->encoded           = mem;
 | |
|     frame->encoded_length    = length;
 | |
|     frame->decoded_length    = decoded_length;
 | |
|   }
 | |
| 
 | |
|   if (0) {
 | |
|   fail:
 | |
|     ;
 | |
|   }
 | |
| 
 | |
|   *ptr += length;
 | |
| 
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| static
 | |
| int parse_data(struct id3_frame *frame,
 | |
| 	       id3_byte_t const *data, id3_length_t length)
 | |
| {
 | |
|   enum id3_field_textencoding encoding;
 | |
|   id3_byte_t const *end;
 | |
|   unsigned int i;
 | |
| 
 | |
|   encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1;
 | |
| 
 | |
|   end = data + length;
 | |
| 
 | |
|   for (i = 0; i < frame->nfields; ++i) {
 | |
|     if (id3_field_parse(&frame->fields[i], &data, end - data, &encoding) == -1)
 | |
|       return -1;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->parse()
 | |
|  * DESCRIPTION:	parse raw frame data according to the specified ID3 tag version
 | |
|  */
 | |
| struct id3_frame *id3_frame_parse(id3_byte_t const **ptr, id3_length_t length,
 | |
| 				  unsigned int version)
 | |
| {
 | |
|   struct id3_frame *frame = 0;
 | |
|   id3_byte_t const *id, *end, *data;
 | |
|   id3_length_t size, decoded_length = 0;
 | |
|   int flags = 0, group_id = 0, encryption_method = 0;
 | |
|   struct id3_compat const *compat = 0;
 | |
|   id3_byte_t *mem = 0;
 | |
|   char xid[4];
 | |
| 
 | |
|   id  = *ptr;
 | |
|   end = *ptr + length;
 | |
| 
 | |
|   if (ID3_TAG_VERSION_MAJOR(version) < 4) {
 | |
|     switch (ID3_TAG_VERSION_MAJOR(version)) {
 | |
|     case 2:
 | |
|       if (length < 6)
 | |
| 	goto fail;
 | |
| 
 | |
|       compat = id3_compat_lookup((char const *)id, 3);
 | |
| 
 | |
|       *ptr += 3;
 | |
|       size  = id3_parse_uint(ptr, 3);
 | |
| 
 | |
|       if (size > end - *ptr)
 | |
| 	goto fail;
 | |
| 
 | |
|       end = *ptr + size;
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case 3:
 | |
|       if (length < 10)
 | |
| 	goto fail;
 | |
| 
 | |
|       compat = id3_compat_lookup((char const *)id, 4);
 | |
| 
 | |
|       *ptr += 4;
 | |
|       size  = id3_parse_uint(ptr, 4);
 | |
|       flags = id3_parse_uint(ptr, 2);
 | |
| 
 | |
|       if (size > end - *ptr)
 | |
| 	goto fail;
 | |
| 
 | |
|       end = *ptr + size;
 | |
| 
 | |
|       if (flags & (ID3_FRAME_FLAG_FORMATFLAGS & ~0x00e0)) {
 | |
| 	frame = unparseable((char const *)id, ptr, end - *ptr, 0, 0, 0, 0);
 | |
| 	goto done;
 | |
|       }
 | |
| 
 | |
|       flags =
 | |
| 	((flags >> 1) & ID3_FRAME_FLAG_STATUSFLAGS) |
 | |
| 	((flags >> 4) & (ID3_FRAME_FLAG_COMPRESSION |
 | |
| 			 ID3_FRAME_FLAG_ENCRYPTION)) |
 | |
| 	((flags << 1) & ID3_FRAME_FLAG_GROUPINGIDENTITY);
 | |
| 
 | |
|       if (flags & ID3_FRAME_FLAG_COMPRESSION) {
 | |
| 	if (end - *ptr < 4)
 | |
| 	  goto fail;
 | |
| 
 | |
| 	decoded_length = id3_parse_uint(ptr, 4);
 | |
|       }
 | |
| 
 | |
|       if (flags & ID3_FRAME_FLAG_ENCRYPTION) {
 | |
| 	if (end - *ptr < 1)
 | |
| 	  goto fail;
 | |
| 
 | |
| 	encryption_method = id3_parse_uint(ptr, 1);
 | |
|       }
 | |
| 
 | |
|       if (flags & ID3_FRAME_FLAG_GROUPINGIDENTITY) {
 | |
| 	if (end - *ptr < 1)
 | |
| 	  goto fail;
 | |
| 
 | |
| 	group_id = id3_parse_uint(ptr, 1);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       goto fail;
 | |
|     }
 | |
| 
 | |
|     /* canonicalize frame ID for ID3v2.4 */
 | |
| 
 | |
|     if (compat && compat->equiv)
 | |
|       id = (id3_byte_t const *)compat->equiv;
 | |
|     else if (ID3_TAG_VERSION_MAJOR(version) == 2) {
 | |
|       xid[0] = 'Y';
 | |
|       xid[1] = id[0];
 | |
|       xid[2] = id[1];
 | |
|       xid[3] = id[2];
 | |
| 
 | |
|       id = (id3_byte_t const *)xid;
 | |
| 
 | |
|       flags |=
 | |
| 	ID3_FRAME_FLAG_TAGALTERPRESERVATION |
 | |
| 	ID3_FRAME_FLAG_FILEALTERPRESERVATION;
 | |
|     }
 | |
|   }
 | |
|   else {  /* ID3v2.4 */
 | |
|     if (length < 10)
 | |
|       goto fail;
 | |
| 
 | |
|     *ptr += 4;
 | |
|     size  = id3_parse_syncsafe(ptr, 4);
 | |
|     flags = id3_parse_uint(ptr, 2);
 | |
| 
 | |
|     if (size > end - *ptr)
 | |
|       goto fail;
 | |
| 
 | |
|     end = *ptr + size;
 | |
| 
 | |
|     if (flags & (ID3_FRAME_FLAG_FORMATFLAGS & ~ID3_FRAME_FLAG_KNOWNFLAGS)) {
 | |
|       frame = unparseable((char const *)id, ptr, end - *ptr, flags, 0, 0, 0);
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|     if (flags & ID3_FRAME_FLAG_GROUPINGIDENTITY) {
 | |
|       if (end - *ptr < 1)
 | |
| 	goto fail;
 | |
| 
 | |
|       group_id = id3_parse_uint(ptr, 1);
 | |
|     }
 | |
| 
 | |
|     if ((flags & ID3_FRAME_FLAG_COMPRESSION) &&
 | |
| 	!(flags & ID3_FRAME_FLAG_DATALENGTHINDICATOR))
 | |
|       goto fail;
 | |
| 
 | |
|     if (flags & ID3_FRAME_FLAG_ENCRYPTION) {
 | |
|       if (end - *ptr < 1)
 | |
| 	goto fail;
 | |
| 
 | |
|       encryption_method = id3_parse_uint(ptr, 1);
 | |
|     }
 | |
| 
 | |
|     if (flags & ID3_FRAME_FLAG_DATALENGTHINDICATOR) {
 | |
|       if (end - *ptr < 4)
 | |
| 	goto fail;
 | |
| 
 | |
|       decoded_length = id3_parse_syncsafe(ptr, 4);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   data = *ptr;
 | |
|   *ptr = end;
 | |
| 
 | |
|   /* undo frame encodings */
 | |
| 
 | |
|   if ((flags & ID3_FRAME_FLAG_UNSYNCHRONISATION) && end - data > 0) {
 | |
|     mem = malloc(end - data);
 | |
|     if (mem == 0)
 | |
|       goto fail;
 | |
| 
 | |
|     memcpy(mem, data, end - data);
 | |
| 
 | |
|     end  = mem + id3_util_deunsynchronise(mem, end - data);
 | |
|     data = mem;
 | |
|   }
 | |
| 
 | |
|   if (flags & ID3_FRAME_FLAG_ENCRYPTION) {
 | |
|     frame = unparseable((char const *)id, &data, end - data, flags,
 | |
| 			group_id, encryption_method, decoded_length);
 | |
|     goto done;
 | |
|   }
 | |
| 
 | |
|   if (flags & ID3_FRAME_FLAG_COMPRESSION) {
 | |
|     id3_byte_t *decomp;
 | |
| 
 | |
|     decomp = id3_util_decompress(data, end - data, decoded_length);
 | |
|     if (decomp == 0)
 | |
|       goto fail;
 | |
| 
 | |
|     if (mem)
 | |
|       free(mem);
 | |
| 
 | |
|     data = mem = decomp;
 | |
|     end  = data + decoded_length;
 | |
|   }
 | |
| 
 | |
|   /* check for obsolescence */
 | |
| 
 | |
|   if (compat && !compat->equiv) {
 | |
|     frame = obsolete((char const *)id, data, end - data);
 | |
|     goto done;
 | |
|   }
 | |
| 
 | |
|   /* generate the internal frame structure */
 | |
| 
 | |
|   frame = id3_frame_new((char const *)id);
 | |
|   if (frame) {
 | |
|     frame->flags    = flags;
 | |
|     frame->group_id = group_id;
 | |
| 
 | |
|     if (compat && compat->translate) {
 | |
|       if (compat->translate(frame, compat->id, data, end - data) == -1)
 | |
| 	goto fail;
 | |
|     }
 | |
|     else {
 | |
|       if (parse_data(frame, data, end - data) == -1)
 | |
| 	goto fail;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (0) {
 | |
|   fail:
 | |
|     if (frame) {
 | |
|       id3_frame_delete(frame);
 | |
|       frame = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  done:
 | |
|   if (mem)
 | |
|     free(mem);
 | |
| 
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| static
 | |
| id3_length_t render_data(id3_byte_t **ptr,
 | |
| 			 union id3_field *fields, unsigned int length)
 | |
| {
 | |
|   id3_length_t size = 0;
 | |
|   enum id3_field_textencoding encoding;
 | |
|   unsigned int i;
 | |
| 
 | |
|   encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1;
 | |
| 
 | |
|   for (i = 0; i < length; ++i)
 | |
|     size += id3_field_render(&fields[i], ptr, &encoding, i < length - 1);
 | |
| 
 | |
|   return size;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NAME:	frame->render()
 | |
|  * DESCRIPTION:	render a single, complete frame
 | |
|  */
 | |
| id3_length_t id3_frame_render(struct id3_frame const *frame,
 | |
| 			      id3_byte_t **ptr, int options)
 | |
| {
 | |
|   id3_length_t size = 0, decoded_length, datalen;
 | |
|   id3_byte_t *size_ptr = 0, *flags_ptr = 0, *data = 0;
 | |
|   int flags;
 | |
| 
 | |
|   assert(frame);
 | |
| 
 | |
|   if ((frame->flags & ID3_FRAME_FLAG_TAGALTERPRESERVATION) ||
 | |
|       ((options & ID3_TAG_OPTION_FILEALTERED) &&
 | |
|        (frame->flags & ID3_FRAME_FLAG_FILEALTERPRESERVATION)))
 | |
|     return 0;
 | |
| 
 | |
|   /* a frame must be at least 1 byte big, excluding the header */
 | |
| 
 | |
|   decoded_length = render_data(0, frame->fields, frame->nfields);
 | |
|   if (decoded_length == 0 && frame->encoded == 0)
 | |
|     return 0;
 | |
| 
 | |
|   /* header */
 | |
| 
 | |
|   size += id3_render_immediate(ptr, frame->id, 4);
 | |
| 
 | |
|   if (ptr)
 | |
|     size_ptr = *ptr;
 | |
| 
 | |
|   size += id3_render_syncsafe(ptr, 0, 4);
 | |
| 
 | |
|   if (ptr)
 | |
|     flags_ptr = *ptr;
 | |
| 
 | |
|   flags = frame->flags;
 | |
| 
 | |
|   size += id3_render_int(ptr, flags, 2);
 | |
| 
 | |
|   if (flags & (ID3_FRAME_FLAG_FORMATFLAGS & ~ID3_FRAME_FLAG_KNOWNFLAGS)) {
 | |
|     size += id3_render_binary(ptr, frame->encoded, frame->encoded_length);
 | |
|     if (size_ptr) {
 | |
|       if (options & ID3_TAG_OPTION_ID3V2_3)
 | |
|         id3_render_int(&size_ptr, size - 10, 4);
 | |
|       else
 | |
|       id3_render_syncsafe(&size_ptr, size - 10, 4);
 | |
|     }
 | |
| 
 | |
|     return size;
 | |
|   }
 | |
| 
 | |
|   flags &= ID3_FRAME_FLAG_KNOWNFLAGS;
 | |
| 
 | |
|   flags &= ~ID3_FRAME_FLAG_UNSYNCHRONISATION;
 | |
|   if (options & ID3_TAG_OPTION_UNSYNCHRONISATION)
 | |
|     flags |= ID3_FRAME_FLAG_UNSYNCHRONISATION;
 | |
| 
 | |
|   if (!(flags & ID3_FRAME_FLAG_ENCRYPTION)) {
 | |
|     flags &= ~ID3_FRAME_FLAG_COMPRESSION;
 | |
|     if (options & ID3_TAG_OPTION_COMPRESSION)
 | |
|       flags |= ID3_FRAME_FLAG_COMPRESSION | ID3_FRAME_FLAG_DATALENGTHINDICATOR;
 | |
|   }
 | |
| 
 | |
|   if (flags & ID3_FRAME_FLAG_GROUPINGIDENTITY)
 | |
|     size += id3_render_int(ptr, frame->group_id, 1);
 | |
|   if (flags & ID3_FRAME_FLAG_ENCRYPTION)
 | |
|     size += id3_render_int(ptr, frame->encryption_method, 1);
 | |
|   if (flags & ID3_FRAME_FLAG_DATALENGTHINDICATOR) {
 | |
|     if (flags & ID3_FRAME_FLAG_ENCRYPTION)
 | |
|       decoded_length = frame->decoded_length;
 | |
|     size += id3_render_syncsafe(ptr, decoded_length, 4);
 | |
|   }
 | |
| 
 | |
|   if (ptr)
 | |
|     data = *ptr;
 | |
| 
 | |
|   if (flags & ID3_FRAME_FLAG_ENCRYPTION)
 | |
|     datalen = id3_render_binary(ptr, frame->encoded, frame->encoded_length);
 | |
|   else {
 | |
|     if (ptr == 0)
 | |
|       datalen = decoded_length;
 | |
|     else {
 | |
|       datalen = render_data(ptr, frame->fields, frame->nfields);
 | |
| 
 | |
|       if (flags & ID3_FRAME_FLAG_COMPRESSION) {
 | |
| 	id3_byte_t *comp;
 | |
| 	id3_length_t complen;
 | |
| 
 | |
| 	comp = id3_util_compress(data, datalen, &complen);
 | |
| 	if (comp == 0)
 | |
| 	  flags &= ~ID3_FRAME_FLAG_COMPRESSION;
 | |
| 	else {
 | |
| 	  *ptr = data;
 | |
| 	  datalen = id3_render_binary(ptr, comp, complen);
 | |
| 
 | |
| 	  free(comp);
 | |
| 	}
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* unsynchronisation */
 | |
| 
 | |
|   if (flags & ID3_FRAME_FLAG_UNSYNCHRONISATION) {
 | |
|     if (data == 0)
 | |
|       datalen *= 2;
 | |
|     else {
 | |
|       id3_length_t newlen;
 | |
| 
 | |
|       newlen = id3_util_unsynchronise(data, datalen);
 | |
|       if (newlen == datalen)
 | |
| 	flags &= ~ID3_FRAME_FLAG_UNSYNCHRONISATION;
 | |
|       else {
 | |
| 	*ptr   += newlen - datalen;
 | |
| 	datalen = newlen;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   size += datalen;
 | |
| 
 | |
|   /* patch size and flags */
 | |
| 
 | |
|   if (size_ptr) {
 | |
|     if (options & ID3_TAG_OPTION_ID3V2_3)
 | |
|       id3_render_int(&size_ptr, size - 10, 4);
 | |
|     else
 | |
|     id3_render_syncsafe(&size_ptr, size - 10, 4);
 | |
|   }
 | |
|   if (flags_ptr)
 | |
|     id3_render_int(&flags_ptr, flags, 2);
 | |
| 
 | |
|   return size;
 | |
| }
 |