aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/id3v24_tag.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/id3v24_tag.c')
-rwxr-xr-xsrc/id3v24_tag.c1499
1 files changed, 1499 insertions, 0 deletions
diff --git a/src/id3v24_tag.c b/src/id3v24_tag.c
new file mode 100755
index 0000000..7b3c729
--- /dev/null
+++ b/src/id3v24_tag.c
@@ -0,0 +1,1499 @@
+/* id3v24_tag.c - 2007/05/25 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2001-2003 Jerome Couderc <easytag@gmail.com>
+ * Copyright (C) 2006-2007 Alexey Illarionov <littlesavage@rambler.ru>
+ *
+ * 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 <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "id3_tag.h"
+#include "picture.h"
+#include "easytag.h"
+#include "browser.h"
+#include "genres.h"
+#include "setting.h"
+#include "misc.h"
+#include "et_core.h"
+#include "msgbox.h"
+#include "charset.h"
+
+#ifdef WIN32
+# include "win32/win32dep.h"
+#endif
+
+
+#ifdef ENABLE_MP3
+
+#include <id3tag.h>
+
+
+/****************
+ * Declarations *
+ ****************/
+#define MULTIFIELD_SEPARATOR " - "
+#define EASYTAG_STRING_ENCODEDBY "Encoded by"
+
+enum {
+ EASYTAG_ID3_FIELD_LATIN1 = 0x0001,
+ EASYTAG_ID3_FIELD_LATIN1FULL = 0x0002,
+ EASYTAG_ID3_FIELD_LATIN1LIST = 0x0004,
+ EASYTAG_ID3_FIELD_STRING = 0x0008,
+ EASYTAG_ID3_FIELD_STRINGFULL = 0x0010,
+ EASYTAG_ID3_FIELD_STRINGLIST = 0x0020,
+ EASYTAG_ID3_FIELD_LANGUAGE = 0x0040
+};
+
+/**************
+ * Prototypes *
+ **************/
+static int etag_guess_byteorder (const id3_ucs4_t *ustr, gchar **ret);
+static int etag_ucs42gchar (const id3_ucs4_t *usrc, unsigned is_latin, unsigned is_utf16, gchar **res);
+static int libid3tag_Get_Frame_Str (const struct id3_frame *frame, unsigned etag_field_type, gchar **retstr);
+
+static void Id3tag_delete_frames (struct id3_tag *tag, const gchar *name, int start);
+static void Id3tag_delete_txxframes (struct id3_tag *tag, const gchar *param1, int start);
+static struct id3_frame *Id3tag_findncreate_frame (struct id3_tag *tag, const gchar *name);
+static struct id3_frame *Id3tag_findncreate_txxframe (struct id3_tag *tag, const gchar *param1);
+static int id3taglib_set_field (struct id3_frame *frame, const gchar *str, enum id3_field_type type, int num, int clear, int id3v1);
+static int etag_set_tags (const gchar *str, const char *frame_name, enum id3_field_type field_type, struct id3_tag *v1tag, struct id3_tag *v2tag, gboolean *strip_tags);
+static int etag_write_tags (const gchar *filename, const struct id3_tag *v1tag, const struct id3_tag *v2tag, gboolean strip_tags);
+
+/*************
+ * Functions *
+ *************/
+
+/*
+ * Read id3v1.x / id3v2 tag and load data into the File_Tag structure.
+ * Returns TRUE on success, else FALSE.
+ * If a tag entry exists (ex: title), we allocate memory, else value stays to NULL
+ */
+gboolean Id3tag_Read_File_Tag (gchar *filename, File_Tag *FileTag)
+{
+ int tmpfile;
+ struct id3_file *file;
+ struct id3_tag *tag;
+ struct id3_frame *frame;
+ union id3_field *field;
+ gchar *string1, *string2;
+ Picture *prev_pic = NULL;
+ int i, j;
+ unsigned tmpupdate, update = 0;
+ long tagsize;
+
+
+ if (!filename || !FileTag)
+ return FALSE;
+
+ if ( (tmpfile=open(filename,O_RDONLY)) < 0 )
+ {
+ gchar *filename_utf8 = filename_to_display(filename);
+ g_print(_("ERROR while opening file: '%s' (%s).\n\a"),filename_utf8,g_strerror(errno));
+ g_free(filename_utf8);
+ return FALSE;
+ }
+
+ string1 = g_try_malloc(ID3_TAG_QUERYSIZE);
+ if (string1==NULL)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ // Check if the file has an ID3v2 tag or/and an ID3v1 tags
+ // 1) ID3v2 tag
+ if (read(tmpfile, string1, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ if ((tagsize = id3_tag_query((id3_byte_t const *)string1, ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
+ {
+ // ID3v2 tag not found!
+ update = FILE_WRITING_ID3V2_WRITE_TAG;
+ }else
+ {
+ /* ID3v2 tag found */
+ if (FILE_WRITING_ID3V2_WRITE_TAG == 0)
+ {
+ update = 1;
+ }else
+ {
+ /* Determine version if user want to upgrade old tags */
+ if (CONVERT_OLD_ID3V2_TAG_VERSION
+ && (string1 = realloc(string1, tagsize))
+ && (read(tmpfile, &string1[ID3_TAG_QUERYSIZE], tagsize - ID3_TAG_QUERYSIZE) == tagsize - ID3_TAG_QUERYSIZE)
+ && (tag = id3_tag_parse((id3_byte_t const *)string1, tagsize))
+ )
+ {
+ unsigned version = id3_tag_version(tag);
+#ifdef ENABLE_ID3LIB
+ /* Besides upgrade old tags we will downgrade id3v2.4 to id3v2.3 */
+ if ( FILE_WRITING_ID3V2_VERSION_4 )
+ {
+ update = (ID3_TAG_VERSION_MAJOR(version) < 4);
+ }else
+ {
+ update = ((ID3_TAG_VERSION_MAJOR(version) < 3)
+ | (ID3_TAG_VERSION_MAJOR(version) == 4));
+ }
+#else
+ update = (ID3_TAG_VERSION_MAJOR(version) < 4);
+#endif
+ id3_tag_delete(tag);
+ }
+ }
+ }
+
+ // 2) ID3v1 tag
+ if ( (lseek(tmpfile,-128, SEEK_END) >= 0) // Go to the beginning of ID3v1 tag
+ && (string1)
+ && (read(tmpfile, string1, 3) == 3)
+ && (string1[0] == 'T')
+ && (string1[1] == 'A')
+ && (string1[2] == 'G')
+ )
+ {
+ // ID3v1 tag found!
+ if (!FILE_WRITING_ID3V1_WRITE_TAG)
+ update = 1;
+ }else
+ {
+ // ID3v1 tag not found!
+ if (FILE_WRITING_ID3V1_WRITE_TAG)
+ update = 1;
+ }
+
+ g_free(string1);
+
+ if ((file = id3_file_fdopen(tmpfile, ID3_FILE_MODE_READONLY)) == NULL)
+ {
+ close(tmpfile);
+ return FALSE;
+ }
+
+ if ( ((tag = id3_file_tag(file)) == NULL)
+ || (tag->nframes == 0))
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+
+
+ /****************
+ * Title (TIT2) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_TITLE, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRINGLIST, &FileTag->title);
+
+ /*****************
+ * Artist (TPE1) *
+ *****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_ARTIST, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRINGLIST, &FileTag->artist);
+
+ /****************
+ * Album (TALB) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_ALBUM, 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->album);
+
+ /************************
+ * Part of a set (TPOS) *
+ ************************/
+ if ( (frame = id3_tag_findframe(tag,"TPOS", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->disc_number);
+
+ /********************
+ * Year (TYER/TDRC) *
+ ********************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_YEAR, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ Strip_String(string1);
+ FileTag->year = string1;
+ }
+ }
+
+ /********************************
+ * Track and Total Track (TRCK) *
+ ********************************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_TRACK, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ string2 = g_utf8_strchr(string1,-1,'/');
+
+ if (NUMBER_TRACK_FORMATED)
+ {
+ if (string2)
+ {
+ FileTag->track_total = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string2+1)); // Just to have numbers like this : '01', '05', '12', ...
+ *string2 = '\0'; // To cut string1
+ }
+ FileTag->track = g_strdup_printf("%.*d",NUMBER_TRACK_FORMATED_SPIN_BUTTON,atoi(string1)); // Just to have numbers like this : '01', '05', '12', ...
+ }else
+ {
+ if (string2)
+ {
+ FileTag->track_total = g_strdup(string2+1);
+ *string2 = '\0'; // To cut string1
+ }
+ FileTag->track = g_strdup(string1);
+ }
+ g_free(string1);
+
+ }
+ }
+
+ /****************
+ * Genre (TCON) *
+ ****************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_GENRE, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if ( string1 )
+ {
+ /*
+ * We manipulate only the name of the genre
+ * Genre is written like this :
+ * - "(<genre_id>)" -> "(3)"
+ * - "<genre_name>" -> "Dance"
+ * - "(<genre_id>)<refinement>" -> "(3)EuroDance"
+ */
+ gchar *tmp;
+ unsigned genre = 0;
+ FileTag->genre = NULL;
+
+ if ( (string1[0]=='(') && (tmp=strchr(string1,')')) && (tmp+1) && (strlen((tmp+1))>0) )
+ /* Convert a genre written as '(3)EuroDance' into 'EuroDance' */
+ {
+ FileTag->genre = g_strdup(tmp+1);
+ } else if ( (string1[0]=='(') && strchr(string1,')') )
+ {
+ /* Convert a genre written as '(3)' into 'Dance' */
+ genre = strtol(string1+1, &tmp, 10);
+ if (*tmp != ')')
+ {
+ FileTag->genre = g_strdup(string1);
+ }
+ } else
+ {
+ genre = strtol(string1, &tmp, 10);
+ if (tmp == string1)
+ FileTag->genre = g_strdup(string1);
+ }
+
+ if (!FileTag->genre
+ && id3_genre_index(genre))
+ FileTag->genre = (gchar *)id3_ucs4_utf8duplicate(id3_genre_index(genre));
+
+ g_free(string1);
+ }
+ }
+
+ /******************
+ * Comment (COMM) *
+ ******************/
+ if ( (frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, 0)) )
+ {
+ update |= libid3tag_Get_Frame_Str(frame, /* EASYTAG_ID3_FIELD_STRING | */ EASYTAG_ID3_FIELD_STRINGFULL,
+ &FileTag->comment);
+ /*{
+ gchar *comment1 = Id3tag_Get_Field(frame,ID3FN_DESCRIPTION)
+ gchar *comment2 = Id3tag_Get_Field(id3_frame,ID3FN_LANGUAGE)
+ }*/
+ }
+
+ /*******************
+ * Composer (TCOM) *
+ *******************/
+ if ( (frame = id3_tag_findframe(tag, "TCOM", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->composer);
+
+ /**************************
+ * Original artist (TOPE) *
+ **************************/
+ if ( (frame = id3_tag_findframe(tag, "TOPE", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->orig_artist);
+
+ /*******************
+ * Copyright (TCOP)*
+ *******************/
+ if ( (frame = id3_tag_findframe(tag, "TCOP", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->copyright);
+
+ /**************
+ * URL (WXXX) *
+ **************/
+ if ( (frame = id3_tag_findframe(tag, "WXXX", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_LATIN1, &FileTag->url);
+
+ /*********************
+ * Encoded by (TENC) *
+ *********************/
+ if ( (frame = id3_tag_findframe(tag, "TENC", 0)) )
+ update |= libid3tag_Get_Frame_Str(frame, ~0, &FileTag->encoded_by);
+ /* Encoded by in TXXX frames */
+ string1 = NULL;
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXX", i)); i++)
+ {
+ if (FileTag->encoded_by)
+ break;
+ tmpupdate = libid3tag_Get_Frame_Str(frame, ~0, &string1);
+ if (string1)
+ {
+ if (strcasestr(string1, EASYTAG_STRING_ENCODEDBY MULTIFIELD_SEPARATOR) == string1)
+ {
+ FileTag->encoded_by = g_strdup(&string1[sizeof(EASYTAG_STRING_ENCODEDBY) + sizeof(MULTIFIELD_SEPARATOR) - 2]);
+ g_free(string1);
+ update |= tmpupdate;
+ }else
+ g_free(string1);
+ }
+ }
+
+ /******************
+ * Picture (APIC) *
+ ******************/
+ for (i = 0; (frame = id3_tag_findframe(tag, "APIC", i)); i++)
+ {
+ Picture *pic;
+
+ pic = Picture_Allocate();
+ if (!prev_pic)
+ FileTag->picture = pic;
+ else
+ prev_pic->next = pic;
+ prev_pic = pic;
+
+ pic->data = NULL;
+
+ // Picture file data
+ for (j = 0; (field = id3_frame_field(frame, j)); j++)
+ {
+ switch (id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_BINARYDATA:
+ {
+ id3_length_t size;
+ id3_byte_t const *data;
+ data = id3_field_getbinarydata(field, &size);
+ if (pic->data)
+ g_free(pic->data);
+ if ( (pic->data = g_memdup(data, size)) )
+ pic->size = size;
+ }
+ break;
+ case ID3_FIELD_TYPE_INT8:
+ pic->type = id3_field_getint(field);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Picture description
+ update |= libid3tag_Get_Frame_Str(frame, EASYTAG_ID3_FIELD_STRING, &pic->description);
+ }
+
+ /**********************
+ * Lyrics (SYLC/USLT) *
+ **********************/
+ /** see also id3/misc_support.h **
+ if ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_SYNCEDLYRICS)) )
+ {
+ gulong size = 0;
+ guchar *data = NULL;
+ gchar *description = NULL;
+ gchar *language = NULL;
+ gint timestamp_format = 0;
+ gint sync_type = 0;
+
+ // SyncLyrics data
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_DATA)) )
+ {
+ size = ID3Field_Size(id3_field);
+ data = g_malloc(size);
+ ID3Field_GetBINARY(id3_field, data, size);
+ }
+
+ // SyncLyrics description
+ description = Id3tag_Get_Field(id3_frame, ID3FN_DESCRIPTION);
+
+ // SyncLyrics language
+ language = Id3tag_Get_Field(id3_frame, ID3FN_LANGUAGE);
+
+ // SyncLyrics timestamp field
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TIMESTAMPFORMAT)) )
+ {
+ timestamp_format = ID3Field_GetINT(id3_field);
+ }
+
+ // SyncLyrics content type
+ if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_CONTENTTYPE)) )
+ {
+ sync_type = ID3Field_GetINT(id3_field);
+ }
+
+ // Print data
+ // j.a. Pouwelse - pouwelse :
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=401873&group_id=979&atid=300979
+ {
+ char tag[255];
+ unsigned int time;
+ luint pos = 0;
+
+ g_print("SyncLyrics/description : %s\n",description);
+ g_print("SyncLyrics/language : %s\n",language);
+ g_print("SyncLyrics/timestamp format : %s (%d)\n",timestamp_format==ID3TSF_FRAME ? "ID3TSF_FRAME" : timestamp_format==ID3TSF_MS ? "ID3TSF_MS" : "" ,timestamp_format);
+ g_print("SyncLyrics/sync type : %s (%d)\n",sync_type==ID3CT_LYRICS ? "ID3CT_LYRICS" : "",sync_type);
+
+
+ g_print("SyncLyrics size : %d\n", size);
+ g_print("Lyrics/data :\n");
+ while (pos < size)
+ {
+ strcpy(tag,data+pos);
+ //g_print("txt start=%d ",pos);
+ pos+=strlen(tag)+1; // shift string and terminating \0
+ //g_print("txt end=%d ",pos);
+ memcpy(&time,data+pos,4);
+ pos+=4;
+ //g_print("%d -> %s\n",time,tag);
+ g_print("%s",tag);
+ }
+ }
+
+ } **/
+
+ if (update)
+ FileTag->saved = FALSE;
+
+ /* Free allocated data */
+ id3_file_close(file);
+
+ return TRUE;
+}
+
+
+/* Guess byteorder of UTF-16 string that was converted to 'ustr' (some ID3
+ * tags contain UTF-16 string without BOM and in fact can be UTF16BE and
+ * UTF-16LE). Function correct byteorder, if it is needed, and return new
+ * corrected utf-8 string in 'ret'.
+ * Return value of function is 0 if byteorder was not changed
+ */
+static int
+etag_guess_byteorder(const id3_ucs4_t *ustr, gchar **ret) /* XXX */
+{
+ unsigned i, len;
+ gunichar *gstr;
+ gchar *tmp, *str, *str2;
+ const gchar *charset;
+
+ if (!ustr || !*ustr)
+ {
+ if (ret)
+ *ret = NULL;
+ return 0;
+ }
+
+ if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ charset = FILE_READING_ID3V1V2_CHARACTER_SET;
+ else if (!FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET) /* XXX */
+ charset = FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET;
+ else g_get_charset(&charset);
+
+ if (!charset)
+ charset = "ISO-8859-1";
+
+ tmp = (gchar *)id3_ucs4_utf8duplicate(ustr);
+ str = g_convert(tmp, -1, charset, "UTF-8", NULL, NULL, NULL);
+ if (str)
+ {
+ g_free(str);
+ if (ret)
+ *ret = tmp;
+ else
+ free (tmp);
+ return 0; /* byteorder not changed */
+ }
+
+ for (len = 0; ustr[len]; len++);
+
+ gstr = g_try_malloc(sizeof(gunichar) * (len + 1));
+ if ( gstr == NULL )
+ {
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+ return 0;
+ }
+
+ for (i = 0; i <= len; i++)
+ gstr[i] = ((ustr[i] & 0xff00) >> 8) | ((ustr[i] & 0xff) << 8);
+ str = g_ucs4_to_utf8(gstr, len, NULL, NULL, NULL);
+ g_free(gstr);
+
+ if (str == NULL)
+ {
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+ return 0;
+ }
+
+ str2 = g_convert(str, -1, charset, "UTF-8", NULL, NULL, NULL);
+
+ if (str2 && *str2)
+ {
+ g_free(str2);
+ free(tmp);
+ if (ret)
+ *ret = str;
+ else
+ free(str);
+ return 1;
+ }
+
+ g_free(str);
+
+ if (ret)
+ *ret = tmp;
+ else
+ free(tmp);
+
+ return 0;
+}
+
+
+/* convert ucs4 string to utf-8 gchar in 'res' according to easytag charset
+ * conversion settings and field type.
+ * function return 0 if byteorder of utf-16 string was changed
+ */
+static int
+etag_ucs42gchar(const id3_ucs4_t *usrc, unsigned is_latin,
+ unsigned is_utf16, gchar **res)
+{
+ gchar *latinstr, *retstr;
+ int retval;
+
+ if (!usrc || !*usrc)
+ {
+ if (res)
+ *res = NULL;
+ return 0;
+ }
+
+ retval = 0, retstr = NULL;
+
+ if (is_latin && USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ {
+ if ((latinstr = (gchar *)id3_ucs4_latin1duplicate(usrc)))
+ {
+ retstr = convert_string(latinstr, FILE_READING_ID3V1V2_CHARACTER_SET, "UTF-8", FALSE);
+ free(latinstr);
+ }
+ }else
+ {
+ if (is_utf16)
+ {
+ retval |= etag_guess_byteorder(usrc, &retstr);
+ }else
+ {
+ retstr = (gchar *)id3_ucs4_utf8duplicate(usrc);
+ }
+ }
+
+ if (res)
+ *res = retstr;
+ else
+ free (retstr);
+
+ return retval;
+}
+
+
+static int
+libid3tag_Get_Frame_Str(const struct id3_frame *frame, unsigned etag_field_type, gchar **retstr)
+{
+ const union id3_field *field;
+ unsigned i, j, strcnt;
+ gchar *ret, *tmpstr, *tmpstr2, *latinstr;
+ unsigned field_type;
+ const id3_ucs4_t *usrc;
+ unsigned is_latin, is_utf16;
+ unsigned retval;
+
+ ret = NULL;
+ retval = 0;
+ is_latin = 1, is_utf16 = 0;
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ if (id3_field_type(field) == ID3_FIELD_TYPE_TEXTENCODING)
+ {
+ is_latin = (id3_field_gettextencoding(field) == ID3_FIELD_TEXTENCODING_ISO_8859_1);
+ is_utf16 = (id3_field_gettextencoding(field) == ID3_FIELD_TEXTENCODING_UTF_16);
+ break;
+ }
+ }
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ tmpstr = tmpstr2 = NULL;
+ switch (field_type = id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_LATIN1:
+ case ID3_FIELD_TYPE_LATIN1FULL:
+ if (field_type == ID3_FIELD_TYPE_LATIN1)
+ {
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_LATIN1))
+ continue;
+ }else
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_LATIN1FULL))
+ continue;
+ latinstr = g_strdup(field_type == ID3_FIELD_TYPE_LATIN1 ? (gchar *)id3_field_getlatin1(field) : (gchar *)id3_field_getfulllatin1(field));
+ if (USE_NON_STANDARD_ID3_READING_CHARACTER_SET)
+ {
+ tmpstr = convert_string(latinstr, FILE_READING_ID3V1V2_CHARACTER_SET, "UTF-8", FALSE);
+ free(latinstr);
+ }
+ else
+ tmpstr = latinstr;
+ break;
+
+ case ID3_FIELD_TYPE_STRING:
+ case ID3_FIELD_TYPE_STRINGFULL:
+ if (field_type == ID3_FIELD_TYPE_STRING)
+ {
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRING))
+ continue;
+ }else
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRINGFULL))
+ continue;
+ usrc = (field_type == ID3_FIELD_TYPE_STRING) ? id3_field_getstring(field) : id3_field_getfullstring(field);
+ retval |= etag_ucs42gchar(usrc, is_latin, is_utf16, &tmpstr);
+ break;
+
+ case ID3_FIELD_TYPE_STRINGLIST:
+ if (!(etag_field_type & EASYTAG_ID3_FIELD_STRINGLIST))
+ continue;
+ strcnt = id3_field_getnstrings(field);
+ for (j = 0; j < strcnt; j++)
+ {
+ retval |= etag_ucs42gchar(
+ id3_field_getstrings(field, j),
+ is_latin, is_utf16, &tmpstr );
+
+ if (tmpstr2 && *tmpstr2 && g_utf8_validate(tmpstr2, -1, NULL))
+ {
+ if (tmpstr)
+ tmpstr = g_strconcat(tmpstr, " ", tmpstr2, NULL);
+ else
+ tmpstr = g_strdup(tmpstr2);
+ }
+
+ free(tmpstr2);
+ }
+
+ default:
+ break;
+ }
+ if (tmpstr && *tmpstr && g_utf8_validate(tmpstr, -1, NULL))
+ {
+ if (ret)
+ ret = g_strconcat(ret, MULTIFIELD_SEPARATOR, tmpstr, NULL);
+ else
+ ret = g_strdup(tmpstr);
+ }
+ g_free(tmpstr);
+ }
+
+ if (retstr)
+ *retstr = ret;
+ else
+ free(ret);
+
+ return retval;
+}
+
+
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+gboolean Id3tag_Write_File_v24Tag (ET_File *ETFile)
+{
+ File_Tag *FileTag;
+ gchar *filename, *filename_utf8;
+ gchar *basename_utf8;
+ struct id3_tag *v1tag, *v2tag;
+ struct id3_frame *frame;
+ union id3_field *field;
+ gchar *string1;
+ Picture *pic;
+ unsigned i;
+ gint error = 0;
+ gboolean strip_tags = TRUE;
+ guchar genre_value = ID3_INVALID_GENRE;
+
+
+ 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;
+
+ v1tag = v2tag = NULL;
+
+ if (FILE_WRITING_ID3V2_WRITE_TAG)
+ {
+ struct id3_file *file;
+ struct id3_tag *tmptag;
+ unsigned i;
+ id3_byte_t *tmpbuf = NULL;
+
+ /* Read old v2 tag */
+ if ((file = id3_file_open(filename, ID3_FILE_MODE_READWRITE)) == NULL)
+ return FALSE;
+
+ if ((tmptag = id3_file_tag(file)) == NULL)
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+
+ id3_tag_options(tmptag, ID3_TAG_OPTION_UNSYNCHRONISATION
+ | ID3_TAG_OPTION_ID3V1
+ | ID3_TAG_OPTION_COMPRESSION
+ | ID3_TAG_OPTION_APPENDEDTAG,
+ ID3_TAG_OPTION_UNSYNCHRONISATION);
+
+ /* XXX Create new tag and copy all frames*/
+ i = id3_tag_render(tmptag, NULL);
+ if ((i > 10)
+ && (tmpbuf = g_try_malloc(i))
+ && (id3_tag_render(tmptag, tmpbuf) != 0)
+ )
+ v2tag = id3_tag_parse(tmpbuf, i);
+ g_free(tmpbuf);
+
+ if (v2tag == NULL)
+ {
+ if ((v2tag = id3_tag_new()) == NULL)
+ {
+ id3_file_close(file);
+ return FALSE;
+ }
+ }
+
+ id3_file_close(file);
+
+ /* Set padding XXX */
+ if ((v2tag->paddedsize < 1024)
+ || ((v2tag->paddedsize > 4096) && (i < 1024))
+ )
+ v2tag->paddedsize = 1024;
+
+ /* Set options */
+ id3_tag_options(v2tag,
+ ID3_TAG_OPTION_UNSYNCHRONISATION
+ | ID3_TAG_OPTION_APPENDEDTAG
+ | ID3_TAG_OPTION_ID3V1
+ | ID3_TAG_OPTION_CRC
+ | ID3_TAG_OPTION_COMPRESSION,
+ ID3_TAG_OPTION_UNSYNCHRONISATION
+ );
+ if (FILE_WRITING_ID3V2_USE_CRC32)
+ id3_tag_options(v2tag, ID3_TAG_OPTION_CRC, ~0);
+ if (FILE_WRITING_ID3V2_USE_COMPRESSION)
+ id3_tag_options(v2tag, ID3_TAG_OPTION_COMPRESSION, ~0);
+ }
+
+ if (FILE_WRITING_ID3V1_WRITE_TAG)
+ {
+ v1tag = id3_tag_new();
+ if (!v1tag)
+ return FALSE;
+ id3_tag_options(v1tag, ID3_TAG_OPTION_ID3V1, ~0);
+ }
+
+
+ /*********
+ * Title *
+ *********/
+ etag_set_tags(FileTag->title, ID3_FRAME_TITLE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /**********
+ * Artist *
+ **********/
+ etag_set_tags(FileTag->artist, ID3_FRAME_ARTIST, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /*********
+ * Album *
+ *********/
+ etag_set_tags(FileTag->album, ID3_FRAME_ALBUM, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /***************
+ * Part of set *
+ ***************/
+ etag_set_tags(FileTag->disc_number, "TPOS", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /********
+ * Year *
+ ********/
+ etag_set_tags(FileTag->year, ID3_FRAME_YEAR, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+
+ /*************************
+ * Track and Total Track *
+ *************************/
+ if ( FileTag->track
+ && FileTag->track_total
+ && *FileTag->track_total )
+ string1 = g_strconcat(FileTag->track,"/",FileTag->track_total,NULL);
+ else
+ string1 = NULL;
+
+ etag_set_tags(string1 ? string1 : FileTag->track, ID3_FRAME_TRACK, ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+ etag_set_tags(FileTag->track, ID3_FRAME_TRACK, ID3_FIELD_TYPE_STRINGLIST, v1tag, NULL, &strip_tags);
+ g_free(string1);
+
+ /*********
+ * Genre *
+ **********
+ /* Genre is written like this :
+ * - "<genre_id>" -> "3"
+ * - "<genre_name>" -> "EuroDance"
+ */
+ if (FileTag->genre)
+ genre_value = Id3tag_String_To_Genre(FileTag->genre);
+
+ if (genre_value == ID3_INVALID_GENRE)
+ string1 = g_strdup(FileTag->genre);
+ else
+ string1 = g_strdup_printf("%d",genre_value);
+
+ etag_set_tags(string1, ID3_FRAME_GENRE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+ g_free(string1);
+
+ /***********
+ * Comment *
+ ***********/
+ etag_set_tags(FileTag->comment, ID3_FRAME_COMMENT, ID3_FIELD_TYPE_STRINGFULL, v1tag, v2tag, &strip_tags);
+
+ /************
+ * Composer *
+ ************/
+ etag_set_tags(FileTag->composer, "TCOM", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*******************
+ * Original artist *
+ *******************/
+ etag_set_tags(FileTag->orig_artist, "TOPE", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*************
+ * Copyright *
+ *************/
+ etag_set_tags(FileTag->copyright, "TCOP", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+
+ /*******
+ * URL *
+ *******/
+ etag_set_tags(FileTag->url, "WXXX", ID3_FIELD_TYPE_LATIN1, NULL, v2tag, &strip_tags);
+
+ /***************
+ * Encoded by *
+ ***************/
+ if ( v2tag && FileTag->encoded_by && *FileTag->encoded_by
+ && (frame = Id3tag_findncreate_txxframe(v2tag, EASYTAG_STRING_ENCODEDBY)))
+ {
+ id3taglib_set_field(frame, EASYTAG_STRING_ENCODEDBY, ID3_FIELD_TYPE_STRING, 0, 1, 0);
+ id3taglib_set_field(frame, FileTag->encoded_by, ID3_FIELD_TYPE_STRING, 1, 0, 0);
+ strip_tags = FALSE;
+ }else
+ if (v2tag)
+ Id3tag_delete_txxframes(v2tag, EASYTAG_STRING_ENCODEDBY, 0);
+
+ /***********
+ * Picture *
+ ***********/
+ Id3tag_delete_frames(v2tag, "APIC", 0);
+
+ pic = FileTag->picture;
+
+ if (v2tag)
+ {
+ while (pic)
+ {
+ if ((frame = id3_frame_new("APIC")) == NULL)
+ continue;
+
+ id3_tag_attachframe(v2tag, frame);
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ switch (id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_LATIN1:
+ switch (Picture_Format(pic))
+ {
+ case PICTURE_FORMAT_JPEG:
+ id3_field_setlatin1(field, (id3_latin1_t const *)"image/jpeg");
+ break;
+ case PICTURE_FORMAT_PNG:
+ id3_field_setlatin1(field, (id3_latin1_t const *)"image/png");
+ break;
+ default:
+ break;
+ }
+ break;
+ case ID3_FIELD_TYPE_INT8:
+ id3_field_setint(field, pic->type);
+ break;
+ case ID3_FIELD_TYPE_BINARYDATA:
+ id3_field_setbinarydata(field, pic->data, pic->size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pic->description)
+ id3taglib_set_field(frame, pic->description, ID3_FIELD_TYPE_STRING, 0, 0, 0);
+
+ strip_tags = FALSE;
+ pic = pic->next;
+ }
+ }
+
+ /****************************************
+ * File length (in milliseconds) DISCARD*
+ ****************************************/
+
+ /*********************************
+ * Update id3v1.x and id3v2 tags *
+ *********************************/
+ error |= etag_write_tags(filename, v1tag, v2tag, strip_tags);
+
+ if (v1tag)
+ id3_tag_delete(v1tag);
+ if (v2tag)
+ id3_tag_delete(v2tag);
+
+ if (error == 0)
+ {
+ basename_utf8 = g_path_get_basename(filename_utf8);
+ Log_Print(_("Updated tag of '%s'"),basename_utf8);
+ g_free(basename_utf8);
+ }
+
+ if (error) return FALSE;
+ else return TRUE;
+
+}
+
+/* Dele all frames with 'name'
+ * begining with frame num 'start' (0-based)
+ * from tag 'tag'
+ */
+static void
+Id3tag_delete_frames(struct id3_tag *tag, const gchar *name, int start)
+{
+ struct id3_frame *frame;
+
+ if (!tag || !name || !*name)
+ return;
+
+ while ((frame = id3_tag_findframe(tag, name, start)))
+ {
+ id3_tag_detachframe(tag, frame);
+ id3_frame_delete(frame);
+ }
+
+}
+
+static void
+Id3tag_delete_txxframes(struct id3_tag *tag, const gchar *param1, int start)
+{
+ int i;
+ struct id3_frame *frame;
+ union id3_field *field;
+ const id3_ucs4_t *ucs4string;
+ gchar *str;
+
+ if (!tag || !param1 || !*param1)
+ return;
+
+ for (i = start; (frame = id3_tag_findframe(tag, "TXXX", i)); )
+ if ( (field = id3_frame_field(frame, 1))
+ && (ucs4string = id3_field_getstring(field)) )
+ {
+ str = NULL;
+ if ((str = (gchar *)id3_ucs4_latin1duplicate(ucs4string))
+ && (strcasestr(str, param1) == str) )
+ {
+ g_free(str);
+ id3_tag_detachframe(tag, frame);
+ id3_frame_delete(frame);
+ }else
+ {
+ i++;
+ g_free(str);
+ }
+ }else
+ i++;
+}
+
+/* Find first frame with name 'name' in tag 'tag'
+ * create new if not found
+ */
+static struct id3_frame *
+Id3tag_findncreate_frame(struct id3_tag *tag, const gchar *name)
+{
+ struct id3_frame *frame;
+
+ if (!tag || !name || !*name)
+ return NULL;
+
+ frame = id3_tag_findframe(tag, name, 0);
+ if (!frame)
+ {
+ if ((frame = id3_frame_new(name)) == NULL)
+ return NULL;
+ id3_tag_attachframe(tag, frame);
+ }
+
+ return frame;
+}
+
+/* Find first "TXX" (user defined text information) frame in tag 'tag'
+ * with first string parameter (name) 'param'
+ * create new if not found
+ */
+static struct id3_frame *
+Id3tag_findncreate_txxframe(struct id3_tag *tag, const gchar *param1)
+{
+ const id3_ucs4_t *ucs4string;
+ struct id3_frame *frame;
+ union id3_field *field;
+ int i;
+ gchar *str;
+
+ if (!tag || !param1 || !*param1)
+ return NULL;
+
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++)
+ if ( (field = id3_frame_field(frame, 1))
+ && (ucs4string = id3_field_getstring(field)) )
+ {
+ str = NULL;
+ if ((str = (gchar *)id3_ucs4_latin1duplicate(ucs4string))
+ && (strcasestr(str, param1) == str) )
+ {
+ g_free(str);
+ break;
+ }else
+ g_free(str);
+ }
+
+ if (frame == NULL)
+ {
+ if ((frame = id3_frame_new("TXXX")) == NULL)
+ return NULL;
+ id3_tag_attachframe(tag, frame);
+ }
+
+ return frame;
+}
+
+static int
+id3taglib_set_field(struct id3_frame *frame, const gchar *str,
+ enum id3_field_type type,
+ int num, int clear, int id3v1)
+{
+ union id3_field *field;
+ enum id3_field_type curtype;
+ id3_ucs4_t *buf;
+ gchar *latinstr, *encname;
+ enum id3_field_textencoding enc_field;
+ unsigned i;
+ unsigned is_set;
+
+ latinstr = NULL, buf = NULL;
+ is_set = 0;
+ enc_field = ID3_FIELD_TEXTENCODING_ISO_8859_1;
+
+ if (str)
+ {
+ /* Prepare str for writing according to easytag charset coversion settings */
+ if ((FILE_WRITING_ID3V2_USE_UNICODE_CHARACTER_SET == 0)
+ || (type == ID3_FIELD_TYPE_LATIN1)
+ || (type == ID3_FIELD_TYPE_LATIN1FULL)
+ || id3v1)
+ {
+ encname = NULL;
+ /* id3v1 fields converted using its own character set and iconv options */
+ if ( id3v1 )
+ {
+ if ( !FILE_WRITING_ID3V1_ICONV_OPTIONS_NO )
+ encname = g_strconcat(
+ FILE_WRITING_ID3V1_CHARACTER_SET,
+ FILE_WRITING_ID3V1_ICONV_OPTIONS_TRANSLIT ? "//TRANSLIT" : "//IGNORE",
+ NULL);
+ else
+ encname = g_strdup(FILE_WRITING_ID3V1_CHARACTER_SET);
+ } else
+ {
+ /* latin1 fields (such as URL) always converted with ISO-8859-1*/
+ if ((type != ID3_FIELD_TYPE_LATIN1) && (type != ID3_FIELD_TYPE_LATIN1FULL))
+ {
+ if ( FILE_WRITING_ID3V2_ICONV_OPTIONS_NO == 0)
+ encname = g_strconcat(
+ FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET,
+ FILE_WRITING_ID3V2_ICONV_OPTIONS_TRANSLIT ? "//TRANSLIT" : "//IGNORE",
+ NULL);
+ else
+ encname = g_strdup(FILE_WRITING_ID3V2_NO_UNICODE_CHARACTER_SET);
+ }
+ }
+
+ latinstr = convert_string(str, "UTF-8", encname ? encname : "ISO-8859-1//IGNORE", TRUE);
+ free(encname);
+ buf = id3_latin1_ucs4duplicate((id3_latin1_t const *)latinstr);
+ } else
+ {
+ if (!strcmp(FILE_WRITING_ID3V2_UNICODE_CHARACTER_SET, "UTF-16"))
+ {
+ enc_field = ID3_FIELD_TEXTENCODING_UTF_16;
+ buf = id3_utf8_ucs4duplicate((id3_utf8_t const *)str);
+ }else
+ {
+ enc_field = ID3_FIELD_TEXTENCODING_UTF_8;
+ buf = id3_utf8_ucs4duplicate((id3_utf8_t const *)str);
+ }
+ }
+ }
+
+ if (frame)
+ frame->flags &= ~ID3_FRAME_FLAG_FORMATFLAGS;
+
+ for (i = 0; (field = id3_frame_field(frame, i)); i++)
+ {
+ if (is_set && !clear)
+ break;
+
+ switch (curtype = id3_field_type(field))
+ {
+ case ID3_FIELD_TYPE_TEXTENCODING:
+ id3_field_settextencoding(field, enc_field);
+ break;
+ case ID3_FIELD_TYPE_LATIN1:
+ if (clear)
+ id3_field_setlatin1(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setlatin1(field, (id3_latin1_t const *)latinstr);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_LATIN1FULL:
+ if (clear)
+ id3_field_setfulllatin1(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setfulllatin1(field, (id3_latin1_t const *)latinstr);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRING:
+ if (clear)
+ id3_field_setstring(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRINGFULL:
+ if (clear)
+ id3_field_setfullstring(field, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if (num == 0)
+ {
+ id3_field_setfullstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ case ID3_FIELD_TYPE_STRINGLIST:
+ if (clear)
+ id3_field_setstrings(field, 0, NULL);
+ if ((type == curtype) && !is_set)
+ {
+ if ((num == 0) && (buf))
+ {
+ id3_field_addstring(field, buf);
+ is_set = 1;
+ }else
+ num--;
+ }
+ break;
+ default:
+ break;
+ }
+ if (is_set)
+ {
+ free(latinstr);
+ free(buf);
+ latinstr = NULL, buf = NULL;
+ }
+ }
+
+ if (latinstr || buf)
+ {
+ free(latinstr);
+ free(buf);
+ return 1;
+ } else
+ return 0;
+}
+
+
+static int
+etag_set_tags(const gchar *str,
+ const char *frame_name,
+ enum id3_field_type field_type,
+ struct id3_tag *v1tag,
+ struct id3_tag *v2tag,
+ gboolean *strip_tags)
+{
+ struct id3_frame *ftmp;
+
+ if ( str && *str )
+ {
+ *strip_tags = FALSE;
+
+ if (v2tag
+ && (ftmp = Id3tag_findncreate_frame(v2tag, frame_name)))
+ id3taglib_set_field(ftmp, str, field_type, 0, 1, 0);
+ if (v1tag
+ && (ftmp = Id3tag_findncreate_frame(v1tag, frame_name)))
+ id3taglib_set_field(ftmp, str, field_type, 0, 1, 1);
+ }else
+ if (v2tag)
+ Id3tag_delete_frames(v2tag, frame_name, 0);
+
+ return 0;
+}
+
+static int
+etag_write_tags(const gchar *filename, const struct id3_tag *v1tag, const struct id3_tag *v2tag, gboolean strip_tags)
+{
+ id3_byte_t *v1buf, *v2buf;
+ id3_length_t v1size = 0, v2size = 0;
+ char tmp[ID3_TAG_QUERYSIZE];
+ int fd;
+ int curpos;
+ long filev2size, ctxsize;
+ char *ctx = NULL;
+ int err = 0;
+
+ v1buf = v2buf = NULL;
+ if ( !strip_tags )
+ {
+ /* Render v1 tag */
+ if (v1tag)
+ {
+ v1size = id3_tag_render(v1tag, NULL);
+ if (v1size == 128)
+ {
+ v1buf = g_try_malloc(v1size);
+ if (id3_tag_render(v1tag, v1buf) != v1size)
+ {
+ /* NOTREACHED */
+ g_free(v1buf);
+ v1buf = NULL;
+ }
+ }
+ }
+
+ /* Render v2 tag */
+ if (v2tag)
+ {
+ v2size = id3_tag_render(v2tag, NULL);
+ if (v2size > 10)
+ {
+ v2buf = g_try_malloc0(v2size);
+ if ((v2size = id3_tag_render(v2tag, v2buf)) == 0)
+ {
+ /* NOTREACHED */
+ g_free(v2buf);
+ v2buf = NULL;
+ }
+ }
+ }
+ }
+ if (v1buf == NULL)
+ v1size = 0;
+ if (v2buf == NULL)
+ v2size = 0;
+
+ if ((fd = open(filename, O_RDWR)) < 0)
+ {
+ err = errno;
+ g_free(v1buf);
+ g_free(v2buf);
+ return (err);
+ }
+
+ err = 1;
+
+ /* Handle Id3v1 tag */
+ if (lseek(fd, -128, SEEK_END) < 0)
+ goto out;
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+
+ if ( (tmp[0] == 'T')
+ && (tmp[1] == 'A')
+ && (tmp[2] == 'G')
+ )
+ {
+ if (lseek(fd, -128, SEEK_END) < 0)
+ goto out;
+ }else
+ if (lseek(fd, 0, SEEK_END) < 0)
+ goto out;
+
+ /* Search id3v2 tags at the end of the file (before any ID3v1 tag) */
+ /* XXX: Unsafe */
+ if (lseek(fd, -ID3_TAG_QUERYSIZE, SEEK_CUR) >= 0)
+ {
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+ filev2size = id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE);
+ if ((filev2size > 10)
+ && lseek(fd, -filev2size, SEEK_CUR))
+ {
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+ if (id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE) != filev2size)
+ lseek(fd, -ID3_TAG_QUERYSIZE - filev2size, SEEK_CUR);
+ else
+ lseek(fd, -ID3_TAG_QUERYSIZE, SEEK_CUR);
+ }
+ }
+
+ /* Write id3v1 tag */
+ if (v1buf)
+ if ( write(fd, v1buf, v1size) != v1size)
+ goto out;
+
+ /* Truncate file (strip tags) */
+ if ((curpos = lseek(fd, 0, SEEK_CUR)) <= 0 )
+ goto out;
+ if ((err = ftruncate(fd, curpos)))
+ goto out;
+
+ /* Handle Id3v2 tag */
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+
+ if (read(fd, tmp, ID3_TAG_QUERYSIZE) != ID3_TAG_QUERYSIZE)
+ goto out;
+
+ filev2size = id3_tag_query((id3_byte_t const *)tmp, ID3_TAG_QUERYSIZE);
+
+ if ( (filev2size == 0)
+ && (v2size == 0))
+ goto out;
+
+ if (filev2size == v2size)
+ {
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+ if (write(fd, v2buf, v2size) != v2size)
+ goto out;
+ }else
+ {
+ /* XXX */
+ ctxsize = lseek(fd, 0, SEEK_END) - filev2size;
+ if ((ctx = g_try_malloc(ctxsize)) == NULL)
+ goto out;
+ if (lseek(fd, filev2size, SEEK_SET) < 0)
+ goto out;
+ if (read(fd, ctx, ctxsize) != ctxsize)
+ goto out;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ goto out;
+ if (v2buf)
+ write(fd, v2buf, v2size);
+
+ if (write(fd, ctx, ctxsize) != ctxsize)
+ {
+ g_print("OOPS\n");
+ goto out;
+ }
+
+ if ((curpos = lseek(fd, 0, SEEK_CUR)) <= 0 )
+ goto out;
+ if ((err = ftruncate(fd, curpos)))
+ goto out;
+ }
+
+ err = 0;
+out:
+ g_free(ctx);
+ lseek(fd, 0, SEEK_SET);
+ close(fd);
+ g_free(v1buf);
+ g_free(v2buf);
+ return err;
+}
+
+#endif /* ENABLE_MP3 */