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



commit 784608c45b8012a55d36ac73b00c1996569d87b3
Author: David King <amigadave amigadave com>
Date:   Thu Nov 13 16:59:45 2014 +0000

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

 src/tags/dsf_tag.c            |  484 +++++++++++++++++++++++++++++------------
 src/tags/id3_tag.c            |  235 ++++++++++-----------
 src/tags/id3_tag.h            |    6 +
 src/tags/id3lib/c_wrapper.cpp |   24 ++
 src/tags/id3lib/id3_bugfix.h  |    2 +
 5 files changed, 491 insertions(+), 260 deletions(-)
---
diff --git a/src/tags/dsf_tag.c b/src/tags/dsf_tag.c
index d9ac7eb..1a80b1e 100644
--- a/src/tags/dsf_tag.c
+++ b/src/tags/dsf_tag.c
@@ -34,6 +34,11 @@
 #include <id3tag.h>
 #include "id3_tag.h"
 
+#ifdef ENABLE_ID3LIB
+#include <id3.h>
+#include "id3lib/id3_bugfix.h"
+#endif
+
 #define DSF_METADATA_CHUNK_OFFSET 20
 
 /*
@@ -189,7 +194,6 @@ 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"))
         {
@@ -201,7 +205,6 @@ et_dsf_tag_read_file_tag (GFile *file,
                       | (ID3_TAG_VERSION_MAJOR (version) == 4));
         }
 #else
-#endif
         update = (ID3_TAG_VERSION_MAJOR (version) < 4);
 #endif
     }
@@ -238,6 +241,325 @@ err:
     return FALSE;
 }
 
+#ifdef ENABLE_ID3LIB
+
+static gboolean
+et_dsf_tag_write_id3v23 (const File_Tag *FileTag,
+                         const guchar *id3_buffer,
+                         long tagsize,
+                         GOutputStream *ostream,
+                         GError **error)
+{
+    GSeekable *seekable;
+    goffset id3_offset;
+    ID3Tag *tag;
+    gboolean strip_tags = TRUE;
+    size_t new_tagsize;
+    guchar *new_tag_buffer;
+    gsize bytes_written;
+
+    /* TODO: Call id3tag_check_if_id3lib_is_buggy(). */
+
+    seekable = G_SEEKABLE (ostream);
+    /* Offset of the ID3v2 tag is at the current stream position. */
+    id3_offset = g_seekable_tell (seekable);
+
+    if ((tag = ID3Tag_New ()) == NULL)
+    {
+        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
+                     g_strerror (ENOMEM));
+        goto err;
+    }
+
+    /* Copy the header out to a separate buffer, as id3lib expects the header
+     * and remainder of the tag to be processed separately. */
+    if (tagsize > ID3_TAGHEADERSIZE)
+    {
+        const guchar id3_header[ID3_TAGHEADERSIZE];
+        ID3_Err id3_err;
+
+        memcpy (&id3_header, id3_buffer, ID3_TAGHEADERSIZE);
+
+        if ((id3_err = ID3Tag_Parse (tag, id3_header,
+                                     &id3_buffer[ID3_TAGHEADERSIZE]))
+            != ID3E_NoError)
+        {
+            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                         Id3tag_Get_Error_Message (id3_err));
+            ID3Tag_Delete (tag);
+            goto err;
+        }
+    }
+
+    ID3Tag_SetPadding (tag, TRUE);
+
+    /* Fill the tag with the new values. */
+    et_id3tag_set_id3tag_from_file_tag (FileTag, tag, &strip_tags);
+
+    /* Truncate the file and update the metadata offset if the tags should be
+     * stripped. */
+    if (strip_tags)
+    {
+        guchar metadata_offset[8] = { 0, };
+
+        ID3Tag_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;
+        }
+
+        return TRUE;
+    }
+
+    new_tagsize = ID3Tag_Size (tag);
+
+    /* FIXME: This is probably a bug in id3lib, as a size of 0 should only be
+     * returned if there are no valid frames, and frames were added earlier in
+     * et_id3tag_set_id3tag_from_file_tag(). */
+    if (new_tagsize == 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error writing tags to file"));
+        ID3Tag_Delete (tag);
+        goto err;
+    }
+
+    /* FIXME: The return vlaue of ID3Tag_Size() should include the header and
+     * tag size, so this looks like an overallocation. However, Valgrind
+     * complains about reading into unallocated memory otherwise. */
+    new_tag_buffer = g_malloc0 (new_tagsize + ID3_TAGHEADERSIZE);
+    /* The call to ID3Tag_Size() gives an overestimate, but ID3Tag_Render()
+     * returns the real size of the tag. */
+    new_tagsize = ID3Tag_Render (tag, new_tag_buffer, ID3TT_ID3V2);
+
+    ID3Tag_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_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error writing tags to file"));
+        g_free (new_tag_buffer);
+        goto err;
+    }
+
+    g_free (new_tag_buffer);
+    return TRUE;
+
+err:
+    return FALSE;
+}
+
+#endif
+
+static gboolean
+et_dsf_tag_write_id3v24 (const File_Tag *FileTag,
+                         const guchar *id3_buffer,
+                         long tagsize,
+                         GOutputStream *ostream,
+                         GError **error)
+{
+    struct id3_tag *tag;
+    GSeekable *seekable;
+    gboolean strip_tags = TRUE;
+    gsize bytes_written;
+    guchar *new_tag_buffer;
+    long new_tagsize;
+    goffset id3_offset;
+
+    if (tagsize == 0)
+    {
+        /* No ID3v2 tag. */
+        tag = id3_tag_new ();
+    }
+    else
+    {
+        /* 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 ();
+        }
+    }
+
+    seekable = G_SEEKABLE (ostream);
+    /* Offset of the ID3v2 tag is at the current stream position. */
+    id3_offset = g_seekable_tell (seekable);
+
+    /* 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);
+    }
+
+    /* Only set the IDv2 tag. */
+    et_id3tag_set_id3_tag_from_file_tag (FileTag, NULL, tag, &strip_tags);
+
+    /* 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;
+        }
+
+        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);
+
+    return TRUE;
+
+err:
+    return FALSE;
+}
+
 gboolean
 et_dsf_tag_write_file_tag (const ET_File *ETFile,
                            GError **error)
@@ -256,13 +578,9 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
     guint64 id3_offset;
     GSeekable *seekable;
     guchar id3_query[ID3_TAG_QUERYSIZE];
+    guchar *id3_buffer;
     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);
@@ -274,7 +592,7 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
     iostream = g_file_open_readwrite (file, NULL, error);
     g_object_unref (file);
 
-    if (iostream == NULL)
+    if (!iostream)
     {
         return FALSE;
     }
@@ -290,14 +608,14 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
         goto err;
     }
 
-    if (memcmp (&header, DSF_HEADER_MAGIC, 4) != 0)
+    if (memcmp (&header, "DSD ", 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)
+    if (memcmp (&header[28], "fmt ", 4) != 0)
     {
         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
                      _("Not a DSF file"));
@@ -349,15 +667,14 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
     {
         /* No ID3v2 tag. */
         id3_offset = file_size;
-
-        tag = id3_tag_new ();
+        id3_buffer = NULL;
         tagsize = 0;
     }
     else
     {
-        guchar *id3_buffer;
 
-        /* Read existing ID3v2 tag. */
+        /* Read in the existing ID3v2 tag, using libid3tag to determine the
+         * size. */
 
         /* Seek to the start of the metadata chunk. */
         if (!g_seekable_seek (seekable, id3_offset, G_SEEK_SET, NULL, error))
@@ -385,7 +702,9 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
             goto err;
         }
 
-        id3_buffer = g_malloc (tagsize);
+        /* FIXME: id3lib seems to read past the end of a buffer that is exactly
+         * the right size, so over-allocate. */
+        id3_buffer = g_malloc0 (tagsize + ID3_TAG_QUERYSIZE);
         /* Copy the query buffer. */
         memcpy (id3_buffer, id3_query, ID3_TAG_QUERYSIZE);
 
@@ -403,17 +722,6 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
             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. */
@@ -422,133 +730,37 @@ et_dsf_tag_write_file_tag (const ET_File *ETFile,
     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))
     {
+        g_free (id3_buffer);
         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)
+#ifdef ENABLE_ID3LIB
+    if (g_settings_get_boolean (MainSettings, "id3v2-version-4"))
     {
-        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)
+#endif
+        if (!et_dsf_tag_write_id3v24 (FileTag, id3_buffer, tagsize, ostream,
+                                      error))
         {
-            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                         _("Error writing tags to file"));
+            g_free (id3_buffer);
             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;
+#ifdef ENABLE_ID3LIB
     }
-
-    id3_tag_delete (tag);
-
-    /* Only truncate the file if the new tag is smaller than the old one. */
-    if (new_tagsize < tagsize)
+    else
     {
-        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))
+        if (!et_dsf_tag_write_id3v23 (FileTag, id3_buffer, tagsize, ostream,
+                                      error))
         {
-            g_free (new_tag_buffer);
+            g_free (id3_buffer);
             goto err;
         }
     }
+#endif
 
-    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);
+    g_free (id3_buffer);
 
     eos_offset = g_seekable_tell (seekable);
 
diff --git a/src/tags/id3_tag.c b/src/tags/id3_tag.c
index a61f938..11191dc 100644
--- a/src/tags/id3_tag.c
+++ b/src/tags/id3_tag.c
@@ -57,7 +57,6 @@
 /**************
  * Prototypes *
  **************/
-static gchar *Id3tag_Get_Error_Message (ID3_Err error);
 static void Id3tag_Prepare_ID3v1 (ID3Tag *id3_tag);
 static gchar *Id3tag_Rules_For_ISO_Fields (const gchar *string,
                                            const gchar *from_codeset,
@@ -130,114 +129,16 @@ et_id3tag_get_tpos_from_file_tag (const File_Tag *FileTag)
     return g_string_free (gstring, FALSE);
 }
 
-/*
- * Write the ID3 tags to the file. Returns TRUE on success, else 0.
- */
-static gboolean
-id3tag_write_file_v23tag (const ET_File *ETFile,
-                          GError **error)
+void
+et_id3tag_set_id3tag_from_file_tag (const File_Tag *FileTag,
+                                    ID3Tag *id3_tag,
+                                    gboolean *strip_tags)
 {
-    const File_Tag *FileTag;
-    const gchar *filename;
-    const gchar *filename_utf8;
-    gchar    *basename_utf8;
-    GFile *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;
-    gboolean success = TRUE;
-    gint number_of_frames;
-    gboolean has_title       = FALSE;
-    gboolean has_artist      = FALSE;
-    gboolean has_album_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;
 
-    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
-    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-    // 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
-        && g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
-    {
-        flag_first_check = FALSE;
-        flag_id3lib_bugged = id3tag_check_if_id3lib_is_buggy (NULL);
-    }
-
-    FileTag  = (File_Tag *)ETFile->FileTag->data;
-    filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
-    filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
-
-    file = g_file_new_for_path (filename);
-
-    /* FIXME: Handle this in the caller instead. */
-    /* This is a protection against a bug in id3lib that enters an infinite
-     * loop with corrupted MP3 files (files containing only zeroes) */
-    if (et_id3tag_check_if_file_is_corrupted (file, error))
-    {
-        GtkWidget *msgdialog;
-        gchar *basename;
-        gchar *basename_utf8;
-
-        basename = g_file_get_basename (file);
-        basename_utf8 = filename_to_display (basename);
-
-        msgdialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow),
-                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                            GTK_MESSAGE_ERROR,
-                                            GTK_BUTTONS_CLOSE,
-                                            _("As the following corrupted file ā€˜%sā€™ will cause an error in 
id3lib, it will not be processed"),
-                                            basename_utf8);
-        gtk_window_set_title (GTK_WINDOW (msgdialog), _("Corrupted file"));
-
-        gtk_dialog_run (GTK_DIALOG (msgdialog));
-        gtk_widget_destroy (msgdialog);
-        g_free (basename);
-        g_free (basename_utf8);
-        g_object_unref (file);
-        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)
-    {
-        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
-                     g_strerror (ENOMEM));
-        g_object_unref (file);
-        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 *
      *********/
@@ -250,7 +151,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_TITLE);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->title);
-        has_title = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -264,7 +165,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_LEADARTIST);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->artist);
-        has_artist = TRUE;
+        *strip_tags = FALSE;
     }
 
        /****************
@@ -277,7 +178,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_BAND);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album_artist);
-        has_album_artist = TRUE;
+        *strip_tags = FALSE;
     }
 
     /*********
@@ -290,7 +191,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_ALBUM);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->album);
-        has_album = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -306,7 +207,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         string1 = et_id3tag_get_tpos_from_file_tag (FileTag);
         Id3tag_Set_Field (id3_frame, ID3FN_TEXT, string1);
         g_free (string1);
-        has_disc_number = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -320,7 +221,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_YEAR);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->year);
-        has_year = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -341,7 +242,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
 
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, string1);
         g_free(string1);
-        has_track = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -376,7 +277,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
 
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, genre_string_tmp);
         g_free(genre_string_tmp);
-        has_genre = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -394,7 +295,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         // 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;
+        *strip_tags = FALSE;
     }
 
 
@@ -408,7 +309,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_COMPOSER);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->composer);
-        has_composer = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -422,7 +323,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         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;
+        *strip_tags = FALSE;
     }
 
 
@@ -436,7 +337,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_COPYRIGHT);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_TEXT, FileTag->copyright);
-        has_copyright = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -450,7 +351,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         id3_frame = ID3Frame_NewID(ID3FID_WWWUSER);
         ID3Tag_AttachFrame(id3_tag,id3_frame);
         Id3tag_Set_Field(id3_frame, ID3FN_URL, FileTag->url);
-        has_composer = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -464,7 +365,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         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;
+        *strip_tags = FALSE;
     }
 
 
@@ -474,8 +375,6 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
     while ( (id3_frame = ID3Tag_FindFrameWithID(id3_tag,ID3FID_PICTURE)) )
         ID3Tag_RemoveFrame(id3_tag,id3_frame);
 
-    has_picture = FALSE;
-
     for (pic = FileTag->picture; pic != NULL; pic = pic->next)
     {
         Picture_Format format = Picture_Format_From_Data(pic);
@@ -527,7 +426,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         if ((id3_field = ID3Frame_GetField(id3_frame,ID3FN_DATA)))
             ID3Field_SetBINARY(id3_field, pic->data, pic->size);
 
-        has_picture = TRUE;
+        *strip_tags = FALSE;
     }
 
 
@@ -549,7 +448,96 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
         g_free(string);
         has_song_len = TRUE;
     }*/
+}
 
+/*
+ * Write the ID3 tags to the file. Returns TRUE on success, else 0.
+ */
+static gboolean
+id3tag_write_file_v23tag (const ET_File *ETFile,
+                          GError **error)
+{
+    const File_Tag *FileTag;
+    const gchar *filename;
+    const gchar *filename_utf8;
+    gchar    *basename_utf8;
+    GFile *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;
+    gboolean success = TRUE;
+    gint number_of_frames;
+    gboolean strip_tags = TRUE;
+    //gboolean has_song_len    = FALSE;
+    static gboolean flag_first_check = TRUE;
+    static gboolean flag_id3lib_bugged = TRUE;
+
+    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    // 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
+        && g_settings_get_boolean (MainSettings, "id3v2-enable-unicode"))
+    {
+        flag_first_check = FALSE;
+        flag_id3lib_bugged = id3tag_check_if_id3lib_is_buggy (NULL);
+    }
+
+    FileTag  = (File_Tag *)ETFile->FileTag->data;
+    filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
+    filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
+
+    file = g_file_new_for_path (filename);
+
+    /* FIXME: Handle this in the caller instead. */
+    /* This is a protection against a bug in id3lib that enters an infinite
+     * loop with corrupted MP3 files (files containing only zeroes) */
+    if (et_id3tag_check_if_file_is_corrupted (file, error))
+    {
+        GtkWidget *msgdialog;
+        gchar *basename;
+        gchar *basename_utf8;
+
+        basename = g_file_get_basename (file);
+        basename_utf8 = filename_to_display (basename);
+
+        msgdialog = gtk_message_dialog_new (GTK_WINDOW (MainWindow),
+                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_CLOSE,
+                                            _("As the following corrupted file ā€˜%sā€™ will cause an error in 
id3lib, it will not be processed"),
+                                            basename_utf8);
+        gtk_window_set_title (GTK_WINDOW (msgdialog), _("Corrupted file"));
+
+        gtk_dialog_run (GTK_DIALOG (msgdialog));
+        gtk_widget_destroy (msgdialog);
+        g_free (basename);
+        g_free (basename_utf8);
+        g_object_unref (file);
+        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)
+    {
+        g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOMEM, "%s",
+                     g_strerror (ENOMEM));
+        g_object_unref (file);
+        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);
+
+    et_id3tag_set_id3tag_from_file_tag (FileTag, id3_tag, &strip_tags);
 
     /******************************
      * Delete an APE tag if found *
@@ -583,9 +571,7 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
      * is set to TRUE, we strip the ID3v1.x and ID3v2 tags. Else, write ID3v2
      * and/or ID3v1. */
     if (g_settings_get_boolean (MainSettings, "id3-strip-empty")
-    && !has_title      && !has_artist   && !has_album_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 )
+        && strip_tags)
     {
         error_strip_id3v1 = ID3Tag_Strip(id3_tag,ID3TT_ID3V1);
         error_strip_id3v2 = ID3Tag_Strip(id3_tag,ID3TT_ID3V2);
@@ -743,7 +729,8 @@ id3tag_write_file_v23tag (const ET_File *ETFile,
 }
 
 
-gchar *Id3tag_Get_Error_Message(ID3_Err error)
+gchar *
+Id3tag_Get_Error_Message (ID3_Err error)
 {
     switch (error)
     {
diff --git a/src/tags/id3_tag.h b/src/tags/id3_tag.h
index 342d22e..a63e423 100644
--- a/src/tags/id3_tag.h
+++ b/src/tags/id3_tag.h
@@ -38,10 +38,16 @@ guchar Id3tag_String_To_Genre (const gchar *genre);
 
 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);
+#ifdef ENABLE_ID3LIB
+#include <id3.h>
+gchar * Id3tag_Get_Error_Message (ID3_Err error);
+void et_id3tag_set_id3tag_from_file_tag (const File_Tag *FileTag, ID3Tag *id3_tag, gboolean *strip_tags);
+#endif /* ENABLE_ID3LIB */
 #endif /* ENABLE_MP3 */
 
 G_END_DECLS
diff --git a/src/tags/id3lib/c_wrapper.cpp b/src/tags/id3lib/c_wrapper.cpp
index 7f63406..2c92755 100644
--- a/src/tags/id3lib/c_wrapper.cpp
+++ b/src/tags/id3lib/c_wrapper.cpp
@@ -119,6 +119,30 @@ extern "C"
     return headerInfo;
   }
 
+  /* Filed as a patch against id3lib:
+   * http://sourceforge.net/p/id3lib/patches/75/ */
+  ID3_C_EXPORT size_t CCONV
+  ID3Tag_Render(const ID3Tag *tag, uchar *buffer, ID3_TagType tt)
+  {
+    size_t size = 0;
+    if (tag)
+    {
+      ID3_CATCH(size = reinterpret_cast<const ID3_Tag *>(tag)->Render(buffer, tt));
+    }
+    return size;
+  }
+
+  ID3_C_EXPORT size_t CCONV
+  ID3Tag_Size(const ID3Tag *tag)
+  {
+    size_t size = 0;
+    if (tag)
+    {
+      ID3_CATCH(size = reinterpret_cast<const ID3_Tag *>(tag)->Size());
+    }
+    return size;
+  }
+
   // Call with :
   //    Mp3_Headerinfo* headerInfo = malloc(sizeof(Mp3_Headerinfo));
   //    ID3Tag_GetMp3HeaderInfo(tag, headerInfo);
diff --git a/src/tags/id3lib/id3_bugfix.h b/src/tags/id3lib/id3_bugfix.h
index 14e97a9..8a281d6 100644
--- a/src/tags/id3lib/id3_bugfix.h
+++ b/src/tags/id3lib/id3_bugfix.h
@@ -42,6 +42,8 @@ extern "C"
   ID3_C_EXPORT ID3_TextEnc           CCONV ID3Field_GetEncoding    (const ID3Field *field);
   ID3_C_EXPORT bool                  CCONV ID3Field_IsEncodable    (const ID3Field *field);
   ID3_C_EXPORT ID3_FieldType         CCONV ID3Field_GetType        (const ID3Field *field);
+  ID3_C_EXPORT size_t                CCONV ID3Tag_Render           (const ID3Tag *tag, uchar *buffer, 
ID3_TagType tt);
+  ID3_C_EXPORT size_t                CCONV ID3Tag_Size             (const ID3Tag *tag);
   //ID3_C_EXPORT ID3_FieldID           CCONV ID3Field_GetID          (const ID3Field *field);
 
   ID3_C_EXPORT const Mp3_Headerinfo* CCONV ID3Tag_GetMp3HeaderInfo (ID3Tag *tag);


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