mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,595 @@
|
||||
//========= 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;
|
||||
}
|
||||
Reference in New Issue
Block a user