# HG changeset patch # User Eugene Zagidullin # Date 1196384193 -10800 # Node ID 8b3c2fe608c9e26593414c058f4ce32ab37a673a # Parent 32f9f1e4a9ec0031b65f6e36712230a38c5527e7 APEv2 writing implemented diff -r 32f9f1e4a9ec -r 8b3c2fe608c9 src/demac/ape.c --- a/src/demac/ape.c Thu Nov 29 04:17:51 2007 +0300 +++ b/src/demac/ape.c Fri Nov 30 03:56:33 2007 +0300 @@ -74,6 +74,18 @@ return tmp[0] | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24); } +int put_le32(uint32_t val, VFSFile *vfd) +{ + unsigned char tmp[4]; + + tmp[0] = (val & 0x000000ff); + tmp[1] = (val & 0x0000ff00) >> 8; + tmp[2] = (val & 0x00ff0000) >> 16; + tmp[3] = (val & 0xff000000) >> 24; + + return aud_vfs_fwrite(tmp, 1, 4, vfd); +} + uint64_t get_le64(VFSFile *vfd) { unsigned char tmp[8]; diff -r 32f9f1e4a9ec -r 8b3c2fe608c9 src/demac/ape.h --- a/src/demac/ape.h Thu Nov 29 04:17:51 2007 +0300 +++ b/src/demac/ape.h Fri Nov 30 03:56:33 2007 +0300 @@ -290,6 +290,7 @@ uint16_t get_le16(VFSFile *vfd); uint32_t get_le32(VFSFile *vfd); +int put_le32(uint32_t val, VFSFile *vfd); uint64_t get_le64(VFSFile *vfd); int ape_read_header(APEContext *ape, VFSFile *pb, int probe_only); diff -r 32f9f1e4a9ec -r 8b3c2fe608c9 src/demac/apev2.c --- a/src/demac/apev2.c Thu Nov 29 04:17:51 2007 +0300 +++ b/src/demac/apev2.c Fri Nov 30 03:56:33 2007 +0300 @@ -37,6 +37,16 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +#define FLAGS_HEADER_EXISTS (1 << 31) +#define FLAGS_HEADER (1 << 29) +#define APE_SIGNATURE MKTAG64('A', 'P', 'E', 'T', 'A', 'G', 'E', 'X') + +typedef struct { + int tag_items; + int tag_size; + VFSFile *vfd; +} iterator_pvt_t; + mowgli_dictionary_t* parse_apev2_tag(VFSFile *vfd) { unsigned char tmp[TMP_BUFSIZE+1]; unsigned char tmp2[TMP_BUFSIZE+1]; @@ -45,12 +55,12 @@ long tag_size, item_size; int item_flags; int tag_items; - int tag_flags; + unsigned int tag_flags; mowgli_dictionary_t *dict; aud_vfs_fseek(vfd, -32, SEEK_END); signature = get_le64(vfd); - if (signature != MKTAG64('A', 'P', 'E', 'T', 'A', 'G', 'E', 'X')) { + if (signature != APE_SIGNATURE) { #ifdef DEBUG fprintf(stderr, "** demac: apev2.c: APE tag not found\n"); #endif @@ -85,23 +95,125 @@ #endif /* read key */ - for(p = tmp; p <= tmp+TMP_BUFSIZE; p++) { - aud_vfs_fread(p, 1, 1, vfd); - if(*p == '\0') break; - } - *(p+1) = '\0'; + if (item_size > 0 && item_size < tag_size) { /* be bulletproof */ + for(p = tmp; p <= tmp+TMP_BUFSIZE; p++) { + aud_vfs_fread(p, 1, 1, vfd); + if(*p == '\0') break; + } + *(p+1) = '\0'; - /* read item */ - aud_vfs_fread(tmp2, 1, MIN(item_size, TMP_BUFSIZE), vfd); - tmp2[item_size] = '\0'; + /* read item */ + aud_vfs_fread(tmp2, 1, MIN(item_size, TMP_BUFSIZE), vfd); + tmp2[item_size] = '\0'; #ifdef DEBUG - fprintf(stderr, "%s: \"%s\", f:%08x\n", tmp, tmp2, item_flags); + fprintf(stderr, "%s: \"%s\", f:%08x\n", tmp, tmp2, item_flags); #endif - /* APEv2 stores all items in utf-8 */ - gchar *item = ((tag_version == 1000 ) ? aud_str_to_utf8((gchar*)tmp2) : g_strdup((gchar*)tmp2)); + /* APEv2 stores all items in utf-8 */ + gchar *item = ((tag_version == 1000 ) ? aud_str_to_utf8((gchar*)tmp2) : g_strdup((gchar*)tmp2)); - mowgli_dictionary_add(dict, (char*)tmp, item); + mowgli_dictionary_add(dict, (char*)tmp, item); + } } - + return dict; } + +static void write_header_or_footer(guint32 version, guint32 size, guint32 items, guint32 flags, VFSFile *vfd) { + guint64 filling = 0; + + aud_vfs_fwrite("APETAGEX", 1, 8, vfd); + put_le32(version, vfd); + put_le32(size, vfd); + put_le32(items, vfd); + put_le32(flags, vfd); + aud_vfs_fwrite(&filling, 1, 8, vfd); +} + +static int foreach_cb (mowgli_dictionary_elem_t *delem, void *privdata) { + iterator_pvt_t *s = (iterator_pvt_t*)privdata; + guint32 item_size, item_flags=0; + if (s->vfd == NULL) { + + if(strlen((char*)(delem->data)) != 0) { + s->tag_items++; + s->tag_size += strlen((char*)(delem->key)) + strlen((char*)(delem->data)) + 9; /* length in bytes not symbols */ + } + + } else { + + if( (item_size = strlen((char*)delem->data)) != 0 ) { +#ifdef DEBUG + fprintf(stderr, "Writing field %s = %s\n", (char*)delem->key, (char*)delem->data); +#endif + put_le32(item_size, s->vfd); + put_le32(item_flags, s->vfd); /* all set to zero */ + aud_vfs_fwrite(delem->key, 1, strlen((char*)delem->key) + 1, s->vfd); /* null-terminated */ + aud_vfs_fwrite(delem->data, 1, item_size, s->vfd); + } + } + + return 1; +} + +gboolean write_apev2_tag(VFSFile *vfd, mowgli_dictionary_t *tag) { + guint64 signature; + guint32 tag_version; + guint32 tag_size, tag_items = 0, tag_flags; + long file_size; + + if (vfd == NULL || tag == NULL) return FALSE; + + aud_vfs_fseek(vfd, -32, SEEK_END); + signature = get_le64(vfd); + + /* strip existing tag */ + if (signature == APE_SIGNATURE) { + tag_version = get_le32(vfd); + tag_size = get_le32(vfd); + tag_items = get_le32(vfd); + tag_flags = get_le32(vfd); + aud_vfs_fseek(vfd, 0, SEEK_END); + file_size = aud_vfs_ftell(vfd); + file_size -= (long)tag_size; + + /* also strip header */ + if((tag_version >= 2000) && (tag_flags | FLAGS_HEADER_EXISTS)) { + aud_vfs_fseek(vfd, -((long)tag_size)-32, SEEK_END); + signature = get_le64(vfd); /* be bulletproof: check header also */ + if (signature == APE_SIGNATURE) { +#ifdef DEBUG + fprintf(stderr, "stripping also header\n"); +#endif + file_size -= 32; + } + } +#ifdef DEBUG + fprintf(stderr, "stripping existing tag\n"); +#endif + if(aud_vfs_truncate(vfd, file_size) < 0) return FALSE; + } + aud_vfs_fseek(vfd, 0, SEEK_END); + + iterator_pvt_t state; + memset(&state, 0, sizeof(iterator_pvt_t)); + + state.tag_size = 32; /* footer size */ + mowgli_dictionary_foreach(tag, foreach_cb, &state); /* let's count tag size */ + tag_size = state.tag_size; + tag_items = state.tag_items; + + if(tag_items == 0) { +#ifdef DEBUG + fprintf(stderr, "tag stripped, all done\n"); +#endif + return TRUE; /* old tag is stripped, new one is empty */ + } + + write_header_or_footer(2000, tag_size, tag_items, FLAGS_HEADER | FLAGS_HEADER_EXISTS, vfd); /* header */ + state.vfd = vfd; + mowgli_dictionary_foreach(tag, foreach_cb, &state); + write_header_or_footer(2000, tag_size, tag_items, FLAGS_HEADER_EXISTS, vfd); /* footer */ + + return TRUE; +} + diff -r 32f9f1e4a9ec -r 8b3c2fe608c9 src/demac/apev2.h --- a/src/demac/apev2.h Thu Nov 29 04:17:51 2007 +0300 +++ b/src/demac/apev2.h Fri Nov 30 03:56:33 2007 +0300 @@ -5,5 +5,6 @@ #include mowgli_dictionary_t* parse_apev2_tag(VFSFile *vfd); +gboolean write_apev2_tag(VFSFile *vfd, mowgli_dictionary_t *tag); #endif diff -r 32f9f1e4a9ec -r 8b3c2fe608c9 src/demac/plugin.c --- a/src/demac/plugin.c Thu Nov 29 04:17:51 2007 +0300 +++ b/src/demac/plugin.c Fri Nov 30 03:56:33 2007 +0300 @@ -389,17 +389,43 @@ } } +static void insert_str_tuple_field_to_dictionary(Tuple *tuple, int fieldn, mowgli_dictionary_t *dict, char *key) { + + if(mowgli_dictionary_find(dict, key) != NULL) g_free(mowgli_dictionary_delete(dict, key)); + + gchar *tmp = (gchar*)aud_tuple_get_string(tuple, fieldn, NULL); + if(tmp != NULL && strlen(tmp) != 0) mowgli_dictionary_add(dict, key, g_strdup(tmp)); +} + +static void insert_int_tuple_field_to_dictionary(Tuple *tuple, int fieldn, mowgli_dictionary_t *dict, char *key) { + int val; + + if(mowgli_dictionary_find(dict, key) != NULL) g_free(mowgli_dictionary_delete(dict, key)); + + if(aud_tuple_get_value_type(tuple, fieldn, NULL) == TUPLE_INT && (val = aud_tuple_get_int(tuple, fieldn, NULL)) >= 0) { + gchar *tmp = g_strdup_printf("%d", val); + mowgli_dictionary_add(dict, key, tmp); + } +} + static gboolean demac_update_song_tuple(Tuple *tuple, VFSFile *vfd) { - fprintf(stderr, "demac_update_song_tuple(): stub\n"); - fprintf(stderr, "Title: %s\n", aud_tuple_get_string(tuple, FIELD_TITLE, NULL)); - fprintf(stderr, "Artist: %s\n", aud_tuple_get_string(tuple, FIELD_ARTIST, NULL)); - fprintf(stderr, "Album: %s\n", aud_tuple_get_string(tuple, FIELD_ALBUM, NULL)); - fprintf(stderr, "Comment: %s\n", aud_tuple_get_string(tuple, FIELD_COMMENT, NULL)); - fprintf(stderr, "Genre: %s\n", aud_tuple_get_string(tuple, FIELD_GENRE, NULL)); - fprintf(stderr, "Year: %d\n", aud_tuple_get_int(tuple, FIELD_YEAR, NULL)); - fprintf(stderr, "Track: %d\n", aud_tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL)); + + mowgli_dictionary_t *tag = parse_apev2_tag(vfd); + if (tag == NULL) tag = mowgli_dictionary_create(g_ascii_strcasecmp); - return TRUE; + insert_str_tuple_field_to_dictionary(tuple, FIELD_TITLE, tag, "Title"); + insert_str_tuple_field_to_dictionary(tuple, FIELD_ARTIST, tag, "Artist"); + insert_str_tuple_field_to_dictionary(tuple, FIELD_ALBUM, tag, "Album"); + insert_str_tuple_field_to_dictionary(tuple, FIELD_COMMENT, tag, "Comment"); + insert_str_tuple_field_to_dictionary(tuple, FIELD_GENRE, tag, "Genre"); + + insert_int_tuple_field_to_dictionary(tuple, FIELD_YEAR, tag, "Year"); + insert_int_tuple_field_to_dictionary(tuple, FIELD_TRACK_NUMBER, tag, "Track"); + + gboolean ret = write_apev2_tag(vfd, tag); + mowgli_dictionary_destroy(tag, destroy_cb, NULL); + + return ret; } static gchar *fmts[] = { "ape", NULL };