/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Installer * * * * $Archive:: /Commando/Code/Installer/CopyThread.cpp $* * * * $Author:: Ian_l $* * * * $Modtime:: 1/13/02 5:00p $* * * * $Revision:: 10 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ // Includes. #include "CopyThread.h" #include "ErrorHandler.h" #include "FDI.h" #include "Installer.h" #include "RegistryManager.h" #include "Resource.h" #include "Translator.h" #include "Verchk.h" #include #include #include #include // Defines. #define COPY_MESSAGE_FORMAT_STRING L"%s %s" // Static data. CopyThreadClass *CopyThreadClass::_ActiveCopyThread = NULL; /*************************************************************************** * Memory allocation function * HUGE * FAR DIAMONDAPI fdi_mem_alloc( ULONG cb ) *=========================================================================*/ FNALLOC( fdi_mem_alloc ) { return (malloc (cb)); } /*************************************************************************** * Memory free function * FAR DIAMONDAPI fdi_mem_free( void HUGE *pv ) *=========================================================================*/ FNFREE( fdi_mem_free ) { free (pv); } /*************************************************************************** * int FAR DIAMONDAPI file_open( char FAR *pszFile, int oflag, int pmode ) *=========================================================================*/ FNOPEN( file_open ) { HANDLE handle; WWASSERT ((oflag & (_O_APPEND | _O_TEMPORARY)) == 0x0); DWORD desiredaccess, creationdisposition, flagsandattributes; if (oflag & _O_WRONLY) { desiredaccess = GENERIC_WRITE; } else { if (oflag & _O_RDWR) { desiredaccess = GENERIC_READ | GENERIC_WRITE; } else { desiredaccess = GENERIC_READ; } } if (oflag & (_O_CREAT | _O_TRUNC)) { creationdisposition = OPEN_ALWAYS; } else { if (oflag & _O_CREAT) { creationdisposition = CREATE_NEW; } else { if (oflag & _O_TRUNC) { creationdisposition = TRUNCATE_EXISTING; } else { creationdisposition = OPEN_EXISTING; } } } if (pmode & _S_IREAD) { flagsandattributes = FILE_ATTRIBUTE_READONLY; } else { flagsandattributes = FILE_ATTRIBUTE_NORMAL; } while ((handle = CreateFile (pszFile, desiredaccess, FILE_SHARE_READ, NULL, creationdisposition, flagsandattributes | FILE_FLAG_WRITE_THROUGH, NULL)) == INVALID_HANDLE_VALUE) { if (!CopyThreadClass::_ActiveCopyThread->Retry()) break; } return ((int) handle); } /*************************************************************************** * UINT FAR DIAMONDAPI file_read( int hf, void FAR *pv, UINT cb ) *=========================================================================*/ FNREAD( file_read ) { // Abort the copying process? if (!CopyThreadClass::_ActiveCopyThread->Get_Abort (true)) { unsigned long bytecount; while (!ReadFile ((void*) hf, pv, cb, &bytecount, NULL)) { if (!CopyThreadClass::_ActiveCopyThread->Retry()) break; } return (bytecount); } else { // NOTE: Returning -1 will cause the calling cabinet process to close all // files, release its resources, and return an error code to the caller of // FDICopy(). return (-1); } } /*************************************************************************** * UINT FAR DIAMONDAPI file_write( int hf, void FAR *pv, UINT cb ) *=========================================================================*/ FNWRITE( file_write ) { // Abort the copying process? if (!CopyThreadClass::_ActiveCopyThread->Get_Abort (true)) { unsigned long bytecount; while (!WriteFile ((void*) hf, pv, cb, &bytecount, NULL)) { if (!CopyThreadClass::_ActiveCopyThread->Retry()) break; } if (bytecount >= 0) { CopyThreadClass::_ActiveCopyThread->Add_Bytes_Copied ((unsigned)bytecount); } return (bytecount); } else { // NOTE: Returning -1 will cause the calling cabinet process to close all // files, release its resources, and return an error code to the caller of // FDICopy(). return (-1); } } /*************************************************************************** * int FAR DIAMONDAPI file_close( int hf ) *=========================================================================*/ FNCLOSE( file_close ) { int result; while (!(result = CloseHandle ((void*) hf))) { if (!CopyThreadClass::_ActiveCopyThread->Retry()) break; } return (result ? 0 : -1); } /*************************************************************************** * long FAR DIAMONDAPI file_seek( int hf, long dist, int seektype ) *=========================================================================*/ FNSEEK( file_seek ) { long result; DWORD movemethod; // Map seek type to Win32. switch (seektype) { case SEEK_SET: movemethod = FILE_BEGIN; break; case SEEK_CUR: movemethod = FILE_CURRENT; break; case SEEK_END: movemethod = FILE_END; break; } while ((result = SetFilePointer ((void*) hf, dist, NULL, movemethod)) == 0xffffffff) { if (!CopyThreadClass::_ActiveCopyThread->Retry()) break; } return (result); } /****************************************************************************** * int FAR DIAMONDAPI notification_function( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) *=============================================================================*/ FNFDINOTIFY( notification_function ) { static WideStringClass _targetpathname; static StringClass _multibytetemporarypathname; static FILETIME _sourcefiletime; switch (fdint) { case fdintCOPY_FILE: { FILETIME localfiletime; // Build target pathname. // NOTE: If the relative path does not contain subdirectories it will not contain a backslash. CopyThreadClass::_ActiveCopyThread->Get_Target_Path (_targetpathname); if (pfdin->psz1 [0] != '\\') _targetpathname += L"\\"; _targetpathname += WideStringClass (pfdin->psz1); _Installer.Log (_targetpathname, pfdin->cb); // If target should be overwritten... if (!DosDateTimeToFileTime (pfdin->date, pfdin->time, &localfiletime)) return (-1); if (!LocalFileTimeToFileTime (&localfiletime, &_sourcefiletime)) return (-1); if (CopyThreadClass::Replace_File (_sourcefiletime, pfdin->cb, _targetpathname)) { WideStringClass statusmessage; long handle; WideStringClass targetpath; WideStringClass filename; // Update the name of the current file being copied. filename = WideStringClass (pfdin->psz1); Extract_Trailing_Name (filename); statusmessage.Format (COPY_MESSAGE_FORMAT_STRING, TxWideStringClass (IDS_COPYING), filename); CopyThreadClass::_ActiveCopyThread->Set_Status_Message (statusmessage); // Create temporary filename. targetpath = _targetpathname; Remove_Trailing_Name (targetpath); if (!Generate_Temporary_Pathname (targetpath, _multibytetemporarypathname)) return (-1); // Open the temporary file for writing. handle = file_open (_multibytetemporarypathname.Peek_Buffer(), _O_BINARY | _O_TRUNC | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, _S_IREAD | _S_IWRITE); // Add temporary filename to log. CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Add (_multibytetemporarypathname); return (handle); } else { // Do not copy file. CopyThreadClass::_ActiveCopyThread->Add_Bytes_Copied ((unsigned) pfdin->cb); return (0); } } case fdintCLOSE_FILE_INFO: { StringClass multibytetargetpathname; HANDLE targetfile; WIN32_FIND_DATA targetfiledata; int count; // Close the temporary file. if (file_close (pfdin->hf) != 0) return (-1); // Delete the original target file (if it exists). multibytetargetpathname = _targetpathname; targetfile = FindFirstFile (multibytetargetpathname, &targetfiledata); if (targetfile != INVALID_HANDLE_VALUE) { if (!FindClose (targetfile)) return (-1); if (targetfiledata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributes (multibytetargetpathname, FILE_ATTRIBUTE_NORMAL)) return (-1); } if (!DeleteFile (multibytetargetpathname)) return (-1); } // Rename the temporary file the target file. if (!MoveFile (_multibytetemporarypathname, multibytetargetpathname)) return (-1); // Remove the temporary file from the file log (the last item added). count = CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Count(); WWASSERT (count > 0); CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Delete (count - 1); // Add the target file to the file log. CopyThreadClass::_ActiveCopyThread->Get_Filename_Log().Add (multibytetargetpathname); // Stamp the target file with write time of source. targetfile = CreateFile (multibytetargetpathname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (targetfile == INVALID_HANDLE_VALUE) return (-1); if (!SetFileTime (targetfile, NULL, NULL, &_sourcefiletime)) return (-1); if (!CloseHandle (targetfile)) return (-1); // Stamp the target file with attributes of source. if (!SetFileAttributes (multibytetargetpathname, pfdin->attribs & (~FILE_ATTRIBUTE_READONLY))) return (-1); // Success. return (1); } case fdintNEXT_CABINET: { // Nothing to do. Assume that the next cabinet file is in the same directory as the current // cabinet file. return (1); } default: return (1); } } /*********************************************************************************************** * CopyThreadClass::CopyThreadClass -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ CopyThreadClass::CopyThreadClass (__int64 bytestocopy) : ThreadClass(), Status (STATUS_OK), Abort (false), CanAbort (true), AbortLock (NULL), IsAborting (false), BytesToCopy (bytestocopy), BytesCopied (0) { // Only one instance can be active. WWASSERT (_ActiveCopyThread == NULL); _ActiveCopyThread = this; } /*********************************************************************************************** * CopyThreadClass::~CopyThreadClass -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ CopyThreadClass::~CopyThreadClass() { WWASSERT (_ActiveCopyThread != NULL); _ActiveCopyThread = NULL; } /*********************************************************************************************** * CopyThreadClass::Thread_Function -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Thread_Function() { try { unsigned i; WideStringClass sourcepath, targetpath; int gamesubdirectorycount, gamefilecount; // Copy the game directory if user requested it. if (_Installer.Install_Game()) { // Create game subdirectories on target. // NOTE 0: This could have been done whilst copying, but is more efficient to do here, // considering that InstallerClass already has this information. // NOTE 1: Directory paths should have been validated, so any errors here are fatal. if (!Create_Directory (_Installer.Get_Target_Game_Path (targetpath), &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR; i = 0; while (_Installer.Get_Target_Sub_Path (i, targetpath)) { _Installer.Log (targetpath); if (!Create_Directory (targetpath, &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR; i++; } Copy_Directory (_Installer.Get_Source_Game_Path (sourcepath), _Installer.Get_Target_Game_Path (targetpath)); } gamesubdirectorycount = Get_Subdirectory_Log().Count(); gamefilecount = Get_Filename_Log().Count(); if (Is_Aborting()) goto abort; // Copy the WOL directory if user requested it. if (_Installer.Install_WOL()) { Copy_Directory (_Installer.Get_Source_WOL_Path (sourcepath), _Installer.Get_Target_WOL_Path (targetpath)); } // Find out if the user wants to abort. At the same time disable the user's ability to abort // because the operations to follow are irreversible ie. this is the last chance for the // user to abort. if (Get_Abort (false)) goto abort; // Update registry and make start menu items. Set_Status_Message (TxWideStringClass (IDS_UPDATING_REGISTRY)); _Installer.Update_Registry(); // Make menu items and icons. Set_Status_Message (TxWideStringClass (IDS_MAKING_MENU_ITEMS)); _Installer.Create_Links(); // Create uninstall logs. Set_Status_Message (TxWideStringClass (IDS_CREATING_UNINSTALL_LOGS)); _Installer.Create_Uninstall_Logs(); // Indicate completion. Set_Status_Message (TxWideStringClass (IDS_INSTALLATION_COMPLETE)); Status = STATUS_SUCCESS; return; abort: Set_Status_Message (WideStringClass (L"")); Status = STATUS_ABORTED; // Remove installed components if game or WOL was freshly installed. if (_Installer.Is_Fresh_Game_Install() || _Installer.Is_Fresh_WOL_Install()) { int i; Set_Status_Message (TxWideStringClass (IDS_REMOVING_INSTALLED_COMPONENTS)); // NOTE: Order of removal must be inverse of order of creation to ensure that leaf // subdirectories are removed before parent directories. To satisfy this // requirement, remove in the following order: // 1. WOL files (in any order). // 2. Game files (in any order). // 3. WOL subdirectories (in reverse order). // 4. Game subdirectories (in reverse order). if (_Installer.Is_Fresh_WOL_Install()) { for (i = gamefilecount; i < Get_Filename_Log().Count(); i++) { DeleteFile (Get_Filename_Log() [i]); } } if (_Installer.Is_Fresh_Game_Install()) { for (i = 0; i < gamefilecount; i++) { DeleteFile (Get_Filename_Log() [i]); } } if (_Installer.Is_Fresh_WOL_Install()) { for (i = Get_Subdirectory_Log().Count() - 1; i >= gamesubdirectorycount; i--) { RemoveDirectory (Get_Subdirectory_Log() [i]); } } if (_Installer.Is_Fresh_Game_Install()) { for (i = gamesubdirectorycount - 1; i >= 0; i--) { RemoveDirectory (Get_Subdirectory_Log() [i]); } } // Give time for user to read status message. Sleep (5000); } } catch (const WideStringClass &errormessage) { // Copy off the errormessage so that it can be interrogated by the thread creator. Set_Status_Message (WideStringClass (L"")); Set_Error_Message (errormessage); Status = STATUS_FAILURE; } } /*********************************************************************************************** * CopyThreadClass::Copy_Directory -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Copy_Directory (const WideStringClass &sourcepath, const WideStringClass &targetpath) { // WARNING: Do not call SetCurrentDirectory() from this thread because it will affect // file access for the entire process. const WCHAR *wildcardname = L"*.*"; HFDI hfdi; ERF erf; WideStringClass sourcepathname; StringClass multibytesourcepathname; WIN32_FIND_DATA finddata; HANDLE handle; bool done = false; hfdi = FDICreate (fdi_mem_alloc, // Memory allocation function. fdi_mem_free, // Memory free function. file_open, // File open function. file_read, // File read function. file_write, // File write function. file_close, // File close function. file_seek, // File seek function. cpu80386, // Type of CPU. &erf); // Pointer to error structure. WWASSERT (hfdi != NULL); // Create subdirectory (if it doesn't already exist). if (!Create_Directory (targetpath, &Get_Subdirectory_Log())) FATAL_SYSTEM_ERROR; // Iterate over files in source directory and copy to target directory. sourcepathname = sourcepath; sourcepathname += L"\\"; sourcepathname += wildcardname; multibytesourcepathname = sourcepathname; // Find a source file. // NOTE At least one file must be in the directory. while ((handle = FindFirstFile (multibytesourcepathname, &finddata)) == INVALID_HANDLE_VALUE) { if (!Retry()) FATAL_SYSTEM_ERROR; } while (!done && !Is_Aborting()) { WideStringClass filename (finddata.cFileName); // Filter out system files. if (filename [0] != L'.') { // Is it a subdirectory? if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { WideStringClass subsourcepath (sourcepath); WideStringClass subtargetpath (targetpath); // Recurse. subsourcepath += L"\\"; subsourcepath += filename; subtargetpath += L"\\"; subtargetpath += filename; _Installer.Log (subtargetpath); Copy_Directory (subsourcepath, subtargetpath); } else { const WCHAR *cabextension = L".cab"; WCHAR extension [_MAX_EXT]; // Is it a CAB file? _wsplitpath (filename, NULL, NULL, NULL, extension); if (_wcsicmp (cabextension, extension) == 0) { StringClass sourcepathbackslash (sourcepath); // NOTE: FDICopy() requires a trailing backslash on the source path. sourcepathbackslash += "\\"; Set_Target_Path (targetpath); // Copy the contents of the cabinet file to the target directory. // If the function fails then throw a 'cabinet error'. if (!FDICopy (hfdi, // Handle to FDI context (created by FDICreate()). finddata.cFileName, // Name of cabinet file, excluding path information. sourcepathbackslash.Peek_Buffer(), // File path to cabinet file. 0, // Flags to control extract operation. notification_function, // Ptr to a notification (status update) function. NULL, // Ptr to a decryption function. NULL)) // If failure was not due to user aborting then throw an error. if (!Is_Aborting()) FATAL_CAB_ERROR (erf.erfOper); } else { // Update the name of the current file being copied. WideStringClass statusmessage; __int64 filesize; statusmessage.Format (COPY_MESSAGE_FORMAT_STRING, TxWideStringClass (IDS_COPYING), filename); Set_Status_Message (statusmessage); // It's a regular file. WideStringClass sourcepathname (sourcepath); WideStringClass targetpathname (targetpath); sourcepathname += L"\\"; sourcepathname += filename; targetpathname += L"\\"; targetpathname += filename; filesize = ((((__int64)MAXDWORD) + 1) * ((__int64) finddata.nFileSizeHigh)) + ((__int64) finddata.nFileSizeLow); _Installer.Log (targetpathname, filesize); // If target should be overwitten... if (Replace_File (sourcepathname, targetpathname)) { Copy_File (sourcepathname, targetpathname); } else { BytesCopied += filesize; } } } } if (Is_Aborting()) break; while (done = (FindNextFile (handle, &finddata) == 0)) { if (GetLastError() == ERROR_NO_MORE_FILES) break; if (!Retry()) FATAL_SYSTEM_ERROR; } } while (!FindClose (handle)) { if (!Retry()) FATAL_SYSTEM_ERROR; } FDIDestroy (hfdi); } /*********************************************************************************************** * CopyThreadClass::Copy_File -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Copy_File (const WideStringClass &sourcepathname, const WideStringClass &targetpathname) { const unsigned buffersize = 32768; static char _buffer [buffersize]; StringClass multibytesourcepathname, multibytetargetpathname; WideStringClass targetpath; StringClass multibytetargetpath, multibytetemporarypathname; HANDLE sourcefile, temporaryfile, targetfile; DWORD sourcebytecount, bytecount; WIN32_FIND_DATA sourcefiledata, targetfiledata; multibytesourcepathname = sourcepathname; multibytetargetpathname = targetpathname; // Open the read file. while ((sourcefile = CreateFile (multibytesourcepathname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { if (!Retry()) FATAL_SYSTEM_ERROR; } // Create temporary file. targetpath = targetpathname; Remove_Trailing_Name (targetpath); if (!Generate_Temporary_Pathname (targetpath, multibytetemporarypathname)) FATAL_SYSTEM_ERROR; // Open the temporary file for writing. temporaryfile = CreateFile (multibytetemporarypathname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); if (temporaryfile == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR; do { // Abort the copying process? if (Get_Abort (true)) break; while (!ReadFile (sourcefile, _buffer, buffersize, &sourcebytecount, NULL)) { if (!Retry()) FATAL_SYSTEM_ERROR; } if (!WriteFile (temporaryfile, _buffer, sourcebytecount, &bytecount, NULL)) FATAL_SYSTEM_ERROR; BytesCopied += sourcebytecount; } while (sourcebytecount == buffersize); // Close. while (!CloseHandle (sourcefile)) { if (!Retry()) FATAL_SYSTEM_ERROR; } while (!CloseHandle (temporaryfile)) { if (!Retry()) FATAL_SYSTEM_ERROR; } while ((sourcefile = FindFirstFile (multibytesourcepathname, &sourcefiledata)) == INVALID_HANDLE_VALUE) { if (!Retry()) FATAL_SYSTEM_ERROR; } while (!FindClose (sourcefile)) { if (!Retry()) FATAL_SYSTEM_ERROR; } // Delete the original target file (if it exists). targetfile = FindFirstFile (multibytetargetpathname, &targetfiledata); if (targetfile != INVALID_HANDLE_VALUE) { if (!FindClose (targetfile)) FATAL_SYSTEM_ERROR; if (targetfiledata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { if (!SetFileAttributes (multibytetargetpathname, FILE_ATTRIBUTE_NORMAL)) FATAL_SYSTEM_ERROR; } if (!DeleteFile (multibytetargetpathname)) FATAL_SYSTEM_ERROR; } // Rename the temporary file the original target file. if (!MoveFile (multibytetemporarypathname, multibytetargetpathname)) FATAL_SYSTEM_ERROR; // Add the name of the file to the file log. Get_Filename_Log().Add (multibytetargetpathname); // Stamp the target file with write time of source. targetfile = CreateFile (multibytetargetpathname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (targetfile == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR; if (!SetFileTime (targetfile, NULL, NULL, &sourcefiledata.ftLastWriteTime)) FATAL_SYSTEM_ERROR; if (!CloseHandle (targetfile)) FATAL_SYSTEM_ERROR; // Stamp the target file with file attributes of source (but clear the read-only flag to ensure that // the file can be patched at a later date). if (!SetFileAttributes (multibytetargetpathname, sourcefiledata.dwFileAttributes & (~FILE_ATTRIBUTE_READONLY))) FATAL_SYSTEM_ERROR; } /*********************************************************************************************** * Replace_File -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool CopyThreadClass::Replace_File (const FILETIME &sourcefiletime, DWORD sourcefilesize, const WideStringClass &targetpathname) { StringClass multibytetargetpathname (targetpathname); WIN32_FIND_DATA targetdata; HANDLE handle; long t; // Does the target file exist? handle = FindFirstFile (multibytetargetpathname, &targetdata); if (handle == INVALID_HANDLE_VALUE) return (true); // Test last write times. t = CompareFileTime (&sourcefiletime, &targetdata.ftLastWriteTime); if (t > 0) return (true); if (t < 0) return (false); // So far files are deemed equal. To err on the side of caution, test file sizes and elect to replace the file if sizes differ. return ((sourcefilesize != targetdata.nFileSizeLow) || (targetdata.nFileSizeHigh != 0)); } /*********************************************************************************************** * Replace_File -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool CopyThreadClass::Replace_File (const WideStringClass &sourcepathname, const WideStringClass &targetpathname) { StringClass multibytesourcepathname (sourcepathname); StringClass multibytetargetpathname (targetpathname); WIN32_FIND_DATA sourcedata, targetdata; HANDLE handle; VS_FIXEDFILEINFO sourceversion, targetversion; long t; // Find source file. It must exist. while ((handle = FindFirstFile (multibytesourcepathname, &sourcedata)) == INVALID_HANDLE_VALUE) { if (!_ActiveCopyThread->Retry()) FATAL_SYSTEM_ERROR; } // Does the target file exist? handle = FindFirstFile (multibytetargetpathname, &targetdata); if (handle == INVALID_HANDLE_VALUE) return (true); // Test version numbers (if they exist). if (GetVersionInfo (multibytesourcepathname.Peek_Buffer(), &sourceversion) && GetVersionInfo (multibytetargetpathname.Peek_Buffer(), &targetversion)) { if (sourceversion.dwFileVersionMS > targetversion.dwFileVersionMS) return (true); if (sourceversion.dwFileVersionMS < targetversion.dwFileVersionMS) return (false); if (sourceversion.dwFileVersionLS > targetversion.dwFileVersionLS) return (true); if (sourceversion.dwFileVersionLS < targetversion.dwFileVersionLS) return (false); } // Test last write times. t = CompareFileTime (&sourcedata.ftLastWriteTime, &targetdata.ftLastWriteTime); if (t > 0) return (true); if (t < 0) return (false); // So far files are deemed equal. To err on the side of caution, test file sizes and elect to replace the file if sizes differ. return ((sourcedata.nFileSizeLow != targetdata.nFileSizeLow) || (sourcedata.nFileSizeHigh != targetdata.nFileSizeHigh)); } /*********************************************************************************************** * CopyThreadClass::Can_Abort -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool CopyThreadClass::Can_Abort (bool lock) { if (lock) { if (AbortLock == NULL) { AbortLock = new FastCriticalSectionClass::LockClass (SectionAbort); } return (CanAbort); } else { FastCriticalSectionClass::LockClass cs (SectionAbort); return (CanAbort); } } /*********************************************************************************************** * CopyThreadClass::Set_Abort -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Set_Abort (bool abort) { Abort = abort; WWASSERT (AbortLock != NULL); delete AbortLock; AbortLock = NULL; } /*********************************************************************************************** * CopyThreadClass::Get_Abort -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool CopyThreadClass::Get_Abort (bool canabort) { FastCriticalSectionClass::LockClass cs (SectionAbort); CanAbort = canabort; if (Abort) IsAborting = true; return (Abort); } /*********************************************************************************************** * CopyThreadClass::Add_Bytes_Copied -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Add_Bytes_Copied (unsigned bytecount) { FastCriticalSectionClass::LockClass cs (SectionBytesCopied); BytesCopied += bytecount; } /*********************************************************************************************** * CopyThreadClass::Get_Fraction_Complete -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ float CopyThreadClass::Get_Fraction_Complete() { FastCriticalSectionClass::LockClass cs (SectionBytesCopied); float fraction; fraction = ((double) BytesCopied) / ((double) BytesToCopy); // Clamp to valid range. fraction = MAX (0.0f, MIN (1.0f, fraction)); return (fraction); } /*********************************************************************************************** * CopyThreadClass::Set\Get_Target_Path -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Set_Target_Path (const WideStringClass &targetpath) { FastCriticalSectionClass::LockClass cs (SectionTargetPath); TargetPath = targetpath; } WCHAR *CopyThreadClass::Get_Target_Path (WideStringClass &targetpath) { FastCriticalSectionClass::LockClass cs (SectionTargetPath); targetpath = TargetPath; return (targetpath.Peek_Buffer()); } /*********************************************************************************************** * CopyThreadClass::Set\Get_Status_Message -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Set_Status_Message (const WideStringClass &statusmessage) { FastCriticalSectionClass::LockClass cs (SectionStatusMessage); StatusMessage = statusmessage; } WCHAR *CopyThreadClass::Get_Status_Message (WideStringClass &statusmessage) { FastCriticalSectionClass::LockClass cs (SectionStatusMessage); statusmessage = StatusMessage; return (statusmessage.Peek_Buffer()); } /*********************************************************************************************** * CopyThreadClass::Set\Get_Error_Message -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Set_Error_Message (const WideStringClass &errormessage) { FastCriticalSectionClass::LockClass cs (SectionErrorMessage); ErrorMessage = errormessage; } WCHAR *CopyThreadClass::Get_Error_Message (WideStringClass &errormessage) { FastCriticalSectionClass::LockClass cs (SectionErrorMessage); errormessage = ErrorMessage; return (errormessage.Peek_Buffer()); } /*********************************************************************************************** * CopyThreadClass::Set\Get_Status -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ void CopyThreadClass::Set_Status (StatusEnum status) { FastCriticalSectionClass::LockClass cs (SectionStatus); Status = status; } CopyThreadClass::StatusEnum CopyThreadClass::Get_Status() { FastCriticalSectionClass::LockClass cs (SectionStatus); return (Status); } /*********************************************************************************************** * CopyThreadClass::Retry -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/22/01 IML : Created. * *=============================================================================================*/ bool CopyThreadClass::Retry() { CopyThreadClass::StatusEnum status; Set_Status (STATUS_ERROR); while ((status = Get_Status()) == STATUS_ERROR) { Sleep (50); } return (status == STATUS_RETRY); }