[easytag/wip/dsf-support: 3/7] Write ID3v2.4 tags to DSF files



commit 72a5803a886d98f5fae149ade058153383ca2de4
Author: David King <amigadave amigadave com>
Date:   Sun Nov 9 23:53:05 2014 +0000

    Write ID3v2.4 tags to DSF files
    
    https://bugzilla.gnome.org/show_bug.cgi?id=708368

 src/tag_area.c        |    5 +
 src/tags/dsf_tag.c    |  367 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/tags/id3_tag.h    |    1 +
 src/tags/id3v24_tag.c |  273 +++++++++++++++++++------------------
 4 files changed, 510 insertions(+), 136 deletions(-)
---
diff --git a/src/tag_area.c b/src/tag_area.c
index c20c2ab..b35c619 100644
--- a/src/tag_area.c
+++ b/src/tag_area.c
@@ -3036,6 +3036,11 @@ et_tag_area_display_et_file (EtTagArea *self,
             gtk_label_set_text (GTK_LABEL (priv->label), _("MP4/M4A/AAC Tag"));
             break;
 #endif
+#ifdef ENABLE_MP3
+        case DSF_TAG:
+            gtk_label_set_text (GTK_LABEL (priv->label), _("ID3 Tag"));
+            break;
+#endif
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
             gtk_label_set_text (GTK_LABEL (priv->label), _("Wavpack Tag"));
diff --git a/src/tags/dsf_tag.c b/src/tags/dsf_tag.c
index 5d5139e..d9ac7eb 100644
--- a/src/tags/dsf_tag.c
+++ b/src/tags/dsf_tag.c
@@ -36,6 +36,26 @@
 
 #define DSF_METADATA_CHUNK_OFFSET 20
 
+/*
+ * guint64_to_le_bytes:
+ * @field: the 64-bit integer to convert
+ * @str: pointer to a buffer of at least 8 bytes
+ *
+ * Write the 64-bit integer to the next 8 characters, in little-endian byte
+ * order.
+ */
+static void
+guint64_to_le_bytes (guint64 field,
+                     guchar *str)
+{
+    gsize i;
+
+    for (i = 0; i < 8; i++)
+    {
+        str[i] = (field >> i * 8) & 0xFF;
+    }
+}
+
 gboolean
 et_dsf_tag_read_file_tag (GFile *file,
                           File_Tag *FileTag,
@@ -169,6 +189,7 @@ et_dsf_tag_read_file_tag (GFile *file,
         unsigned version = id3_tag_version (tag);
 
 #ifdef ENABLE_ID3LIB
+#if 0 /* Disable tag downgrading from 2.4 until id3lib render is supported. */
         /* Besides upgrading old tags, downgrade ID3v2.4 to ID3v2.3 */
         if (g_settings_get_boolean (MainSettings, "id3v2-version-4"))
         {
@@ -180,6 +201,7 @@ et_dsf_tag_read_file_tag (GFile *file,
                       | (ID3_TAG_VERSION_MAJOR (version) == 4));
         }
 #else
+#endif
         update = (ID3_TAG_VERSION_MAJOR (version) < 4);
 #endif
     }
@@ -220,12 +242,349 @@ gboolean
 et_dsf_tag_write_file_tag (const ET_File *ETFile,
                            GError **error)
 {
-    g_return_val_if_fail (ETFile != NULL, FALSE);
+    const File_Tag *FileTag;
+    const gchar *filename;
+    GFile *file;
+    GFileIOStream *iostream;
+    GInputStream *istream;
+    GOutputStream *ostream;
+    guchar header[DSF_HEADER_LENGTH];
+    gsize bytes_read;
+    guint64 file_size_header;
+    GFileInfo *info;
+    goffset file_size;
+    guint64 id3_offset;
+    GSeekable *seekable;
+    guchar id3_query[ID3_TAG_QUERYSIZE];
+    long tagsize;
+    goffset eos_offset;
+    struct id3_tag *tag;
+    gboolean strip_tags = TRUE;
+    long new_tagsize;
+    guchar *new_tag_buffer;
+    gsize bytes_written;
+
+    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
     g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-    /* TODO: Implement! */
-    /* TODO: Read header, seek to ID3v2 offset, truncate file, write new tag,
-     * using ID3v2.3 or 2.4 depending on id3v2-version-4 setting. */
+    FileTag = (File_Tag *)ETFile->FileTag->data;
+    filename = ((File_Name *)ETFile->FileNameCur->data)->value;
+
+    file = g_file_new_for_path (filename);
+    iostream = g_file_open_readwrite (file, NULL, error);
+    g_object_unref (file);
+
+    if (iostream == NULL)
+    {
+        return FALSE;
+    }
+
+    istream = g_io_stream_get_input_stream (G_IO_STREAM (iostream));
+
+    /* Read the complete header from the file. */
+    if (!g_input_stream_read_all (istream, &header, DSF_HEADER_LENGTH,
+                                  &bytes_read, NULL, error))
+    {
+        g_debug ("Only %" G_GSIZE_FORMAT " bytes out of %d "
+                 "bytes were read", bytes_read, DSF_HEADER_LENGTH);
+        goto err;
+    }
+
+    if (memcmp (&header, DSF_HEADER_MAGIC, 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSF file"));
+        goto err;
+    }
+
+    if (memcmp (&header[28], DSF_HEADER_FORMAT_MAGIC, 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSF file"));
+        goto err;
+    }
+
+    /* 12 (8 bytes) total file size. */
+    file_size_header = guint64_from_le_bytes (&header[DSF_HEADER_FILE_SIZE_OFFSET]);
+
+    info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (istream),
+                                           G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                           NULL, error);
+
+    if (info == NULL)
+    {
+        goto err;
+    }
+
+    file_size = g_file_info_get_size (info);
+    g_object_unref (info);
+
+    if (file_size_header != file_size)
+    {
+        g_debug ("DSF file is %" G_GUINT64_FORMAT
+                 " bytes, but the file size stored in the header is %"
+                 G_GOFFSET_FORMAT " bytes", file_size, file_size_header);
+    }
+
+    /* 20 (8 bytes) metadata chunk offset, or 0 if no tag is present. */
+    id3_offset = guint64_from_le_bytes (&header[DSF_METADATA_CHUNK_OFFSET]);
+
+    if (id3_offset >= file_size)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Invalid DSF header"));
+        goto err;
+    }
+
+    seekable = G_SEEKABLE (istream);
+
+    if (!g_seekable_can_seek (seekable))
+    {
+        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+                     g_strerror (EBADF));
+        goto err;
+    }
+
+    if (id3_offset == 0)
+    {
+        /* No ID3v2 tag. */
+        id3_offset = file_size;
+
+        tag = id3_tag_new ();
+        tagsize = 0;
+    }
+    else
+    {
+        guchar *id3_buffer;
+
+        /* Read existing ID3v2 tag. */
+
+        /* Seek to the start of the metadata chunk. */
+        if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
+        {
+            goto err;
+        }
+
+        if (!g_input_stream_read_all (istream, id3_query, ID3_TAG_QUERYSIZE,
+                                      &bytes_read, NULL, error))
+        {
+            goto err;
+        }
+        else if (bytes_read != ID3_TAG_QUERYSIZE)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+                         _("Error reading tags from file"));
+            goto err;
+        }
+
+        if ((tagsize = id3_tag_query ((id3_byte_t const *)id3_query,
+                                      ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+                         _("Error reading tags from file"));
+            goto err;
+        }
+
+        id3_buffer = g_malloc (tagsize);
+        /* Copy the query buffer. */
+        memcpy (id3_buffer, id3_query, ID3_TAG_QUERYSIZE);
+
+        if (!g_input_stream_read_all (istream, &id3_buffer[ID3_TAG_QUERYSIZE],
+                                      tagsize - ID3_TAG_QUERYSIZE, &bytes_read,
+                                      NULL, error))
+        {
+            g_free (id3_buffer);
+            goto err;
+        }
+        else if (bytes_read != tagsize - ID3_TAG_QUERYSIZE)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+                         _("Error reading tags from file"));
+            g_free (id3_buffer);
+            goto err;
+        }
+
+        /* Create an empty tag if there was a problem reading the existing
+         * tag. */
+        if (!(tag = id3_tag_parse ((id3_byte_t const *)id3_buffer, tagsize)))
+        {
+            tag = id3_tag_new ();
+        }
+
+        g_free (id3_buffer);
+
+        /* TODO: Check for an empty tag? */
+    }
+
+    /* No reading from this point forward. */
+    g_input_stream_close (istream, NULL, NULL);
+
+    ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
+    seekable = G_SEEKABLE (ostream);
+
+    /* Set padding. */
+    if ((tag->paddedsize < 1024) || ((tag->paddedsize > 4096)
+                                     && (tagsize < 1024)))
+    {
+        tag->paddedsize = 1024;
+    }
+
+    /* Set options. */
+    id3_tag_options (tag, ID3_TAG_OPTION_UNSYNCHRONISATION
+                     | ID3_TAG_OPTION_APPENDEDTAG
+                     | ID3_TAG_OPTION_ID3V1
+                     | ID3_TAG_OPTION_CRC
+                     | ID3_TAG_OPTION_COMPRESSION,
+                     0);
+
+    if (g_settings_get_boolean (MainSettings, "id3v2-crc32"))
+    {
+        id3_tag_options (tag, ID3_TAG_OPTION_CRC, ~0);
+    }
+
+    if (g_settings_get_boolean (MainSettings, "id3v2-compression"))
+    {
+        id3_tag_options (tag, ID3_TAG_OPTION_COMPRESSION, ~0);
+    }
+
+    /* Seek to the start of the metadata chunk. */
+    if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
+    {
+        goto err;
+    }
+
+    /* Only set the IDv2 tag. */
+    et_id3tag_set_id3_tag_from_file_tag (FileTag, NULL, tag, &strip_tags);
+
+    /* FIXME; For ID3v2.3 support, add ID3Tag_Render() to id3lib c_wrapper.cpp:
+     * http://sourceforge.net/p/id3lib/patches/75/ */
+
+    /* Truncate the file and update the metadata offset if the tags should be
+     * stripped. */
+    if (strip_tags)
+    {
+        guchar metadata_offset[8] = { 0, };
+
+        id3_tag_delete (tag);
+
+        if (!g_seekable_can_truncate (seekable))
+        {
+            g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+                         g_strerror (EBADF));
+            goto err;
+        }
+
+        if (!g_seekable_truncate (seekable, id3_offset, NULL, error))
+        {
+            goto err;
+        }
+
+        if (!g_seekable_seek (seekable, DSF_METADATA_CHUNK_OFFSET, G_SEEK_SET,
+                              NULL, error))
+        {
+            goto err;
+        }
+
+        if (!g_output_stream_write_all (ostream, metadata_offset, 8,
+                                        &bytes_written, NULL, error))
+        {
+            goto err;
+        }
+        else if (bytes_written != 8)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         _("Error writing tags to file"));
+            goto err;
+        }
+
+        g_object_unref (iostream);
+        return TRUE;
+    }
+
+    new_tagsize = id3_tag_render (tag, NULL);
+    new_tag_buffer = g_malloc (new_tagsize);
+
+    if ((new_tagsize = id3_tag_render (tag, new_tag_buffer)) == 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error writing tags to file"));
+        id3_tag_delete (tag);
+        g_free (new_tag_buffer);
+        goto err;
+    }
+
+    id3_tag_delete (tag);
+
+    /* Only truncate the file if the new tag is smaller than the old one. */
+    if (new_tagsize < tagsize)
+    {
+        if (!g_seekable_can_truncate (seekable))
+        {
+            g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_BADF, "%s",
+                         g_strerror (EBADF));
+            g_free (new_tag_buffer);
+            goto err;
+        }
+
+        if (!g_seekable_truncate (seekable, id3_offset + new_tagsize, NULL,
+                                  error))
+        {
+            g_free (new_tag_buffer);
+            goto err;
+        }
+    }
+
+    if (!g_output_stream_write_all (ostream, new_tag_buffer, new_tagsize,
+                                    &bytes_written, NULL, error))
+    {
+        g_free (new_tag_buffer);
+        goto err;
+    }
+    else if (bytes_written != new_tagsize)
+    {
+        g_free (new_tag_buffer);
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error writing tags to file"));
+        goto err;
+    }
+
+    g_free (new_tag_buffer);
+
+    eos_offset = g_seekable_tell (seekable);
+
+    /* If the new tag caused the file to change in size, update the size in the
+     * DSF header. */
+    if (eos_offset != file_size)
+    {
+        guchar new_file_size[8];
+        gsize bytes_written;
+
+        if (!g_seekable_seek (seekable, DSF_HEADER_FILE_SIZE_OFFSET,
+                              G_SEEK_SET, NULL, error))
+        {
+            goto err;
+        }
+
+        guint64_to_le_bytes (eos_offset, &new_file_size[0]);
+
+        if (!g_output_stream_write_all (ostream, new_file_size, 8,
+                                        &bytes_written, NULL, error))
+        {
+            goto err;
+        }
+        else if (bytes_written != 8)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         _("Error writing tags to file"));
+            goto err;
+        }
+    }
+
+    g_object_unref (iostream);
+    return TRUE;
+
+err:
+    g_object_unref (iostream);
     return FALSE;
 }
 
diff --git a/src/tags/id3_tag.h b/src/tags/id3_tag.h
index 5f74991..342d22e 100644
--- a/src/tags/id3_tag.h
+++ b/src/tags/id3_tag.h
@@ -41,6 +41,7 @@ gchar *et_id3tag_get_tpos_from_file_tag (const File_Tag *file_tag);
 #ifdef ENABLE_MP3
 #include <id3tag.h>
 gboolean et_id3tag_fill_file_tag_from_id3tag (struct id3_tag *tag, File_Tag *file_tag);
+void et_id3tag_set_id3_tag_from_file_tag (const File_Tag *FileTag, struct id3_tag *v1tag, struct id3_tag 
*v2tag, gboolean *strip_tags);
 #endif /* ENABLE_MP3 */
 
 G_END_DECLS
diff --git a/src/tags/id3v24_tag.c b/src/tags/id3v24_tag.c
index b1347d2..da585ff 100644
--- a/src/tags/id3v24_tag.c
+++ b/src/tags/id3v24_tag.c
@@ -836,145 +836,37 @@ libid3tag_Get_Frame_Str (const struct id3_frame *frame,
     return retval;
 }
 
-
-/*
- * Write the ID3 tags to the file. Returns TRUE on success, else 0.
- */
-gboolean
-id3tag_write_file_v24tag (const ET_File *ETFile,
-                          GError **error)
+void
+et_id3tag_set_id3_tag_from_file_tag (const File_Tag *FileTag,
+                                     struct id3_tag *v1tag,
+                                     struct id3_tag *v2tag,
+                                     gboolean *strip_tags)
 {
-    const File_Tag *FileTag;
-    const gchar *filename;
-    struct id3_tag   *v1tag, *v2tag;
     struct id3_frame *frame;
-    union id3_field  *field;
-    gchar            *string1;
-    Picture          *pic;
-    gboolean strip_tags = TRUE;
+    union id3_field *field;
+    gchar *string1;
     guchar genre_value = ID3_INVALID_GENRE;
-    gboolean success;
-
-    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
-    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-    FileTag       = (File_Tag *)ETFile->FileTag->data;
-    filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
-
-    v1tag = v2tag = NULL;
-
-    /* Write ID3v2 tag. */
-    if (g_settings_get_boolean (MainSettings, "id3v2-enabled"))
-    {
-        struct id3_file *file;
-        struct id3_tag *tmptag;
-        unsigned tagsize;
-        id3_byte_t *tmpbuf = NULL;
-
-        /* Read old v2 tag */
-        if ((file = id3_file_open(filename, ID3_FILE_MODE_READWRITE)) == NULL)
-        {
-            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                         _("Error reading tags from file"));
-            return FALSE;
-        }
-
-        if ((tmptag = id3_file_tag(file)) == NULL)
-        {
-            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                         _("Error reading tags from file"));
-            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); // Taglib doesn't support frames with 
unsynchronisation (patch from Alexey Illarionov) http://bugs.kde.org/show_bug.cgi?id=138829
-                        0);
-
-        /* XXX Create new tag and copy all frames*/
-        tagsize = id3_tag_render(tmptag, NULL);
-        if ((tagsize > 10)
-        && (tmpbuf = g_try_malloc(tagsize))
-        && (id3_tag_render(tmptag, tmpbuf) != 0)
-        )
-            v2tag = id3_tag_parse(tmpbuf, tagsize);
-        g_free(tmpbuf);
-
-        if (v2tag == NULL)
-        {
-            if ((v2tag = id3_tag_new()) == NULL)
-            {
-                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                             _("Error reading tags from file"));
-                id3_file_close(file);
-                return FALSE;
-            }
-        }
-
-        id3_file_close(file);
-
-        /* Set padding  XXX */
-        if ((v2tag->paddedsize < 1024)
-        || ((v2tag->paddedsize > 4096) && (tagsize < 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); // Taglib doesn't support frames with 
unsynchronisation (patch from Alexey Illarionov) http://bugs.kde.org/show_bug.cgi?id=138829
-                        0);
-        
-        if (g_settings_get_boolean (MainSettings, "id3v2-crc32"))
-        {
-            id3_tag_options (v2tag, ID3_TAG_OPTION_CRC, ~0);
-        }
-        if (g_settings_get_boolean (MainSettings, "id3v2-compression"))
-        {
-            id3_tag_options (v2tag, ID3_TAG_OPTION_COMPRESSION, ~0);
-        }
-    }
-
-    /* Write ID3v1 tag. */
-    if (g_settings_get_boolean (MainSettings, "id3v1-enabled"))
-    {
-        v1tag = id3_tag_new();
-        if (!v1tag)
-        {
-            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                         _("Error reading tags from file"));
-            return FALSE;
-        }
-        
-        id3_tag_options(v1tag, ID3_TAG_OPTION_ID3V1, ~0);
-    }
-
+    Picture *pic;
 
     /*********
      * Title *
      *********/
-    etag_set_tags(FileTag->title, ID3_FRAME_TITLE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+    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);
+    etag_set_tags(FileTag->artist, ID3_FRAME_ARTIST, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, strip_tags);
 
     /**********
      * Album Artist *
      **********/
-    etag_set_tags(FileTag->album_artist, "TPE2", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+    etag_set_tags(FileTag->album_artist, "TPE2", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, strip_tags);
 
     /*********
      * Album *
      *********/
-    etag_set_tags(FileTag->album, ID3_FRAME_ALBUM, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+    etag_set_tags(FileTag->album, ID3_FRAME_ALBUM, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, strip_tags);
 
     /***************
      * Part of set *
@@ -983,14 +875,14 @@ id3tag_write_file_v24tag (const ET_File *ETFile,
     {
         string1 = et_id3tag_get_tpos_from_file_tag (FileTag);
         etag_set_tags (string1, "TPOS", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag,
-                       &strip_tags);
+                       strip_tags);
         g_free (string1);
     }
 
     /********
      * Year *
      ********/
-    etag_set_tags(FileTag->year, ID3_FRAME_YEAR, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+    etag_set_tags(FileTag->year, ID3_FRAME_YEAR, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, strip_tags);
 
     /*************************
      * Track and Total Track *
@@ -1002,8 +894,8 @@ id3tag_write_file_v24tag (const ET_File *ETFile,
     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);
+    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);
 
     /*********
@@ -1026,33 +918,33 @@ id3tag_write_file_v24tag (const ET_File *ETFile,
         string1 = g_strdup_printf ("(%d)",genre_value);
     }
 
-    etag_set_tags(string1, ID3_FRAME_GENRE, ID3_FIELD_TYPE_STRINGLIST, v1tag, v2tag, &strip_tags);
+    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);
+    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);
+    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);
+    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);
+    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);
+    etag_set_tags(FileTag->url, "WXXX", ID3_FIELD_TYPE_LATIN1, NULL, v2tag, strip_tags);
 
     /***************
      * Encoded by  *
@@ -1065,7 +957,7 @@ id3tag_write_file_v24tag (const ET_File *ETFile,
     //    strip_tags = FALSE;
     //}else
     // Save encoder name in TENC frame instead of the TXX frame
-    etag_set_tags(FileTag->encoded_by, "TENC", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, &strip_tags);
+    etag_set_tags(FileTag->encoded_by, "TENC", ID3_FIELD_TYPE_STRINGLIST, NULL, v2tag, strip_tags);
     if (v2tag)
         Id3tag_delete_txxframes(v2tag, EASYTAG_STRING_ENCODEDBY, 0);
 
@@ -1116,6 +1008,123 @@ id3tag_write_file_v24tag (const ET_File *ETFile,
      * File length (in milliseconds) DISCARD*
      ****************************************/
 
+}
+
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+gboolean
+id3tag_write_file_v24tag (const ET_File *ETFile,
+                          GError **error)
+{
+    const File_Tag *FileTag;
+    const gchar *filename;
+    struct id3_tag   *v1tag, *v2tag;
+    gboolean strip_tags = TRUE;
+    gboolean success;
+
+    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    FileTag       = (File_Tag *)ETFile->FileTag->data;
+    filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
+
+    v1tag = v2tag = NULL;
+
+    /* Write ID3v2 tag. */
+    if (g_settings_get_boolean (MainSettings, "id3v2-enabled"))
+    {
+        struct id3_file *file;
+        struct id3_tag *tmptag;
+        unsigned tagsize;
+        id3_byte_t *tmpbuf = NULL;
+
+        /* Read old v2 tag */
+        if ((file = id3_file_open(filename, ID3_FILE_MODE_READWRITE)) == NULL)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         _("Error reading tags from file"));
+            return FALSE;
+        }
+
+        if ((tmptag = id3_file_tag(file)) == NULL)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         _("Error reading tags from file"));
+            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); // Taglib doesn't support frames with 
unsynchronisation (patch from Alexey Illarionov) http://bugs.kde.org/show_bug.cgi?id=138829
+                        0);
+
+        /* XXX Create new tag and copy all frames*/
+        tagsize = id3_tag_render(tmptag, NULL);
+        if ((tagsize > 10)
+        && (tmpbuf = g_try_malloc(tagsize))
+        && (id3_tag_render(tmptag, tmpbuf) != 0)
+        )
+            v2tag = id3_tag_parse(tmpbuf, tagsize);
+        g_free(tmpbuf);
+
+        if (v2tag == NULL)
+        {
+            if ((v2tag = id3_tag_new()) == NULL)
+            {
+                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                             _("Error reading tags from file"));
+                id3_file_close(file);
+                return FALSE;
+            }
+        }
+
+        id3_file_close(file);
+
+        /* Set padding  XXX */
+        if ((v2tag->paddedsize < 1024)
+        || ((v2tag->paddedsize > 4096) && (tagsize < 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); // Taglib doesn't support frames with 
unsynchronisation (patch from Alexey Illarionov) http://bugs.kde.org/show_bug.cgi?id=138829
+                        0);
+
+        if (g_settings_get_boolean (MainSettings, "id3v2-crc32"))
+        {
+            id3_tag_options (v2tag, ID3_TAG_OPTION_CRC, ~0);
+        }
+        if (g_settings_get_boolean (MainSettings, "id3v2-compression"))
+        {
+            id3_tag_options (v2tag, ID3_TAG_OPTION_COMPRESSION, ~0);
+        }
+    }
+
+    /* Write ID3v1 tag. */
+    if (g_settings_get_boolean (MainSettings, "id3v1-enabled"))
+    {
+        v1tag = id3_tag_new();
+        if (!v1tag)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         _("Error reading tags from file"));
+            return FALSE;
+        }
+
+        id3_tag_options(v1tag, ID3_TAG_OPTION_ID3V1, ~0);
+    }
+
+    et_id3tag_set_id3_tag_from_file_tag (FileTag, v1tag, v2tag, &strip_tags);
+
     /*********************************
      * Update id3v1.x and id3v2 tags *
      *********************************/
@@ -1406,7 +1415,7 @@ id3taglib_set_field(struct id3_frame *frame,
 }
 
 
-static int
+int
 etag_set_tags (const gchar *str,
                const char *frame_name,
                enum id3_field_type field_type,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]