[easytag/wip/dsf-support: 2/2] Read ID3v2 tags from DSF files



commit 33ea6f630464064de96e6fcac75bda2686136e18
Author: David King <amigadave amigadave com>
Date:   Sun Nov 9 12:32:14 2014 +0000

    Read ID3v2 tags from DSF files
    
    Split off a helper et_id3tag_fill_file_tag_from_id3tag() function from
    id3tag_read_file_tag() and use it to read the parsed id3_tag struct.

 Makefile.am           |    2 +
 src/et_core.c         |   24 +++-
 src/et_core.h         |    1 +
 src/tag_area.c        |   20 +++
 src/tags/dsf_tag.c    |  242 +++++++++++++++++++++++++++++++++
 src/tags/dsf_tag.h    |   31 +++++
 src/tags/id3_tag.h    |    7 +
 src/tags/id3v24_tag.c |  359 ++++++++++++++++++++-----------------------------
 8 files changed, 473 insertions(+), 213 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0fc9d46..a787261 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,6 +71,7 @@ easytag_SOURCES = \
        src/tags/libapetag/info_mpc.c \
        src/tags/ape_tag.c \
        src/tags/dsf_header.c \
+       src/tags/dsf_tag.c \
        src/tags/flac_header.c \
        src/tags/flac_tag.c \
        src/tags/gio_wrapper.cc \
@@ -124,6 +125,7 @@ easytag_headers = \
        src/tags/libapetag/info_mpc.h \
        src/tags/ape_tag.h \
        src/tags/dsf_header.h \
+       src/tags/dsf_tag.h \
        src/tags/flac_header.h \
        src/tags/flac_tag.h \
        src/tags/gio_wrapper.h \
diff --git a/src/et_core.c b/src/et_core.c
index fabf16f..cb163c3 100644
--- a/src/et_core.c
+++ b/src/et_core.c
@@ -39,7 +39,8 @@
 #include "monkeyaudio_header.h"
 #include "musepack_header.h"
 #ifdef ENABLE_MP3
-#   include "id3_tag.h"
+#include "dsf_tag.h"
+#include "id3_tag.h"
 #endif
 #include "picture.h"
 #include "ape_tag.h"
@@ -111,7 +112,7 @@ const ET_File_Description ETFileDescription[] =
     { MP4_FILE, ".m4p", MP4_TAG}, /* Implemented by Michael Ihde. */
     { MP4_FILE, ".m4v", MP4_TAG},
 #endif
-    { DSF_FILE, ".dsf", UNKNOWN_TAG },
+    { DSF_FILE, ".dsf", DSF_TAG }, /* ID3v2 tag at offset in DSF file. */
 #ifdef ENABLE_WAVPACK
     { WAVPACK_FILE, ".wv", WAVPACK_TAG}, /* Implemented by Maarten Maathuis. */
 #endif
@@ -581,6 +582,17 @@ GList *ET_Add_File_To_File_List (gchar *filename)
             }
             break;
 #endif
+#ifdef ENABLE_MP3 /* DSF tags use an embedded ID3v2 tag. */
+        case DSF_TAG:
+            if (!et_dsf_tag_read_file_tag (file, FileTag, &error))
+            {
+                Log_Print (LOG_ERROR,
+                           _("Error reading tag from DSF file ‘%s’: %s"),
+                           filename_utf8, error->message);
+                g_clear_error (&error);
+            }
+            break;
+#endif
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
             if (!wavpack_tag_read_file_tag (file, FileTag, &error))
@@ -3120,6 +3132,9 @@ void ET_Save_File_Data_From_UI (ET_File *ETFile)
 #ifdef ENABLE_MP4
         case MP4_TAG:
 #endif
+#ifdef ENABLE_MP3
+        case DSF_TAG:
+#endif
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
 #endif
@@ -3570,6 +3585,11 @@ ET_Save_File_Tag_To_HD (ET_File *ETFile, GError **error)
             state = mp4tag_write_file_tag (ETFile, error);
             break;
 #endif
+#ifdef ENABLE_MP3
+        case DSF_TAG:
+            state = et_dsf_tag_write_file_tag (ETFile, error);
+            break;
+#endif
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
             state = wavpack_tag_write_file_tag (ETFile, error);
diff --git a/src/et_core.h b/src/et_core.h
index 785a9aa..9b6f993 100644
--- a/src/et_core.h
+++ b/src/et_core.h
@@ -67,6 +67,7 @@ typedef enum
     MP4_TAG,
     WAVPACK_TAG,
     OPUS_TAG,
+    DSF_TAG, /* ID3v2 tag in DSF file. */
     UNKNOWN_TAG
 } ET_Tag_Type;
 
diff --git a/src/tag_area.c b/src/tag_area.c
index 7c6c98b..dd03722 100644
--- a/src/tag_area.c
+++ b/src/tag_area.c
@@ -1431,6 +1431,7 @@ load_picture_from_file (GFile *file,
             case OPUS_TAG:
             case APE_TAG:
             case FLAC_TAG:
+            case DSF_TAG:
             case WAVPACK_TAG:
                 pic->description = g_strdup (filename_utf8);
 
@@ -2608,6 +2609,25 @@ et_tag_area_update_controls (EtTagArea *self,
             break;
 #endif
 
+        /* Identical to ID3v2. */
+#ifdef ENABLE_MP3
+        case DSF_TAG:
+            gtk_widget_show (priv->disc_number_label);
+            gtk_widget_show (priv->disc_number_entry);
+            gtk_widget_show (priv->composer_label);
+            gtk_widget_show (priv->composer_entry);
+            gtk_widget_show (priv->orig_artist_label);
+            gtk_widget_show (priv->orig_artist_entry);
+            gtk_widget_show (priv->copyright_label);
+            gtk_widget_show (priv->copyright_entry);
+            gtk_widget_show (priv->url_label);
+            gtk_widget_show (priv->url_entry);
+            gtk_widget_show (priv->encoded_by_label);
+            gtk_widget_show (priv->encoded_by_entry);
+            et_tag_area_show_images_tab (self);
+            break;
+#endif /* ENABLE_MP3 */
+
 #ifdef ENABLE_WAVPACK
         case WAVPACK_TAG:
             gtk_widget_show (priv->disc_number_label);
diff --git a/src/tags/dsf_tag.c b/src/tags/dsf_tag.c
new file mode 100644
index 0000000..6bf7d89
--- /dev/null
+++ b/src/tags/dsf_tag.c
@@ -0,0 +1,242 @@
+/* EasyTAG - Tag editor for audio files.
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <errno.h>
+
+#include "dsf_tag.h"
+
+#include "easytag.h"
+#include "et_core.h"
+#include "misc.h"
+
+#ifdef ENABLE_MP3
+
+#include <id3tag.h>
+#include "id3_tag.h"
+
+#define DSF_HEADER_LENGTH 80
+
+/* Read the little-endian 64-bit integer from the next 8 characters. */
+static guint64
+guint64_from_bytes (guchar *str)
+{
+    gsize i;
+    guint64 result = 0;
+
+    for (i = 8; i > 0; i--)
+    {
+        result = (result << 8) + str[i - 1];
+    }
+
+    return result;
+}
+
+gboolean
+et_dsf_tag_read_file_tag (GFile *file,
+                          File_Tag *FileTag,
+                          GError **error)
+{
+    GInputStream *istream;
+    guchar header[DSF_HEADER_LENGTH];
+    gsize bytes_read;
+    guint64 size;
+    guint64 id3_offset;
+    GSeekable *seekable;
+    guchar id3_query[ID3_TAG_QUERYSIZE];
+    long tagsize;
+    guchar *id3_buffer;
+    struct id3_tag *tag;
+    gboolean update;
+
+    g_return_val_if_fail (file != NULL && FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    istream = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+    if (!istream)
+    {
+        return FALSE;
+    }
+
+    /* 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, "DSD ", 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSD file"));
+        goto err;
+    }
+
+    if (memcmp (&header[28], "fmt ", 4) != 0)
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Not a DSD file"));
+        goto err;
+    }
+
+    /* 12 (8 bytes) total file size. */
+    size = guint64_from_bytes (&header[12]);
+    
+    /* 20 (8 bytes) metadata chunk offset, or 0 if no tag is present. */
+    id3_offset = guint64_from_bytes (&header[20]);
+    
+    if (id3_offset == 0)
+    {
+        /* No ID3v2 tag. */
+        g_object_unref (istream);
+        return TRUE;
+    }
+
+    if (id3_offset >= 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 (!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_malloc0 (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;
+    }
+
+    g_object_unref (istream);
+
+    if ((tag = id3_tag_parse ((id3_byte_t const *)id3_buffer, tagsize)))
+    {
+        unsigned version = id3_tag_version (tag);
+
+#ifdef ENABLE_ID3LIB
+        /* Besides upgrading old tags, downgrade ID3v2.4 to ID3v2.3 */
+        if (g_settings_get_boolean (MainSettings, "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
+    }
+    else
+    {
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error reading tags from file"));
+        g_free (id3_buffer);
+        return FALSE;
+    }
+
+    g_free (id3_buffer);
+
+    /* Check for an empty tag. */
+    if (tag->nframes == 0)
+    {
+        id3_tag_delete (tag);
+        return TRUE;
+    }
+
+    update |= et_id3tag_fill_file_tag_from_id3tag (tag, FileTag);
+
+    if (update)
+    {
+        FileTag->saved = FALSE;
+    }
+
+    id3_tag_delete (tag);
+
+    return TRUE;
+
+err:
+    g_object_unref (istream);
+    return FALSE;
+}
+
+gboolean
+et_dsf_tag_write_file_tag (const ET_File *ETFile,
+                           GError **error)
+{
+    g_return_val_if_fail (ETFile != 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. */
+    return FALSE;
+}
+
+#endif /* ENABLE_MP3 */
diff --git a/src/tags/dsf_tag.h b/src/tags/dsf_tag.h
new file mode 100644
index 0000000..ba32b6f
--- /dev/null
+++ b/src/tags/dsf_tag.h
@@ -0,0 +1,31 @@
+/* EasyTAG - Tag editor for audio files
+ * Copyright (C) 2014  David King <amigadave amigadave com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ET_DSF_TAG_H_
+#define ET_DSF_TAG_H_
+
+#include "et_core.h"
+
+G_BEGIN_DECLS
+
+gboolean et_dsf_tag_read_file_tag (GFile *file, File_Tag *FileTag, GError **error);
+gboolean et_dsf_tag_write_file_tag (const ET_File *ETFile, GError **error);
+
+G_END_DECLS
+
+#endif /* ET_DSF_TAG_H_ */
diff --git a/src/tags/id3_tag.h b/src/tags/id3_tag.h
index fd72dcc..5f74991 100644
--- a/src/tags/id3_tag.h
+++ b/src/tags/id3_tag.h
@@ -20,6 +20,8 @@
 #ifndef ET_ID3TAG_H_
 #define ET_ID3TAG_H_
 
+#include "config.h"
+
 #include <glib.h>
 #include "et_core.h"
 
@@ -36,6 +38,11 @@ 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);
+#endif /* ENABLE_MP3 */
+
 G_END_DECLS
 
 #endif /* ET_ID3TAG_H_ */
diff --git a/src/tags/id3v24_tag.c b/src/tags/id3v24_tag.c
index ee7739b..6f0c54c 100644
--- a/src/tags/id3v24_tag.c
+++ b/src/tags/id3v24_tag.c
@@ -80,168 +80,16 @@ static int    etag_set_tags             (const gchar *str, const char *frame_nam
 static gboolean etag_write_tags (const gchar *filename, struct id3_tag const *v1tag,
                             struct id3_tag const *v2tag, gboolean strip_tags, GError **error);
 
-/*************
- * 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 (GFile *gfile,
-                      File_Tag *FileTag,
-                      GError **error)
+et_id3tag_fill_file_tag_from_id3tag (struct id3_tag *tag,
+                                     File_Tag *FileTag)
 {
-    GInputStream *istream;
-    gsize bytes_read;
-    GSeekable *seekable;
-    gchar *filename;
-    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;
-
-    g_return_val_if_fail (gfile != NULL && FileTag != NULL, FALSE);
-    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
-    istream = G_INPUT_STREAM (g_file_read (gfile, NULL, error));
-
-    if (!istream)
-    {
-        return FALSE;
-    }
-
-    string1 = g_malloc0 (ID3_TAG_QUERYSIZE);
-
-    /* Check if the file has an ID3v2 tag or/and an ID3v1 tags.
-     * 1) ID3v2 tag. */
-    if (!g_input_stream_read_all (istream, string1, ID3_TAG_QUERYSIZE,
-                                  &bytes_read, NULL, error))
-    {
-        g_object_unref (istream);
-        g_free (string1);
-        return FALSE;
-    }
-    else if (bytes_read != ID3_TAG_QUERYSIZE)
-    {
-        g_object_unref (istream);
-        g_free (string1);
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
-                     _("Error reading tags from file"));
-        return FALSE;
-    }
-
-    if ((tagsize = id3_tag_query((id3_byte_t const *)string1, ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
-    {
-        /* ID3v2 tag not found! */
-        update = g_settings_get_boolean (MainSettings, "id3v2-enabled");
-    }else
-    {
-        /* ID3v2 tag found */
-        if (!g_settings_get_boolean (MainSettings, "id3v2-enabled"))
-        {
-            /* To delete the tag. */
-            update = 1;
-        }else
-        {
-            /* Determine version if user want to upgrade old tags */
-            if (g_settings_get_boolean (MainSettings, "id3v2-convert-old")
-            && (string1 = g_realloc (string1, tagsize))
-                && g_input_stream_read_all (istream,
-                                            &string1[ID3_TAG_QUERYSIZE],
-                                            tagsize - ID3_TAG_QUERYSIZE,
-                                            &bytes_read, NULL, error)
-                && bytes_read == 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 (g_settings_get_boolean (MainSettings, "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. */
-    seekable = G_SEEKABLE (istream);
-
-    if (!g_seekable_can_seek (seekable))
-    {
-        g_object_unref (istream);
-        g_free (string1);
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
-                     _("Error reading tags from file"));
-        return FALSE;
-    }
-
-    /* Go to the beginning of ID3v1 tag. */
-    if (g_seekable_seek (seekable, -128, G_SEEK_END, NULL, error)
-    && (string1)
-        && g_input_stream_read_all (istream, string1, 3, &bytes_read, NULL,
-                                    NULL /* Ignore errors. */)
-        && bytes_read == 3
-    && (string1[0] == 'T')
-    && (string1[1] == 'A')
-    && (string1[2] == 'G')
-       )
-    {
-        /* ID3v1 tag found! */
-        if (!g_settings_get_boolean (MainSettings, "id3v1-enabled"))
-        {
-            update = 1;
-        }
-    }else
-    {
-        /* ID3v1 tag not found! */
-        if (g_settings_get_boolean (MainSettings, "id3v1-enabled"))
-        {
-            update = 1;
-        }
-    }
-
-    g_free (string1);
-    g_object_unref (istream);
-
-    filename = g_file_get_path (gfile);
-
-    if ((file = id3_file_open (filename, ID3_FILE_MODE_READONLY)) == NULL)
-    {
-        g_free (filename);
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                     _("Error reading tags from file"));
-        return FALSE;
-    }
-
-    g_free (filename);
-
-    if ( ((tag = id3_file_tag(file)) == NULL)
-    ||   (tag->nframes == 0))
-    {
-        id3_file_close(file);
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
-                     _("Error reading tags from file"));
-        return FALSE;
-    }
-
+    gsize i, j;
+    gboolean tmpupdate, update = 0;
 
     /****************
      * Title (TIT2) *
@@ -487,79 +335,168 @@ id3tag_read_file_tag (GFile *gfile,
             }
         }
 
-        // Picture description
+        /* 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)) )
+    return update;
+}
+
+/*
+ * 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 (GFile *gfile,
+                      File_Tag *FileTag,
+                      GError **error)
+{
+    GInputStream *istream;
+    gchar *string1;
+    gsize bytes_read;
+    GSeekable *seekable;
+    gchar *filename;
+    struct id3_file *file;
+    struct id3_tag *tag;
+    gboolean update = 0;
+    long tagsize;
+
+    g_return_val_if_fail (gfile != NULL && FileTag != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    istream = G_INPUT_STREAM (g_file_read (gfile, NULL, error));
+
+    if (!istream)
+    {
+        return FALSE;
+    }
+
+    string1 = g_malloc0 (ID3_TAG_QUERYSIZE);
+
+    /* Check if the file has an ID3v2 tag or/and an ID3v1 tags.
+     * 1) ID3v2 tag. */
+    if (!g_input_stream_read_all (istream, string1, ID3_TAG_QUERYSIZE,
+                                  &bytes_read, NULL, error))
+    {
+        g_object_unref (istream);
+        g_free (string1);
+        return FALSE;
+    }
+    else if (bytes_read != ID3_TAG_QUERYSIZE)
+    {
+        g_object_unref (istream);
+        g_free (string1);
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+                     _("Error reading tags from file"));
+        return FALSE;
+    }
+
+    if ((tagsize = id3_tag_query((id3_byte_t const *)string1, ID3_TAG_QUERYSIZE)) <= ID3_TAG_QUERYSIZE)
     {
-        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)) )
+        /* ID3v2 tag not found! */
+        update = g_settings_get_boolean (MainSettings, "id3v2-enabled");
+    }else
+    {
+        /* ID3v2 tag found */
+        if (!g_settings_get_boolean (MainSettings, "id3v2-enabled"))
         {
-            size = ID3Field_Size(id3_field);
-            data = g_malloc(size);
-            ID3Field_GetBINARY(id3_field, data, size);
+            /* To delete the tag. */
+            update = 1;
+        }else
+        {
+            /* Determine version if user want to upgrade old tags */
+            if (g_settings_get_boolean (MainSettings, "id3v2-convert-old")
+            && (string1 = g_realloc (string1, tagsize))
+                && g_input_stream_read_all (istream,
+                                            &string1[ID3_TAG_QUERYSIZE],
+                                            tagsize - ID3_TAG_QUERYSIZE,
+                                            &bytes_read, NULL, error)
+                && bytes_read == 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 (g_settings_get_boolean (MainSettings, "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);
+            }
         }
+    }
 
-        // SyncLyrics description
-        description = Id3tag_Get_Field(id3_frame, ID3FN_DESCRIPTION);
+    /* 2) ID3v1 tag. */
+    seekable = G_SEEKABLE (istream);
 
-        // SyncLyrics language
-        language = Id3tag_Get_Field(id3_frame, ID3FN_LANGUAGE);
+    if (!g_seekable_can_seek (seekable))
+    {
+        g_object_unref (istream);
+        g_free (string1);
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "%s",
+                     _("Error reading tags from file"));
+        return FALSE;
+    }
 
-        // SyncLyrics timestamp field
-        if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_TIMESTAMPFORMAT)) )
+    /* Go to the beginning of ID3v1 tag. */
+    if (g_seekable_seek (seekable, -128, G_SEEK_END, NULL, error)
+    && (string1)
+        && g_input_stream_read_all (istream, string1, 3, &bytes_read, NULL,
+                                    NULL /* Ignore errors. */)
+        && bytes_read == 3
+    && (string1[0] == 'T')
+    && (string1[1] == 'A')
+    && (string1[2] == 'G')
+       )
+    {
+        /* ID3v1 tag found! */
+        if (!g_settings_get_boolean (MainSettings, "id3v1-enabled"))
         {
-            timestamp_format = ID3Field_GetINT(id3_field);
+            update = 1;
         }
-
-        // SyncLyrics content type
-        if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_CONTENTTYPE)) )
+    }else
+    {
+        /* ID3v1 tag not found! */
+        if (g_settings_get_boolean (MainSettings, "id3v1-enabled"))
         {
-            sync_type = ID3Field_GetINT(id3_field);
+            update = 1;
         }
+    }
 
-        // 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_free (string1);
+    g_object_unref (istream);
+
+    filename = g_file_get_path (gfile);
 
-            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);
+    if ((file = id3_file_open (filename, ID3_FILE_MODE_READONLY)) == NULL)
+    {
+        g_free (filename);
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error reading tags from file"));
+        return FALSE;
+    }
 
+    g_free (filename);
 
-            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 ( ((tag = id3_file_tag(file)) == NULL)
+    ||   (tag->nframes == 0))
+    {
+        id3_file_close(file);
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+                     _("Error reading tags from file"));
+        return FALSE;
+    }
 
-    } **/
+    update |= et_id3tag_fill_file_tag_from_id3tag (tag, FileTag);
 
     if (update)
         FileTag->saved = FALSE;


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