Counter Strike : Global Offensive Source Code
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.

1702 lines
42 KiB

  1. //===== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======//
  2. //
  3. //
  4. //===========================================================================//
  5. #include "basefilesystem.h"
  6. #include "filesystem/ixboxinstaller.h"
  7. #include "tier1/utlbuffer.h"
  8. #include "tier0/icommandline.h"
  9. #include "tier2/tier2.h"
  10. #include "characterset.h"
  11. #include "xbox/xbox_launch.h"
  12. #include "fmtstr.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. // Uncomment to allow system to operate (Image must be built with expected segmented zips)
  16. #if defined( _X360 )
  17. #define SUPPORTS_INSTALL_TO_XBOX_HDD
  18. #endif
  19. #define INSTALL_MANAGER_PROCESSOR 3
  20. #define INSTALL_READ_PROCESSOR 3
  21. #define INSTALL_WRITE_PROCESSOR 4
  22. #if !defined( _CERT )
  23. ConVar xbox_install_fake_readerror( "xbox_install_fake_readerror", "0", 0 );
  24. ConVar xbox_install_fake_writeerror( "xbox_install_fake_writeerror", "0", 0 );
  25. #endif
  26. // artifical condition to disallow a qualified installation
  27. // this allows an install to be re-enabled
  28. ConVar xbox_install_allowed( "xbox_install_allowed", "1", 0 );
  29. struct installEntry_t
  30. {
  31. const char *pSource;
  32. const char *pTarget;
  33. };
  34. installEntry_t g_InstallScript[] =
  35. {
  36. {"D:\\csgo\\zip0.360.zip", "csgo\\zip0.360.zip"},
  37. // no localiztion install
  38. #if 0
  39. {"D:\\csgo_%\\zip0.360.zip", "csgo_%\\zip0.360.zip"},
  40. #endif
  41. // put down last as final complete marker
  42. {"D:\\version.xtx", "version.txt"},
  43. };
  44. bool ReadFileTest( const char *pFilename, DWORD nMaxReadSize, DWORD nRandomOffset )
  45. {
  46. OVERLAPPED Overlapped = { 0 };
  47. HANDLE hFile = INVALID_HANDLE_VALUE;
  48. void *pBuffer = NULL;
  49. bool bSuccess = false;
  50. char fixedFilename[MAX_PATH];
  51. V_strncpy( fixedFilename, pFilename, sizeof( fixedFilename ) );
  52. V_FixSlashes( fixedFilename );
  53. pFilename = fixedFilename;
  54. // validate the source file
  55. hFile = CreateFile( pFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL );
  56. if ( hFile == INVALID_HANDLE_VALUE )
  57. {
  58. // failure
  59. goto cleanUp;
  60. }
  61. DWORD nFileSize = GetFileSize( hFile, NULL );
  62. if ( nFileSize == (DWORD)-1 )
  63. {
  64. // failure
  65. goto cleanUp;
  66. }
  67. DWORD nBufferSize = AlignValue( nFileSize, SOURCE_SECTOR_SIZE );
  68. nBufferSize = min( nBufferSize, nMaxReadSize );
  69. pBuffer = malloc( nBufferSize );
  70. if ( !pBuffer )
  71. {
  72. // failure
  73. goto cleanUp;
  74. }
  75. if ( nRandomOffset + nBufferSize > nFileSize )
  76. {
  77. nRandomOffset = 0;
  78. }
  79. Overlapped.Offset = nRandomOffset;
  80. float startTime = Plat_FloatTime();
  81. // read file
  82. BOOL bResult = ReadFile( hFile, pBuffer, nBufferSize, NULL, &Overlapped );
  83. DWORD dwError = GetLastError();
  84. if ( !bResult && dwError != ERROR_IO_PENDING )
  85. {
  86. if ( dwError != ERROR_HANDLE_EOF )
  87. {
  88. goto cleanUp;
  89. }
  90. }
  91. DWORD dwBytesRead = 0;
  92. bResult = GetOverlappedResult( hFile, &Overlapped, &dwBytesRead, TRUE );
  93. dwError = GetLastError();
  94. if ( dwBytesRead != nBufferSize )
  95. {
  96. goto cleanUp;
  97. }
  98. float elapsed, totalSizeMB;
  99. elapsed = Plat_FloatTime() - startTime;
  100. totalSizeMB = (float)nBufferSize/(1024.0f*1024.0f);
  101. Msg( "Read: %s, %.2f MB in %.2f secs, %.2f MB/sec\n", pFilename, totalSizeMB, elapsed, totalSizeMB/elapsed );
  102. bSuccess = true;
  103. cleanUp:
  104. if ( hFile != INVALID_HANDLE_VALUE )
  105. {
  106. CloseHandle( hFile );
  107. }
  108. if ( pBuffer )
  109. {
  110. free( pBuffer );
  111. }
  112. return bSuccess;
  113. }
  114. struct FileDetails_t
  115. {
  116. FileDetails_t()
  117. {
  118. m_nRealFileSize = 0;
  119. m_bFileIsValid = false;
  120. }
  121. CUtlString m_SourceName;
  122. CUtlString m_TargetName;
  123. // this is the size we want the target file to be at completion
  124. DWORD m_nRealFileSize;
  125. // this is the reserved size of the target file
  126. bool m_bFileIsValid;
  127. };
  128. struct InstallData_t
  129. {
  130. void Reset()
  131. {
  132. m_Files.Purge();
  133. m_nTotalSize = 0;
  134. m_nVersion = 0;
  135. m_bValid = false;
  136. m_bCompleted = false;
  137. m_bFailed = false;
  138. }
  139. CUtlVector< FileDetails_t > m_Files;
  140. DWORD m_nTotalSize;
  141. DWORD m_nVersion;
  142. bool m_bValid;
  143. bool m_bCompleted;
  144. bool m_bFailed;
  145. };
  146. // copying onto HDD
  147. #define INSTALL_BUFFER_SIZE (512*1024)
  148. #define INSTALL_NUM_BUFFERS 4
  149. struct CopyFile_t
  150. {
  151. // source file
  152. HANDLE m_hSrcFile;
  153. DWORD m_srcFileSize;
  154. int m_readBufferSize;
  155. // target file
  156. HANDLE m_hDstFile;
  157. DWORD m_dstCurrentFileSize;
  158. CopyStats_t *m_pCopyStats;
  159. };
  160. struct Buffer_t
  161. {
  162. unsigned char *pData;
  163. DWORD dwSize;
  164. Buffer_t* pNext;
  165. int id;
  166. };
  167. class CXboxInstaller : public CTier2AppSystem< IXboxInstaller >
  168. {
  169. typedef CTier2AppSystem< IXboxInstaller > BaseClass;
  170. public:
  171. CXboxInstaller();
  172. virtual ~CXboxInstaller();
  173. // Inherited from IAppSystem
  174. virtual InitReturnVal_t Init();
  175. virtual void Shutdown();
  176. virtual bool Setup( bool bForceInstall );
  177. virtual void ResetSetup();
  178. virtual bool Start();
  179. virtual void Stop();
  180. virtual bool IsStopped( bool bForceStop );
  181. virtual DWORD GetTotalSize();
  182. virtual DWORD GetVersion();
  183. virtual const CopyStats_t *GetCopyStats();
  184. virtual bool IsInstallEnabled();
  185. virtual bool IsFullyInstalled();
  186. virtual bool ShouldRestart();
  187. virtual bool ForceCachePaths();
  188. virtual void SpewStatus();
  189. DWORD ReadFileThread( CopyFile_t *pCopyFile );
  190. DWORD WriteFileThread( CopyFile_t *pCopyFile );
  191. DWORD InstallThreadFunc();
  192. private:
  193. bool BuildInstallScript( bool bForceFullInstall );
  194. bool IsTargetFileValid( const char *pFilename, DWORD dwSrcFileSize );
  195. bool DoesFileExist( const char *pFilename, DWORD *pSize );
  196. bool CopyFileOverlapped( FileDetails_t *pFileDetails, CopyStats_t *pCopyStats );
  197. bool CreateFilePath( const char *inPath );
  198. bool FixupNamespaceFilename( const char *pLanguage, const char *pFilename, char *pOutFilename, int outFilenameSize );
  199. Buffer_t *LockBufferForRead();
  200. Buffer_t *LockBufferForWrite();
  201. void AddBufferForRead( Buffer_t *pBuffer );
  202. void AddBufferForWrite( Buffer_t *pBuffer );
  203. bool PrepareCachePartitionForInstall();
  204. CRITICAL_SECTION m_CriticalSection;
  205. HANDLE m_hReadEvent;
  206. HANDLE m_hWriteEvent;
  207. HANDLE m_hInstallThread;
  208. Buffer_t *m_pReadBuffers;
  209. Buffer_t *m_pWriteBuffers;
  210. DWORD *m_pNumReadBuffers;
  211. DWORD *m_pNumWriteBuffers;
  212. InstallData_t m_InstallData;
  213. CopyStats_t m_CopyStats;
  214. unsigned char *m_pCopyBuffers[INSTALL_NUM_BUFFERS];
  215. bool m_bHasHDD;
  216. bool m_bInit;
  217. bool m_bAppSystemInit;
  218. bool m_bForcedCachePaths;
  219. // used to stop the process under NON-ERROR conditions
  220. CInterlockedInt m_bStopping;
  221. // stops the process, read errors are fatal
  222. CInterlockedInt m_bReadError;
  223. // stops the process, write errors just permanently halt the install
  224. CInterlockedInt m_bWriteError;
  225. };
  226. static CXboxInstaller g_XboxInstaller;
  227. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CXboxInstaller, IXboxInstaller, XBOXINSTALLER_INTERFACE_VERSION, g_XboxInstaller );
  228. CON_COMMAND( xbox_install_testcache, "" )
  229. {
  230. if ( !g_XboxInstaller.IsFullyInstalled() )
  231. {
  232. return;
  233. }
  234. DWORD nRandomOffset = 0;
  235. DWORD nInitialSize = 16*1024;
  236. for ( int i = 0; i < 8; i++ )
  237. {
  238. Msg( "\nRead Test at %.2f MB\n", (float)nInitialSize/(1024.0f*1024.0f) );
  239. nRandomOffset += 4*1024*1024;
  240. ReadFileTest( CFmtStr( "%s/csgo/zip0.360.zip", CACHE_PATH_CSTIKRE15 ), nInitialSize, nRandomOffset );
  241. ReadFileTest( "d:/csgo/zip0.360.zip", nInitialSize, nRandomOffset );
  242. nInitialSize <<= 1;
  243. }
  244. }
  245. // for thrash testing only
  246. CON_COMMAND( xbox_install_teststop, "" )
  247. {
  248. g_XboxInstaller.Stop();
  249. }
  250. // for thrash testing only
  251. CON_COMMAND( xbox_install_teststart, "" )
  252. {
  253. g_XboxInstaller.Start();
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Isolated all mounting/unmounting here.
  257. //-----------------------------------------------------------------------------
  258. static bool MountCachePartition( bool bFormat )
  259. {
  260. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  261. // under demo conditions cannot allow any HDD access
  262. return false;
  263. #endif
  264. static bool s_bMounted = false;
  265. if ( s_bMounted )
  266. {
  267. if ( bFormat )
  268. {
  269. // must unmount before format
  270. XUnmountUtilityDrive();
  271. s_bMounted = false;
  272. }
  273. else
  274. {
  275. // already mounted
  276. return true;
  277. }
  278. }
  279. COM_TimestampedLog( "XMountUtilityDrive( %s )", ( bFormat ? "format" : "" ) );
  280. // small cluster size not useful, as we are block installing large files
  281. DWORD dwResult = XMountUtilityDrive( bFormat, 64*1024, 256*1024 );
  282. if ( dwResult == ERROR_SUCCESS )
  283. {
  284. s_bMounted = true;
  285. }
  286. COM_TimestampedLog( "XMountUtilityDrive() - Finish" );
  287. return s_bMounted;
  288. }
  289. //-----------------------------------------------------------------------------
  290. // USED BY THE FILESYSTEM ONLY!!!! AT IT'S INIT/BOOT TIME
  291. // This is here as a back door, it has to solve the query during the filesystem's
  292. // init method, without using the filesystem.
  293. //-----------------------------------------------------------------------------
  294. bool IsAlreadyInstalledToXboxHDDCache()
  295. {
  296. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  297. // under demo conditions cannot allow any HDD access
  298. return false;
  299. #endif
  300. // TCR best practices allows a backdoor (LS+RS) to reforce HDD cache installers.
  301. // Tech support may tell users with flaky HDD to do this.
  302. // Our inputsystem wrapper is not available yet due to init chain, so go directly to hardware.
  303. bool bForceInstall = false;
  304. DWORD dwResult;
  305. for ( DWORD i=0; i < XUSER_MAX_COUNT; i++ )
  306. {
  307. XINPUT_STATE state;
  308. ZeroMemory( &state, sizeof( XINPUT_STATE ) );
  309. dwResult = XInputGetState( i, &state );
  310. if ( dwResult != ERROR_SUCCESS )
  311. continue;
  312. // it must be (LS+RS) exactlty only on any controller, and not a mashing of buttons
  313. if ( state.Gamepad.wButtons == ( XINPUT_GAMEPAD_LEFT_SHOULDER|XINPUT_GAMEPAD_RIGHT_SHOULDER ) &&
  314. state.Gamepad.bLeftTrigger == 0 &&
  315. state.Gamepad.bRightTrigger == 0 )
  316. {
  317. bForceInstall = true;
  318. }
  319. else if ( state.Gamepad.wButtons || state.Gamepad.bLeftTrigger || state.Gamepad.bRightTrigger )
  320. {
  321. // other buttons are being held, sorry
  322. bForceInstall = false;
  323. break;
  324. }
  325. }
  326. if ( CommandLine()->FindParm( "-forceinstall" ) != 0 )
  327. {
  328. bForceInstall = true;
  329. }
  330. if ( !xbox_install_allowed.GetBool() )
  331. {
  332. // ignore any prior install by clearing any existing install
  333. bForceInstall = true;
  334. }
  335. if ( g_XboxInstaller.Setup( bForceInstall ) )
  336. {
  337. return g_XboxInstaller.IsFullyInstalled();
  338. }
  339. return false;
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Constructor
  343. //-----------------------------------------------------------------------------
  344. CXboxInstaller::CXboxInstaller()
  345. {
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Destructor
  349. //-----------------------------------------------------------------------------
  350. CXboxInstaller::~CXboxInstaller()
  351. {
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Initialization
  355. //-----------------------------------------------------------------------------
  356. InitReturnVal_t CXboxInstaller::Init()
  357. {
  358. InitReturnVal_t nRetVal = BaseClass::Init();
  359. if ( nRetVal != INIT_OK )
  360. {
  361. return nRetVal;
  362. }
  363. // NOTHING CAN GO HERE!!!!
  364. // This subverts normal startup procedures and performs a few operations
  365. // for the filesystem's init() before it gets inited.
  366. m_bAppSystemInit = true;
  367. return INIT_OK;
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Shutdown
  371. //-----------------------------------------------------------------------------
  372. void CXboxInstaller::Shutdown()
  373. {
  374. BaseClass::Shutdown();
  375. }
  376. //-----------------------------------------------------------------------------
  377. // CreateFilePath
  378. //
  379. // Create full path to specified file.
  380. //-----------------------------------------------------------------------------
  381. bool CXboxInstaller::CreateFilePath( const char *inPath )
  382. {
  383. char* ptr;
  384. char dirPath[MAX_PATH];
  385. BOOL bSuccess;
  386. // prime and skip to first seperator after the drive path
  387. strcpy( dirPath, inPath );
  388. ptr = strchr( dirPath, '\\' );
  389. while ( ptr )
  390. {
  391. ptr = strchr( ptr+1, '\\' );
  392. if ( ptr )
  393. {
  394. *ptr = '\0';
  395. bSuccess = ::CreateDirectory( dirPath, NULL );
  396. *ptr = '\\';
  397. }
  398. }
  399. // ensure read-only is cleared
  400. SetFileAttributes( inPath, FILE_ATTRIBUTE_NORMAL );
  401. return true;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // FixupNamespaceFilename
  405. //-----------------------------------------------------------------------------
  406. bool CXboxInstaller::FixupNamespaceFilename( const char *pLanguage, const char *pFilename, char *pOutFilename, int outFilenameSize )
  407. {
  408. char newFilename[MAX_PATH];
  409. bool bFixup = false;
  410. int dstLen = 0;
  411. int srcLen = strlen( pFilename );
  412. for ( int i=0; i<srcLen+1; i++ )
  413. {
  414. // replace every occurrence of % with language
  415. if ( pFilename[i] == '%' )
  416. {
  417. int len = strlen( pLanguage );
  418. memcpy( newFilename + dstLen, pLanguage, len );
  419. dstLen += len;
  420. bFixup = true;
  421. }
  422. else
  423. {
  424. newFilename[dstLen] = pFilename[i];
  425. dstLen++;
  426. }
  427. }
  428. V_strncpy( pOutFilename, newFilename, outFilenameSize );
  429. return bFixup;
  430. }
  431. //-----------------------------------------------------------------------------
  432. // LockBufferForRead
  433. //
  434. //-----------------------------------------------------------------------------
  435. Buffer_t *CXboxInstaller::LockBufferForRead()
  436. {
  437. EnterCriticalSection( &m_CriticalSection );
  438. if ( m_pReadBuffers )
  439. {
  440. ResetEvent( m_hReadEvent );
  441. }
  442. else
  443. {
  444. do
  445. {
  446. // prevent any possible block
  447. if ( ( m_bStopping || m_bReadError || m_bWriteError ) )
  448. {
  449. LeaveCriticalSection( &m_CriticalSection );
  450. return NULL;
  451. }
  452. LeaveCriticalSection( &m_CriticalSection );
  453. // out of data, wait for it
  454. WaitForSingleObject( m_hReadEvent, INFINITE );
  455. EnterCriticalSection( &m_CriticalSection );
  456. }
  457. while ( !m_pReadBuffers );
  458. }
  459. Buffer_t *pBuffer = m_pReadBuffers;
  460. m_pReadBuffers = pBuffer->pNext;
  461. (*m_pNumReadBuffers)--;
  462. LeaveCriticalSection( &m_CriticalSection );
  463. return pBuffer;
  464. }
  465. //-----------------------------------------------------------------------------
  466. // LockBufferForWrite
  467. //
  468. //-----------------------------------------------------------------------------
  469. Buffer_t *CXboxInstaller::LockBufferForWrite()
  470. {
  471. EnterCriticalSection( &m_CriticalSection );
  472. if ( m_pWriteBuffers )
  473. {
  474. ResetEvent( m_hWriteEvent );
  475. }
  476. else
  477. {
  478. do
  479. {
  480. // prevent any possible block
  481. if ( ( m_bStopping || m_bReadError || m_bWriteError ) )
  482. {
  483. LeaveCriticalSection( &m_CriticalSection );
  484. return NULL;
  485. }
  486. LeaveCriticalSection( &m_CriticalSection );
  487. // out of data, wait for more
  488. WaitForSingleObject( m_hWriteEvent, INFINITE );
  489. EnterCriticalSection( &m_CriticalSection );
  490. }
  491. while ( !m_pWriteBuffers );
  492. }
  493. Buffer_t *pBuffer = m_pWriteBuffers;
  494. m_pWriteBuffers = pBuffer->pNext;
  495. (*m_pNumWriteBuffers)--;
  496. LeaveCriticalSection( &m_CriticalSection );
  497. return pBuffer;
  498. }
  499. //-----------------------------------------------------------------------------
  500. // AddBufferForRead
  501. //
  502. //-----------------------------------------------------------------------------
  503. void CXboxInstaller::AddBufferForRead( Buffer_t *pBuffer )
  504. {
  505. EnterCriticalSection( &m_CriticalSection );
  506. // add to end of list
  507. Buffer_t *pCurrent = m_pReadBuffers;
  508. while ( pCurrent && pCurrent->pNext )
  509. {
  510. pCurrent = pCurrent->pNext;
  511. }
  512. if ( pCurrent )
  513. {
  514. pBuffer->pNext = pCurrent->pNext;
  515. pCurrent->pNext = pBuffer;
  516. }
  517. else
  518. {
  519. pBuffer->pNext = NULL;
  520. m_pReadBuffers = pBuffer;
  521. }
  522. (*m_pNumReadBuffers)++;
  523. LeaveCriticalSection( &m_CriticalSection );
  524. SetEvent( m_hReadEvent );
  525. }
  526. //-----------------------------------------------------------------------------
  527. // AddBufferForWrite
  528. //
  529. //-----------------------------------------------------------------------------
  530. void CXboxInstaller::AddBufferForWrite( Buffer_t *pBuffer )
  531. {
  532. EnterCriticalSection( &m_CriticalSection );
  533. // add to end of list
  534. Buffer_t* pCurrent = m_pWriteBuffers;
  535. while ( pCurrent && pCurrent->pNext )
  536. {
  537. pCurrent = pCurrent->pNext;
  538. }
  539. if ( pCurrent )
  540. {
  541. pBuffer->pNext = pCurrent->pNext;
  542. pCurrent->pNext = pBuffer;
  543. }
  544. else
  545. {
  546. pBuffer->pNext = NULL;
  547. m_pWriteBuffers = pBuffer;
  548. }
  549. (*m_pNumWriteBuffers)++;
  550. LeaveCriticalSection( &m_CriticalSection );
  551. SetEvent( m_hWriteEvent );
  552. }
  553. //-----------------------------------------------------------------------------
  554. // ReadFileThread
  555. //
  556. //-----------------------------------------------------------------------------
  557. DWORD CXboxInstaller::ReadFileThread( CopyFile_t *pCopyFile )
  558. {
  559. OVERLAPPED overlappedRead = {0};
  560. DWORD startTime;
  561. DWORD dwBytesRead;
  562. DWORD dwError;
  563. BOOL bResult;
  564. Buffer_t *pBuffer = NULL;
  565. // start reading from resume point
  566. overlappedRead.Offset = pCopyFile->m_dstCurrentFileSize;
  567. // Copy from the buffer to the Hard Drive
  568. while ( overlappedRead.Offset < pCopyFile->m_srcFileSize )
  569. {
  570. pBuffer = LockBufferForRead();
  571. if ( !pBuffer )
  572. {
  573. break;
  574. }
  575. if ( m_bStopping || m_bReadError || m_bWriteError )
  576. {
  577. // stopping or
  578. // errors occuring, cease all reading
  579. break;
  580. }
  581. startTime = GetTickCount();
  582. dwBytesRead = 0;
  583. int numAttempts = 0;
  584. retry:
  585. // read file from DVD
  586. bResult = ReadFile( pCopyFile->m_hSrcFile, pBuffer->pData, pCopyFile->m_readBufferSize, NULL, &overlappedRead );
  587. dwError = GetLastError();
  588. if ( !bResult && dwError != ERROR_IO_PENDING )
  589. {
  590. if ( dwError == ERROR_HANDLE_EOF )
  591. {
  592. // nothing more to read
  593. break;
  594. }
  595. numAttempts++;
  596. if ( numAttempts == 3 )
  597. {
  598. // error
  599. m_bReadError = true;
  600. break;
  601. }
  602. else
  603. {
  604. goto retry;
  605. }
  606. }
  607. else
  608. {
  609. // Wait for the operation to finish
  610. GetOverlappedResult( pCopyFile->m_hSrcFile, &overlappedRead, &dwBytesRead, TRUE );
  611. }
  612. #if !defined( _CERT )
  613. if ( xbox_install_fake_readerror.GetBool() )
  614. {
  615. dwBytesRead = 0;
  616. }
  617. #endif
  618. if ( !dwBytesRead )
  619. {
  620. m_bReadError = true;
  621. break;
  622. }
  623. overlappedRead.Offset += dwBytesRead;
  624. pCopyFile->m_pCopyStats->m_BufferReadSize = dwBytesRead;
  625. pCopyFile->m_pCopyStats->m_BufferReadTime = GetTickCount() - startTime;
  626. pCopyFile->m_pCopyStats->m_TotalReadSize += pCopyFile->m_pCopyStats->m_BufferReadSize;
  627. pCopyFile->m_pCopyStats->m_TotalReadTime += pCopyFile->m_pCopyStats->m_BufferReadTime;
  628. pBuffer->dwSize = dwBytesRead;
  629. AddBufferForWrite( pBuffer );
  630. }
  631. if ( ( m_bStopping || m_bReadError || m_bWriteError ) && pBuffer )
  632. {
  633. // the aborted buffer must be returned to the pool
  634. // this unblocks the write thread who will detect the error
  635. AddBufferForWrite( pBuffer );
  636. }
  637. return 0;
  638. }
  639. static DWORD WINAPI ReadFileThread( LPVOID lParam )
  640. {
  641. return g_XboxInstaller.ReadFileThread( (CopyFile_t*)lParam );
  642. }
  643. //-----------------------------------------------------------------------------
  644. // WriteFileThread
  645. //
  646. //-----------------------------------------------------------------------------
  647. DWORD CXboxInstaller::WriteFileThread( CopyFile_t *pCopyFile )
  648. {
  649. OVERLAPPED overlappedWrite = {0};
  650. DWORD startTime;
  651. DWORD dwBytesWrite;
  652. DWORD dwWriteSize;
  653. DWORD dwError;
  654. BOOL bResult;
  655. Buffer_t *pBuffer = NULL;
  656. unsigned char *pWriteBuffer;
  657. // start writing from the resume point
  658. overlappedWrite.Offset = pCopyFile->m_dstCurrentFileSize;
  659. pCopyFile->m_pCopyStats->m_BytesCopied += pCopyFile->m_dstCurrentFileSize;
  660. pCopyFile->m_pCopyStats->m_WriteSize += pCopyFile->m_dstCurrentFileSize;
  661. while ( overlappedWrite.Offset < pCopyFile->m_srcFileSize )
  662. {
  663. // wait for wake-up event
  664. pBuffer = LockBufferForWrite();
  665. if ( !pBuffer )
  666. {
  667. break;
  668. }
  669. if ( m_bStopping || m_bReadError || m_bWriteError )
  670. {
  671. // stopping or
  672. // errors occuring, cease all writing
  673. break;
  674. }
  675. dwWriteSize = pBuffer->dwSize;
  676. pWriteBuffer = pBuffer->pData;
  677. if ( overlappedWrite.Offset + dwWriteSize >= pCopyFile->m_srcFileSize )
  678. {
  679. // last buffer, ensure all data is written
  680. dwWriteSize = ALIGN_VALUE( dwWriteSize, TARGET_SECTOR_SIZE );
  681. }
  682. startTime = GetTickCount();
  683. dwBytesWrite = 0;
  684. int numAttempts = 0;
  685. retry:
  686. // write file to HDD
  687. bResult = WriteFile( pCopyFile->m_hDstFile, pWriteBuffer, (dwWriteSize/TARGET_SECTOR_SIZE) * TARGET_SECTOR_SIZE, NULL, &overlappedWrite );
  688. dwError = GetLastError();
  689. if ( !bResult && dwError != ERROR_IO_PENDING )
  690. {
  691. numAttempts++;
  692. if ( numAttempts == 3 )
  693. {
  694. // error
  695. m_bWriteError = true;
  696. break;
  697. }
  698. else
  699. {
  700. goto retry;
  701. }
  702. }
  703. else
  704. {
  705. // Wait for the operation to finish
  706. bResult = GetOverlappedResult( pCopyFile->m_hDstFile, &overlappedWrite, &dwBytesWrite, TRUE );
  707. dwError = GetLastError();
  708. }
  709. #if !defined( _CERT )
  710. if ( xbox_install_fake_writeerror.GetBool() )
  711. {
  712. dwBytesWrite = 0;
  713. }
  714. #endif
  715. if ( !dwBytesWrite )
  716. {
  717. m_bWriteError = true;
  718. break;
  719. }
  720. // track expected size
  721. overlappedWrite.Offset += dwBytesWrite;
  722. pCopyFile->m_pCopyStats->m_BytesCopied += dwBytesWrite;
  723. pCopyFile->m_pCopyStats->m_WriteSize += dwBytesWrite;
  724. pCopyFile->m_pCopyStats->m_BufferWriteSize = dwBytesWrite;
  725. pCopyFile->m_pCopyStats->m_BufferWriteTime = GetTickCount() - startTime;
  726. pCopyFile->m_pCopyStats->m_TotalWriteSize += pCopyFile->m_pCopyStats->m_BufferWriteSize;
  727. pCopyFile->m_pCopyStats->m_TotalWriteTime += pCopyFile->m_pCopyStats->m_BufferWriteTime;
  728. // done with buffer
  729. AddBufferForRead( pBuffer );
  730. }
  731. if ( ( m_bStopping || m_bReadError || m_bWriteError ) && pBuffer )
  732. {
  733. // the aborted buffer must be returned to the pool
  734. // this unblocks the read thread who will detect the error
  735. AddBufferForRead( pBuffer );
  736. }
  737. return 0;
  738. }
  739. static DWORD WINAPI WriteFileThread( LPVOID lParam )
  740. {
  741. return g_XboxInstaller.WriteFileThread( (CopyFile_t*)lParam );
  742. }
  743. //-----------------------------------------------------------------------------
  744. // CopyFileOverlapped
  745. //
  746. //-----------------------------------------------------------------------------
  747. bool CXboxInstaller::CopyFileOverlapped( FileDetails_t *pFileDetails, CopyStats_t *pCopyStats )
  748. {
  749. CopyFile_t copyFile = {0};
  750. Buffer_t buffers[INSTALL_NUM_BUFFERS] = {0};
  751. HANDLE hReadThread = NULL;
  752. HANDLE hWriteThread = NULL;
  753. bool bSuccess = false;
  754. DWORD startCopyTime;
  755. DWORD dwResult;
  756. const char *pSrcFilename = pFileDetails->m_SourceName.String();
  757. const char *pDstFilename = pFileDetails->m_TargetName.String();
  758. startCopyTime = GetTickCount();
  759. m_pReadBuffers = NULL;
  760. m_pWriteBuffers = NULL;
  761. pCopyStats->m_NumReadBuffers = 0;
  762. pCopyStats->m_NumWriteBuffers = 0;
  763. m_pNumReadBuffers = &pCopyStats->m_NumReadBuffers;
  764. m_pNumWriteBuffers = &pCopyStats->m_NumWriteBuffers;
  765. strcpy( pCopyStats->m_srcFilename, pSrcFilename );
  766. strcpy( pCopyStats->m_dstFilename, pDstFilename );
  767. copyFile.m_hSrcFile = INVALID_HANDLE_VALUE;
  768. copyFile.m_hDstFile = INVALID_HANDLE_VALUE;
  769. copyFile.m_pCopyStats = pCopyStats;
  770. if ( !m_hReadEvent )
  771. {
  772. m_hReadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  773. if ( !m_hReadEvent )
  774. {
  775. goto cleanUp;
  776. }
  777. }
  778. if ( !m_hWriteEvent )
  779. {
  780. m_hWriteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  781. if ( !m_hWriteEvent )
  782. {
  783. goto cleanUp;
  784. }
  785. }
  786. // expected startup state
  787. ResetEvent( m_hReadEvent );
  788. ResetEvent( m_hWriteEvent );
  789. // validate the source file
  790. copyFile.m_hSrcFile = CreateFile( pSrcFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL );
  791. if ( copyFile.m_hSrcFile == INVALID_HANDLE_VALUE )
  792. {
  793. // failure
  794. goto cleanUp;
  795. }
  796. // ensure the target file path exists
  797. CreateFilePath( pDstFilename );
  798. // validate the target file
  799. copyFile.m_hDstFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL );
  800. if ( copyFile.m_hDstFile == INVALID_HANDLE_VALUE )
  801. {
  802. // failure
  803. goto cleanUp;
  804. }
  805. pCopyStats->m_ReadSize = pFileDetails->m_nRealFileSize;
  806. pCopyStats->m_WriteSize = 0;
  807. // setup for copy
  808. copyFile.m_readBufferSize = INSTALL_BUFFER_SIZE;
  809. copyFile.m_srcFileSize = pFileDetails->m_nRealFileSize;
  810. // not supporting resume, always copy entire file
  811. copyFile.m_dstCurrentFileSize = 0;
  812. // setup read buffers
  813. for ( int i = 0; i < INSTALL_NUM_BUFFERS; i++ )
  814. {
  815. buffers[i].pData = m_pCopyBuffers[i];
  816. buffers[i].dwSize = 0;
  817. buffers[i].pNext = NULL;
  818. AddBufferForRead( &buffers[i] );
  819. }
  820. // pre-size the target file in aligned buffers
  821. DWORD dwAligned = ALIGN_VALUE( copyFile.m_srcFileSize, TARGET_SECTOR_SIZE );
  822. dwResult = SetFilePointer( copyFile.m_hDstFile, dwAligned, NULL, FILE_BEGIN );
  823. if ( dwResult == INVALID_SET_FILE_POINTER )
  824. {
  825. // failure
  826. goto cleanUp;
  827. }
  828. SetEndOfFile( copyFile.m_hDstFile );
  829. // start the read thread
  830. hReadThread = CreateThread( 0, 0, &::ReadFileThread, &copyFile, CREATE_SUSPENDED, 0 );
  831. if ( !hReadThread )
  832. {
  833. // failure
  834. goto cleanUp;
  835. }
  836. XSetThreadProcessor( hReadThread, INSTALL_READ_PROCESSOR );
  837. ResumeThread( hReadThread );
  838. // start the write thread
  839. hWriteThread = CreateThread( 0, 0, &::WriteFileThread, &copyFile, CREATE_SUSPENDED, 0 );
  840. if ( !hWriteThread )
  841. {
  842. // failure
  843. goto cleanUp;
  844. }
  845. XSetThreadProcessor( hWriteThread, INSTALL_WRITE_PROCESSOR );
  846. ResumeThread( hWriteThread );
  847. // wait for threads to finish
  848. WaitForSingleObject( hWriteThread, INFINITE );
  849. WaitForSingleObject( hReadThread, INFINITE );
  850. CloseHandle( copyFile.m_hDstFile );
  851. copyFile.m_hDstFile = INVALID_HANDLE_VALUE;
  852. if ( m_bReadError || m_bWriteError )
  853. {
  854. goto cleanUp;
  855. }
  856. // only change the file to its true size if file copying has completed
  857. if ( pCopyStats->m_WriteSize >= copyFile.m_srcFileSize )
  858. {
  859. // file has completed copying, fixup to the correct final file size
  860. // no need to change the file size if sector aligned
  861. if ( copyFile.m_srcFileSize % TARGET_SECTOR_SIZE )
  862. {
  863. // re-open file as non-buffered to adjust to correct file size
  864. HANDLE hFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  865. if ( hFile == INVALID_HANDLE_VALUE )
  866. {
  867. goto cleanUp;
  868. }
  869. SetFilePointer( hFile, copyFile.m_srcFileSize, NULL, FILE_BEGIN );
  870. SetEndOfFile( hFile );
  871. CloseHandle( hFile );
  872. }
  873. pCopyStats->m_WriteSize = copyFile.m_srcFileSize;
  874. // copy is complete
  875. pFileDetails->m_bFileIsValid = true;
  876. }
  877. // finished
  878. bSuccess = true;
  879. cleanUp:
  880. if ( copyFile.m_hSrcFile != INVALID_HANDLE_VALUE )
  881. {
  882. CloseHandle( copyFile.m_hSrcFile );
  883. }
  884. if ( copyFile.m_hDstFile != INVALID_HANDLE_VALUE )
  885. {
  886. CloseHandle( copyFile.m_hDstFile );
  887. }
  888. if ( hReadThread )
  889. {
  890. CloseHandle( hReadThread );
  891. }
  892. if ( hWriteThread )
  893. {
  894. CloseHandle( hWriteThread );
  895. }
  896. if ( !bSuccess )
  897. {
  898. pCopyStats->m_CopyErrors++;
  899. }
  900. pCopyStats->m_CopyTime = GetTickCount() - startCopyTime;
  901. if ( m_bReadError )
  902. {
  903. FSDirtyDiskReportFunc_t func = g_pFullFileSystem->GetDirtyDiskReportFunc();
  904. if ( func )
  905. {
  906. func();
  907. }
  908. }
  909. return bSuccess;
  910. }
  911. //-----------------------------------------------------------------------------
  912. // DoesFileExist
  913. //-----------------------------------------------------------------------------
  914. bool CXboxInstaller::DoesFileExist( const char *pFilename, DWORD *pSize )
  915. {
  916. if ( pSize )
  917. {
  918. *pSize = 0;
  919. }
  920. HANDLE hFile = CreateFile( pFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  921. if ( hFile != INVALID_HANDLE_VALUE )
  922. {
  923. // must be able to get file size
  924. DWORD dwFileSize = GetFileSize( hFile, NULL );
  925. CloseHandle( hFile );
  926. if ( dwFileSize != (DWORD)-1 )
  927. {
  928. // file exists and can get file size
  929. if ( pSize )
  930. {
  931. *pSize = dwFileSize;
  932. }
  933. return true;
  934. }
  935. }
  936. // not present
  937. return false;
  938. }
  939. //-----------------------------------------------------------------------------
  940. // IsTargetFileValid
  941. //
  942. // Optional non-zero expected source file size must match.
  943. // Passing in 0 for srcFileSize is the simpler existence check.
  944. //-----------------------------------------------------------------------------
  945. bool CXboxInstaller::IsTargetFileValid( const char *pFilename, DWORD dwSrcFileSize )
  946. {
  947. DWORD dwTargetFileSize = 0;
  948. // all valid target files are non-zero, CANNOT allow 0 sized files
  949. // the target file size could possibly be larger if a copy was aborted
  950. if ( !DoesFileExist( pFilename, &dwTargetFileSize ) || !dwTargetFileSize )
  951. {
  952. return false;
  953. }
  954. // valid means the target file's contents are finalized and can be trusted
  955. // all the sizes must be in agreement
  956. if ( dwSrcFileSize && dwSrcFileSize != dwTargetFileSize )
  957. {
  958. return false;
  959. }
  960. // valid (or if source size not supplied, then present)
  961. return true;
  962. }
  963. //-----------------------------------------------------------------------------
  964. // Ensure the cache partition has enough space for the install. Possibly
  965. // unmounts and reformats.
  966. //-----------------------------------------------------------------------------
  967. bool CXboxInstaller::PrepareCachePartitionForInstall()
  968. {
  969. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  970. // under demo conditions cannot allow any HDD access
  971. return false;
  972. #endif
  973. if ( m_InstallData.m_bValid )
  974. {
  975. // already installed, nothing to do
  976. return true;
  977. }
  978. // Always reformat - the cache partition will fragment easily
  979. // a reformat erases any existing data
  980. // the full install will be needed
  981. DWORD nBytesNeeded = m_InstallData.m_nTotalSize + 16*1024*1024;
  982. // force a re-format
  983. // expect ~2GB free, according to docs
  984. if ( !MountCachePartition( true ) )
  985. {
  986. // can't remount
  987. return false;
  988. }
  989. ULARGE_INTEGER FreeBytesAvailable = { 0 };
  990. ULARGE_INTEGER TotalNumberOfBytes = { 0 };
  991. ULARGE_INTEGER TotalNumberOfFreeBytes = { 0 };
  992. // check the partition again
  993. if ( !GetDiskFreeSpaceEx(
  994. "cache:\\",
  995. &FreeBytesAvailable,
  996. &TotalNumberOfBytes,
  997. &TotalNumberOfFreeBytes ) )
  998. {
  999. return false;
  1000. }
  1001. if ( nBytesNeeded > FreeBytesAvailable.LowPart )
  1002. {
  1003. // huh? could not get enough free bytes after a re-format!
  1004. // very bad
  1005. return false;
  1006. }
  1007. return true;
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. // BuildInstallScript
  1011. //
  1012. // Parse filenames to be copied. Builds install script.
  1013. // Returns false if install is not possible (i.e. no HDD).
  1014. // Returns true with script. Script may be empty as install is valid.
  1015. //-----------------------------------------------------------------------------
  1016. bool CXboxInstaller::BuildInstallScript( bool bForceFullInstall )
  1017. {
  1018. char srcFile[MAX_PATH];
  1019. char dstFile[MAX_PATH];
  1020. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  1021. // under demo conditions cannot allow any HDD access
  1022. return false;
  1023. #endif
  1024. if ( m_bAppSystemInit )
  1025. {
  1026. if ( g_pFullFileSystem->IsInstalledToXboxHDDCache() )
  1027. {
  1028. // nothing to do, already validated, cache is in use
  1029. return true;
  1030. }
  1031. if ( !g_pFullFileSystem->IsInstallAllowed() )
  1032. {
  1033. // the filesystem has trumped, we cannot run
  1034. return false;
  1035. }
  1036. }
  1037. // clear out any prior results
  1038. // setup default state
  1039. ResetSetup();
  1040. // mount cache partition as-is
  1041. // need to do this to validate possible existing install (i.e. at powerup)
  1042. if ( !MountCachePartition( false ) )
  1043. {
  1044. // no HDD
  1045. return false;
  1046. }
  1047. // get the version, expected to be at root of disk
  1048. CUtlBuffer sourceVersionBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1049. if ( !g_pFullFileSystem->ReadFile( "d:/version.xtx", NULL, sourceVersionBuffer ) )
  1050. {
  1051. // missing critical install file
  1052. return false;
  1053. }
  1054. m_InstallData.m_nVersion = atoi( (const char *)sourceVersionBuffer.Base() );
  1055. // only interested in installing a localized audio component
  1056. // not all supported languages have a localized audio component
  1057. const char *pLanguageString = "";
  1058. if ( XBX_IsAudioLocalized() )
  1059. {
  1060. pLanguageString = XBX_GetLanguageString();
  1061. // paranoid check
  1062. if ( !V_stricmp( pLanguageString, "english" ) )
  1063. {
  1064. // bad, system is confused
  1065. pLanguageString = "";
  1066. }
  1067. }
  1068. bool bForce = bForceFullInstall;
  1069. characterset_t breakSet;
  1070. CharacterSetBuild( &breakSet, "" );
  1071. // scan install script
  1072. // ensure every expected file exists properly at the target, otherwise an install is necessary
  1073. for ( int i = 0; i < ARRAYSIZE( g_InstallScript ); i++ )
  1074. {
  1075. V_strncpy( srcFile, g_InstallScript[i].pSource, sizeof( srcFile ) );
  1076. V_ComposeFileName( CACHE_PATH_CSTIKRE15, g_InstallScript[i].pTarget, dstFile, sizeof( dstFile ) );
  1077. // name may be optionally decorated
  1078. // replace with language token
  1079. bool bIsLocalizedFile = FixupNamespaceFilename( pLanguageString, srcFile, srcFile, sizeof( srcFile ) );
  1080. if ( bIsLocalizedFile )
  1081. {
  1082. // this is a localized file
  1083. if ( !pLanguageString[0] )
  1084. {
  1085. // ignore installing localized files when not configured for that language
  1086. continue;
  1087. }
  1088. FixupNamespaceFilename( pLanguageString, dstFile, dstFile, sizeof( dstFile ) );
  1089. }
  1090. // explicitly attempting to open file at source, same as overlapped copy to ensure i/o will succeed later
  1091. DWORD dwSrcSize;
  1092. if ( !DoesFileExist( srcFile, &dwSrcSize ) )
  1093. {
  1094. // can't validate source
  1095. return false;
  1096. }
  1097. // target file may exist, but aborted
  1098. bool bIsValid = IsTargetFileValid( dstFile, dwSrcSize );
  1099. if ( !bIsValid )
  1100. {
  1101. bForce = true;
  1102. }
  1103. if ( !bForce )
  1104. {
  1105. // the version must be present and match, otherwise full re-install
  1106. const char *pVersion = V_stristr( dstFile, "version" );
  1107. if ( pVersion )
  1108. {
  1109. // compare contents of version file
  1110. CUtlBuffer targetVersionBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1111. if ( !g_pFullFileSystem->ReadFile( dstFile, NULL, targetVersionBuffer ) )
  1112. {
  1113. return false;
  1114. }
  1115. if ( V_strcmp( (const char*)sourceVersionBuffer.Base(), (const char *)targetVersionBuffer.Base() ) )
  1116. {
  1117. // differing contents, full re-install
  1118. bForce = true;
  1119. }
  1120. }
  1121. }
  1122. int iIndex = m_InstallData.m_Files.AddToTail();
  1123. m_InstallData.m_Files[iIndex].m_SourceName = srcFile;
  1124. m_InstallData.m_Files[iIndex].m_TargetName = dstFile;
  1125. m_InstallData.m_Files[iIndex].m_nRealFileSize = dwSrcSize;
  1126. m_InstallData.m_Files[iIndex].m_bFileIsValid = bIsValid;
  1127. m_InstallData.m_nTotalSize += AlignValue( dwSrcSize, TARGET_SECTOR_SIZE );
  1128. }
  1129. // cannot support indeterminate installs, either all of its valid, or it gets wiped
  1130. // base on above logic, any invalid file would have set force
  1131. bool bValid = true;
  1132. if ( bForce )
  1133. {
  1134. // force discard all
  1135. // any invalid file will trigger a reformat
  1136. for ( int i = 0; i < m_InstallData.m_Files.Count(); i++ )
  1137. {
  1138. m_InstallData.m_Files[i].m_bFileIsValid = false;
  1139. }
  1140. bValid = false;
  1141. }
  1142. m_InstallData.m_bValid = bValid;
  1143. m_InstallData.m_bCompleted = bValid;
  1144. return true;
  1145. }
  1146. //-----------------------------------------------------------------------------
  1147. // Copies all install files to the hard drive
  1148. //-----------------------------------------------------------------------------
  1149. DWORD CXboxInstaller::InstallThreadFunc()
  1150. {
  1151. bool bSuccess;
  1152. m_CopyStats.m_InstallStartTime = GetTickCount();
  1153. // allocate buffers
  1154. for ( int i=0; i < INSTALL_NUM_BUFFERS; i++ )
  1155. {
  1156. m_pCopyBuffers[i] = new unsigned char[INSTALL_BUFFER_SIZE];
  1157. }
  1158. for ( int i = 0; i < m_InstallData.m_Files.Count(); ++i )
  1159. {
  1160. if ( m_bStopping )
  1161. {
  1162. break;
  1163. }
  1164. if ( m_InstallData.m_Files[i].m_bFileIsValid )
  1165. {
  1166. // this file is valid
  1167. m_CopyStats.m_BytesCopied += AlignValue( m_InstallData.m_Files[i].m_nRealFileSize, TARGET_SECTOR_SIZE );
  1168. continue;
  1169. }
  1170. bSuccess = CopyFileOverlapped( &m_InstallData.m_Files[i], &m_CopyStats );
  1171. if ( !bSuccess )
  1172. {
  1173. // quit
  1174. break;
  1175. }
  1176. }
  1177. // all files must be valid
  1178. bool bValid = true;
  1179. for ( int i = 0; i < m_InstallData.m_Files.Count(); ++i )
  1180. {
  1181. if ( !m_InstallData.m_Files[i].m_bFileIsValid )
  1182. {
  1183. bValid = false;
  1184. break;
  1185. }
  1186. }
  1187. if ( bValid )
  1188. {
  1189. // Despite what the docs say, which do NOT have our best interest,
  1190. // this will cause fragmentation if it is called frequently.
  1191. // We only call it ONCE here at the end of the successful install operation.
  1192. // The resume pattern must re-format and restart.
  1193. XFlushUtilityDrive();
  1194. }
  1195. // release buffers
  1196. for ( int i=0; i < INSTALL_NUM_BUFFERS; i++ )
  1197. {
  1198. delete [] m_pCopyBuffers[i];
  1199. }
  1200. m_CopyStats.m_InstallStopTime = GetTickCount();
  1201. // set when install operation is complete, regardless of error
  1202. m_InstallData.m_bValid = bValid;
  1203. m_InstallData.m_bCompleted = true;
  1204. m_InstallData.m_bFailed = (m_CopyStats.m_CopyErrors != 0);
  1205. return 0;
  1206. }
  1207. DWORD WINAPI InstallThreadFunc( LPVOID lpParam )
  1208. {
  1209. return g_XboxInstaller.InstallThreadFunc();
  1210. }
  1211. //-----------------------------------------------------------------------------
  1212. // Starts installation to disk.
  1213. //-----------------------------------------------------------------------------
  1214. bool CXboxInstaller::Start()
  1215. {
  1216. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  1217. // under demo conditions cannot allow any HDD access
  1218. return false;
  1219. #endif
  1220. if ( !m_bHasHDD || !m_InstallData.m_nTotalSize || m_InstallData.m_bValid || m_InstallData.m_bFailed )
  1221. {
  1222. // nothing to do, cannot be started or
  1223. // either completed or failed
  1224. return false;
  1225. }
  1226. if ( !xbox_install_allowed.GetBool() )
  1227. {
  1228. // artifical condition to disallow a qualified installation
  1229. return false;
  1230. }
  1231. if ( m_bStopping )
  1232. {
  1233. // we must be completely stopped before restarting
  1234. // force the stop process to complete
  1235. // this will cause a hitch if the time between Stop() and Start() is very short
  1236. IsStopped( true );
  1237. }
  1238. else if ( m_hInstallThread )
  1239. {
  1240. // already started
  1241. return true;
  1242. }
  1243. if ( !m_bInit )
  1244. {
  1245. m_bInit = true;
  1246. InitializeCriticalSection( &m_CriticalSection );
  1247. }
  1248. Msg( "Xbox Install: Starting...\n" );
  1249. // reset expected state
  1250. V_memset( &m_CopyStats, 0, sizeof( CopyStats_t ) );
  1251. m_InstallData.m_bCompleted = false;
  1252. if ( m_bStopping )
  1253. {
  1254. // Cannot support resuming
  1255. // reset any prior results
  1256. for ( int i = 0; i < m_InstallData.m_Files.Count(); i++ )
  1257. {
  1258. m_InstallData.m_Files[i].m_bFileIsValid = false;
  1259. }
  1260. // reformat prior to restart
  1261. // prevents ANY fragmentation
  1262. if ( !MountCachePartition( true ) )
  1263. {
  1264. return false;
  1265. }
  1266. }
  1267. m_bStopping = false;
  1268. m_bReadError = false;
  1269. m_bWriteError = false;
  1270. // Start the install thread
  1271. m_hInstallThread = CreateThread( NULL, 0, &::InstallThreadFunc, NULL, CREATE_SUSPENDED, 0 );
  1272. if ( !m_hInstallThread )
  1273. {
  1274. // failed, install operation is not running
  1275. m_InstallData.m_bCompleted = true;
  1276. m_InstallData.m_bFailed = true;
  1277. return false;
  1278. }
  1279. XSetThreadProcessor( m_hInstallThread, INSTALL_MANAGER_PROCESSOR );
  1280. ResumeThread( m_hInstallThread );
  1281. // started
  1282. return true;
  1283. }
  1284. //-----------------------------------------------------------------------------
  1285. // Poll for install stoppage. Optionally synchronously force the stop.
  1286. //-----------------------------------------------------------------------------
  1287. bool CXboxInstaller::IsStopped( bool bForceStop )
  1288. {
  1289. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  1290. // under demo conditions cannot allow any HDD access
  1291. // installer is not running and will never run
  1292. return true;
  1293. #endif
  1294. if ( !m_bHasHDD || !m_hInstallThread )
  1295. {
  1296. // not running or already stopped
  1297. return true;
  1298. }
  1299. if ( bForceStop )
  1300. {
  1301. // caller may not have invoked
  1302. // start the stopping procedure
  1303. Stop();
  1304. }
  1305. // poll or wait for the install thread to terminate
  1306. if ( m_hInstallThread )
  1307. {
  1308. DWORD dwValue = WaitForSingleObject( m_hInstallThread, bForceStop ? INFINITE : 0 );
  1309. if ( dwValue != WAIT_OBJECT_0 )
  1310. {
  1311. return false;
  1312. }
  1313. CloseHandle( m_hInstallThread );
  1314. m_hInstallThread = NULL;
  1315. }
  1316. Msg( "Xbox Install: Stopped.\n" );
  1317. return true;
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Signal to stop the install. Use IsStopped() to poll or synchronously stop.
  1321. //-----------------------------------------------------------------------------
  1322. void CXboxInstaller::Stop()
  1323. {
  1324. #if defined( _DEMO ) || !defined( SUPPORTS_INSTALL_TO_XBOX_HDD )
  1325. // under demo conditions cannot allow any HDD access
  1326. return;
  1327. #endif
  1328. if ( !m_bHasHDD )
  1329. {
  1330. return;
  1331. }
  1332. if ( m_bStopping )
  1333. {
  1334. // already stopping
  1335. return;
  1336. }
  1337. if ( !m_hInstallThread || m_InstallData.m_bCompleted || m_InstallData.m_bFailed )
  1338. {
  1339. // already stopped
  1340. return;
  1341. }
  1342. Msg( "Xbox Install: Stopping...\n" );
  1343. m_bStopping = true;
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Discards the setup.
  1347. //-----------------------------------------------------------------------------
  1348. void CXboxInstaller::ResetSetup()
  1349. {
  1350. V_memset( &m_CopyStats, 0, sizeof( CopyStats_t ) );
  1351. m_InstallData.Reset();
  1352. m_bHasHDD = false;
  1353. }
  1354. //-----------------------------------------------------------------------------
  1355. // Setup for install.
  1356. //-----------------------------------------------------------------------------
  1357. bool CXboxInstaller::Setup( bool bForceInstall )
  1358. {
  1359. if ( BuildInstallScript( bForceInstall ) )
  1360. {
  1361. m_bHasHDD = PrepareCachePartitionForInstall();
  1362. }
  1363. return m_bHasHDD;
  1364. }
  1365. bool CXboxInstaller::IsFullyInstalled()
  1366. {
  1367. return m_InstallData.m_bValid;
  1368. }
  1369. DWORD CXboxInstaller::GetTotalSize()
  1370. {
  1371. return m_InstallData.m_nTotalSize;
  1372. }
  1373. DWORD CXboxInstaller::GetVersion()
  1374. {
  1375. return m_InstallData.m_nVersion;
  1376. }
  1377. const CopyStats_t *CXboxInstaller::GetCopyStats()
  1378. {
  1379. return &m_CopyStats;
  1380. }
  1381. //-----------------------------------------------------------------------------
  1382. // Identifies that we may or can be installing.
  1383. // Cannot go TRUE unless the filesystem allows the install.
  1384. //-----------------------------------------------------------------------------
  1385. bool CXboxInstaller::IsInstallEnabled()
  1386. {
  1387. return m_bHasHDD;
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // Returns TRUE if the install has completed and a restart is desireable
  1391. // to achieve a remount.
  1392. //-----------------------------------------------------------------------------
  1393. bool CXboxInstaller::ShouldRestart()
  1394. {
  1395. // strong exact checking for paranoia reasons
  1396. if ( IsFullyInstalled() &&
  1397. !g_pFullFileSystem->IsInstalledToXboxHDDCache() &&
  1398. g_pFullFileSystem->IsDVDHosted() )
  1399. {
  1400. // Install is valid and..
  1401. // the game did not startup with the install already intact and...
  1402. // the game is running with its primary paths set to the DVD and..
  1403. return true;
  1404. }
  1405. return false;
  1406. }
  1407. bool CXboxInstaller::ForceCachePaths()
  1408. {
  1409. bool bDidFixup = false;
  1410. // restarting is valid also indicates we can patch the search paths
  1411. // when the image is already installed, but the search paths haven't been patched, do it now
  1412. if ( !m_bForcedCachePaths && ( ShouldRestart() || g_pFullFileSystem->IsInstalledToXboxHDDCache() ) )
  1413. {
  1414. // regardless of outcome, we only do it once
  1415. // if it didn't occur, too bad, we don't do it ever again
  1416. m_bForcedCachePaths = true;
  1417. bDidFixup = g_pFullFileSystem->FixupSearchPathsAfterInstall();
  1418. }
  1419. return bDidFixup;
  1420. }
  1421. void CXboxInstaller::SpewStatus()
  1422. {
  1423. Msg( "Install Status:\n" );
  1424. Msg( "Version: %d (%s) (Xbox)\n", GetVersion(), XBX_GetLanguageString() );
  1425. Msg( "DVD Hosted: %s\n", g_pFullFileSystem->IsDVDHosted() ? "Enabled" : "Disabled" );
  1426. if ( g_pFullFileSystem->IsInstalledToXboxHDDCache() )
  1427. {
  1428. Msg( "Existing Image Found.\n" );
  1429. }
  1430. if ( !IsInstallEnabled() || !xbox_install_allowed.GetBool() )
  1431. {
  1432. Msg( "Install Disabled.\n" );
  1433. }
  1434. if ( IsFullyInstalled() )
  1435. {
  1436. Msg( "Fully Installed.\n" );
  1437. }
  1438. Msg( "Progress: %d/%d MB\n", GetCopyStats()->m_BytesCopied/(1024*1024), GetTotalSize()/(1024*1024) );
  1439. }