From b98be427f98532d2716461f9b660d668ee44f2f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= <lalinsky@gmail.com>
Date: Sat, 10 Nov 2007 15:16:49 +0000
Subject: [PATCH] Use Unicode filenames on Windows.

git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@734944 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
---
 taglib/flac/flacfile.cpp         | 11 ++++++
 taglib/flac/flacfile.h           | 14 ++++++++
 taglib/mpc/mpcfile.cpp           |  9 +++++
 taglib/mpc/mpcfile.h             | 10 ++++++
 taglib/mpeg/mpegfile.cpp         | 13 +++++++
 taglib/mpeg/mpegfile.h           | 12 +++++++
 taglib/ogg/flac/oggflacfile.cpp  |  9 +++++
 taglib/ogg/flac/oggflacfile.h    | 10 ++++++
 taglib/ogg/oggfile.cpp           |  7 ++++
 taglib/ogg/oggfile.h             | 13 +++++++
 taglib/ogg/vorbis/vorbisfile.cpp |  9 +++++
 taglib/ogg/vorbis/vorbisfile.h   | 10 ++++++
 taglib/toolkit/taglib.h          |  4 +++
 taglib/toolkit/tfile.cpp         | 60 +++++++++++++++++++++++++++++++-
 taglib/toolkit/tfile.h           | 18 ++++++++++
 15 files changed, 208 insertions(+), 1 deletion(-)

diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp
index e44f771a..f829076d 100644
--- a/taglib/flac/flacfile.cpp
+++ b/taglib/flac/flacfile.cpp
@@ -119,6 +119,17 @@ FLAC::File::File(const char *file, ID3v2::FrameFactory *frameFactory,
   read(readProperties, propertiesStyle);
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+FLAC::File::File(const wchar_t *file, ID3v2::FrameFactory *frameFactory,
+                 bool readProperties, Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate;
+  d->ID3v2FrameFactory = frameFactory;
+  read(readProperties, propertiesStyle);
+}
+#endif
+
 FLAC::File::~File()
 {
   delete d;
diff --git a/taglib/flac/flacfile.h b/taglib/flac/flacfile.h
index 5cc70843..9c016488 100644
--- a/taglib/flac/flacfile.h
+++ b/taglib/flac/flacfile.h
@@ -88,6 +88,20 @@ namespace TagLib {
            bool readProperties = true,
            Properties::ReadStyle propertiesStyle = Properties::Average);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs a FLAC file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * If this file contains and ID3v2 tag the frames will be created using
+       * \a frameFactory.
+       */
+      File(const wchar_t *file, ID3v2::FrameFactory *frameFactory,
+           bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+#endif
+
       /*!
        * Destroys this instance of the File.
        */
diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp
index a5a8a005..352a3152 100644
--- a/taglib/mpc/mpcfile.cpp
+++ b/taglib/mpc/mpcfile.cpp
@@ -100,6 +100,15 @@ MPC::File::File(const char *file, bool readProperties,
   read(readProperties, propertiesStyle);
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+MPC::File::File(const wchar_t *file, bool readProperties,
+                Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+#endif
+
 MPC::File::~File()
 {
   delete d;
diff --git a/taglib/mpc/mpcfile.h b/taglib/mpc/mpcfile.h
index d4c337dd..4f4cce58 100644
--- a/taglib/mpc/mpcfile.h
+++ b/taglib/mpc/mpcfile.h
@@ -88,6 +88,16 @@ namespace TagLib {
       File(const char *file, bool readProperties = true,
            Properties::ReadStyle propertiesStyle = Properties::Average);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs an MPC file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(const wchar_t *file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+#endif
+
       /*!
        * Destroys this instance of the File.
        */
diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp
index d9035f1e..21538baa 100644
--- a/taglib/mpeg/mpegfile.cpp
+++ b/taglib/mpeg/mpegfile.cpp
@@ -246,6 +246,19 @@ MPEG::File::File(const char *file, ID3v2::FrameFactory *frameFactory,
   }
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+MPEG::File::File(const wchar_t *file, ID3v2::FrameFactory *frameFactory,
+                 bool readProperties, Properties::ReadStyle propertiesStyle) :
+  TagLib::File(file)
+{
+  d = new FilePrivate(frameFactory);
+  if(isOpen()) {
+    d->tag = new MPEGTag(this);
+    read(readProperties, propertiesStyle);
+  }
+}
+#endif
+
 MPEG::File::~File()
 {
   delete d;
diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h
index 8df12fb0..a6f9cbd1 100644
--- a/taglib/mpeg/mpegfile.h
+++ b/taglib/mpeg/mpegfile.h
@@ -91,6 +91,18 @@ namespace TagLib {
            bool readProperties = true,
            Properties::ReadStyle propertiesStyle = Properties::Average);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs an MPEG file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.  The frames will be created using
+       * \a frameFactory.
+       */
+      File(const wchar_t *file, ID3v2::FrameFactory *frameFactory,
+           bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+#endif
+
       /*!
        * Destroys this instance of the File.
        */
diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp
index b69fb64d..0ab961fd 100644
--- a/taglib/ogg/flac/oggflacfile.cpp
+++ b/taglib/ogg/flac/oggflacfile.cpp
@@ -75,6 +75,15 @@ Ogg::FLAC::File::File(const char *file, bool readProperties,
   read(readProperties, propertiesStyle);
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+Ogg::FLAC::File::File(const wchar_t *file, bool readProperties,
+                      Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+#endif
+
 Ogg::FLAC::File::~File()
 {
   delete d;
diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h
index 14ac2c12..5c9d7e97 100644
--- a/taglib/ogg/flac/oggflacfile.h
+++ b/taglib/ogg/flac/oggflacfile.h
@@ -71,6 +71,16 @@ namespace TagLib {
       File(const char *file, bool readProperties = true,
            Properties::ReadStyle propertiesStyle = Properties::Average);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs an Ogg/FLAC file from \a file.  If \a readProperties is true
+       * the file's audio properties will also be read using \a propertiesStyle.
+       * If false, \a propertiesStyle is ignored.
+       */
+      File(const wchar_t *file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+#endif
+
       /*!
        * Destroys this instance of the File.
        */
diff --git a/taglib/ogg/oggfile.cpp b/taglib/ogg/oggfile.cpp
index a6efb240..bc33ab01 100644
--- a/taglib/ogg/oggfile.cpp
+++ b/taglib/ogg/oggfile.cpp
@@ -213,6 +213,13 @@ Ogg::File::File(const char *file) : TagLib::File(file)
   d = new FilePrivate;
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+Ogg::File::File(const wchar_t *file) : TagLib::File(file)
+{
+  d = new FilePrivate;
+}
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // private members
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/oggfile.h b/taglib/ogg/oggfile.h
index 6825bddf..76b5c9c5 100644
--- a/taglib/ogg/oggfile.h
+++ b/taglib/ogg/oggfile.h
@@ -92,6 +92,19 @@ namespace TagLib {
        */
       File(const char *file);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs an Ogg file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       *
+       * \note This constructor is protected since Ogg::File shouldn't be
+       * instantiated directly but rather should be used through the codec
+       * specific subclasses.
+       */
+      File(const wchar_t *file);
+#endif
+
     private:
       File(const File &);
       File &operator=(const File &);
diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp
index 033b8cdf..0e9cc9e1 100644
--- a/taglib/ogg/vorbis/vorbisfile.cpp
+++ b/taglib/ogg/vorbis/vorbisfile.cpp
@@ -68,6 +68,15 @@ Vorbis::File::File(const char *file, bool readProperties,
   read(readProperties, propertiesStyle);
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+Vorbis::File::File(const wchar_t *file, bool readProperties,
+                   Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+#endif
+
 Vorbis::File::~File()
 {
   delete d;
diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h
index 16d630ef..ddff68e2 100644
--- a/taglib/ogg/vorbis/vorbisfile.h
+++ b/taglib/ogg/vorbis/vorbisfile.h
@@ -70,6 +70,16 @@ namespace TagLib {
       File(const char *file, bool readProperties = true,
            Properties::ReadStyle propertiesStyle = Properties::Average);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+      /*!
+       * Contructs a Vorbis file from \a file.  If \a readProperties is true the
+       * file's audio properties will also be read using \a propertiesStyle.  If
+       * false, \a propertiesStyle is ignored.
+       */
+      File(const wchar_t *file, bool readProperties = true,
+           Properties::ReadStyle propertiesStyle = Properties::Average);
+#endif
+
       /*!
        * Destroys this instance of the File.
        */
diff --git a/taglib/toolkit/taglib.h b/taglib/toolkit/taglib.h
index 41476cf0..46092c6e 100644
--- a/taglib/toolkit/taglib.h
+++ b/taglib/toolkit/taglib.h
@@ -30,6 +30,10 @@
 #define TAGLIB_MINOR_VERSION 4
 #define TAGLIB_PATCH_VERSION 0
 
+#ifdef _WIN32
+#define TAGLIB_UNICODE_FILENAMES 1
+#endif
+
 #include <string>
 
 //! A namespace for all TagLib related classes and functions
diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp
index 90c296b7..0f875ec0 100644
--- a/taglib/toolkit/tfile.cpp
+++ b/taglib/toolkit/tfile.cpp
@@ -45,6 +45,15 @@
 # define W_OK 2
 #endif
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+# ifdef _WIN32
+#  include <wchar.h>
+#  include <windows.h>
+# else
+#  error "Unicode filenames are not supported on this platform."
+# endif
+#endif
+
 using namespace TagLib;
 
 class File::FilePrivate
@@ -53,6 +62,9 @@ public:
   FilePrivate(const char *fileName) :
     file(0),
     name(fileName),
+#ifdef TAGLIB_UNICODE_FILENAMES
+    wname(0),
+#endif
     readOnly(true),
     valid(true),
     size(0)
@@ -61,10 +73,16 @@ public:
   ~FilePrivate()
   {
     free((void *)name);
+#ifdef TAGLIB_UNICODE_FILENAMES
+    free((void *)wname);
+#endif
   }
 
   FILE *file;
   const char *name;
+#ifdef TAGLIB_UNICODE_FILENAMES
+  const wchar_t *wname;
+#endif
   bool readOnly;
   bool valid;
   ulong size;
@@ -87,12 +105,45 @@ File::File(const char *file)
   if(d->file)
     d->readOnly = false;
   else
-    d->file = fopen(file,"rb");
+    d->file = fopen(file, "rb");
 
   if(!d->file)
     debug("Could not open file " + String(file));
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+File::File(const wchar_t *file)
+{
+#ifdef _WIN32
+  d = new FilePrivate(0);
+  d->wname = _wcsdup(file);
+
+  if(GetVersion() < 0x80000000) {
+    d->file = _wfopen(file, L"rb+");
+    if(d->file)
+      d->readOnly = false;
+    else
+      d->file = _wfopen(file, L"rb");
+  }
+  else {
+    size_t size = wcslen(file) + 1;
+    d->name = (char *)malloc(size);
+    if(WideCharToMultiByte(CP_ACP, 0, file, -1, (CHAR*)d->name, size,
+                           NULL, NULL) > 0) {
+      d->file = fopen(d->name, "rb+");
+      if(d->file)
+        d->readOnly = false;
+      else
+        d->file = fopen(d->name, "rb");
+    }
+  }
+
+  if(!d->file)
+    debug("Could not open file " + String(file, String::UTF16LE));
+#endif
+}
+#endif
+
 File::~File()
 {
   if(d->file)
@@ -105,6 +156,13 @@ const char *File::name() const
   return d->name;
 }
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+const wchar_t *File::unicodeName() const
+{
+  return d->wname;
+}
+#endif
+
 ByteVector File::readBlock(ulong length)
 {
   if(!d->file) {
diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h
index 1dad4700..59a82e94 100644
--- a/taglib/toolkit/tfile.h
+++ b/taglib/toolkit/tfile.h
@@ -69,6 +69,13 @@ namespace TagLib {
      */
     const char *name() const;
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+    /*!
+     * Returns the file name in UTF-16.
+     */
+    const wchar_t *unicodeName() const;
+#endif
+
     /*!
      * Returns a pointer to this file's tag.  This should be reimplemented in
      * the concrete subclasses.
@@ -225,6 +232,17 @@ namespace TagLib {
      */
     File(const char *file);
 
+#ifdef TAGLIB_UNICODE_FILENAMES
+    /*!
+     * Construct a File object and opens the \a file.  \a file should be a
+     * be a null-terminated Unicode string in UTF-16.
+     *
+     * \note Constructor is protected since this class should only be
+     * instantiated through subclasses.
+     */
+    File(const wchar_t *file);
+#endif
+
     /*!
      * Marks the file as valid or invalid.
      *