diff src/libid3tag-0.15.1b/frame.c @ 0:c7265573341e

Import initial set of sources
author Chris Cannam
date Mon, 18 Mar 2013 14:12:14 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libid3tag-0.15.1b/frame.c	Mon Mar 18 14:12:14 2013 +0000
@@ -0,0 +1,626 @@
+/*
+ * 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.15 2004/01/23 09:41:32 rob 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(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(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(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 = 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 = 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(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(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(id, data, end - data);
+    goto done;
+  }
+
+  /* generate the internal frame structure */
+
+  frame = id3_frame_new(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)
+      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)
+    id3_render_syncsafe(&size_ptr, size - 10, 4);
+  if (flags_ptr)
+    id3_render_int(&flags_ptr, flags, 2);
+
+  return size;
+}