summaryrefslogtreecommitdiff
path: root/utils/xbox/xbox_loader/xbox_fileCopy.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/xbox/xbox_loader/xbox_fileCopy.cpp
downloadarchived-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.cpp595
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, &copyFile, 0, 0 );
+ if ( !hReadThread )
+ {
+ // failure
+ goto cleanUp;
+ }
+
+ // wait for buffers to populate
+
+ // start the write thread
+ hWriteThread = CreateThread( 0, 0, &WriteFileThread, &copyFile, 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;
+}