Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Copies a file using overlapped async IO.
  4. //
  5. // Stub executeable
  6. //=====================================================================================//
  7. #include "xbox_loader.h"
  8. #define BUFFER_SIZE (1*1024*1024)
  9. #define NUM_BUFFERS 4
  10. #define ALIGN(x,y) (((x)+(y)-1) & ~((y)-1))
  11. struct CopyFile_t
  12. {
  13. // source file
  14. HANDLE m_hSrcFile;
  15. DWORD m_srcFileSize;
  16. int m_readBufferSize;
  17. unsigned int m_numReadCycles;
  18. // target file
  19. HANDLE m_hDstFile;
  20. DWORD m_dstFileSize;
  21. // source file gets decompressed
  22. bool m_bInflate;
  23. unsigned char *m_pInflateBuffer;
  24. int m_inflateBufferSize;
  25. bool m_bCopyError;
  26. CopyStats_t *m_pCopyStats;
  27. };
  28. struct Buffer_t
  29. {
  30. unsigned char *pData;
  31. DWORD dwSize;
  32. Buffer_t* pNext;
  33. int id;
  34. };
  35. Buffer_t *g_pReadBuffers = NULL;
  36. Buffer_t *g_pWriteBuffers = NULL;
  37. CRITICAL_SECTION g_criticalSection;
  38. HANDLE g_hReadEvent;
  39. HANDLE g_hWriteEvent;
  40. DWORD *g_pNumReadBuffers;
  41. DWORD *g_pNumWriteBuffers;
  42. //-----------------------------------------------------------------------------
  43. // CreateFilePath
  44. //
  45. // Create full path to specified file.
  46. //-----------------------------------------------------------------------------
  47. bool CreateFilePath( const char *inPath )
  48. {
  49. char* ptr;
  50. char dirPath[MAX_PATH];
  51. BOOL bSuccess;
  52. // prime and skip to first seperator after the drive path
  53. strcpy( dirPath, inPath );
  54. ptr = strchr( dirPath, '\\' );
  55. while ( ptr )
  56. {
  57. ptr = strchr( ptr+1, '\\' );
  58. if ( ptr )
  59. {
  60. *ptr = '\0';
  61. bSuccess = ::CreateDirectory( dirPath, NULL );
  62. *ptr = '\\';
  63. }
  64. }
  65. // ensure read-only is cleared
  66. SetFileAttributes( inPath, FILE_ATTRIBUTE_NORMAL );
  67. return true;
  68. }
  69. //-----------------------------------------------------------------------------
  70. // LockBufferForRead
  71. //
  72. //-----------------------------------------------------------------------------
  73. Buffer_t *LockBufferForRead()
  74. {
  75. if ( !g_pReadBuffers )
  76. {
  77. // out of data, wait for it
  78. WaitForSingleObject( g_hReadEvent, INFINITE );
  79. }
  80. else
  81. {
  82. ResetEvent( g_hReadEvent );
  83. }
  84. EnterCriticalSection( &g_criticalSection );
  85. Buffer_t *pBuffer = g_pReadBuffers;
  86. g_pReadBuffers = pBuffer->pNext;
  87. (*g_pNumReadBuffers)--;
  88. LeaveCriticalSection( &g_criticalSection );
  89. return pBuffer;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // LockBufferForWrite
  93. //
  94. //-----------------------------------------------------------------------------
  95. Buffer_t* LockBufferForWrite()
  96. {
  97. if ( !g_pWriteBuffers )
  98. {
  99. // out of data, wait for more
  100. WaitForSingleObject( g_hWriteEvent, INFINITE );
  101. }
  102. else
  103. {
  104. ResetEvent( g_hWriteEvent );
  105. }
  106. EnterCriticalSection( &g_criticalSection );
  107. Buffer_t *pBuffer = g_pWriteBuffers;
  108. g_pWriteBuffers = pBuffer->pNext;
  109. (*g_pNumWriteBuffers)--;
  110. LeaveCriticalSection( &g_criticalSection );
  111. return pBuffer;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // AddBufferForRead
  115. //
  116. //-----------------------------------------------------------------------------
  117. void AddBufferForRead( Buffer_t *pBuffer )
  118. {
  119. EnterCriticalSection( &g_criticalSection );
  120. // add to end of list
  121. Buffer_t *pCurrent = g_pReadBuffers;
  122. while ( pCurrent && pCurrent->pNext )
  123. {
  124. pCurrent = pCurrent->pNext;
  125. }
  126. if ( pCurrent )
  127. {
  128. pBuffer->pNext = pCurrent->pNext;
  129. pCurrent->pNext = pBuffer;
  130. }
  131. else
  132. {
  133. pBuffer->pNext = NULL;
  134. g_pReadBuffers = pBuffer;
  135. }
  136. (*g_pNumReadBuffers)++;
  137. LeaveCriticalSection( &g_criticalSection );
  138. SetEvent( g_hReadEvent );
  139. }
  140. //-----------------------------------------------------------------------------
  141. // AddBufferForWrite
  142. //
  143. //-----------------------------------------------------------------------------
  144. void AddBufferForWrite( Buffer_t *pBuffer )
  145. {
  146. EnterCriticalSection( &g_criticalSection );
  147. // add to end of list
  148. Buffer_t* pCurrent = g_pWriteBuffers;
  149. while ( pCurrent && pCurrent->pNext )
  150. {
  151. pCurrent = pCurrent->pNext;
  152. }
  153. if ( pCurrent )
  154. {
  155. pBuffer->pNext = pCurrent->pNext;
  156. pCurrent->pNext = pBuffer;
  157. }
  158. else
  159. {
  160. pBuffer->pNext = NULL;
  161. g_pWriteBuffers = pBuffer;
  162. }
  163. (*g_pNumWriteBuffers)++;
  164. LeaveCriticalSection( &g_criticalSection );
  165. SetEvent( g_hWriteEvent );
  166. }
  167. //-----------------------------------------------------------------------------
  168. // ReadFileThread
  169. //
  170. //-----------------------------------------------------------------------------
  171. DWORD WINAPI ReadFileThread( LPVOID lParam )
  172. {
  173. CopyFile_t *pCopyFile;
  174. OVERLAPPED overlappedRead = {0};
  175. DWORD startTime;
  176. DWORD dwBytesRead;
  177. DWORD dwError;
  178. BOOL bResult;
  179. Buffer_t *pBuffer;
  180. pCopyFile = (CopyFile_t*)lParam;
  181. // Copy from the buffer to the Hard Drive
  182. for ( unsigned int readCycle = 0; readCycle < pCopyFile->m_numReadCycles; ++readCycle )
  183. {
  184. pBuffer = LockBufferForRead();
  185. startTime = GetTickCount();
  186. dwBytesRead = 0;
  187. int numAttempts = 0;
  188. retry:
  189. // read file from DVD
  190. bResult = ReadFile( pCopyFile->m_hSrcFile, pBuffer->pData, pCopyFile->m_readBufferSize, NULL, &overlappedRead );
  191. dwError = GetLastError();
  192. if ( !bResult && dwError != ERROR_IO_PENDING )
  193. {
  194. if ( dwError == ERROR_HANDLE_EOF )
  195. {
  196. // nothing more to read
  197. break;
  198. }
  199. numAttempts++;
  200. if ( numAttempts == 3 )
  201. {
  202. // error
  203. pCopyFile->m_bCopyError = true;
  204. break;
  205. }
  206. else
  207. {
  208. goto retry;
  209. }
  210. }
  211. else
  212. {
  213. // Wait for the operation to finish
  214. GetOverlappedResult( pCopyFile->m_hSrcFile, &overlappedRead, &dwBytesRead, TRUE );
  215. overlappedRead.Offset += dwBytesRead;
  216. }
  217. if ( !dwBytesRead )
  218. {
  219. pCopyFile->m_bCopyError = true;
  220. break;
  221. }
  222. pCopyFile->m_pCopyStats->m_bufferReadSize = dwBytesRead;
  223. pCopyFile->m_pCopyStats->m_bufferReadTime = GetTickCount() - startTime;
  224. pCopyFile->m_pCopyStats->m_totalReadSize += pCopyFile->m_pCopyStats->m_bufferReadSize;
  225. pCopyFile->m_pCopyStats->m_totalReadTime += pCopyFile->m_pCopyStats->m_bufferReadTime;
  226. pBuffer->dwSize = dwBytesRead;
  227. AddBufferForWrite( pBuffer );
  228. }
  229. return 0;
  230. }
  231. //-----------------------------------------------------------------------------
  232. // WriteFileThread
  233. //
  234. //-----------------------------------------------------------------------------
  235. DWORD WINAPI WriteFileThread( LPVOID lParam )
  236. {
  237. CopyFile_t *pCopyFile;
  238. OVERLAPPED overlappedWrite = {0};
  239. DWORD startTime;
  240. DWORD dwBytesWrite;
  241. DWORD dwWriteSize;
  242. DWORD dwError;
  243. BOOL bResult;
  244. Buffer_t *pBuffer;
  245. unsigned char *pWriteBuffer;
  246. pCopyFile = (CopyFile_t*)lParam;
  247. while ( overlappedWrite.Offset < pCopyFile->m_dstFileSize )
  248. {
  249. // wait for wake-up event
  250. pBuffer = LockBufferForWrite();
  251. if ( pCopyFile->m_bInflate )
  252. {
  253. startTime = GetTickCount();
  254. DWORD dwSkip = overlappedWrite.Offset ? 0 : sizeof( xCompressHeader );
  255. dwWriteSize = JCALG1_Decompress_Formatted_Buffer( pBuffer->dwSize - dwSkip, pBuffer->pData + dwSkip, pCopyFile->m_inflateBufferSize, pCopyFile->m_pInflateBuffer );
  256. if ( dwWriteSize == (DWORD)-1 )
  257. {
  258. pCopyFile->m_bCopyError = true;
  259. break;
  260. }
  261. pCopyFile->m_pCopyStats->m_inflateSize = dwWriteSize;
  262. pCopyFile->m_pCopyStats->m_inflateTime = GetTickCount() - startTime;
  263. pWriteBuffer = pCopyFile->m_pInflateBuffer;
  264. }
  265. else
  266. {
  267. // straight copy
  268. dwWriteSize = pBuffer->dwSize;
  269. pWriteBuffer = pBuffer->pData;
  270. }
  271. if ( overlappedWrite.Offset + dwWriteSize >= pCopyFile->m_dstFileSize )
  272. {
  273. // last buffer, ensure all data is written
  274. dwWriteSize = ALIGN( dwWriteSize, 512 );
  275. }
  276. startTime = GetTickCount();
  277. dwBytesWrite = 0;
  278. int numAttempts = 0;
  279. retry:
  280. // write file to HDD
  281. bResult = WriteFile( pCopyFile->m_hDstFile, pWriteBuffer, (dwWriteSize/512) * 512, NULL, &overlappedWrite );
  282. dwError = GetLastError();
  283. if ( !bResult && dwError != ERROR_IO_PENDING )
  284. {
  285. numAttempts++;
  286. if ( numAttempts == 3 )
  287. {
  288. // error
  289. pCopyFile->m_bCopyError = true;
  290. break;
  291. }
  292. else
  293. {
  294. goto retry;
  295. }
  296. }
  297. else
  298. {
  299. // Wait for the operation to finish
  300. GetOverlappedResult( pCopyFile->m_hDstFile, &overlappedWrite, &dwBytesWrite, TRUE );
  301. overlappedWrite.Offset += dwBytesWrite;
  302. }
  303. if ( dwBytesWrite )
  304. {
  305. // track expected size
  306. pCopyFile->m_pCopyStats->m_bytesCopied += dwBytesWrite;
  307. pCopyFile->m_pCopyStats->m_writeSize += dwBytesWrite;
  308. }
  309. else
  310. {
  311. pCopyFile->m_bCopyError = true;
  312. break;
  313. }
  314. pCopyFile->m_pCopyStats->m_bufferWriteSize = dwBytesWrite;
  315. pCopyFile->m_pCopyStats->m_bufferWriteTime = GetTickCount() - startTime;
  316. pCopyFile->m_pCopyStats->m_totalWriteSize += pCopyFile->m_pCopyStats->m_bufferWriteSize;
  317. pCopyFile->m_pCopyStats->m_totalWriteTime += pCopyFile->m_pCopyStats->m_bufferWriteTime;
  318. AddBufferForRead( pBuffer );
  319. }
  320. return 0;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // CopyFileInit
  324. //
  325. //-----------------------------------------------------------------------------
  326. void CopyFileInit()
  327. {
  328. static bool init = false;
  329. if ( !init )
  330. {
  331. InitializeCriticalSection( &g_criticalSection );
  332. g_hReadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  333. g_hWriteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  334. init = true;
  335. }
  336. else
  337. {
  338. // expected startup state
  339. ResetEvent( g_hReadEvent );
  340. ResetEvent( g_hWriteEvent );
  341. g_pReadBuffers = NULL;
  342. g_pWriteBuffers = NULL;
  343. }
  344. }
  345. //-----------------------------------------------------------------------------
  346. // CopyFileOverlapped
  347. //
  348. //-----------------------------------------------------------------------------
  349. bool CopyFileOverlapped( const char *pSrcFilename, const char *pDstFilename, xCompressHeader *pxcHeader, CopyStats_t *pCopyStats )
  350. {
  351. CopyFile_t copyFile = {0};
  352. Buffer_t buffers[NUM_BUFFERS] = {0};
  353. HANDLE hReadThread = NULL;
  354. HANDLE hWriteThread = NULL;
  355. bool bSuccess = false;
  356. DWORD startCopyTime;
  357. DWORD dwResult;
  358. int i;
  359. startCopyTime = GetTickCount();
  360. CopyFileInit();
  361. g_pNumReadBuffers = &pCopyStats->m_numReadBuffers;
  362. g_pNumWriteBuffers = &pCopyStats->m_numWriteBuffers;
  363. strcpy( pCopyStats->m_srcFilename, pSrcFilename );
  364. strcpy( pCopyStats->m_dstFilename, pDstFilename );
  365. copyFile.m_hSrcFile = INVALID_HANDLE_VALUE;
  366. copyFile.m_hDstFile = INVALID_HANDLE_VALUE;
  367. copyFile.m_pCopyStats = pCopyStats;
  368. copyFile.m_bCopyError = false;
  369. // validate the source file
  370. copyFile.m_hSrcFile = CreateFile( pSrcFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL );
  371. if ( copyFile.m_hSrcFile == INVALID_HANDLE_VALUE )
  372. {
  373. // failure
  374. goto cleanUp;
  375. }
  376. copyFile.m_srcFileSize = GetFileSize( copyFile.m_hSrcFile, NULL );
  377. if ( copyFile.m_srcFileSize == (DWORD)-1 )
  378. {
  379. // failure
  380. goto cleanUp;
  381. }
  382. // ensure the target file path exists
  383. CreateFilePath( pDstFilename );
  384. // validate the target file
  385. copyFile.m_hDstFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL );
  386. if ( copyFile.m_hDstFile == INVALID_HANDLE_VALUE )
  387. {
  388. // failure
  389. goto cleanUp;
  390. }
  391. pCopyStats->m_readSize = copyFile.m_srcFileSize;
  392. pCopyStats->m_writeSize = 0;
  393. if ( pxcHeader )
  394. {
  395. // read in chunks of compressed blocks
  396. copyFile.m_readBufferSize = pxcHeader->nReadBlockSize;
  397. copyFile.m_dstFileSize = pxcHeader->nUncompressedFileSize;
  398. }
  399. else
  400. {
  401. // setup for copy
  402. copyFile.m_readBufferSize = BUFFER_SIZE;
  403. copyFile.m_dstFileSize = copyFile.m_srcFileSize;
  404. }
  405. // setup read buffers
  406. for ( i=0; i<NUM_BUFFERS; i++)
  407. {
  408. buffers[i].pData = new unsigned char[copyFile.m_readBufferSize];
  409. buffers[i].dwSize = 0;
  410. buffers[i].pNext = NULL;
  411. AddBufferForRead( &buffers[i] );
  412. }
  413. copyFile.m_numReadCycles = (copyFile.m_srcFileSize + copyFile.m_readBufferSize - 1)/copyFile.m_readBufferSize;
  414. // setup write buffer
  415. if ( pxcHeader )
  416. {
  417. copyFile.m_pInflateBuffer = new unsigned char[pxcHeader->nDecompressionBufferSize];
  418. copyFile.m_inflateBufferSize = pxcHeader->nDecompressionBufferSize;
  419. copyFile.m_bInflate = true;
  420. }
  421. else
  422. {
  423. copyFile.m_bInflate = false;
  424. }
  425. // pre-size the target file in aligned buffers
  426. DWORD dwAligned = ALIGN( copyFile.m_dstFileSize, 512 );
  427. dwResult = SetFilePointer( copyFile.m_hDstFile, dwAligned, NULL, FILE_BEGIN );
  428. if ( dwResult == INVALID_SET_FILE_POINTER )
  429. {
  430. // failure
  431. goto cleanUp;
  432. }
  433. SetEndOfFile( copyFile.m_hDstFile );
  434. // start the read thread
  435. hReadThread = CreateThread( 0, 0, &ReadFileThread, &copyFile, 0, 0 );
  436. if ( !hReadThread )
  437. {
  438. // failure
  439. goto cleanUp;
  440. }
  441. // wait for buffers to populate
  442. // start the write thread
  443. hWriteThread = CreateThread( 0, 0, &WriteFileThread, &copyFile, 0, 0 );
  444. if ( !hWriteThread )
  445. {
  446. // failure
  447. goto cleanUp;
  448. }
  449. // wait for write thread to finish
  450. WaitForSingleObject( hWriteThread, INFINITE );
  451. WaitForSingleObject( hReadThread, INFINITE );
  452. if ( copyFile.m_bCopyError )
  453. {
  454. goto cleanUp;
  455. }
  456. // Fixup the file size
  457. CloseHandle( copyFile.m_hDstFile );
  458. copyFile.m_hDstFile = INVALID_HANDLE_VALUE;
  459. if ( copyFile.m_dstFileSize % 512 )
  460. {
  461. // re-open file as non-buffered to adjust to correct file size
  462. HANDLE hFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  463. SetFilePointer( hFile, copyFile.m_dstFileSize, NULL, FILE_BEGIN );
  464. SetEndOfFile( hFile );
  465. CloseHandle( hFile );
  466. }
  467. // finished
  468. bSuccess = true;
  469. cleanUp:
  470. if ( copyFile.m_hSrcFile != INVALID_HANDLE_VALUE )
  471. {
  472. CloseHandle( copyFile.m_hSrcFile );
  473. }
  474. if ( copyFile.m_hDstFile != INVALID_HANDLE_VALUE )
  475. {
  476. CloseHandle( copyFile.m_hDstFile );
  477. }
  478. if ( hReadThread )
  479. {
  480. CloseHandle( hReadThread );
  481. }
  482. if ( hWriteThread )
  483. {
  484. CloseHandle( hWriteThread );
  485. }
  486. for ( i=0; i<NUM_BUFFERS; i++ )
  487. {
  488. if ( buffers[i].pData )
  489. {
  490. delete [] buffers[i].pData;
  491. }
  492. }
  493. if ( copyFile.m_pInflateBuffer )
  494. {
  495. delete [] copyFile.m_pInflateBuffer;
  496. }
  497. if ( !bSuccess )
  498. {
  499. pCopyStats->m_copyErrors++;
  500. }
  501. pCopyStats->m_copyTime = GetTickCount() - startCopyTime;
  502. return bSuccess;
  503. }