//========= Copyright Valve Corporation, All rights reserved. ============// // // FILEIO.CPP // // File I/O Service Routines //=====================================================================================// #include "vxconsole.h" //----------------------------------------------------------------------------- // SystemTimeToString // // mm/dd/yyyy hh:mm:ss am //----------------------------------------------------------------------------- char *SystemTimeToString( SYSTEMTIME *systemTime, char *buffer, int bufferSize ) { char timeString[256]; char dateString[256]; int length; GetDateFormat( LOCALE_SYSTEM_DEFAULT, 0, systemTime, "MM'/'dd'/'yyyy", dateString, sizeof( dateString ) ); GetTimeFormat( LOCALE_SYSTEM_DEFAULT, 0, systemTime, "hh':'mm':'ss tt", timeString, sizeof( timeString ) ); length = _snprintf( buffer, bufferSize, "%s %s", dateString, timeString ); if ( length == -1 ) buffer[bufferSize-1] = '\0'; return buffer; } //----------------------------------------------------------------------------- // CompareFileTimes_NTFStoFATX // //----------------------------------------------------------------------------- int CompareFileTimes_NTFStoFATX( FILETIME* ntfsFileTime, char *ntfsTimeString, int ntfsStringSize, FILETIME* fatxFileTime, char *fatxTimeString, int fatxStringSize ) { SYSTEMTIME ntfsSystemTime; SYSTEMTIME fatxSystemTime; int diff; int ntfsSeconds; int fatxSeconds; TIME_ZONE_INFORMATION tzInfo; GetTimeZoneInformation( &tzInfo ); // cannot compare UTC file times directly // disjoint filesystems - xbox has a +/- 2s error // daylight savings time handling on each file system may cause problems FileTimeToSystemTime( ntfsFileTime, &ntfsSystemTime ); FileTimeToSystemTime( fatxFileTime, &fatxSystemTime ); // operate on local times, assumes xbox and pc are both set for same time zone and daylight saving SYSTEMTIME ntfsLocalTime; SYSTEMTIME fatxLocalTime; SystemTimeToTzSpecificLocalTime( &tzInfo, &ntfsSystemTime, &ntfsLocalTime ); SystemTimeToTzSpecificLocalTime( &tzInfo, &fatxSystemTime, &fatxLocalTime ); if ( ntfsTimeString ) { SystemTimeToString( &ntfsLocalTime, ntfsTimeString, ntfsStringSize ); } if ( fatxTimeString ) { SystemTimeToString( &fatxLocalTime, fatxTimeString, fatxStringSize ); } diff = ntfsLocalTime.wYear-fatxLocalTime.wYear; if ( diff ) return diff; diff = ntfsLocalTime.wMonth-fatxLocalTime.wMonth; if ( diff ) return diff; diff = ntfsLocalTime.wDay-fatxLocalTime.wDay; if ( diff ) return diff; diff = ntfsLocalTime.wHour-fatxLocalTime.wHour; if ( diff ) return diff; // allow for +/- 3s error ntfsSeconds = ntfsLocalTime.wHour*60*60 + ntfsLocalTime.wMinute*60 + ntfsLocalTime.wSecond; fatxSeconds = fatxLocalTime.wHour*60*60 + fatxLocalTime.wMinute*60 + fatxLocalTime.wSecond; diff = ntfsSeconds-fatxSeconds; if ( diff > 3 || diff < -3 ) return diff; // times are considered equal return 0; } //----------------------------------------------------------------------------- // FreeTargetFileList // //----------------------------------------------------------------------------- void FreeTargetFileList( fileNode_t* pFileList ) { fileNode_t *nodePtr; fileNode_t *nextPtr; if ( !pFileList ) return; nodePtr = pFileList; while ( nodePtr ) { nextPtr = nodePtr->nextPtr; Sys_Free( nodePtr->filename ); Sys_Free( nodePtr ); nodePtr = nextPtr; } } //----------------------------------------------------------------------------- // GetTargetFileList_r // //----------------------------------------------------------------------------- bool GetTargetFileList_r( char* targetPath, bool recurse, int attributes, int level, fileNode_t** pFileList ) { HRESULT hr; PDM_WALK_DIR pWalkDir = NULL; DM_FILE_ATTRIBUTES fileAttr; bool valid; char filename[MAX_PATH]; fileNode_t* nodePtr; bool bGetNormal; int fixedAttributes; if ( !level ) *pFileList = NULL; fixedAttributes = attributes; if ( fixedAttributes & FILE_ATTRIBUTE_NORMAL ) { fixedAttributes &= ~FILE_ATTRIBUTE_NORMAL; bGetNormal = true; } else bGetNormal = false; while ( 1 ) { hr = DmWalkDir( &pWalkDir, targetPath, &fileAttr ); if ( hr != XBDM_NOERR ) break; strcpy( filename, targetPath ); Sys_AddFileSeperator( filename, sizeof( filename ) ); strcat( filename, fileAttr.Name ); // restrict to desired attributes if ( ( bGetNormal && !fileAttr.Attributes ) || ( fileAttr.Attributes & fixedAttributes ) ) { Sys_NormalizePath( filename, false ); // create a new file node nodePtr = ( fileNode_t* )Sys_Alloc( sizeof( fileNode_t ) ); // link it in nodePtr->filename = Sys_CopyString( filename ); nodePtr->changeTime = fileAttr.ChangeTime; nodePtr->creationTime = fileAttr.CreationTime; nodePtr->sizeHigh = fileAttr.SizeHigh; nodePtr->sizeLow = fileAttr.SizeLow; nodePtr->attributes = fileAttr.Attributes; nodePtr->level = level; nodePtr->nextPtr = *pFileList; *pFileList = nodePtr; } if ( fileAttr.Attributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( recurse ) { // descend into directory valid = GetTargetFileList_r( filename, recurse, attributes, level+1, pFileList ); if ( !valid ) return false; } } } DmCloseDir( pWalkDir ); if ( hr != XBDM_ENDOFLIST ) { // failure return false; } // ok return true; } //----------------------------------------------------------------------------- // FileSyncEx // // -1: failure, 0: nothing, 1: synced //----------------------------------------------------------------------------- int FileSyncEx( const char* localFilename, const char* targetFilename, int fileSyncMode, bool bVerbose, bool bNoWrite ) { bool copy; bool pathExist; WIN32_FILE_ATTRIBUTE_DATA localAttributes; DM_FILE_ATTRIBUTES targetAttributes; HRESULT hr; int errCode; int deltaTime; char localTimeString[256]; char targetTimeString[256]; if ( ( fileSyncMode & FSYNC_TYPEMASK ) == FSYNC_OFF ) { return 0; } if ( !GetFileAttributesEx( localFilename, GetFileExInfoStandard, &localAttributes ) ) { // failed to get the local file's attributes if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_RED, "Sync Failure: Local file %s not available\n", localFilename ); } return -1; } if ( localAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // ignore directory return 0; } if ( fileSyncMode & FSYNC_ANDEXISTSONTARGET ) { hr = DmGetFileAttributes( targetFilename, &targetAttributes ); if ( hr != XBDM_NOERR ) { // target doesn't exist, no sync operation should commence if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "No Update, Target file %s not available\n", targetFilename ); } return 0; } } // default success, no operation errCode = 0; // default, create path and copy copy = true; pathExist = false; if ( ( fileSyncMode & FSYNC_TYPEMASK ) == FSYNC_IFNEWER ) { hr = DmGetFileAttributes( targetFilename, &targetAttributes ); if ( hr == XBDM_NOERR ) { // target path to file exists pathExist = true; // compare times deltaTime = CompareFileTimes_NTFStoFATX( &localAttributes.ftLastWriteTime, localTimeString, sizeof( localTimeString), &targetAttributes.ChangeTime, targetTimeString, sizeof( targetTimeString ) ); if ( deltaTime < 0 ) { // ntfs is older, fatx is newer, no update if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "No Update, %s [%s] is newer than %s [%s]\n", targetFilename, targetTimeString, localFilename, localTimeString ); } copy = false; } else if ( !deltaTime ) { // equal times, compare sizes if ( localAttributes.nFileSizeLow == targetAttributes.SizeLow && localAttributes.nFileSizeHigh == targetAttributes.SizeHigh ) { // file appears synced copy = false; } if ( bVerbose ) { if ( copy ) { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Update, %s [%s] [%d] has different size than %s [%s] [%d]\n", targetFilename, targetTimeString, targetAttributes.SizeLow, localFilename, localTimeString, localAttributes.nFileSizeLow ); } else { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "No Update, %s [%s] [%d] has same time and file size as %s [%s] [%d]\n", targetFilename, targetTimeString, targetAttributes.SizeLow, localFilename, localTimeString, localAttributes.nFileSizeLow ); } } } else { // ntfs is newer, fatx is older, update if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Update, %s [%s] is older than %s [%s]\n", targetFilename, targetTimeString, localFilename, localTimeString ); } } } } else if ( ( fileSyncMode & FSYNC_TYPEMASK ) == FSYNC_ALWAYS ) { if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Force Update, %s\n", targetFilename ); } } if ( copy && !bNoWrite ) { if ( !pathExist ) { CreateTargetPath( targetFilename ); } hr = DmSendFile( localFilename, targetFilename ); if ( hr == XBDM_NOERR ) { // force the target to match the local attributes // to ensure sync memset( &targetAttributes, 0, sizeof( targetAttributes ) ); targetAttributes.SizeHigh = localAttributes.nFileSizeHigh; targetAttributes.SizeLow = localAttributes.nFileSizeLow; targetAttributes.CreationTime = localAttributes.ftCreationTime; targetAttributes.ChangeTime = localAttributes.ftLastWriteTime; DmSetFileAttributes( targetFilename, &targetAttributes ); // success, file copied errCode = 1; } else { // failure if ( bVerbose ) { ConsoleWindowPrintf( XBX_CLR_RED, "Sync Failed!\n" ); } errCode = -1; } DebugCommand( "0x%8.8x = FileSyncEx( %s, %s )\n", hr, localFilename, targetFilename ); } return errCode; } //----------------------------------------------------------------------------- // LoadTargetFile // //----------------------------------------------------------------------------- bool LoadTargetFile( const char *pTargetPath, int *pFileSize, void **pData ) { DM_FILE_ATTRIBUTES fileAttributes; HRESULT hr; DWORD bytesRead; char *pBuffer; *pFileSize = 0; *pData = (void *)NULL; hr = DmGetFileAttributes( pTargetPath, &fileAttributes ); if ( hr != XBDM_NOERR || !fileAttributes.SizeLow ) return false; // allocate for size and terminating null pBuffer = (char *)Sys_Alloc( fileAttributes.SizeLow+1 ); hr = DmReadFilePartial( pTargetPath, 0, (LPBYTE)pBuffer, fileAttributes.SizeLow, &bytesRead ); if ( hr != XBDM_NOERR || ( bytesRead != fileAttributes.SizeLow ) ) { Sys_Free( pBuffer ); return false; } // add a terminating null pBuffer[fileAttributes.SizeLow] = '\0'; *pFileSize = fileAttributes.SizeLow; *pData = pBuffer; // success return true; } //----------------------------------------------------------------------------- // CreateTargetPath // //----------------------------------------------------------------------------- bool CreateTargetPath( const char *pTargetFilename ) { // create path chain char *pPath; char dirPath[MAX_PATH]; // prime and skip to first seperator strcpy( dirPath, pTargetFilename ); pPath = strchr( dirPath, '\\' ); while ( pPath ) { pPath = strchr( pPath+1, '\\' ); if ( pPath ) { *pPath = '\0'; DmMkdir( dirPath ); *pPath = '\\'; } } return true; }