Mercurial > audlegacy
changeset 4887:0ddbd0025174 default tip
added libaudtag. (not used yet.)
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Wed, 05 May 2010 18:26:06 +0900 |
parents | 54b4f7aaca24 |
children | |
files | src/Makefile src/libaudtag/Makefile src/libaudtag/aac/aac.c src/libaudtag/aac/aac.h src/libaudtag/ape/ape.c src/libaudtag/ape/ape.h src/libaudtag/audtag.c src/libaudtag/audtag.h src/libaudtag/id3/frame.h src/libaudtag/id3/id3v1.c src/libaudtag/id3/id3v1.h src/libaudtag/id3/id3v2.c src/libaudtag/id3/id3v2.h src/libaudtag/tag_module.c src/libaudtag/tag_module.h src/libaudtag/util.c src/libaudtag/util.h src/libaudtag/wma/guid.c src/libaudtag/wma/guid.h src/libaudtag/wma/module.h src/libaudtag/wma/wma.c src/libaudtag/wma/wma.h src/libaudtag/wma/wma_fmt.h |
diffstat | 23 files changed, 3791 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Makefile Wed Nov 11 15:34:23 2009 +0900 +++ b/src/Makefile Wed May 05 18:26:06 2010 +0900 @@ -1,4 +1,4 @@ -SUBDIRS = libguess libSAD audlegacy libid3tag libaudutil +SUBDIRS = libguess libSAD audlegacy libid3tag libaudutil libaudtag include ../extra.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/Makefile Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,22 @@ +LIB = ${LIB_PREFIX}audtag${LIB_SUFFIX} +LIB_MAJOR = 1 +LIB_MINOR = 0 + +SRCS = audtag.c \ + util.c \ + tag_module.c \ + wma/guid.c \ + wma/wma.c \ + id3/id3v1.c \ + id3/id3v2.c \ + ape/ape.c \ + aac/aac.c + +INCLUDES = audtag.h + +include ../../buildsys.mk +include ../../extra.mk + +CPPFLAGS += ${LIB_CPPFLAGS} ${GLIB_CFLAGS} ${MOWGLI_CFLAGS} -D_AUDACIOUS_CORE -I.. -I../.. +CFLAGS += ${LIB_CFLAGS} +LIBS += ${GLIB_LIBS} ${MOWGLI_LIBS}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/aac/aac.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,479 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <glib/gstdio.h> +#include <audlegacy/tuple.h> +#include <audlegacy/vfs.h> +#include "../tag_module.h" +#include "aac.h" +#include "../util.h" + +static const char *ID3v1GenreList[] = { + "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", + "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", + "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", + "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", + "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", + "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", + "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", + "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", + "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", + "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", + "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", + "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", + "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", + "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", + "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", + "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", + "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", + "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", + "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", + "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", + "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror", + "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat", + "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C", + "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", + "SynthPop", +}; + +gchar *atom_types[] = { "\251alb", "\251nam", "cprt", "\251art", "\251ART", "trkn", "\251day", "gnre", "desc" }; + +Atom *readAtom(VFSFile * fd) +{ + guint32 size = read_BEuint32(fd); + if (size > vfs_fsize(fd)) + return NULL; + + Atom *atom = g_new0(Atom, 1); + atom->size = size; + atom->name = read_char_data(fd, 4); + atom->body = read_char_data(fd, atom->size - 8); + atom->type = 0; + return atom; +} + +void writeAtom(VFSFile * fd, Atom * atom) +{ + write_BEuint32(fd, atom->size); + vfs_fwrite(atom->name, 4, 1, fd); + vfs_fwrite(atom->body, atom->size - 8, 1, fd); +} + +void printAtom(Atom * atom) +{ + AUDDBG("size = %x\n", atom->size); + AUDDBG("name = %s\n", atom->name); +} + +StrDataAtom *readStrDataAtom(VFSFile * fd) +{ + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom->atomsize = read_BEuint32(fd); + atom->name = read_char_data(fd, 4); + atom->datasize = read_BEuint32(fd); + atom->dataname = read_char_data(fd, 4); + atom->vflag = read_BEuint32(fd); + atom->nullData = read_BEuint32(fd); + atom->data = read_char_data(fd, atom->datasize - 16); + atom->type = 1; + return atom; +} + +void writeStrDataAtom(VFSFile * fd, StrDataAtom * atom) +{ + write_BEuint32(fd, atom->atomsize); + vfs_fwrite(atom->name, 4, 1, fd); + write_BEuint32(fd, atom->datasize); + vfs_fwrite(atom->dataname, 4, 1, fd); + write_BEuint32(fd, atom->vflag); + write_BEuint32(fd, atom->nullData); + vfs_fwrite(atom->data, atom->datasize - 16, 1, fd); +} + +Atom *findAtom(VFSFile * fd, gchar * name) +{ + Atom *atom = readAtom(fd); + while (strcmp(atom->name, name) && !vfs_feof(fd)) + { + g_free(atom); + atom = readAtom(fd); + } + if (vfs_feof(fd)) + { + g_free(atom); + AUDDBG("The atom %s could not be found\n", name); + return NULL; + } + return atom; +} + +Atom *getilstAtom(VFSFile * fd) +{ + Atom *moov = findAtom(fd, MOOV); + + // search atom childs + vfs_fseek(fd, -(moov->size - 7), SEEK_CUR); + Atom *udta = findAtom(fd, UDTA); + + + vfs_fseek(fd, -(udta->size - 7), SEEK_CUR); + Atom *meta = findAtom(fd, META); + + vfs_fseek(fd, -(meta->size - 11), SEEK_CUR); + Atom *ilst = findAtom(fd, ILST); + + int zz = vfs_ftell(fd); + AUDDBG("zzz = %d\n", zz); + ilstFileOffset = vfs_ftell(fd) - ilst->size; + vfs_fseek(fd, -(ilst->size - 7), SEEK_CUR); + + return ilst; + +} + +int getAtomID(gchar * name) +{ + g_return_val_if_fail(name != NULL, -1); + int i = 0; + for (i = 0; i < MP4_ITEMS_NO; i++) + { + if (!strcmp(name, atom_types[i])) + return i; + } + return -1; +} + +StrDataAtom *makeAtomWithData(const gchar * data, StrDataAtom * atom, int field) +{ + guint32 charsize = strlen(data); + atom->atomsize = charsize + 24; + atom->name = atom_types[field]; + atom->datasize = charsize + 16; + atom->dataname = "data"; + atom->vflag = 0x0; + atom->nullData = 0x0; + atom->data = (gchar *) data; + atom->type = 1; + return atom; + +} + +void writeAtomListToFile(VFSFile * from, VFSFile * to, int offset, mowgli_list_t * list) +{ + //read free atom if we have any :D + guint32 oset = ilstFileOffset + ilstAtom->size; + vfs_fseek(from, oset, SEEK_SET); + mowgli_list_t *atoms_before_free = mowgli_list_create(); + Atom *atom = readAtom(from); + while (strcmp(atom->name, "free") && !vfs_feof(from)) + { + mowgli_node_add(atom, mowgli_node_create(), atoms_before_free); + g_free(atom); + atom = readAtom(from); + } + g_free(atom); + if (vfs_feof(from)) + { + AUDDBG("No free atoms\n"); + g_free(atom); + atom = NULL; + } + + //write ilst atom header + gchar il[4] = ILST; + vfs_fwrite(&newilstSize, 4, 1, to); + vfs_fwrite(il, 4, 1, to); + //write ilst + + mowgli_node_t *n, *tn; + + MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head) + { + if (((Atom *) (n->data))->type == 0) + { + writeAtom(to, (Atom *) (n->data)); + } + else + { + writeStrDataAtom(to, (StrDataAtom *) (n->data)); + } + } + + //write all atoms before free + if (atoms_before_free->count != 0) + { + + MOWGLI_LIST_FOREACH_SAFE(n, tn, list->head) + { + writeAtom(to, (Atom *) (n->data)); + } + } + if (atom != NULL) + { + atom->size -= newilstSize - ilstAtom->size; + } + writeAtom(to, atom); +} + +gboolean aac_can_handle_file(VFSFile * f) +{ + Atom *first_atom = readAtom(f); + if (first_atom == NULL) + return FALSE; + if (!strcmp(first_atom->name, FTYP)) + return TRUE; + return FALSE; +} + +Tuple *aac_populate_tuple_from_file(Tuple * tuple, VFSFile * f) +{ + if (ilstAtom) + g_free(ilstAtom); + ilstAtom = getilstAtom(f); + int size_read = 0; + + if (dataAtoms != NULL) + { + mowgli_node_t *n, *tn; + + MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head) + { + mowgli_node_delete(n, dataAtoms); + } + } + dataAtoms = mowgli_list_create(); + + while (size_read < ilstAtom->size) + { + Atom *at = readAtom(f); + mowgli_node_add(at, mowgli_node_create(), dataAtoms); + int atomtype = getAtomID(at->name); + if (atomtype == -1) + { + size_read += at->size; + continue; + } + g_free(at); + vfs_fseek(f, -(at->size), SEEK_CUR); + StrDataAtom *a = readStrDataAtom(f); + size_read += a->atomsize; + + switch (atomtype) + { + case MP4_ALBUM: + { + tuple_associate_string(tuple, FIELD_ALBUM, NULL, a->data); + } + break; + case MP4_TITLE: + { + tuple_associate_string(tuple, FIELD_TITLE, NULL, a->data); + } + break; + case MP4_COPYRIGHT: + { + tuple_associate_string(tuple, FIELD_COPYRIGHT, NULL, a->data); + } + break; + case MP4_ARTIST: + case MP4_ARTIST2: + { + tuple_associate_string(tuple, FIELD_ARTIST, NULL, a->data); + } + break; + case MP4_TRACKNR: + { + //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data); + } + break; + case MP4_YEAR: + { + tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(a->data)); + } + break; + case MP4_GENRE: + { + guint8 *val = (guint8 *) (a->data + (a->datasize - 17)); + const gchar *genre = ID3v1GenreList[*val - 1]; + tuple_associate_string(tuple, FIELD_GENRE, NULL, genre); + } + break; + case MP4_COMMENT: + { + tuple_associate_string(tuple, FIELD_COMMENT, NULL, a->data); + } + break; + } + } + return tuple; +} + +gboolean aac_write_tuple_to_file(Tuple * tuple, VFSFile * f) +{ +#ifdef BROKEN + return FALSE; +#endif + newilstSize = 0; + mowgli_node_t *n, *tn; + mowgli_list_t *newdataAtoms; + newdataAtoms = mowgli_list_create(); + + MOWGLI_LIST_FOREACH_SAFE(n, tn, dataAtoms->head) + { + int atomtype = getAtomID(((StrDataAtom *) (n->data))->name); + switch (atomtype) + { + case MP4_ALBUM: + { + const gchar *strVal = tuple_get_string(tuple, FIELD_ALBUM, NULL); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_ALBUM); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + case MP4_TITLE: + { + const gchar *strVal = tuple_get_string(tuple, FIELD_TITLE, NULL); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_TITLE); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + case MP4_COPYRIGHT: + { + const gchar *strVal = tuple_get_string(tuple, FIELD_COPYRIGHT, NULL); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_COPYRIGHT); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + case MP4_ARTIST: + case MP4_ARTIST2: + { + const gchar *strVal = tuple_get_string(tuple, FIELD_ARTIST, NULL); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_ARTIST2); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + case MP4_TRACKNR: + { + //tuple_associate_string(tuple,FIELD_ALBUM,NULL,a->data); + } + break; + case MP4_YEAR: + { + int iyear = tuple_get_int(tuple, FIELD_YEAR, NULL); + gchar *strVal = g_strdup_printf("%d", iyear); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_YEAR); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + /* + case MP4_GENRE: + { + + guint8 *val = (guint8*)(a->data + (a->datasize-17)); + const gchar* genre = ID3v1GenreList[*val-1]; + tuple_associate_string(tuple,FIELD_GENRE,NULL,genre); + + }break; + */ + case MP4_COMMENT: + { + const gchar *strVal = tuple_get_string(tuple, FIELD_COMMENT, NULL); + if (strVal != NULL) + { + StrDataAtom *atom = g_new0(StrDataAtom, 1); + atom = makeAtomWithData(strVal, atom, MP4_COMMENT); + mowgli_node_add(atom, mowgli_node_create(), newdataAtoms); + newilstSize += atom->atomsize; + } + else + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + } + break; + default: + { + mowgli_node_add(n->data, mowgli_node_create(), newdataAtoms); + newilstSize += ((Atom *) (n->data))->size; + } + break; + } + } + + VFSFile *tmp; + const gchar *tmpdir = g_get_tmp_dir(); + gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "tmp.mp4"); + tmp = vfs_fopen(tmp_path, "w"); + copyAudioData(f, tmp, 0, ilstFileOffset); + writeAtomListToFile(f, tmp, ilstFileOffset, newdataAtoms); + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/aac/aac.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef AAC_H +#define AAC_H +#include <audlegacy/tuple.h> +#include <audlegacy/vfs.h> +#include "../tag_module.h" + +#define FTYP "ftyp" +#define MOOV "moov" +#define ILST "ilst" +#define UDTA "udta" +#define META "meta" +#define ILST "ilst" +#define FREE "free" + +enum { + MP4_ALBUM = 0, + MP4_TITLE, + MP4_COPYRIGHT, + MP4_ARTIST, + MP4_ARTIST2, + MP4_TRACKNR, + MP4_YEAR, + MP4_GENRE, + MP4_COMMENT, + MP4_ITEMS_NO +}; + +typedef struct mp4atom +{ + guint32 size; + gchar* name; //4 bytes + gchar* body; + int type; +}Atom; + + +typedef struct strdataatom +{ + guint32 atomsize; + gchar* name; + guint32 datasize; + gchar* dataname; + guint32 vflag; + guint32 nullData; + gchar* data; + int type; +}StrDataAtom; + +Atom *ilstAtom; +guint64 ilstFileOffset; +guint32 newilstSize ; +mowgli_list_t *dataAtoms; +mowgli_dictionary_t *ilstAtoms; + +/* TAG plugin API */ +gboolean aac_can_handle_file(VFSFile *f); +Tuple *aac_populate_tuple_from_file(Tuple *tuple,VFSFile *f); +gboolean aac_write_tuple_to_file(Tuple* tuple, VFSFile *f); + +static const tag_module_t aac = { + .name = "AAC", + .can_handle_file = aac_can_handle_file, + .populate_tuple_from_file = aac_populate_tuple_from_file, + .write_tuple_to_file = aac_write_tuple_to_file, +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/ape/ape.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,433 @@ +/* + * Copyright 2010 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +/* TODO: + * - ReplayGain info + * - Support updating files that have their tag at the beginning? + */ + +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <audlegacy/vfs.h> + +#include "ape.h" + +#define DEBUG(...) fprintf (stderr, "APE: " __VA_ARGS__) + +typedef struct +{ + gchar magic[8]; + guint32 version; /* LE */ + guint32 length; /* LE */ + guint32 items; /* LE */ + guint32 flags; /* LE */ + guint64 reserved; +} +APEHeader; + +typedef struct +{ + gchar * key, * value; +} +ValuePair; + +#define APE_FLAG_HAS_HEADER (1 << 31) +#define APE_FLAG_HAS_NO_FOOTER (1 << 30) +#define APE_FLAG_IS_HEADER (1 << 29) + +static gboolean ape_read_header (VFSFile * handle, APEHeader * header) +{ + if (vfs_fread (header, 1, sizeof (APEHeader), handle) != sizeof (APEHeader)) + return FALSE; + + if (strncmp (header->magic, "APETAGEX", 8)) + return FALSE; + + header->version = GUINT32_FROM_LE (header->version); + header->length = GUINT32_FROM_LE (header->length); + header->items = GUINT32_FROM_LE (header->items); + header->flags = GUINT32_FROM_LE (header->flags); + + if (header->length < sizeof (APEHeader)) + return FALSE; + + return TRUE; +} + +static gboolean ape_find_header (VFSFile * handle, APEHeader * header, gint * + start, gint * length, gint * data_start, gint * data_length) +{ + APEHeader secondary; + + if (vfs_fseek (handle, 0, SEEK_SET)) + return FALSE; + + if (ape_read_header (handle, header)) + { + DEBUG ("Found header at 0, length = %d, version = %d.\n", (gint) + header->length, (gint) header->version); + * start = 0; + * length = header->length; + * data_start = sizeof (APEHeader); + * data_length = header->length - sizeof (APEHeader); + + if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags & + APE_FLAG_IS_HEADER)) + { + DEBUG ("Invalid header flags (%u).\n", (guint) header->flags); + return FALSE; + } + + if (! (header->flags & APE_FLAG_HAS_NO_FOOTER)) + { + if (vfs_fseek (handle, header->length, SEEK_CUR)) + return FALSE; + + if (! ape_read_header (handle, & secondary)) + { + DEBUG ("Expected footer, but found none.\n"); + return FALSE; + } + + * length += sizeof (APEHeader); + } + + return TRUE; + } + + if (vfs_fseek (handle, -sizeof (APEHeader), SEEK_END)) + return FALSE; + + if (ape_read_header (handle, header)) + { + DEBUG ("Found footer at %d, length = %d, version = %d.\n", (gint) + vfs_ftell (handle) - (gint) sizeof (APEHeader), (gint) header->length, + (gint) header->version); + * start = vfs_ftell (handle) - header->length; + * length = header->length; + * data_start = vfs_ftell (handle) - header->length; + * data_length = header->length - sizeof (APEHeader); + + if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags & + APE_FLAG_IS_HEADER)) + { + DEBUG ("Invalid footer flags (%u).\n", (guint) header->flags); + return FALSE; + } + + if (header->flags & APE_FLAG_HAS_HEADER) + { + if (vfs_fseek (handle, -(gint) header->length - sizeof (APEHeader), + SEEK_CUR)) + return FALSE; + + if (! ape_read_header (handle, & secondary)) + { + DEBUG ("Expected header, but found none.\n"); + return FALSE; + } + + * start -= sizeof (APEHeader); + * length += sizeof (APEHeader); + } + + return TRUE; + } + + DEBUG ("No header found.\n"); + return FALSE; +} + +static gboolean ape_is_our_file (VFSFile * handle) +{ + APEHeader header; + gint start, length, data_start, data_length; + + return ape_find_header (handle, & header, & start, & length, & data_start, + & data_length); +} + +static ValuePair * ape_read_item (void * * data, gint length) +{ + guint32 * header = * data; + gchar * value; + ValuePair * pair; + + if (length < 8) + { + DEBUG ("Expected item, but only %d bytes remain in tag.\n", length); + return NULL; + } + + value = memchr ((gchar *) (* data) + 8, 0, length - 8); + + if (value == NULL) + { + DEBUG ("Unterminated item key (max length = %d).\n", length - 8); + return NULL; + } + + value ++; + + if (header[0] > (gchar *) (* data) + length - value) + { + DEBUG ("Item value of length %d, but only %d bytes remain in tag.\n", + (gint) header[0], (gint) ((gchar *) (* data) + length - value)); + return NULL; + } + + pair = g_malloc (sizeof (ValuePair)); + pair->key = g_strdup ((gchar *) (* data) + 8); + pair->value = g_strndup (value, header[0]); + + * data = value + header[0]; + + return pair; +} + +static GList * ape_read_tag (VFSFile * handle) +{ + GList * list = NULL; + APEHeader header; + gint start, length, data_start, data_length; + void * data, * item; + + if (! ape_find_header (handle, & header, & start, & length, & data_start, + & data_length)) + return NULL; + + if (vfs_fseek (handle, data_start, SEEK_SET)) + return NULL; + + data = g_malloc (data_length); + + if (vfs_fread (data, 1, data_length, handle) != data_length) + { + g_free (data); + return NULL; + } + + DEBUG ("Reading %d items:\n", header.items); + item = data; + + while (header.items --) + { + ValuePair * pair = ape_read_item (& item, (gchar *) data + data_length - + (gchar *) item); + + if (pair == NULL) + break; + + DEBUG ("Read: %s = %s.\n", pair->key, pair->value); + list = g_list_prepend (list, pair); + } + + g_free (data); + return g_list_reverse (list); +} + +static void free_tag_list (GList * list) +{ + while (list != NULL) + { + g_free (((ValuePair *) list->data)->key); + g_free (((ValuePair *) list->data)->value); + g_free (list->data); + list = g_list_delete_link (list, list); + } +} + +static Tuple * ape_fill_tuple (Tuple * tuple, VFSFile * handle) +{ + GList * list = ape_read_tag (handle), * node; + + for (node = list; node != NULL; node = node->next) + { + gchar * key = ((ValuePair *) node->data)->key; + gchar * value = ((ValuePair *) node->data)->value; + + if (! strcmp (key, "Artist")) + tuple_associate_string (tuple, FIELD_ARTIST, NULL, value); + else if (! strcmp (key, "Title")) + tuple_associate_string (tuple, FIELD_TITLE, NULL, value); + else if (! strcmp (key, "Album")) + tuple_associate_string (tuple, FIELD_ALBUM, NULL, value); + else if (! strcmp (key, "Comment")) + tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); + else if (! strcmp (key, "Genre")) + tuple_associate_string (tuple, FIELD_GENRE, NULL, value); + else if (! strcmp (key, "Track")) + tuple_associate_int (tuple, FIELD_TRACK_NUMBER, NULL, atoi (value)); + else if (! strcmp (key, "Date")) + tuple_associate_int (tuple, FIELD_YEAR, NULL, atoi (value)); + } + + free_tag_list (list); + return tuple; +} + +static gboolean ape_write_item (VFSFile * handle, const gchar * key, + const gchar * value, int * written_length) +{ + gint key_len = strlen (key) + 1; + gint value_len = strlen (value); + guint32 header[2]; + + DEBUG ("Write: %s = %s.\n", key, value); + + header[0] = GUINT32_TO_LE (value_len); + header[1] = 0; + + if (vfs_fwrite (header, 1, 8, handle) != 8) + return FALSE; + + if (vfs_fwrite (key, 1, key_len, handle) != key_len) + return FALSE; + + if (vfs_fwrite (value, 1, value_len, handle) != value_len) + return FALSE; + + * written_length += 8 + key_len + value_len; + return TRUE; +} + +static gboolean write_string_item (Tuple * tuple, int field, VFSFile * handle, + const gchar * key, int * written_length, int * written_items) +{ + const gchar * value = tuple_get_string (tuple, field, NULL); + + if (value == NULL) + return TRUE; + + if (! ape_write_item (handle, key, value, written_length)) + return FALSE; + + (* written_items) ++; + return TRUE; +} + +static gboolean write_integer_item (Tuple * tuple, int field, VFSFile * handle, + const gchar * key, int * written_length, int * written_items) +{ + gint value = tuple_get_int (tuple, field, NULL); + gchar scratch[32]; + + if (! value) + return TRUE; + + snprintf (scratch, sizeof scratch, "%d", value); + + if (! ape_write_item (handle, key, scratch, written_length)) + return FALSE; + + (* written_items) ++; + return TRUE; +} + +static gboolean write_header (gint data_length, gint items, gboolean is_header, + VFSFile * handle) +{ + APEHeader header; + + memcpy (header.magic, "APETAGEX", 8); + header.version = GUINT32_TO_LE (2000); + header.length = GUINT32_TO_LE (data_length + sizeof (APEHeader)); + header.items = GUINT32_TO_LE (items); + header.flags = is_header ? GUINT32_TO_LE (APE_FLAG_HAS_HEADER | + APE_FLAG_IS_HEADER) : GUINT32_TO_LE (APE_FLAG_HAS_HEADER); + header.reserved = 0; + + return vfs_fwrite (& header, 1, sizeof (APEHeader), handle) == sizeof + (APEHeader); +} + +static gboolean ape_write_tag (Tuple * tuple, VFSFile * handle) +{ + GList * list = ape_read_tag (handle), * node; + APEHeader header; + gint start, length, data_start, data_length, items; + + if (! ape_find_header (handle, & header, & start, & length, & data_start, + & data_length)) + goto ERROR; + + if (start + length != vfs_fsize (handle)) + { + DEBUG ("Writing tags is only supported at end of file.\n"); + goto ERROR; + } + + if (vfs_truncate (handle, start) || vfs_fseek (handle, start, SEEK_SET) || + ! write_header (0, 0, TRUE, handle)) + goto ERROR; + + length = 0; + items = 0; + + if (! write_string_item (tuple, FIELD_ARTIST, handle, "Artist", & length, + & items) || ! write_string_item (tuple, FIELD_TITLE, handle, "Title", + & length, & items) || ! write_string_item (tuple, FIELD_ALBUM, handle, + "Album", & length, & items) || ! write_string_item (tuple, FIELD_COMMENT, + handle, "Comment", & length, & items) || ! write_string_item (tuple, + FIELD_GENRE, handle, "Genre", & length, & items) || ! write_integer_item + (tuple, FIELD_TRACK_NUMBER, handle, "Track", & length, & items) || + ! write_integer_item (tuple, FIELD_YEAR, handle, "Date", & length, & items)) + goto ERROR; + + for (node = list; node != NULL; node = node->next) + { + gchar * key = ((ValuePair *) node->data)->key; + gchar * value = ((ValuePair *) node->data)->value; + + if (! strcmp (key, "Artist") || ! strcmp (key, "Title") || ! strcmp + (key, "Album") || ! strcmp (key, "Comment") || ! strcmp (key, "Genre") + || ! strcmp (key, "Track") || ! strcmp (key, "Date")) + continue; + + if (! ape_write_item (handle, key, value, & length)) + goto ERROR; + + items ++; + } + + DEBUG ("Wrote %d items, %d bytes.\n", items, length); + + if (write_header (length, items, FALSE, handle) || vfs_fseek (handle, start, + SEEK_SET) || ! write_header (length, items, TRUE, handle)) + goto ERROR; + + free_tag_list (list); + return TRUE; + +ERROR: + free_tag_list (list); + return FALSE; +} + +const tag_module_t ape = +{ + .name = "APE", + .can_handle_file = ape_is_our_file, + .populate_tuple_from_file = ape_fill_tuple, + .write_tuple_to_file = ape_write_tag, +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/ape/ape.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,28 @@ +/* + * Copyright 2010 John Lindgren + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef AUDTAG_APE_H +#define AUDTAG_APE_H + +#include "../tag_module.h" + +extern const tag_module_t ape; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/audtag.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,52 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include "audlegacy/tuple.h" +#include "audtag.h" +#include "tag_module.h" +#include "util.h" + +void tag_init(void) +{ + init_tag_modules(); +} + +/* The tuple's file-related attributes are already set */ + +gboolean tag_tuple_read (Tuple * tuple, VFSFile * fd) +{ + tag_module_t *mod = find_tag_module(fd); + + if (mod == NULL) + return FALSE; + + AUDDBG("Tag module %s has accepted %s\n", mod->name, fd->uri); + return mod->populate_tuple_from_file (tuple, fd) != NULL; +} + +gboolean tag_tuple_write_to_file(Tuple * tuple, VFSFile * fd) +{ + tag_module_t *mod = find_tag_module(fd); + + if (mod == NULL) + return FALSE; + + return mod->write_tuple_to_file(tuple, fd); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/audtag.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,42 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +/* External Interface of the tagging library */ + +#ifndef AUDTAG_H +#define AUDTAG_H + +G_BEGIN_DECLS + +#include <glib.h> +#include <mowgli.h> +#include "audlegacy/tuple.h" +#include "audlegacy/vfs.h" + +void tag_init(void); +void tag_terminate(void); + +gboolean tag_tuple_read (Tuple * tuple, VFSFile *fd); +gboolean tag_tuple_write_to_file(Tuple *tuple, VFSFile *fd); + +G_END_DECLS +#endif /* AUDTAG_H */ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/id3/frame.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,49 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef AUD_ID3_FRAME +#define AUD_ID3_FRAME + +#include <glib-2.0/glib.h> + +enum { + ID3_ALBUM = 0, + ID3_TITLE, + ID3_COMPOSER, + ID3_COPYRIGHT, + ID3_DATE, + ID3_TIME, + ID3_LENGTH, + ID3_ARTIST, + ID3_TRACKNR, + ID3_YEAR, + ID3_GENRE, + ID3_COMMENT, + ID3_PRIVATE, + ID3_ENCODER, + ID3_RECORDING_TIME, + ID3_TXXX, + ID3_TAGS_NO +}; + +char * id3_frames[] = {"TALB","TIT2","TCOM", "TCOP", "TDAT", "TIME", "TLEN", +"TPE1", "TRCK", "TYER","TCON", "COMM", "PRIV", "TSSE", "TDRC", "TXXX"}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/id3/id3v1.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,134 @@ +/* + * Copyright 2010 Tony Vroon + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <glib.h> +#include <glib/gstdio.h> + +#include "id3v1.h" +#include "../util.h" +#include <inttypes.h> +#include "../tag_module.h" +#include "../../audlegacy/strings.h" + +static gboolean has_id3v1_ext; + +gboolean id3v1_can_handle_file(VFSFile *f) +{ + gchar *tag = g_new0(gchar, 4); + + vfs_fseek(f, -355, SEEK_END); + tag = read_char_data(f, 4); + if (!strncmp(tag, "TAG+", 4)) + has_id3v1_ext = TRUE; + else + has_id3v1_ext = FALSE; + + vfs_fseek(f, -128, SEEK_END); + tag = read_char_data(f, 3); + if (!strncmp(tag, "TAG", 3)) + { + g_free(tag); + return TRUE; + } + + g_free(tag); + return FALSE; +} + +static gchar *convert_to_utf8(gchar *str) +{ + return g_strchomp(str_to_utf8(str)); +} + +Tuple *id3v1_populate_tuple_from_file(Tuple *tuple, VFSFile *f) +{ + gchar *title = g_new0(gchar, 30); + gchar *artist = g_new0(gchar, 30); + gchar *album = g_new0(gchar, 30); + gchar *year = g_new0(gchar, 4); + gchar *comment = g_new0(gchar, 30); + gchar *track = g_new0(gchar, 1); + gchar *genre = g_new0(gchar, 1); + gboolean genre_set = FALSE; + vfs_fseek(f, -125, SEEK_END); + title = read_char_data(f, 30); + artist = read_char_data(f, 30); + album = read_char_data(f, 30); + year = read_char_data(f, 4); + comment = read_char_data(f, 30); + genre = read_char_data(f, 1); + + if (comment[28] == 0 && comment[29] != 0) + { + *track = comment[29]; + } + + title = convert_to_utf8(title); + artist = convert_to_utf8(artist); + album = convert_to_utf8(album); + comment = convert_to_utf8(comment); + + if (has_id3v1_ext) + { + vfs_fseek(f, -351, SEEK_END); + gchar *tmp_title = g_strconcat(title, convert_to_utf8(read_char_data(f, 60)), NULL); + gchar *tmp_artist = g_strconcat(artist, convert_to_utf8(read_char_data(f, 60)), NULL); + gchar *tmp_album = g_strconcat(album, convert_to_utf8(read_char_data(f, 60)), NULL); + vfs_fseek(f, -170, SEEK_END); + gchar *tmp_genre = g_new0(gchar, 30); + tmp_genre = convert_to_utf8(read_char_data(f, 30)); + g_free(title); + g_free(artist); + g_free(album); + title = tmp_title; + artist = tmp_artist; + album = tmp_album; + + if (g_strcmp0(tmp_genre, NULL) == 1) + { + tuple_associate_string(tuple, FIELD_GENRE, NULL, tmp_genre); + genre_set = TRUE; + } + + g_free(tmp_genre); + } + + tuple_associate_string(tuple, FIELD_TITLE, NULL, title); + tuple_associate_string(tuple, FIELD_ARTIST, NULL, artist); + tuple_associate_string(tuple, FIELD_ALBUM, NULL, album); + tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(year)); + tuple_associate_string(tuple, FIELD_COMMENT, NULL, comment); + tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, *track); + if (!genre_set) tuple_associate_string(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(*genre)); + + g_free(title); + g_free(artist); + g_free(album); + g_free(year); + g_free(comment); + g_free(track); + g_free(genre); + return tuple; +} + +gboolean id3v1_write_tuple_to_file(Tuple * tuple, VFSFile * f) +{ + return FALSE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/id3/id3v1.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,40 @@ +/* + * Copyright 2010 Tony Vroon + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef ID3V1_H + +#define ID3V1_H + +#include <audlegacy/tuple.h> +#include <audlegacy/vfs.h> +#include "../tag_module.h" + +/* TAG plugin API */ +gboolean id3v1_can_handle_file(VFSFile *f); +Tuple *id3v1_populate_tuple_from_file(Tuple *tuple,VFSFile *f); +gboolean id3v1_write_tuple_to_file(Tuple* tuple, VFSFile *f); + +static const tag_module_t id3v1 = { + .name = "ID3v1", + .can_handle_file = id3v1_can_handle_file, + .populate_tuple_from_file = id3v1_populate_tuple_from_file, + .write_tuple_to_file = id3v1_write_tuple_to_file, +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/id3/id3v2.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,691 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <glib.h> +#include <glib/gstdio.h> + +#include "id3v2.h" +#include "../util.h" +#include <inttypes.h> +#include "../tag_module.h" +#include "frame.h" + +#define TAG_SIZE 1 + +guint32 read_syncsafe_int32(VFSFile * fd) +{ + guint32 val = read_BEuint32(fd); + guint32 mask = 0x7f; + guint32 intVal = 0; + intVal = ((intVal) | (val & mask)); + int i; + for (i = 0; i < 3; i++) + { + mask = mask << 8; + guint32 tmp = (val & mask); + tmp = tmp >> 1; + intVal = intVal | tmp; + }; + return intVal; +} + +ID3v2Header *readHeader(VFSFile * fd) +{ + ID3v2Header *header = g_new0(ID3v2Header, 1); + header->id3 = read_char_data(fd, 3); + header->version = read_LEuint16(fd); + header->flags = *read_char_data(fd, 1); + header->size = read_syncsafe_int32(fd); + return header; +} + +ExtendedHeader *readExtendedHeader(VFSFile * fd) +{ + ExtendedHeader *header = g_new0(ExtendedHeader, 1); + header->header_size = read_syncsafe_int32(fd); + header->flags = read_LEuint16(fd); + header->padding_size = read_BEuint32(fd); + return header; +} + +ID3v2FrameHeader *readID3v2FrameHeader(VFSFile * fd) +{ + ID3v2FrameHeader *frameheader = g_new0(ID3v2FrameHeader, 1); + frameheader->frame_id = read_char_data(fd, 4); + frameheader->size = read_syncsafe_int32(fd); + frameheader->flags = read_LEuint16(fd); + if ((frameheader->flags & 0x100) == 0x100) + frameheader->size = read_syncsafe_int32(fd); + return frameheader; +} + +static gint unsyncsafe (gchar * data, gint size) +{ + gchar * get = data, * set = data; + + while (size --) + { + gchar c = * set ++ = * get ++; + + if (c == (gchar) 0xff && size) + { + size --; + get ++; + } + } + + return set - data; +} + +static gchar * read_text_frame (VFSFile * handle, ID3v2FrameHeader * header) +{ + gint size = header->size; + gchar data[size]; + + if (vfs_fread (data, 1, size, handle) != size) + return NULL; + + if (header->flags & 0x200) + size = unsyncsafe (data, size); + + switch (data[0]) + { + case 0: + return g_convert (data + 1, size - 1, "UTF-8", "ISO-8859-1", NULL, NULL, + NULL); + case 1: + if (data[1] == (gchar) 0xff) + return g_convert (data + 3, size - 3, "UTF-8", "UTF-16LE", NULL, + NULL, NULL); + else + return g_convert (data + 3, size - 3, "UTF-8", "UTF-16BE", NULL, + NULL, NULL); + case 2: + return g_convert (data + 1, size - 1, "UTF-8", "UTF-16BE", NULL, NULL, + NULL); + case 3: + return g_strndup (data + 1, size - 1); + default: + AUDDBG ("Throwing away %i bytes of text due to invalid encoding %d\n", + size - 1, (gint) data[0]); + return NULL; + } +} + +static gboolean read_comment_frame (VFSFile * handle, ID3v2FrameHeader * header, + gchar * * lang, gchar * * type, gchar * * value) +{ + gint size = header->size; + gchar data[size]; + gchar * pair, * sep; + gsize converted; + + if (vfs_fread (data, 1, size, handle) != size) + return FALSE; + + if (header->flags & 0x200) + size = unsyncsafe (data, size); + + switch (data[0]) + { + case 0: + pair = g_convert (data + 4, size - 4, "UTF-8", "ISO-8859-1", NULL, + & converted, NULL); + break; + case 1: + if (data[4] == (gchar) 0xff) + pair = g_convert (data + 6, size - 6, "UTF-8", "UTF-16LE", NULL, + & converted, NULL); + else + pair = g_convert (data + 6, size - 6, "UTF-8", "UTF-16BE", NULL, + & converted, NULL); + break; + case 2: + pair = g_convert (data + 4, size - 4, "UTF-8", "UTF-16BE", NULL, + & converted, NULL); + break; + case 3: + pair = g_malloc (size - 3); + memcpy (pair, data + 4, size - 4); + pair[size - 4] = 0; + converted = size - 4; + break; + default: + AUDDBG ("Throwing away %i bytes of text due to invalid encoding %d\n", + size - 4, (gint) data[0]); + pair = NULL; + break; + } + + if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL) + return FALSE; + + * lang = g_strndup (data + 1, 3); + * type = g_strdup (pair); + * value = g_strdup (sep + 1); + + g_free (pair); + return TRUE; +} + +GenericFrame *readGenericFrame(VFSFile * fd, GenericFrame * gf) +{ + gf->header = readID3v2FrameHeader(fd); + gf->frame_body = read_char_data(fd, gf->header->size); + + return gf; +} + + +void readAllFrames(VFSFile * fd, int framesSize) +{ + int pos = 0; + int i = 0; + while (pos < framesSize) + { + GenericFrame *gframe = g_new0(GenericFrame, 1); + gframe = readGenericFrame(fd, gframe); + if (isValidFrame(gframe)) + { + mowgli_dictionary_add(frames, gframe->header->frame_id, gframe); + mowgli_node_add(gframe->header->frame_id, mowgli_node_create(), frameIDs); + pos += gframe->header->size; + i++; + } + else + break; + } + +} + +void write_int32(VFSFile * fd, guint32 val) +{ + guint32 be_val = GUINT32_TO_BE(val); + vfs_fwrite(&be_val, 4, 1, fd); +} + +void write_syncsafe_int32(VFSFile * fd, guint32 val) +{ + //TODO write the correct function - this is just for testing + int i = 0; + guint32 tmp = 0x0; + guint32 mask = 0x7f; + guint32 syncVal = 0; + tmp = val & mask; + syncVal = tmp; + for (i = 0; i < 3; i++) + { + tmp = 0; + mask <<= 7; + tmp = val & mask; + tmp <<= 1; + syncVal |= tmp; + } + guint32 be_val = GUINT32_TO_BE(syncVal); + vfs_fwrite(&be_val, 4, 1, fd); +} + + +void write_ASCII(VFSFile * fd, int size, gchar * value) +{ + vfs_fwrite(value, size, 1, fd); +} + + +void write_utf8(VFSFile * fd, int size, gchar * value) +{ + GError *error = NULL; + gsize bytes_read = 0, bytes_write = 0; + gchar *isoVal = g_convert(value, size, "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); + vfs_fwrite(isoVal, size, 1, fd); +} + +guint32 writeAllFramesToFile(VFSFile * fd) +{ + guint32 size = 0; + mowgli_node_t *n, *tn; + MOWGLI_LIST_FOREACH_SAFE(n, tn, frameIDs->head) + { + GenericFrame *frame = (GenericFrame *) mowgli_dictionary_retrieve(frames, (gchar *) (n->data)); + if (frame) + { + writeGenericFrame(fd, frame); + size += frame->header->size + 10; + } + } + return size; +} + +void writeID3HeaderToFile(VFSFile * fd, ID3v2Header * header) +{ + vfs_fwrite(header->id3, 3, 1, fd); + vfs_fwrite(&header->version, 2, 1, fd); + vfs_fwrite(&header->flags, 1, 1, fd); + write_syncsafe_int32(fd, header->size); +} + +void writePaddingToFile(VFSFile * fd, int ksize) +{ + gchar padding = 0; + int i = 0; + for (i = 0; i < ksize; i++) + vfs_fwrite(&padding, 1, 1, fd); +} + + +void writeID3FrameHeaderToFile(VFSFile * fd, ID3v2FrameHeader * header) +{ + vfs_fwrite(header->frame_id, 4, 1, fd); + write_int32(fd, header->size); + vfs_fwrite(&header->flags, 2, 1, fd); +} + +void writeGenericFrame(VFSFile * fd, GenericFrame * frame) +{ + writeID3FrameHeaderToFile(fd, frame->header); + vfs_fwrite(frame->frame_body, frame->header->size, 1, fd); +} + +gboolean isExtendedHeader(ID3v2Header * header) +{ + if ((header->flags & 0x40) == (0x40)) + return TRUE; + else + return FALSE; +} + +int getFrameID(ID3v2FrameHeader * header) +{ + int i = 0; + for (i = 0; i < ID3_TAGS_NO; i++) + { + if (!strcmp(header->frame_id, id3_frames[i])) + return i; + } + return -1; +} + + +void skipFrame(VFSFile * fd, guint32 size) +{ + vfs_fseek(fd, size, SEEK_CUR); +} + +static void associate_string (Tuple * tuple, VFSFile * handle, gint field, + const gchar * customfield, ID3v2FrameHeader * header) +{ + gchar * text = read_text_frame (handle, header); + + if (text == NULL) + return; + + if (customfield != NULL) + AUDDBG ("custom field %s = %s\n", customfield, text); + else + AUDDBG ("field %i = %s\n", field, text); + + tuple_associate_string (tuple, field, customfield, text); + g_free (text); +} + +static void associate_int (Tuple * tuple, VFSFile * handle, gint field, + const gchar * customfield, ID3v2FrameHeader * header) +{ + gchar * text = read_text_frame (handle, header); + + if (text == NULL) + return; + + if (customfield != NULL) + AUDDBG ("custom field %s = %s\n", customfield, text); + else + AUDDBG ("field %i = %s\n", field, text); + + tuple_associate_int (tuple, field, customfield, atoi (text)); + g_free (text); +} + +static void decode_private_info(Tuple * tuple, VFSFile * fd, ID3v2FrameHeader * header) +{ + gchar *value = read_char_data(fd, header->size); + if (!strncmp(value, "WM/", 3)) + { + AUDDBG("Windows Media tag: %s\n", value); + } else { + AUDDBG("Unable to decode private data, skipping: %s\n", value); + } +} + +static void decode_comment (Tuple * tuple, VFSFile * handle, ID3v2FrameHeader * + header) +{ + gchar * lang, * type, * value; + + if (! read_comment_frame (handle, header, & lang, & type, & value)) + return; + + AUDDBG ("comment: lang = %s, type = %s, value = %s\n", lang, type, value); + + if (! type[0]) /* blank type == actual comment */ + tuple_associate_string (tuple, FIELD_COMMENT, NULL, value); + + g_free (lang); + g_free (type); + g_free (value); +} + +static void decode_txxx (Tuple * tuple, VFSFile * handle, ID3v2FrameHeader * header) +{ + gchar * text = read_text_frame (handle, header); + + if (text == NULL) + return; + + gchar * separator = strchr(text, 0); + + if (separator == NULL) + return; + + gchar * value = separator + 1; + AUDDBG ("Field '%s' has value '%s'\n", text, value); + tuple_associate_string (tuple, -1, text, value); + + g_free (text); +} + +Tuple *decodeGenre(Tuple * tuple, VFSFile * fd, ID3v2FrameHeader header) +{ + gint numericgenre; + gchar * text = read_text_frame (fd, & header); + + if (text == NULL) + return tuple; + + if (text[0] == '(') + numericgenre = atoi (text + 1); + else + numericgenre = atoi (text); + + if (numericgenre > 0) + { + tuple_associate_string(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(numericgenre)); + return tuple; + } + tuple_associate_string(tuple, FIELD_GENRE, NULL, text); + g_free (text); + return tuple; +} + +gboolean isValidFrame(GenericFrame * frame) +{ + if (strlen(frame->header->frame_id) != 0) + return TRUE; + else + return FALSE; +} + + + +void add_newISO8859_1FrameFromString(const gchar * value, int id3_field) +{ + GError *error = NULL; + gsize bytes_read = 0, bytes_write = 0; + gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); + ID3v2FrameHeader *header = g_new0(ID3v2FrameHeader, 1); + header->frame_id = id3_frames[id3_field]; + header->flags = 0; + header->size = strlen(retVal) + 1; + gchar *buf = g_new0(gchar, header->size + 1); + memcpy(buf + 1, retVal, header->size); + GenericFrame *frame = g_new0(GenericFrame, 1); + frame->header = header; + frame->frame_body = buf; + mowgli_dictionary_add(frames, header->frame_id, frame); + mowgli_node_add(frame->header->frame_id, mowgli_node_create(), frameIDs); + +} + + +void add_newFrameFromTupleStr(Tuple * tuple, int field, int id3_field) +{ + const gchar *value = tuple_get_string(tuple, field, NULL); + add_newISO8859_1FrameFromString(value, id3_field); +} + + +void add_newFrameFromTupleInt(Tuple * tuple, int field, int id3_field) +{ + int intvalue = tuple_get_int(tuple, field, NULL); + gchar *value = g_strdup_printf("%d", intvalue); + add_newISO8859_1FrameFromString(value, id3_field); + +} + + + +void add_frameFromTupleStr(Tuple * tuple, int field, int id3_field) +{ + const gchar *value = tuple_get_string(tuple, field, NULL); + GError *error = NULL; + gsize bytes_read = 0, bytes_write = 0; + gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); + + GenericFrame *frame = mowgli_dictionary_retrieve(frames, id3_frames[id3_field]); + if (frame != NULL) + { + frame->header->size = strlen(retVal) + 1; + gchar *buf = g_new0(gchar, frame->header->size + 1); + memcpy(buf + 1, retVal, frame->header->size); + frame->frame_body = buf; + } + else + add_newFrameFromTupleStr(tuple, field, id3_field); + +} + +void add_frameFromTupleInt(Tuple * tuple, int field, int id3_field) +{ + int intvalue = tuple_get_int(tuple, field, NULL); + gchar *value = g_strdup_printf("%d", intvalue); + GError *error = NULL; + gsize bytes_read = 0, bytes_write = 0; + gchar *retVal = g_convert(value, strlen(value), "ISO-8859-1", "UTF-8", &bytes_read, &bytes_write, &error); + + GenericFrame *frame = mowgli_dictionary_retrieve(frames, id3_frames[id3_field]); + if (frame != NULL) + { + frame->header->size = strlen(retVal) + 1; + gchar *buf = g_new0(gchar, frame->header->size + 1); + memcpy(buf + 1, retVal, frame->header->size); + frame->frame_body = buf; + } + else + add_newFrameFromTupleStr(tuple, field, id3_field); + +} + +gboolean id3v2_can_handle_file(VFSFile * f) +{ + ID3v2Header *header = readHeader(f); + if (!strcmp(header->id3, "ID3")) + return TRUE; + return FALSE; +} + + + +Tuple *id3v2_populate_tuple_from_file(Tuple * tuple, VFSFile * f) +{ + vfs_fseek(f, 0, SEEK_SET); + ExtendedHeader *extHeader; + ID3v2Header *header = readHeader(f); + int pos = 0; + if (isExtendedHeader(header)) + { + extHeader = readExtendedHeader(f); + vfs_fseek(f, 10 + extHeader->header_size, SEEK_SET); + } + + while (pos < header->size) + { + ID3v2FrameHeader *frame = readID3v2FrameHeader(f); + if (frame->size == 0) + break; + int id = getFrameID(frame); + pos = pos + frame->size + 10; + if (pos > header->size) + break; + switch (id) + { + case ID3_ALBUM: + associate_string (tuple, f, FIELD_ALBUM, NULL, frame); + break; + case ID3_TITLE: + associate_string (tuple, f, FIELD_TITLE, NULL, frame); + break; + case ID3_COMPOSER: + associate_string (tuple, f, FIELD_ARTIST, NULL, frame); + break; + case ID3_COPYRIGHT: + associate_string (tuple, f, FIELD_COPYRIGHT, NULL, frame); + break; + case ID3_DATE: + associate_string (tuple, f, FIELD_DATE, NULL, frame); + break; + case ID3_TIME: + associate_int (tuple, f, FIELD_LENGTH, NULL, frame); + break; + case ID3_LENGTH: + associate_int (tuple, f, FIELD_LENGTH, NULL, frame); + break; + case ID3_ARTIST: + associate_string (tuple, f, FIELD_ARTIST, NULL, frame); + break; + case ID3_TRACKNR: + associate_int (tuple, f, FIELD_TRACK_NUMBER, NULL, frame); + break; + case ID3_YEAR: + case ID3_RECORDING_TIME: + associate_int (tuple, f, FIELD_YEAR, NULL, frame); + break; + case ID3_GENRE: + tuple = decodeGenre(tuple, f, *frame); + break; + case ID3_COMMENT: + decode_comment (tuple, f, frame); + break; + case ID3_PRIVATE: + decode_private_info (tuple, f, frame); + break; + case ID3_ENCODER: + associate_string (tuple, f, -1, "encoder", frame); + break; + case ID3_TXXX: + decode_txxx (tuple, f, frame); + break; + default: + AUDDBG("Skipping %i bytes over unsupported ID3 frame %s\n", frame->size, frame->frame_id); + skipFrame(f, frame->size); + } + } + return tuple; +} + + +gboolean id3v2_write_tuple_to_file(Tuple * tuple, VFSFile * f) +{ + VFSFile *tmp; + vfs_fseek(f, 0, SEEK_SET); + + ExtendedHeader *extHeader; + if (frameIDs != NULL) + { + mowgli_node_t *n, *tn; + MOWGLI_LIST_FOREACH_SAFE(n, tn, frameIDs->head) + { + mowgli_node_delete(n, frameIDs); + } + } + frameIDs = mowgli_list_create(); + ID3v2Header *header = readHeader(f); + int framesSize = header->size; + + if (isExtendedHeader(header)) + { + extHeader = readExtendedHeader(f); + framesSize -= 10; + framesSize -= extHeader->padding_size; + } + + //read all frames into generic frames; + frames = mowgli_dictionary_create(strcasecmp); + readAllFrames(f, header->size); + + //make the new frames from tuple and replace in the dictionary the old frames with the new ones + if (tuple_get_string(tuple, FIELD_ARTIST, NULL)) + add_frameFromTupleStr(tuple, FIELD_ARTIST, ID3_ARTIST); + + if (tuple_get_string(tuple, FIELD_TITLE, NULL)) + add_frameFromTupleStr(tuple, FIELD_TITLE, ID3_TITLE); + + if (tuple_get_string(tuple, FIELD_ALBUM, NULL)) + add_frameFromTupleStr(tuple, FIELD_ALBUM, ID3_ALBUM); + + if (tuple_get_string(tuple, FIELD_COMMENT, NULL)) + add_frameFromTupleStr(tuple, FIELD_COMMENT, ID3_COMMENT); + + if (tuple_get_string(tuple, FIELD_GENRE, NULL)) + add_frameFromTupleStr(tuple, FIELD_GENRE, ID3_GENRE); + + if (tuple_get_int(tuple, FIELD_YEAR, NULL) != 0) + add_frameFromTupleInt(tuple, FIELD_YEAR, ID3_YEAR); + + if (tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL) != 0) + add_frameFromTupleInt(tuple, FIELD_TRACK_NUMBER, ID3_TRACKNR); + + const gchar *tmpdir = g_get_tmp_dir(); + gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "tmp.mpc"); + tmp = vfs_fopen(tmp_path, "w+"); + + int oldSize = header->size; + header->size = TAG_SIZE * 1024; + + writeID3HeaderToFile(tmp, header); + + int size = writeAllFramesToFile(tmp); + writePaddingToFile(tmp, TAG_SIZE * 1024 - size - 10); + + copyAudioToFile(f, tmp, oldSize); + + + gchar *uri = g_strdup(f->uri); + vfs_fclose(tmp); + gchar *f1 = g_filename_from_uri(tmp_path, NULL, NULL); + gchar *f2 = g_filename_from_uri(uri, NULL, NULL); + if (g_rename(f1, f2) == 0) + { + AUDDBG("the tag was updated successfully\n"); + } + else + { + AUDDBG("an error has occured\n"); + } + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/id3/id3v2.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,122 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef ID3_H + +#define ID3_H + +#include <audlegacy/tuple.h> +#include <audlegacy/vfs.h> +#include "../tag_module.h" + + +typedef struct id3v2 +{ + gchar *id3; + guint16 version; + gchar flags; + guint32 size; +} ID3v2Header; + +typedef struct extHeader +{ + guint32 header_size; + guint16 flags; + guint32 padding_size; +}ExtendedHeader; + +typedef struct frameheader +{ + gchar* frame_id; + guint32 size; + guint16 flags; +}ID3v2FrameHeader; + +typedef struct genericframe +{ + ID3v2FrameHeader *header; + gchar* frame_body; +}GenericFrame; + +guint32 read_syncsafe_int32(VFSFile *fd); + +ID3v2Header *readHeader(VFSFile *fd); + +ExtendedHeader *readExtendedHeader(VFSFile *fd); + +ID3v2FrameHeader *readID3v2FrameHeader(VFSFile *fd); + +gchar* readFrameBody(VFSFile *fd,int size); + +GenericFrame *readGenericFrame(VFSFile *fd,GenericFrame *gf); + +void readAllFrames(VFSFile *fd,int framesSize); + +void write_int32(VFSFile *fd, guint32 val); + +void write_syncsafe_int32(VFSFile *fd, guint32 val); + +void write_ASCII(VFSFile *fd, int size, gchar* value); + +void write_utf8(VFSFile *fd, int size,gchar* value); + +guint32 writeAllFramesToFile(VFSFile *fd); + +void writeID3HeaderToFile(VFSFile *fd,ID3v2Header *header); + +void writePaddingToFile(VFSFile *fd, int ksize); + +void writeID3FrameHeaderToFile(VFSFile *fd, ID3v2FrameHeader *header); + +void writeGenericFrame(VFSFile *fd,GenericFrame *frame); + +gboolean isExtendedHeader(ID3v2Header *header); + +int getFrameID(ID3v2FrameHeader *header); + +void skipFrame(VFSFile *fd, guint32 size); + +gboolean isValidFrame(GenericFrame *frame); + +void add_newISO8859_1FrameFromString(const gchar *value,int id3_field); + +void add_newFrameFromTupleStr(Tuple *tuple, int field,int id3_field); + +void add_newFrameFromTupleInt(Tuple *tuple,int field,int id3_field); + +void add_frameFromTupleStr(Tuple *tuple, int field,int id3_field); + +void add_frameFromTupleInt(Tuple *tuple, int field,int id3_field); + +mowgli_dictionary_t *frames ; +mowgli_list_t *frameIDs; + +/* TAG plugin API */ +gboolean id3v2_can_handle_file(VFSFile *f); +Tuple *id3v2_populate_tuple_from_file(Tuple *tuple,VFSFile *f); +gboolean id3v2_write_tuple_to_file(Tuple* tuple, VFSFile *f); + +static const tag_module_t id3v2 = { + .name = "ID3v2", + .can_handle_file = id3v2_can_handle_file, + .populate_tuple_from_file = id3v2_populate_tuple_from_file, + .write_tuple_to_file = id3v2_write_tuple_to_file, +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/tag_module.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,55 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <glib.h> +#include <audlegacy/tuple.h> +#include <audlegacy/vfs.h> +#include "util.h" +#include "tag_module.h" +#include "wma/module.h" +#include "id3/id3v1.h" +#include "id3/id3v2.h" +#include "ape/ape.h" +#include "aac/aac.h" + +void init_tag_modules(void) +{ + mowgli_node_add((void *)&wma, mowgli_node_create(), &tag_modules); + mowgli_node_add((void *)&id3v2, mowgli_node_create(), &tag_modules); + mowgli_node_add((void *)&ape, mowgli_node_create(), &tag_modules); + mowgli_node_add((void *)&id3v1, mowgli_node_create(), &tag_modules); +/* + mowgli_node_add((void *)&aac, mowgli_node_create(), &tag_modules); +*/ +} + +tag_module_t *find_tag_module(VFSFile * fd) +{ + mowgli_node_t *mod, *tmod; + MOWGLI_LIST_FOREACH_SAFE(mod, tmod, tag_modules.head) + { + vfs_fseek(fd, 0, SEEK_SET); + if (((tag_module_t *) (mod->data))->can_handle_file(fd)) + return (tag_module_t *) (mod->data); + } + + AUDDBG("no module found\n"); + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/tag_module.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,50 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +/* Interface of the tagging library */ + +#ifndef TAG_MODULE_H +#define TAG_MODULE_H + +G_BEGIN_DECLS + +#include <glib.h> +#include <mowgli.h> +#include "audlegacy/tuple.h" +#include "audlegacy/vfs.h" + +mowgli_list_t tag_modules; +int number_of_modules; +typedef Tuple* pTuple; + +typedef struct _module { + gchar *name; + gboolean(*can_handle_file) (VFSFile *fd); + pTuple(*populate_tuple_from_file)(Tuple *tuple, VFSFile* fd); + gboolean(*write_tuple_to_file) (Tuple * tuple, VFSFile *fd); +} tag_module_t; + +/* this function must be modified when including new modules */ +void init_tag_modules(void); + +tag_module_t *find_tag_module(VFSFile *fd); + +G_END_DECLS +#endif /* TAG_MODULE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/util.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,446 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <glib.h> +#include "util.h" +#include <inttypes.h> + +/* convert windows time to unix time */ +time_t unix_time(guint64 win_time) +{ + guint64 t = (guint64) ((win_time / 10000000LL) - 11644473600LL); + return (time_t) t; +} + +guint16 get_year(guint64 win_time) +{ + GDate *d = g_date_new(); + g_date_set_time_t(d, unix_time(win_time)); + guint16 year = g_date_get_year(d); + g_date_free(d); + return year; +} + +Tuple *makeTuple(Tuple * tuple, const gchar * title, const gchar * artist, const gchar * comment, const gchar * album, const gchar * genre, const gchar * year, const gchar * filePath, int tracnr) +{ + + tuple_associate_string(tuple, FIELD_ARTIST, NULL, artist); + tuple_associate_string(tuple, FIELD_TITLE, NULL, title); + tuple_associate_string(tuple, FIELD_COMMENT, NULL, comment); + tuple_associate_string(tuple, FIELD_ALBUM, NULL, album); + tuple_associate_string(tuple, FIELD_GENRE, NULL, genre); + tuple_associate_string(tuple, FIELD_YEAR, NULL, year); + tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, tracnr); + tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, filePath); + return tuple; +} + +const gchar *get_complete_filepath(Tuple * tuple) +{ + const gchar *filepath; + const gchar *dir; + const gchar *file; + + dir = tuple_get_string(tuple, FIELD_FILE_PATH, NULL); + file = tuple_get_string(tuple, FIELD_FILE_NAME, NULL); + filepath = g_strdup_printf("%s/%s", dir, file); + AUDDBG("file path = %s\n", filepath); + return filepath; +} + +void print_tuple(Tuple * tuple) +{ +#if WMA_DEBUG + AUDDBG("--------------TUPLE PRINT --------------------\n"); + const gchar *title = tuple_get_string(tuple, FIELD_TITLE, NULL); + AUDDBG("title = %s\n", title); + /* artist */ + const gchar *artist = tuple_get_string(tuple, FIELD_ARTIST, NULL); + AUDDBG("artist = %s\n", artist); + + /* copyright */ + const gchar *copyright = tuple_get_string(tuple, FIELD_COPYRIGHT, NULL); + AUDDBG("copyright = %s\n", copyright); + + /* comment / description */ + + const gchar *comment = tuple_get_string(tuple, FIELD_COMMENT, NULL); + AUDDBG("comment = %s\n", comment); + + /* codec name */ + const gchar *codec_name = tuple_get_string(tuple, FIELD_CODEC, NULL); + AUDDBG("codec = %s\n", codec_name); + + /* album */ + const gchar *album = tuple_get_string(tuple, FIELD_ALBUM, NULL); + AUDDBG("Album = %s\n", album); + + /*track number */ + gint track_nr = tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL); + AUDDBG("Track nr = %d\n", track_nr); + + /* genre */ + const gchar *genre = tuple_get_string(tuple, FIELD_GENRE, NULL); + AUDDBG("Genre = %s \n", genre); + + /* length */ + gint length = tuple_get_int(tuple, FIELD_LENGTH, NULL); + AUDDBG("Length = %d\n", length); + + /* year */ + gint year = tuple_get_int(tuple, FIELD_YEAR, NULL); + AUDDBG("Year = %d\n", year); + + /* quality */ + const gchar *quality = tuple_get_string(tuple, FIELD_QUALITY, NULL); + AUDDBG("quality = %s\n", quality); + + /* path */ + const gchar *path = tuple_get_string(tuple, FIELD_FILE_PATH, NULL); + AUDDBG("path = %s\n", path); + + /* filename */ + const gchar *filename = tuple_get_string(tuple, FIELD_FILE_NAME, NULL); + AUDDBG("filename = %s\n", filename); + + AUDDBG("-----------------END---------------------\n"); +#endif +} + +void seek(VFSFile * f, long pos) +{ + + vfs_fseek(f, pos, SEEK_SET); +} + +void skip(VFSFile * f, int amount) +{ + vfs_fseek(f, amount, SEEK_CUR); +} + +gchar *read_char_data(VFSFile * fd, int size) +{ + gchar *value = g_new0(gchar, size); + vfs_fread(value, size, 1, fd); + return value; +} + +gboolean write_char_data(VFSFile * f, gchar * data, size_t i) +{ + return (vfs_fwrite(data, i, 1, f) == i); +} + +gchar *utf8(gunichar2 * s) +{ + g_return_val_if_fail(s != NULL, NULL); + return g_utf16_to_utf8(s, -1, NULL, NULL, NULL); +} + +gunichar2 *fread_utf16(VFSFile * f, guint64 size) +{ + gunichar2 *p = (gunichar2 *) g_malloc0(size); + if (vfs_fread(p, 1, size, f) != size) + { + g_free(p); + p = NULL; + } + gchar *s = utf8(p); + AUDDBG("Converted to UTF8: '%s'\n", s); + g_free(s); + return p; +} + +gboolean write_utf16(VFSFile * f, gunichar2 * data, size_t i) +{ + return (vfs_fwrite(data, i, 1, f) == i); +} + +guint8 read_uint8(VFSFile * fd) +{ + guint16 i; + if (vfs_fread(&i, 1, 1, fd) == 1) + { + return i; + } + return -1; +} + +guint16 read_LEuint16(VFSFile * fd) +{ + guint16 a; + if (vfs_fget_le16(&a, fd)) + return a; + else + return -1; +} + +guint16 read_BEuint16(VFSFile * fd) +{ + guint16 a; + if (vfs_fget_be16(&a, fd)) + return a; + else + return -1; +} + +guint32 read_LEuint32(VFSFile * fd) +{ + guint32 a; + if (vfs_fget_le32(&a, fd)) + return a; + else + return -1; +} + +guint32 read_BEuint32(VFSFile * fd) +{ + guint32 a; + if (vfs_fget_be32(&a, fd)) + return a; + else + return -1; +} + +guint64 read_LEuint64(VFSFile * fd) +{ + guint64 a; + if (vfs_fget_le64(&a, fd)) + return a; + else + return -1; +} + +guint64 read_BEuint64(VFSFile * fd) +{ + guint64 a; + if (vfs_fget_be64(&a, fd)) + return a; + else + return 1; +} + +gboolean write_uint8(VFSFile * fd, guint8 val) +{ + return (vfs_fwrite(&val, 1, 1, fd) == 1); +} + +gboolean write_LEuint16(VFSFile * fd, guint16 val) +{ + guint16 le_val = GUINT32_TO_LE(val); + return (vfs_fwrite(&le_val, 2, 1, fd) == 2); +} + +gboolean write_BEuint32(VFSFile * fd, guint32 val) +{ + guint32 be_val = GUINT32_TO_BE(val); + return (vfs_fwrite(&be_val, 4, 1, fd) == 4); +} + +gboolean write_LEuint32(VFSFile * fd, guint32 val) +{ + guint32 le_val = GUINT32_TO_LE(val); + return (vfs_fwrite(&le_val, 4, 1, fd) == 4); +} + +gboolean write_LEuint64(VFSFile * fd, guint64 val) +{ + guint64 le_val = GUINT64_TO_LE(val); + return (vfs_fwrite(&le_val, 8, 1, fd) == 8); +} + +void copyAudioToFile(VFSFile * from, VFSFile * to, guint32 pos) +{ + vfs_fseek(from, pos, SEEK_SET); + while (vfs_feof(from) == 0) + { + gchar buf[4096]; + gint n = vfs_fread(buf, 1, 4096, from); + vfs_fwrite(buf, n, 1, to); + } +} + +void copyAudioData(VFSFile * from, VFSFile * to, guint32 pos_from, guint32 pos_to) +{ + vfs_fseek(from, pos_from, SEEK_SET); + int bytes_read = pos_from; + while (bytes_read < pos_to - 4096) + { + gchar buf[4096]; + guint32 n = vfs_fread(buf, 1, 4096, from); + vfs_fwrite(buf, n, 1, to); + bytes_read += n; + } + if (bytes_read < pos_to) + { + guint32 buf_size = pos_to - bytes_read; + gchar buf2[buf_size]; + int nn = vfs_fread(buf2, 1, buf_size, from); + vfs_fwrite(buf2, nn, 1, to); + } +} + +gchar *convert_numericgenre_to_text(gint numericgenre) +{ + const struct + { + gint numericgenre; + gchar *genre; + } + table[] = + { + {GENRE_BLUES, "Blues"}, + {GENRE_CLASSIC_ROCK, "Classic Rock"}, + {GENRE_COUNTRY, "Country"}, + {GENRE_DANCE, "Dance"}, + {GENRE_DISCO, "Disco"}, + {GENRE_FUNK, "Funk"}, + {GENRE_GRUNGE, "Grunge"}, + {GENRE_HIPHOP, "Hip-Hop"}, + {GENRE_JAZZ, "Jazz"}, + {GENRE_METAL, "Metal"}, + {GENRE_NEW_AGE, "New Age"}, + {GENRE_OLDIES, "Oldies"}, + {GENRE_OTHER, "Other"}, + {GENRE_POP, "Pop"}, + {GENRE_R_B, "R&B"}, + {GENRE_RAP, "Rap"}, + {GENRE_REGGAE, "Reggae"}, + {GENRE_ROCK, "Rock"}, + {GENRE_TECHNO, "Techno"}, + {GENRE_INDUSTRIAL, "Industrial"}, + {GENRE_ALTERNATIVE, "Alternative"}, + {GENRE_SKA, "Ska"}, + {GENRE_DEATH_METAL, "Death Metal"}, + {GENRE_PRANKS, "Pranks"}, + {GENRE_SOUNDTRACK, "Soundtrack"}, + {GENRE_EURO_TECHNO, "Euro-Techno"}, + {GENRE_AMBIENT, "Ambient"}, + {GENRE_TRIP_HOP, "Trip-Hop"}, + {GENRE_VOCAL, "Vocal"}, + {GENRE_JAZZ_FUNK, "Jazz+Funk"}, + {GENRE_FUSION, "Fusion"}, + {GENRE_TRANCE, "Trance"}, + {GENRE_CLASSICAL, "Classical"}, + {GENRE_INSTRUMENTAL, "Instrumental"}, + {GENRE_ACID, "Acid"}, + {GENRE_HOUSE, "House"}, + {GENRE_GAME, "Game"}, + {GENRE_SOUND_CLIP, "Sound Clip"}, + {GENRE_GOSPEL, "Gospel"}, + {GENRE_NOISE, "Noise"}, + {GENRE_ALTERNROCK, "AlternRock"}, + {GENRE_BASS, "Bass"}, + {GENRE_SOUL, "Soul"}, + {GENRE_PUNK, "Punk"}, + {GENRE_SPACE, "Space"}, + {GENRE_MEDITATIVE, "Meditative"}, + {GENRE_INSTRUMENTAL_POP, "Instrumental Pop"}, + {GENRE_INSTRUMENTAL_ROCK, "Instrumental Rock"}, + {GENRE_ETHNIC, "Ethnic"}, + {GENRE_GOTHIC, "Gothic"}, + {GENRE_DARKWAVE, "Darkwave"}, + {GENRE_TECHNO_INDUSTRIAL, "Techno-Industrial"}, + {GENRE_ELECTRONIC, "Electronic"}, + {GENRE_POP_FOLK, "Pop-Folk"}, + {GENRE_EURODANCE, "Eurodance"}, + {GENRE_DREAM, "Dream"}, + {GENRE_SOUTHERN_ROCK, "Southern Rock"}, + {GENRE_COMEDY, "Comedy"}, + {GENRE_CULT, "Cult"}, + {GENRE_GANGSTA, "Gangsta"}, + {GENRE_TOP40, "Top 40"}, + {GENRE_CHRISTIAN_RAP, "Christian Rap"}, + {GENRE_POP_FUNK, "Pop/Funk"}, + {GENRE_JUNGLE, "Jungle"}, + {GENRE_NATIVE_AMERICAN, "Native American"}, + {GENRE_CABARET, "Cabaret"}, + {GENRE_NEW_WAVE, "New Wave"}, + {GENRE_PSYCHADELIC, "Psychadelic"}, + {GENRE_RAVE, "Rave"}, + {GENRE_SHOWTUNES, "Showtunes"}, + {GENRE_TRAILER, "Trailer"}, + {GENRE_LO_FI, "Lo-Fi"}, + {GENRE_TRIBAL, "Tribal"}, + {GENRE_ACID_PUNK, "Acid Punk"}, + {GENRE_ACID_JAZZ, "Acid Jazz"}, + {GENRE_POLKA, "Polka"}, + {GENRE_RETRO, "Retro"}, + {GENRE_MUSICAL, "Musical"}, + {GENRE_ROCK_ROLL, "Rock & Roll"}, + {GENRE_HARD_ROCK, "Hard Rock"}, + {GENRE_FOLK, "Folk"}, + {GENRE_FOLK_ROCK, "Folk-Rock"}, + {GENRE_NATIONAL_FOLK, "National Folk"}, + {GENRE_SWING, "Swing"}, + {GENRE_FAST_FUSION, "Fast Fusion"}, + {GENRE_BEBOB, "Bebob"}, + {GENRE_LATIN, "Latin"}, + {GENRE_REVIVAL, "Revival"}, + {GENRE_CELTIC, "Celtic"}, + {GENRE_BLUEGRASS, "Bluegrass"}, + {GENRE_AVANTGARDE, "Avantgarde"}, + {GENRE_GOTHIC_ROCK, "Gothic Rock"}, + {GENRE_PROGRESSIVE_ROCK, "Progressive Rock"}, + {GENRE_PSYCHEDELIC_ROCK, "Psychedelic Rock"}, + {GENRE_SYMPHONIC_ROCK, "Symphonic Rock"}, + {GENRE_SLOW_ROCK, "Slow Rock"}, + {GENRE_BIG_BAND, "Big Band"}, + {GENRE_CHORUS, "Chorus"}, + {GENRE_EASY_LISTENING, "Easy Listening"}, + {GENRE_ACOUSTIC, "Acoustic"}, + {GENRE_HUMOUR, "Humour"}, + {GENRE_SPEECH, "Speech"}, + {GENRE_CHANSON, "Chanson"}, + {GENRE_OPERA, "Opera"}, + {GENRE_CHAMBER_MUSIC, "Chamber Music"}, + {GENRE_SONATA, "Sonata"}, + {GENRE_SYMPHONY, "Symphony"}, + {GENRE_BOOTY_BASS, "Booty Bass"}, + {GENRE_PRIMUS, "Primus"}, + {GENRE_PORN_GROOVE, "Porn Groove"}, + {GENRE_SATIRE, "Satire"}, + {GENRE_SLOW_JAM, "Slow Jam"}, + {GENRE_CLUB, "Club"}, + {GENRE_TANGO, "Tango"}, + {GENRE_SAMBA, "Samba"}, + {GENRE_FOLKLORE, "Folklore"}, + {GENRE_BALLAD, "Ballad"}, + {GENRE_POWER_BALLAD, "Power Ballad"}, + {GENRE_RHYTHMIC_SOUL, "Rhythmic Soul"}, + {GENRE_FREESTYLE, "Freestyle"}, + {GENRE_DUET, "Duet"}, + {GENRE_PUNK_ROCK, "Punk Rock"}, + {GENRE_DRUM_SOLO, "Drum Solo"}, + {GENRE_A_CAPELLA, "A capella"}, + {GENRE_EURO_HOUSE, "Euro-House"}, + }; + + gint count; + + for (count = 0; count < G_N_ELEMENTS(table); count++) + { + if (table[count].numericgenre == numericgenre) + { + return table[count].genre; + } + } + + return "Unknown"; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/util.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,211 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef TAGUTIL_H + +#define TAGUTIL_H + +#include <glib.h> +#include "audlegacy/tuple.h" +#include "audlegacy/vfs.h" + +#define WMA_DEBUG 1 + +#define BROKEN 1 + +enum { + GENRE_BLUES = 0, + GENRE_CLASSIC_ROCK, + GENRE_COUNTRY, + GENRE_DANCE, + GENRE_DISCO, + GENRE_FUNK, + GENRE_GRUNGE, + GENRE_HIPHOP, + GENRE_JAZZ, + GENRE_METAL, + GENRE_NEW_AGE, + GENRE_OLDIES, + GENRE_OTHER, + GENRE_POP, + GENRE_R_B, + GENRE_RAP, + GENRE_REGGAE, + GENRE_ROCK, + GENRE_TECHNO, + GENRE_INDUSTRIAL, + GENRE_ALTERNATIVE, + GENRE_SKA, + GENRE_DEATH_METAL, + GENRE_PRANKS, + GENRE_SOUNDTRACK, + GENRE_EURO_TECHNO, + GENRE_AMBIENT, + GENRE_TRIP_HOP, + GENRE_VOCAL, + GENRE_JAZZ_FUNK, + GENRE_FUSION, + GENRE_TRANCE, + GENRE_CLASSICAL, + GENRE_INSTRUMENTAL, + GENRE_ACID, + GENRE_HOUSE, + GENRE_GAME, + GENRE_SOUND_CLIP, + GENRE_GOSPEL, + GENRE_NOISE, + GENRE_ALTERNROCK, + GENRE_BASS, + GENRE_SOUL, + GENRE_PUNK, + GENRE_SPACE, + GENRE_MEDITATIVE, + GENRE_INSTRUMENTAL_POP, + GENRE_INSTRUMENTAL_ROCK, + GENRE_ETHNIC, + GENRE_GOTHIC, + GENRE_DARKWAVE, + GENRE_TECHNO_INDUSTRIAL, + GENRE_ELECTRONIC, + GENRE_POP_FOLK, + GENRE_EURODANCE, + GENRE_DREAM, + GENRE_SOUTHERN_ROCK, + GENRE_COMEDY, + GENRE_CULT, + GENRE_GANGSTA, + GENRE_TOP40, + GENRE_CHRISTIAN_RAP, + GENRE_POP_FUNK, + GENRE_JUNGLE, + GENRE_NATIVE_AMERICAN, + GENRE_CABARET, + GENRE_NEW_WAVE, + GENRE_PSYCHADELIC, + GENRE_RAVE, + GENRE_SHOWTUNES, + GENRE_TRAILER, + GENRE_LO_FI, + GENRE_TRIBAL, + GENRE_ACID_PUNK, + GENRE_ACID_JAZZ, + GENRE_POLKA, + GENRE_RETRO, + GENRE_MUSICAL, + GENRE_ROCK_ROLL, + GENRE_HARD_ROCK, + GENRE_FOLK, + GENRE_FOLK_ROCK, + GENRE_NATIONAL_FOLK, + GENRE_SWING, + GENRE_FAST_FUSION, + GENRE_BEBOB, + GENRE_LATIN, + GENRE_REVIVAL, + GENRE_CELTIC, + GENRE_BLUEGRASS, + GENRE_AVANTGARDE, + GENRE_GOTHIC_ROCK, + GENRE_PROGRESSIVE_ROCK, + GENRE_PSYCHEDELIC_ROCK, + GENRE_SYMPHONIC_ROCK, + GENRE_SLOW_ROCK, + GENRE_BIG_BAND, + GENRE_CHORUS, + GENRE_EASY_LISTENING, + GENRE_ACOUSTIC, + GENRE_HUMOUR, + GENRE_SPEECH, + GENRE_CHANSON, + GENRE_OPERA, + GENRE_CHAMBER_MUSIC, + GENRE_SONATA, + GENRE_SYMPHONY, + GENRE_BOOTY_BASS, + GENRE_PRIMUS, + GENRE_PORN_GROOVE, + GENRE_SATIRE, + GENRE_SLOW_JAM, + GENRE_CLUB, + GENRE_TANGO, + GENRE_SAMBA, + GENRE_FOLKLORE, + GENRE_BALLAD, + GENRE_POWER_BALLAD, + GENRE_RHYTHMIC_SOUL, + GENRE_FREESTYLE, + GENRE_DUET, + GENRE_PUNK_ROCK, + GENRE_DRUM_SOLO, + GENRE_A_CAPELLA, + GENRE_EURO_HOUSE +}; + +time_t unix_time(guint64 win_time); + +guint16 get_year(guint64 win_time); + +void print_tuple(Tuple *tuple); + +//Tuple *makeTuple(Tuple *tuple, const gchar* title, const gchar* artist, +// const gchar* comment, const gchar* album, +// const gchar * genre, const gchar* year, +// const gchar* filePath, int tracnr); + +gchar *utf8(gunichar2* s); +const gchar* get_complete_filepath(Tuple *tuple); + +gchar *read_char_data(VFSFile *fd, int size); +gboolean write_char_data(VFSFile *f, gchar *data, size_t i); + +gunichar2 *fread_utf16(VFSFile* f, guint64 size); +gboolean write_utf16(VFSFile *f, gunichar2 *data, size_t i); + +guint8 read_uint8(VFSFile *fd); +guint16 read_LEuint16(VFSFile *fd); +guint16 read_BEuint16(VFSFile *fd); +guint32 read_LEuint32(VFSFile *fd); +guint32 read_BEuint32(VFSFile *fd); +guint64 read_LEuint64(VFSFile *fd); +guint64 read_BEuint64(VFSFile *fd); + + +gboolean write_uint8(VFSFile *fd, guint8 val); +gboolean write_BEuint16(VFSFile *fd, guint16 val); +gboolean write_LEuint16(VFSFile *fd, guint16 val); +gboolean write_BEuint32(VFSFile *fd, guint32 val); +gboolean write_LEuint32(VFSFile *fd, guint32 val); +gboolean write_BEuint64(VFSFile *fd, guint64 val); +gboolean write_LEuint64(VFSFile *fd, guint64 val); + +guint64 read_LEint64(VFSFile *fd); +void copyAudioToFile(VFSFile *from, VFSFile *to, guint32 pos); +void copyAudioData(VFSFile* from, VFSFile *to, guint32 pos_from, guint32 pos_to); + +gchar *convert_numericgenre_to_text(gint numericgenre); + +/* macro for debug print */ +#ifdef WMA_DEBUG +# define AUDDBG(...) do { g_print("%s:%d %s(): ", __FILE__, (int)__LINE__, __FUNCTION__); g_print(__VA_ARGS__); } while (0) +#else +# define AUDDBG(...) do { } while (0) +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/guid.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,128 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +/* this stuff may be moved to ../util.h if needed by other formats */ + +#include <inttypes.h> + +#include "audlegacy/vfs.h" +#include "../util.h" +#include "guid.h" +#include "wma_fmt.h" + +GUID *guid_read_from_file(VFSFile * f) +{ + GUID temp; + + if ((f == NULL) || (vfs_fread(&temp, sizeof(GUID), 1, f) != 1)) + return NULL; + + temp.le32 = GUINT32_FROM_LE(temp.le32); + temp.le16_1 = GUINT16_FROM_LE(temp.le16_1); + temp.le16_2 = GUINT16_FROM_LE(temp.le16_2); + temp.be64 = GUINT64_FROM_BE(temp.be64); + + return g_memdup(&temp, sizeof(GUID)); +} + +gboolean guid_write_to_file(VFSFile * f, int guid_type) +{ + g_return_val_if_fail(f != NULL, FALSE); + + GUID *g = guid_convert_from_string(wma_guid_map(guid_type)); + + gboolean ret = write_LEuint32(f, g->le32) && write_LEuint16(f, g->le16_1) && write_LEuint16(f, g->le16_1) && write_LEuint64(f, g->be64); + g_free(g); + return ret; +} + +GUID *guid_convert_from_string(const gchar * string) +{ + GUID temp; + + if (sscanf(string, "%" SCNx32 "-%" SCNx16 "-%" SCNx16 "-%" SCNx64, &temp.le32, &temp.le16_1, &temp.le16_2, &temp.be64) != 4) + return NULL; + return g_memdup(&temp, sizeof(GUID)); +} + +gchar *guid_convert_to_string(const GUID * g) +{ + + return g_strdup_printf("%8x-%hx-%hx-%" PRIx64 "\n", GUINT32_TO_LE(g->le32), GUINT16_TO_LE(g->le16_1), GUINT16_TO_LE(g->le16_2), GUINT64_TO_BE(g->be64)); +} + +gboolean guid_equal(GUID * g1, GUID * g2) +{ + /* + AUDDBG("GUID 1 = %8x-%hx-%hx-%"PRIx64"\n", g1->le32, g1->le16_1, g1->le16_2, g1->be64); + AUDDBG("GUID 2 = %8x-%hx-%hx-%"PRIx64"\n", g2->le32, g2->le16_1, g2->le16_2, g2->be64); + */ + + g_return_val_if_fail((g1 != NULL) && (g2 != NULL), FALSE); + if (!memcmp(g1, g2, 16)) + { + // AUDDBG("equal\n"); + + return TRUE; + } + /* AUDDBG("not equal\n"); */ + return FALSE; +} + +int get_guid_type(GUID * g) +{ + GUID *g1; + int i; + for (i = 0; i < ASF_OBJECT_LAST - 1; i++) + { + g1 = guid_convert_from_string(wma_guid_map(i)); + if (guid_equal(g, g1)) + { + + g_free(g1); + return i; + } + } + return -1; +} + +const gchar *wma_guid_map(int i) +{ + const gchar *_guid_map[ASF_OBJECT_LAST] = { + ASF_HEADER_OBJECT_GUID, + ASF_FILE_PROPERTIES_OBJECT_GUID, + ASF_STREAM_PROPERTIES_OBJECT_GUID, + ASF_HEADER_EXTENSION_OBJECT_GUID, + ASF_CODEC_LIST_OBJECT_GUID, + ASF_SCRIPT_COMMAND_OBJECT_GUID, + ASF_MARKER_OBJECT_GUID, + ASF_BITRATE_MUTUAL_EXCLUSION_OBJECT_GUID, + ASF_ERROR_CORRECTION_OBJECT_GUID, + ASF_CONTENT_DESCRIPTION_OBJECT_GUID, + ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID, + ASF_CONTENT_BRANDING_OBJECT_GUID, + ASF_STREAM_BITRATE_PROPERTIES_OBJECT_GUID, + ASF_CONTENT_ENCRYPTION_OBJECT_GUID, + ASF_EXTENDED_CONTENT_ENCRYPTION_OBJECT_GUID, + ASF_DIGITAL_SIGNATURE_OBJECT_GUID, + ASF_PADDING_OBJECT_GUID + }; + return _guid_map[i]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/guid.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,45 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef _GUID_H +#define _GUID_H + +#include <glib.h> +#include <audlegacy/vfs.h> + +/* this stuff may be moved to ../util.h if needed by other formats */ +typedef struct _guid { + guint32 le32; + guint16 le16_1; + guint16 le16_2; + guint64 be64; +}GUID; + + +GUID *guid_read_from_file(VFSFile *); +gboolean guid_write_to_file(VFSFile *, int); + +GUID *guid_convert_from_string(const gchar *); +gchar *guid_convert_to_string(const GUID*); +gboolean guid_equal(GUID *, GUID *); +int get_guid_type(GUID *); +const gchar *wma_guid_map(int); + +#endif /* _GUID_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/module.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef TAG_WMA_MODULE_H + +#define TAG_WMA_MODULE_H +#include "../tag_module.h" +#include "wma.h" +static const tag_module_t wma = { + .name = "WMA", + .can_handle_file = wma_can_handle_file, + .populate_tuple_from_file = wma_populate_tuple_from_file, + .write_tuple_to_file = wma_write_tuple_to_file, +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/wma.c Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,429 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#include <inttypes.h> +#include <glib-2.0/glib/gstdio.h> +#include <audlegacy/tuple.h> + +#include "guid.h" +#include "wma.h" +#include "wma_fmt.h" +#include "module.h" +#include "../util.h" + +/* static functions */ +static GenericHeader *read_generic_header(VFSFile * f, gboolean read_data) +{ + AUDDBG("read top-level header object\n"); + g_return_val_if_fail((f != NULL), NULL); + GenericHeader *header = g_new0(GenericHeader, 1); + header->guid = guid_read_from_file(f); + header->size = read_LEuint64(f); + if (read_data) + header->data = (gchar *) read_char_data(f, header->size); + else + header->data = NULL; + + gchar *s = guid_convert_to_string(header->guid); + AUDDBG("read GUID: %s\n", s); + g_free(s); + + return header; +} + +static HeaderObj *read_top_header_object(VFSFile * f) +{ + AUDDBG("read top-level header object\n"); + g_return_val_if_fail((f != NULL), NULL); + HeaderObj *header = g_new0(HeaderObj, 1); + + //we have already read the GUID so we skip it (16 bytes) + vfs_fseek(f, 16, SEEK_SET); + + header->size = read_LEuint64(f); + header->objectsNr = read_LEuint32(f); + AUDDBG("Number of child objects: %d\n", header->objectsNr); + + header->res1 = read_uint8(f); + header->res2 = read_uint8(f); + + if ((header->size == -1) || (header->objectsNr == -1) || (header->res2 != 2)) + { + g_free(header); + return NULL; + } + return header; +} + +static ContentDescrObj *read_content_descr_obj(VFSFile * f) +{ + ContentDescrObj *cdo = g_new0(ContentDescrObj, 1); + cdo->guid = guid_read_from_file(f); + cdo->size = read_LEuint64(f); + cdo->title_length = read_LEuint16(f); + cdo->author_length = read_LEuint16(f); + cdo->copyright_length = read_LEuint16(f); + cdo->desc_length = read_LEuint16(f); + cdo->rating_length = read_LEuint16(f); + cdo->title = fread_utf16(f, cdo->title_length); + cdo->author = fread_utf16(f, cdo->author_length); + cdo->copyright = fread_utf16(f, cdo->copyright_length); + cdo->description = fread_utf16(f, cdo->desc_length); + cdo->rating = fread_utf16(f, cdo->rating_length); + return cdo; +} + +static ExtContentDescrObj *read_ext_content_descr_obj(VFSFile * f, Tuple * t, gboolean populate_tuple) +{ + ExtContentDescrObj *ecdo = g_new0(ExtContentDescrObj, 1); + ecdo->guid = guid_read_from_file(f); + ecdo->size = read_LEuint64(f); + ecdo->content_desc_count = read_LEuint16(f); + ecdo->descriptors = read_descriptors(f, ecdo->content_desc_count, t, populate_tuple); + return ecdo; +} + +static guint find_descriptor_id(gchar * s) +{ + AUDDBG("finding descriptor id for '%s'\n", s); + g_return_val_if_fail(s != NULL, -1); + gchar *l[DESC_LAST] = { DESC_ALBUM_STR, DESC_YEAR_STR, DESC_GENRE_STR, DESC_TRACK_STR }; + guint i; + for (i = 0; i < DESC_LAST; i++) + if (!strcmp(l[i], s)) + { + AUDDBG("found descriptor %s\n", s); + return i; + } + return -1; +} + +static ContentDescriptor *read_descriptor(VFSFile * f, Tuple * t, gboolean populate_tuple) +{ + ContentDescriptor *cd = g_new0(ContentDescriptor, 1); + gchar *val = NULL, *name = NULL; + guint32 intval = -1; + gint dtype; + AUDDBG("reading name_len\n"); + cd->name_len = read_LEuint16(f); + AUDDBG("reading name\n"); + cd->name = fread_utf16(f, cd->name_len); + AUDDBG("reading val_type\n"); + cd->val_type = read_LEuint16(f); + AUDDBG("reading val_len\n"); + cd->val_len = read_LEuint16(f); + + name = utf8(cd->name); + dtype = find_descriptor_id(name); + g_free(name); + + AUDDBG("reading val\n"); + + if (populate_tuple) + { + /*we only parse int's and UTF strings, everything else is handled as raw data */ + if (cd->val_type == 0) + { /*UTF16 */ + cd->val = read_char_data(f, cd->val_len); + val = utf8((gunichar2 *) cd->val); + AUDDBG("val: '%s' dtype: %d\n", val, dtype); + if (dtype == DESC_ALBUM) + tuple_associate_string(t, FIELD_ALBUM, NULL, val); + if (dtype == DESC_GENRE) + tuple_associate_string(t, FIELD_GENRE, NULL, val); + if (dtype == DESC_TRACK) + tuple_associate_int(t, FIELD_TRACK_NUMBER, NULL, atoi(val)); + if (dtype == DESC_YEAR) + tuple_associate_int(t, FIELD_YEAR, NULL, atoi(val)); + } + else + { + if (cd->val_type == 3) + { + intval = read_LEuint32(f); + AUDDBG("intval: %d, dtype: %d\n", intval, dtype); + if (dtype == DESC_TRACK) + tuple_associate_int(t, FIELD_TRACK_NUMBER, NULL, intval); + } + else + cd->val = read_char_data(f, cd->val_len); + } + } + else + cd->val = read_char_data(f, cd->val_len); + AUDDBG("read str_val: '%s', intval: %d\n", val, intval); + AUDDBG("exiting read_descriptor \n\n"); + return cd; +} + +static ContentDescriptor **read_descriptors(VFSFile * f, guint count, Tuple * t, gboolean populate_tuple) +{ + if (count == 0) + return NULL; + ContentDescriptor **descs = g_new0(ContentDescriptor *, count); + int i; + for (i = 0; i < count; i++) + descs[i] = read_descriptor(f, t, populate_tuple); + return descs; +} + +void free_content_descr_obj(ContentDescrObj * c) +{ + g_free(c->guid); + g_free(c->title); + g_free(c->author); + g_free(c->copyright); + g_free(c->description); + g_free(c->rating); + g_free(c); +} + +void free_content_descr(ContentDescriptor * cd) +{ + g_free(cd->name); + g_free(cd->val); + g_free(cd); +} + +void free_ext_content_descr_obj(ExtContentDescrObj * ecdo) +{ + int i; + g_free(ecdo->guid); + for (i = 0; i < ecdo->content_desc_count; i++) + free_content_descr(ecdo->descriptors[i]); + g_free(ecdo); +} + +/* returns the offset of the object in the file */ +static long ftell_object_by_guid(VFSFile * f, GUID * g) +{ + AUDDBG("seeking object %s, with ID %d \n", guid_convert_to_string(g), get_guid_type(g)); + HeaderObj *h = read_top_header_object(f); + g_return_val_if_fail((f != NULL) && (g != NULL) && (h != NULL), -1); + + gint i = 0; + while (i < h->objectsNr) + { + GenericHeader *gen_hdr = read_generic_header(f, FALSE); + AUDDBG("encountered GUID %s, with ID %d\n", guid_convert_to_string(gen_hdr->guid), get_guid_type(gen_hdr->guid)); + if (guid_equal(gen_hdr->guid, g)) + { + g_free(h); + g_free(gen_hdr); + guint64 ret = vfs_ftell(f) - 24; + AUDDBG("at offset %" PRIx64 "\n", ret); + return ret; + } + vfs_fseek(f, gen_hdr->size - 24, SEEK_CUR); //most headers have a size as their second field" + i++; + } + AUDDBG("The object was not found\n"); + + return -1; +} + +VFSFile *make_temp_file() +{ + /* create a temporary file */ + const gchar *tmpdir = g_get_tmp_dir(); + gchar *tmp_path = g_strdup_printf("file://%s/%s", tmpdir, "wmatmp.wma"); + return vfs_fopen(tmp_path, "w+"); +} + +long ftell_object_by_str(VFSFile * f, gchar * s) +{ + GUID *g = guid_convert_from_string(s); + long res = ftell_object_by_guid(f, g); + g_free(g); + return res; +} + +static void write_content_descr_obj_from_tuple(VFSFile * f, ContentDescrObj * cdo, Tuple * t) +{ + glong size; + gboolean free_cdo = FALSE; + if (cdo == NULL) + { + cdo = g_new0(ContentDescrObj, 1); + free_cdo = TRUE; + } + + cdo->title = g_utf8_to_utf16(tuple_get_string(t, FIELD_TITLE, NULL), -1, NULL, &size, NULL); + cdo->title_length = 2 * (size + 1); + cdo->author = g_utf8_to_utf16(tuple_get_string(t, FIELD_ARTIST, NULL), -1, NULL, &size, NULL); + cdo->author_length = 2 * (size + 1); + cdo->copyright = g_utf8_to_utf16(tuple_get_string(t, FIELD_COPYRIGHT, NULL), -1, NULL, &size, NULL); + cdo->copyright_length = 2 * (size + 1); + cdo->description = g_utf8_to_utf16(tuple_get_string(t, FIELD_COMMENT, NULL), -1, NULL, &size, NULL); + cdo->desc_length = 2 * (size + 1); + cdo->size = 34 + cdo->title_length + cdo->author_length + cdo->copyright_length + cdo->desc_length; + guid_write_to_file(f, ASF_CONTENT_DESCRIPTION_OBJECT); + write_LEuint64(f, cdo->size); + write_LEuint16(f, cdo->title_length); + write_LEuint16(f, cdo->author_length); + write_LEuint16(f, cdo->copyright_length); + write_LEuint16(f, cdo->desc_length); + write_LEuint16(f, 2); // rating_length = 2 + write_utf16(f, cdo->title, cdo->title_length); + write_utf16(f, cdo->title, cdo->title_length); + write_utf16(f, cdo->author, cdo->author_length); + write_utf16(f, cdo->copyright, cdo->copyright_length); + write_utf16(f, cdo->description, cdo->desc_length); + write_utf16(f, NULL, 2); //rating == NULL + + if (free_cdo) + free_content_descr_obj(cdo); +} + +static void write_ext_content_descr_obj_from_tuple(VFSFile * f, ExtContentDescrObj * ecdo, Tuple * tuple) +{ +} + +static gboolean write_generic_header(VFSFile * f, GenericHeader * gh) +{ + AUDDBG("Writing generic header\n"); + guid_write_to_file(f, get_guid_type(gh->guid)); + return write_char_data(f, gh->data, gh->size); +} + +static void free_generic_header(GenericHeader * gh) +{ + g_free(gh->guid); + g_free(gh->data); + g_free(gh); +} + +static gboolean write_top_header_object(VFSFile * f, HeaderObj * header) +{ + AUDDBG("write header object\n"); + vfs_fseek(f, 0, SEEK_SET); + return (guid_write_to_file(f, ASF_HEADER_OBJECT) && write_LEuint64(f, header->size) && write_LEuint32(f, header->objectsNr) && write_uint8(f, header->res1) && /* the reserved fields */ + write_uint8(f, header->res2)); +} + +/* interface functions */ +gboolean wma_can_handle_file(VFSFile * f) +{ + GUID *guid1 = guid_read_from_file(f); + GUID *guid2 = guid_convert_from_string(ASF_HEADER_OBJECT_GUID); + gboolean equal = ((guid1 != NULL) && guid_equal(guid1, guid2)); + + g_free(guid1); + g_free(guid2); + + return equal; +} + +Tuple *wma_populate_tuple_from_file(Tuple * t, VFSFile * f) +{ + gchar *artist = NULL, *title = NULL, *comment = NULL; + + print_tuple(t); + gint seek_res = vfs_fseek(f, ftell_object_by_str(f, ASF_CONTENT_DESCRIPTION_OBJECT_GUID), SEEK_SET); + if (seek_res == 0) + { //if the CONTENT_DESCRIPTION_OBJECT was found + ContentDescrObj *cdo = read_content_descr_obj(f); + artist = utf8(cdo->author); + title = utf8(cdo->title); + comment = utf8(cdo->description); + free_content_descr_obj(cdo); + tuple_associate_string(t, FIELD_ARTIST, NULL, artist); + tuple_associate_string(t, FIELD_TITLE, NULL, title); + tuple_associate_string(t, FIELD_COMMENT, NULL, comment); + } + seek_res = vfs_fseek(f, ftell_object_by_str(f, ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID), SEEK_SET); + /*this populates the tuple with the attributes stored as extended descriptors */ + ExtContentDescrObj *ecdo = read_ext_content_descr_obj(f, t, TRUE); + free_ext_content_descr_obj(ecdo); + print_tuple(t); + return t; +} + +gboolean wma_write_tuple_to_file(Tuple * tuple, VFSFile * f) +{ + + +#if BROKEN + return FALSE; +#endif + + HeaderObj *top_ho = read_top_header_object(f); + VFSFile *tmpfile = make_temp_file(); + gint i, cdo_pos, ecdo_pos; + GUID *g; + /*read all the headers and write them to the new file */ + /*the headers that contain tuple data will be overwritten */ + AUDDBG("Header Object size: %" PRId64 "\n", top_ho->size); + //vfs_fseek(tmpfile, ) + for (i = 0; i < top_ho->objectsNr; i++) + { + GenericHeader *gh = read_generic_header(f, TRUE); + g = guid_convert_from_string(ASF_CONTENT_DESCRIPTION_OBJECT_GUID); + if (guid_equal(gh->guid, g)) + { + write_content_descr_obj_from_tuple(tmpfile, (ContentDescrObj *) gh, tuple); + g_free(g); + } + else + { + g = guid_convert_from_string(ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID); + if (guid_equal(gh->guid, g)) + write_ext_content_descr_obj_from_tuple(tmpfile, (ExtContentDescrObj *) gh, tuple); + else + { + write_generic_header(tmpfile, gh); + g_free(g); + } + } + free_generic_header(gh); + } + /*check wether these headers existed in the original file */ + cdo_pos = ftell_object_by_str(f, ASF_CONTENT_DESCRIPTION_OBJECT_GUID); + ecdo_pos = ftell_object_by_str(f, ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID); + if (cdo_pos == -1) + { + write_content_descr_obj_from_tuple(tmpfile, NULL, tuple); + top_ho->objectsNr++; + } + if (ecdo_pos != -1) + { + write_ext_content_descr_obj_from_tuple(tmpfile, NULL, tuple); + top_ho->objectsNr++; + } + write_top_header_object(tmpfile, top_ho); + + gchar *f1 = g_filename_from_uri(tmpfile->uri, NULL, NULL); + gchar *f2 = g_filename_from_uri(f->uri, NULL, NULL); + vfs_fclose(tmpfile); + /* + if (g_rename(f1, f2) == 0) + { + AUDDBG("the tag was updated successfully\n"); + } + else + { + AUDDBG("an error has occured\n"); + } + */ + g_free(f1); + g_free(f2); + + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/wma.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,54 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef _WMA_H + +#define _WMA_H + +#include "audlegacy/tuple.h" +#include "audlegacy/vfs.h" +#include "wma_fmt.h" + +#ifndef TAG_WMA_MODULE_H +/* static functions */ + +/* reads the whole structure, containing the data */ +static GenericHeader *read_generic_header(VFSFile *f, gboolean read_data); + +static HeaderObj *read_top_header_object(VFSFile *f); + +static gboolean write_top_header_object(VFSFile *f, HeaderObj *h); + +static ContentDescriptor **read_descriptors(VFSFile *f, guint count, Tuple*t, gboolean populate_tuple); + +static ContentDescrObj *read_content_descr_obj(VFSFile *f); + +static ExtContentDescrObj *read_ext_content_descr_obj(VFSFile * f, Tuple *t, gboolean populate_tuple); + +static long ftell_object_by_guid(VFSFile *f, GUID *g); +static long ftell_object_by_str(VFSFile *f, gchar *s); +#endif + +/* TAG plugin API */ +gboolean wma_can_handle_file(VFSFile *f); +Tuple *wma_populate_tuple_from_file(Tuple *t, VFSFile *f); +gboolean wma_write_tuple_to_file(Tuple *t, VFSFile *f); + +#endif /* _WMA_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libaudtag/wma/wma_fmt.h Wed May 05 18:26:06 2010 +0900 @@ -0,0 +1,162 @@ +/* + * Copyright 2009 Paula Stanciu + * + * This file is part of Audacious. + * + * Audacious 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, version 3 of the License. + * + * Audacious 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 + * Audacious. If not, see <http://www.gnu.org/licenses/>. + * + * The Audacious team does not consider modular code linking to Audacious or + * using our public API to be a derived work. + */ + +#ifndef _WMA_FMT_H +#define _WMA_FMT_H + +#include "guid.h" + + +#define ASF_HEADER_OBJECT_GUID "75B22630-668E-11CF-A6D900AA0062CE6C" +#define ASF_FILE_PROPERTIES_OBJECT_GUID "8CABDCA1-A947-11CF-8EE400C00C205365" +#define ASF_STREAM_PROPERTIES_OBJECT_GUID "B7DC0791-A9B7-11CF-8EE600C00C205365" +#define ASF_HEADER_EXTENSION_OBJECT_GUID "5FBF03B5-A92E-11CF-8EE300C00C205365" +#define ASF_CODEC_LIST_OBJECT_GUID "86D15240-311D-11D0-A3A400A0C90348F6" +#define ASF_SCRIPT_COMMAND_OBJECT_GUID "1EFB1A30-0B62-11D0-A39B00A0C90348F6" +#define ASF_MARKER_OBJECT_GUID "F487CD01-A951-11CF-8EE600C00C205365" +#define ASF_BITRATE_MUTUAL_EXCLUSION_OBJECT_GUID "D6E229DC-35DA-11D1-903400A0C90349BE" +#define ASF_ERROR_CORRECTION_OBJECT_GUID "75B22635-668E-11CF-A6D900AA0062CE6C" +#define ASF_CONTENT_DESCRIPTION_OBJECT_GUID "75B22633-668E-11CF-A6D900AA0062CE6C" +#define ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT_GUID "D2D0A440-E307-11D2-97F000A0C95EA850" +#define ASF_CONTENT_BRANDING_OBJECT_GUID "2211B3FA-BD23-11D2-B4B700A0C955FC6E" +#define ASF_STREAM_BITRATE_PROPERTIES_OBJECT_GUID "7BF875CE-468D-11D1-8D82006097C9A2B2" +#define ASF_CONTENT_ENCRYPTION_OBJECT_GUID "2211B3FB-BD23-11D2-B4B700A0C955FC6E" +#define ASF_EXTENDED_CONTENT_ENCRYPTION_OBJECT_GUID "298AE614-2622-4C17-B935DAE07EE9289C" +#define ASF_DIGITAL_SIGNATURE_OBJECT_GUID "2211B3FC-BD23-11D2-B4B700A0C955FC6E" +#define ASF_PADDING_OBJECT_GUID "1806D474-CADF-4509-A4BA9AABCB96AAE8" + +typedef enum { + ASF_HEADER_OBJECT = 0, + ASF_FILE_PROPERTIES_OBJECT, /* 1 */ + ASF_STREAM_PROPERTIES_OBJECT, + ASF_HEADER_EXTENSION_OBJECT, + ASF_CODEC_LIST_OBJECT, + ASF_SCRIPT_COMMAND_OBJECT, /* 5 */ + ASF_MARKER_OBJECT, + ASF_BITRATE_MUTUAL_EXCLUSION_OBJECT, + ASF_ERROR_CORRECTION_OBJECT, + ASF_CONTENT_DESCRIPTION_OBJECT, + ASF_EXTENDED_CONTENT_DESCRIPTION_OBJECT, /* 10*/ + ASF_CONTENT_BRANDING_OBJECT, + ASF_STREAM_BITRATE_PROPERTIES_OBJECT, + ASF_CONTENT_ENCRYPTION_OBJECT, + ASF_EXTENDED_CONTENT_ENCRYPTION_OBJECT, + ASF_DIGITAL_SIGNATURE_OBJECT, /* 15 */ + ASF_PADDING_OBJECT, + ASF_OBJECT_LAST /* dummy */ +} ObjectType; + +#define DESC_ALBUM_STR "WM/AlbumTitle" +#define DESC_YEAR_STR "WM/Year" +#define DESC_GENRE_STR "WM/Genre" +#define DESC_TRACK_STR "WM/TrackNumber" + +typedef enum { + DESC_ALBUM = 0, + DESC_YEAR, + DESC_GENRE, + DESC_TRACK, + DESC_LAST +} DescrIndexes; + +/* + * this should be fine for all headers whose content is irrelevant, + * but the size is needed so that we can skip it + */ +typedef struct _generic_header { + GUID *guid; + guint64 size; + gchar *data; +} GenericHeader; + +typedef struct _header_object { + GUID *guid; + guint64 size; + guint32 objectsNr; + guint8 res1; + guint8 res2; +} HeaderObj; + +/* + * this is special, its size does not include the size of the ext_data + */ +typedef struct _header_extension_object { + GUID *guid; + guint64 size; + guint32 objects_count; + guint8 res1; + guint8 res2; + guint32 ext_data_size; + gchar *ext_data; +} HeaderExtensionObject; + +typedef struct _file_properties_header { + GUID *guid; + guint64 size; + gchar dontcare1[16]; + guint64 duration; //expressed as the count of 100ns intervals + gchar dontcare2[32]; +} FilePropertiesHeader; + +typedef struct _content_description_object { + GUID *guid; + guint64 size; + guint16 title_length; + guint16 author_length; + guint16 copyright_length; /* dontcare*/ + guint16 desc_length; + guint16 rating_length; /* dontcare*/ + gunichar2 *title; + gunichar2 *author; + gunichar2 *copyright; /* dontcare*/ + gunichar2 *description; + gunichar2 *rating; /* dontcare*/ +} ContentDescrObj; + +/* descr_val_type's meaning + * Value Type length + * 0x0000 Unicode string varies + * 0x0001 BYTE array varies + * 0x0002 BOOL 32 + * 0x0003 DWORD 32 + * 0x0004 QWORD 64 + * 0x0005 WORD 16 + */ +typedef struct _content_descriptor { + guint16 name_len; + gunichar2 *name; + guint16 val_type; + guint16 val_len; + gchar * val; +} ContentDescriptor; + +typedef struct _extended_content_description_object { + GUID *guid; + guint64 size; + guint16 content_desc_count; + ContentDescriptor **descriptors; +} ExtContentDescrObj; + +typedef struct _content_field { + guint16 size; + gunichar2 *strValue; +} ContentField; + +#endif /* _WMA_FMT_H */