diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/xbox/xbox_loader/xbox_fileCopy.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'utils/xbox/xbox_loader/xbox_fileCopy.cpp')
| -rw-r--r-- | utils/xbox/xbox_loader/xbox_fileCopy.cpp | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/utils/xbox/xbox_loader/xbox_fileCopy.cpp b/utils/xbox/xbox_loader/xbox_fileCopy.cpp new file mode 100644 index 0000000..39d0c65 --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_fileCopy.cpp @@ -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; +} |