Mercurial > audlegacy-plugins
view src/demac/apev2.c @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | d10f13536b94 |
children | ed6c81bd9016 |
line wrap: on
line source
/* * Audacious Monkey's Audio plugin, an APE tag reading stuff * * Copyright (C) Eugene Zagidullin 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA * */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <glib.h> #include <mowgli.h> #include <audacious/vfs.h> #include <audacious/plugin.h> #include "ape.h" #include "apev2.h" #define TMP_BUFSIZE 256 #ifndef MIN #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]; guint64 signature; int tag_version; long tag_size, item_size; int item_flags; int tag_items; unsigned int tag_flags; mowgli_dictionary_t *dict; aud_vfs_fseek(vfd, -32, SEEK_END); signature = get_le64(vfd); if (signature != APE_SIGNATURE) { #ifdef DEBUG fprintf(stderr, "** demac: apev2.c: APE tag not found\n"); #endif return NULL; } tag_version = get_le32(vfd); tag_size = get_le32(vfd); tag_items = get_le32(vfd); tag_flags = get_le32(vfd); #ifdef DEBUG fprintf(stderr, "** demac: apev2.c: found APE tag version %d, size %ld, contains %d items, flags %08x\n", tag_version, tag_size, tag_items, tag_flags); #endif if(tag_items == 0) { #ifdef DEBUG fprintf(stderr, "** demac: apev2.c: found empty tag\n"); #endif return NULL; } dict = mowgli_dictionary_create(g_ascii_strcasecmp); aud_vfs_fseek(vfd, -tag_size, SEEK_END); int i; unsigned char *p; for(i=0; i<tag_items; i++) { item_size = get_le32(vfd); item_flags = get_le32(vfd); #ifdef DEBUG fprintf(stderr, "** demac: apev2.c: item %d, size %ld, flags %d\n", i, item_size, item_flags); #endif /* read key */ 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'; #ifdef DEBUG 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)); 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); } gboolean write_apev2_tag(VFSFile *vfd, mowgli_dictionary_t *tag) { guint64 signature; guint32 tag_version; guint32 tag_size, tag_items = 0, tag_flags; guint32 item_size, item_flags=0; long file_size; void *current_field; 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); mowgli_dictionary_iteration_state_t state; tag_size = 32; /* footer size */ /* let's count tag size */ tag_items = 0; MOWGLI_DICTIONARY_FOREACH(current_field, &state, tag) { if(strlen((char*)current_field) != 0) { tag_items++; tag_size += strlen((char*)state.cur->key) + strlen((char*)current_field) + 9; /* length in bytes not symbols */ } } 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 */ MOWGLI_DICTIONARY_FOREACH(current_field, &state, tag) { if( (item_size = strlen((char*)current_field)) != 0 ) { #ifdef DEBUG fprintf(stderr, "Writing field %s = %s\n", (char*)state.cur->key, (char*)current_field); #endif put_le32(item_size, vfd); put_le32(item_flags, vfd); /* all set to zero */ aud_vfs_fwrite(state.cur->key, 1, strlen((char*)state.cur->key) + 1, vfd); /* null-terminated */ aud_vfs_fwrite(current_field, 1, item_size, vfd); } } write_header_or_footer(2000, tag_size, tag_items, FLAGS_HEADER_EXISTS, vfd); /* footer */ return TRUE; }