major plugin revamp based on x-fixer's code

This commit is contained in:
Josh Coalson
2004-09-24 13:57:40 +00:00
parent 838408a3ee
commit 9745f25e78
30 changed files with 2991 additions and 1963 deletions

View File

@@ -1,169 +1,719 @@
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include "canonical_tag.h"
#include "id3v2.h"
#include "vorbiscomment.h"
#include "FLAC/assert.h"
#include "FLAC/metadata.h"
static void local__safe_free(void *object)
{
if(0 != object)
free(object);
}
static void local__copy_field(char **dest, const char *src, unsigned n)
{
if(n > 0) {
const char *p = src + n;
while(p > src && *(--p) == ' ')
;
n = p - src + 1;
if(0 != (*dest = malloc(n+1))) {
memcpy(*dest, src, n);
(*dest)[n] = '\0';
}
}
else
*dest = 0;
}
static FLAC__bool local__get_id3v1_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__Id3v1_Tag id3v1_tag;
if(FLAC_plugin__id3v1_tag_get(filename, &id3v1_tag)) {
FLAC_plugin__canonical_tag_convert_from_id3v1(tag, &id3v1_tag);
return true;
}
return false;
}
FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new()
{
FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag));
if(0 != object)
FLAC_plugin__canonical_tag_init(object);
return object;
}
void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object)
{
FLAC__ASSERT(0 != object);
FLAC_plugin__canonical_tag_clear(object);
free(object);
}
void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object)
{
FLAC__ASSERT(0 != object);
object->title = 0;
object->composer = 0;
object->performer = 0;
object->album = 0;
object->year_recorded = 0;
object->year_performed = 0;
object->track_number = 0;
object->tracks_in_album = 0;
object->genre = 0;
object->comment = 0;
}
void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object)
{
FLAC__ASSERT(0 != object);
local__safe_free(object->title);
local__safe_free(object->composer);
local__safe_free(object->performer);
local__safe_free(object->album);
local__safe_free(object->year_recorded);
local__safe_free(object->year_performed);
local__safe_free(object->track_number);
local__safe_free(object->tracks_in_album);
local__safe_free(object->genre);
local__safe_free(object->comment);
FLAC_plugin__canonical_tag_init(object);
}
static void local__grab(char **dest, char **src)
{
if(0 == *dest) {
*dest = *src;
*src = 0;
}
}
void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, FLAC_Plugin__CanonicalTag *src)
{
local__grab(&dest->title, &src->title);
local__grab(&dest->composer, &src->composer);
local__grab(&dest->performer, &src->performer);
local__grab(&dest->album, &src->album);
local__grab(&dest->year_recorded, &src->year_recorded);
local__grab(&dest->year_performed, &src->year_performed);
local__grab(&dest->track_number, &src->track_number);
local__grab(&dest->tracks_in_album, &src->tracks_in_album);
local__grab(&dest->genre, &src->genre);
local__grab(&dest->comment, &src->comment);
}
void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v1_Tag *id3v1_tag)
{
local__copy_field(&object->title, id3v1_tag->title, 30);
local__copy_field(&object->composer, id3v1_tag->artist, 30);
local__copy_field(&object->performer, id3v1_tag->artist, 30);
local__copy_field(&object->album, id3v1_tag->album, 30);
local__copy_field(&object->year_performed, id3v1_tag->year, 4);
/* Check for v1.1 tags. */
if (id3v1_tag->comment.v1_1.zero == 0) {
if(0 != (object->track_number = malloc(4)))
sprintf(object->track_number, "%u", (unsigned)id3v1_tag->comment.v1_1.track);
local__copy_field(&object->comment, id3v1_tag->comment.v1_1.comment, 28);
}
else {
object->track_number = strdup("0");
local__copy_field(&object->comment, id3v1_tag->comment.v1_0.comment, 30);
}
object->genre = strdup(FLAC_plugin__id3v1_tag_get_genre_as_string(id3v1_tag->genre));
}
void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__CanonicalTag id3v1_tag, id3v2_tag;
FLAC_plugin__vorbiscomment_get(filename, tag);
FLAC_plugin__canonical_tag_init(&id3v2_tag);
(void)FLAC_plugin__id3v2_tag_get(filename, &id3v2_tag);
FLAC_plugin__canonical_tag_init(&id3v1_tag);
(void)local__get_id3v1_tag_as_canonical(filename, &id3v1_tag);
/* merge tags, preferring, in order: vorbis comments, id3v2, id3v1 */
FLAC_plugin__canonical_tag_merge(tag, &id3v2_tag);
FLAC_plugin__canonical_tag_merge(tag, &id3v1_tag);
FLAC_plugin__canonical_tag_clear(&id3v1_tag);
FLAC_plugin__canonical_tag_clear(&id3v2_tag);
}
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include "canonical_tag.h"
#include "id3v2.h"
#include "vorbiscomment.h"
#include "FLAC/assert.h"
#include "FLAC/metadata.h"
#if 0
#define __USE_GNU /*@@@@@@ needed on glibc systems to get wcsdup() and wcscasecmp() */
#endif
#include <wchar.h>
/*
* Here lies hackage to get any missing wide character string functions we
* need. The fallback implementations here are from glibc.
*/
#if !defined(_MSC_VER) && !defined(HAVE_WCSDUP)
/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
/* Duplicate S, returning an identical malloc'd string. */
wchar_t *
wcsdup (s)
const wchar_t *s;
{
size_t len = (__wcslen (s) + 1) * sizeof (wchar_t);
void *new = malloc (len);
if (new == NULL)
return NULL;
return (wchar_t *) memcpy (new, (void *) s, len);
}
#endif
#if !defined(_MSC_VER) && !defined(HAVE_WCSCASECMP)
/* Copyright (C) 1991, 1992, 1995, 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <wctype.h>
#include <wchar.h>
#ifndef weak_alias
# define __wcscasecmp wcscasecmp
# define TOLOWER(Ch) towlower (Ch)
#else
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
# define __wcscasecmp __wcscasecmp_l
# define TOLOWER(Ch) __towlower_l ((Ch), loc)
# else
# define TOLOWER(Ch) towlower (Ch)
# endif
#endif
#ifdef USE_IN_EXTENDED_LOCALE_MODEL
# define LOCALE_PARAM , loc
# define LOCALE_PARAM_DECL __locale_t loc;
#else
# define LOCALE_PARAM
# define LOCALE_PARAM_DECL
#endif
/* Compare S1 and S2, ignoring case, returning less than, equal to or
greater than zero if S1 is lexicographically less than,
equal to or greater than S2. */
int
__wcscasecmp (s1, s2 LOCALE_PARAM)
const wchar_t *s1;
const wchar_t *s2;
LOCALE_PARAM_DECL
{
wint_t c1, c2;
if (s1 == s2)
return 0;
do
{
c1 = TOLOWER (*s1++);
c2 = TOLOWER (*s2++);
if (c1 == L'\0')
break;
}
while (c1 == c2);
return c1 - c2;
}
#ifndef __wcscasecmp
weak_alias (__wcscasecmp, wcscasecmp)
#endif
#endif
#ifndef _MSC_VER
/* @@@ cheesy and does base 10 only */
wchar_t *local__itow(int value, wchar_t *string)
{
if (value == 0) {
string[0] = (wchar_t)'0';
string[1] = (wchar_t)0;
}
else {
/* convert backwards, then reverse string */
wchar_t *start = string, *s;
if (value < 0) {
*start++ = (wchar_t)'-';
value = -value; /* @@@ overflow at INT_MIN */
}
s = start;
while (value > 0) {
*s++ = (wchar_t)((value % 10) + '0');
value /= 10;
}
*s-- = (wchar_t)0;
while (s > start) {
wchar_t tmp = *s;
*s-- = *start;
*start++ = tmp;
}
}
return string;
}
#endif
/*
* helpers
*/
/* TODO: should be moved out somewhere? @@@ */
wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src)
{
int len;
wchar_t *dest;
FLAC__ASSERT(0 != src);
len = strlen(src) + 1;
/* copy */
dest = malloc(len*sizeof(wchar_t));
if (dest) mbstowcs(dest, src, len);
return dest;
}
/* TODO: more validation? @@@ */
static __inline int utf8len(const FLAC__byte *utf8)
{
FLAC__ASSERT(0 != utf8);
if ((*utf8 & 0x80) == 0)
return 1;
else if ((*utf8 & 0xE0) == 0xC0)
return 2;
else if ((*utf8 & 0xF0) == 0xE0)
return 3;
else return 0;
}
/* TODO: validation? @@@ */
static __inline int utf8_to_ucs2(const FLAC__byte *utf8, wchar_t *ucs2)
{
int len;
FLAC__ASSERT(utf8!=0 && *utf8!=0 && ucs2!=0);
if (!(len = utf8len(utf8))) return 0;
if (len == 1)
*ucs2 = *utf8;
else if (len == 2)
*ucs2 = (*utf8 & 0x3F)<<6 | (*(utf8+1) & 0x3F);
else if (len == 3)
*ucs2 = (*utf8 & 0x1F)<<12 | (*(utf8+1) & 0x3F)<<6 | (*(utf8+2) & 0x3F);
else {
FLAC__ASSERT(len == 0);
}
return len;
}
wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length)
{
wchar_t *out, *p;
const char *s;
int len = 0;
/* calculate length */
for (s=src; length && *s; len++)
{
int l = utf8len(s);
if (!l) break;
s += l;
length -= l;
}
/* allocate */
len++;
p = out = (wchar_t*)malloc(len * sizeof(wchar_t));
if (!out) return NULL;
/* convert */
for (s=src; --len; p++)
{
int l = utf8_to_ucs2(s, p);
/* l==0 is possible, because real conversion */
/* might do more careful validation */
if (!l) break;
s += l;
}
*p = 0;
return out;
}
static __inline int ucs2len(wchar_t ucs2)
{
if (ucs2 < 0x0080)
return 1;
else if (ucs2 < 0x0800)
return 2;
else return 3;
}
static __inline int ucs2_to_utf8(wchar_t ucs2, FLAC__byte *utf8)
{
if (ucs2 < 0x080)
{
utf8[0] = (FLAC__byte)ucs2;
return 1;
}
else if (ucs2 < 0x800)
{
utf8[0] = 0xc0 | (ucs2 >> 6);
utf8[1] = 0x80 | (ucs2 & 0x3f);
return 2;
}
else
{
utf8[0] = 0xe0 | (ucs2 >> 12);
utf8[1] = 0x80 | ((ucs2 >> 6) & 0x3f);
utf8[2] = 0x80 | (ucs2 & 0x3f);
return 3;
}
}
char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src)
{
const wchar_t *s;
char *out, *p;
int len = 0;
FLAC__ASSERT(0 != src);
/* calculate length */
for (s=src; *s; s++)
len += ucs2len(*s);
/* allocate */
len++;
p = out = malloc(len);
if (!out) return NULL;
/* convert */
for (s=src; *s; s++)
{
int l = ucs2_to_utf8(*s, p);
p += l;
}
*p = 0;
return out;
}
/*
* init/clear/delete
*/
FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new()
{
FLAC_Plugin__CanonicalTag *object = (FLAC_Plugin__CanonicalTag*)malloc(sizeof(FLAC_Plugin__CanonicalTag));
if (object != 0)
FLAC_plugin__canonical_tag_init(object);
return object;
}
void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *object)
{
FLAC_plugin__canonical_tag_clear(object);
free(object);
}
void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *object)
{
object->head = object->tail = 0;
object->count = 0;
}
static void FLAC_plugin__canonical_tag_clear_entry(FLAC__tag_entry *entry)
{
free(entry->name);
free(entry->value);
free(entry);
}
void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *object)
{
FLAC__tag_entry *entry = object->head;
while (entry)
{
FLAC__tag_entry *next = entry->next;
FLAC_plugin__canonical_tag_clear_entry(entry);
entry = next;
}
FLAC_plugin__canonical_tag_init(object);
}
/*
* internal
*/
static FLAC__tag_entry *FLAC_plugin__canonical_find(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)
{
FLAC__tag_entry *entry = tag->head;
while (entry)
{
#if defined _MSC_VER || defined __MINGW32__
#define FLAC__WCSCASECMP wcsicmp
#else
#define FLAC__WCSCASECMP wcscasecmp
#endif
if (!FLAC__WCSCASECMP(name, entry->name))
#undef FLAC__WCSCASECMP
break;
entry = entry->next;
}
return entry;
}
/* NOTE: does NOT copy strings. takes ownership over passed strings. */
static void FLAC_plugin__canonical_add_tail(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value)
{
FLAC__tag_entry *entry = (FLAC__tag_entry*)malloc(sizeof(FLAC__tag_entry));
if (!entry)
{
free(name);
free(value);
return;
}
/* init */
entry->name = name;
entry->value = value;
/* add */
entry->prev = tag->tail;
if (tag->tail)
tag->tail->next = entry;
tag->tail = entry;
if (!tag->head)
tag->head = entry;
entry->next = 0;
tag->count++;
}
static void FLAC_plugin__canonical_add_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)
{
FLAC_plugin__canonical_add_tail(tag, wcsdup(name), wcsdup(value));
}
/* NOTE: does NOT copy value, but copies name */
static void FLAC_plugin__canonical_set_nc(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, wchar_t *value)
{
FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);
if (entry)
{
free(entry->value);
entry->value = value;
}
else FLAC_plugin__canonical_add_tail(tag, wcsdup(name), value);
}
/* NOTE: does NOT copy strings. takes ownership over passed strings. (except sep!) */
static void FLAC_plugin__canonical_add_nc(FLAC_Plugin__CanonicalTag *tag, wchar_t *name, wchar_t *value, const wchar_t *sep)
{
FLAC__tag_entry *entry;
if (sep && (entry = FLAC_plugin__canonical_find(tag, name)))
{
unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1;
wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t));
if (newvalue)
{
entry->value = newvalue;
wcscat(entry->value, sep);
wcscat(entry->value, value);
}
free(name);
free(value);
}
else FLAC_plugin__canonical_add_tail(tag, name, value);
}
/*
* manipulation
*/
void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)
{
FLAC_plugin__canonical_set_nc(tag, name, wcsdup(value));
}
void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value)
{
FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);
if (!entry) FLAC_plugin__canonical_add_new(tag, name, value);
}
void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value)
{
wchar_t *val = FLAC_plugin__convert_ansi_to_wide(value);
if (val) FLAC_plugin__canonical_set_nc(tag, name, val);
}
void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep)
{
FLAC__tag_entry *entry;
if (sep && (entry = FLAC_plugin__canonical_find(tag, name)))
{
unsigned newlen = wcslen(entry->value) + wcslen(value) + wcslen(sep) + 1;
wchar_t *newvalue = realloc(entry->value, newlen*sizeof(wchar_t));
if (newvalue)
{
entry->value = newvalue;
wcscat(entry->value, sep);
wcscat(entry->value, value);
}
}
else FLAC_plugin__canonical_add_new(tag, name, value);
}
void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep)
{
wchar_t *n = FLAC_plugin__convert_utf8_to_ucs2(name, namelen);
wchar_t *v = FLAC_plugin__convert_utf8_to_ucs2(value, vallen);
wchar_t *s = sep ? FLAC_plugin__convert_utf8_to_ucs2(sep, -1) : 0;
if (n && v)
{
FLAC_plugin__canonical_add_nc(tag, n, v, s);
}
else
{
if (n) free(n);
if (v) free(v);
}
if (s) free(s);
}
const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)
{
FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);
return entry ? entry->value : 0;
}
FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)
{
FLAC__tag_entry *entry = FLAC_plugin__canonical_find(tag, name);
if (entry)
{
if (entry->prev)
entry->prev->next = entry->next;
else tag->head = entry->next;
if (entry->next)
entry->next->prev = entry->prev;
else tag->tail = entry->prev;
FLAC_plugin__canonical_tag_clear_entry(entry);
tag->count--;
return true;
}
return false;
}
void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name)
{
while (FLAC_plugin__canonical_remove(tag, name));
}
char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it)
{
int len1 = wcslen(it->name);
int len2 = wcslen(it->value);
int len = len1 + len2 + 1;
wchar_t *val = malloc((len+1) * sizeof(wchar_t));
if (val)
{
char *res;
memcpy(val, it->name, len1 * sizeof(wchar_t));
val[len1] = '=';
memcpy(val+len1+1, it->value, len2 * sizeof(wchar_t));
val[len] = 0;
res = FLAC_plugin__convert_ucs2_to_utf8(val);
free(val);
return res;
}
return NULL;
}
/*
* merging
*/
void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src)
{
FLAC__tag_entry *entry = src->head;
while (entry)
{
FLAC_plugin__canonical_set_new(dest, entry->name, entry->value);
entry = entry->next;
}
}
static wchar_t *local__copy_field(const char *src, unsigned n)
{
const char *p = src + n;
wchar_t *dest;
FLAC__ASSERT(n > 0);
while (p>src && *(--p)==' ');
n = p - src + 1;
if (!n) return NULL;
if ((dest = malloc((n+1)*sizeof(wchar_t))) != 0)
{
mbstowcs(dest, src, n);
dest[n] = 0;
}
return dest;
}
static void local__add_id3_field(FLAC_Plugin__CanonicalTag *object, const char *value, size_t length, const wchar_t *new_name)
{
wchar_t *tmp;
if (0 != value && length > 0) {
tmp = local__copy_field(value, length);
if (tmp)
FLAC_plugin__canonical_add_tail(object, wcsdup(new_name), tmp);
}
}
void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v1_Tag *id3v1_tag)
{
wchar_t *tmp;
FLAC_plugin__canonical_tag_clear(object);
local__add_id3_field(object, id3v1_tag->title, 30, L"TITLE");
local__add_id3_field(object, id3v1_tag->artist, 30, L"ARTIST");
local__add_id3_field(object, id3v1_tag->album, 30, L"ALBUM");
local__add_id3_field(object, id3v1_tag->year, 4, L"YEAR");
/* check for v1.1 tags */
if (id3v1_tag->zero == 0)
{
if (id3v1_tag->track && (tmp=(wchar_t*)malloc(sizeof(id3v1_tag->track)*4*sizeof(wchar_t)))!=0)
{
#ifdef _MSC_VER
_itow(id3v1_tag->track, tmp, 10);
#else
local__itow(id3v1_tag->track, tmp);
#endif
FLAC_plugin__canonical_add_tail(object, wcsdup(L"TRACKNUMBER"), tmp);
}
local__add_id3_field(object, id3v1_tag->comment, 28, L"DESCRIPTION");
}
else
{
local__add_id3_field(object, id3v1_tag->comment, 30, L"DESCRIPTION");
}
tmp = FLAC_plugin__convert_ansi_to_wide(FLAC_plugin__id3v1_tag_get_genre_as_string(id3v1_tag->genre));
if (tmp) FLAC_plugin__canonical_add_tail(object, wcsdup(L"GENRE"), tmp);
}
void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *object, const FLAC_Plugin__Id3v2_Tag *id3v2_tag)
{
FLAC_plugin__canonical_tag_clear(object);
local__add_id3_field(object, id3v2_tag->title , strlen(id3v2_tag->title) , L"TITLE");
local__add_id3_field(object, id3v2_tag->composer , strlen(id3v2_tag->composer) , L"ARTIST");
local__add_id3_field(object, id3v2_tag->performer , strlen(id3v2_tag->performer) , L"PERFORMER");
local__add_id3_field(object, id3v2_tag->album , strlen(id3v2_tag->album) , L"ALBUM");
local__add_id3_field(object, id3v2_tag->year_recorded , strlen(id3v2_tag->year_recorded) , L"YEAR_RECORDED");
local__add_id3_field(object, id3v2_tag->year_performed , strlen(id3v2_tag->year_performed) , L"YEAR_PERFORMED");
local__add_id3_field(object, id3v2_tag->track_number , strlen(id3v2_tag->track_number) , L"TRACKNUMBER");
local__add_id3_field(object, id3v2_tag->tracks_in_album, strlen(id3v2_tag->tracks_in_album), L"TRACKS_IN_ALBUM");
local__add_id3_field(object, id3v2_tag->genre , strlen(id3v2_tag->genre) , L"GENRE");
local__add_id3_field(object, id3v2_tag->comment , strlen(id3v2_tag->comment) , L"DESCRIPTION");
}
static FLAC__bool local__get_id3v1_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__Id3v1_Tag id3v1_tag;
if (FLAC_plugin__id3v1_tag_get(filename, &id3v1_tag))
{
FLAC_plugin__canonical_tag_convert_from_id3v1(tag, &id3v1_tag);
return true;
}
return false;
}
static FLAC__bool local__get_id3v2_tag_as_canonical(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__Id3v2_Tag id3v2_tag;
if (FLAC_plugin__id3v2_tag_get(filename, &id3v2_tag))
{
FLAC_plugin__canonical_tag_convert_from_id3v2(tag, &id3v2_tag);
return true;
}
return false;
}
void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__CanonicalTag id3v1_tag;
FLAC_plugin__canonical_tag_init(&id3v1_tag);
(void)local__get_id3v1_tag_as_canonical(filename, &id3v1_tag);
FLAC_plugin__canonical_tag_merge(tag, &id3v1_tag);
FLAC_plugin__canonical_tag_clear(&id3v1_tag);
}
void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC_Plugin__CanonicalTag id3v2_tag;
FLAC_plugin__canonical_tag_init(&id3v2_tag);
(void)local__get_id3v2_tag_as_canonical(filename, &id3v2_tag);
FLAC_plugin__canonical_tag_merge(tag, &id3v2_tag);
FLAC_plugin__canonical_tag_clear(&id3v2_tag);
}
void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep)
{
FLAC_plugin__vorbiscomment_get(filename, tag, sep);
FLAC_plugin__canonical_tag_add_id3v2(filename, tag);
FLAC_plugin__canonical_tag_add_id3v1(filename, tag);
}

View File

@@ -1,55 +1,118 @@
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
#define FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
#include "id3v1.h"
typedef struct {
char *title;
char *composer;
char *performer;
char *album;
char *year_recorded;
char *year_performed;
char *track_number;
char *tracks_in_album;
char *genre;
char *comment;
} FLAC_Plugin__CanonicalTag;
FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new();
void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *);
void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *);
void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *);
/* For each null field in dest, move the corresponding field from src
* WATCHOUT: note that src is not-const, because fields are 'moved' from
* src to dest and the src field is set to null.
*/
void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, FLAC_Plugin__CanonicalTag *src);
void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v1_Tag *);
/* Returns a merged tag based on any Vorbis comments, id3v2 tag, and id3v1.
* In case of overlaps the preceding precedence applies.
*/
void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag);
#endif
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
#define FLAC__PLUGIN_COMMON__CANONICAL_TAG_H
#include "id3v1.h"
#include "id3v2.h"
/* TODO: splay tree? */
typedef struct tagFLAC__tag_entry FLAC__tag_entry;
struct tagFLAC__tag_entry
{
FLAC__tag_entry *next, *prev;
/* TODO: name in ascii? */
wchar_t *name;
wchar_t *value;
};
typedef struct {
FLAC__tag_entry *head, *tail;
unsigned count;
} FLAC_Plugin__CanonicalTag;
typedef FLAC__tag_entry *FLAC__tag_iterator;
FLAC_Plugin__CanonicalTag *FLAC_plugin__canonical_tag_new();
void FLAC_plugin__canonical_tag_init(FLAC_Plugin__CanonicalTag *);
void FLAC_plugin__canonical_tag_clear(FLAC_Plugin__CanonicalTag *);
void FLAC_plugin__canonical_tag_delete(FLAC_Plugin__CanonicalTag *);
/* note that multiple fields with the same name are allowed.
* set - adds field if it's not present yet, or replaces
* existing field if it's present.
*/
void FLAC_plugin__canonical_set(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value);
void FLAC_plugin__canonical_set_ansi(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const char *value);
/* set_new - only adds field if it's not present yet. */
void FLAC_plugin__canonical_set_new(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value);
/* add - adds field if it's not present yet, or merges value with existing
* field, if it's present. (sep - separator string to use when merging;
* if sep==NULL no merging occurs - always adds new field)
*/
void FLAC_plugin__canonical_add(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name, const wchar_t *value, const wchar_t *sep);
void FLAC_plugin__canonical_add_utf8(FLAC_Plugin__CanonicalTag *tag, const char *name, const char *value, unsigned namelen, unsigned vallen, const char *sep); /* 'namelen'/'vallen' may be (unsigned)(-1) if 'name'/'value' is NUL-terminated */
/* gets value of the first field with the given name (NULL if field not found) */
const wchar_t *FLAC_plugin__canonical_get(const FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);
/* removes first field with the given name.
* (returns 'true' if deleted, 'false' if not found)
*/
FLAC__bool FLAC_plugin__canonical_remove(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);
/* removes all fields with the given name. */
void FLAC_plugin__canonical_remove_all(FLAC_Plugin__CanonicalTag *tag, const wchar_t *name);
/* enumeration */
static __inline unsigned FLAC_plugin__canonical_get_count(FLAC_Plugin__CanonicalTag *tag)
{
return tag->count;
}
static __inline FLAC__tag_iterator FLAC_plugin__canonical_first(FLAC_Plugin__CanonicalTag *tag)
{
return tag->head;
}
static __inline FLAC__tag_iterator FLAC_plugin__canonical_next(FLAC__tag_iterator it)
{
return it->next;
}
static __inline wchar_t *FLAC_plugin__canonical_get_name(FLAC__tag_iterator it)
{
return it->name;
}
static __inline wchar_t *FLAC_plugin__canonical_get_value(FLAC__tag_iterator it)
{
return it->value;
}
/* returns a new string containing the current entry in UTF-8 in "NAME=VALUE" form */
char *FLAC_plugin__canonical_get_formatted(FLAC__tag_iterator it);
void FLAC_plugin__canonical_tag_merge(FLAC_Plugin__CanonicalTag *dest, const FLAC_Plugin__CanonicalTag *src);
void FLAC_plugin__canonical_tag_convert_from_id3v1(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v1_Tag *);
void FLAC_plugin__canonical_tag_convert_from_id3v2(FLAC_Plugin__CanonicalTag *, const FLAC_Plugin__Id3v2_Tag *);
void FLAC_plugin__canonical_tag_add_id3v1(const char *filename, FLAC_Plugin__CanonicalTag *tag);
void FLAC_plugin__canonical_tag_add_id3v2(const char *filename, FLAC_Plugin__CanonicalTag *tag);
/* Returns a merged tag based on any Vorbis comments, id3v2 tag, and id3v1.
* In case of overlaps the preceding precedence applies.
*
* sep - separator to use when merging fields with same name (in VorbisComment).
* should be in UTF-8. if sep==NULL, no merging occurs, so multiple fields
* with the same name can exist.
*/
void FLAC_plugin__canonical_tag_get_combined(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep);
/* helpers */
wchar_t *FLAC_plugin__convert_ansi_to_wide(const char *src);
wchar_t *FLAC_plugin__convert_utf8_to_ucs2(const char *src, unsigned length); /* 'length' may be (unsigned)(-1) if 'src' is NUL-terminated */
char *FLAC_plugin__convert_ucs2_to_utf8(const wchar_t *src);
#endif

View File

@@ -1,229 +1,214 @@
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include "FLAC/assert.h"
#include "id3v1.h"
#include "locale_hack.h"
/*
* Do not sort genres!!
* Last Update: 2000/04/30
*/
static const char * const FLAC_plugin__id3v1_tag_genre_table[] =
{
"Blues", /* 0 */
"Classic Rock",
"Country",
"Dance",
"Disco",
"Funk", /* 5 */
"Grunge",
"Hip-Hop",
"Jazz",
"Metal",
"New Age", /* 10 */
"Oldies",
"Other",
"Pop",
"R&B",
"Rap", /* 15 */
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative", /* 20 */
"Ska",
"Death Metal",
"Pranks",
"Soundtrack",
"Euro-Techno", /* 25 */
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz+Funk",
"Fusion", /* 30 */
"Trance",
"Classical",
"Instrumental",
"Acid",
"House", /* 35 */
"Game",
"Sound Clip",
"Gospel",
"Noise",
"Altern Rock", /* 40 */
"Bass",
"Soul",
"Punk",
"Space",
"Meditative", /* 45 */
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave", /* 50 */
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream", /* 55 */
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 40", /* 60 */
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native American",
"Cabaret", /* 65 */
"New Wave",
"Psychadelic",
"Rave",
"Showtunes",
"Trailer", /* 70 */
"Lo-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka", /* 75 */
"Retro",
"Musical",
"Rock & Roll",
"Hard Rock",
"Folk", /* 80 */
"Folk/Rock",
"National Folk",
"Fast Fusion",
"Swing",
"Bebob", /* 85 */
"Latin",
"Revival",
"Celtic",
"Bluegrass",
"Avantgarde", /* 90 */
"Gothic Rock",
"Progressive Rock",
"Psychedelic Rock",
"Symphonic Rock",
"Slow Rock", /* 95 */
"Big Band",
"Chorus",
"Easy Listening",
"Acoustic",
"Humour", /* 100 */
"Speech",
"Chanson",
"Opera",
"Chamber Music",
"Sonata", /* 105 */
"Symphony",
"Booty Bass",
"Primus",
"Porn Groove",
"Satire", /* 110 */
"Slow Jam",
"Club",
"Tango",
"Samba",
"Folklore", /* 115 */
"Ballad",
"Power Ballad",
"Rhythmic Soul",
"Freestyle",
"Duet", /* 120 */
"Punk Rock",
"Drum Solo",
"A Capella",
"Euro-House",
"Dance Hall", /* 125 */
"Goa",
"Drum & Bass",
"Club-House",
"Hardcore",
"Terror", /* 130 */
"Indie",
"BritPop",
"Negerpunk",
"Polsk Punk",
"Beat", /* 135 */
"Christian Gangsta Rap",
"Heavy Metal",
"Black Metal",
"Crossover",
"Contemporary Christian",/* 140 */
"Christian Rock",
"Merengue",
"Salsa",
"Thrash Metal",
"Anime", /* 145 */
"JPop",
"Synthpop"
};
FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag)
{
char raw[128];
FILE *f;
FLAC__ASSERT(0 != filename);
FLAC__ASSERT(0 != tag);
memset(tag, 0, sizeof(FLAC_Plugin__Id3v1_Tag));
if(0 == (f = fopen(filename, "rb")))
return false;
if(-1 == fseek(f, -128, SEEK_END)) {
fclose(f);
return false;
}
if(fread(raw, 1, 128, f) < 128) {
fclose(f);
return false;
}
fclose(f);
if(strncmp(raw, "TAG", 3))
return false;
else {
memcpy(tag->tag, raw, 3);
memcpy(tag->title, raw+3, 30);
memcpy(tag->artist, raw+33, 30);
memcpy(tag->album, raw+63, 30);
memcpy(tag->year, raw+93, 4);
memcpy(tag->comment.v1_0.comment, raw+97, 30);
tag->genre = raw[127];
return true;
}
}
const char *FLAC_plugin__id3v1_tag_get_genre_as_string(unsigned char genre_code)
{
if (genre_code < FLAC_plugin__id3v1_tag_genre_table_max())
return gettext(FLAC_plugin__id3v1_tag_genre_table[genre_code]);
return "";
}
unsigned FLAC_plugin__id3v1_tag_genre_table_max()
{
return sizeof(FLAC_plugin__id3v1_tag_genre_table) / sizeof(FLAC_plugin__id3v1_tag_genre_table[0]) - 1;
}
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include "FLAC/assert.h"
#include "id3v1.h"
#include "locale_hack.h"
/*
* Do not sort genres!!
* Last Update: 2000/04/30
*/
static const char * const FLAC_plugin__id3v1_tag_genre_table[] =
{
"Blues", /* 0 */
"Classic Rock",
"Country",
"Dance",
"Disco",
"Funk", /* 5 */
"Grunge",
"Hip-Hop",
"Jazz",
"Metal",
"New Age", /* 10 */
"Oldies",
"Other",
"Pop",
"R&B",
"Rap", /* 15 */
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative", /* 20 */
"Ska",
"Death Metal",
"Pranks",
"Soundtrack",
"Euro-Techno", /* 25 */
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz+Funk",
"Fusion", /* 30 */
"Trance",
"Classical",
"Instrumental",
"Acid",
"House", /* 35 */
"Game",
"Sound Clip",
"Gospel",
"Noise",
"Altern Rock", /* 40 */
"Bass",
"Soul",
"Punk",
"Space",
"Meditative", /* 45 */
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave", /* 50 */
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream", /* 55 */
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 40", /* 60 */
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native American",
"Cabaret", /* 65 */
"New Wave",
"Psychadelic",
"Rave",
"Showtunes",
"Trailer", /* 70 */
"Lo-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka", /* 75 */
"Retro",
"Musical",
"Rock & Roll",
"Hard Rock",
"Folk", /* 80 */
"Folk/Rock",
"National Folk",
"Fast Fusion",
"Swing",
"Bebob", /* 85 */
"Latin",
"Revival",
"Celtic",
"Bluegrass",
"Avantgarde", /* 90 */
"Gothic Rock",
"Progressive Rock",
"Psychedelic Rock",
"Symphonic Rock",
"Slow Rock", /* 95 */
"Big Band",
"Chorus",
"Easy Listening",
"Acoustic",
"Humour", /* 100 */
"Speech",
"Chanson",
"Opera",
"Chamber Music",
"Sonata", /* 105 */
"Symphony",
"Booty Bass",
"Primus",
"Porn Groove",
"Satire", /* 110 */
"Slow Jam",
"Club",
"Tango",
"Samba",
"Folklore", /* 115 */
"Ballad",
"Power Ballad",
"Rhythmic Soul",
"Freestyle",
"Duet", /* 120 */
"Punk Rock",
"Drum Solo",
"A Capella",
"Euro-House",
"Dance Hall", /* 125 */
"Goa",
"Drum & Bass",
"Club-House",
"Hardcore",
"Terror", /* 130 */
"Indie",
"BritPop",
"Negerpunk",
"Polsk Punk",
"Beat", /* 135 */
"Christian Gangsta Rap",
"Heavy Metal",
"Black Metal",
"Crossover",
"Contemporary Christian",/* 140 */
"Christian Rock",
"Merengue",
"Salsa",
"Thrash Metal",
"Anime", /* 145 */
"JPop",
"Synthpop"
};
FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag)
{
FILE *f;
int res;
FLAC__ASSERT(0 != filename);
FLAC__ASSERT(0 != tag);
memset(tag, 0, sizeof(FLAC_Plugin__Id3v1_Tag));
if(0 == (f = fopen(filename, "rb")))
return false;
if(-1 == fseek(f, -128, SEEK_END)) {
fclose(f);
return false;
}
res = fread(tag, 128, 1, f);
fclose(f);
return res==1 && !strncmp(tag->tag, "TAG", 3);
}
const char *FLAC_plugin__id3v1_tag_get_genre_as_string(unsigned char genre_code)
{
if (genre_code < (sizeof(FLAC_plugin__id3v1_tag_genre_table)/sizeof(FLAC_plugin__id3v1_tag_genre_table[0])))
return gettext(FLAC_plugin__id3v1_tag_genre_table[genre_code]);
return "Unknown";
}
unsigned FLAC_plugin__id3v1_tag_genre_table_max()
{
return sizeof(FLAC_plugin__id3v1_tag_genre_table) / sizeof(FLAC_plugin__id3v1_tag_genre_table[0]) - 1;
}

View File

@@ -23,25 +23,23 @@
#include "FLAC/ordinals.h"
#pragma pack(push, 1)
typedef struct {
char tag[3];
char title[30];
char artist[30];
char album[30];
char year[4];
union {
struct {
char comment[30];
} v1_0;
struct {
char comment[28];
char zero;
unsigned char track;
} v1_1;
} comment;
/* always use layout of id3 v1.1 */
char comment[28];
char zero;
unsigned char track;
unsigned char genre;
} FLAC_Plugin__Id3v1_Tag;
#pragma pack(pop)
FLAC__bool FLAC_plugin__id3v1_tag_get(const char *filename, FLAC_Plugin__Id3v1_Tag *tag);

View File

@@ -24,6 +24,11 @@
#include <config.h>
#endif
#include "FLAC/assert.h"
#include <stdlib.h> /* for free() */
#include <string.h> /* for memset() */
#ifdef FLAC__HAS_ID3LIB
#include <id3.h>
#include <stdio.h>
@@ -32,8 +37,6 @@
#include <ctype.h>
#include <unistd.h>
#include "FLAC/assert.h"
#include "id3v1.h" /* for genre stuff */
#include "locale_hack.h"
@@ -113,25 +116,25 @@ static size_t local__ID3Field_GetASCII_wrapper(const ID3Field *field, char *buff
* = 3.7.13 : first item num is 0 for ID3Field_GetASCII
* >= 3.8.0 : doesn't need item num for ID3Field_GetASCII
*/
# if (ID3LIB_MAJOR >= 3)
# if (ID3LIB_MAJOR >= 3)
/* (>= 3.x.x) */
# if (ID3LIB_MINOR <= 7)
# if (ID3LIB_MINOR <= 7)
/* (3.0.0 to 3.7.x) */
# if (ID3LIB_PATCH >= 13)
# if (ID3LIB_PATCH >= 13)
/* (>= 3.7.13) */
return ID3Field_GetASCII(field, buffer, maxChars, itemNum);
# else
# else
return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1);
# endif
# else
# endif
# else
/* (>= to 3.8.0) */
/*return ID3Field_GetASCII(field, buffer, maxChars); */
return ID3Field_GetASCIIItem(field, buffer, maxChars, itemNum);
# endif
# else
# endif
# else
/* Not tested (< 3.x.x) */
return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1);
# endif
# endif
}
@@ -161,7 +164,7 @@ static const char *local__genre_to_string(unsigned genre_code)
* Returns true on success, else false.
* If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
*/
static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__CanonicalTag *tag)
static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__Id3v2_Tag *tag)
{
FILE *file;
ID3Tag *id3_tag = 0; /* Tag defined by id3lib */
@@ -368,8 +371,22 @@ static FLAC__bool local__get_tag(const char *filename, FLAC_Plugin__CanonicalTag
}
#endif /* ifdef FLAC__HAS_ID3LIB */
FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__CanonicalTag *tag)
FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Id3v2_Tag *tag)
{
FLAC__ASSERT(0 != tag);
if(
0 != tag->title ||
0 != tag->composer ||
0 != tag->performer ||
0 != tag->album ||
0 != tag->year_recorded ||
0 != tag->year_performed ||
0 != tag->track_number ||
0 != tag->tracks_in_album ||
0 != tag->genre ||
0 != tag->comment
)
return false;
#ifdef FLAC__HAS_ID3LIB
return local__get_tag(filename, tag);
#else
@@ -377,3 +394,29 @@ FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Canonic
return false;
#endif
}
void FLAC_plugin__id3v2_tag_clear(FLAC_Plugin__Id3v2_Tag *tag)
{
FLAC__ASSERT(0 != tag);
if(0 != tag->title)
free(tag->title);
if(0 != tag->composer)
free(tag->composer);
if(0 != tag->performer)
free(tag->performer);
if(0 != tag->album)
free(tag->album);
if(0 != tag->year_recorded)
free(tag->year_recorded);
if(0 != tag->year_performed)
free(tag->year_performed);
if(0 != tag->track_number)
free(tag->track_number);
if(0 != tag->tracks_in_album)
free(tag->tracks_in_album);
if(0 != tag->genre)
free(tag->genre);
if(0 != tag->comment)
free(tag->comment);
memset(tag, 0, sizeof(*tag));
}

View File

@@ -19,8 +19,39 @@
#ifndef FLAC__PLUGIN_COMMON__ID3V2_H
#define FLAC__PLUGIN_COMMON__ID3V2_H
#include "canonical_tag.h"
#include "FLAC/ordinals.h"
FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__CanonicalTag *tag);
/*
* This is a simple structure that holds pointers to field values (in ASCII)
* for fields we care about.
*/
typedef struct {
char *title;
char *composer;
char *performer;
char *album;
char *year_recorded;
char *year_performed;
char *track_number;
char *tracks_in_album;
char *genre;
char *comment;
} FLAC_Plugin__Id3v2_Tag;
/* Fills up an existing FLAC_Plugin__Id3v2_Tag. All pointers must be NULL on
* entry or the function will return false. For any field for which there is
* no corresponding ID3 frame, it's pointer will be NULL.
*
* If loading fails, all pointers will be cleared and the function will return
* false.
*
* If the function returns true, be sure to call FLAC_plugin__id3v2_tag_clear()
* when you are done with 'tag'.
*/
FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__Id3v2_Tag *tag);
/* free()s any non-NULL pointers in 'tag'. Does NOT free(tag).
*/
void FLAC_plugin__id3v2_tag_clear(FLAC_Plugin__Id3v2_Tag *tag);
#endif

View File

@@ -41,7 +41,7 @@ RSC=rc.exe
# PROP Intermediate_Dir "Release_static"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MD /W3 /WX /GX /Ox /Og /Oi /Os /Op /I ".\include" /I "..\..\include" /D "FLAC__NO_DLL" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe

View File

@@ -1,180 +1,110 @@
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "vorbiscomment.h"
#include "FLAC/metadata.h"
static int local__vcentry_matches(const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
{
#if defined _MSC_VER || defined __MINGW32__
#define FLAC__STRNCASECMP strnicmp
#else
#define FLAC__STRNCASECMP strncasecmp
#endif
const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
const unsigned field_name_length = strlen(field_name);
return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry->entry, field_name_length));
}
static void local__vcentry_parse_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, char **dest)
{
const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
if(0 == eq)
return;
else {
const unsigned value_length = entry->length - (unsigned)((++eq) - entry->entry);
*dest = malloc(value_length + 1);
if(0 != *dest) {
memcpy(*dest, eq, value_length);
(*dest)[value_length] = '\0';
}
}
}
static void local__vc_change_field(FLAC__StreamMetadata *block, const char *name, const char *value)
{
int i, l;
char *s;
/* find last */
for (l = -1; (i = FLAC__metadata_object_vorbiscomment_find_entry_from(block, l + 1, name)) != -1; l = i)
;
if(!value || !strlen(value)) {
if (l != -1)
FLAC__metadata_object_vorbiscomment_delete_comment(block, l);
return;
}
s = malloc(strlen(name) + strlen(value) + 2);
if(s) {
FLAC__StreamMetadata_VorbisComment_Entry entry;
sprintf(s, "%s=%s", name, value);
entry.length = strlen(s);
entry.entry = (FLAC__byte *)s;
if(l == -1)
FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true);
else
FLAC__metadata_object_vorbiscomment_set_comment(block, l, entry, /*copy=*/true);
free(s);
}
}
void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
if(0 != iterator) {
if(FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) {
FLAC__bool got_vorbis_comments = false;
do {
if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator);
if(0 != block) {
unsigned i;
const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;
for(i = 0; i < vc->num_comments; i++) {
if(local__vcentry_matches("artist", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->composer);
else if(local__vcentry_matches("performer", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->performer);
else if(local__vcentry_matches("album", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->album);
else if(local__vcentry_matches("title", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->title);
else if(local__vcentry_matches("tracknumber", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->track_number);
else if(local__vcentry_matches("genre", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->genre);
else if(local__vcentry_matches("description", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->comment);
else if(local__vcentry_matches("date", &vc->comments[i]))
local__vcentry_parse_value(&vc->comments[i], &tag->year_recorded);
}
FLAC__metadata_object_delete(block);
got_vorbis_comments = true;
}
}
} while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
}
FLAC__metadata_simple_iterator_delete(iterator);
}
}
FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC__bool got_vorbis_comments = false;
FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
FLAC__StreamMetadata *block;
if(!iterator || !FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/false, /*preserve_file_stats=*/true))
return false;
do {
if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT)
got_vorbis_comments = true;
} while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
if(!got_vorbis_comments) {
/* create a new block */
block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(!block) {
FLAC__metadata_simple_iterator_delete(iterator);
return false;
}
}
else
block = FLAC__metadata_simple_iterator_get_block(iterator);
local__vc_change_field(block, "ARTIST", tag->composer);
local__vc_change_field(block, "PERFORMER", tag->performer);
local__vc_change_field(block, "ALBUM", tag->album);
local__vc_change_field(block, "TITLE", tag->title);
local__vc_change_field(block, "TRACKNUMBER", tag->track_number);
local__vc_change_field(block, "GENRE", tag->genre);
local__vc_change_field(block, "DESCRIPTION", tag->comment);
local__vc_change_field(block, "DATE", tag->year_recorded);
if(!got_vorbis_comments) {
if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true)) {
FLAC__metadata_object_delete(block);
FLAC__metadata_simple_iterator_delete(iterator);
return false;
}
}
else {
if(!FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true)) {
FLAC__metadata_object_delete(block);
FLAC__metadata_simple_iterator_delete(iterator);
return false;
}
}
FLAC__metadata_object_delete(block);
FLAC__metadata_simple_iterator_delete(iterator);
return true;
}
/* plugin_common - Routines common to several plugins
* Copyright (C) 2002,2003,2004 Josh Coalson
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "vorbiscomment.h"
#include "FLAC/metadata.h"
static void local__add_vcentry(FLAC_Plugin__CanonicalTag *tag, FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *sep)
{
FLAC__byte *value = memchr(entry->entry, '=', entry->length);
int len;
if (!value) return;
len = value - entry->entry;
value++;
FLAC_plugin__canonical_add_utf8(tag, entry->entry, value, len, entry->length-len-1, sep);
}
void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep)
{
FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
if(0 != iterator) {
if(FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) {
FLAC__bool got_vorbis_comments = false;
do {
if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator);
if(0 != block) {
unsigned i;
const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;
for(i = 0; i < vc->num_comments; i++)
local__add_vcentry(tag, vc->comments+i, sep);
FLAC__metadata_object_delete(block);
got_vorbis_comments = true;
}
}
} while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
}
FLAC__metadata_simple_iterator_delete(iterator);
}
}
FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag)
{
FLAC__bool got_vorbis_comments = false, result;
FLAC__Metadata_SimpleIterator *iterator = FLAC__metadata_simple_iterator_new();
FLAC__StreamMetadata *block;
FLAC__tag_iterator it;
unsigned position = 0;
if (!iterator || !FLAC__metadata_simple_iterator_init(iterator, filename, /*read_only=*/false, /*preserve_file_stats=*/true))
return false;
do {
if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT)
got_vorbis_comments = true;
} while (!got_vorbis_comments && FLAC__metadata_simple_iterator_next(iterator));
if(!got_vorbis_comments) {
/* create a new block */
block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(!block) {
FLAC__metadata_simple_iterator_delete(iterator);
return false;
}
}
else
block = FLAC__metadata_simple_iterator_get_block(iterator);
FLAC__metadata_object_vorbiscomment_resize_comments(block, FLAC_plugin__canonical_get_count(tag));
for (it=FLAC_plugin__canonical_first(tag); it; it=FLAC_plugin__canonical_next(it))
{
FLAC__StreamMetadata_VorbisComment_Entry entry;
/* replace entry */
entry.entry = FLAC_plugin__canonical_get_formatted(it);
entry.length = strlen(entry.entry);
FLAC__metadata_object_vorbiscomment_set_comment(block, position++, entry, /*copy=*/false);
}
if (!got_vorbis_comments)
result = FLAC__metadata_simple_iterator_insert_block_after(iterator, block, /*use_padding=*/true);
else
result = FLAC__metadata_simple_iterator_set_block(iterator, block, /*use_padding=*/true);
FLAC__metadata_object_delete(block);
FLAC__metadata_simple_iterator_delete(iterator);
return result;
}

View File

@@ -22,7 +22,7 @@
#include "canonical_tag.h"
#include "FLAC/ordinals.h"
void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag);
void FLAC_plugin__vorbiscomment_get(const char *filename, FLAC_Plugin__CanonicalTag *tag, const char *sep);
FLAC__bool FLAC_plugin__vorbiscomment_set(const char *filename, FLAC_Plugin__CanonicalTag *tag);
#endif