diff --git a/libplatform/io/FileSystem_win32.cpp b/libplatform/io/FileSystem_win32.cpp new file mode 100644 index 0000000..4a21381 --- /dev/null +++ b/libplatform/io/FileSystem_win32.cpp @@ -0,0 +1,146 @@ +#include "src/impl.h" +#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */ +#include + +namespace mp4v2 { + using namespace impl; +} + +namespace mp4v2 { namespace platform { namespace io { + +/////////////////////////////////////////////////////////////////////////////// + +static DWORD getAttributes ( string path_ ); + +/** + * Call GetFileAttributesW throw exceptions for errors + * + * @param path_ the path to get attributes for + * + * @retval INVALID_FILE_ATTRIBUTES @p path_ doesn't exist + * @retval anything else the attributes of @p path_ + */ +static DWORD +getAttributes ( string path_ ) +{ + win32::Utf8ToFilename filename(path_); + + if (!filename.IsUTF16Valid()) + { + // throw an exception to avoid changing the + // signature of this function and dealing with all + // the places it's called. + ostringstream msg; + msg << "can't convert file to UTF-16(" << filename.utf8 << ")"; + throw new Exception(msg.str(),__FILE__,__LINE__,__FUNCTION__); + } + + DWORD attributes = ::GetFileAttributesW(filename); + if( attributes == INVALID_FILE_ATTRIBUTES ) + { + DWORD last_err = GetLastError(); + + // Distinguish between an error and the path not existing + if ((last_err == ERROR_FILE_NOT_FOUND) || (last_err == ERROR_PATH_NOT_FOUND)) + { + return attributes; + } + + // Anything else is an error + ostringstream msg; + msg << "GetFileAttributes(" << filename.utf8 << ") failed (" << last_err << ")"; + throw new Exception(msg.str(),__FILE__,__LINE__,__FUNCTION__); + } + + // path exists so return its attributes + return attributes; +} + +bool +FileSystem::exists( string path_ ) +{ + return( getAttributes(path_) != INVALID_FILE_ATTRIBUTES ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +FileSystem::isDirectory( string path_ ) +{ + DWORD attributes = getAttributes( path_ ); + if( attributes == INVALID_FILE_ATTRIBUTES ) + return false; + + return ( ( attributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +FileSystem::isFile( string path_ ) +{ + DWORD attributes = getAttributes( path_ ); + if( attributes == INVALID_FILE_ATTRIBUTES ) + return false; + + return ( ( attributes & FILE_ATTRIBUTE_DIRECTORY ) != FILE_ATTRIBUTE_DIRECTORY ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +FileSystem::getFileSize( string path_, File::Size& size_ ) +{ + win32::Utf8ToFilename filename(path_); + + if (!filename.IsUTF16Valid()) + { + // The logging is done + return true; + } + + size_ = 0; + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if( !GetFileAttributesExW( filename, GetFileExInfoStandard, (LPVOID)&data ) ) + { + log.errorf("%s: GetFileAttributesExW(%s) failed (%d)",__FUNCTION__,filename.utf8.c_str(), + GetLastError()); + return true; + } + + size_ = ( (File::Size)data.nFileSizeHigh << 32 ) | data.nFileSizeLow; + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +FileSystem::rename( string from, string to ) +{ + win32::Utf8ToFilename from_file(from); + win32::Utf8ToFilename to_file(to); + + if (!from_file.IsUTF16Valid() || !to_file.IsUTF16Valid()) + { + return true; + } + + if (!::MoveFileExW( from_file, to_file, + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) ) + { + log.errorf("%s: MoveFileExW(%s,%s) failed (%d)",__FUNCTION__,from_file.utf8.c_str(),to_file.utf8.c_str(), + GetLastError()); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +string FileSystem::DIR_SEPARATOR = "\\"; +string FileSystem::PATH_SEPARATOR = ";"; + +/////////////////////////////////////////////////////////////////////////////// + +}}} // namespace mp4v2::platform::io diff --git a/libplatform/io/File_win32.cpp b/libplatform/io/File_win32.cpp new file mode 100644 index 0000000..8fe44a0 --- /dev/null +++ b/libplatform/io/File_win32.cpp @@ -0,0 +1,267 @@ +#include "src/impl.h" +#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */ +#include + +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 + +namespace mp4v2 { namespace platform { namespace io { + +/////////////////////////////////////////////////////////////////////////////// + +class StandardFileProvider : public FileProvider +{ +public: + StandardFileProvider(); + + bool open( std::string name, Mode mode ); + bool seek( Size pos ); + bool read( void* buffer, Size size, Size& nin, Size maxChunkSize ); + bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize ); + bool close(); + + int64_t getSize(); + +private: + HANDLE _handle; + + /** + * The UTF-8 encoded file name + */ + std::string _name; +}; + +/////////////////////////////////////////////////////////////////////////////// + +StandardFileProvider::StandardFileProvider() + : _handle( INVALID_HANDLE_VALUE ) +{ +} + +/** + * Open a file + * + * @param name the name of a file to open + * @param mode the mode to open @p name + * + * @retval false successfully opened @p name + * @retval true error opening @p name + */ +bool +StandardFileProvider::open( std::string name, Mode mode ) +{ + DWORD access = 0; + DWORD share = 0; + DWORD crdisp = 0; + DWORD flags = FILE_ATTRIBUTE_NORMAL; + + switch( mode ) { + case MODE_UNDEFINED: + case MODE_READ: + default: + access |= GENERIC_READ; + share |= FILE_SHARE_READ; + crdisp |= OPEN_EXISTING; + break; + + case MODE_MODIFY: + access |= GENERIC_READ | GENERIC_WRITE; + share |= FILE_SHARE_READ; + crdisp |= OPEN_EXISTING; + break; + + case MODE_CREATE: + access |= GENERIC_READ | GENERIC_WRITE; + share |= FILE_SHARE_READ; + crdisp |= CREATE_ALWAYS; + break; + } + + win32::Utf8ToFilename filename(name); + + if (!filename.IsUTF16Valid()) + { + // The logging is done + return true; + } + + 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 = name; + return false; +} + +/** + * Seek to an offset in the file + * + * @param pos the offset from the beginning of the file to + * seek to + * + * @retval false successfully seeked to @p pos + * @retval true error seeking to @p pos + */ +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; +} + +/** + * Read from the file + * + * @param buffer populated with at most @p size bytes from + * the file + * + * @param size the maximum number of bytes to read + * + * @param nin the + * + * @retval false successfully read from the file + * @retval true error reading from the file + */ +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()); + 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; + return false; +} + +/** + * Write to the file + * + * @param buffer the data to write + * + * @param size the number of bytes of @p buffer to write + * + * @param nout populated with the number of bytes actually + * written if the function succeeds + * + * @retval false successfully wrote to the file + * @retval true error writing to the file + */ +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()); + return true; + } + log.verbose2f("%s: WriteFile(%s,%d) succeeded: wrote %d byte(s)",__FUNCTION__, + _name.c_str(),(DWORD)(size & MAXDWORD),nwrote); + nout = nwrote; + return false; +} + +/** + * Close the file + * + * @retval false successfully closed the file + * @retval true error closing the file + */ +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; +} + +int64_t StandardFileProvider::getSize() +{ + int64_t retSize = 0; + FileSystem::getFileSize( _name, retSize ); + return retSize; +} + +/////////////////////////////////////////////////////////////////////////////// + +FileProvider& +FileProvider::standard() +{ + return *new StandardFileProvider(); +} + +/////////////////////////////////////////////////////////////////////////////// + +}}} // namespace mp4v2::platform::io diff --git a/libplatform/number/random_win32.cpp b/libplatform/number/random_win32.cpp new file mode 100644 index 0000000..f884e5d --- /dev/null +++ b/libplatform/number/random_win32.cpp @@ -0,0 +1,24 @@ +#include "libplatform/impl.h" +#include + +namespace mp4v2 { namespace platform { namespace number { + +/////////////////////////////////////////////////////////////////////////////// + +uint32_t +random32() +{ + return uint32_t( ::rand() << 16 | ::rand() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +srandom( uint32_t seed ) +{ + ::srand( seed ); +} + +/////////////////////////////////////////////////////////////////////////////// + +}}} // namespace mp4v2::platform::time diff --git a/libplatform/platform_win32.cpp b/libplatform/platform_win32.cpp new file mode 100644 index 0000000..60e6813 --- /dev/null +++ b/libplatform/platform_win32.cpp @@ -0,0 +1,1092 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 /* for replace */ +#include + +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 &p_utf8string ) + : _wideCharString( NULL ) + , utf8( _utf8 ) +{ + string utf8string = p_utf8string.substr( GetPrefixLen( p_utf8string ) ); + // 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 --git a/libplatform/platform_win32.h b/libplatform/platform_win32.h new file mode 100644 index 0000000..2be4952 --- /dev/null +++ b/libplatform/platform_win32.h @@ -0,0 +1,95 @@ +#ifndef MP4V2_PLATFORM_WIN32_H +#define MP4V2_PLATFORM_WIN32_H + +/////////////////////////////////////////////////////////////////////////////// + +// mingw needs this to enable some newer 64-bit functions +#ifdef __MINGW32__ +# undef __MSVCRT_VERSION__ +# define __MSVCRT_VERSION__ 0x800 +// JAN: see http://code.google.com/p/mp4v2/issues/detail?id=132 +# define _USE_32BIT_TIME_T +#endif + +// set minimum win32 API requirement to Windows 2000 or higher +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +#endif +#ifndef WINVER +# define WINVER 0x0500 +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#include "libplatform/platform_base.h" +#include + +/////////////////////////////////////////////////////////////////////////////// + +namespace mp4v2 { namespace platform { + using namespace std; + + using ::int8_t; + using ::int16_t; + using ::int32_t; + using ::int64_t; + + using ::uint8_t; + using ::uint16_t; + using ::uint32_t; + using ::uint64_t; +}} // namespace mp4v2::platform + +/////////////////////////////////////////////////////////////////////////////// + +// fprintf macros for unsigned types - mingw32 is a good source if more needed +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 "I64d" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 "I64u" + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 "I64x" + +/////////////////////////////////////////////////////////////////////////////// + +// 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 + +#define UINT8_C(x) x +#define UINT16_C(x) x +#define UINT32_C(x) x ## UL +#define UINT64_C(x) x ## ULL + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef min +# undef min +#endif + +#ifdef max +# undef max +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#define snprintf(s,n,...) _snprintf(s,n,__VA_ARGS__) +#define strcasecmp(s1,s2) _stricmp(s1,s2) +#define strdup(s) _strdup(s) + +/////////////////////////////////////////////////////////////////////////////// + +// macro clashes with symbol +#undef LC_NONE + +#endif // MP4V2_PLATFORM_WIN32_H diff --git a/libplatform/platform_win32_impl.h b/libplatform/platform_win32_impl.h new file mode 100644 index 0000000..d52c5f4 --- /dev/null +++ b/libplatform/platform_win32_impl.h @@ -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 + +/////////////////////////////////////////////////////////////////////////////// + +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 --git a/libplatform/process/process_win32.cpp b/libplatform/process/process_win32.cpp new file mode 100644 index 0000000..b784680 --- /dev/null +++ b/libplatform/process/process_win32.cpp @@ -0,0 +1,16 @@ +#include "libplatform/impl.h" +#include + +namespace mp4v2 { namespace platform { namespace process { + +/////////////////////////////////////////////////////////////////////////////// + +int32_t +getpid() +{ + return ::_getpid(); +} + +/////////////////////////////////////////////////////////////////////////////// + +}}} // namespace mp4v2::platform::process diff --git a/libplatform/time/time_win32.cpp b/libplatform/time/time_win32.cpp new file mode 100644 index 0000000..88a7ea5 --- /dev/null +++ b/libplatform/time/time_win32.cpp @@ -0,0 +1,18 @@ +#include "libplatform/impl.h" +#include + +namespace mp4v2 { namespace platform { namespace time { + +/////////////////////////////////////////////////////////////////////////////// + +milliseconds_t +getLocalTimeMilliseconds() +{ + __timeb64 buf; + _ftime64( &buf ); + return milliseconds_t( buf.time ) * 1000 + buf.millitm; +} + +/////////////////////////////////////////////////////////////////////////////// + +}}} // namespace mp4v2::platform::time