//============================================================================ // IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS //============================================================================ //============================================================================ // IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES //============================================================================ //============================================================================ // IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES //============================================================================ //============================================================================ // IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) //============================================================================ //============================================================================ // IMPLEMENTATION PRIVATE DATA //============================================================================ //============================================================================ // INTERFACE DATA //============================================================================ //============================================================================ // IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES //============================================================================ //============================================================================ // IMPLEMENTATION PRIVATE FUNCTIONS //============================================================================ //============================================================================ // INTERFACE FUNCTIONS //============================================================================ // Returns the Path for a File Name. VFS_BOOL VFS_Util_GetPath( const VFS_String& strFileName, VFS_String& strPath ) { // Following exceptions: // Path of "/" is "" // Path of (char)":/" is ("" if( ( strFileName.size() == 1 && strFileName[ 0 ] == VFS_PATH_SEPARATOR ) || ( strFileName.size() == 3 && strFileName[ 1 ] == VFS_TEXT( ':' ) && strFileName[ 2 ] == VFS_PATH_SEPARATOR ) ) { strPath = VFS_TEXT( "" ); return VFS_TRUE; } strPath = WithoutTrailingSeparator( strFileName, VFS_FALSE ); // Is there a trailing backslash? if( strPath.rfind( VFS_PATH_SEPARATOR ) != VFS_String::npos ) { // Remove all the text starting from the trailing backslash. strPath = strPath.substr( 0, strPath.rfind( VFS_PATH_SEPARATOR ) ); } else { strPath = VFS_TEXT( "" ); } return VFS_TRUE; }
// Check / modify the Archive Extension. VFS_String CArchive::CheckExtension( VFS_String strFileName ) { static const VFS_String RIGHT( VFS_String( VFS_TEXT( "." ) ) + VFS_ARCHIVE_FILE_EXTENSION ); if( strFileName.size() < RIGHT.size() || ToLower( strFileName.substr( strFileName.size() - RIGHT.size(), RIGHT.size() ) ) != ToLower( RIGHT ) ) return strFileName + VFS_TEXT( "." ) + VFS_ARCHIVE_FILE_EXTENSION; return strFileName; }
VFS_BOOL VFS_Archive_GetInfo( const VFS_String& strArchiveFileName, VFS_EntityInfo& Info ) { // Not initialized yet? if( !IsInit() ) { SetLastError( VFS_ERROR_NOT_INITIALIZED_YET ); return VFS_FALSE; } // Get Info and change Type. if( !VFS_File_GetInfo( strArchiveFileName + VFS_TEXT( "." ) + VFS_ARCHIVE_FILE_EXTENSION, Info ) ) return VFS_FALSE; // Check the Extension. VFS_String strExtension; if( !VFS_Util_GetExtension( Info.strName, strExtension ) ) return VFS_FALSE; if( ToLower( strExtension ) != ToLower( VFS_ARCHIVE_FILE_EXTENSION ) ) { SetLastError( VFS_ERROR_NOT_AN_ARCHIVE ); return VFS_FALSE; } // Remove the Extension (THE ONLY TIME THE USER IS CONFRONTATED WITH THE ARCHIVE FILE EXTENSION IS WHEN HE USES EXPLICITELY THE VFS_ARCHIVE_FILE_EXTENSION CONSTANT). Info.strName = Info.strName.substr( 0, Info.strName.size() - strExtension.size() - 1 ); Info.strPath = Info.strPath.substr( 0, Info.strPath.size() - strExtension.size() - 1 ); // Change the Type in the Entity Information Record. Info.eType = VFS_ARCHIVE; return VFS_TRUE; }
// Determines whether the specified file name is absolute. VFS_BOOL VFS_Util_IsAbsoluteFileName( const VFS_String& strFileName ) { // There are two possibilities for an absolute File Name. // - <path separator>....... // - <drive letter><path separator>....... if( strFileName.size() == 0 ) return VFS_FALSE; if( strFileName[ 0 ] == VFS_PATH_SEPARATOR ) return VFS_TRUE; if( strFileName.size() < 3 ) return VFS_FALSE; if( isalpha( strFileName[ 0 ] ) && strFileName[ 1 ] == VFS_TEXT( ':' ) && strFileName[ 2 ] == VFS_PATH_SEPARATOR ) return VFS_TRUE; return VFS_FALSE; }
// Returns the Name for a File Name. VFS_BOOL VFS_Util_GetName( const VFS_String& strFileName, VFS_String& strName ) { // Following exceptions: // Name of "/" is "/" // Name of (char)":/" is (char)":/" if( ( strFileName.size() == 1 && strFileName[ 0 ] == VFS_PATH_SEPARATOR ) || ( strFileName.size() == 3 && strFileName[ 1 ] == VFS_TEXT( ':' ) && strFileName[ 2 ] == VFS_PATH_SEPARATOR ) ) { strName = strFileName; return VFS_TRUE; } strName = WithoutTrailingSeparator( strFileName, VFS_TRUE ); // Is there a trailing backslash? if( strName.rfind( VFS_PATH_SEPARATOR ) != VFS_String::npos ) { // Remove all the text from the beginning to the trailing backslash. strName = strName.substr( strName.rfind( VFS_PATH_SEPARATOR ) + 1 ); } return VFS_TRUE; }
// Create an Archive from the specified File List. VFS_BOOL VFS_Archive_CreateFromFileList( const VFS_String& strArchiveFileName, const VFS_FileNameMap& Files, const VFS_FilterNameList& UsedFilters ) { static VFS_BYTE Chunk[ FILE_COPY_CHUNK_SIZE ]; // If there's already an Archive with the same File Name and it's open... VFS_EntityInfo Info; if( VFS_Archive_GetInfo( ToLower( strArchiveFileName ), Info ) ) { // Check if the Archive is open. if( GetOpenArchives().find( ToLower( Info.strPath ) ) != GetOpenArchives().end() ) { // Check if the Reference Count is != 0. if( GetOpenArchives()[ ToLower( Info.strPath ) ]->GetRefCount() > 0 ) { // We don't want to manipulate an open Archive, do we? SetLastError( VFS_ERROR_IN_USE ); return VFS_FALSE; } else { // Free the Archive. delete GetOpenArchives()[ ToLower( Info.strPath ) ]; GetOpenArchives().erase( ToLower( Info.strPath ) ); } } } else { Info.strPath = ToLower( strArchiveFileName ); SetLastError( VFS_ERROR_NONE ); } // Check the Filter Names and make a List of all Filter Pointers. VFS_FilterList Filters; for( VFS_FilterNameList::const_iterator iter = UsedFilters.begin(); iter != UsedFilters.end(); iter++ ) { // Bad Filter Name? if( !VFS_ExistsFilter( *iter ) ) { SetLastError( VFS_ERROR_INVALID_PARAMETER ); return VFS_FALSE; } // Add the Filter. Filters.push_back( VFS_GetFilter( *iter ) ); } // Check all Files. for( VFS_FileNameMap::const_iterator iter2 = Files.begin(); iter2 != Files.end(); iter2++ ) { if( !VFS_File_Exists( ( *iter2 ).first ) ) { SetLastError( VFS_ERROR_NOT_FOUND ); return VFS_FALSE; } VFS_String strName; VFS_Util_GetName( ( *iter2 ).second, strName ); if( strName.size() > VFS_MAX_NAME_LENGTH ) { SetLastError( VFS_ERROR_INVALID_PARAMETER ); return VFS_FALSE; } } // Make a list of the Directories to create. typedef vector< VFS_String > NameMap; NameMap Dirs; for( VFS_FileNameMap::const_iterator iter3 = Files.begin(); iter3 != Files.end(); iter3++ ) { VFS_String strDir; VFS_Util_GetPath( ( *iter3 ).second, strDir ); strDir = ToLower( strDir ); if( strDir != VFS_TEXT( "" ) && find( Dirs.begin(), Dirs.end(), strDir ) == Dirs.end() ) { // Add the top-level Dirs. while( strDir.rfind( VFS_PATH_SEPARATOR ) != VFS_String::npos ) { if( find( Dirs.begin(), Dirs.end(), strDir ) != Dirs.end() ) break; Dirs.push_back( ToLower( strDir ) ); if( strDir.size() > VFS_MAX_NAME_LENGTH ) { SetLastError( VFS_ERROR_INVALID_PARAMETER ); return VFS_FALSE; } strDir = strDir.substr( 0, strDir.rfind( VFS_PATH_SEPARATOR ) ); } if( find( Dirs.begin(), Dirs.end(), strDir ) == Dirs.end() ) { Dirs.push_back( ToLower( strDir ) ); if( strDir.size() > VFS_MAX_NAME_LENGTH ) { SetLastError( VFS_ERROR_INVALID_PARAMETER ); return VFS_FALSE; } } } } // (Re)create the Target File. VFS_Handle hFile = VFS_File_Create( Info.strPath + VFS_TEXT( "." ) + VFS_ARCHIVE_FILE_EXTENSION, VFS_READ | VFS_WRITE ); if( hFile == VFS_INVALID_HANDLE_VALUE ) return VFS_FALSE; // Write the Header. ARCHIVE_HEADER Header; memcpy( Header.ID, ARCHIVE_ID, sizeof( ARCHIVE_ID ) ); Header.wVersion = VFS_VERSION; Header.dwNumFilters = ( VFS_DWORD )Filters.size(); Header.dwNumDirs = ( VFS_DWORD )Dirs.size(); Header.dwNumFiles = ( VFS_DWORD )Files.size(); VFS_DWORD dwWritten; if( !VFS_File_Write( hFile, ( const VFS_BYTE* ) &Header, sizeof( ARCHIVE_HEADER ), &dwWritten ) ) { VFS_File_Close( hFile ); return VFS_FALSE; } // Write the Filters. for( VFS_FilterList::iterator iter4 = Filters.begin(); iter4 != Filters.end(); iter4++ ) { ARCHIVE_FILTER Filter; strcpy( Filter.szName, ToLower( ( *iter4 )->GetName() ).c_str() ); if( !VFS_File_Write( hFile, ( const VFS_BYTE* ) &Filter, sizeof( ARCHIVE_FILTER ) ) ) { VFS_File_Close( hFile ); return VFS_FALSE; } } // Write the Directories. for( NameMap::iterator iter5 = Dirs.begin(); iter5 != Dirs.end(); iter5++ ) { ARCHIVE_DIR Dir; // Get the Name of the Dir and add it. VFS_String strName; VFS_Util_GetName( *iter5, strName ); strcpy( Dir.szName, ToLower( strName ).c_str() ); // Remove the <pathsep> and the Name from the path; the rest should be the Parent Directory. if( ( *iter5 ).find( VFS_PATH_SEPARATOR ) != VFS_String::npos ) { // Get the Name of the Parent Directory. VFS_String strParentDir = ( *iter5 ).substr( 0, ( *iter5 ).rfind( VFS_PATH_SEPARATOR ) ); // Get the Index of the Parent Directory. assert( find( Dirs.begin(), Dirs.end(), ToLower( strParentDir ) ) != Dirs.end() ); Dir.dwParentIndex = ( VFS_DWORD )( find( Dirs.begin(), Dirs.end(), ToLower( strParentDir ) ) - Dirs.begin() ); } else Dir.dwParentIndex = DIR_INDEX_ROOT; if( !VFS_File_Write( hFile, ( const VFS_BYTE* ) &Dir, sizeof( ARCHIVE_DIR ) ) ) { VFS_File_Close( hFile ); return VFS_FALSE; } } // Get the starting offset for the file data. VFS_DWORD dwOffset = sizeof( ARCHIVE_HEADER ) + Header.dwNumFilters * sizeof( ARCHIVE_FILTER ) + Header.dwNumDirs * sizeof( ARCHIVE_DIR ) + Header.dwNumFiles * sizeof( ARCHIVE_FILE ); // Let the Filters store the configuration Data. for( VFS_FilterList::iterator iter6 = Filters.begin(); iter6 != Filters.end(); iter6++ ) { // Setup diverse global Variables. g_ToBuffer.clear(); // Call the Saver Proc. if( !( *iter6 )->SaveConfigData( Writer ) ) { VFS_File_Close( hFile ); return VFS_FALSE; } // Save it. VFS_DWORD dwPos = VFS_File_Tell( hFile ); VFS_File_Seek( hFile, dwOffset, VFS_SET ); VFS_File_Write( hFile, &*g_ToBuffer.begin(), ( VFS_DWORD )g_ToBuffer.size() ); VFS_File_Seek( hFile, dwPos, VFS_SET ); dwOffset += ( VFS_DWORD )g_ToBuffer.size(); } // Write the Files. for( VFS_FileNameMap::const_iterator iter7 = Files.begin(); iter7 != Files.end(); iter7++ ) { // Prepare the record. ARCHIVE_FILE File; // Get the Name of the File and add it. VFS_String strName; VFS_Util_GetName( ( *iter7 ).second, strName ); strcpy( File.szName, ToLower( strName ).c_str() ); // Get the Parent Dir ID. if( ( *iter7 ).second.find( VFS_PATH_SEPARATOR ) != VFS_String::npos ) { // Get the Name of the Parent Directory. VFS_String strParentDir = ( *iter7 ).second.substr( 0, ( *iter7 ).second.rfind( VFS_PATH_SEPARATOR ) ); // Get the Index of the Parent Directory. assert( find( Dirs.begin(), Dirs.end(), ToLower( strParentDir ) ) != Dirs.end() ); File.dwDirIndex = ( VFS_DWORD )( find( Dirs.begin(), Dirs.end(), ToLower( strParentDir ) ) - Dirs.begin() ); } else File.dwDirIndex = DIR_INDEX_ROOT; // Open the Source File. VFS_Handle hSrc = VFS_File_Open( ( *iter7 ).first, VFS_READ ); if( hSrc == VFS_INVALID_HANDLE_VALUE ) { VFS_File_Close( hFile ); return VFS_FALSE; } // Store the uncompressed size. File.dwUncompressedSize = VFS_File_GetSize( hSrc ); // Setup diverse global Variables. g_FromBuffer.clear(); g_ToBuffer.clear(); // Read in the File. VFS_DWORD dwRead; g_FromPos = 0; do { if( !VFS_File_Read( hSrc, Chunk, FILE_COPY_CHUNK_SIZE, &dwRead ) ) { VFS_File_Close( hSrc ); VFS_File_Close( hFile ); return VFS_FALSE; } g_FromBuffer.reserve( g_FromBuffer.size() + dwRead ); for( VFS_DWORD dwIndex = 0; dwIndex < dwRead; dwIndex++ ) g_FromBuffer.push_back( Chunk[ dwIndex ] ); } while( dwRead > 0 ); // Close the File. VFS_File_Close( hSrc ); // Call the Filters. VFS_EntityInfo Info; VFS_File_GetInfo( ( *iter7 ).first, Info ); for( VFS_FilterList::iterator iter8 = Filters.begin(); iter8 != Filters.end(); iter8++ ) { g_FromPos = 0; if( !( *iter8 )->Encode( Reader, Writer, Info ) ) { VFS_ErrorCode eError = VFS_GetLastError(); if( eError == VFS_ERROR_NONE ) eError = VFS_ERROR_GENERIC; SetLastError( eError ); VFS_File_Close( hFile ); return VFS_FALSE; } g_FromBuffer = g_ToBuffer; g_ToBuffer.clear(); } // Store the final Result. VFS_DWORD dwPos = VFS_File_Tell( hFile ); VFS_File_Seek( hFile, dwOffset, VFS_SET ); VFS_File_Write( hFile, &*g_FromBuffer.begin(), ( VFS_DWORD )g_FromBuffer.size() ); File.dwCompressedSize = ( VFS_DWORD )g_FromBuffer.size(); VFS_File_Seek( hFile, dwPos, VFS_SET ); dwOffset += File.dwCompressedSize; if( !VFS_File_Write( hFile, ( const VFS_BYTE* ) &File, sizeof( ARCHIVE_FILE ) ) ) { VFS_File_Close( hFile ); return VFS_FALSE; } } // Close the File. if( !VFS_File_Close( hFile ) ) return VFS_FALSE; return VFS_TRUE; }