summaryrefslogtreecommitdiff
path: root/src/libaudtag/id3/id3-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libaudtag/id3/id3-common.c')
-rw-r--r--src/libaudtag/id3/id3-common.c287
1 files changed, 232 insertions, 55 deletions
diff --git a/src/libaudtag/id3/id3-common.c b/src/libaudtag/id3/id3-common.c
index aec6eb2..2a8a87e 100644
--- a/src/libaudtag/id3/id3-common.c
+++ b/src/libaudtag/id3/id3-common.c
@@ -1,6 +1,6 @@
/*
* id3-common.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,7 +17,10 @@
* the use of this software.
*/
+#include "id3-common.h"
+
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <glib.h>
@@ -25,7 +28,11 @@
#include <libaudcore/audstrings.h>
#include "../util.h"
-#include "id3-common.h"
+
+#define ID3_ENCODING_LATIN1 0
+#define ID3_ENCODING_UTF16 1
+#define ID3_ENCODING_UTF16_BE 2
+#define ID3_ENCODING_UTF8 3
static void * memchr16 (const void * mem, int16_t chr, int len)
{
@@ -41,76 +48,246 @@ static void * memchr16 (const void * mem, int16_t chr, int len)
return NULL;
}
-char * convert_text (const char * text, int length, int encoding, bool_t
- nulled, int * _converted, const char * * after)
+void id3_strnlen (const char * data, int size, int encoding,
+ int * bytes_without_nul, int * bytes_with_nul)
{
- char * buffer = NULL;
- gsize converted = 0;
+ bool_t is16 = (encoding == ID3_ENCODING_UTF16 || encoding == ID3_ENCODING_UTF16_BE);
+ char * nul = is16 ? memchr16 (data, 0, size) : memchr (data, 0, size);
- TAGDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding,
- nulled);
+ if (nul)
+ {
+ if (bytes_without_nul)
+ * bytes_without_nul = nul - data;
+ if (bytes_with_nul)
+ * bytes_with_nul = is16 ? nul + 2 - data : nul + 1 - data;
+ }
+ else
+ {
+ if (bytes_without_nul)
+ * bytes_without_nul = size;
+ if (bytes_with_nul)
+ * bytes_with_nul = size;
+ }
+}
- if (nulled)
+char * id3_convert (const char * data, int size, int encoding)
+{
+ if (encoding == ID3_ENCODING_UTF16)
+ return str_convert (data, size, "UTF-16", "UTF-8");
+ else if (encoding == ID3_ENCODING_UTF16_BE)
+ return str_convert (data, size, "UTF-16BE", "UTF-8");
+ else
+ return str_to_utf8 (data, size);
+}
+
+char * id3_decode_text (const char * data, int size)
+{
+ if (size < 1)
+ return NULL;
+
+ return id3_convert ((const char *) data + 1, size - 1, data[0]);
+}
+
+void id3_associate_string (Tuple * tuple, int field, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
+
+ if (text && text[0])
{
- const char * null;
+ TAGDBG ("Field %i = %s.\n", field, text);
+ tuple_set_str (tuple, field, text);
+ }
- switch (encoding)
- {
- case 0:
- case 3:
- if ((null = memchr (text, 0, length)) == NULL)
- return NULL;
+ str_unref (text);
+}
- length = null - text;
- TAGDBG ("length before null = %d\n", length);
+void id3_associate_int (Tuple * tuple, int field, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
- if (after != NULL)
- * after = null + 1;
+ if (text && atoi (text) >= 0)
+ {
+ TAGDBG ("Field %i = %s.\n", field, text);
+ tuple_set_int (tuple, field, atoi (text));
+ }
- break;
- case 1:
- case 2:
- if ((null = memchr16 (text, 0, length)) == NULL)
- return NULL;
+ str_unref (text);
+}
- length = null - text;
- TAGDBG ("length before null = %d\n", length);
+void id3_decode_genre (Tuple * tuple, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
+ int numericgenre;
- if (after != NULL)
- * after = null + 2;
+ if (! text)
+ return;
- break;
- }
+ if (text[0] == '(')
+ numericgenre = atoi (text + 1);
+ else
+ numericgenre = atoi (text);
+
+ if (numericgenre > 0)
+ tuple_set_str (tuple, FIELD_GENRE, convert_numericgenre_to_text (numericgenre));
+ else
+ tuple_set_str (tuple, FIELD_GENRE, text);
+
+ str_unref (text);
+}
+
+void id3_decode_comment (Tuple * tuple, const char * data, int size)
+{
+ if (size < 4)
+ return;
+
+ int before_nul, after_nul;
+ id3_strnlen (data + 4, size - 4, data[0], & before_nul, & after_nul);
+
+ const char * lang = data + 1;
+ char * type = id3_convert (data + 4, before_nul, data[0]);
+ char * value = id3_convert (data + 4 + after_nul, size - 4 - after_nul, data[0]);
+
+ TAGDBG ("Comment: lang = %.3s, type = %s, value = %s.\n", lang, type, value);
+
+ if (type && ! type[0] && value) /* blank type = actual comment */
+ tuple_set_str (tuple, FIELD_COMMENT, value);
+
+ str_unref (type);
+ str_unref (value);
+}
+
+static bool_t decode_rva_block (const char * * _data, int * _size,
+ int * channel, int * adjustment, int * adjustment_unit, int * peak,
+ int * peak_unit)
+{
+ const char * data = * _data;
+ int size = * _size;
+ int peak_bits;
+
+ if (size < 4)
+ return FALSE;
+
+ * channel = data[0];
+ * adjustment = (char) data[1]; /* first byte is signed */
+ * adjustment = (* adjustment << 8) | data[2];
+ * adjustment_unit = 512;
+ peak_bits = data[3];
+
+ data += 4;
+ size -= 4;
+
+ TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n",
+ * channel, * adjustment, * adjustment_unit, peak_bits);
+
+ if (peak_bits > 0 && peak_bits < sizeof (int) * 8)
+ {
+ int bytes = (peak_bits + 7) / 8;
+ int count;
+
+ if (bytes > size)
+ return FALSE;
+
+ * peak = 0;
+ * peak_unit = 1 << peak_bits;
+
+ for (count = 0; count < bytes; count ++)
+ * peak = (* peak << 8) | data[count];
+
+ data += bytes;
+ size -= count;
+
+ TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit);
+ }
+ else
+ {
+ * peak = 0;
+ * peak_unit = 0;
}
- switch (encoding)
+ * _data = data;
+ * _size = size;
+ return TRUE;
+}
+
+void id3_decode_rva (Tuple * tuple, const char * data, int size)
+{
+ const char * domain;
+ int channel, adjustment, adjustment_unit, peak, peak_unit;
+
+ if (memchr (data, 0, size) == NULL)
+ return;
+
+ domain = data;
+
+ TAGDBG ("RVA domain: %s\n", domain);
+
+ size -= strlen (domain) + 1;
+ data += strlen (domain) + 1;
+
+ while (size > 0)
{
- case 0:
- case 3:;
- int converted_int = 0;
- buffer = str_to_utf8_full (text, length, NULL, & converted_int);
- converted = converted_int;
- break;
- case 1:
- if (text[0] == (char) 0xff)
- buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16LE", NULL,
- & converted, NULL);
+ if (! decode_rva_block (& data, & size, & channel, & adjustment,
+ & adjustment_unit, & peak, & peak_unit))
+ break;
+
+ if (channel != 1) /* specific channel? */
+ continue;
+
+ if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT) == TUPLE_INT)
+ adjustment = adjustment * (int64_t) tuple_get_int (tuple,
+ FIELD_GAIN_GAIN_UNIT) / adjustment_unit;
else
- buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16BE", NULL,
- & converted, NULL);
-
- break;
- case 2:
- buffer = g_convert (text, length, "UTF-8", "UTF-16BE", NULL,
- & converted, NULL);
- break;
+ tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, adjustment_unit);
+
+ if (peak_unit)
+ {
+ if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT) == TUPLE_INT)
+ peak = peak * (int64_t) tuple_get_int (tuple,
+ FIELD_GAIN_PEAK_UNIT) / peak_unit;
+ else
+ tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, peak_unit);
+ }
+
+ if (! g_ascii_strcasecmp (domain, "album"))
+ {
+ tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, adjustment);
+
+ if (peak_unit)
+ tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, peak);
+ }
+ else if (! g_ascii_strcasecmp (domain, "track"))
+ {
+ tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, adjustment);
+
+ if (peak_unit)
+ tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, peak);
+ }
}
+}
+
+bool_t id3_decode_picture (const char * data, int size, int * type,
+ void * * image_data, int64_t * image_size)
+{
+ const char * nul;
+ if (size < 2 || ! (nul = memchr (data + 1, 0, size - 2)))
+ return FALSE;
+
+ const char * body = nul + 2;
+ int body_size = data + size - body;
+
+ int before_nul2, after_nul2;
+ id3_strnlen (body, body_size, data[0], & before_nul2, & after_nul2);
+
+ const char * mime = data + 1;
+ char * desc = id3_convert (body, before_nul2, data[0]);
- TAGDBG ("length converted: %d\n", (int) converted);
- TAGDBG ("string: %s\n", buffer);
+ * type = nul[1];
+ * image_size = body_size - after_nul2;
+ * image_data = g_memdup (body + after_nul2, * image_size);
- if (_converted != NULL)
- * _converted = converted;
+ TAGDBG ("Picture: mime = %s, type = %d, desc = %s, size = %d.\n", mime,
+ * type, desc, (int) * image_size);
- return buffer;
+ str_unref (desc);
+ return TRUE;
}