diff options
Diffstat (limited to 'src/id3_tag.c')
-rwxr-xr-x | src/id3_tag.c | 1362 |
1 files changed, 1362 insertions, 0 deletions
diff --git a/src/id3_tag.c b/src/id3_tag.c new file mode 100755 index 0000000..9092fee --- /dev/null +++ b/src/id3_tag.c @@ -0,0 +1,1362 @@ +/* id3tag.c - 2001/02/16 */ +/* + * EasyTAG - Tag editor for MP3 and Ogg Vorbis files + * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com> + * + * 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 <config.h> + +#include <gtk/gtk.h> +#include <glib/gi18n-lib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> + +#include "id3_tag.h" +#include "ape_tag.h" +#include "picture.h" +#include "easytag.h" +#include "browser.h" +#include "genres.h" +#include "setting.h" +#include "log.h" +#include "misc.h" +#include "et_core.h" +#include "msgbox.h" +#include "charset.h" + +#ifdef ENABLE_MP3 + +#ifdef ENABLE_ID3LIB +#include <id3.h> +#include "id3lib/id3_bugfix.h" +#endif + +/**************** + * Declarations * + ****************/ +#define ID3V2_MAX_STRING_LEN 4096 +#define MULTIFIELD_SEPARATOR " - " + + +#ifdef ENABLE_ID3LIB + +/************** + * Prototypes * + **************/ +gchar *Id3tag_Get_Error_Message (ID3_Err error); +void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag); +gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string, const gchar *from_codeset, const gchar *to_codeset); +gchar *Id3tag_Get_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid); +ID3_TextEnc Id3tag_Set_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid, gchar *string); + +ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename); +ID3_C_EXPORT size_t ID3Field_GetASCII_1 (const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum); +ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum); + +gboolean Id3tag_Check_If_File_Is_Corrupted (gchar *filename); + +gboolean Id3tag_Check_If_Id3lib_Is_Bugged (void); + + + +/************* + * Functions * + *************/ + +/* + * Write the ID3 tags to the file. Returns TRUE on success, else 0. + */ +gboolean Id3tag_Write_File_v23Tag (ET_File *ETFile) +{ + File_Tag *FileTag; + gchar *filename; + gchar *filename_utf8; + gchar *basename_utf8; + //gchar *temp; + FILE *file; + ID3Tag *id3_tag = NULL; + ID3_Err error_strip_id3v1 = ID3E_NoError; + ID3_Err error_strip_id3v2 = ID3E_NoError; + ID3_Err error_update_id3v1 = ID3E_NoError; + ID3_Err error_update_id3v2 = ID3E_NoError; + gint error = 0; + gint number_of_frames; + gboolean has_title = FALSE; + gboolean has_artist = FALSE; + gboolean has_album = FALSE; + gboolean has_disc_number = FALSE; + gboolean has_year = FALSE; + gboolean has_track = FALSE; + gboolean has_genre = FALSE; + gboolean has_comment = FALSE; + gboolean has_composer = FALSE; + gboolean has_orig_artist = FALSE; + gboolean has_copyright = FALSE; + gboolean has_url = FALSE; + gboolean has_encoded_by = FALSE; + gboolean has_picture = FALSE; + //gboolean has_song_len = FALSE; + static gboolean flag_first_check = TRUE; + static gboolean flag_id3lib_bugged = TRUE; + + ID3Frame *id3_frame; + ID3Field *id3_field; + //gchar *string; + gchar *string1; + Picture *pic; + + + // When writing the first MP3 file, we check if the version of id3lib of the + // system doesn't contain a bug when writting Unicode tags + if (flag_first_check + && (FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET) ) + { + flag_first_check = FALSE; + flag_id3lib_bugged = Id3tag_Check_If_Id3lib_Is_Bugged(); + } + + if (!ETFile || !ETFile->FileTag) + return FALSE; + + FileTag = (File_Tag *)ETFile->FileTag->data; + filename = ((File_Name *)ETFile->FileNameCur->data)->value; + filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8; + + /* Test to know if we can write into the file */ + if ( (file=fopen(filename,"r+"))==NULL ) + { + Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno)); + return FALSE; + } + fclose(file); + + /* This is a protection against a bug in id3lib that generate an infinite + * loop with corrupted MP3 files (files containing only zeroes) */ + if (Id3tag_Check_If_File_Is_Corrupted(filename)) + return FALSE; + + /* We get again the tag from the file to keep also unused data (by EasyTAG), then + * we replace the changed data */ + if ( (id3_tag = ID3Tag_New()) == NULL ) + return FALSE; + + basename_utf8 = g_path_get_basename(filename_utf8); + + ID3Tag_Link(id3_tag,filename); + + /* Set padding when tag was changed, for faster writing */ + ID3Tag_SetPadding(id3_tag,TRUE); + + + /********* + * Title * + *********/ + // To avoid problem with a corrupted field, we remove it before to create a new one. + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + + if (FileTag->title && g_utf8_strlen(FileTag->title, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_TITLE); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->title); + has_title = TRUE; + } + + + /********** + * Artist * + **********/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_LEADARTIST)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->artist && g_utf8_strlen(FileTag->artist, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_LEADARTIST); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->artist); + has_artist = TRUE; + } + + + /********* + * Album * + *********/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ALBUM)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->album && g_utf8_strlen(FileTag->album, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_ALBUM); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album); + has_album = TRUE; + } + + + /*************** + * Part of set * + ***************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PARTINSET)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->disc_number && g_utf8_strlen(FileTag->disc_number, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_PARTINSET); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->disc_number); + has_disc_number = TRUE; + } + + + /******** + * Year * + ********/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_YEAR)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->year && g_utf8_strlen(FileTag->year, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_YEAR); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->year); + has_year = TRUE; + } + + + /************************* + * Track and Total Track * + *************************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TRACKNUM)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->track && g_utf8_strlen(FileTag->track, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_TRACKNUM); + ID3Tag_AttachFrame(id3_tag,id3_frame); + + if ( FileTag->track_total && g_utf8_strlen(FileTag->track_total, -1) > 0) + string1 = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL); + else + string1 = g_strdup(FileTag->track); + + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string1); + g_free(string1); + has_track = TRUE; + } + + + /********* + * Genre * + ********* + * Genre is written like this : + * - "(<genre_id>)" -> "(3)" + * - "(<genre_id>)<refinement>" -> "(3)EuroDance" + */ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_CONTENTTYPE)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->genre && strlen(FileTag->genre)>0 ) + { + gchar *genre_string_tmp; + guchar genre_value; + + id3_frame = ID3Frame_NewID(ID3FID_CONTENTTYPE); + ID3Tag_AttachFrame(id3_tag,id3_frame); + + genre_value = Id3tag_String_To_Genre(FileTag->genre); + // If genre not defined don't write genre value between brackets! (priority problem noted with some tools) + if (genre_value == ID3_INVALID_GENRE) + genre_string_tmp = g_strdup_printf("%s",FileTag->genre); + else + genre_string_tmp = g_strdup_printf("(%d)",genre_value); + + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, genre_string_tmp); + g_free(genre_string_tmp); + has_genre = TRUE; + } + + + /*********** + * Comment * + ***********/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMMENT)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->comment && g_utf8_strlen(FileTag->comment, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_COMMENT); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->comment); + // These 2 following fields allow synchronisation between id3v2 and id3v1 tags with id3lib + // Disabled as when using unicode, the comment field stay in ISO. + //Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, "ID3v1 Comment"); + //Id3tag_Set_Field(id3_frame, ID3FN_LANGUAGE, "XXX"); + has_comment = TRUE; + } + + + /************ + * Composer * + ************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COMPOSER)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->composer && g_utf8_strlen(FileTag->composer, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_COMPOSER); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->composer); + has_composer = TRUE; + } + + + /******************* + * Original artist * + *******************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ORIGARTIST)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->orig_artist && g_utf8_strlen(FileTag->orig_artist, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_ORIGARTIST); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->orig_artist); + has_orig_artist = TRUE; + } + + + /************* + * Copyright * + *************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_COPYRIGHT)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->copyright && g_utf8_strlen(FileTag->copyright, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_COPYRIGHT); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->copyright); + has_copyright = TRUE; + } + + + /******* + * URL * + *******/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_WWWUSER)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->url && g_utf8_strlen(FileTag->url, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_WWWUSER); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_URL, FileTag->url); + has_composer = TRUE; + } + + + /************** + * Encoded by * + **************/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_ENCODEDBY)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (FileTag->encoded_by && g_utf8_strlen(FileTag->encoded_by, -1) > 0) + { + id3_frame = ID3Frame_NewID(ID3FID_ENCODEDBY); + ID3Tag_AttachFrame(id3_tag,id3_frame); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->encoded_by); + has_encoded_by = TRUE; + } + + + /*********** + * Picture * + ***********/ + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PICTURE)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + pic = FileTag->picture; + if (!pic) + has_picture = 0; + while (pic) + { + id3_frame = ID3Frame_NewID(ID3FID_PICTURE); + ID3Tag_AttachFrame(id3_tag,id3_frame); + + switch (Picture_Format(pic)) + { + case PICTURE_FORMAT_JPEG: + if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE))) + ID3Field_SetASCII(id3_field, "image/jpeg"); + if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT))) + ID3Field_SetASCII(id3_field, "JPG"); + break; + + case PICTURE_FORMAT_PNG: + if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_MIMETYPE))) + ID3Field_SetASCII(id3_field, "image/png"); + if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_IMAGEFORMAT))) + ID3Field_SetASCII(id3_field, "PNG"); + break; + } + + if ((id3_field = ID3Frame_GetField(id3_frame, ID3FN_PICTURETYPE))) + ID3Field_SetINT(id3_field, pic->type); + + if (pic->description) + Id3tag_Set_Field(id3_frame, ID3FN_DESCRIPTION, pic->description); + + if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_DATA))) + ID3Field_SetBINARY(id3_field, pic->data, pic->size); + + pic = pic->next; + has_picture = TRUE; + } + + + /********************************* + * File length (in milliseconds) * + *********************************/ + /* Don't write this field, not useful? * + while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_SONGLEN)) ) + ID3Tag_RemoveFrame(id3_tag,id3_frame); + if (ETFile->ETFileInfo && ((ET_File_Info *)ETFile->ETFileInfo)->duration > 0 ) + { + gchar *string; + + id3_frame = ID3Frame_NewID(ID3FID_SONGLEN); + ID3Tag_AttachFrame(id3_tag,id3_frame); + + string = g_strdup_printf("%d",((ET_File_Info *)ETFile->ETFileInfo)->duration * 1000); + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string); + g_free(string); + has_song_len = TRUE; + }*/ + + + /****************************** + * Delete an APE tag if found * + ******************************/ + { + // Delete the APE tag (create a dummy ETFile for the Ape_Tag_... function) + ET_File *ETFile_tmp = ET_File_Item_New(); + File_Name *FileName_tmp = ET_File_Name_Item_New(); + File_Tag *FileTag_tmp = ET_File_Tag_Item_New(); + // Same file... + FileName_tmp->value = g_strdup(filename); + FileName_tmp->value_utf8 = g_strdup(filename_utf8); // Not necessary to fill 'value_ck' + ETFile_tmp->FileNameList = g_list_append(NULL,FileName_tmp); + ETFile_tmp->FileNameCur = ETFile_tmp->FileNameList; + // With empty tag... + ETFile_tmp->FileTagList = g_list_append(NULL,FileTag_tmp); + ETFile_tmp->FileTag = ETFile_tmp->FileTagList; + Ape_Tag_Write_File_Tag(ETFile_tmp); + ET_Free_File_List_Item(ETFile_tmp); + } + + + /********************************* + * Update id3v1.x and id3v2 tags * + *********************************/ + /* Get the number of frames into the tag, cause if it is + * equal to 0, id3lib-3.7.12 doesn't update the tag */ + number_of_frames = ID3Tag_NumFrames(id3_tag); + + /* If all fields (managed in the UI) are empty and option STRIP_TAG_WHEN_EMPTY_FIELDS + * is set to 1, we strip the ID3v1.x and ID3v2 tags. Else, write ID3v2 and/or ID3v1 + */ + if ( STRIP_TAG_WHEN_EMPTY_FIELDS + && !has_title && !has_artist && !has_album && !has_year && !has_track + && !has_genre && !has_composer && !has_orig_artist && !has_copyright && !has_url + && !has_encoded_by && !has_picture && !has_comment && !has_disc_number)//&& !has_song_len ) + { + error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1); + error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2); + /* Check error messages */ + if (error_strip_id3v1 == ID3E_NoError && error_strip_id3v2 == ID3E_NoError) + { + Log_Print(_("Removed tag of '%s'"),basename_utf8); + }else + { + if (error_strip_id3v1 != ID3E_NoError) + Log_Print(_("Error while removing ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v1)); + if (error_strip_id3v2 != ID3E_NoError) + Log_Print(_("Error while removing ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v2)); + error++; + } + + }else + { + /* It's better to remove the id3v1 tag before, to synchronize it with the + * id3v2 tag (else id3lib doesn't do it correctly) + */ + error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1); + + /* + * ID3v2 tag + */ + if (FILE_WRITING_ID3V2_WRITE_TAG && number_of_frames!=0) + { + error_update_id3v2 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2); + if (error_update_id3v2 != ID3E_NoError) + { + Log_Print(_("Error while updating ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_update_id3v2)); + error++; + }else + { + /* See known problem on the top : [ 1016290 ] Unicode16 writing bug. + * When we write the tag in Unicode, we try to check if it was correctly + * written. So to test it : we read again the tag, and then compare + * with the previous one. We check up to find an error (as only some + * characters are affected). + * If the patch to id3lib was applied to fix the problem (tested + * by Id3tag_Check_If_Id3lib_Is_Bugged) we didn't make the following + * test => OK */ + if (flag_id3lib_bugged + && ( FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET )) + { + File_Tag *FileTag_tmp = ET_File_Tag_Item_New(); + if (Id3tag_Read_File_Tag(filename,FileTag_tmp) == TRUE + && ET_Detect_Changes_Of_File_Tag(FileTag,FileTag_tmp) == TRUE) + { + GtkWidget *msgbox = NULL; + gchar *msg; + + msg = g_strdup_printf(_("You have tried to save this tag to Unicode " + "but it was detected that your version of id3lib is bugged.\n" + "If you reload this file, some characters in the tag may be not " + "displayed correctly...\nPlease, apply to id3lib the patch " + "src/id3lib/patch_id3lib_3.8.3_UTF16_writing_bug.diff\n" + "available in EasyTAG package sources.\n" + "Note that this message will appear only one time.\n\n" + "File : %s"),filename_utf8); + //Log_Print(msg); + + msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0); + g_free(msg); + msg_box_hide_check_button(MSG_BOX(msgbox)); + msg_box_run(MSG_BOX(msgbox)); + gtk_widget_destroy(msgbox); + flag_id3lib_bugged = FALSE; // To display the message only one time + } + ET_Free_File_Tag_Item(FileTag_tmp); + } + + } + }else + { + error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2); + if (error_strip_id3v2 != ID3E_NoError) + { + Log_Print(_("Error while removing ID3v2 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v2)); + error++; + } + } + + /* + * ID3v1 tag + * Must be set after ID3v2 or ID3Tag_UpdateByTagType cause damage to unicode strings + */ + // id3lib writes incorrectly the ID3v2 tag if unicode used when writing ID3v1 tag + if (FILE_WRITING_ID3V1_WRITE_TAG && number_of_frames!=0) + { + // By default id3lib converts id3tag to ISO-8859-1 (single byte character set) + // Note : converting UTF-16 string (two bytes character set) to ISO-8859-1 + // remove only the second byte => a strange string appears... + Id3tag_Prepare_ID3v1(id3_tag); + + error_update_id3v1 = ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V1); + if (error_update_id3v1 != ID3E_NoError) + { + Log_Print(_("Error while updating ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_update_id3v1)); + error++; + } + }else + { + error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1); + if (error_strip_id3v1 != ID3E_NoError) + { + Log_Print(_("Error while removing ID3v1 tag of '%s' (%s)"),basename_utf8,Id3tag_Get_Error_Message(error_strip_id3v1)); + error++; + } + } + + if (error == 0) + Log_Print(_("Updated tag of '%s'"),basename_utf8); + + } + + /* Free allocated data */ + ID3Tag_Delete(id3_tag); + g_free(basename_utf8); + + if (error) return FALSE; + else return TRUE; + +} + + +gchar *Id3tag_Get_Error_Message(ID3_Err error) +{ + switch (error) + { + case ID3E_NoError: + return _("No error reported"); + case ID3E_NoMemory: + return _("No available memory"); + case ID3E_NoData: + return _("No data to parse"); + case ID3E_BadData: + return _("Improperly formatted data"); + case ID3E_NoBuffer: + return _("No buffer to write to"); + case ID3E_SmallBuffer: + return _("Buffer is too small"); + case ID3E_InvalidFrameID: + return _("Invalid frame ID"); + case ID3E_FieldNotFound: + return _("Requested field not found"); + case ID3E_UnknownFieldType: + return _("Unknown field type"); + case ID3E_TagAlreadyAttached: + return _("Tag is already attached to a file"); + case ID3E_InvalidTagVersion: + return _("Invalid tag version"); + case ID3E_NoFile: + return _("No file to parse"); + case ID3E_ReadOnly: + return _("Attempting to write to a read-only file"); + case ID3E_zlibError: + return _("Error in compression/uncompression"); + default: + return _("Unknown error message!"); + } + +} + + + +/* + * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags + * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely + * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them, + * fall back to the ID3v1 tags. + * (Written by Holger Schemel). + */ +ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename) +{ + size_t offset; + +# if (0) // Link the file with the both tags may cause damage to unicode strings +//# if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) && (ID3LIB_PATCH >= 1) ) // Same test used in Id3tag_Read_File_Tag to use ID3Tag_HasTagType + /* No problem of priority, so we link the file with the both tags + * to manage => ID3Tag_HasTagType works correctly */ + offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1 | ID3TT_ID3V2); +# elif ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) ) + /* Version 3.8.0pre2 gives priority to tag id3v1 instead of id3v2, so we + * try to fix it by linking the file with the id3v2 tag first. This bug + * was fixed in the final version of 3.8.0 but we can't know it... */ + /* First, try to get the ID3v2 tags */ + 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); + } +# else + /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */ + offset = ID3Tag_Link(id3tag,filename); +# endif + //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_path_get_basename(filename)); + return offset; +} + + +/* + * As the ID3Field_GetASCII function differs with the version of id3lib, we must redefine it. + */ +ID3_C_EXPORT size_t ID3Field_GetASCII_1(const ID3Field *field, char *buffer, size_t maxChars, size_t itemNum) +{ + + /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION + * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH + * + * <= 3.7.12 : first item num is 1 for ID3Field_GetASCII + * = 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) +# if (ID3LIB_MINOR <= 7) + // (3.0.0 to 3.7.x) +# if (ID3LIB_PATCH >= 13) + // (>= 3.7.13) + return ID3Field_GetASCII(field,buffer,maxChars,itemNum); +# else + 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); +# endif +# else + // Not tested (< 3.x.x) + return ID3Field_GetASCII(field,buffer,maxChars,itemNum+1); +# endif +} + + + +/* + * As the ID3Field_GetUNICODE function differs with the version of id3lib, we must redefine it. + */ +ID3_C_EXPORT size_t ID3Field_GetUNICODE_1 (const ID3Field *field, unicode_t *buffer, size_t maxChars, size_t itemNum) +{ + + /* Defined by id3lib: ID3LIB_MAJOR_VERSION, ID3LIB_MINOR_VERSION, ID3LIB_PATCH_VERSION + * Defined by autoconf: ID3LIB_MAJOR, ID3LIB_MINOR, ID3LIB_PATCH + * + * <= 3.7.12 : first item num is 1 for ID3Field_GetUNICODE + * = 3.7.13 : first item num is 0 for ID3Field_GetUNICODE + * >= 3.8.0 : doesn't need item num for ID3Field_GetUNICODE + */ + //g_print("id3lib version: %d.%d.%d\n",ID3LIB_MAJOR,ID3LIB_MINOR,ID3LIB_PATCH); +# if (ID3LIB_MAJOR >= 3) + // (>= 3.x.x) +# if (ID3LIB_MINOR <= 7) + // (3.0.0 to 3.7.x) +# if (ID3LIB_PATCH >= 13) + // (>= 3.7.13) + return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum); +# else + return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1); +# endif +# else + // (>= to 3.8.0) + return ID3Field_GetUNICODE(field,buffer,maxChars); + // ID3Field_GetUNICODEItem always return 0 with id3lib3.8.3, it is bug in size_t D3_FieldImpl::Get() + //return ID3Field_GetUNICODEItem(field,buffer,maxChars,itemNum); +# endif +# else + // Not tested (< 3.x.x) + return ID3Field_GetUNICODE(field,buffer,maxChars,itemNum+1); +# endif +} + + + + +/* + * Source : "http://www.id3.org/id3v2.4.0-structure.txt" + * + * Frames that allow different types of text encoding contains a text + * encoding description byte. Possible encodings: + * + * $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. + * $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM ($FF FE + * or $FE FF). All strings in the same frame SHALL have the same + * byteorder. Terminated with $00 00. + * $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. + * Terminated with $00 00. + * $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. + * + * For example : + * T P E 1 . . . . . . . ? ? J . o . n . . G . i . n . d . i . c . k . + * Hex : 54 50 45 31 00 00 00 19 00 00 01 ff fe 4a 00 6f 00 6e 00 20 00 47 00 69 00 6e 00 64 00 6e 00 63 00 6b 00 + * ^ + * |___ UTF-16 + */ +/* + * Read the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the + * id3_frame, and convert the string if needed to UTF-8. + */ +gchar *Id3tag_Get_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid) +{ + ID3Field *id3_field = NULL; + ID3Field *id3_field_encoding = NULL; + size_t num_chars = 0; + gchar *string = NULL, *string1 = NULL; + + //g_print("Id3tag_Get_Field - ID3Frame '%s'\n",ID3FrameInfo_ShortName(ID3Frame_GetID(id3_frame))); + + if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) ) + { + ID3_TextEnc enc = ID3TE_NONE; + + // Data of the field must be a TEXT (ID3FTY_TEXTSTRING) + if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING) + { + Log_Print("Id3tag_Get_Field() must be used only for fields containing text.\n"); + return NULL; + } + + /* + * We prioritize the encoding of the field. If the encoding of the field + * is ISO-8859-1, it can be read with an other single byte encoding. + */ + // Get encoding from content of file... + id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC); + if (id3_field_encoding) + enc = ID3Field_GetINT(id3_field_encoding); + // Else, get encoding from the field + //enc = ID3Field_GetEncoding(id3_field); + + if (enc != ID3TE_UTF16 && enc != ID3TE_UTF8) // Encoding is ISO-8859-1? + { + if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET) // Override with an other character set? + { + // Encoding set by user to ???. + if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"ISO-8859-1") == 0 ) + { + enc = ID3TE_ISO8859_1; + }else if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-16BE") == 0 + || strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-16LE") == 0 ) + { + enc = ID3TE_UTF16; + }else if ( strcmp(FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-8") == 0 ) + { + enc = ID3TE_UTF8; + }else + { + enc = 9999; + } + } + } + + // Some fields, as URL, aren't encodable, so there were written using ISO characters. + if ( !ID3Field_IsEncodable(id3_field) ) + { + enc = ID3TE_ISO8859_1; + } + + // Action according the encoding... + switch ( enc ) + { + case ID3TE_ISO8859_1: + string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0); + string1 = convert_string(string,"ISO-8859-1","UTF-8",FALSE); + break; + + case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4) + // For UTF-8, this part do the same thing that enc=9999 + string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0); + //string1 = convert_string(string,"UTF-8","UTF-8",FALSE); // Nothing to do + if (g_utf8_validate(string,-1,NULL)) + string1 = g_strdup(string); + break; + + case ID3TE_UTF16: + // Id3lib (3.8.3 at least) always returns Unicode strings in UTF-16BE. + case ID3TE_UTF16BE: + string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetUNICODE_1(id3_field,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0); + // "convert_string_1" as we need to pass length for UTF-16 + string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE); + break; + + case 9999: + string = g_malloc0(sizeof(char)*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0); + string1 = convert_string(string,FILE_READING_ID3V1V2_CHARACTER_SET,"UTF-8",FALSE); + break; + + default: + string = g_malloc0(sizeof(char)*4*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetASCII_1(id3_field,string,ID3V2_MAX_STRING_LEN,0); + string1 = convert_to_utf8(string); + break; + } + } + //g_print(">>ID:%d >'%s' (string1:'%s') (num_chars:%d)\n",ID3Field_GetINT(id3_field_encoding),string,string1,num_chars); + + // In case the conversion fails, try 'filename_to_display' character fix... + if (num_chars && !string1) + { + gchar *escaped_str = g_strescape(string, NULL); + Log_Print("Id3tag_Get_Field: Trying to fix string '%s' ...",escaped_str); + g_free(escaped_str); + + string1 = filename_to_display(string); + + if (string1) + Log_Print("OK"); + else + Log_Print("KO"); + } + g_free(string); + + return string1; +} + + +/* + * Set the content (ID3FN_TEXT, ID3FN_URL, ...) of the id3_field of the + * id3_frame. Check also if the string must be written from UTF-8 (gtk2) in the + * ISO-8859-1 encoding or UTF-16. + * + * Return the encoding used as if UTF-16 was used, we musn't write the ID3v1 tag. + * + * Known problem with id3lib + * - [ 1016290 ] Unicode16 writing bug + * http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979 + * For example with Latin-1 characters (like éöäüß) not saved correctly + * in Unicode, due to id3lib (for "é" it will write "E9 FF" instead of "EF 00") + */ +/* + * OLD NOTE : PROBLEM with ID3LIB + * - [ 1074169 ] Writing ID3v1 tag breaks Unicode string in v2 tags + * http://sourceforge.net/tracker/index.php?func=detail&aid=1074169&group_id=979&atid=100979 + * => don't write id3v1 tag if Unicode is used, up to patch applied + * - [ 1073951 ] Added missing Field Encoding functions to C wrapper + * http://sourceforge.net/tracker/index.php?func=detail&aid=1073951&group_id=979&atid=300979 + */ +ID3_TextEnc Id3tag_Set_Field (const ID3Frame *id3_frame, ID3_FieldID id3_fieldid, gchar *string) +{ + ID3Field *id3_field = NULL; + ID3Field *id3_field_encoding = NULL; + gchar *string_converted = NULL; + + if ( (id3_field = ID3Frame_GetField(id3_frame,id3_fieldid)) ) + { + ID3_TextEnc enc = ID3TE_NONE; + + // Data of the field must be a TEXT (ID3FTY_TEXTSTRING) + if (ID3Field_GetType(id3_field) != ID3FTY_TEXTSTRING) + { + Log_Print("Id3tag_Set_Field() must be used only for fields containing text."); + return ID3TE_NONE; + } + + /* + * We prioritize the rule selected in options. If the encoding of the + * field is ISO-8859-1, we can write it to an other single byte encoding. + */ + if (FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET) + { + // Check if we can write the tag using ISO-8859-1 instead of UTF-16... + if ( (string_converted = g_convert(string, strlen(string), "ISO-8859-1", + "UTF-8", NULL, NULL ,NULL)) ) + { + enc = ID3TE_ISO8859_1; + g_free(string_converted); + }else + { + // Force to UTF-16 as UTF-8 isn't supported + enc = ID3TE_UTF16; + } + } else + { + // Other encoding selected + // Encoding set by user to ???. + if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"ISO-8859-1") == 0 ) + { + enc = ID3TE_ISO8859_1; + }else if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-16BE") == 0 + || strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-16LE") == 0 ) + { + enc = ID3TE_UTF16; + }else if ( strcmp(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,"UTF-8") == 0 ) + { + enc = ID3TE_UTF8; + }else + { + enc = 9999; + } + } + + // Some fields, as URL, aren't encodable, so there were written using ISO characters! + if ( !ID3Field_IsEncodable(id3_field) ) + { + enc = ID3TE_ISO8859_1; + } + + // Action according the encoding... + switch ( enc ) + { + case ID3TE_ISO8859_1: + // Write into ISO-8859-1 + //string_converted = convert_string(string,"UTF-8","ISO-8859-1",TRUE); + string_converted = Id3tag_Rules_For_ISO_Fields(string,"UTF-8","ISO-8859-1"); + ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1, but better to precise if field has an other encoding... + ID3Field_SetASCII(id3_field,string_converted); + g_free(string_converted); + + id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC); + if (id3_field_encoding) + ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1); + + return ID3TE_ISO8859_1; + break; + + /*** Commented as it doesn't work with id3lib 3.8.3 : + *** - it writes a strange UTF-8 string (2 bytes per character. Second char is FF) with a BOM + *** - it set the frame encoded to UTF-8 : "$03" which is OK + case ID3TE_UTF8: // Shouldn't work with id3lib 3.8.3 (supports only ID3v2.3, not ID3v2.4) + ID3Field_SetEncoding(id3_field,ID3TE_UTF8); + ID3Field_SetASCII(id3_field,string); + + id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC); + if (id3_field_encoding) + ID3Field_SetINT(id3_field_encoding,ID3TE_UTF8); + + return ID3TE_UTF8; + break; + ***/ + + case ID3TE_UTF16: + //case ID3TE_UTF16BE: + + /* See known problem on the top : [ 1016290 ] Unicode16 writing bug */ + // Write into UTF-16 + string_converted = convert_string_1(string, strlen(string), "UTF-8", + "UTF-16BE", FALSE); + + // id3lib (3.8.3 at least) always takes big-endian input for Unicode + // fields, even if the field is set little-endian. + ID3Field_SetEncoding(id3_field,ID3TE_UTF16); + ID3Field_SetUNICODE(id3_field,(const unicode_t*)string_converted); + g_free(string_converted); + + id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC); + if (id3_field_encoding) + ID3Field_SetINT(id3_field_encoding,ID3TE_UTF16); + + return ID3TE_UTF16; + break; + + case 9999: + default: + //string_converted = convert_string(string,"UTF-8",FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,TRUE); + string_converted = Id3tag_Rules_For_ISO_Fields(string,"UTF-8",FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET); + ID3Field_SetEncoding(id3_field,ID3TE_ISO8859_1); + ID3Field_SetASCII(id3_field,string_converted); + g_free(string_converted); + + id3_field_encoding = ID3Frame_GetField(id3_frame,ID3FN_TEXTENC); + if (id3_field_encoding) + ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1); + + return ID3TE_NONE; + break; + } + } + + return ID3TE_NONE; +} + + +/* + * By default id3lib converts id3tag to ISO-8859-1 (single byte character set) + * Note : converting UTF-16 string (two bytes character set) to ISO-8859-1 + * remove only the second byte => a strange string appears... + */ +void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag) +{ + ID3Frame *frame; + ID3Field *id3_field_encoding; + ID3Field *id3_field_text; + + if ( id3_tag != NULL ) + { + ID3TagIterator *id3_tag_iterator; + size_t num_chars = 0; + gchar *string, *string1, *string_converted; + + id3_tag_iterator = ID3Tag_CreateIterator(id3_tag); + while ( NULL != (frame = ID3TagIterator_GetNext(id3_tag_iterator)) ) + { + ID3_TextEnc enc = ID3TE_ISO8859_1; + ID3_FrameID frameid; + + frameid = ID3Frame_GetID(frame); + + if (frameid != ID3FID_TITLE + && frameid != ID3FID_LEADARTIST + && frameid != ID3FID_ALBUM + && frameid != ID3FID_YEAR + && frameid != ID3FID_TRACKNUM + && frameid != ID3FID_CONTENTTYPE + && frameid != ID3FID_COMMENT) + continue; + + id3_field_encoding = ID3Frame_GetField(frame, ID3FN_TEXTENC); + if (id3_field_encoding != NULL) + enc = ID3Field_GetINT(id3_field_encoding); + id3_field_text = ID3Frame_GetField(frame, ID3FN_TEXT); + + /* The frames in ID3TE_ISO8859_1 are already converted to the selected + * single-byte character set if used. So we treat only Unicode frames */ + if ( (id3_field_text != NULL) + && (enc != ID3TE_ISO8859_1) ) + { + // Read UTF-16 frame + string = g_malloc0(sizeof(unicode_t)*ID3V2_MAX_STRING_LEN+1); + num_chars = ID3Field_GetUNICODE_1(id3_field_text,(unicode_t *)string,ID3V2_MAX_STRING_LEN,0); + // "convert_string_1" as we need to pass length for UTF-16 + string1 = convert_string_1(string,num_chars,"UTF-16BE","UTF-8",FALSE); + + string_converted = Id3tag_Rules_For_ISO_Fields(string1,"UTF-8",FILE_WRITING_ID3V1_CHARACTER_SET); + + if (string_converted) + { + ID3Field_SetEncoding(id3_field_text,ID3TE_ISO8859_1); // Not necessary for ISO-8859-1 + ID3Field_SetASCII(id3_field_text,string_converted); + ID3Field_SetINT(id3_field_encoding,ID3TE_ISO8859_1); + g_free(string_converted); + } + g_free(string); + g_free(string1); + } + } + ID3TagIterator_Delete(id3_tag_iterator); + } +} + +/* + * This function must be used for tag fields containing ISO data. + * We use specials functionalities of iconv : //TRANSLIT and //IGNORE (the last + * one doesn't work on my system) to force conversion to the target encoding. + */ +gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string, const gchar *from_codeset, const gchar *to_codeset) +{ + gchar *string_converted = NULL; + + if (!string || !from_codeset || !to_codeset) + return NULL; + + if (FILE_WRITING_ID3V1_ICONV_OPTIONS_NO) + { + string_converted = convert_string(string,from_codeset,to_codeset,TRUE); + + }else if (FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT) + { + // iconv_open (3): + // When the string "//TRANSLIT" is appended to tocode, transliteration + // is activated. This means that when a character cannot be represented + // in the target character set, it can be approximated through one or + // several similarly looking characters. + gchar *to_enc = g_strconcat(to_codeset, "//TRANSLIT", NULL); + string_converted = convert_string(string,from_codeset,to_enc,TRUE); + g_free(to_enc); + + }else if (FILE_WRITING_ID3V1_ICONV_OPTIONS_IGNORE) + { + // iconv_open (3): + // When the string "//IGNORE" is appended to tocode, characters that + // cannot be represented in the target character set will be silently + // discarded. + gchar *to_enc = g_strconcat(to_codeset, "//IGNORE", NULL); + string_converted = convert_string(string,from_codeset,to_enc,TRUE); + g_free(to_enc); + } + + return string_converted; +} + +/* + * Some files which contains only zeroes create an infinite loop in id3lib... + * To generate a file with zeroes : dd if=/dev/zero bs=1M count=6 of=test-corrupted-mp3-zero-contend.mp3 + */ +gboolean Id3tag_Check_If_File_Is_Corrupted (gchar *filename) +{ + FILE *file; + unsigned char tmp[256]; + unsigned char tmp0[256]; + gint bytes_read; + gboolean result = TRUE; + gint cmp; + + if (!filename) + return FALSE; + + if ( (file=fopen(filename,"rb"))==NULL ) + { + gchar *filename_utf8 = filename_to_display(filename); + Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno)); + g_free(filename_utf8); + return FALSE; + } + + memset(&tmp0,0,256); + while (!feof(file)) + { + bytes_read = fread(tmp, 1, 256, file); + if ( (cmp=memcmp(tmp,tmp0,bytes_read)) != 0) + { + result = FALSE; + break; + } + } + fclose(file); + + if (result) + { + GtkWidget *msgbox = NULL; + gchar *msg; + gchar *basename; + gchar *basename_utf8; + + basename = g_path_get_basename(filename); + basename_utf8 = filename_to_display(basename); + + msg = g_strdup_printf(_("As the following corrupted file: '%s'\nwill cause " + "an error in id3lib, it will not be processed by the program."),basename_utf8); + msgbox = msg_box_new(_("Corrupted file..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_CLOSE,0); + msg_box_hide_check_button(MSG_BOX(msgbox)); + msg_box_run(MSG_BOX(msgbox)); + gtk_widget_destroy(msgbox); + g_free(msg); + g_free(basename); + g_free(basename_utf8); + } + + return result; +} + + +/* + * Function to detect if id3lib isn't bugged when writting to Unicode + * Returns TRUE if bugged, else FALSE + */ +gboolean Id3tag_Check_If_Id3lib_Is_Bugged (void) +{ + FILE *file; + unsigned char tmp[16] = {0xFF, 0xFB, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + ID3Tag *id3_tag = NULL; + gchar *filename; + gchar *result = NULL; + ID3Frame *id3_frame; + gboolean use_unicode; + + + if (!HOME_VARIABLE) + return FALSE; + + // Create a temporary file + filename = g_strconcat(HOME_VARIABLE, + HOME_VARIABLE[strlen(HOME_VARIABLE)-1]!=G_DIR_SEPARATOR?G_DIR_SEPARATOR_S:"", + ".easytag"G_DIR_SEPARATOR_S"test_easytag.mp3", + NULL); + if ( (file=fopen(filename,"w+"))==NULL ) + { + gchar *filename_utf8 = filename_to_display(filename); + Log_Print(_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno)); + g_free(filename_utf8); + return FALSE; + } + // Set data in the file + fwrite(&tmp,16,1,file); + fclose(file); + + // Save state of switches as we must force to Unicode before writting + use_unicode = FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET; + FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = TRUE; + + id3_tag = ID3Tag_New(); + ID3Tag_Link_1(id3_tag,filename); + + // Create a new 'title' field for testing + id3_frame = ID3Frame_NewID(ID3FID_TITLE); + ID3Tag_AttachFrame(id3_tag,id3_frame); + // Use a Chinese character instead of the latin-1 character as in Id3tag_Set_Field() + // we try to convert the string to ISO-8859-1 even in the Unicode mode. + //Id3tag_Set_Field(id3_frame, ID3FN_TEXT, "é"); // This latin-1 character is written in Unicode as 'E9 FF' instead of 'E9 00' if bugged + Id3tag_Set_Field(id3_frame, ID3FN_TEXT, "グ"); // This Chinese character is written in Unicode as 'FF FE B0 FF' instead of 'FF FE B0 30' if bugged + + // Update the tag + ID3Tag_UpdateByTagType(id3_tag,ID3TT_ID3V2); + ID3Tag_Delete(id3_tag); + + FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET = use_unicode; + + + id3_tag = ID3Tag_New(); + ID3Tag_Link_1(id3_tag,filename); + // Read the written field + if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_TITLE)) ) + { + result = Id3tag_Get_Field(id3_frame,ID3FN_TEXT); + } + + ID3Tag_Delete(id3_tag); + remove(filename); + + // Same string found? if yes => not bugged + //if ( result && strcmp(result,"é")!=0 ) + if ( result && strcmp(result,"グ")!=0 ) + { + return TRUE; + } + + return FALSE; +} + +#endif /* ENABLE_ID3LIB */ + + +/* + * Write tag according the version selected by the user + */ +gboolean Id3tag_Write_File_Tag (ET_File *ETFile) +{ +#ifdef ENABLE_ID3LIB + if (FILE_WRITING_ID3V2_VERSION_4) + return Id3tag_Write_File_v24Tag(ETFile); + else + return Id3tag_Write_File_v23Tag(ETFile); +#else + return Id3tag_Write_File_v24Tag(ETFile); +#endif +} + +#endif /* ENABLE_MP3 */ + + +// Placed out #ifdef ENABLE_MP3 as not dependant of id3lib, and used in CDDB +/* + * Returns the corresponding genre value of the input string (for ID3v1.x), + * else returns 0xFF (unknown genre, but not invalid). + */ +guchar Id3tag_String_To_Genre (gchar *genre) +{ + guint i; + + if (genre != NULL) + { + for (i=0; i<=GENRE_MAX; i++) + if (strcasecmp(genre,id3_genres[i])==0) + return (guchar)i; + } + return (guchar)0xFF; +} + + +/* + * 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) + */ +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]; +} + |