summaryrefslogtreecommitdiff
path: root/plugins/wma/asfheader.c
diff options
context:
space:
mode:
authorGravatar waker <wakeroid@gmail.com>2012-12-29 23:21:19 +0100
committerGravatar waker <wakeroid@gmail.com>2013-01-12 19:37:05 +0100
commit94e2ef5704b74ebca17d9add9b6c3587514123e2 (patch)
tree5cbff3b2be9092522a0e935a3e05d6ff08a3e875 /plugins/wma/asfheader.c
parent282d6a989995509a3a3281fef43f9ca3fe6dce2a (diff)
new wma plugin WIP
Diffstat (limited to 'plugins/wma/asfheader.c')
-rw-r--r--plugins/wma/asfheader.c814
1 files changed, 814 insertions, 0 deletions
diff --git a/plugins/wma/asfheader.c b/plugins/wma/asfheader.c
new file mode 100644
index 00000000..95dd0299
--- /dev/null
+++ b/plugins/wma/asfheader.c
@@ -0,0 +1,814 @@
+// this file is a modified version for deadbeef player
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * $Id$
+ *
+ * Copyright (C) 2007 Dave Chapman
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include "../../deadbeef.h"
+
+#include <libasf/asf.h>
+
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+//#define trace(...) { fprintf (stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+#define DEBUGF trace
+
+extern DB_functions_t *deadbeef;
+
+enum {
+ AFMT_WMAPRO = 1,
+ AFMT_WMAVOICE = 2,
+};
+
+static inline uint16_t swap16(uint16_t value)
+ /*
+ result[15..8] = value[ 7..0];
+ result[ 7..0] = value[15..8];
+ */
+{
+ return (value >> 8) | (value << 8);
+}
+
+
+static inline uint32_t swap32(uint32_t value)
+ /*
+ result[31..24] = value[ 7.. 0];
+ result[23..16] = value[15.. 8];
+ result[15.. 8] = value[23..16];
+ result[ 7.. 0] = value[31..24];
+ */
+{
+ uint32_t hi = swap16(value >> 16);
+ uint32_t lo = swap16(value & 0xffff);
+ return (lo << 16) | hi;
+}
+
+#if WORDS_BIGENDIAN
+#define read_uint16be(fd,buf) deadbeef->fread((buf), 2, 1, (fd))
+#define read_uint32be(fd,buf) deadbeef->fread((buf), 4, 1, (fd))
+#define read_uint64be(fd,buf) deadbeef->fread((buf), 8, 1, (fd))
+int read_uint16le(DB_FILE *fd, uint16_t* buf);
+int read_uint32le(DB_FILE *fd, uint32_t* buf);
+int read_uint64le(DB_FILE *fd, uint64_t* buf);
+#define letoh16(x) swap16(x)
+#define letoh32(x) swap32(x)
+#define htole16(x) swap16(x)
+#define htole32(x) swap32(x)
+#define betoh16(x) (x)
+#define betoh32(x) (x)
+#define htobe16(x) (x)
+#define htobe32(x) (x)
+#define swap_odd_even_be32(x) swap_odd_even32(x)
+#define swap_odd_even_le32(x) (x)
+#else
+#define letoh16(x) (x)
+#define letoh32(x) (x)
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define betoh16(x) swap16(x)
+#define betoh32(x) swap32(x)
+//#define htobe16(x) swap16(x)
+//#define htobe32(x) swap32(x)
+#define swap_odd_even_be32(x) (x)
+#define swap_odd_even_le32(x) swap_odd_even32(x)
+int read_uint16be(DB_FILE *fd, uint16_t* buf);
+int read_uint32be(DB_FILE *fd, uint32_t* buf);
+int read_uint64be(DB_FILE *fd, uint64_t* buf);
+#define read_uint16le(fd,buf) deadbeef->fread((buf), 2, 1, (fd))
+#define read_uint32le(fd,buf) deadbeef->fread((buf), 4, 1, (fd))
+#define read_uint64le(fd,buf) deadbeef->fread((buf), 8, 1, (fd))
+#endif
+
+
+#if !WORDS_BIGENDIAN
+/* Read an unsigned 16-bit integer from a big-endian file. */
+int read_uint16be(DB_FILE *fd, uint16_t* buf)
+{
+ size_t n;
+
+ n = deadbeef->fread(buf, 1, 2, fd);
+ *buf = betoh16(*buf);
+ return n;
+}
+/* Read an unsigned 32-bit integer from a big-endian file. */
+int read_uint32be(DB_FILE *fd, uint32_t* buf)
+{
+ size_t n;
+
+ n = deadbeef->fread(buf, 1, 4, fd);
+ *buf = betoh32(*buf);
+ return n;
+}
+/* Read an unsigned 64-bit integer from a big-endian file. */
+int read_uint64be(DB_FILE *fd, uint64_t* buf)
+{
+ size_t n;
+ uint8_t data[8];
+ int i;
+
+ n = deadbeef->fread(data, 1, 8, fd);
+
+ for (i=0, *buf=0; i<=7; i++) {
+ *buf <<= 8;
+ *buf |= data[i];
+ }
+ return n;
+}
+#else
+/* Read unsigned integers from a little-endian file. */
+int read_uint16le(DB_FILE *fd, uint16_t* buf)
+{
+ size_t n;
+
+ n = deadbeef->fread((char*) buf, 1, 2, fd);
+ *buf = letoh16(*buf);
+ return n;
+}
+int read_uint32le(DB_FILE *fd, uint32_t* buf)
+{
+ size_t n;
+
+ n = deadbeef->fread(buf, 1, 4, fd);
+ *buf = letoh32(*buf);
+ return n;
+}
+int read_uint64le(DB_FILE *fd, uint64_t* buf)
+{
+ size_t n;
+ uint8_t data[8];
+ int i;
+
+ n = deadbeef->fread(data, 1, 8, fd);
+
+ for (i=7, *buf=0; i>=0; i--) {
+ *buf <<= 8;
+ *buf |= data[i];
+ }
+
+ return n;
+}
+#endif
+
+/* TODO: Just read the GUIDs into a 16-byte array, and use memcmp to compare */
+struct guid_s {
+ uint32_t v1;
+ uint16_t v2;
+ uint16_t v3;
+ uint8_t v4[8];
+};
+typedef struct guid_s guid_t;
+
+struct asf_object_s {
+ guid_t guid;
+ uint64_t size;
+ uint64_t datalen;
+};
+typedef struct asf_object_s asf_object_t;
+
+static const guid_t asf_guid_null =
+{0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
+/* top level object guids */
+
+static const guid_t asf_guid_header =
+{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+
+static const guid_t asf_guid_data =
+{0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+
+static const guid_t asf_guid_index =
+{0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
+
+/* header level object guids */
+
+static const guid_t asf_guid_file_properties =
+{0x8cabdca1, 0xa947, 0x11cf, {0x8E, 0xe4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+
+static const guid_t asf_guid_stream_properties =
+{0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+
+static const guid_t asf_guid_content_description =
+{0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+
+static const guid_t asf_guid_extended_content_description =
+{0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
+
+static const guid_t asf_guid_content_encryption =
+{0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7, 0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e}};
+
+static const guid_t asf_guid_extended_content_encryption =
+{0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35, 0xda, 0xe0, 0x7e, 0xe9, 0x28, 0x9c}};
+
+/* stream type guids */
+
+static const guid_t asf_guid_stream_type_audio =
+{0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+
+static int asf_guid_match(const guid_t *guid1, const guid_t *guid2)
+{
+ if((guid1->v1 != guid2->v1) ||
+ (guid1->v2 != guid2->v2) ||
+ (guid1->v3 != guid2->v3) ||
+ (memcmp(guid1->v4, guid2->v4, 8))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Read the 16 byte GUID from a file */
+static void asf_readGUID(DB_FILE *fd, guid_t* guid)
+{
+ read_uint32le(fd, &guid->v1);
+ read_uint16le(fd, &guid->v2);
+ read_uint16le(fd, &guid->v3);
+ deadbeef->fread(guid->v4, 8, 1, fd);
+}
+
+static void asf_read_object_header(asf_object_t *obj, DB_FILE *fd)
+{
+ asf_readGUID(fd, &obj->guid);
+ read_uint64le(fd, &obj->size);
+ obj->datalen = 0;
+}
+
+/* Parse an integer from the extended content object - we always
+ convert to an int, regardless of native format.
+*/
+static int asf_intdecode(DB_FILE *fd, int type, int length)
+{
+ uint16_t tmp16;
+ uint32_t tmp32;
+ uint64_t tmp64;
+
+ if (type == 3) {
+ read_uint32le(fd, &tmp32);
+ deadbeef->fseek(fd,length - 4,SEEK_CUR);
+ return (int)tmp32;
+ } else if (type == 4) {
+ read_uint64le(fd, &tmp64);
+ deadbeef->fseek(fd,length - 8,SEEK_CUR);
+ return (int)tmp64;
+ } else if (type == 5) {
+ read_uint16le(fd, &tmp16);
+ deadbeef->fseek(fd,length - 2,SEEK_CUR);
+ return (int)tmp16;
+ }
+
+ return 0;
+}
+
+/* Decode a LE utf16 string from a disk buffer into a fixed-sized
+ utf8 buffer.
+*/
+#define MASK 0xC0 /* 11000000 */
+#define COMP 0x80 /* 10x */
+
+static const unsigned char utf8comp[6] =
+{
+ 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
+};
+
+/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */
+unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8)
+{
+ int tail = 0;
+
+ if (ucs > 0x7F)
+ while (ucs >> (5*tail + 6))
+ tail++;
+
+ *utf8++ = (ucs >> (6*tail)) | utf8comp[tail];
+ while (tail--)
+ *utf8++ = ((ucs >> (6*tail)) & (MASK ^ 0xFF)) | COMP;
+
+ return utf8;
+}
+
+
+static void asf_utf16LEdecode(DB_FILE *fd,
+ uint16_t utf16bytes,
+ unsigned char **utf8,
+ int* utf8bytes
+ )
+{
+ unsigned long ucs;
+ int n;
+ unsigned char utf16buf[256];
+ unsigned char* utf16 = utf16buf;
+ unsigned char* newutf8;
+
+ n = deadbeef->fread(utf16buf, 1, MIN(sizeof(utf16buf), utf16bytes), fd);
+ utf16bytes -= n;
+
+ while (n > 0) {
+ /* Check for a surrogate pair */
+ if (utf16[1] >= 0xD8 && utf16[1] < 0xE0) {
+ if (n < 4) {
+ /* Run out of utf16 bytes, read some more */
+ utf16buf[0] = utf16[0];
+ utf16buf[1] = utf16[1];
+
+ n = deadbeef->fread(utf16buf + 2, 1, MIN(sizeof(utf16buf)-2, utf16bytes), fd);
+ utf16 = utf16buf;
+ utf16bytes -= n;
+ n += 2;
+ }
+
+ if (n < 4) {
+ /* Truncated utf16 string, abort */
+ break;
+ }
+ ucs = 0x10000 + ((utf16[0] << 10) | ((utf16[1] - 0xD8) << 18)
+ | utf16[2] | ((utf16[3] - 0xDC) << 8));
+ utf16 += 4;
+ n -= 4;
+ } else {
+ ucs = (utf16[0] | (utf16[1] << 8));
+ utf16 += 2;
+ n -= 2;
+ }
+
+ if (*utf8bytes > 6) {
+ newutf8 = utf8encode(ucs, *utf8);
+ *utf8bytes -= (newutf8 - *utf8);
+ *utf8 += (newutf8 - *utf8);
+ }
+
+ /* We have run out of utf16 bytes, read more if available */
+ if ((n == 0) && (utf16bytes > 0)) {
+ n = deadbeef->fread(utf16buf, 1, MIN(sizeof(utf16buf), utf16bytes), fd);
+ utf16 = utf16buf;
+ utf16bytes -= n;
+ }
+ }
+
+ *utf8[0] = 0;
+ --*utf8bytes;
+
+ if (utf16bytes > 0) {
+ /* Skip any remaining bytes */
+ deadbeef->fseek(fd, utf16bytes, SEEK_CUR);
+ }
+ return;
+}
+
+static int asf_parse_header(DB_FILE *fd, asf_waveformatex_t* wfx, DB_playItem_t *it)
+{
+ asf_object_t current;
+ asf_object_t header;
+ uint64_t datalen;
+ int i;
+ int fileprop = 0;
+ uint64_t play_duration;
+ uint16_t flags;
+ uint32_t subobjects;
+ uint8_t utf8buf[512];
+ int codectype = 0;
+ unsigned char id3buffer[2048];
+ unsigned char *id3buf = id3buffer;
+ int id3buf_remaining = sizeof(id3buffer);
+
+ asf_read_object_header((asf_object_t *) &header, fd);
+
+ DEBUGF("header.size=%d\n",(int)header.size);
+ if (header.size < 30) {
+ /* invalid size for header object */
+ return ASF_ERROR_OBJECT_SIZE;
+ }
+
+ read_uint32le(fd, &subobjects);
+
+ /* Two reserved bytes - do we need to read them? */
+ deadbeef->fseek(fd, 2, SEEK_CUR);
+
+ DEBUGF("Read header - size=%d, subobjects=%d\n",(int)header.size, (int)subobjects);
+
+ if (subobjects > 0) {
+ header.datalen = header.size - 30;
+
+ /* TODO: Check that we have datalen bytes left in the file */
+ datalen = header.datalen;
+
+ for (i=0; i<(int)subobjects; i++) {
+ DEBUGF("Parsing header object %d - datalen=%d\n",i,(int)datalen);
+ if (datalen < 24) {
+ DEBUGF("not enough data for reading object\n");
+ break;
+ }
+
+ asf_read_object_header(&current, fd);
+
+ if (current.size > datalen || current.size < 24) {
+ DEBUGF("invalid object size - current.size=%d, datalen=%d\n",(int)current.size,(int)datalen);
+ break;
+ }
+
+ if (asf_guid_match(&current.guid, &asf_guid_file_properties)) {
+ if (current.size < 104)
+ return ASF_ERROR_OBJECT_SIZE;
+
+ if (fileprop) {
+ /* multiple file properties objects not allowed */
+ return ASF_ERROR_INVALID_OBJECT;
+ }
+
+ fileprop = 1;
+
+ /* Get the number of logical packets - uint64_t at offset 32
+ * (Big endian byte order) */
+ deadbeef->fseek(fd, 32, SEEK_CUR);
+ read_uint64le(fd, &wfx->numpackets);
+ read_uint64le(fd, &wfx->play_duration);
+ read_uint64le(fd, &wfx->send_duration);
+ read_uint64le(fd, &wfx->preroll);
+ read_uint32le(fd, &wfx->flags);
+
+// DEBUGF("****** length = %lld\n", wfx->play_duration);
+
+ /* Read the packet size - uint32_t at offset 68 */
+// deadbeef->fseek(fd, 20, SEEK_CUR);
+ read_uint32le(fd, &wfx->packet_size);
+ read_uint32le(fd, &wfx->max_packet_size);
+
+ /* Skip bytes remaining in object */
+ deadbeef->fseek(fd, current.size - 24 - 72 - 4, SEEK_CUR);
+ } else if (asf_guid_match(&current.guid, &asf_guid_stream_properties)) {
+ guid_t guid;
+ uint32_t propdatalen;
+
+ if (current.size < 78)
+ return ASF_ERROR_OBJECT_SIZE;
+
+#if 0
+ asf_byteio_getGUID(&guid, current->data);
+ datalen = asf_byteio_getDWLE(current->data + 40);
+ flags = asf_byteio_getWLE(current->data + 48);
+#endif
+
+ asf_readGUID(fd, &guid);
+
+ deadbeef->fseek(fd, 24, SEEK_CUR);
+ read_uint32le(fd, &propdatalen);
+ deadbeef->fseek(fd, 4, SEEK_CUR);
+ read_uint16le(fd, &flags);
+
+ if (!asf_guid_match(&guid, &asf_guid_stream_type_audio)) {
+ DEBUGF("Found stream properties for non audio stream, skipping\n");
+ deadbeef->fseek(fd,current.size - 24 - 50,SEEK_CUR);
+ } else if (wfx->audiostream == -1) {
+ deadbeef->fseek(fd, 4, SEEK_CUR);
+ DEBUGF("Found stream properties for audio stream %d\n",flags&0x7f);
+
+ if (propdatalen < 18) {
+ return ASF_ERROR_INVALID_LENGTH;
+ }
+
+#if 0
+ if (asf_byteio_getWLE(data + 16) > datalen - 16) {
+ return ASF_ERROR_INVALID_LENGTH;
+ }
+#endif
+ read_uint16le(fd, &wfx->codec_id);
+ read_uint16le(fd, &wfx->channels);
+ read_uint32le(fd, &wfx->rate);
+ read_uint32le(fd, &wfx->bitrate);
+ wfx->bitrate *= 8;
+ read_uint16le(fd, &wfx->blockalign);
+ read_uint16le(fd, &wfx->bitspersample);
+ read_uint16le(fd, &wfx->datalen);
+
+ if (wfx->codec_id == ASF_CODEC_ID_WMAV1) {
+ deadbeef->fread(wfx->data, 4, 1, fd);
+ deadbeef->fseek(fd,current.size - 24 - 72 - 4,SEEK_CUR);
+ wfx->audiostream = flags&0x7f;
+ } else if (wfx->codec_id == ASF_CODEC_ID_WMAV2) {
+ deadbeef->fread(wfx->data, 6, 1, fd);
+ deadbeef->fseek(fd,current.size - 24 - 72 - 6,SEEK_CUR);
+ wfx->audiostream = flags&0x7f;
+ } else if (wfx->codec_id == ASF_CODEC_ID_WMAPRO) {
+ /* wma pro decoder needs the extra-data */
+ deadbeef->fread(wfx->data, wfx->datalen, 1, fd);
+ deadbeef->fseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
+ wfx->audiostream = flags&0x7f;
+ /* Correct codectype to redirect playback to the proper .codec */
+ codectype = AFMT_WMAPRO;
+ } else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) {
+ deadbeef->fread(wfx->data, wfx->datalen, 1, fd);
+ deadbeef->fseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
+ wfx->audiostream = flags&0x7f;
+ codectype = AFMT_WMAVOICE;
+ } else {
+ trace("Unsupported WMA codec (Lossless, Voice, etc)\n");
+ deadbeef->fseek(fd,current.size - 24 - 72,SEEK_CUR);
+ }
+ }
+ } else if (it && asf_guid_match(&current.guid, &asf_guid_content_description)) {
+ /* Object contains five 16-bit string lengths, followed by the five strings:
+ title, artist, copyright, description, rating
+ */
+ uint16_t strlength[5];
+ int i;
+
+ DEBUGF("Found GUID_CONTENT_DESCRIPTION - size=%d\n",(int)(current.size - 24));
+
+ /* Read the 5 string lengths - number of bytes included trailing zero */
+ for (i=0; i<5; i++) {
+ read_uint16le(fd, &strlength[i]);
+ DEBUGF("strlength = %u\n",strlength[i]);
+ }
+
+ if (strlength[0] > 0) { /* 0 - Title */
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, strlength[0], &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "title", s);
+ }
+
+ if (strlength[1] > 0) { /* 1 - Artist */
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, strlength[1], &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "artist", s);
+ }
+
+ deadbeef->fseek(fd, strlength[2], SEEK_CUR); /* 2 - copyright */
+
+ if (strlength[3] > 0) { /* 3 - description */
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, strlength[3], &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "comment", s);
+ }
+
+ deadbeef->fseek(fd, strlength[4], SEEK_CUR); /* 4 - rating */
+ } else if (it && asf_guid_match(&current.guid, &asf_guid_extended_content_description)) {
+ uint16_t count;
+ int i;
+ int bytesleft = current.size - 24;
+ DEBUGF("Found GUID_EXTENDED_CONTENT_DESCRIPTION\n");
+
+ read_uint16le(fd, &count);
+ bytesleft -= 2;
+ DEBUGF("extended metadata count = %u\n",count);
+
+ for (i=0; i < count; i++) {
+ uint16_t length, type;
+ unsigned char* utf8 = utf8buf;
+ int utf8length = 512;
+
+ read_uint16le(fd, &length);
+ asf_utf16LEdecode(fd, length, &utf8, &utf8length);
+ bytesleft -= 2 + length;
+
+ read_uint16le(fd, &type);
+ read_uint16le(fd, &length);
+ trace ("ext md id: %s\n", utf8buf);
+
+ if (!strcmp("WM/TrackNumber",utf8buf)) {
+ if (type == 0) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "track", s);
+ } else if ((type >=2) && (type <= 5)) {
+ int tracknum = asf_intdecode(fd, type, length);
+ char n[100];
+ snprintf (n, sizeof (n), "%d", tracknum);
+ deadbeef->pl_append_meta (it, "track", n);
+ } else {
+ deadbeef->fseek(fd, length, SEEK_CUR);
+ }
+ } else if (!strcmp("TotalTracks",utf8buf)) {
+ if (type == 0) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "numtracks", s);
+ } else if ((type >=2) && (type <= 5)) {
+ int tracknum = asf_intdecode(fd, type, length);
+ char n[100];
+ snprintf (n, sizeof (n), "%d", tracknum);
+ deadbeef->pl_append_meta (it, "numtracks", n);
+ } else {
+ deadbeef->fseek(fd, length, SEEK_CUR);
+ }
+ } else if (!strcmp("WM/PartOfSet",utf8buf)) {
+ if (type == 0) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "disc", s);
+ } else if ((type >=2) && (type <= 5)) {
+ int tracknum = asf_intdecode(fd, type, length);
+ char n[100];
+ snprintf (n, sizeof (n), "%d", tracknum);
+ deadbeef->pl_append_meta (it, "disc", n);
+ } else {
+ deadbeef->fseek(fd, length, SEEK_CUR);
+ }
+ } else if ((!strcmp("WM/Genre", utf8buf)) && (type == 0)) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "genre", s);
+ } else if ((!strcmp("WM/AlbumTitle", utf8buf)) && (type == 0)) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "album", s);
+ } else if ((!strcmp("WM/AlbumArtist", utf8buf)) && (type == 0)) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "albumartist", s);
+ } else if ((!strcmp("WM/Composer", utf8buf)) && (type == 0)) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "composer", s);
+ } else if (!strcmp("WM/Year", utf8buf)) {
+ if (type == 0) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "year", s);
+ } else if ((type >=2) && (type <= 5)) {
+ int year = asf_intdecode(fd, type, length);
+ char n[100];
+ snprintf (n, sizeof (n), "%d", year);
+ deadbeef->pl_append_meta (it, "year", n);
+ } else {
+ deadbeef->fseek(fd, length, SEEK_CUR);
+ }
+ } else if (!strncmp("replaygain_", utf8buf, 11)) {
+ char *s = utf8buf;
+ char *value = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ // parse_replaygain(utf8buf, value, id3);
+ if (!strncasecmp (s, "replaygain_album_gain", 21)) {
+ deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (value));
+ }
+ else if (!strncasecmp (s, "replaygain_album_peak", 21)) {
+ deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (value));
+ }
+ else if (!strncasecmp (s, "replaygain_track_gain", 21)) {
+ deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (value));
+ }
+ else if (!strncasecmp (s, "replaygain_track_peak", 21)) {
+ deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (value));
+ }
+ } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) {
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, "MusicBrainzId", s);
+#ifdef HAVE_ALBUMART
+ } else if (!strcmp("WM/Picture", utf8buf)) {
+ uint32_t datalength, strlength;
+ /* Expected is either "01 00 xx xx 03 yy yy yy yy" or
+ * "03 yy yy yy yy". xx is the size of the WM/Picture
+ * container in bytes. yy equals the raw data length of
+ * the embedded image. */
+ deadbeef->fseek(fd, -4, SEEK_CUR);
+ deadbeef->fread(&type, 1, 1, fd);
+ if (type == 1) {
+ deadbeef->fseek(fd, 3, SEEK_CUR);
+ deadbeef->fread(&type, 1, 1, fd);
+ /* In case the parsing will fail in the next step we
+ * might at least be able to skip the whole section. */
+ datalength = length - 1;
+ }
+ if (type == 3) {
+ /* Read the raw data length of the embedded image. */
+ read_uint32le(fd, &datalength);
+
+ /* Reset utf8 buffer */
+ utf8 = utf8buf;
+ utf8length = 512;
+
+ /* Gather the album art format, this string has a
+ * double zero-termination. */
+ asf_utf16LEdecode(fd, 32, &utf8, &utf8length);
+ strlength = (strlen(utf8buf) + 2) * 2;
+ deadbeef->fseek(fd, strlength-32, SEEK_CUR);
+ if (!strcmp("image/jpeg", utf8buf)) {
+ id3->albumart.type = AA_TYPE_JPG;
+ } else if (!strcmp("image/jpg", utf8buf)) {
+ /* image/jpg is technically invalid,
+ * but it does occur in the wild */
+ id3->albumart.type = AA_TYPE_JPG;
+ } else if (!strcmp("image/png", utf8buf)) {
+ id3->albumart.type = AA_TYPE_PNG;
+ } else {
+ id3->albumart.type = AA_TYPE_UNKNOWN;
+ }
+
+ /* Set the album art size and position. */
+ if (id3->albumart.type != AA_TYPE_UNKNOWN) {
+ id3->albumart.pos = deadbeef->fseek(fd, 0, SEEK_CUR);
+ id3->albumart.size = datalength;
+ id3->has_embedded_albumart = true;
+ }
+ }
+
+ deadbeef->fseek(fd, datalength, SEEK_CUR);
+#endif
+ } else {
+ if (type == 0) { // FIXME: custom fields -- after others work
+ unsigned char *s = id3buf;
+ asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
+ deadbeef->pl_append_meta (it, utf8buf, s);
+ }
+ else {
+ deadbeef->fseek(fd, length, SEEK_CUR);
+ }
+ }
+ bytesleft -= 4 + length;
+ }
+
+ deadbeef->fseek(fd, bytesleft, SEEK_CUR);
+ } else if (asf_guid_match(&current.guid, &asf_guid_content_encryption)
+ || asf_guid_match(&current.guid, &asf_guid_extended_content_encryption)) {
+ DEBUGF("File is encrypted\n");
+ return ASF_ERROR_ENCRYPTED;
+ } else {
+ DEBUGF("Skipping %d bytes of object\n",(int)(current.size - 24));
+ deadbeef->fseek(fd,current.size - 24,SEEK_CUR);
+ }
+
+ DEBUGF("Parsed object - size = %d\n",(int)current.size);
+ datalen -= current.size;
+ }
+
+ if (i != (int)subobjects || datalen != 0) {
+ DEBUGF("header data doesn't match given subobject count\n");
+ return ASF_ERROR_INVALID_VALUE;
+ }
+
+ DEBUGF("%d subobjects read successfully\n", i);
+ }
+
+#if 0
+ tmp = asf_parse_header_validate(file, &header);
+ if (tmp < 0) {
+ /* header read ok but doesn't validate correctly */
+ return tmp;
+ }
+#endif
+
+ DEBUGF("header validated correctly\n");
+
+ return 0;
+}
+
+int get_asf_metadata(DB_FILE *fd, DB_playItem_t *it, asf_waveformatex_t *wfx, int64_t *first_frame_offset)
+{
+ int res;
+ asf_object_t obj;
+
+ wfx->audiostream = -1;
+
+ res = asf_parse_header(fd, wfx, it);
+
+ if (res < 0) {
+ DEBUGF("ASF: parsing error - %d\n",res);
+ return 0;
+ }
+
+ if (wfx->audiostream == -1) {
+ DEBUGF("ASF: No WMA streams found\n");
+ return 0;
+ }
+
+ asf_read_object_header(&obj, fd);
+
+ if (!asf_guid_match(&obj.guid, &asf_guid_data)) {
+ DEBUGF("ASF: No data object found\n");
+ return 0;
+ }
+
+ /* Store the current file position - no need to parse the header
+ again in the codec. The +26 skips the rest of the data object
+ header.
+ */
+ *first_frame_offset = deadbeef->ftell(fd) + 26;
+
+ return 1;
+}