diff --git a/src/plugin_common/id3v2.c b/src/plugin_common/id3v2.c index 901b94a3..c90f159c 100644 --- a/src/plugin_common/id3v2.c +++ b/src/plugin_common/id3v2.c @@ -20,385 +20,49 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "id3v2.h" + +#ifdef FLAC__HAS_ID3LIB #include -#include #include #include #include #include #include -#include "configure.h" -#include "genres.h" -#include "charset.h" -#include "mylocale.h" -#include "id3_tag.h" +#include "FLAC/assert.h" + +#include "id3v1.h" /* for genre stuff */ +#include "locale_hack.h" -/**************** - * Declarations * - ****************/ #define ID3V2_MAX_STRING_LEN 4096 #define NUMBER_TRACK_FORMATED 1 -/************** - * Prototypes * - **************/ -static size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename); -static size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum); -static gchar *Id3tag_Genre_To_String (unsigned char genre_code); -static void Strip_String (gchar *string); - -@@@@ - -/************* - * Functions * - *************/ -/* - * Read id3v1.x / id3v2 tag and load data into the File_Tag structure using id3lib functions. - * Returns TRUE on success, else FALSE. - * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL +/* local__strip() based on glib's g_strchomp() and g_strchug(): + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * (LGPL 2 follows) */ -gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag) +static void local__strip(char *string) { - FILE *file; - ID3Tag *id3_tag = NULL; /* Tag defined by the id3lib */ - gchar *string, *string1, *string2; - gboolean USE_CHARACTER_SET_TRANSLATION; + char *s; - USE_CHARACTER_SET_TRANSLATION = flac_cfg.convert_char_set; + if(0 == string) + return; - if (!filename || !FileTag) - return FALSE; + for(s = string; *s && isspace(*s); s++) + ; - if ( (file=fopen(filename,"r"))==NULL ) - { - g_print(_("ERROR while opening file: '%s' (%s).\n\a"),filename,g_strerror(errno)); - return FALSE; - } - fclose(file); // We close it cause id3lib opens/closes file itself + memmove(string, s, strlen((char*)s) + 1); + if(!*string) + return; - /* Get data from tag */ - if ( (id3_tag = ID3Tag_New()) ) - { - ID3Frame *id3_frame; - ID3Field *id3_field; - luint frm_size; - luint num_chars; - guint field_num = 0; // First field - - /* Link the file to the tag */ - frm_size = ID3Tag_Link_1(id3_tag,filename); - - string = g_malloc(ID3V2_MAX_STRING_LEN+1); - - /********* - * Title * - *********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - // Note: if 'num_chars' is equal to 0, then the field is empty or corrupted! - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - Strip_String(string1); - FileTag->title = g_strdup(string1); - g_free(string1); - }else - { - Strip_String(string); - FileTag->title = g_strdup(string); - } - } - } - } - - - /********** - * Artist * - **********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - Strip_String(string1); - FileTag->artist = g_strdup(string1); - g_free(string1); - }else - { - Strip_String(string); - FileTag->artist = g_strdup(string); - } - } - } - } - - - /********* - * Album * - *********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - Strip_String(string1); - FileTag->album = g_strdup(string1); - g_free(string1); - }else - { - Strip_String(string); - FileTag->album = g_strdup(string); - } - } - } - } - - - /******** - * Year * - ********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - gchar *tmp_str; - - Strip_String(string); - - /* Fix for id3lib 3.7.x: if the id3v1.x tag was filled with spaces - * instead of zeroes, then the year field contains garbages! */ - tmp_str = string; - while (isdigit(*tmp_str)) tmp_str++; - *tmp_str = 0; - /* End of fix for id3lib 3.7.x */ - - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - Strip_String(string1); - FileTag->year = g_strdup(string1); - g_free(string1); - }else - { - Strip_String(string); - FileTag->year = g_strdup(string); - } - } - } - } - - - /************************* - * Track and Total Track * - *************************/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - - Strip_String(string); - - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - string2 = strchr(string1,'/'); - if (NUMBER_TRACK_FORMATED) - { - if (string2) - { - FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ... - *string2 = '\0'; - } - FileTag->track = g_strdup_printf("%.2d",atoi(string1)); // Just to have numbers like this : '01', '05', '12', ... - }else - { - if (string2) - { - FileTag->track_total = g_strdup(string2+1); - *string2 = '\0'; - } - FileTag->track = g_strdup(string1); - } - g_free(string1); - }else - { - string2 = strchr(string,'/'); - if (NUMBER_TRACK_FORMATED) - { - if (string2) - { - FileTag->track_total = g_strdup_printf("%.2d",atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ... - *string2 = '\0'; - } - FileTag->track = g_strdup_printf("%.2d",atoi(string)); // Just to have numbers like this : '01', '05', '12', ... - }else - { - if (string2) - { - FileTag->track_total = g_strdup(string2+1); - *string2 = '\0'; - } - FileTag->track = g_strdup(string); - } - } - } - } - } - - - /********* - * Genre * - *********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - /* - * We manipulate only the name of the genre - */ - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - gchar *tmp; - - Strip_String(string); - - if ( (string[0]=='(') && (tmp=strchr(string,')')) && (strlen((tmp+1))>0) ) - { - - /* Convert a genre written as '(3)Dance' into 'Dance' */ - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(tmp+1); - FileTag->genre = g_strdup(string1); - g_free(string1); - }else - { - FileTag->genre = g_strdup(tmp+1); - } - - }else if ( (string[0]=='(') && (tmp=strchr(string,')')) ) - { - - /* Convert a genre written as '(3)' into 'Dance' */ - *tmp = 0; - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(Id3tag_Genre_To_String(atoi(string+1))); - FileTag->genre = g_strdup(string1); - g_free(string1); - }else - { - FileTag->genre = g_strdup(Id3tag_Genre_To_String(atoi(string+1))); - } - - }else - { - - /* Genre is already written as 'Dance' */ - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - FileTag->genre = g_strdup(string1); - g_free(string1); - }else - { - FileTag->genre = g_strdup(string); - } - - } - } - } - } - - - /*********** - * Comment * - ***********/ - if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) ) - { - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_TEXT)) ) - { - if ( (num_chars=ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,field_num)) > 0 - && string != NULL ) - { - if (USE_CHARACTER_SET_TRANSLATION) - { - string1 = convert_from_file_to_user(string); - Strip_String(string1); - FileTag->comment = g_strdup(string1); - g_free(string1); - }else - { - Strip_String(string); - FileTag->comment = g_strdup(string); - } - } - } - /*if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_DESCRIPTION)) ) - { - gchar *comment1 = g_malloc0(MAX_STRING_LEN+1); - num_chars = ID3Field_GetASCII(id3_field,comment1,MAX_STRING_LEN,Item_Num); - g_free(comment1); - } - if ( (id3_field = ID3Frame_GetField(id3_frame,ID3FN_LANGUAGE)) ) - { - gchar *comment2 = g_malloc0(MAX_STRING_LEN+1); - num_chars = ID3Field_GetASCII(id3_field,comment2,MAX_STRING_LEN,Item_Num); - g_free(comment2); - }*/ - } - g_free(string); - - /* Free allocated data */ - ID3Tag_Delete(id3_tag); - } - - return TRUE; + for(s = string + strlen (string) - 1; s >= string && isspace(*s); s--) + *s = '\0'; } -/* - * Returns the name of a genre code if found - * Three states for genre code : - * - defined (0 to GENRE_MAX) - * - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1) - * - invalid (>ID3_INVALID_GENRE) - */ -static gchar *Id3tag_Genre_To_String (unsigned char genre_code) -{ - if (genre_code>=ID3_INVALID_GENRE) /* empty */ - return ""; - else if (genre_code>GENRE_MAX) /* unknown tag */ - return "Unknown"; - else /* known tag */ - return id3_genres[genre_code]; -} - - /* * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags @@ -407,23 +71,21 @@ static gchar *Id3tag_Genre_To_String (unsigned char genre_code) * fall back to the ID3v1 tags. * (Written by Holger Schemel). */ -static size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename) +static size_t local__ID3Tag_Link_wrapper(ID3Tag *id3tag, const char *filename) { size_t offset; # if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) ) /* First, try to get the ID3v2 tags */ - offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2); - if (offset == 0) - { + offset = ID3Tag_LinkWithFlags(id3tag, filename, ID3TT_ID3V2); + if (offset == 0) { /* No ID3v2 tags available => try to get the ID3v1 tags */ - offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1); + offset = ID3Tag_LinkWithFlags(id3tag, filename, ID3TT_ID3V1); } # else /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */ - offset = ID3Tag_Link(id3tag,filename); + offset = ID3Tag_Link(id3tag, filename); # endif - //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_basename(filename)); return offset; } @@ -431,7 +93,8 @@ static size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename) /* * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it. */ -static size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, index_t itemNum) +/* [JEC] old id3lib versions may have used index_t for itemNum but size_t is what it wants now and seems safe enough. */ +static size_t local__ID3Field_GetASCII_wrapper(const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum) { /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION @@ -441,33 +104,266 @@ static size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t ma * = 3.7.13 : first item num is 0 for ID3Field_GetASCII * >= 3.8.0 : doesn't need item num for ID3Field_GetASCII */ - //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH); # if (ID3LIB_MAJOR >= 3) - // (>= 3.x.x) + /* (>= 3.x.x) */ # if (ID3LIB_MINOR <= 7) - // (3.0.0 to 3.7.x) + /* (3.0.0 to 3.7.x) */ # if (ID3LIB_PATCH >= 13) - // (>= 3.7.13) - return ID3Field_GetASCII(field,buffer,maxChars,itemNum); + /* (>= 3.7.13) */ + return ID3Field_GetASCII(field, buffer, maxChars, itemNum); # else - return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1); + return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1); # endif # else - // (>= to 3.8.0) - //return ID3Field_GetASCII(field,buffer,maxChars); - return ID3Field_GetASCIIItem(field,buffer,maxChars,itemNum); + /* (>= to 3.8.0) */ + /*return ID3Field_GetASCII(field, buffer, maxChars); */ + return ID3Field_GetASCIIItem(field, buffer, maxChars, itemNum); # endif # else - // Not tested (< 3.x.x) - return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1); + /* Not tested (< 3.x.x) */ + return ID3Field_GetASCII(field, buffer, maxChars, itemNum+1); # endif } + /* - * Delete spaces at the end and the beginning of the string + * Returns the name of a genre code if found + * Three states for genre code : + * - defined (0 to GENRE_MAX) + * - undefined/unknown (GENRE_MAX+1 to ID3_INVALID_GENRE-1) + * - invalid (>ID3_INVALID_GENRE) */ -static void Strip_String (gchar *string) +static const char *local__genre_to_string(unsigned genre_code) { - if (!string) return; - string = g_strstrip(string); + if(genre_code >= FLAC_PLUGIN__ID3V1_TAG_INVALID_GENRE) + return ""; + else { + const char *s = FLAC_plugin__id3v1_tag_get_genre_as_string((unsigned)genre_code); + if(s[0] == 0) + return "Unknown"; + else + return s; + } +} + + +/* + * Read id3v1.x / id3v2 tag and load data into the File_Tag structure using id3lib functions. + * 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) +{ + FILE *file; + ID3Tag *id3_tag = 0; /* Tag defined by id3lib */ + char *string, *string1; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tag); + + if(0 == (file = fopen(filename, "r"))) { +#ifdef DEBUG + fprintf(stderr, _("ERROR while opening file: '%s' (%s).\n\a"), filename, strerror(errno)); +#endif + return false; + } + fclose(file); /* We close it cause id3lib opens/closes file itself */ + + + /* Get data from tag */ + if(0 != (id3_tag = ID3Tag_New())) { + ID3Frame *id3_frame; + ID3Field *id3_field; + luint frm_size; + luint num_chars; + size_t field_num = 0; /* First field */ + + /* Link the file to the tag */ + frm_size = local__ID3Tag_Link_wrapper(id3_tag, filename); + + string = malloc(ID3V2_MAX_STRING_LEN+1); + + /********* + * Title * + *********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_TITLE))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + /* Note: if 'num_chars' is equal to 0, then the field is empty or corrupted! */ + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + tag->title = strdup(string); + } + } + } + + + /************ + * Composer * + ************/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_COMPOSER))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + tag->composer = strdup(string); + } + } + } + + + /********** + * Artist * + **********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_LEADARTIST))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + tag->performer = strdup(string); + } + } + } + + + /********* + * Album * + *********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_ALBUM))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + tag->album = strdup(string); + } + } + } + + + /******** + * Year * + ********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_YEAR))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + char *tmp_str; + + local__strip(string); + + /* Fix for id3lib 3.7.x: if the id3v1.x tag was filled with spaces + * instead of zeroes, then the year field contains garbages! */ + tmp_str = string; + while (isdigit(*tmp_str)) tmp_str++; + *tmp_str = 0; + /* End of fix for id3lib 3.7.x */ + + local__strip(string); + tag->year_recorded = strdup(string); + tag->year_performed = strdup(string); + } + } + } + + + /************************* + * Track and Total Track * + *************************/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_TRACKNUM))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + + string1 = strchr(string, '/'); + if (NUMBER_TRACK_FORMATED) { + if (string1) { + /* Just to have numbers like this : '01', '05', '12', ... */ + tag->tracks_in_album = malloc(64); + sprintf(tag->tracks_in_album, "%.2d", atoi(string1+1)); + *string1 = '\0'; + } + /* Just to have numbers like this : '01', '05', '12', ... */ + tag->track_number = malloc(64); + sprintf(tag->track_number, "%.2d", atoi(string)); + } + else { + if (string1) { + tag->tracks_in_album = strdup(string1+1); + *string1 = '\0'; + } + tag->track_number = strdup(string); + } + } + } + } + + + /********* + * Genre * + *********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_CONTENTTYPE))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + /* + * We manipulate only the name of the genre + */ + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + char *tmp; + + local__strip(string); + + if((string[0]=='(') && (tmp=strchr(string, ')')) && (strlen((tmp+1))>0)) { + /* Convert a genre written as '(3)Dance' into 'Dance' */ + tag->genre = strdup(tmp+1); + + } + else if((string[0]=='(') && (tmp=strchr(string, ')'))) { + /* Convert a genre written as '(3)' into 'Dance' */ + *tmp = 0; + tag->genre = strdup(local__genre_to_string((unsigned)atoi(string+1))); + + } + else { + /* Genre is already written as 'Dance' */ + tag->genre = strdup(string); + } + } + } + } + + + /*********** + * Comment * + ***********/ + if(0 != (id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_COMMENT))) { + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TEXT))) { + if((num_chars = local__ID3Field_GetASCII_wrapper(id3_field, string, ID3V2_MAX_STRING_LEN, field_num)) > 0 && string != NULL) { + local__strip(string); + tag->comment = strdup(string); + } + } +#if 0 + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_DESCRIPTION))) { + char *comment1 = calloc(MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII(id3_field, comment1, MAX_STRING_LEN, Item_Num); + free(comment1); + } + if(0 != (id3_field = ID3Frame_GetField(id3_frame, ID3FN_LANGUAGE))) { + char *comment2 = calloc(MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII(id3_field, comment2, MAX_STRING_LEN, Item_Num); + free(comment2); + } +#endif + } + free(string); + + /* Free allocated data */ + ID3Tag_Delete(id3_tag); + } + + return true; +} +#endif /* ifdef FLAC__HAS_ID3LIB */ + +FLAC__bool FLAC_plugin__id3v2_tag_get(const char *filename, FLAC_Plugin__CanonicalTag *tag) +{ +#ifdef FLAC__HAS_ID3LIB + return local__get_tag(filename, tag); +#else + return false; +#endif }