diff -Naur mp4v2-2.0.0/Makefile mp4v2-2.0.0-win32/Makefile
--- mp4v2-2.0.0/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ mp4v2-2.0.0-win32/Makefile	2021-11-27 20:52:57.873064800 +0100
@@ -0,0 +1,9 @@
+########## MP4v2 directory makefile ##########
+
+all:
+	cd libplatform && $(MAKE)
+	cd src && $(MAKE)
+
+clean:
+	cd libplatform && $(MAKE) clean
+	cd src && $(MAKE) clean
diff -Naur mp4v2-2.0.0/configure mp4v2-2.0.0-win32/configure
--- mp4v2-2.0.0/configure	2012-05-21 01:17:59.000000000 +0200
+++ mp4v2-2.0.0-win32/configure	2021-11-27 20:52:57.879038700 +0100
@@ -1,4 +1,5 @@
 #! /bin/sh
+exit 0
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.65 for MP4v2 2.0.0.
 #
diff -Naur mp4v2-2.0.0/include/mp4v2/platform.h mp4v2-2.0.0-win32/include/mp4v2/platform.h
--- mp4v2-2.0.0/include/mp4v2/platform.h	2012-05-21 01:11:52.000000000 +0200
+++ mp4v2-2.0.0-win32/include/mp4v2/platform.h	2021-11-27 20:52:57.926414700 +0100
@@ -6,25 +6,7 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <stdarg.h>
-
-// Thanks, MSFT, for making C99 a total PITA.  Declare this not to define any stdint stuff; this is useful
-// if you're going to be using mp4v2 on windows with some other library that defines its own stdint.
-// TODO msft has finally re-included stdint in vs2010, so maybe at some point in the future this won't be needed.
-#ifndef MP4V2_NO_STDINT_DEFS
-    #if defined( _WIN32 ) && !defined( __MINGW32__ )
-        typedef char      int8_t;
-        typedef short     int16_t;
-        typedef int       int32_t;
-        typedef long long int64_t;
-
-        typedef unsigned char      uint8_t;
-        typedef unsigned short     uint16_t;
-        typedef unsigned int       uint32_t;
-        typedef unsigned long long uint64_t;
-    #else
-        #include <stdint.h>
-    #endif
-#endif
+#include <stdint.h>
 
 #if defined( _WIN32 ) || defined( __MINGW32__ )
 #   if defined( MP4V2_EXPORTS )
diff -Naur mp4v2-2.0.0/libplatform/Makefile mp4v2-2.0.0-win32/libplatform/Makefile
--- mp4v2-2.0.0/libplatform/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ mp4v2-2.0.0-win32/libplatform/Makefile	2021-11-27 20:52:57.928177200 +0100
@@ -0,0 +1,20 @@
+########## MP4v2 directory makefile ##########
+
+OBJECTS	  = io/File.o io/File_win32.o io/FileSystem.o io/FileSystem_win32.o number/random_win32.o process/process_win32.o prog/option.o sys/error.o time/time.o time/time_win32.o platform_win32.o
+
+INCDIR	  = ..
+LIBDIR	  = .
+LIB	  = $(LIBDIR)/libplatform.a
+
+CXXOPTS	  = $(CXXFLAGS) -I$(INCDIR) -I../include
+
+all : $(LIB)
+
+$(LIB) : $(OBJECTS)
+	ar cru $(LIB) $(OBJECTS)
+
+clean :
+	rm -f $(LIB) $(OBJECTS)
+
+.cpp.o:
+	$(CXX) $(CXXOPTS) -c $< -o $@
diff -Naur mp4v2-2.0.0/libplatform/io/File.h mp4v2-2.0.0-win32/libplatform/io/File.h
--- mp4v2-2.0.0/libplatform/io/File.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/io/File.h	2021-11-27 20:52:57.931178100 +0100
@@ -5,7 +5,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class MP4V2_EXPORT FileProvider
+class FileProvider
 {
 public:
     static FileProvider& standard();
@@ -47,7 +47,7 @@
 ///
 ///////////////////////////////////////////////////////////////////////////////
 
-class MP4V2_EXPORT File : public FileProvider
+class File : public FileProvider
 {
 public:
     ///////////////////////////////////////////////////////////////////////////
diff -Naur mp4v2-2.0.0/libplatform/io/FileSystem.h mp4v2-2.0.0-win32/libplatform/io/FileSystem.h
--- mp4v2-2.0.0/libplatform/io/FileSystem.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/io/FileSystem.h	2021-11-27 20:52:57.936177700 +0100
@@ -10,7 +10,7 @@
 /// FileSystem abstracts operations on files and directories.
 ///
 ///////////////////////////////////////////////////////////////////////////////
-class MP4V2_EXPORT FileSystem
+class FileSystem
 {
 public:
     static string DIR_SEPARATOR;  //!< separator string used in file pathnames
diff -Naur mp4v2-2.0.0/libplatform/io/File_win32.cpp mp4v2-2.0.0-win32/libplatform/io/File_win32.cpp
--- mp4v2-2.0.0/libplatform/io/File_win32.cpp	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/io/File_win32.cpp	2021-11-27 20:52:57.941192900 +0100
@@ -6,23 +6,6 @@
     using namespace impl;
 }
 
-/**
- * Set this to 1 to compile in extra debugging
- */
-#define EXTRA_DEBUG 0
-
-/**
- * @def LOG_PRINTF
- *
- * call log.printf if EXTRA_DEBUG is defined to 1.  Do
- * nothing otherwise
- */
-#if EXTRA_DEBUG
-#define LOG_PRINTF(X) log.printf X
-#else
-#define LOG_PRINTF(X)
-#endif
-
 namespace mp4v2 { namespace platform { namespace io {
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -39,18 +22,13 @@
     bool close();
 
 private:
-    HANDLE _handle;
-
-    /**
-     * The UTF-8 encoded file name
-     */
-    std::string _name;
+    FILE* _file;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 StandardFileProvider::StandardFileProvider()
-    : _handle( INVALID_HANDLE_VALUE )
+    : _file( NULL )
 {
 }
 
@@ -66,30 +44,20 @@
 bool
 StandardFileProvider::open( std::string name, Mode mode )
 {
-    DWORD access = 0;
-    DWORD share  = 0;
-    DWORD crdisp = 0;
-    DWORD flags  = FILE_ATTRIBUTE_NORMAL;
-
+    const wchar_t *om;
     switch( mode ) {
         case MODE_UNDEFINED:
         case MODE_READ:
         default:
-            access |= GENERIC_READ;
-            share  |= FILE_SHARE_READ;
-            crdisp |= OPEN_EXISTING;
+            om = L"rbN";
             break;
 
         case MODE_MODIFY:
-            access |= GENERIC_READ | GENERIC_WRITE;
-            share  |= FILE_SHARE_READ;
-            crdisp |= OPEN_EXISTING;
+            om = L"r+bN";
             break;
 
         case MODE_CREATE:
-            access |= GENERIC_READ | GENERIC_WRITE;
-            share  |= FILE_SHARE_READ;
-            crdisp |= CREATE_ALWAYS;
+            om = L"w+bN";
             break;
     }
 
@@ -102,20 +70,9 @@
     }
 
     ASSERT(LPCWSTR(filename));
-    _handle = CreateFileW( filename, access, share, NULL, crdisp, flags, NULL );
-    if (_handle == INVALID_HANDLE_VALUE)
-    {
-        log.errorf("%s: CreateFileW(%s) failed (%d)",__FUNCTION__,filename.utf8.c_str(),GetLastError());
-        return true;
-    }
-
-    /*
-    ** Make a copy of the name for future log messages, etc.
-    */
-    log.verbose2f("%s: CreateFileW(%s) succeeded",__FUNCTION__,filename.utf8.c_str());
 
-    _name = filename.utf8;
-    return false;
+    _file = _wfopen( filename, om );
+    return (_file == NULL);
 }
 
 /**
@@ -130,19 +87,7 @@
 bool
 StandardFileProvider::seek( Size pos )
 {
-    LARGE_INTEGER n;
-
-    ASSERT(_handle != INVALID_HANDLE_VALUE);
-
-    n.QuadPart = pos;
-    if (!SetFilePointerEx( _handle, n, NULL, FILE_BEGIN ))
-    {
-        log.errorf("%s: SetFilePointerEx(%s,%" PRId64 ") failed (%d)",__FUNCTION__,_name.c_str(),
-                                pos,GetLastError());
-        return true;
-    }
-
-    return false;
+    return _fseeki64(_file, pos, SEEK_SET);
 }
 
 /**
@@ -161,23 +106,10 @@
 bool
 StandardFileProvider::read( void* buffer, Size size, Size& nin, Size maxChunkSize )
 {
-    DWORD nread = 0;
-
-    ASSERT(_handle != INVALID_HANDLE_VALUE);
-
-    // ReadFile takes a DWORD for number of bytes to read so
-    // make sure we're not asking for more than fits.
-    // MAXDWORD from WinNT.h.
-    ASSERT(size <= MAXDWORD);
-    if( ReadFile( _handle, buffer, (DWORD)(size & MAXDWORD), &nread, NULL ) == 0 )
-    {
-        log.errorf("%s: ReadFile(%s,%d) failed (%d)",__FUNCTION__,_name.c_str(),
-                   (DWORD)(size & MAXDWORD),GetLastError());
+    Size count = fread(buffer, 1, size, _file);
+    if( ferror(_file) )
         return true;
-    }
-    LOG_PRINTF((MP4_LOG_VERBOSE3,"%s: ReadFile(%s,%d) succeeded: read %d byte(s)",__FUNCTION__,
-               _name.c_str(),(DWORD)(size & MAXDWORD),nread));
-    nin = nread;
+    nin = count;
     return false;
 }
 
@@ -197,23 +129,10 @@
 bool
 StandardFileProvider::write( const void* buffer, Size size, Size& nout, Size maxChunkSize )
 {
-    DWORD nwrote = 0;
-
-    ASSERT(_handle != INVALID_HANDLE_VALUE);
-
-    // ReadFile takes a DWORD for number of bytes to read so
-    // make sure we're not asking for more than fits.
-    // MAXDWORD from WinNT.h.
-    ASSERT(size <= MAXDWORD);
-    if( WriteFile( _handle, buffer, (DWORD)(size & MAXDWORD), &nwrote, NULL ) == 0 )
-    {
-        log.errorf("%s: WriteFile(%s,%d) failed (%d)",__FUNCTION__,_name.c_str(),
-                   (DWORD)(size & MAXDWORD),GetLastError());
+    Size count = fwrite(buffer, 1, size, _file);
+    if( ferror(_file) )
         return true;
-    }
-    log.verbose2f("%s: WriteFile(%s,%d) succeeded: wrote %d byte(s)",__FUNCTION__,
-                  _name.c_str(),(DWORD)(size & MAXDWORD),nwrote);
-    nout = nwrote;
+    nout = count;
     return false;
 }
 
@@ -226,23 +145,7 @@
 bool
 StandardFileProvider::close()
 {
-    BOOL retval;
-
-    retval = CloseHandle( _handle );
-    if (!retval)
-    {
-        log.errorf("%s: CloseHandle(%s) failed (%d)",__FUNCTION__,
-                   _name.c_str(),GetLastError());
-    }
-
-    // Whether we succeeded or not, clear the handle and
-    // forget the name
-    _handle = INVALID_HANDLE_VALUE;
-    _name.clear();
-
-    // CloseHandle return 0/false to indicate failure, but
-    // we return 0/false to indicate success, so negate.
-    return !retval;
+    return fclose(_file);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff -Naur mp4v2-2.0.0/libplatform/number/random.h mp4v2-2.0.0-win32/libplatform/number/random.h
--- mp4v2-2.0.0/libplatform/number/random.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/number/random.h	2021-11-27 20:52:57.944568100 +0100
@@ -6,10 +6,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /// Generate 32-bit pseudo-random number.
-MP4V2_EXPORT uint32_t random32();
+uint32_t random32();
 
 /// Seed pseudo-random number generator.
-MP4V2_EXPORT void srandom( uint32_t );
+void srandom( uint32_t );
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff -Naur mp4v2-2.0.0/libplatform/platform_win32.cpp mp4v2-2.0.0-win32/libplatform/platform_win32.cpp
--- mp4v2-2.0.0/libplatform/platform_win32.cpp	1970-01-01 01:00:00.000000000 +0100
+++ mp4v2-2.0.0-win32/libplatform/platform_win32.cpp	2021-11-27 20:52:57.950585900 +0100
@@ -0,0 +1,1091 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  The contents of this file are subject to the Mozilla Public License
+//  Version 1.1 (the "License"); you may not use this file except in
+//  compliance with the License. You may obtain a copy of the License at
+//  http://www.mozilla.org/MPL/
+//
+//  Software distributed under the License is distributed on an "AS IS"
+//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+//  License for the specific language governing rights and limitations
+//  under the License.
+// 
+//  The Original Code is MP4v2.
+// 
+//  The Initial Developer of the Original Code is David Byron.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */
+#include <algorithm> /* for replace */
+#include <windows.h>
+
+namespace mp4v2 {
+    using namespace impl;
+}
+
+/**
+ * Set this to 1 to compile in extra debugging
+ */
+#define EXTRA_DEBUG 0
+
+/**
+ * @def LOG_PRINTF
+ *
+ * call log.printf if EXTRA_DEBUG is defined to 1.  Do
+ * nothing otherwise
+ */
+#if EXTRA_DEBUG
+#define LOG_PRINTF(X) log.printf X
+#else
+#define LOG_PRINTF(X)
+#endif
+
+/**
+ * Section 2.13 "Special Characters and Noncharacters" of
+ * _The Unicode Standard, Version 5.0_
+ * (http://www.unicode.org/versions/Unicode5.0.0/bookmarks.html)
+ * defines "The Replacement Character" U+FFFD as the
+ * "general substitute character" that "can be substituted
+ * for any 'unknown' character in another encoding that can
+ * not be mapped in terms of known Unicode characters"
+ *
+ * See also section D.7 of 10646.
+ */
+#define REPLACEMENT_CHAR    0xFFFD
+
+namespace mp4v2 { namespace platform { namespace win32 {
+
+/**
+ * A structure to store the number of characters required to
+ * encode a particular UCS-4 character in UTF-8
+ */
+struct utf8_len_info
+{
+    /**
+     * This structure applies to a number >= @p range_min.
+     */
+    UINT32      range_min;
+
+    /**
+     * This structure applies to a number <= @p range_max.
+     */
+    UINT32      range_max;
+
+    /**
+     * The number of characters required to encode a number
+     * in [@p range_min,@p range_max] as UTF-8.
+     */
+    size_t      num_chars;
+};
+
+/**
+ * A structure to store the number of characters required to
+ * encode a particular UCS-4 character in UTF-8.  For now
+ * we're using wide characters (which according to
+ * http://msdn.microsoft.com/en-us/library/ms776414.aspx
+ * means UTF-16 since Windows 2000) so we're only using up
+ * to 4-byte UTF-8 sequences.  Parts of the range aren't
+ * valid (e.g. [U+D800,U+DFFF] but that's handled elsewhere.
+ */
+static struct utf8_len_info s_len_info[] =
+{
+    { 0x00000000, 0x0000007F, 1 },
+    { 0x00000080, 0x000007FF, 2 },
+    { 0x00000800, 0x0000FFFF, 3 },
+    { 0x00010000, 0x001FFFFF, 4 },
+    { 0x00200000, 0x03FFFFFF, 5 },
+    { 0x04000000, 0x7FFFFFFF, 6 }
+};
+
+/**
+ * Utf8ToFilename constructor
+ *
+ * @param utf8string a UTF-8 encoded string that does not
+ * begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @see IsValidUTF16 to see whether the constructor
+ * succeeded
+ */
+Utf8ToFilename::Utf8ToFilename( const string &utf8string )
+    : _wideCharString( NULL )
+      , utf8( _utf8 )
+{
+    // See
+    // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
+    // for notes about path lengths, prefixes, etc.  The
+    // goal is to support the longest path possible.
+    // Relative paths are limited to 260 characters but
+    // absolute paths can be up to about 32767
+    // characters if properly prefixed.
+
+    // If utf8string is a relative path, convert it to
+    // UTF-16 and be done.
+    if (!IsAbsolute(utf8string))
+    {
+        _wideCharString = ConvertToUTF16(utf8string);
+        return;
+    }
+
+    // Since the prefix has backslashes, convert any forward
+    // slashes in utf8string to backslashes to keep Windows
+    // happy
+    const string *utf8ToUse = &utf8string;
+    string forwardSlash;
+
+    if (utf8string.find('/') != std::string::npos)
+    {
+        forwardSlash = utf8string;
+        std::replace(forwardSlash.begin(),forwardSlash.end(),'/','\\');
+        utf8ToUse = &forwardSlash;
+    }
+    ASSERT(utf8ToUse);
+    ASSERT((*utf8ToUse).length() > 0);
+
+    // utf8string is an absolute path.  It could be a
+    // UNC path (\\host\path).  The prefix is different
+    // for UNC paths than it is for non-UNC paths.
+    string prefixedPath;
+
+    if (IsUncPath(*utf8ToUse))
+    {
+        // utf8string begins with two backslashes, but
+        // with a prefix we only need one so we can't
+        // just prepend a prefix.
+        prefixedPath = "\\\\?\\UNC" + (*utf8ToUse).substr(1);
+    }
+    else
+    {
+        prefixedPath = "\\\\?\\" + *utf8ToUse;
+    }
+
+    // Transform prefixedPath to UTF-16 so it's
+    // appropriate for CreateFileW
+    _wideCharString = ConvertToUTF16(prefixedPath);
+}
+
+Utf8ToFilename::~Utf8ToFilename( )
+{
+    if( _wideCharString != NULL )
+    {
+        free(_wideCharString);
+        _wideCharString = NULL;
+    }
+}
+
+/**
+ * Convert a UTF-8 encoded string to a UTF-16 string
+ *
+ * @param utf8 the NUL-terminated UTF-8 string to decode
+ *
+ * @retval NULL error allocating memory for UTF-16 string
+ *
+ * @retval non-NULL NUL-terminated UTF-16 version of @p
+ * utf8.  Invalid portions of UTF-8 are represented by a
+ * replacement character U+FFFD.  The caller is
+ * responsible for freeing this memory.
+ */
+wchar_t *
+Utf8ToFilename::ConvertToUTF16 ( const string &utf8string )
+{
+    int         num_bytes;
+    size_t      num_chars;
+    wchar_t     *retval;
+
+    ASSERT(sizeof(wchar_t) == 2);
+
+    // Store the utf8 string in our member variable so it's
+    // available
+    _utf8 = utf8string;
+
+    // We need to find out how many characters we're dealing
+    // with so we know how much memory to allocate.  At the
+    // same time, it's possible that the string we've been
+    // given isn't valid UTF-8.  So, just use the length of
+    // the string we've been given as the number of
+    // characters to allocate.  The decoded string can't be
+    // longer than this, even taking into account surrogate
+    // pairs since they require 4 UTF-8 characters but only
+    // two UTF-16 character elements.
+    num_chars = utf8string.length();
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: entry point (%d character string)",
+                __FUNCTION__,num_chars));
+
+    /*
+    ** Allocate space for the decoded string.  Add one
+    ** for the NUL terminator.
+    */
+    num_bytes = (num_chars + 1) * sizeof(wchar_t);
+    retval = (wchar_t *)malloc(num_bytes);
+    if (!retval)
+    {
+        log.errorf("%s: error allocating memory for %d byte(s)",__FUNCTION__,num_bytes);
+        return NULL;
+    }
+
+    /*
+    ** ConvertToUTF16Buf zeroes out the memory so don't
+    ** do it here
+    */
+
+    // ConvertToUTF16Buf shouldn't fail if we allocated
+    // enough memory for the entire string.  Check
+    // anyway just to be safe.
+    if (!ConvertToUTF16Buf(utf8string.c_str(),retval,num_bytes))
+    {
+        // But ASSERT so we can find the problem and fix
+        // it.
+        ASSERT(0);
+        free(retval);
+        retval = NULL;
+        return NULL;
+    }
+
+    return retval;
+}
+
+/**
+ * Convert a UTF-8 encoded string to a UTF-16 string in
+ * a previously allocated buffer.
+ *
+ * @param utf8 the NUL-terminated UTF-8 string to decode
+ *
+ * @param utf16_buf the buffer in which to place the
+ * UTF-16 version of @p utf8.  If there's enough space
+ * to hold a NUL terminator, @p utf16_buf contains one.
+ * If not, @p utf16_buf is not NUL terminated.
+ *
+ * @param num_bytes the number of bytes that @p
+ * utf16_str points to
+ *
+ * @retval 0 error converting @p name to UTF-16,
+ * including when @p utf8 requires more space to encode
+ * in UTF-16 than indicated by @p num_bytes.  In that
+ * case, @p utf16_buf contains the UTF-16 encoding of as
+ * much of @p utf8 as possible.
+ *
+ * @retval 1 successfully converted @p name to @p UTF-16
+ * in @p utf16_buf. wide character (UTF-16) version of
+ * @p Invalid portions of UTF-8 are represented by a
+ * replacement character U+FFFD.
+ */
+int
+Utf8ToFilename::ConvertToUTF16Buf ( const char      *utf8,
+                                    wchar_t         *utf16_buf,
+                                    size_t          num_bytes )
+{
+    size_t      i;
+    const UINT8 *next_char;
+    size_t      num_chars;
+    size_t      num_utf16_chars;
+    size_t      num_input_bytes;
+    const UINT8 *p;
+    wchar_t     this_utf16[2];
+
+    ASSERT(utf8);
+    ASSERT(utf16_buf || (num_bytes == 0));
+    ASSERT(sizeof(wchar_t) == 2);
+
+    ASSERT(num_bytes % sizeof(wchar_t) == 0);
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: converting \"%s\"",__FUNCTION__,utf8));
+
+    num_chars = strlen(utf8);
+
+    // If the input is NUL-terminated (which it better
+    // be), the NUL-terminator is a valid input byte as
+    // well
+    num_input_bytes = num_chars + 1;
+
+    // Make sure the buffer we've been given is long
+    // enough.  We might need one UTF-16 character for
+    // every UTF-8 character.  And one more for the NUL
+    // terminator.
+    //
+    // Here, check that there's room for a NUL
+    // terminator in the output string.  This makes it
+    // safe to dereference p in the while loop below.
+    // It's probably enough to check num_bytes == 0 here
+    // but if we did that we'd have to change the error
+    // message after the while loop to be less specific.
+    // This way we give the caller more info about the
+    // input string.
+    if (num_bytes < sizeof(wchar_t))
+    {
+        log.errorf("%s: %u byte(s) is not enough to transform a %u byte UTF-8 string "
+                   "to NUL-terminated UTF-16",__FUNCTION__,num_bytes,num_input_bytes);
+        return 0;
+    }
+
+    ASSERT(num_bytes > 0);
+    ASSERT(utf16_buf);
+    memset(utf16_buf,0,num_bytes);
+
+    // The number of UTF-16 characters we've got space for
+    // in utf16_buf
+    num_utf16_chars = num_bytes / sizeof(wchar_t);
+
+    p = (const UINT8 *)utf8;
+    i = 0;
+    while (*p && (i < num_utf16_chars))
+    {
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: decoding first UTF-8 byte 0x%02X (UTF-16 "
+                    "character %d of at most %d)",__FUNCTION__,*p,(i + 1),
+                    num_utf16_chars));
+
+        memset(this_utf16,0,sizeof(this_utf16));
+
+        // This function decodes illegal bytes/sequences
+        // with a replacement character and returns the
+        // pointer to the next character to decode.  Pass
+        // NULL since we don't care about detecting invalid
+        // characters here.
+        next_char = Utf8DecodeChar(p,num_input_bytes,this_utf16,NULL);
+
+        // We've always got one character to assign
+        utf16_buf[i++] = this_utf16[0];
+
+        // If we're dealing with a surrogate pair,
+        // assign the low half too
+        if (this_utf16[1])
+        {
+            // We may not have any more room in the
+            // UTF-16 buffer.  Check to make sure we
+            // don't step on someone else's memory.  We
+            // need to return failure here instead of
+            // depending on our other logic to do it for
+            // us.  We'll get out of the while loop with
+            // no extra code, but if we're dealing with
+            // the UTF-16 encoding of the last character
+            // in the input string, there won't appear
+            // to be anything wrong.
+            if (i >= num_utf16_chars)
+            {
+                log.errorf("%s: out of space in %u  byte output string to store surrogate "
+                           "pair low half (0x%04X)",__FUNCTION__,num_bytes,this_utf16[1]);
+                return 0;
+            }
+             
+            utf16_buf[i++] = this_utf16[1];
+        }
+
+        // Put this here to make it brutally clear that
+        // the cast is safe
+        ASSERT(next_char >= p);
+        num_input_bytes -= (size_t)(next_char - p);
+        p = next_char;
+    }
+
+    if (*p)
+    {
+        // Since num_input_bytes includes 1 for the
+        // NUL-terminator, it's got to be bigger than
+        // one here.
+        ASSERT(num_input_bytes > 1);
+        log.errorf("%s: %u byte(s) of input string remain(s) undecoded (%s): out of space in "
+                   "%u byte output string",__FUNCTION__,(num_input_bytes - 1),p,num_bytes);
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * Accessor for the length of a prefix (i.e. \\\?\\ or
+ * \\\?\\UNC\\) that begins a filename
+ *
+ * @param utf8string the UTF-8 encoded filename to
+ * examine
+ *
+ * @return the length of the prefix of @p utf8string in
+ * characters
+ */
+int
+Utf8ToFilename::GetPrefixLen ( const string &utf8string )
+{
+    if (utf8string.find("\\\\?\\") == 0)
+    {
+        return strlen("\\\\?\\");
+    }
+
+    if (utf8string.find("\\\\?\\UNC\\") == 0)
+    {
+        return strlen("\\\\?\\UNC\\");
+    }
+
+    return 0;
+}
+
+/**
+ * Determine if a path is absolute or not
+ *
+ * @param utf8string the UTF-8 encoded path to examine
+ * that does not begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @retval 0 @p utf8string is not an absolute path
+ * @retval 1 @p utf8string is an absolute path
+ */       
+int
+Utf8ToFilename::IsAbsolute ( const string &utf8string )
+{
+    // Assume utf8string doesn't already start with a
+    // long filename prefix (i.e. \\?\ or \\?\UNC\)
+    // since the logic here depends on that.
+    ASSERT(GetPrefixLen(utf8string) == 0);
+
+    // Is an empty string absolute or relative?  It's
+    // not absolute since we can't tell what
+    // drive/volume it's for so say it's relative.
+    if (utf8string.length() == 0)
+    {
+        return 0;
+    }
+        
+    // Here we're looking for:
+    //  x:   drive relative
+    //  x:\  absolute path
+    if (utf8string[1] == ':')
+    {
+        // It starts with x:, but is it x:/ ?
+        if ((utf8string.length() >= 2) && IsPathSeparator(utf8string[2]))
+        {
+            // Yup -- it's absolute
+            return 1;
+        }
+
+        // Nope, not x:/, just x:something
+        return 0;
+    }
+
+    // UNC paths are absolute paths too
+    return IsUncPath(utf8string);
+}
+
+/**
+ * Determine if a character is a valid path separator
+ *
+ * @param c the character to check
+ *
+ * @retval 0 @p c is not a valid path separator
+ * @retval 1 @p c is a valid path separator
+ */
+int
+Utf8ToFilename::IsPathSeparator ( char c )
+{
+    return ((c == '\\') || (c == '/'));
+}
+
+/**
+ * Determine if a path is a UNC path
+ *
+ * @param utf8string the UTF-8 encoded path to examine
+ * that does not begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @retval 0 @p utf8string is not a UNC path
+ * @retval 1 @p utf8string is a UNC path
+ */       
+int
+Utf8ToFilename::IsUncPath ( const string &utf8string )
+{
+    const char  *host;
+    int         num_slashes;
+    const char  *p;
+
+    // Assume utf8string doesn't already start with a
+    // long filename prefix (i.e. \\?\ or \\?\UNC\)
+    // since the logic here depends on that.
+    ASSERT(GetPrefixLen(utf8string) == 0);
+
+    // Is an empty string a UNC path?  No.
+    if (utf8string.length() == 0)
+    {
+        return 0;
+    }
+
+    //  Recognize:
+    //    //volume/path
+    //    \\volume\path
+    if (!IsPathSeparator(utf8string[0]))
+    {
+        // If it doesn't start with a path separator, it's
+        // not a UNC path.
+        return 0;
+    }
+
+    // The path starts with a slash, so it could be a UNC
+    // path.  See if it starts with two slashes...Be careful
+    // though, it might have more than 2 slashes.
+    p = utf8string.c_str();
+    num_slashes = 0;
+    while (*p && IsPathSeparator(*p))
+    {
+        num_slashes++;
+        p++;
+    }
+
+    // We found a slash at the beginning so we better have
+    // at least one here
+    ASSERT(num_slashes >= 1);
+    if ((num_slashes > 2) || !(*p))
+    {
+        // If we've got more than two slashes or we've
+        // run off the end of the string (///foo or
+        // //)...who knows how the OS will handle it,
+        // but it's not a UNC path.
+        log.errorf("%s: don't understand path(%s)",__FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+
+    // If we've only got one slash, it looks like a
+    // drive relative path.  If it's something like
+    // /foo//bar it's not clear how the OS handles it,
+    // but that's someone else's problem.  It's not a
+    // UNC path.
+    if (num_slashes == 1)
+    {
+        return 0;
+    }
+    
+    // If we're here, we've got two slashes followed by
+    // a non-slash.  Something like //foo.  To be a
+    // proper UNC path, we need to see a hostname
+    // (e.g. foo), and then another slash.  If not, it's
+    // not a UNC path.
+    ASSERT(num_slashes == 2);
+
+    // Tempting to use STRTOK_R here, but that modifies
+    // the original string.  Instead of making a copy,
+    // search manually.
+    host = p;
+    while (*p && !IsPathSeparator(*p))
+    {
+        p++;
+    }
+
+    // We checked for separators above, so we better
+    // have moved on at least a bit
+    ASSERT(host != p);
+    if (!(*p))
+    {
+        // We ran off the end of the string without finding
+        // another separator.  So, we've got something like
+        // 
+        //  //foobar
+        // 
+        // which isn't a UNC path.
+        log.warningf("%s: incomplete UNC path: host only(%s)",__FUNCTION__,
+                     utf8string.c_str());
+        return 0;
+    }
+
+    // p points to a separator, so...we've got one of:
+    //  //host//
+    //  //host//blah
+    //  //host/bar
+    //
+    // Of these, only the last is a proper UNC path.  See
+    // what we've got after p.
+    num_slashes = 0;
+    while (*p && IsPathSeparator(*p))
+    {
+        num_slashes++;
+        p++;
+    }
+
+    // We better have at least one slash or our logic is
+    // broken
+    ASSERT(num_slashes >= 1);
+    if (!(*p))
+    {
+        // //host// (or maybe //host///), but no path
+        // part after the host
+        log.warningf("%s: incomplete UNC path: no path after host(%s)",
+                     __FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+
+    if (num_slashes > 1)
+    {
+        // Another busted case //host//blah or
+        // //host///blah, etc.
+        log.warningf("%s: invalid UNC path: too many slashes after host(%s)",
+                     __FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+    
+    // If we're here it means num_slashes is exactly 1
+    // so we've got //host/something so we're calling
+    // that a UNC path.
+    return 1;
+}
+
+/**
+ * Accessor for whether the UTF-16 encoded string is valid
+ *
+ * @retval false the UTF-16 encoded string is not valid
+ * @retval true the UTF-16 encoded string is valid
+ */
+bool
+Utf8ToFilename::IsUTF16Valid( ) const
+{
+    return (_wideCharString ? true : false);
+}
+
+/**
+ * Decode one UTF-8 encoded character into a UTF-16
+ * character.  The trouble here is that UTF-16 is really a
+ * variable length encoding to handle surrogate pairs
+ * (0xD800 --> 0xDFFF).  This way UTF-16 can handle more
+ * than 2^16 characters.  So we need to be careful.  UCS-2
+ * is a fixed width (16-bit) encoding that we could use, but
+ * then we can only handle 2^16 characters (the BMP).  To
+ * handle all 2^21 characters, we need UTF-16.
+ *
+ * What does Windows really use?  UTF-16.  See
+ * http://unicode.org/iuc/iuc17/b2/slides.ppt for a
+ * discussion.
+ * http://discuss.fogcreek.com/joelonsoftware5/default.asp?cmd=show&ixPost=168543
+ * also has some info.
+ *
+ * @param utf8_char the UTF-8 character to decode, possibly
+ * occupying multiple bytes, not necessarily NUL terminated
+ *
+ * @param num_bytes the number of bytes that @p utf8_char
+ * points to (must be > 0)
+ *
+ * @param utf16 populated with the UTF-16 equivalent of @p
+ * utf8_char.  Note that this must point to at least 2
+ * wchar_t's of memory so there's room to hold a surrogate
+ * pair.
+ *
+ * @param invalid populated with 1 if @p utf8_char doesn't
+ * point to a valid UTF-8 encoded character, 0 if @p
+ * utf8_char is valid.
+ *
+ * @return the next byte to examine for subsequent decoding
+ * (some number of bytes after @p utf8_char).  This may not
+ * be valid to dereference depending on the value of @p
+ * num_bytes.
+ */
+const UINT8 *
+Utf8ToFilename::Utf8DecodeChar ( const UINT8    *utf8_char,
+                                 size_t         num_bytes,
+                                 wchar_t        *utf16,
+                                 int            *invalid )
+
+{
+    wchar_t     high_half;
+    int         i;
+    UINT8       len;
+    wchar_t     low_half;
+    UINT8       mask;
+    const UINT8 *p;
+    UINT32      ucs4;
+    int         valid_len;
+
+    ASSERT(utf8_char);
+    ASSERT(num_bytes > 0);
+    ASSERT(utf16);
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: decoding UTF-8 string at address 0x%p",
+                __FUNCTION__,utf8_char));
+
+    /*
+    ** Assume utf8_char is invalid until we learn otherwise
+    */
+    if (invalid)
+    {
+        *invalid = 1;
+    }
+
+    /*
+    ** Traverse the UTF-8 encoding and figure out what we've
+    ** got.
+    */
+    p = (const UINT8 *)(utf8_char);
+
+    /*
+    ** This is the number of bytes we expect based on the
+    ** first octet.  If subsequent bytes are NUL or invalid,
+    ** then it may not the same as the actual len.
+    */
+    len = Utf8NumOctets(*p);
+    if (len == 0)
+    {
+        log.errorf("%s: 0x%02X is not a valid first byte of a UTF-8 encoded character",__FUNCTION__,*p);
+
+        /*
+        ** Use the replacement character and advance past
+        ** the invalid byte
+        */
+        *utf16 = REPLACEMENT_CHAR;
+        return p + 1;
+    }
+
+    /*
+    ** Handle one byte encodings in a special case.  See
+    ** below for an explanation of how we mask successive
+    ** bytes of an encoding to see why.  We're depending on
+    ** the validation in Utf8NumOctets here to make this OK.
+    */
+    if (len == 1)
+    {
+        /*
+        ** There's no intermediate UCS-4 step here.  We go
+        ** straight to UTF-16 since they're the same.
+        */
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: one byte UTF-16 encoding: 0x%02X",
+                    __FUNCTION__,*p));
+        *utf16 = *p;
+        if (invalid)
+        {
+            *invalid = 0;
+        }
+        return p + 1;
+    }
+
+    /*
+    ** Make sure we've got enough bytes in our input string
+    ** to form a valid UTF-8 character
+    */
+    if (len > num_bytes)
+    {
+        log.errorf("%s: first byte 0x%02X indicates a %d byte "
+                   "UTF-8 character, but we only have %u valid byte(s)",
+                   __FUNCTION__,*p,len,num_bytes);
+        *utf16 = REPLACEMENT_CHAR;
+        return p + 1;
+    }
+
+    /*
+    ** Traverse the bytes that should be part of this UTF-8
+    ** encoded character and make sure we don't have an
+    ** overlength encoding, and make sure that each
+    ** character is valid.
+    */
+
+    /*
+    ** As we traverse each character, we mask off the
+    ** appropriate number of bits and include them in the
+    ** overall result.
+    **
+    ** 1 byte encoding [U+00000000,U+0000007F]: 7 bits (7 bits total) (handled above)
+    ** 2 byte encoding [U+00000080,U+000007FF]: 5 bits, 6 bits (11 bits total)
+    ** 3 byte encoding [U+00000800,U+0000FFFF]: 4 bits, 6 bits, 6 bits (16 bits total)
+    ** 4 byte encoding [U+00010000,U+001FFFFF]: 3 bits, 6 bits, 6 bits, 6 bits (21 bits total)
+    ** 5 byte encoding [U+00200000,U+03FFFFFF]: 2 bits, 6 bits, 6 bits, 6 bits, 6 bits (26 bits total)
+    ** 6 byte encoding [U+04000000,U+7FFFFFFF]: 1 bit, 6 bits, 6 bits, 6 bits, 6 bits, 6 bits (31 bits total)
+    **
+    ** So, mask the initial byte appropriately, then take
+    ** the bottom 6 bits from the remaining bytes.  To be
+    ** brutally explicit, the first byte mask is:
+    **
+    ** 1 byte encoding: 0x7F (or 0x80 - 1) (or (1 << 7) - 1)
+    ** 2 byte encoding: 0x1F (or 0x20 - 1) (or (1 << 5) - 1)
+    ** 3 byte encoding: 0x0F (or 0x10 - 1) (or (1 << 4) - 1)
+    ** 4 byte encoding: 0x07 (or 0x08 - 1) (or (1 << 3) - 1)
+    ** 5 byte encoding: 0x03 (or 0x04 - 1) (or (1 << 2) - 1)
+    ** 6 byte encoding: 0x01 (or 0x02 - 1) (or (1 << 1) - 1)
+    **    
+    ** So, the one byte encoding is a special case (again,
+    ** handled above), but for the other lengths, the mask
+    ** is (1 << (7 - len)) - 1.
+    */
+
+    /*
+    ** Handle the first byte of multi-byte encodings since
+    ** it's special
+    */
+    ASSERT(len > 1);
+    ASSERT(len <= 6);
+    mask = (1 << (7 - len)) - 1;
+    ucs4 = *p & mask;
+    p++;
+
+    /*
+    ** Now handle the remaining bytes
+    */
+    for (i = 1;(i < len);i++)
+    {
+        if ((*p < 0x80) || (*p > 0xBF))
+        {
+            log.errorf("%s: 0x%02X is not a valid continuation character in a UTF-8 encoding",
+                       __FUNCTION__,*p);
+
+            /*
+            ** Use the replacement character and return the
+            ** next byte after the invalid sequence as the
+            ** place for subsequent decoding operations.  In
+            ** this case the invalid continuation character
+            ** could be the beginning of the next valid
+            ** sequence, so return that.
+            */
+            *utf16 = REPLACEMENT_CHAR;
+            return p;
+        }
+            
+        /*
+        ** For the remainder of the bytes, shift over what
+        ** we've already got by 6 bits, and then OR in the
+        ** bottom 6 bits of the current byte.
+        */
+        ucs4 = (ucs4 << 6) | (*p & 0x3F);
+        p++;
+    }
+
+    /*
+    ** p is now pointing to the beginning of the next UTF-8
+    ** sequence to decode...
+    */
+
+    /*
+    ** Finally, detect overlong encodings.  For example, a
+    ** line feed (U+000A) should be encoded as 0x0A
+    ** (0b00001010) but could in theory be encoded in UTF-8
+    ** as 0xC0 0x8A (0b10001010).
+    **
+    ** Another example is the forward slash (/) (U+002F).
+    ** It should be encoded as 0x2F, but could in theory be
+    ** encoded in UTF-8 as 0xC0 0xAF (which we'll catch
+    ** because 0xC0 is an invalid first byte of a UTF-8
+    ** encoding), but could also be 0xE0 0x80 0xAF.
+    **
+    ** I can't see any reasonable way to do this other than
+    ** to check the decoded character against its expected
+    ** length
+    */
+    valid_len = Utf8LenFromUcs4(ucs4);
+    if (valid_len == 0)
+    {
+        /*
+        ** This should never happen
+        */
+        log.errorf("%s: decoded a character that we can't encode again (0x%08X)",__FUNCTION__,ucs4);
+        ASSERT(0);
+
+        /*
+        ** If it does, use the replacement character
+        */
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    if (len != valid_len)
+    {
+        ASSERT(len > valid_len);
+        log.errorf("%s: overlong encoding(%s)...should be %d byte(s), not %d",__FUNCTION__,
+                   utf8_char,valid_len,len);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** UTF-16 can only hold 21 bits.  As of now (21-dec-10),
+    ** there's no Unicode code point bigger than 2^21.  To
+    ** be safe, check...
+    */
+    if (ucs4 > 0x0010FFFF)
+    {
+        log.errorf("%s: code point 0x%08X is too big",__FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** Check to make sure we're not working with a "code
+    ** point" that is in the range used to indicate
+    ** surrogate pairs.
+    */
+    if ((ucs4 >= 0x0000D800) && (ucs4 <= 0x0000DFFF))
+    {
+        log.errorf("%s: code point 0x%08X is in the range used to indicate surrogate pairs",
+                   __FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** To (try to) be complete, check for a couple more
+    ** invalid code points
+    */
+    if ((ucs4 == 0x0000FFFF) || (ucs4 == 0x0000FFFE))
+    {
+        log.errorf("%s: invalid code point (0x%08X)",__FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** Finally, convert from UCS-4 to UTF-16.  This may be a
+    ** straightforward assignment, but we have to deal with
+    ** surrogate pairs
+    */
+    if (ucs4 <= 0x0000FFFF)
+    {
+        *utf16 = ucs4 & 0xFFFF;
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: UTF-16 encoding of 0x%08X is 0x%04X",
+                    __FUNCTION__,ucs4,*utf16));
+        if (invalid)
+        {
+            *invalid = 0;
+        }
+        return p;
+    }
+
+    /*
+    ** Transform UCS-4 into a UTF-16 surrogate pair
+    */
+
+    /*
+    ** Grab bits [10,20] (where bit 0 is the LSB) and shift
+    ** them down
+    */
+    high_half = 0xD800 + ((ucs4 - 0x00010000) >> 10);
+
+    /*
+    ** And the bottom 10 bits [0,9]
+    */
+    low_half = 0xDC00 + (ucs4 & 0x03FF);
+
+    utf16[0] = high_half;
+    utf16[1] = low_half;
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: UTF-16 encoding of 0x%08X is 0x%04X:0x%04X",
+                __FUNCTION__,ucs4,utf16[0],utf16[1]));
+
+    if (invalid)
+    {
+        *invalid = 0;
+    }
+
+    return p;
+}
+
+/**
+ * Determine the number of bytes required to hold the UTF-8
+ * encoding of a UCS-4 code point
+ *
+ * @param ucs4 the code point
+ *
+ * @param use_syslog 1 to use syslog, 0 otherwise
+ *
+ * @retval 0 @p ucs4 is not a valid code point
+ *
+ * @retval [1,6] the number of bytes required to hold the
+ * UTF-8 encoding of @p ucs4
+ */
+size_t
+Utf8ToFilename::Utf8LenFromUcs4 ( UINT32 ucs4 )
+{
+    size_t      table_idx;
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: processing UCS-4 code point 0x%08X",
+                __FUNCTION__,ucs4));
+
+    for (table_idx = 0;(table_idx < (sizeof(s_len_info) /
+                                     sizeof(struct utf8_len_info)));
+         table_idx++)
+    {
+        if ((s_len_info[table_idx].range_min <= ucs4) &&
+            (ucs4 <= s_len_info[table_idx].range_max))
+        {
+            return s_len_info[table_idx].num_chars;
+        }
+    }
+
+    log.errorf("%s: 0x%08X is an invalid code point",__FUNCTION__,ucs4);
+
+    return 0;
+}
+
+/**
+ * Determine the number of octets that a UTF-8 encoded
+ * character should occupy based on its first byte
+ *
+ * @param utf8_first_byte the byte to examine
+ *
+ * @retval 0 @p utf8_first_byte is not a valid first byte of
+ * a UTF-8 encoded character
+ *
+ * @retval [1,6] the number of octets that @p
+ * utf8_first_byte should occupy
+ */
+UINT8
+Utf8ToFilename::Utf8NumOctets ( UINT8 utf8_first_byte )
+{
+    /**
+     * Here's a mapping from the first byte of a UTF-8
+     * character to the number of bytes it should contain
+     * based on information from
+     * http://www.unicode.org/versions/corrigendum1.html as
+     * well as
+     * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+     *
+     * [0x00,0x7F]: 1       (0-127)     (128 possible values)
+     * [0x80,0xBF]: invalid (128-191)   (64 possible values)
+     * [0xC0,0xDF]: 2       (192-223)   (32 possible values) (see below)
+     * [0xE0,0xEF]: 3       (224-239)   (16 possible values)
+     * [0xF0,0xF7]: 4       (240 - 247) (8 possible values)
+     * [0xF8,0xFB]: 5       (248 - 251) (4 possible values)
+     * [0xFC,0xFD]: 6       (252 - 253) (2 possible values)
+     * [0xFE,0xFF]: invalid (254 - 255) (2 possible values)
+     *
+     * There's some gray area about 0xC0 and 0xC1.  It's
+     * clear they are invalid first bytes but the question
+     * is how to handle it.  If I reject them here, they'll
+     * get replaced with the REPLACEMENT character.  But, if
+     * I allow them here, it's likely that both this byte
+     * and the subsequent one will get replaced with only
+     * one replacement character.  This is what
+     * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+     * assumes in sections 4.1.1, 4.2.1 and 4.3.1.
+     */
+    if (utf8_first_byte <= 0x7F)
+    {
+        return 1;
+    }
+
+    if ((utf8_first_byte >= 0x80) && (utf8_first_byte <= 0xBF))
+    {
+        return 0;
+    }
+
+    if ((utf8_first_byte >= 0xC0) && (utf8_first_byte <= 0xDF))
+    {
+        return 2;
+    }
+
+    if ((utf8_first_byte >= 0xE0) && (utf8_first_byte <= 0xEF))
+    {
+        return 3;
+    }
+
+    if ((utf8_first_byte >= 0xF0) && (utf8_first_byte <= 0xF7))
+    {
+        return 4;
+    }
+
+    if ((utf8_first_byte >= 0xF8) && (utf8_first_byte <= 0xFB))
+    {
+        return 5;
+    }
+
+    if ((utf8_first_byte >= 0xFC) && (utf8_first_byte <= 0xFD))
+    {
+        return 6;
+    }
+
+    ASSERT((utf8_first_byte == 0xFE) || (utf8_first_byte == 0xFF));
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::win32
diff -Naur mp4v2-2.0.0/libplatform/platform_win32.h mp4v2-2.0.0-win32/libplatform/platform_win32.h
--- mp4v2-2.0.0/libplatform/platform_win32.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/platform_win32.h	2021-11-27 20:52:57.957201300 +0100
@@ -59,15 +59,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // some macros for constant expressions
-#define INT8_C(x)    x
-#define INT16_C(x)   x
-#define INT32_C(x)   x ## L
-#define INT64_C(x)   x ## LL
+#ifndef UINT64_C
+#   define INT8_C(x)    x
+#   define INT16_C(x)   x
+#   define INT32_C(x)   x ## L
+#   define INT64_C(x)   x ## LL
 
-#define UINT8_C(x)   x
-#define UINT16_C(x)  x
-#define UINT32_C(x)  x ## UL
-#define UINT64_C(x)  x ## ULL
+#   define UINT8_C(x)   x
+#   define UINT16_C(x)  x
+#   define UINT32_C(x)  x ## UL
+#   define UINT64_C(x)  x ## ULL
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff -Naur mp4v2-2.0.0/libplatform/platform_win32_impl.h mp4v2-2.0.0-win32/libplatform/platform_win32_impl.h
--- mp4v2-2.0.0/libplatform/platform_win32_impl.h	1970-01-01 01:00:00.000000000 +0100
+++ mp4v2-2.0.0-win32/libplatform/platform_win32_impl.h	2021-11-27 20:52:57.959563300 +0100
@@ -0,0 +1,68 @@
+// Note that we have a separate platform_win32_impl.h to deal with the fact that windows.h defines a macro
+// called FindAtom, which mp4v2 also defines.  In older versions of visual studio, this actually causes
+// some pretty seriously issues with naming collisions and the defined macros (think infamous min/max macro
+// of windows.h vs stdc++'s min/max template functions)
+#include <windows.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace platform { namespace win32 {
+
+class Utf8ToFilename
+{
+    public:
+    Utf8ToFilename( const string &utf8string );
+    ~Utf8ToFilename( );
+
+    bool                IsUTF16Valid( ) const;
+
+    operator LPCWSTR( ) const { return _wideCharString; }
+    operator LPWSTR( ) const { return _wideCharString; }
+
+    private:
+    Utf8ToFilename ( const Utf8ToFilename &src );
+    Utf8ToFilename &operator= ( const Utf8ToFilename &src );
+    
+    wchar_t             *ConvertToUTF16 ( const string &utf8 );
+
+    static int          ConvertToUTF16Buf ( const char      *utf8,
+                                            wchar_t         *utf16_buf,
+                                            size_t          num_bytes );
+    static int          GetPrefixLen ( const string &utf8string );
+
+    static int          IsAbsolute ( const string &utf8string );
+
+    static int          IsPathSeparator ( char c );
+
+    static int          IsUncPath ( const string &utf8string );
+
+    static const UINT8  *Utf8DecodeChar (
+        const UINT8     *utf8_char,
+        size_t          num_bytes,
+        wchar_t         *utf16,
+        int             *invalid
+        );
+
+    static size_t       Utf8LenFromUcs4 ( UINT32 ucs4 );
+
+    static UINT8        Utf8NumOctets ( UINT8 utf8_first_byte );
+
+    /**
+     * The UTF-8 encoding of the filename actually used
+     */
+    string      _utf8;
+
+    /**
+     * The UTF-16 encoding of the filename actually used
+     */
+    wchar_t*    _wideCharString;
+
+    public:
+
+    /**
+     * Accessor for @p _utf8
+     */
+    const string&       utf8;
+};
+
+}}} // namespace mp4v2::platform::win32
diff -Naur mp4v2-2.0.0/libplatform/process/process.h mp4v2-2.0.0-win32/libplatform/process/process.h
--- mp4v2-2.0.0/libplatform/process/process.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/process/process.h	2021-11-27 20:52:57.961686000 +0100
@@ -7,7 +7,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-MP4V2_EXPORT int32_t getpid();
+int32_t getpid();
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff -Naur mp4v2-2.0.0/libplatform/prog/option.h mp4v2-2.0.0-win32/libplatform/prog/option.h
--- mp4v2-2.0.0/libplatform/prog/option.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/prog/option.h	2021-11-27 20:52:57.966702800 +0100
@@ -23,32 +23,32 @@
 
 //! On return from getOption() or getOptionSingle(),
 //! points to an option argument, if it is anticipated.
-MP4V2_EXPORT extern const char* optarg;
+extern const char* optarg;
 
 //! On return from getOption() or getOptionSingle(),
 //! contains the index to the next argv argument for a subsequent call to
 //! getOption() or getOptionSingle().
 //! Initialized to 1 and must be set manually to 1 prior to invoking
 //! getOption() or getOptionSingle() to evaluate multiple sets of arguments.
-MP4V2_EXPORT extern int optind;
+extern int optind;
 
 //! On return from getOption() or getOptionSingle(),
 //! saves the last known option character returned by
 //! getOption() or getOptionSingle().
 //! On error, contains the character/code of option which caused error.
-MP4V2_EXPORT extern int optopt;
+extern int optopt;
 
 //! Initialized to 1 and may be set to 0 to disable error messages.
-MP4V2_EXPORT extern int opterr;
+extern int opterr;
 
 //! Must be set to 1 before evaluating the 2nd or each additional set
 //! of arguments.
-MP4V2_EXPORT extern int optreset;
+extern int optreset;
 
 //! Structure describing a single option.
 //! An instance of Option is required for each program option and is
 //! initialized before use with getOption() or getOptionWord().
-struct MP4V2_EXPORT Option
+struct Option
 {
     //! expectation-type indicating number of arguments expected
     //! on the command-line following the option-argument itself
@@ -130,7 +130,6 @@
 //!     A -1 will be returned when the argument list has been exhausted.
 //!
 ///////////////////////////////////////////////////////////////////////////////
-MP4V2_EXPORT
 int getOption( int argc, char* const* argv, const char* optstr, const Option* longopts, int* idx );
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -164,7 +163,6 @@
 //!     A -1 will be returned when the argument list has been exhausted.
 //!
 ///////////////////////////////////////////////////////////////////////////////
-MP4V2_EXPORT
 int getOptionSingle( int argc, char* const* argv, const char* optstr, const Option* longopts, int* idx );
 
 ///////////////////////////////////////////////////////////////////////////////
diff -Naur mp4v2-2.0.0/libplatform/sys/error.h mp4v2-2.0.0-win32/libplatform/sys/error.h
--- mp4v2-2.0.0/libplatform/sys/error.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/sys/error.h	2021-11-27 20:52:57.969316800 +0100
@@ -5,9 +5,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-MP4V2_EXPORT int         getLastError    ( );
-MP4V2_EXPORT const char* getLastErrorStr ( );
-MP4V2_EXPORT const char* getErrorStr     ( int );
+int         getLastError    ( );
+const char* getLastErrorStr ( );
+const char* getErrorStr     ( int );
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff -Naur mp4v2-2.0.0/libplatform/time/time.h mp4v2-2.0.0-win32/libplatform/time/time.h
--- mp4v2-2.0.0/libplatform/time/time.h	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/time/time.h	2021-11-27 20:52:57.971342400 +0100
@@ -23,7 +23,7 @@
 //! @return local-time in milliseconds elapsed since the epoch.
 //!
 ///////////////////////////////////////////////////////////////////////////////
-MP4V2_EXPORT milliseconds_t getLocalTimeMilliseconds();
+milliseconds_t getLocalTimeMilliseconds();
 
 ///////////////////////////////////////////////////////////////////////////////
 //!
@@ -37,7 +37,7 @@
 //! @return local-time in seconds elapsed since the epoch.
 //!
 ///////////////////////////////////////////////////////////////////////////////
-MP4V2_EXPORT seconds_t getLocalTimeSeconds();
+seconds_t getLocalTimeSeconds();
 
 ///////////////////////////////////////////////////////////////////////////////
 //! @}
diff -Naur mp4v2-2.0.0/libplatform/time/time_win32.cpp mp4v2-2.0.0-win32/libplatform/time/time_win32.cpp
--- mp4v2-2.0.0/libplatform/time/time_win32.cpp	2012-05-21 01:11:55.000000000 +0200
+++ mp4v2-2.0.0-win32/libplatform/time/time_win32.cpp	2021-11-27 20:52:57.973040800 +0100
@@ -8,8 +8,8 @@
 milliseconds_t
 getLocalTimeMilliseconds()
 {
-    __timeb64 buf;
-    _ftime64( &buf );
+    _timeb buf;
+    _ftime( &buf );
     return milliseconds_t( buf.time ) * 1000 + buf.millitm;
 }
 
diff -Naur mp4v2-2.0.0/src/Makefile mp4v2-2.0.0-win32/src/Makefile
--- mp4v2-2.0.0/src/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ mp4v2-2.0.0-win32/src/Makefile	2021-11-27 21:06:28.206999200 +0100
@@ -0,0 +1,29 @@
+OBJECTS	  = bmff/typebmff.o itmf/CoverArtBox.o itmf/generic.o itmf/Tags.o itmf/type.o qtff/coding.o qtff/ColorParameterBox.o qtff/PictureAspectRatioBox.o 3gp.o atom_ac3.o atom_amr.o atom_avc1.o atom_avcC.o atom_chpl.o atom_colr.o atom_d263.o atom_dac3.o atom_damr.o atom_dref.o atom_elst.o atom_enca.o atom_encv.o atom_free.o atom_ftab.o atom_ftyp.o atom_gmin.o atom_hdlr.o atom_hinf.o atom_hnti.o atom_href.o atom_mdat.o atom_mdhd.o atom_meta.o atom_mp4s.o atom_mp4v.o atom_mvhd.o atom_nmhd.o atom_ohdr.o atom_pasp.o atom_root.o atom_rtp.o atom_s263.o atom_sdp.o atom_sdtp.o atom_smi.o atom_sound.o atom_standard.o atom_stbl.o atom_stdp.o atom_stsc.o atom_stsd.o atom_stsz.o atom_stz2.o atom_text.o atom_tfhd.o atom_tkhd.o atom_treftype.o atom_trun.o atom_tx3g.o atom_udta.o atom_url.o atom_urn.o atom_uuid.o atom_video.o atom_vmhd.o cmeta.o descriptors.o exception.o isma.o log.o mp4.o mp4atom.o mp4container.o mp4descriptor.o mp4file.o mp4file_io.o mp4info.o mp4property.o mp4track.o mp4util.o ocidescriptors.o odcommands.o qosqualifiers.o rtphint.o text.o
+
+LIBDIR	  = ../libplatform
+LIBS	  = -lplatform
+
+DLL	  = libmp4v2-0.dll
+
+CXXOPTS	  = $(CXXFLAGS) -DMP4V2_EXPORTS -I.. -I../include
+LDOPTS	  = -L$(LIBDIR) $(LIBS)
+
+# Link static stdc++ on x86, because of unstable
+# exception handling with SJLJ otherwise.
+ifeq ($(BUILD_X86),True)
+	LDOPTS += -lstdc++.static
+else
+	LDOPTS += -lstdc++
+endif
+
+all : $(DLL)
+
+$(DLL) : $(OBJECTS)
+	$(CC) --shared -o $@ $(OBJECTS) $(LDOPTS)
+	strip --strip-all $@
+
+clean :
+	rm -f $(DLL) $(OBJECTS)
+
+.cpp.o:
+	$(CXX) $(CXXOPTS) -c $< -o $@
diff -Naur mp4v2-2.0.0/src/bmff/typebmff.h mp4v2-2.0.0-win32/src/bmff/typebmff.h
--- mp4v2-2.0.0/src/bmff/typebmff.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/bmff/typebmff.h	2021-11-27 20:52:57.978226400 +0100
@@ -519,7 +519,7 @@
 };
 
 typedef Enum<LanguageCode,ILC_UNDEFINED> EnumLanguageCode;
-MP4V2_EXPORT extern const EnumLanguageCode enumLanguageCode;
+extern const EnumLanguageCode enumLanguageCode;
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff -Naur mp4v2-2.0.0/src/enum.h mp4v2-2.0.0-win32/src/enum.h
--- mp4v2-2.0.0/src/enum.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/enum.h	2021-11-27 20:52:57.982225200 +0100
@@ -71,7 +71,7 @@
 class Enum
 {
 public:
-    struct MP4V2_EXPORT Entry
+    struct Entry
     {
         T type;
         const string compact;
diff -Naur mp4v2-2.0.0/src/exception.h mp4v2-2.0.0-win32/src/exception.h
--- mp4v2-2.0.0/src/exception.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/exception.h	2021-11-27 20:52:57.988147400 +0100
@@ -29,7 +29,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class MP4V2_EXPORT Exception
+class Exception
 {
 public:
     explicit Exception( const string&   what_,
@@ -47,7 +47,7 @@
     const string        function;
 };
 
-class MP4V2_EXPORT PlatformException : public Exception
+class PlatformException : public Exception
 {
 public:
     explicit PlatformException( const string&   what_,
diff -Naur mp4v2-2.0.0/src/itmf/CoverArtBox.h mp4v2-2.0.0-win32/src/itmf/CoverArtBox.h
--- mp4v2-2.0.0/src/itmf/CoverArtBox.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/itmf/CoverArtBox.h	2021-11-27 20:52:57.993145800 +0100
@@ -30,14 +30,14 @@
 
 /// Functional class for covr-box (Cover-art Box) support.
 ///
-class MP4V2_EXPORT CoverArtBox
+class CoverArtBox
 {
 public:
     /// Data object for covr-box item.
     /// This object correlates to one covr->data atom and offers automatic
     /// memory freeing when <b>autofree</b> is true.
     ///
-    class MP4V2_EXPORT Item
+    class Item
     {
     public:
         Item();
diff -Naur mp4v2-2.0.0/src/itmf/type.h mp4v2-2.0.0-win32/src/itmf/type.h
--- mp4v2-2.0.0/src/itmf/type.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/itmf/type.h	2021-11-27 20:52:57.995959600 +0100
@@ -56,7 +56,7 @@
 };
 
 typedef Enum<BasicType,BT_UNDEFINED> EnumBasicType;
-MP4V2_EXPORT extern const EnumBasicType enumBasicType;
+extern const EnumBasicType enumBasicType;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -199,7 +199,7 @@
 };
 
 typedef Enum<GenreType,GENRE_UNDEFINED> EnumGenreType;
-MP4V2_EXPORT extern const EnumGenreType enumGenreType;
+extern const EnumGenreType enumGenreType;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -219,7 +219,7 @@
 };
 
 typedef Enum<StikType,STIK_UNDEFINED> EnumStikType;
-MP4V2_EXPORT extern const EnumStikType enumStikType;
+extern const EnumStikType enumStikType;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -233,7 +233,7 @@
 };
 
 typedef Enum<AccountType,AT_UNDEFINED> EnumAccountType;
-MP4V2_EXPORT extern const EnumAccountType enumAccountType;
+extern const EnumAccountType enumAccountType;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -267,7 +267,7 @@
 };
 
 typedef Enum<CountryCode,CC_UNDEFINED> EnumCountryCode;
-MP4V2_EXPORT extern const EnumCountryCode enumCountryCode;
+extern const EnumCountryCode enumCountryCode;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -282,11 +282,11 @@
 };
 
 typedef Enum<ContentRating,CR_UNDEFINED> EnumContentRating;
-MP4V2_EXPORT extern const EnumContentRating enumContentRating;
+extern const EnumContentRating enumContentRating;
 
 ///////////////////////////////////////////////////////////////////////////////
 /// compute BasicType by examining raw bytes header.
-MP4V2_EXPORT BasicType
+BasicType
 computeBasicType( const void* buffer, uint32_t size );
 
 ///////////////////////////////////////////////////////////////////////////////
diff -Naur mp4v2-2.0.0/src/log.h mp4v2-2.0.0-win32/src/log.h
--- mp4v2-2.0.0/src/log.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/log.h	2021-11-27 20:52:57.999899900 +0100
@@ -32,7 +32,7 @@
  * Handle logging either to standard out or to a callback
  * function
  */
-class MP4V2_EXPORT Log {
+class Log {
 private:
     MP4LogLevel                 _verbosity;
     static MP4LogCallback       _cb_func;
diff -Naur mp4v2-2.0.0/src/qtff/ColorParameterBox.h mp4v2-2.0.0-win32/src/qtff/ColorParameterBox.h
--- mp4v2-2.0.0/src/qtff/ColorParameterBox.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/qtff/ColorParameterBox.h	2021-11-27 20:52:58.002900000 +0100
@@ -40,13 +40,13 @@
 ///
 /// This implementation supports parameter-type 'nclc' only.
 ///
-class MP4V2_EXPORT ColorParameterBox
+class ColorParameterBox
 {
 public:
     /// Data object for colr-box item.
     /// This object correlates to one colr-box (Color Parameter Box).
     ///
-    class MP4V2_EXPORT Item
+    class Item
     {
     public:
         Item();
@@ -88,7 +88,7 @@
         uint16_t matrixIndex;
     };
 
-    class MP4V2_EXPORT IndexedItem {
+    class IndexedItem {
     public:
         IndexedItem();
 
diff -Naur mp4v2-2.0.0/src/qtff/PictureAspectRatioBox.h mp4v2-2.0.0-win32/src/qtff/PictureAspectRatioBox.h
--- mp4v2-2.0.0/src/qtff/PictureAspectRatioBox.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/qtff/PictureAspectRatioBox.h	2021-11-27 20:52:58.004919700 +0100
@@ -39,12 +39,12 @@
 /// This implementation assumes a maximum count of <b>1</b> for
 /// VideoSampleEntry of the supported codings.
 ///
-class MP4V2_EXPORT PictureAspectRatioBox
+class PictureAspectRatioBox
 {
 public:
     /// Data object for pasp-box item.
     /// This object correlates to one pasp-box (Picture Aspect Ratio Box).
-    class MP4V2_EXPORT Item
+    class Item
     {
     public:
         Item ();
@@ -69,7 +69,7 @@
         uint32_t vSpacing;
     };
 
-    class MP4V2_EXPORT IndexedItem {
+    class IndexedItem {
     public:
         IndexedItem();
 
diff -Naur mp4v2-2.0.0/src/text.h mp4v2-2.0.0-win32/src/text.h
--- mp4v2-2.0.0/src/text.h	2012-05-21 01:11:53.000000000 +0200
+++ mp4v2-2.0.0-win32/src/text.h	2021-11-27 20:52:58.006338400 +0100
@@ -5,7 +5,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-struct MP4V2_EXPORT LessIgnoreCase : less<string>
+struct LessIgnoreCase : less<string>
 {
     bool operator()( const string&, const string& ) const;
 };
diff -Naur mp4v2-2.0.0/vstudio9.0/libmp4v2/libmp4v2.vcproj mp4v2-2.0.0-win32/vstudio9.0/libmp4v2/libmp4v2.vcproj
--- mp4v2-2.0.0/vstudio9.0/libmp4v2/libmp4v2.vcproj	2012-05-21 01:11:52.000000000 +0200
+++ mp4v2-2.0.0-win32/vstudio9.0/libmp4v2/libmp4v2.vcproj	2021-11-27 20:53:20.215324500 +0100
@@ -1610,78 +1610,6 @@
 				>
 			</File>
 		</Filter>
-		<Filter
-			Name="resources"
-			>
-			<File
-				RelativePath=".\Version.rc"
-				>
-				<FileConfiguration
-					Name="Debug Static (MTd)|Win32"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release Static (MT)|Win32"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug Static (MDd)|Win32"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release Static (MD)|Win32"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug Static (MTd)|x64"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release Static (MT)|x64"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug Static (MDd)|x64"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release Static (MD)|x64"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCResourceCompilerTool"
-					/>
-				</FileConfiguration>
-			</File>
-		</Filter>
 	</Files>
 	<Globals>
 	</Globals>
