You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
14 KiB
595 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Copies a file using overlapped async IO. |
|
// |
|
// Stub executeable |
|
//=====================================================================================// |
|
#include "xbox_loader.h" |
|
|
|
#define BUFFER_SIZE (1*1024*1024) |
|
#define NUM_BUFFERS 4 |
|
#define ALIGN(x,y) (((x)+(y)-1) & ~((y)-1)) |
|
|
|
struct CopyFile_t |
|
{ |
|
// source file |
|
HANDLE m_hSrcFile; |
|
DWORD m_srcFileSize; |
|
int m_readBufferSize; |
|
unsigned int m_numReadCycles; |
|
|
|
// target file |
|
HANDLE m_hDstFile; |
|
DWORD m_dstFileSize; |
|
|
|
// source file gets decompressed |
|
bool m_bInflate; |
|
unsigned char *m_pInflateBuffer; |
|
int m_inflateBufferSize; |
|
|
|
bool m_bCopyError; |
|
CopyStats_t *m_pCopyStats; |
|
}; |
|
|
|
struct Buffer_t |
|
{ |
|
unsigned char *pData; |
|
DWORD dwSize; |
|
Buffer_t* pNext; |
|
int id; |
|
}; |
|
|
|
Buffer_t *g_pReadBuffers = NULL; |
|
Buffer_t *g_pWriteBuffers = NULL; |
|
|
|
CRITICAL_SECTION g_criticalSection; |
|
HANDLE g_hReadEvent; |
|
HANDLE g_hWriteEvent; |
|
DWORD *g_pNumReadBuffers; |
|
DWORD *g_pNumWriteBuffers; |
|
|
|
//----------------------------------------------------------------------------- |
|
// CreateFilePath |
|
// |
|
// Create full path to specified file. |
|
//----------------------------------------------------------------------------- |
|
bool CreateFilePath( const char *inPath ) |
|
{ |
|
char* ptr; |
|
char dirPath[MAX_PATH]; |
|
BOOL bSuccess; |
|
|
|
// prime and skip to first seperator after the drive path |
|
strcpy( dirPath, inPath ); |
|
ptr = strchr( dirPath, '\\' ); |
|
while ( ptr ) |
|
{ |
|
ptr = strchr( ptr+1, '\\' ); |
|
if ( ptr ) |
|
{ |
|
*ptr = '\0'; |
|
bSuccess = ::CreateDirectory( dirPath, NULL ); |
|
*ptr = '\\'; |
|
} |
|
} |
|
|
|
// ensure read-only is cleared |
|
SetFileAttributes( inPath, FILE_ATTRIBUTE_NORMAL ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// LockBufferForRead |
|
// |
|
//----------------------------------------------------------------------------- |
|
Buffer_t *LockBufferForRead() |
|
{ |
|
if ( !g_pReadBuffers ) |
|
{ |
|
// out of data, wait for it |
|
WaitForSingleObject( g_hReadEvent, INFINITE ); |
|
} |
|
else |
|
{ |
|
ResetEvent( g_hReadEvent ); |
|
} |
|
|
|
EnterCriticalSection( &g_criticalSection ); |
|
|
|
Buffer_t *pBuffer = g_pReadBuffers; |
|
g_pReadBuffers = pBuffer->pNext; |
|
|
|
(*g_pNumReadBuffers)--; |
|
|
|
LeaveCriticalSection( &g_criticalSection ); |
|
|
|
return pBuffer; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// LockBufferForWrite |
|
// |
|
//----------------------------------------------------------------------------- |
|
Buffer_t* LockBufferForWrite() |
|
{ |
|
if ( !g_pWriteBuffers ) |
|
{ |
|
// out of data, wait for more |
|
WaitForSingleObject( g_hWriteEvent, INFINITE ); |
|
} |
|
else |
|
{ |
|
ResetEvent( g_hWriteEvent ); |
|
} |
|
|
|
EnterCriticalSection( &g_criticalSection ); |
|
|
|
Buffer_t *pBuffer = g_pWriteBuffers; |
|
g_pWriteBuffers = pBuffer->pNext; |
|
|
|
(*g_pNumWriteBuffers)--; |
|
|
|
LeaveCriticalSection( &g_criticalSection ); |
|
|
|
return pBuffer; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// AddBufferForRead |
|
// |
|
//----------------------------------------------------------------------------- |
|
void AddBufferForRead( Buffer_t *pBuffer ) |
|
{ |
|
EnterCriticalSection( &g_criticalSection ); |
|
|
|
// add to end of list |
|
Buffer_t *pCurrent = g_pReadBuffers; |
|
while ( pCurrent && pCurrent->pNext ) |
|
{ |
|
pCurrent = pCurrent->pNext; |
|
} |
|
if ( pCurrent ) |
|
{ |
|
pBuffer->pNext = pCurrent->pNext; |
|
pCurrent->pNext = pBuffer; |
|
} |
|
else |
|
{ |
|
pBuffer->pNext = NULL; |
|
g_pReadBuffers = pBuffer; |
|
} |
|
|
|
(*g_pNumReadBuffers)++; |
|
|
|
LeaveCriticalSection( &g_criticalSection ); |
|
|
|
SetEvent( g_hReadEvent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// AddBufferForWrite |
|
// |
|
//----------------------------------------------------------------------------- |
|
void AddBufferForWrite( Buffer_t *pBuffer ) |
|
{ |
|
EnterCriticalSection( &g_criticalSection ); |
|
|
|
// add to end of list |
|
Buffer_t* pCurrent = g_pWriteBuffers; |
|
while ( pCurrent && pCurrent->pNext ) |
|
{ |
|
pCurrent = pCurrent->pNext; |
|
} |
|
if ( pCurrent ) |
|
{ |
|
pBuffer->pNext = pCurrent->pNext; |
|
pCurrent->pNext = pBuffer; |
|
} |
|
else |
|
{ |
|
pBuffer->pNext = NULL; |
|
g_pWriteBuffers = pBuffer; |
|
} |
|
|
|
(*g_pNumWriteBuffers)++; |
|
|
|
LeaveCriticalSection( &g_criticalSection ); |
|
|
|
SetEvent( g_hWriteEvent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// ReadFileThread |
|
// |
|
//----------------------------------------------------------------------------- |
|
DWORD WINAPI ReadFileThread( LPVOID lParam ) |
|
{ |
|
CopyFile_t *pCopyFile; |
|
OVERLAPPED overlappedRead = {0}; |
|
DWORD startTime; |
|
DWORD dwBytesRead; |
|
DWORD dwError; |
|
BOOL bResult; |
|
Buffer_t *pBuffer; |
|
|
|
pCopyFile = (CopyFile_t*)lParam; |
|
|
|
// Copy from the buffer to the Hard Drive |
|
for ( unsigned int readCycle = 0; readCycle < pCopyFile->m_numReadCycles; ++readCycle ) |
|
{ |
|
pBuffer = LockBufferForRead(); |
|
|
|
startTime = GetTickCount(); |
|
dwBytesRead = 0; |
|
|
|
int numAttempts = 0; |
|
retry: |
|
// read file from DVD |
|
bResult = ReadFile( pCopyFile->m_hSrcFile, pBuffer->pData, pCopyFile->m_readBufferSize, NULL, &overlappedRead ); |
|
dwError = GetLastError(); |
|
if ( !bResult && dwError != ERROR_IO_PENDING ) |
|
{ |
|
if ( dwError == ERROR_HANDLE_EOF ) |
|
{ |
|
// nothing more to read |
|
break; |
|
} |
|
|
|
numAttempts++; |
|
if ( numAttempts == 3 ) |
|
{ |
|
// error |
|
pCopyFile->m_bCopyError = true; |
|
break; |
|
} |
|
else |
|
{ |
|
goto retry; |
|
} |
|
} |
|
else |
|
{ |
|
// Wait for the operation to finish |
|
GetOverlappedResult( pCopyFile->m_hSrcFile, &overlappedRead, &dwBytesRead, TRUE ); |
|
overlappedRead.Offset += dwBytesRead; |
|
} |
|
|
|
if ( !dwBytesRead ) |
|
{ |
|
pCopyFile->m_bCopyError = true; |
|
break; |
|
} |
|
|
|
pCopyFile->m_pCopyStats->m_bufferReadSize = dwBytesRead; |
|
pCopyFile->m_pCopyStats->m_bufferReadTime = GetTickCount() - startTime; |
|
pCopyFile->m_pCopyStats->m_totalReadSize += pCopyFile->m_pCopyStats->m_bufferReadSize; |
|
pCopyFile->m_pCopyStats->m_totalReadTime += pCopyFile->m_pCopyStats->m_bufferReadTime; |
|
|
|
pBuffer->dwSize = dwBytesRead; |
|
AddBufferForWrite( pBuffer ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// WriteFileThread |
|
// |
|
//----------------------------------------------------------------------------- |
|
DWORD WINAPI WriteFileThread( LPVOID lParam ) |
|
{ |
|
CopyFile_t *pCopyFile; |
|
OVERLAPPED overlappedWrite = {0}; |
|
DWORD startTime; |
|
DWORD dwBytesWrite; |
|
DWORD dwWriteSize; |
|
DWORD dwError; |
|
BOOL bResult; |
|
Buffer_t *pBuffer; |
|
unsigned char *pWriteBuffer; |
|
|
|
pCopyFile = (CopyFile_t*)lParam; |
|
|
|
while ( overlappedWrite.Offset < pCopyFile->m_dstFileSize ) |
|
{ |
|
// wait for wake-up event |
|
pBuffer = LockBufferForWrite(); |
|
|
|
if ( pCopyFile->m_bInflate ) |
|
{ |
|
startTime = GetTickCount(); |
|
|
|
DWORD dwSkip = overlappedWrite.Offset ? 0 : sizeof( xCompressHeader ); |
|
dwWriteSize = JCALG1_Decompress_Formatted_Buffer( pBuffer->dwSize - dwSkip, pBuffer->pData + dwSkip, pCopyFile->m_inflateBufferSize, pCopyFile->m_pInflateBuffer ); |
|
if ( dwWriteSize == (DWORD)-1 ) |
|
{ |
|
pCopyFile->m_bCopyError = true; |
|
break; |
|
} |
|
|
|
pCopyFile->m_pCopyStats->m_inflateSize = dwWriteSize; |
|
pCopyFile->m_pCopyStats->m_inflateTime = GetTickCount() - startTime; |
|
|
|
pWriteBuffer = pCopyFile->m_pInflateBuffer; |
|
} |
|
else |
|
{ |
|
// straight copy |
|
dwWriteSize = pBuffer->dwSize; |
|
pWriteBuffer = pBuffer->pData; |
|
} |
|
|
|
if ( overlappedWrite.Offset + dwWriteSize >= pCopyFile->m_dstFileSize ) |
|
{ |
|
// last buffer, ensure all data is written |
|
dwWriteSize = ALIGN( dwWriteSize, 512 ); |
|
} |
|
|
|
startTime = GetTickCount(); |
|
dwBytesWrite = 0; |
|
|
|
int numAttempts = 0; |
|
retry: |
|
// write file to HDD |
|
bResult = WriteFile( pCopyFile->m_hDstFile, pWriteBuffer, (dwWriteSize/512) * 512, NULL, &overlappedWrite ); |
|
dwError = GetLastError(); |
|
if ( !bResult && dwError != ERROR_IO_PENDING ) |
|
{ |
|
numAttempts++; |
|
if ( numAttempts == 3 ) |
|
{ |
|
// error |
|
pCopyFile->m_bCopyError = true; |
|
break; |
|
} |
|
else |
|
{ |
|
goto retry; |
|
} |
|
} |
|
else |
|
{ |
|
// Wait for the operation to finish |
|
GetOverlappedResult( pCopyFile->m_hDstFile, &overlappedWrite, &dwBytesWrite, TRUE ); |
|
overlappedWrite.Offset += dwBytesWrite; |
|
} |
|
|
|
if ( dwBytesWrite ) |
|
{ |
|
// track expected size |
|
pCopyFile->m_pCopyStats->m_bytesCopied += dwBytesWrite; |
|
pCopyFile->m_pCopyStats->m_writeSize += dwBytesWrite; |
|
} |
|
else |
|
{ |
|
pCopyFile->m_bCopyError = true; |
|
break; |
|
} |
|
|
|
pCopyFile->m_pCopyStats->m_bufferWriteSize = dwBytesWrite; |
|
pCopyFile->m_pCopyStats->m_bufferWriteTime = GetTickCount() - startTime; |
|
pCopyFile->m_pCopyStats->m_totalWriteSize += pCopyFile->m_pCopyStats->m_bufferWriteSize; |
|
pCopyFile->m_pCopyStats->m_totalWriteTime += pCopyFile->m_pCopyStats->m_bufferWriteTime; |
|
|
|
AddBufferForRead( pBuffer ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CopyFileInit |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CopyFileInit() |
|
{ |
|
static bool init = false; |
|
if ( !init ) |
|
{ |
|
InitializeCriticalSection( &g_criticalSection ); |
|
g_hReadEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
|
g_hWriteEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
|
init = true; |
|
} |
|
else |
|
{ |
|
// expected startup state |
|
ResetEvent( g_hReadEvent ); |
|
ResetEvent( g_hWriteEvent ); |
|
|
|
g_pReadBuffers = NULL; |
|
g_pWriteBuffers = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CopyFileOverlapped |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CopyFileOverlapped( const char *pSrcFilename, const char *pDstFilename, xCompressHeader *pxcHeader, CopyStats_t *pCopyStats ) |
|
{ |
|
CopyFile_t copyFile = {0}; |
|
Buffer_t buffers[NUM_BUFFERS] = {0}; |
|
HANDLE hReadThread = NULL; |
|
HANDLE hWriteThread = NULL; |
|
bool bSuccess = false; |
|
DWORD startCopyTime; |
|
DWORD dwResult; |
|
int i; |
|
|
|
startCopyTime = GetTickCount(); |
|
|
|
CopyFileInit(); |
|
|
|
g_pNumReadBuffers = &pCopyStats->m_numReadBuffers; |
|
g_pNumWriteBuffers = &pCopyStats->m_numWriteBuffers; |
|
|
|
strcpy( pCopyStats->m_srcFilename, pSrcFilename ); |
|
strcpy( pCopyStats->m_dstFilename, pDstFilename ); |
|
|
|
copyFile.m_hSrcFile = INVALID_HANDLE_VALUE; |
|
copyFile.m_hDstFile = INVALID_HANDLE_VALUE; |
|
copyFile.m_pCopyStats = pCopyStats; |
|
copyFile.m_bCopyError = false; |
|
|
|
// validate the source file |
|
copyFile.m_hSrcFile = CreateFile( pSrcFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL ); |
|
if ( copyFile.m_hSrcFile == INVALID_HANDLE_VALUE ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
|
|
copyFile.m_srcFileSize = GetFileSize( copyFile.m_hSrcFile, NULL ); |
|
if ( copyFile.m_srcFileSize == (DWORD)-1 ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
|
|
// ensure the target file path exists |
|
CreateFilePath( pDstFilename ); |
|
|
|
// validate the target file |
|
copyFile.m_hDstFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL ); |
|
if ( copyFile.m_hDstFile == INVALID_HANDLE_VALUE ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
|
|
pCopyStats->m_readSize = copyFile.m_srcFileSize; |
|
pCopyStats->m_writeSize = 0; |
|
|
|
if ( pxcHeader ) |
|
{ |
|
// read in chunks of compressed blocks |
|
copyFile.m_readBufferSize = pxcHeader->nReadBlockSize; |
|
copyFile.m_dstFileSize = pxcHeader->nUncompressedFileSize; |
|
} |
|
else |
|
{ |
|
// setup for copy |
|
copyFile.m_readBufferSize = BUFFER_SIZE; |
|
copyFile.m_dstFileSize = copyFile.m_srcFileSize; |
|
} |
|
|
|
// setup read buffers |
|
for ( i=0; i<NUM_BUFFERS; i++) |
|
{ |
|
buffers[i].pData = new unsigned char[copyFile.m_readBufferSize]; |
|
buffers[i].dwSize = 0; |
|
buffers[i].pNext = NULL; |
|
AddBufferForRead( &buffers[i] ); |
|
} |
|
copyFile.m_numReadCycles = (copyFile.m_srcFileSize + copyFile.m_readBufferSize - 1)/copyFile.m_readBufferSize; |
|
|
|
// setup write buffer |
|
if ( pxcHeader ) |
|
{ |
|
copyFile.m_pInflateBuffer = new unsigned char[pxcHeader->nDecompressionBufferSize]; |
|
copyFile.m_inflateBufferSize = pxcHeader->nDecompressionBufferSize; |
|
copyFile.m_bInflate = true; |
|
} |
|
else |
|
{ |
|
copyFile.m_bInflate = false; |
|
} |
|
|
|
// pre-size the target file in aligned buffers |
|
DWORD dwAligned = ALIGN( copyFile.m_dstFileSize, 512 ); |
|
dwResult = SetFilePointer( copyFile.m_hDstFile, dwAligned, NULL, FILE_BEGIN ); |
|
if ( dwResult == INVALID_SET_FILE_POINTER ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
SetEndOfFile( copyFile.m_hDstFile ); |
|
|
|
// start the read thread |
|
hReadThread = CreateThread( 0, 0, &ReadFileThread, ©File, 0, 0 ); |
|
if ( !hReadThread ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
|
|
// wait for buffers to populate |
|
|
|
// start the write thread |
|
hWriteThread = CreateThread( 0, 0, &WriteFileThread, ©File, 0, 0 ); |
|
if ( !hWriteThread ) |
|
{ |
|
// failure |
|
goto cleanUp; |
|
} |
|
|
|
// wait for write thread to finish |
|
WaitForSingleObject( hWriteThread, INFINITE ); |
|
WaitForSingleObject( hReadThread, INFINITE ); |
|
|
|
if ( copyFile.m_bCopyError ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
// Fixup the file size |
|
CloseHandle( copyFile.m_hDstFile ); |
|
copyFile.m_hDstFile = INVALID_HANDLE_VALUE; |
|
|
|
if ( copyFile.m_dstFileSize % 512 ) |
|
{ |
|
// re-open file as non-buffered to adjust to correct file size |
|
HANDLE hFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); |
|
SetFilePointer( hFile, copyFile.m_dstFileSize, NULL, FILE_BEGIN ); |
|
SetEndOfFile( hFile ); |
|
CloseHandle( hFile ); |
|
} |
|
|
|
// finished |
|
bSuccess = true; |
|
|
|
cleanUp: |
|
if ( copyFile.m_hSrcFile != INVALID_HANDLE_VALUE ) |
|
{ |
|
CloseHandle( copyFile.m_hSrcFile ); |
|
} |
|
|
|
if ( copyFile.m_hDstFile != INVALID_HANDLE_VALUE ) |
|
{ |
|
CloseHandle( copyFile.m_hDstFile ); |
|
} |
|
|
|
if ( hReadThread ) |
|
{ |
|
CloseHandle( hReadThread ); |
|
} |
|
|
|
if ( hWriteThread ) |
|
{ |
|
CloseHandle( hWriteThread ); |
|
} |
|
|
|
for ( i=0; i<NUM_BUFFERS; i++ ) |
|
{ |
|
if ( buffers[i].pData ) |
|
{ |
|
delete [] buffers[i].pData; |
|
} |
|
} |
|
|
|
if ( copyFile.m_pInflateBuffer ) |
|
{ |
|
delete [] copyFile.m_pInflateBuffer; |
|
} |
|
|
|
if ( !bSuccess ) |
|
{ |
|
pCopyStats->m_copyErrors++; |
|
} |
|
|
|
pCopyStats->m_copyTime = GetTickCount() - startCopyTime; |
|
|
|
return bSuccess; |
|
}
|
|
|