This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Installer/CopyThread.cpp

1106 lines
47 KiB
C++
Raw Permalink Normal View History

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <share.h>
// 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);
}