diff --git a/platform/gucefCORE/include/CFileAccess.h b/platform/gucefCORE/include/CFileAccess.h index 7f98cb375..f6bc0dff9 100644 --- a/platform/gucefCORE/include/CFileAccess.h +++ b/platform/gucefCORE/include/CFileAccess.h @@ -33,6 +33,11 @@ #define GUCEF_CORE_CIOACCESS_H #endif /* GUCEF_CORE_CIOACCESS_H ? */ +#ifndef GUCEF_CORE_CRESOURCEMETADATA_H +#include "gucefCORE_CResourceMetaData.h" +#define GUCEF_CORE_CRESOURCEMETADATA_H +#endif /* GUCEF_CORE_CRESOURCEMETADATA_H ? */ + /*-------------------------------------------------------------------------// // // // NAMESPACE // @@ -57,16 +62,27 @@ class GUCEF_CORE_PUBLIC_CPP CFileAccess : public CIOAccess CFileAccess( const CString& file , const char* mode = "rb" ); + CFileAccess( const CString& file , + const CResourceMetaData& metaData , + const char* mode = "wb" ); + CFileAccess( void ); virtual ~CFileAccess() GUCEF_VIRTUAL_OVERRIDE; /** - * open the resource for I/O - */ + * open the resource for I/O + */ virtual bool Open( const CString& file , const char* mode = "rb" ); + /** + * open the resource for I/O + */ + virtual bool Open( const CString& file , + const CResourceMetaData& metaData , + const char* mode = "wb" ); + /** * open the resource for I/O */ diff --git a/platform/gucefCORE/include/dvcppfileutils.h b/platform/gucefCORE/include/dvcppfileutils.h index 2588cabdc..80504485e 100644 --- a/platform/gucefCORE/include/dvcppfileutils.h +++ b/platform/gucefCORE/include/dvcppfileutils.h @@ -91,6 +91,17 @@ GetFileCreationTime( const CString& path ); /*-------------------------------------------------------------------------*/ +/** + * Attempts to set various file meta-data using the file system + * The implementation will apply the meta-data 'best effort' based on the constraints of the actual underlying storage + * and return true if within the constraints of the file system the meta-data was applied successfully. + */ +GUCEF_CORE_PUBLIC_CPP bool +SetFileMetaData( const CString& filePath , + const CResourceMetaData& metaData ); + +/*-------------------------------------------------------------------------*/ + /** * Attempts to obtain various file meta-data from the file system */ diff --git a/platform/gucefCORE/include/dvcppstringutils.h b/platform/gucefCORE/include/dvcppstringutils.h index 4ba65765b..a449e7388 100644 --- a/platform/gucefCORE/include/dvcppstringutils.h +++ b/platform/gucefCORE/include/dvcppstringutils.h @@ -486,10 +486,10 @@ inline CString ToString( const CAsciiString::StringSet& el ) { CAsciiString out; #elif ( GUCEF_DEFAULT_STRING_FORMAT == GUCEF_DATATYPE_UTF8_STRING ) inline CString ToString( const CUtf8String& str ) { return str; } inline CString ToString( const CAsciiString& str ) { return CUtf8String( str ); } -inline CString ToString( const CUtf8String::StringVector& el ) { CUtf8String out; return out.Combine( el, ',' ); } -inline CString ToString( const CAsciiString::StringVector& el ) { CAsciiString out; return CUtf8String( out.Combine( el, ',' ) ); } -inline CString ToString( const CUtf8String::StringSet& el ) { CUtf8String out; return out.Combine( el, ',' ); } -inline CString ToString( const CAsciiString::StringSet& el ) { CAsciiString out; return CUtf8String( out.Combine( el, ',' ) ); } +inline CString ToString( const CUtf8String::StringVector& el, Int32 sepChar = ',' ) { CUtf8String out; return out.Combine( el, sepChar ); } +inline CString ToString( const CAsciiString::StringVector& el, char sepChar = ',' ) { CAsciiString out; return CUtf8String( out.Combine( el, sepChar ) ); } +inline CString ToString( const CUtf8String::StringSet& el, Int32 sepChar = ',' ) { CUtf8String out; return out.Combine( el, sepChar ); } +inline CString ToString( const CAsciiString::StringSet& el, char sepChar = ',' ) { CAsciiString out; return CUtf8String( out.Combine( el, sepChar ) ); } inline CString ToString( const CUtf8String::StringMap& el ) { CUtf8String out; return out.Combine( el, '=', ',' ); } #endif @@ -526,8 +526,8 @@ inline CString ToString( void ) { return typeid( typeNameParam ).name(); } /*-------------------------------------------------------------------------*/ -inline CString::StringSet ToStringSet( const CUtf8String& str ) { CString::StringSet set; set.insert( str ); return set; } -inline CString::StringSet ToStringSet( const CAsciiString& str ) { CString::StringSet set; set.insert( str ); return set; } +inline CString::StringSet ToStringSet( const CUtf8String& str ) { return StringToStringSet( str ); } +inline CString::StringSet ToStringSet( const CAsciiString& str ) { return StringToStringSet( str ); } inline CString::StringSet ToStringSet( const CUtf8String::StringVector& vec ) { CString::StringSet set; CUtf8String::StringVector::const_iterator i=vec.begin(); while ( i != vec.end() ) { set.insert( (*i) ); ++i; } return set; } inline CString::StringSet ToStringSet( const CAsciiString::StringVector& vec ) { CString::StringSet set; CAsciiString::StringVector::const_iterator i=vec.begin(); while ( i != vec.end() ) { set.insert( (*i) ); ++i; } return set; } #if ( GUCEF_DEFAULT_STRING_FORMAT == GUCEF_DATATYPE_ASCII_STRING ) diff --git a/platform/gucefCORE/include/gucefCORE_CDateTime.h b/platform/gucefCORE/include/gucefCORE_CDateTime.h index 977553bd1..1627aea8f 100644 --- a/platform/gucefCORE/include/gucefCORE_CDateTime.h +++ b/platform/gucefCORE/include/gucefCORE_CDateTime.h @@ -181,6 +181,8 @@ class GUCEF_CORE_PUBLIC_CPP CDateTime : public CDate , explicit CDateTime( const struct _FILETIME& src ); + struct _FILETIME ToWindowsFiletime( void ) const; + #endif virtual ~CDateTime(); diff --git a/platform/gucefCORE/src/CFileAccess.cpp b/platform/gucefCORE/src/CFileAccess.cpp index 68e84bd0e..4a075f1f5 100644 --- a/platform/gucefCORE/src/CFileAccess.cpp +++ b/platform/gucefCORE/src/CFileAccess.cpp @@ -48,6 +48,11 @@ #define GUCEF_CORE_GUCEF_ESSENTIALS_H #endif /* GUCEF_CORE_GUCEF_ESSENTIALS_H ? */ +#if ( GUCEF_PLATFORM == GUCEF_PLATFORM_MSWIN ) + #include + #include +#endif + /*-------------------------------------------------------------------------// // // // NAMESPACE // @@ -64,10 +69,13 @@ namespace CORE { //-------------------------------------------------------------------------*/ CFileAccess::CFileAccess( void ) - : CIOAccess() , - m_filename() , - m_mode() , - m_file( NULL ) + : CIOAccess() + , _writeable( false ) + , _readable( false ) + , m_mode() + , m_file( GUCEF_NULL ) + , m_filename() + , _size( 0 ) {GUCEF_TRACE; } @@ -76,10 +84,13 @@ CFileAccess::CFileAccess( void ) CFileAccess::CFileAccess( const CString& file , const char* mode /* = "rb" */ ) - : CIOAccess(), - m_filename( file ) , - m_mode( mode ) , - m_file( NULL ) + : CIOAccess() + , _writeable( false ) + , _readable( false ) + , m_mode() + , m_file( GUCEF_NULL ) + , m_filename() + , _size( 0 ) {GUCEF_TRACE; Open( file, mode ); @@ -87,6 +98,23 @@ CFileAccess::CFileAccess( const CString& file , /*-------------------------------------------------------------------------*/ +CFileAccess::CFileAccess( const CString& file , + const CResourceMetaData& metaData , + const char* mode ) + : CIOAccess() + , _writeable( false ) + , _readable( false ) + , m_mode() + , m_file( GUCEF_NULL ) + , m_filename() + , _size( 0 ) +{GUCEF_TRACE; + + Open( file, metaData, mode ); +} + +/*-------------------------------------------------------------------------*/ + CFileAccess::~CFileAccess() {GUCEF_TRACE; @@ -137,10 +165,11 @@ CFileAccess::GetErrorString( int errorCode ) void CFileAccess::Open( void ) {GUCEF_TRACE; - Close(); + + Close(); - m_file = fopen( m_filename.C_String() , - m_mode.C_String() ); + m_file = fopen( m_filename.C_String() , + m_mode.C_String() ); } /*-------------------------------------------------------------------------*/ @@ -148,7 +177,8 @@ CFileAccess::Open( void ) bool CFileAccess::Open( const CString& file , const char* mode ) -{ +{GUCEF_TRACE; + Close(); m_filename = file; @@ -168,7 +198,7 @@ CFileAccess::Open( const CString& file , } if ( _readable ) { - _size = Filesize( file.C_String() ); + _size = FileSize( file ); } errno = 0; @@ -185,14 +215,151 @@ CFileAccess::Open( const CString& file , /*-------------------------------------------------------------------------*/ +bool +CFileAccess::Open( const CString& file , + const CResourceMetaData& metaData , + const char* mode ) +{GUCEF_TRACE; + + Close(); + + m_filename = file; + m_mode = mode; + + _readable = ( strchr( mode, 'r' ) != NULL ) || ( strchr( mode, 'a' ) != NULL ); + _writeable = ( strchr( mode, 'w' ) != NULL ) || ( strchr( mode, 'a' ) != NULL ); + + if ( _writeable ) + { + CString path = StripFilename( file ); + if ( !CreateDirs( path ) ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: Failed to recursively create directories" ); + return false; + } + } + if ( _readable ) + { + _size = FileSize( file ); + } + + errno = 0; + + #if ( GUCEF_PLATFORM == GUCEF_PLATFORM_MSWIN ) + + // Get the current file attributes + // we only want to change the flags we have and keep the rest as-is + CResourceMetaData originalMetaData; + if ( FileExists( m_filename ) ) + { + if ( !GetFileMetaData( m_filename, originalMetaData ) ) + return false; + } + + std::wstring wFilename = ToWString( m_filename ); + + WIN32_FILE_ATTRIBUTE_DATA data; + memset( &data, 0, sizeof data ); + + // Convert boolean flags to file attribute flags + if ( metaData.hasIsHidden ) { metaData.isHidden ? data.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN : data.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; } + else { originalMetaData.isHidden ? data.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN : data.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; } + + if ( metaData.hasIsReadOnly ) { metaData.isReadOnly ? data.dwFileAttributes |= FILE_ATTRIBUTE_READONLY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; } + else { originalMetaData.isReadOnly ? data.dwFileAttributes |= FILE_ATTRIBUTE_READONLY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; } + + if ( metaData.hasIsSystemResource ) { metaData.isSystemResource ? data.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM : data.dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM; } + else { originalMetaData.isSystemResource ? data.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM : data.dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM; } + + if ( metaData.hasIsArchive ) { metaData.isArchive ? data.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; } + else { originalMetaData.isArchive ? data.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; } + + if ( metaData.hasIsCompressed ) { metaData.isCompressed ? data.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_COMPRESSED; } + else { originalMetaData.isCompressed ? data.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_COMPRESSED; } + + if ( metaData.hasIsEncrypted ) { metaData.isEncrypted ? data.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED; } + else { originalMetaData.isEncrypted ? data.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED; } + + if ( metaData.hasIsTemporary ) { metaData.isTemporary ? data.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY; } + else { originalMetaData.isTemporary ? data.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY; } + + //if ( metaData.hasIsOffline ) { metaData.isOffline ? data.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE; } + //else + { originalMetaData.hasIsOffline ? data.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE; } + + // Open using Win32 + HANDLE hFile = INVALID_HANDLE_VALUE; + if ( _readable && !_writeable ) + hFile = ::CreateFileW( wFilename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, data.dwFileAttributes, NULL ); + else if ( _readable && _writeable ) + hFile = ::CreateFileW( wFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, data.dwFileAttributes, NULL ); + else if ( !_readable && _writeable ) + hFile = ::CreateFileW( wFilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, data.dwFileAttributes, NULL ); + + if ( INVALID_HANDLE_VALUE == hFile ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: Failed to open handle to file \"" + file + "\". mode=" + CString(mode) + " error=" + GetErrorString( errno ) ); + return false; + } + + // Convert date time to windows file time as needed + metaData.hasCreationDateTime ? data.ftCreationTime = metaData.creationDateTime.ToWindowsFiletime() : data.ftCreationTime = originalMetaData.creationDateTime.ToWindowsFiletime(); + metaData.hasModifiedDateTime ? data.ftLastWriteTime = metaData.modifiedDateTime.ToWindowsFiletime() : data.ftLastWriteTime = originalMetaData.modifiedDateTime.ToWindowsFiletime(); + metaData.hasLastAccessedDateTime ? data.ftLastAccessTime = metaData.lastAccessedDateTime.ToWindowsFiletime() : data.ftLastAccessTime = originalMetaData.lastAccessedDateTime.ToWindowsFiletime(); + + if ( 0 == ::SetFileTime( hFile, &data.ftCreationTime, &data.ftLastAccessTime, &data.ftLastWriteTime ) ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: SetFileTime failed with error code: " + ToString( (UInt32) ::GetLastError() ) ); + } + + // Convert HANDLE to file descriptor + // This transfers ownership of the HANDLE to the file descriptor + int fd = ::_open_osfhandle( (intptr_t) hFile, _O_RDONLY ); + if ( fd == -1 ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: Failed to convert HANDLE to file descriptor" ); + ::CloseHandle( hFile ); + hFile = INVALID_HANDLE_VALUE; + return false; + } + + // Convert file descriptor to FILE* + // This transfers ownership of the file descriptor to the FILE* + m_file = ::_fdopen( fd, mode ); + if ( m_file == NULL) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: Failed to convert file descriptor to FILE*" ); + _close( fd ); + } + + #elif ( ( GUCEF_PLATFORM == GUCEF_PLATFORM_LINUX ) || ( GUCEF_PLATFORM == GUCEF_PLATFORM_ANDROID ) ) + + #else + + /* + * Unsupported platform + */ + + #endif + + if ( m_file == NULL && 0 != errno ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "FileAccess:Open: Failed to open file with meta-data \"" + file + "\". mode=" + CString(mode) + " error=" + GetErrorString( errno ) ); + } + + return m_file != NULL; +} + +/*-------------------------------------------------------------------------*/ + void CFileAccess::Close( void ) {GUCEF_TRACE; if ( NULL != m_file ) { - fclose( m_file ); - m_file = NULL; + fclose( m_file ); + m_file = NULL; } } @@ -354,7 +521,7 @@ bool CFileAccess::IsValid( void ) {GUCEF_TRACE; - return File_Exists( m_filename.C_String() ) == 1; + return FileExists( m_filename ); } /*-------------------------------------------------------------------------*/ @@ -369,7 +536,7 @@ CFileAccess::GetSize( void ) const { fflush( m_file ); } - return Filesize( m_filename.C_String() ); + return FileSize( m_filename ); } return 0; } @@ -394,11 +561,11 @@ CFileAccess::Clone( void ) const if ( IsReadable() ) { - return GUCEF_NEW CFileAccess( m_filename, "rb" ); + return GUCEF_NEW CFileAccess( m_filename, "rb" ); } // Cannot be cloned - return NULL; + return GUCEF_NULL; } /*-------------------------------------------------------------------------*/ @@ -418,7 +585,7 @@ CFileAccess::SetFileToUse( const CString& filename , bool moveIfCurrentlyOpen ) {GUCEF_TRACE; - if ( NULL != m_file ) + if ( GUCEF_NULL != m_file ) { // We already have a file open. fclose( m_file ); @@ -432,7 +599,7 @@ CFileAccess::SetFileToUse( const CString& filename , } m_file = fopen( filename.C_String(), mode ); - if ( NULL != m_file ) + if ( GUCEF_NULL != m_file ) { m_filename = filename; m_mode = mode; diff --git a/platform/gucefCORE/src/dvcppfileutils.cpp b/platform/gucefCORE/src/dvcppfileutils.cpp index 3fd618736..4f1fcfaf9 100644 --- a/platform/gucefCORE/src/dvcppfileutils.cpp +++ b/platform/gucefCORE/src/dvcppfileutils.cpp @@ -177,6 +177,110 @@ GetFileCreationTime( const CString& path ) /*-------------------------------------------------------------------------*/ +bool +SetFileMetaData( const CString& filePath , + const CResourceMetaData& metaData ) +{GUCEF_TRACE; + + if ( FileExists( filePath ) ) + { + #if ( GUCEF_PLATFORM == GUCEF_PLATFORM_MSWIN ) + + bool totalSuccess = true; + std::wstring wFilepath = ToWString( filePath ); + + // Get the current file attributes + // we only want to change the flags we have and keep the rest as-is + CResourceMetaData originalMetaData; + if ( !GetFileMetaData( filePath, originalMetaData ) ) + return false; + + WIN32_FILE_ATTRIBUTE_DATA data; + memset( &data, 0, sizeof data ); + + // Convert boolean flags to file attribute flags + if ( metaData.hasIsHidden ) { metaData.isHidden ? data.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN : data.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; } + else { originalMetaData.isHidden ? data.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN : data.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; } + + if ( metaData.hasIsReadOnly ) { metaData.isReadOnly ? data.dwFileAttributes |= FILE_ATTRIBUTE_READONLY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; } + else { originalMetaData.isReadOnly ? data.dwFileAttributes |= FILE_ATTRIBUTE_READONLY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; } + + if ( metaData.hasIsSystemResource ) { metaData.isSystemResource ? data.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM : data.dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM; } + else { originalMetaData.isSystemResource ? data.dwFileAttributes |= FILE_ATTRIBUTE_SYSTEM : data.dwFileAttributes &= ~FILE_ATTRIBUTE_SYSTEM; } + + if ( metaData.hasIsArchive ) { metaData.isArchive ? data.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; } + else { originalMetaData.isArchive ? data.dwFileAttributes |= FILE_ATTRIBUTE_ARCHIVE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; } + + if ( metaData.hasIsCompressed ) { metaData.isCompressed ? data.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_COMPRESSED; } + else { originalMetaData.isCompressed ? data.dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_COMPRESSED; } + + if ( metaData.hasIsEncrypted ) { metaData.isEncrypted ? data.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED; } + else { originalMetaData.isEncrypted ? data.dwFileAttributes |= FILE_ATTRIBUTE_ENCRYPTED : data.dwFileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED; } + + if ( metaData.hasIsTemporary ) { metaData.isTemporary ? data.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY; } + else { originalMetaData.isTemporary ? data.dwFileAttributes |= FILE_ATTRIBUTE_TEMPORARY : data.dwFileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY; } + + //if ( metaData.hasIsOffline ) { metaData.isOffline ? data.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE; } + //else + { originalMetaData.hasIsOffline ? data.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE : data.dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE; } + + // Set the new file attributes + BOOL result = ::SetFileAttributesW( wFilepath.c_str(), data.dwFileAttributes ); + if ( 0 == result ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "SetFileMetaData: SetFileAttributesW failed with error code: " + ToString( (UInt32) ::GetLastError() ) ); + totalSuccess = false; + } + + // Convert date time to windows file time as needed + metaData.hasCreationDateTime ? data.ftCreationTime = metaData.creationDateTime.ToWindowsFiletime() : data.ftCreationTime = originalMetaData.creationDateTime.ToWindowsFiletime(); + metaData.hasModifiedDateTime ? data.ftLastWriteTime = metaData.modifiedDateTime.ToWindowsFiletime() : data.ftLastWriteTime = originalMetaData.modifiedDateTime.ToWindowsFiletime(); + metaData.hasLastAccessedDateTime ? data.ftLastAccessTime = metaData.lastAccessedDateTime.ToWindowsFiletime() : data.ftLastAccessTime = originalMetaData.lastAccessedDateTime.ToWindowsFiletime(); + + // Open the file for writing attributes without needing a handle for data read/write operations + // we dont want to touch the file contents + HANDLE hFile = ::CreateFileW( wFilepath.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( INVALID_HANDLE_VALUE != hFile ) + { + if ( 0 == ::SetFileTime( hFile, &data.ftCreationTime, &data.ftLastAccessTime, &data.ftLastWriteTime ) ) + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "SetFileMetaData: SetFileTime failed with error code: " + ToString( (UInt32) ::GetLastError() ) ); + totalSuccess = false; + } + ::CloseHandle( hFile ); + hFile = INVALID_HANDLE_VALUE; + } + else + { + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "SetFileMetaData: CreateFileW for FILE_WRITE_ATTRIBUTES failed with error code: " + ToString( (UInt32) ::GetLastError() ) ); + totalSuccess = false; + } + + return totalSuccess; + + #elif ( ( GUCEF_PLATFORM == GUCEF_PLATFORM_LINUX ) || ( GUCEF_PLATFORM == GUCEF_PLATFORM_ANDROID ) ) + + /* + * Unsupported platform - @TODO + */ + return false; + + #else + + /* + * Unsupported platform + */ + return false; + + #endif + } + + // Cannot set meta-data on a non-existing file + return false; +} + +/*-------------------------------------------------------------------------*/ + bool GetFileMetaData( const CString& filePath , CResourceMetaData& metaData ) @@ -225,6 +329,11 @@ GetFileMetaData( const CString& filePath , data.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE ? metaData.isOffline = true : metaData.isOffline = false; metaData.hasIsOffline = true; } + else + { + // Failed to get file attributes. + GUCEF_DEBUG_LOG( LOGLEVEL_NORMAL, "GetFileMetaData: GetFileAttributesExW failed with error code: " + ToString( (UInt32) ::GetLastError() ) ); + } // true no matter what since access rights also factor into it // the meta data class has its own flags diff --git a/platform/gucefCORE/src/dvcppstringutils.cpp b/platform/gucefCORE/src/dvcppstringutils.cpp index 72dcfd1b2..93c80a289 100644 --- a/platform/gucefCORE/src/dvcppstringutils.cpp +++ b/platform/gucefCORE/src/dvcppstringutils.cpp @@ -64,6 +64,8 @@ #define MAX_DIR_LENGTH PATH_MAX #define GUCEF_DIRSEPCHAROPPOSITE '\\' #define GUCEF_DIRSEPCHAR '/' + #include + #include #else #error Unsupported OS #endif @@ -1492,6 +1494,40 @@ IsAbsolutePath( const CString& path ) /*-------------------------------------------------------------------------*/ +#if ( ( GUCEF_PLATFORM == GUCEF_PLATFORM_LINUX ) || ( GUCEF_PLATFORM == GUCEF_PLATFORM_ANDROID ) ) + +// function to calculate UTF8 buffer size with handling for invalid UTF16 sequences +size_t +CalculateUtf8BufferSizeForUtf16( const uint16_t* utf16, size_t utf16_length ) +{GUCEF_TRACE; + + size_t utf8_buffer_size = 0; + for (size_t i = 0; i < utf16_length; ++i) { + if (utf16[i] >= 0xD800 && utf16[i] <= 0xDBFF) { + // Check for a valid low surrogate following the high surrogate + if (i + 1 < utf16_length && utf16[i + 1] >= 0xDC00 && utf16[i + 1] <= 0xDFFF) { + utf8_buffer_size += 4; // Valid surrogate pair + i++; // Skip the next unit as it's part of the surrogate pair + } else { + // Handle invalid surrogate pair (e.g., missing low surrogate) + utf8_buffer_size += 3; // Allocate space as if it were a BMP character + // Note: Actual handling might vary based on requirements (e.g., error reporting) + } + } else if (utf16[i] >= 0xDC00 && utf16[i] <= 0xDFFF) { + // Handle a low surrogate without a preceding high surrogate + utf8_buffer_size += 3; // Allocate space as if it were a BMP character + // Note: Actual handling might vary based on requirements + } else { + utf8_buffer_size += 3; // BMP character + } + } + return utf8_buffer_size; +} + +#endif + +/*-------------------------------------------------------------------------*/ + bool Utf16toUtf8( const std::wstring& wstr , std::string& str ) @@ -1516,7 +1552,14 @@ Utf16toUtf8( const std::wstring& wstr , return true; - #else + #elif ( ( GUCEF_PLATFORM == GUCEF_PLATFORM_LINUX ) || ( GUCEF_PLATFORM == GUCEF_PLATFORM_ANDROID ) ) + + size_t neededBufferSize = CalculateUtf8BufferSizeForUtf16( wstr.c_str(), wstr.size() ) + 1; + str.resize( neededBufferSize, '\0' ); + ::utf16_to_utf8( wstr.c_str(), str.c_str(), neededBufferSize ); + return true; + + #else return false; diff --git a/platform/gucefCORE/src/gucefCORE_CDateTime.cpp b/platform/gucefCORE/src/gucefCORE_CDateTime.cpp index 06c3fb2c6..d72ab8198 100644 --- a/platform/gucefCORE/src/gucefCORE_CDateTime.cpp +++ b/platform/gucefCORE/src/gucefCORE_CDateTime.cpp @@ -691,6 +691,18 @@ CDateTime::CDateTime( const struct _FILETIME& src ) COSDateTimeUtils::Win32FileTimeToDateTime( src, *this ); } +/*-------------------------------------------------------------------------*/ + +struct _FILETIME +CDateTime::ToWindowsFiletime( void ) const +{GUCEF_TRACE; + + FILETIME fileTime; + memset( &fileTime, 0, sizeof fileTime ); + COSDateTimeUtils::DateTimeToWin32FileTime( *this, fileTime ); + return fileTime; +} + #endif /*-------------------------------------------------------------------------*/ diff --git a/platform/gucefCORE/src/gucefCORE_CResourceMetaData.cpp b/platform/gucefCORE/src/gucefCORE_CResourceMetaData.cpp index 82982eb35..7d1506d48 100644 --- a/platform/gucefCORE/src/gucefCORE_CResourceMetaData.cpp +++ b/platform/gucefCORE/src/gucefCORE_CResourceMetaData.cpp @@ -94,18 +94,18 @@ CResourceMetaData::CResourceMetaData( const CResourceMetaData& src ) , isReadOnly( src.isReadOnly ) , hasIsHidden( src.hasIsHidden ) , isHidden( src.isHidden ) - , hasIsSystemResource( false ) - , isSystemResource( false ) - , hasIsArchive( false ) - , isArchive( false ) - , hasIsCompressed( false ) - , isCompressed( false ) - , hasIsEncrypted( false ) - , isEncrypted( false ) - , hasIsTemporary( false ) - , isTemporary( false ) - , hasIsOffline( false ) - , isOffline( false ) + , hasIsSystemResource( src.hasIsSystemResource ) + , isSystemResource( src.isSystemResource ) + , hasIsArchive( src.hasIsArchive ) + , isArchive( src.isArchive ) + , hasIsCompressed( src.hasIsCompressed ) + , isCompressed( src.isCompressed ) + , hasIsEncrypted( src.hasIsEncrypted ) + , isEncrypted( src.isEncrypted ) + , hasIsTemporary( src.hasIsTemporary ) + , isTemporary( src.isTemporary ) + , hasIsOffline( src.hasIsOffline ) + , isOffline( src.isOffline ) {GUCEF_TRACE; } diff --git a/platform/gucefVFS/include/gucefVFS_AsyncVfsTaskData.h b/platform/gucefVFS/include/gucefVFS_AsyncVfsTaskData.h index fc15be9b2..07dd8957a 100644 --- a/platform/gucefVFS/include/gucefVFS_AsyncVfsTaskData.h +++ b/platform/gucefVFS/include/gucefVFS_AsyncVfsTaskData.h @@ -72,6 +72,7 @@ enum EAsyncVfsOperationType : int ASYNCVFSOPERATIONTYPE_MOUNTARCHIVE , ASYNCVFSOPERATIONTYPE_STOREDATAASFILE , ASYNCVFSOPERATIONTYPE_COPYFILE , + ASYNCVFSOPERATIONTYPE_COPYFILECONTENT , ASYNCVFSOPERATIONTYPE_MOVEFILE , ASYNCVFSOPERATIONTYPE_ENCODEFILE , ASYNCVFSOPERATIONTYPE_DECODEFILE , @@ -244,6 +245,23 @@ class GUCEF_VFS_PUBLIC_CPP CCopyFileTaskData : public CAsyncVfsTaskData /*-------------------------------------------------------------------------*/ +class GUCEF_VFS_PUBLIC_CPP CCopyFileContentTaskData : public CAsyncVfsTaskData +{ + public: + + CORE::CString originalFilepath; + CORE::CString copyFilepath; + bool overwrite; + + virtual CICloneable* Clone( void ) const GUCEF_VIRTUAL_OVERRIDE; + + CCopyFileContentTaskData( void ); + CCopyFileContentTaskData( const CCopyFileContentTaskData& src ); + virtual ~CCopyFileContentTaskData(); +}; + +/*-------------------------------------------------------------------------*/ + class GUCEF_VFS_PUBLIC_CPP CMoveFileTaskData : public CAsyncVfsTaskData { public: diff --git a/platform/gucefVFS/include/gucefVFS_CArchive.h b/platform/gucefVFS/include/gucefVFS_CArchive.h index 929429be6..2d323009c 100644 --- a/platform/gucefVFS/include/gucefVFS_CArchive.h +++ b/platform/gucefVFS/include/gucefVFS_CArchive.h @@ -117,6 +117,19 @@ class GUCEF_VFS_PUBLIC_CPP CArchive : public CORE::CObservingNotifier const UInt32 memLoadSize = 0 , const bool overwrite = false ) = 0; + /** + * Default implementation always fails. + * Derived classes should override this method to provide the functionality if feasible. + * Recommended implementation is to apply the meta-data 'best effort' based on the constraints of the actual underlying storage + * and to return true if within the constraints the meta-data was applied successfully. + * Aside from the metadata handling the bahaviour of this member function should be the same as GetFile(). + */ + virtual TBasicVfsResourcePtr GetFileAs( const CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ); + virtual bool StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , const CORE::UInt64 offset , @@ -150,6 +163,15 @@ class GUCEF_VFS_PUBLIC_CPP CArchive : public CORE::CObservingNotifier virtual UInt64 GetFileSize( const CString& filePath ) const = 0; + /** + * Default implementation always fails. + * Derived classes should override this method to provide the functionality if feasible. + * Recommended implementation is to apply the meta-data 'best effort' based on the constraints of the actual underlying storage + * and to return true if within the constraints the meta-data was applied successfully. + */ + virtual bool SetFileMetaData( const CString& filePath , + const CORE::CResourceMetaData& metaData ); + virtual bool GetFileMetaData( const CString& filePath , CORE::CResourceMetaData& metaData ) const = 0; diff --git a/platform/gucefVFS/include/gucefVFS_CFileSystemArchive.h b/platform/gucefVFS/include/gucefVFS_CFileSystemArchive.h index b4340e218..e3305eed1 100644 --- a/platform/gucefVFS/include/gucefVFS_CFileSystemArchive.h +++ b/platform/gucefVFS/include/gucefVFS_CFileSystemArchive.h @@ -78,6 +78,12 @@ class GUCEF_VFS_PUBLIC_CPP CFileSystemArchive : public CArchive const UInt32 memLoadSize = 0 , const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual TBasicVfsResourcePtr GetFileAs( const CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual bool StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , const CORE::UInt64 offset , @@ -111,6 +117,9 @@ class GUCEF_VFS_PUBLIC_CPP CFileSystemArchive : public CArchive virtual UInt64 GetFileSize( const CString& filePath ) const GUCEF_VIRTUAL_OVERRIDE; + virtual bool SetFileMetaData( const CString& filePath , + const CORE::CResourceMetaData& metaData ) GUCEF_VIRTUAL_OVERRIDE; + virtual bool GetFileMetaData( const CString& filePath , CORE::CResourceMetaData& metaData ) const GUCEF_VIRTUAL_OVERRIDE; @@ -183,10 +192,11 @@ class GUCEF_VFS_PUBLIC_CPP CFileSystemArchive : public CArchive bool addFiles , bool addDirs ) const; - TBasicVfsResourcePtr LoadFromDisk( const CString& file , - const char* mode = "rb" , - const UInt32 memLoadSize = 0 , - const bool overwrite = false ); + TBasicVfsResourcePtr LoadFromDisk( const CString& file , + const char* mode , + const CORE::CResourceMetaData* metaData , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ); private: typedef CORE::CTSharedPtr< CORE::CDynamicBuffer, MT::CMutex > TDynamicBufferPtr; diff --git a/platform/gucefVFS/include/gucefVFS_CVFS.h b/platform/gucefVFS/include/gucefVFS_CVFS.h index 873de4a60..88aad2aed 100644 --- a/platform/gucefVFS/include/gucefVFS_CVFS.h +++ b/platform/gucefVFS/include/gucefVFS_CVFS.h @@ -214,6 +214,11 @@ class GUCEF_VFS_PUBLIC_CPP CVFS : public CORE::CTSGNotifier , const char* mode = "rb" , const bool overwrite = false ); + TBasicVfsResourcePtr GetFileAs( const CORE::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const bool overwrite = false ); + /** * Loads the entire file at the given location into memory if possible */ @@ -259,6 +264,37 @@ class GUCEF_VFS_PUBLIC_CPP CVFS : public CORE::CTSGNotifier , /** * Copies a file from one vfs path to another if possible * the target file must be located in a writable target mounted archive + * Note that meta-data is explicitly not copied, only the file content + * use CopyFile() if you care about the meta-data + * + * @param originalFilepath path to the original file to be copied + * @param copyFilepath destination path for the file copy + * @param overwrite whether to overwrite any existing file at the target path if one exists + */ + bool CopyFileContent( const CORE::CString& originalFilepath , + const CORE::CString& copyFilepath , + const bool overwrite = false ); + + /** + * Async copies file content from one vfs path to another if possible + * the target file must be located in a writable target mounted archive + * See CopyFileContent() + * + * @param originalFilepath path to the original file to be copied + * @param copyFilepath destination path for the file copy + * @param overwrite whether to overwrite any existing file at the target path if one exists + * @param asyncRequestId optional: user defined identifier for the async request to be provided back in the response + */ + bool CopyFileContentAsync( const CORE::CString& originalFilepath , + const CORE::CString& copyFilepath , + const bool overwrite = false , + const CORE::CString& asyncRequestId = CORE::CString::Empty ); + + /** + * Copies a file from one vfs path to another if possible + * the target file must be located in a writable target mounted archive + * Note that meta-data is explicitly copied as well to the extent feasible using the underlying backends + * use CopyFileContent() if you only care about the file content as it would incur less overhead * * @param originalFilepath path to the original file to be copied * @param copyFilepath destination path for the file copy @@ -271,6 +307,7 @@ class GUCEF_VFS_PUBLIC_CPP CVFS : public CORE::CTSGNotifier , /** * Async copies a file from one vfs path to another if possible * the target file must be located in a writable target mounted archive + * See CopyFile() * * @param originalFilepath path to the original file to be copied * @param copyFilepath destination path for the file copy @@ -552,6 +589,9 @@ class GUCEF_VFS_PUBLIC_CPP CVFS : public CORE::CTSGNotifier , bool GetFileMetaData( const CString& filename , CORE::CResourceMetaData& metaData ) const; + bool SetFileMetaData( const CString& filename , + CORE::CResourceMetaData& metaData ); + bool GetActualFilePath( const CString& file , CString& path ) const; @@ -638,6 +678,7 @@ class GUCEF_VFS_PUBLIC_CPP CVFS : public CORE::CTSGNotifier , CORE::CString path; bool writeable; TArchivePtr archive; + CORE::CString archiveType; CORE::CString mountPath; }; typedef struct SMountEntry TMountEntry; diff --git a/platform/gucefVFS/src/gucefVFS_AsyncVfsTaskData.cpp b/platform/gucefVFS/src/gucefVFS_AsyncVfsTaskData.cpp index 1106f220e..99fbe410f 100644 --- a/platform/gucefVFS/src/gucefVFS_AsyncVfsTaskData.cpp +++ b/platform/gucefVFS/src/gucefVFS_AsyncVfsTaskData.cpp @@ -462,6 +462,44 @@ CCopyFileTaskData::Clone( void ) const /*-------------------------------------------------------------------------*/ +CCopyFileContentTaskData::CCopyFileContentTaskData( void ) + : CAsyncVfsTaskData() + , originalFilepath() + , copyFilepath() + , overwrite( false ) +{GUCEF_TRACE; + +} + +/*-------------------------------------------------------------------------*/ + +CCopyFileContentTaskData::CCopyFileContentTaskData( const CCopyFileContentTaskData& src ) + : CAsyncVfsTaskData( src ) + , originalFilepath( src.originalFilepath ) + , copyFilepath( src.copyFilepath ) + , overwrite( src.overwrite ) +{GUCEF_TRACE; + +} + +/*-------------------------------------------------------------------------*/ + +CCopyFileContentTaskData::~CCopyFileContentTaskData() +{GUCEF_TRACE; + +} + +/*-------------------------------------------------------------------------*/ + +CORE::CICloneable* +CCopyFileContentTaskData::Clone( void ) const +{GUCEF_TRACE; + + return GUCEF_NEW CCopyFileContentTaskData( *this ); +} + +/*-------------------------------------------------------------------------*/ + CMoveFileTaskData::CMoveFileTaskData( void ) : CAsyncVfsTaskData() , originalFilepath() diff --git a/platform/gucefVFS/src/gucefVFS_CArchive.cpp b/platform/gucefVFS/src/gucefVFS_CArchive.cpp index 7cbc9c594..bcc9d8704 100644 --- a/platform/gucefVFS/src/gucefVFS_CArchive.cpp +++ b/platform/gucefVFS/src/gucefVFS_CArchive.cpp @@ -73,6 +73,19 @@ CArchive::operator=( const CArchive& src ) /*-------------------------------------------------------------------------*/ +TBasicVfsResourcePtr +CArchive::GetFileAs( const CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode , + const UInt32 memLoadSize , + const bool overwrite ) +{GUCEF_TRACE; + + return TBasicVfsResourcePtr(); +} + +/*-------------------------------------------------------------------------*/ + bool CArchive::AddDirToWatch( const CString& dirToWatch , const CDirWatchOptions& options , @@ -136,6 +149,16 @@ CArchive::IsConnected( void ) const return true; } +/*-------------------------------------------------------------------------*/ + +bool +CArchive::SetFileMetaData( const CString& filePath , + const CORE::CResourceMetaData& metaData ) +{GUCEF_TRACE; + + return false; +} + /*-------------------------------------------------------------------------// // // // NAMESPACE // diff --git a/platform/gucefVFS/src/gucefVFS_CAsyncVfsOperation.cpp b/platform/gucefVFS/src/gucefVFS_CAsyncVfsOperation.cpp index 1214ed6db..0bb89fd3a 100644 --- a/platform/gucefVFS/src/gucefVFS_CAsyncVfsOperation.cpp +++ b/platform/gucefVFS/src/gucefVFS_CAsyncVfsOperation.cpp @@ -185,6 +185,23 @@ CAsyncVfsOperation::OnTaskCycle( CORE::CICloneable* taskData ) if ( !NotifyObservers( AsyncVfsOperationCompletedEvent, &syncCallResult ) ) return true; break; } + case ASYNCVFSOPERATIONTYPE_COPYFILECONTENT: + { + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "AsyncVfsOperation(" + CORE::PointerToString( this ) + "):OnTaskCycle: Async invocation of CopyFileContent" ); + + CCopyFileContentTaskData* specificSyncCallData = static_cast< CCopyFileContentTaskData* >( syncCallData ); + bool success = CVfsGlobal::Instance()->GetVfs().CopyFileContent( specificSyncCallData->originalFilepath , + specificSyncCallData->copyFilepath , + specificSyncCallData->overwrite ); + + syncCallResult.successState = success; + syncCallResult.durationInMilliSecs = (UInt32) startTime.CORE::CDateTime::GetTimeDifferenceInMillisecondsToNow(); + + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "AsyncVfsOperation(" + CORE::PointerToString( this ) + "):OnTaskCycle: Completed Async invocation of CopyFileContent" ); + + if ( !NotifyObservers( AsyncVfsOperationCompletedEvent, &syncCallResult ) ) return true; + break; + } case ASYNCVFSOPERATIONTYPE_ENCODEFILE: { GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "AsyncVfsOperation(" + CORE::PointerToString( this ) + "):OnTaskCycle: Async invocation of EncodeFile" ); diff --git a/platform/gucefVFS/src/gucefVFS_CFileSystemArchive.cpp b/platform/gucefVFS/src/gucefVFS_CFileSystemArchive.cpp index fed694d65..3e92ecd65 100644 --- a/platform/gucefVFS/src/gucefVFS_CFileSystemArchive.cpp +++ b/platform/gucefVFS/src/gucefVFS_CFileSystemArchive.cpp @@ -193,15 +193,34 @@ CFileSystemArchive::UnloadArchive( void ) /*-------------------------------------------------------------------------*/ TBasicVfsResourcePtr -CFileSystemArchive::GetFile( const CString& file , +CFileSystemArchive::GetFile( const CString& filepath , const char* mode , const UInt32 memLoadSize , const bool overwrite ) {GUCEF_TRACE; - return LoadFromDisk( file , + return LoadFromDisk( filepath , mode , + GUCEF_NULL , + memLoadSize , + overwrite ); +} + +/*-------------------------------------------------------------------------*/ + +TBasicVfsResourcePtr +CFileSystemArchive::GetFileAs( const CString& filepath , + const CORE::CResourceMetaData& metaData , + const char* mode , + const UInt32 memLoadSize , + const bool overwrite ) + +{GUCEF_TRACE; + + return LoadFromDisk( filepath , + mode , + &metaData , memLoadSize , overwrite ); } @@ -455,6 +474,17 @@ CFileSystemArchive::GetFileHash( const CString& filePath ) const /*-------------------------------------------------------------------------*/ +bool +CFileSystemArchive::SetFileMetaData( const CString& filePath , + const CORE::CResourceMetaData& metaData ) +{GUCEF_TRACE; + + CString path = CORE::CombinePath( m_rootDir, filePath.ReplaceChar( GUCEF_DIRSEPCHAROPPOSITE, GUCEF_DIRSEPCHAR ) ); + return CORE::SetFileMetaData( path, metaData ); +} + +/*-------------------------------------------------------------------------*/ + bool CFileSystemArchive::GetFileMetaData( const CString& filePath , CORE::CResourceMetaData& metaData ) const @@ -508,10 +538,11 @@ CFileSystemArchive::GetType( void ) const /*-------------------------------------------------------------------------*/ TBasicVfsResourcePtr -CFileSystemArchive::LoadFromDisk( const CString& filePath , - const char* mode , - const UInt32 memLoadSize , - const bool overwrite ) +CFileSystemArchive::LoadFromDisk( const CString& filePath , + const char* mode , + const CORE::CResourceMetaData* metaData , + const UInt32 memLoadSize , + const bool overwrite ) {GUCEF_TRACE; // Create a file path for this root @@ -520,23 +551,26 @@ CFileSystemArchive::LoadFromDisk( const CString& filePath , bool isAppend( ( strchr( mode, 'a' ) != NULL ) || ( strchr( mode, '+' ) != NULL ) ); bool needwriteable( ( strchr( mode, 'a' ) != NULL ) || ( strchr( mode, 'w' ) != NULL ) || ( strchr( mode, '+' ) != NULL ) ); - // Check if we can perform read-only access which allows us - // to share the resource - if ( ( strcmp( mode, "rb" ) == 0 ) || - ( strcmp( mode, "r" ) == 0 ) ) + if ( GUCEF_NULL == metaData ) { - // Check our cache for this file - TFileMemCache::iterator n = m_diskCacheList.find( path ); - if ( n != m_diskCacheList.end() ) + // Check if we can perform read-only access which allows us + // to share the resource + if ( ( strcmp( mode, "rb" ) == 0 ) || + ( strcmp( mode, "r" ) == 0 ) ) { - // We found the file in our cache, we will link to the existing buffer. - TDynamicBufferPtr bufferPtr = (*n).second; - CORE::DynamicBufferAccessPtr bufferAccess( GUCEF_NEW CORE::CDynamicBufferAccess( bufferPtr.GetPointer(), false ) ); - TVfsResourcePtr vfsResource( GUCEF_NEW CVFSHandle( bufferAccess , - path , - filePath , - bufferPtr ) ); - return vfsResource; + // Check our cache for this file + TFileMemCache::iterator n = m_diskCacheList.find( path ); + if ( n != m_diskCacheList.end() ) + { + // We found the file in our cache, we will link to the existing buffer. + TDynamicBufferPtr bufferPtr = (*n).second; + CORE::DynamicBufferAccessPtr bufferAccess( GUCEF_NEW CORE::CDynamicBufferAccess( bufferPtr.GetPointer(), false ) ); + TVfsResourcePtr vfsResource( GUCEF_NEW CVFSHandle( bufferAccess , + path , + filePath , + bufferPtr ) ); + return vfsResource; + } } } @@ -557,7 +591,7 @@ CFileSystemArchive::LoadFromDisk( const CString& filePath , ( exists && isAppend ) ) { // Attempt to get access to the file - CORE::FileAccessPtr fa( GUCEF_NEW CORE::CFileAccess( path, mode ) ); + CORE::FileAccessPtr fa( GUCEF_NULL == metaData ? GUCEF_NEW CORE::CFileAccess( path, mode ) : GUCEF_NEW CORE::CFileAccess( path, *metaData, mode ) ); if ( !fa.IsNULL() && !fa->IsValid() ) { // try a different root diff --git a/platform/gucefVFS/src/gucefVFS_CVFS.cpp b/platform/gucefVFS/src/gucefVFS_CVFS.cpp index 4ab5ef1a9..507472a9f 100644 --- a/platform/gucefVFS/src/gucefVFS_CVFS.cpp +++ b/platform/gucefVFS/src/gucefVFS_CVFS.cpp @@ -661,20 +661,112 @@ CVFS::MoveFileAsync( const CORE::CString& oldFilePath , /*-------------------------------------------------------------------------*/ +bool +CVFS::CopyFileContent( const CORE::CString& originalFilepath , + const CORE::CString& copyFilepath , + const bool overwrite ) +{GUCEF_TRACE; + + if ( originalFilepath == copyFilepath ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContent: original and target path cannot be the same: " + originalFilepath ); + return false; + } + + TBasicVfsResourcePtr originalFile = GetFile( originalFilepath, "rb", overwrite ); + if ( !originalFile || GUCEF_NULL == originalFile->GetAccess() || !originalFile->GetAccess()->IsValid() ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContent: Cannot obtain original file: " + originalFilepath ); + return false; + } + UInt64 origSize = originalFile->GetAccess()->GetSize(); + + TBasicVfsResourcePtr targetFile = GetFile( copyFilepath, "wb", overwrite ); + if ( !targetFile || GUCEF_NULL == targetFile->GetAccess() || !targetFile->GetAccess()->IsValid() ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContent: Cannot obtain access to output file: " + copyFilepath ); + return false; + } + + // Perform the actual byte copy synchronously and check if the expected size matches the actual size + UInt64 bytesWritten = targetFile->GetAccess()->Write( *originalFile->GetAccess() ); + if ( origSize != bytesWritten ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContent: Failed to fully copy file \"" + originalFilepath + + "\" to \"" + copyFilepath + "\". Bytes written (" + CORE::ToString( bytesWritten ) + + ") does not match expected size (" + CORE::ToString( origSize ) ); + + targetFile.Unlink(); + + // We should not leave a corrupt file behind + if ( !DeleteFile( copyFilepath, false ) ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_IMPORTANT, "VFS:CopyFileContent: Failed to delete partially copied file at \"" + copyFilepath + "\". Corrupt file may now exist in target location, please check!" ); + } + return false; + } + + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContent: Successfully copied file \"" + originalFilepath + + "\" to \"" + copyFilepath + "\"" ); + return true; +} + +/*-------------------------------------------------------------------------*/ + +bool +CVFS::CopyFileContentAsync( const CORE::CString& originalFilepath , + const CORE::CString& copyFilepath , + const bool overwrite , + const CORE::CString& asyncRequestId ) +{GUCEF_TRACE; + + try + { + CCopyFileContentTaskData operationData; + operationData.operationType = ASYNCVFSOPERATIONTYPE_COPYFILECONTENT; + operationData.asyncRequestId = asyncRequestId; + operationData.originalFilepath = originalFilepath; + operationData.copyFilepath = copyFilepath; + operationData.overwrite = overwrite; + + CORE::ThreadPoolPtr threadPool = CORE::CCoreGlobal::Instance()->GetTaskManager().GetOrCreateThreadPool( m_asyncOpsThreadpool ); + return !CORE::TaskStatusIsAnError( threadPool->QueueTask( CAsyncVfsOperation::TaskType, &operationData, GUCEF_NULL, &AsObserver() ) ); + } + catch ( const timeout_exception& ) + { + GUCEF_WARNING_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFileContentAsync: Failed to queue task due to timeout. asyncRequestId=" + + asyncRequestId ); + } + catch ( const std::exception& e ) + { + GUCEF_EXCEPTION_LOG( CORE::LOGLEVEL_IMPORTANT, "VFS:CopyFileContentAsync: Failed to queue task due to exception. asyncRequestId=" + + asyncRequestId + " what=" + CORE::ToString( e.what() ) ); + } + return false; +} + +/*-------------------------------------------------------------------------*/ + bool CVFS::CopyFile( const CORE::CString& originalFilepath , const CORE::CString& copyFilepath , const bool overwrite ) {GUCEF_TRACE; -return false; - if ( originalFilepath == copyFilepath ) { GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: original and target path cannot be the same: " + originalFilepath ); return false; } + CORE::CResourceMetaData metaData; + if ( !GetFileMetaData( originalFilepath , + metaData ) ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Cannot obtain meta-data on original file: " + originalFilepath ); + return false; + } + TBasicVfsResourcePtr originalFile = GetFile( originalFilepath, "rb", overwrite ); if ( !originalFile || GUCEF_NULL == originalFile->GetAccess() || !originalFile->GetAccess()->IsValid() ) { @@ -682,15 +774,54 @@ return false; return false; } - TBasicVfsResourcePtr targetFile = GetFile( copyFilepath, "wb", overwrite ); + // Applying meta-data when obtaining the target file is ideal since it avoids the possibility of race conditions + // where the file is created without the meta-data and then the meta-data is applied afterwards + // However not all backend implementations support this so we fall back to applying the meta-data afterwards + TBasicVfsResourcePtr targetFile = GetFileAs( copyFilepath, metaData, "wb", overwrite ); if ( !targetFile || GUCEF_NULL == targetFile->GetAccess() || !targetFile->GetAccess()->IsValid() ) { - GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Cannot obtain access to output file: " + copyFilepath ); - return false; + GUCEF_WARNING_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Cannot obtain access to output file using source metadata as a constraint: " + copyFilepath ); + + // Try again without the meta-data constraint + targetFile = GetFile( copyFilepath, "wb", overwrite ); + if ( !targetFile || GUCEF_NULL == targetFile->GetAccess() || !targetFile->GetAccess()->IsValid() ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Cannot obtain access to output file: " + copyFilepath ); + return false; + } + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Obtained access to output file without using source metadata as a constraint: " + copyFilepath ); } + // Perform the actual byte copy synchronously and check if the expected size matches the actual size UInt64 bytesWritten = targetFile->GetAccess()->Write( *originalFile->GetAccess() ); + if ( metaData.hasResourceSizeInBytes ) + { + if ( bytesWritten != metaData.resourceSizeInBytes ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Failed to fully copy file \"" + originalFilepath + + "\" to \"" + copyFilepath + "\". Bytes written (" + CORE::ToString( bytesWritten ) + + ") does not match expected size (" + CORE::ToString( metaData.resourceSizeInBytes ) + ")" ); + + targetFile.Unlink(); + + // We should not leave a corrupt file behind + if ( !DeleteFile( copyFilepath, false ) ) + { + GUCEF_ERROR_LOG( CORE::LOGLEVEL_IMPORTANT, "VFS:CopyFile: Failed to delete partially copied file at \"" + copyFilepath + "\". Corrupt file may now exist in target location, please check!" ); + } + return false; + } + } + // Again set the meta-data, this time after the file has been written + // We need to do this regardless to fix the 'lastModified' time if possible + // hopefully the other flags were already set during the file creation + if ( !SetFileMetaData( copyFilepath, metaData ) ) + { + GUCEF_WARNING_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Could not apply meta-data to copied file \"" + copyFilepath + + "\" using info from \"" + originalFilepath + "\". File content is copied but meta-data will differ" ); + } + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "VFS:CopyFile: Successfully copied file \"" + originalFilepath + "\" to \"" + copyFilepath + "\"" ); return true; @@ -1140,6 +1271,9 @@ CVFS::GetFile( const CORE::CString& file , CString filepath = ConformVfsFilePath( file ); bool fileMustExist = *mode == 'r'; + if ( filepath.IsNULLOrEmpty() || GUCEF_NULL == mode ) + return TBasicVfsResourcePtr(); + MT::CScopeReaderLock lock( m_rwdataLock ); // Get a list of all eligable mounts @@ -1187,6 +1321,51 @@ CVFS::GetFile( const CORE::CString& file , /*-------------------------------------------------------------------------*/ +TBasicVfsResourcePtr +CVFS::GetFileAs( const CORE::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode , + const bool overwrite ) +{GUCEF_TRACE; + + CString filepath = ConformVfsFilePath( file ); + + if ( filepath.IsNULLOrEmpty() || GUCEF_NULL == mode ) + return TBasicVfsResourcePtr(); + + MT::CScopeReaderLock lock( m_rwdataLock ); + + // Get a list of all eligable mounts + TConstMountLinkVector mountLinks; + GetEligableMounts( filepath, overwrite, mountLinks ); + + // Find the file in the available archives + TConstMountLinkVector::iterator i = mountLinks.begin(); + while ( i != mountLinks.end() ) + { + TConstMountLink& mountLink = (*i); + + GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "Vfs: Found requested file using mount link remainder: " + mountLink.remainder ); + TArchivePtr archive = mountLink.mountEntry->archive; + TBasicVfsResourcePtr filePtr = archive->GetFileAs( mountLink.remainder , + metaData , + mode , + m_maxmemloadsize , + overwrite ); + + if ( filePtr ) + return filePtr; + + ++i; + } + + // Unable to load file + GUCEF_ERROR_LOG( CORE::LOGLEVEL_BELOW_NORMAL, "Vfs: Unable to locate a mount which can provide the file using the provided meta-data constraints: " + file ); + return TBasicVfsResourcePtr(); +} + +/*-------------------------------------------------------------------------*/ + bool CVFS::LoadFile( CORE::CDynamicBuffer& destinationBuffer , const CORE::CString& filePath , @@ -2278,6 +2457,37 @@ CVFS::GetFileList( CORE::CDataNode& outputDataTree , /*-------------------------------------------------------------------------*/ +bool +CVFS::SetFileMetaData( const CString& filename , + CORE::CResourceMetaData& metaData ) +{GUCEF_TRACE; + + CString path = ConformVfsFilePath( filename ); + + MT::CScopeReaderLock lock( m_rwdataLock ); + + // Get a list of all eligable mounts + TConstMountLinkVector mountLinks; + GetEligableMounts( path, false, mountLinks ); + + // Search for a file and then get the meta data + TConstMountLinkVector::iterator i = mountLinks.begin(); + while ( i != mountLinks.end() ) + { + TConstMountLink& mountLink = (*i); + TArchivePtr archive = mountLink.mountEntry->archive; + if ( archive->SetFileMetaData( mountLink.remainder, metaData ) ) + { + return true; + } + ++i; + } + + return false; +} + +/*-------------------------------------------------------------------------*/ + bool CVFS::GetFileMetaData( const CString& filename , CORE::CResourceMetaData& metaData ) const diff --git a/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3Archive.h b/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3Archive.h index bf4045ff2..3612cb3a5 100644 --- a/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3Archive.h +++ b/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3Archive.h @@ -84,6 +84,12 @@ class GUCEF_HIDDEN CS3Archive : public VFS::CArchive const VFS::UInt32 memLoadSize = 0 , const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual VFS::TBasicVfsResourcePtr GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual bool StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , const CORE::UInt64 offset , diff --git a/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3BucketArchive.h b/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3BucketArchive.h index 5857bb265..63024dae9 100644 --- a/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3BucketArchive.h +++ b/plugins/VFS/vfspluginAWSS3/include/vfspluginAWSS3_CS3BucketArchive.h @@ -85,6 +85,12 @@ class GUCEF_HIDDEN CS3BucketArchive : public VFS::CArchive const VFS::UInt32 memLoadSize = 0 , const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual VFS::TBasicVfsResourcePtr GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual bool StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , const CORE::UInt64 offset , diff --git a/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3Archive.cpp b/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3Archive.cpp index bdd3cfab5..5ca66f92e 100644 --- a/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3Archive.cpp +++ b/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3Archive.cpp @@ -135,6 +135,20 @@ CS3Archive::GetFile( const VFS::CString& file , /*-------------------------------------------------------------------------*/ +VFS::TBasicVfsResourcePtr +CS3Archive::GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode , + const UInt32 memLoadSize , + const bool overwrite ) +{GUCEF_TRACE; + + // We dont currently support applying meta-data + return VFS::TBasicVfsResourcePtr(); +} + +/*-------------------------------------------------------------------------*/ + bool CS3Archive::StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , diff --git a/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3BucketArchive.cpp b/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3BucketArchive.cpp index 3ed946f18..9d9d4bfe3 100644 --- a/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3BucketArchive.cpp +++ b/plugins/VFS/vfspluginAWSS3/src/vfspluginAWSS3_CS3BucketArchive.cpp @@ -161,6 +161,20 @@ CS3BucketArchive::GetFile( const VFS::CString& file , /*-------------------------------------------------------------------------*/ +VFS::TBasicVfsResourcePtr +CS3BucketArchive::GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode , + const UInt32 memLoadSize , + const bool overwrite ) +{GUCEF_TRACE; + + // We dont currently support applying meta-data + return VFS::TBasicVfsResourcePtr(); +} + +/*-------------------------------------------------------------------------*/ + bool CS3BucketArchive::StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , diff --git a/plugins/VFS/vfspluginZIP/include/vfspluginZIP_CZIPArchive.h b/plugins/VFS/vfspluginZIP/include/vfspluginZIP_CZIPArchive.h index fdd75e70d..1c9dd2320 100644 --- a/plugins/VFS/vfspluginZIP/include/vfspluginZIP_CZIPArchive.h +++ b/plugins/VFS/vfspluginZIP/include/vfspluginZIP_CZIPArchive.h @@ -79,6 +79,12 @@ class CZIPArchive : public VFS::CArchive const VFS::UInt32 memLoadSize = 0 , const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual VFS::TBasicVfsResourcePtr GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode = "wb" , + const UInt32 memLoadSize = 0 , + const bool overwrite = false ) GUCEF_VIRTUAL_OVERRIDE; + virtual bool StoreAsFile( const CORE::CString& filepath , const CORE::CDynamicBuffer& data , const CORE::UInt64 offset , diff --git a/plugins/VFS/vfspluginZIP/src/vfspluginZIP_CZIPArchive.cpp b/plugins/VFS/vfspluginZIP/src/vfspluginZIP_CZIPArchive.cpp index 7b2feeb58..33f5f0546 100644 --- a/plugins/VFS/vfspluginZIP/src/vfspluginZIP_CZIPArchive.cpp +++ b/plugins/VFS/vfspluginZIP/src/vfspluginZIP_CZIPArchive.cpp @@ -172,6 +172,20 @@ CZIPArchive::GetFile( const VFS::CString& file , /*-------------------------------------------------------------------------*/ +VFS::TBasicVfsResourcePtr +CZIPArchive::GetFileAs( const VFS::CString& file , + const CORE::CResourceMetaData& metaData , + const char* mode , + const UInt32 memLoadSize , + const bool overwrite ) +{GUCEF_TRACE; + + // We dont currently support applying meta-data + return VFS::TBasicVfsResourcePtr(); +} + +/*-------------------------------------------------------------------------*/ + bool CZIPArchive::DeleteFile( const VFS::CString& filePath ) {GUCEF_TRACE; diff --git a/tools/FileSorter/FileSorter.ini b/tools/FileSorter/FileSorter.ini index dba0b2e02..309ec209f 100644 --- a/tools/FileSorter/FileSorter.ini +++ b/tools/FileSorter/FileSorter.ini @@ -128,6 +128,8 @@ "monthFolderDecoration"="-== {month}. {monthName} ==-" "useDayFolder"="true" "dayFolderDecoration"="{month}.{day}.{year}" +"sortAllFileTypes"="false" +"fileTypesToSort"="image,video" [FileSorter\FileSorterConfig\FileTypeConfig\FileTypes\FileType] "type"="image" diff --git a/tools/FileSorter/FileSorter_d.ini b/tools/FileSorter/FileSorter_d.ini index 1cf6a174b..fcfa1ebf8 100644 --- a/tools/FileSorter/FileSorter_d.ini +++ b/tools/FileSorter/FileSorter_d.ini @@ -128,6 +128,8 @@ "monthFolderDecoration"="-== {month}. {monthName} ==-" "useDayFolder"="true" "dayFolderDecoration"="{month}.{day}.{year}" +"sortAllFileTypes"="false" +"fileTypesToSort"="image,video" [FileSorter\FileSorterConfig\FileTypeConfig\FileTypes\FileType] "type"="image" diff --git a/tools/FileSorter/include/FileSorter.h b/tools/FileSorter/include/FileSorter.h index c86ff67a2..af9bd4c52 100644 --- a/tools/FileSorter/include/FileSorter.h +++ b/tools/FileSorter/include/FileSorter.h @@ -25,6 +25,10 @@ // // //-------------------------------------------------------------------------*/ +#include +#include +#include + #ifndef GUCEF_CORE_CICONFIGURABLE_H #include "CIConfigurable.h" #define GUCEF_CORE_CICONFIGURABLE_H @@ -159,6 +163,7 @@ enum EFileType }; typedef enum EFileType TFileType; +typedef std::set< TFileType > TFileTypeSet; typedef std::map< CORE::CString, TFileType > TStringToFileTypeMap; typedef std::map< TFileType, CORE::CString > TFileTypeToStringMap; @@ -182,7 +187,10 @@ class FileTypeConfig : public CORE::CIConfigurable TFileType GetFileTypeForFileExtension( const CORE::CString& fileExt ) const; - TFileType GetFileTypeForString( const CORE::CString& fileTypeStr ) const; + static CORE::CString GetStringForFileType( const TFileType fileType ); + static TFileType GetFileTypeForString( const CORE::CString& fileTypeStr ); + static TFileTypeSet GetFileTypesForStringSet( const CORE::CString::StringSet& fileTypeStrs ); + static CORE::CString::StringSet GetStringSetForFileTypes( const TFileTypeSet& fileTypes ); const CORE::CString& GetSortRootFolderForFileType( TFileType fileType ) const; @@ -223,6 +231,8 @@ class FileSorterConfig : public CORE::CGloballyConfigurable CORE::CString monthFolderDecoration; bool useDayFolder; CORE::CString dayFolderDecoration; + bool sortAllFileTypes; + TFileTypeSet fileTypesToSort; private: diff --git a/tools/FileSorter/src/FileSorter.cpp b/tools/FileSorter/src/FileSorter.cpp index 114c24a7f..d75164b04 100644 --- a/tools/FileSorter/src/FileSorter.cpp +++ b/tools/FileSorter/src/FileSorter.cpp @@ -191,10 +191,42 @@ FileTypeConfig::GetClassTypeName( void ) const return ClassTypeName; } +/*-------------------------------------------------------------------------*/ + + CORE::CString +FileTypeConfig::GetStringForFileType( const TFileType fileType ) +{GUCEF_TRACE; + + switch ( fileType ) + { + case TFileType::FILETYPE_TEXT: return "text"; + case TFileType::FILETYPE_BINARY: return "binary"; + case TFileType::FILETYPE_IMAGE: return "image"; + case TFileType::FILETYPE_AUDIO: return "audio"; + case TFileType::FILETYPE_VIDEO: return "video"; + case TFileType::FILETYPE_ARCHIVE: return "archive"; + case TFileType::FILETYPE_DOCUMENT: return "document"; + case TFileType::FILETYPE_PRESENTATION: return "presentation"; + case TFileType::FILETYPE_SPREADSHEET: return "spreadsheet"; + case TFileType::FILETYPE_DATABASE: return "database"; + case TFileType::FILETYPE_EXECUTABLE: return "executable"; + case TFileType::FILETYPE_SCRIPT: return "script"; + case TFileType::FILETYPE_WEBPAGE: return "webpage"; + case TFileType::FILETYPE_SOURCECODE: return "sourcecode"; + case TFileType::FILETYPE_CONFIGURATION: return "configuration"; + case TFileType::FILETYPE_LOG: return "log"; + case TFileType::FILETYPE_TEMPORARY: return "temporary"; + case TFileType::FILETYPE_COMPRESSED: return "compressed"; + case TFileType::FILETYPE_OTHER: return "other"; + case TFileType::FILETYPE_UNKNOWN: return "unknown"; + default: return "unknown"; + } +} + /*-------------------------------------------------------------------------*/ TFileType -FileTypeConfig::GetFileTypeForString( const CORE::CString& fileTypeStr ) const +FileTypeConfig::GetFileTypeForString( const CORE::CString& fileTypeStr ) {GUCEF_TRACE; const CORE::CString& fileTypeStrLc = fileTypeStr.Lowercase(); @@ -247,6 +279,40 @@ FileTypeConfig::GetFileTypeForString( const CORE::CString& fileTypeStr ) const /*-------------------------------------------------------------------------*/ +TFileTypeSet +FileTypeConfig::GetFileTypesForStringSet( const CORE::CString::StringSet& fileTypeStrs ) +{GUCEF_TRACE; + + TFileTypeSet fileTypes; + CORE::CString::StringSet::const_iterator i = fileTypeStrs.begin(); + while ( i != fileTypeStrs.end() ) + { + const CORE::CString& fileTypeStr = (*i); + fileTypes.insert( GetFileTypeForString( fileTypeStr ) ); + ++i; + } + return fileTypes; +} + +/*-------------------------------------------------------------------------*/ + +CORE::CString::StringSet +FileTypeConfig::GetStringSetForFileTypes( const TFileTypeSet& fileTypes ) +{GUCEF_TRACE; + + CORE::CString::StringSet fileTypeStrs; + TFileTypeSet::const_iterator i = fileTypes.begin(); + while ( i != fileTypes.end() ) + { + TFileType fileType = (*i); + fileTypeStrs.insert( GetStringForFileType( fileType ) ); + ++i; + } + return fileTypeStrs; +} + +/*-------------------------------------------------------------------------*/ + bool FileTypeConfig::LoadConfig( const CORE::CDataNode& cfg ) {GUCEF_TRACE; @@ -331,6 +397,8 @@ FileSorterConfig::FileSorterConfig( void ) , monthFolderDecoration( "-== {month}. {monthName} ==-" ) , useDayFolder( true ) , dayFolderDecoration( "{month}.{day}.{year}" ) + , sortAllFileTypes( false ) + , fileTypesToSort() {GUCEF_TRACE; } @@ -372,7 +440,14 @@ FileSorterConfig::LoadConfig( const CORE::CDataNode& globalConfig ) monthFolderDecoration = cfg->GetAttributeValueOrChildValueByName( "monthFolderDecoration", monthFolderDecoration ).AsString( monthFolderDecoration, true ); useDayFolder = cfg->GetAttributeValueOrChildValueByName( "useDayFolder", useDayFolder ).AsBool( useDayFolder, true ); dayFolderDecoration = cfg->GetAttributeValueOrChildValueByName( "dayFolderDecoration", dayFolderDecoration ).AsString( dayFolderDecoration, true ); - + sortAllFileTypes = cfg->GetAttributeValueOrChildValueByName( "sortAllFileTypes", useDayFolder ).AsBool( sortAllFileTypes, true ); + + CORE::CString::StringSet fileTypesToSortStrSet = FileTypeConfig::GetStringSetForFileTypes( fileTypesToSort ); + CORE::CString fileTypesToSortStr = CORE::ToString( fileTypesToSortStrSet ); + fileTypesToSortStr = cfg->GetAttributeValueOrChildValueByName( "fileTypesToSort", fileTypesToSortStr ).AsString( fileTypesToSortStr, true ); + fileTypesToSortStrSet = CORE::StringToStringSet( fileTypesToSortStr ); + fileTypesToSort = FileTypeConfig::GetFileTypesForStringSet( fileTypesToSortStrSet ); + const CORE::CDataNode* fileTypeConfigNode = cfg->FindChild( "FileTypeConfig" ); if ( GUCEF_NULL != fileTypeConfigNode ) { @@ -398,10 +473,22 @@ FileSorter::SortFile( const CORE::CString& currentVfsFilePath ) {GUCEF_TRACE; VFS::CVFS& vfs = VFS::CVfsGlobal::Instance()->GetVfs(); - - CORE::CString filename = CORE::ExtractFilename( currentVfsFilePath ); + CORE::CString fileExt = CORE::ExtractFileExtention( currentVfsFilePath ); TFileType fileType = m_appConfig.fileTypeConfig.GetFileTypeForFileExtension( fileExt ); + + if ( !m_appConfig.sortAllFileTypes ) + { + TFileTypeSet::iterator i = m_appConfig.fileTypesToSort.find( fileType ); + if ( i == m_appConfig.fileTypesToSort.end() ) + { + GUCEF_LOG( CORE::LOGLEVEL_NORMAL, "FileSorter: File \"" + currentVfsFilePath + "\" is of type \"" + + FileTypeConfig::GetStringForFileType( fileType ) + "\" and is not in the list of file types to sort. Skipping" ); + return true; + } + } + + CORE::CString filename = CORE::ExtractFilename( currentVfsFilePath ); const CORE::CString& typeSortRootFolder = m_appConfig.fileTypeConfig.GetSortRootFolderForFileType( fileType ); CORE::CString targetVfsPath = m_appConfig.vfsSortedTargetRootPath + "/" + typeSortRootFolder;