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.

1262 lines
39 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "basefilesystem.h"
  7. #include "tier0/vprof.h"
  8. // NOTE: This has to be the last file included!
  9. #include "tier0/memdbgon.h"
  10. //-----------------------------------------------------------------------------
  11. // CFileTracker.
  12. //-----------------------------------------------------------------------------
  13. CFileTracker::CFileTracker( CBaseFileSystem *pFileSystem )
  14. {
  15. m_pFileSystem = pFileSystem;
  16. }
  17. CFileTracker::~CFileTracker()
  18. {
  19. Clear();
  20. }
  21. void CFileTracker::NoteFileLoadedFromDisk( const char *pFilename, const char *pPathID, FileHandle_t fp )
  22. {
  23. AUTO_LOCK( m_Mutex );
  24. if ( !pPathID )
  25. pPathID = "";
  26. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  27. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  28. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  29. {
  30. if ( pInfo )
  31. Warning( "(Duplicate): [%s]\\%s", pPathID, pFilename );
  32. else
  33. Warning( "(Unique ): [%s]\\%s", pPathID, pFilename );
  34. }
  35. if ( pInfo )
  36. {
  37. // Clear all the flags, but remember if we ever had a CRC.
  38. pInfo->m_Flags &= k_eFileFlagsGotCRCOnce;
  39. pInfo->m_Flags &= ~k_eFileFlagsFailedToLoadLastTime;
  40. }
  41. else
  42. {
  43. pInfo = pPath->AddFileInfo( pFilename );
  44. pInfo->m_Flags = k_eFileFlags_None;
  45. }
  46. if ( !fp )
  47. {
  48. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  49. {
  50. Warning( "\n" );
  51. }
  52. return;
  53. }
  54. // Remember that we calculated the CRC and that it is unverified.
  55. pInfo->m_CRC = CalculateCRCForFile( fp );
  56. pInfo->m_Flags |= k_eFileFlagsHasCRC | k_eFileFlagsGotCRCOnce;
  57. if ( pInfo->m_iNeedsVerificationListIndex == -1 )
  58. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  59. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  60. {
  61. Warning( " - %u\n", pInfo->m_CRC );
  62. }
  63. }
  64. void CFileTracker::NoteFileFailedToLoad( const char *pFilename, const char *pPathID )
  65. {
  66. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  67. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  68. if ( pInfo )
  69. {
  70. pInfo->m_Flags |= k_eFileFlagsFailedToLoadLastTime;
  71. }
  72. }
  73. CRC32_t CFileTracker::CalculateCRCForFile( FileHandle_t fp )
  74. {
  75. CRC32_t crc;
  76. // Calculate the CRC.
  77. unsigned int initialFilePos = m_pFileSystem->Tell( fp );
  78. m_pFileSystem->Seek( fp, 0, FILESYSTEM_SEEK_HEAD );
  79. #define CRC_CHUNK_SIZE (32*1024)
  80. char tempBuf[CRC_CHUNK_SIZE];
  81. CRC32_Init( &crc );
  82. unsigned int fileLength = m_pFileSystem->Size( fp );
  83. int nChunks = fileLength / CRC_CHUNK_SIZE + 1;
  84. unsigned int curStartByte = 0;
  85. for ( int iChunk=0; iChunk < nChunks; iChunk++ )
  86. {
  87. int curEndByte = MIN( curStartByte + CRC_CHUNK_SIZE, fileLength );
  88. int chunkLen = curEndByte - curStartByte;
  89. if ( chunkLen == 0 )
  90. break;
  91. m_pFileSystem->Read( tempBuf, chunkLen, fp ); // TODO: handle errors here..
  92. CRC32_ProcessBuffer( &crc, tempBuf, chunkLen );
  93. curStartByte += CRC_CHUNK_SIZE;
  94. }
  95. CRC32_Final( &crc );
  96. // Go back to where we started.
  97. m_pFileSystem->Seek( fp, initialFilePos, FILESYSTEM_SEEK_HEAD );
  98. return crc;
  99. }
  100. CFileInfo* CFileTracker::GetFileInfo( const char *pFilename, const char *pPathID )
  101. {
  102. AUTO_LOCK( m_Mutex );
  103. CPathIDFileList *pPath = GetPathIDFileList( pPathID, false );
  104. if ( !pPath )
  105. return NULL;
  106. return pPath->FindFileInfo( pFilename );
  107. }
  108. int CFileTracker::GetFileInfos( CFileInfo **ppFileInfos, int nMaxFileInfos, const char *pFilename )
  109. {
  110. AUTO_LOCK( m_Mutex );
  111. int nOut = 0;
  112. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  113. {
  114. CFileInfo *pCur = m_PathIDs[i]->FindFileInfo( pFilename );
  115. if ( pCur )
  116. {
  117. if ( nOut < nMaxFileInfos )
  118. {
  119. ppFileInfos[nOut++] = pCur;
  120. }
  121. else
  122. {
  123. Assert( !"CFileTracker::GetFileInfos - overflowed list!" );
  124. }
  125. }
  126. }
  127. return nOut;
  128. }
  129. void CFileTracker::NoteFileLoadedFromSteam( const char *pFilename, const char *pPathID, bool bForcedLoadFromSteam )
  130. {
  131. AUTO_LOCK( m_Mutex );
  132. if ( !pPathID )
  133. pPathID = "";
  134. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  135. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  136. if ( !pInfo )
  137. pInfo = pPath->AddFileInfo( pFilename );
  138. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  139. {
  140. Warning( "From Steam: [%s]\\%s\n", pPathID, pFilename );
  141. }
  142. pInfo->m_Flags = k_eFileFlagsLoadedFromSteam;
  143. if ( bForcedLoadFromSteam )
  144. pInfo->m_Flags |= k_eFileFlagsForcedLoadFromSteam;
  145. }
  146. void CFileTracker::CalculateMissingCRCs( IFileList *pWantCRCList )
  147. {
  148. // First build a list of files that need a CRC and don't have one.
  149. m_Mutex.Lock();
  150. CUtlLinkedList<CFileInfo*,int> needCRCList;
  151. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  152. {
  153. CPathIDFileList *pPath = m_PathIDs[i];
  154. int j;
  155. for ( j=pPath->m_Files.First(); j != pPath->m_Files.InvalidIndex(); j=pPath->m_Files.Next( j ) )
  156. {
  157. CFileInfo *pInfo = pPath->m_Files[j];
  158. if ( !( pInfo->m_Flags & k_eFileFlagsLoadedFromSteam ) && !( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  159. {
  160. // If the new "force match" list doesn't care whether the file has a CRC or not, then don't bother to calculate it.
  161. if ( !pWantCRCList->IsFileInList( pInfo->GetFilename() ) )
  162. continue;
  163. needCRCList.AddToTail( pInfo );
  164. }
  165. }
  166. }
  167. m_Mutex.Unlock();
  168. // Then, when the mutex is not locked, go generate the CRCs for them.
  169. FOR_EACH_LL( needCRCList, i )
  170. {
  171. CFileInfo *pInfo = needCRCList[i];
  172. CalculateMissingCRC( pInfo->GetFilename(), pInfo->GetPathIDString() );
  173. }
  174. }
  175. void CFileTracker::CacheFileCRC( const char *pPathID, const char *pRelativeFilename )
  176. {
  177. Assert( ThreadInMainThread() );
  178. // Get the file's info. Load the file if necessary.
  179. CFileInfo *pInfo = GetFileInfo( pRelativeFilename, pPathID );
  180. if ( !pInfo )
  181. {
  182. CalculateMissingCRC( pRelativeFilename, pPathID );
  183. pInfo = GetFileInfo( pRelativeFilename, pPathID );
  184. }
  185. if ( !pInfo )
  186. return;
  187. // Already cached a CRC for this file?
  188. if ( !( pInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  189. {
  190. // Ok, it's from disk but we don't have the CRC.
  191. CalculateMissingCRC( pInfo->GetFilename(), pInfo->GetPathIDString() );
  192. }
  193. }
  194. void CFileTracker::CacheFileCRC_Copy( const char *pPathID, const char *pRelativeFilename, const char *pPathIDToCopyFrom )
  195. {
  196. Assert( ThreadInMainThread() );
  197. // Get the file's info. Load the file if necessary.
  198. CFileInfo *pSourceInfo = GetFileInfo( pRelativeFilename, pPathIDToCopyFrom );
  199. if ( !pSourceInfo || !( pSourceInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  200. {
  201. // Strange, we don't have a CRC for the one they wanted to copy from, so calculate that CRC.
  202. CacheFileCRC( pPathIDToCopyFrom, pRelativeFilename );
  203. if ( !( pSourceInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  204. {
  205. // Still didn't get it. Ok.. well get a CRC for the target one anyway.
  206. CacheFileCRC( pPathID, pRelativeFilename );
  207. return;
  208. }
  209. }
  210. // Setup a CFileInfo for the target..
  211. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  212. CFileInfo *pDestInfo = pPath->FindFileInfo( pRelativeFilename );
  213. if ( !pDestInfo )
  214. pDestInfo = pPath->AddFileInfo( pRelativeFilename );
  215. pDestInfo->m_CRC = pSourceInfo->m_CRC;
  216. pDestInfo->m_Flags = pSourceInfo->m_Flags;
  217. }
  218. EFileCRCStatus CFileTracker::CheckCachedFileCRC( const char *pPathID, const char *pRelativeFilename, CRC32_t *pCRC )
  219. {
  220. Assert( ThreadInMainThread() );
  221. // Get the file's info. Load the file if necessary.
  222. CFileInfo *pInfo = GetFileInfo( pRelativeFilename, pPathID );
  223. if ( pInfo && (pInfo->m_Flags & k_eFileFlagsGotCRCOnce) )
  224. {
  225. *pCRC = pInfo->m_CRC;
  226. return k_eFileCRCStatus_GotCRC;
  227. }
  228. else
  229. {
  230. return k_eFileCRCStatus_CantOpenFile;
  231. }
  232. }
  233. void CFileTracker::CalculateMissingCRC( const char *pFilename, const char *pPathID )
  234. {
  235. //
  236. // Cache off this file's flags and restore it after the FindFileInSearchPaths call.
  237. //
  238. // This works around an exploit where they do this:
  239. // - Run a (local) listen server and load a hacked material
  240. // - Disconnect
  241. // - Delete (or rename) the hacked material file
  242. // - Connect to a server using sv_pure 1 and allow_from_disk+check_crc on the hacked file
  243. //
  244. // What happens is that it comes through here (CalculateMissingCRC) after getting the server's whitelist.
  245. // Then it calls FindFileInSearchPaths below and gets the file out of Steam,
  246. // ** which marks the file as k_eFileFlagsLoadedFromSteam **, so it doesn't give that file to
  247. // the server for the CRC check in GetUnverifiedCRCFiles.
  248. //
  249. // By preserving the flags here and not letting FindFileInSearchPaths modify it, we make sure that
  250. // we remember that the hacked file was loaded from disk.
  251. //
  252. int nOldFlags = -1;
  253. CFileInfo *pInfo = GetFileInfo( pFilename, pPathID );
  254. if ( pInfo )
  255. nOldFlags = pInfo->m_Flags;
  256. // Force it to make a CRC of disk files.
  257. FileHandle_t fh = m_pFileSystem->FindFileInSearchPaths( pFilename, "rb", pPathID, FSOPEN_FORCE_TRACK_CRC, NULL, true );
  258. if ( !fh )
  259. return;
  260. if ( pInfo )
  261. {
  262. // Restore the flags (see above for a description of why we do this).
  263. if ( nOldFlags != -1 )
  264. pInfo->m_Flags = nOldFlags;
  265. // Now we're about to modify the file itself.. lock the mutex.
  266. AUTO_LOCK( m_Mutex );
  267. // The FindFileInSearchPaths call might have done the CRC for us.
  268. if ( !( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  269. {
  270. pInfo->m_CRC = CalculateCRCForFile( fh );
  271. pInfo->m_Flags |= k_eFileFlagsHasCRC | k_eFileFlagsGotCRCOnce;
  272. if ( pInfo->m_iNeedsVerificationListIndex == -1 )
  273. {
  274. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  275. }
  276. }
  277. }
  278. else
  279. {
  280. Assert( false );
  281. }
  282. m_pFileSystem->Close( fh );
  283. }
  284. void CFileTracker::MarkAllCRCsUnverified()
  285. {
  286. AUTO_LOCK( m_Mutex );
  287. // First clear the 'needs verification' list.
  288. MarkAllCRCsVerified();
  289. Assert( m_NeedsVerificationList.Count() == 0 );
  290. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  291. {
  292. CPathIDFileList *pPath = m_PathIDs[i];
  293. int j;
  294. for ( j=pPath->m_Files.First(); j != pPath->m_Files.InvalidIndex(); j=pPath->m_Files.Next( j ) )
  295. {
  296. CFileInfo *pInfo = pPath->m_Files[j];
  297. if ( !(pInfo->m_Flags & k_eFileFlagsLoadedFromSteam) && ( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  298. {
  299. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  300. }
  301. }
  302. }
  303. }
  304. void CFileTracker::MarkAllCRCsVerified( bool bLockMutex )
  305. {
  306. if ( bLockMutex )
  307. m_Mutex.Lock();
  308. FOR_EACH_LL( m_NeedsVerificationList, i )
  309. {
  310. m_NeedsVerificationList[i]->m_iNeedsVerificationListIndex = -1;
  311. }
  312. m_NeedsVerificationList.Purge();
  313. if ( bLockMutex )
  314. m_Mutex.Unlock();
  315. }
  316. void CFileTracker::Clear()
  317. {
  318. AUTO_LOCK( m_Mutex );
  319. m_PathIDs.PurgeAndDeleteElements();
  320. }
  321. CPathIDFileList* CFileTracker::GetPathIDFileList( const char *pPathID, bool bAutoAdd )
  322. {
  323. AUTO_LOCK( m_Mutex );
  324. if ( !pPathID )
  325. pPathID = "";
  326. int i = m_PathIDs.Find( pPathID );
  327. if ( i == m_PathIDs.InvalidIndex() )
  328. {
  329. if ( bAutoAdd )
  330. {
  331. CPathIDFileList *pPath = new CPathIDFileList;
  332. pPath->m_PathID = pPathID;
  333. m_PathIDs.Insert( pPathID, pPath );
  334. return pPath;
  335. }
  336. else
  337. {
  338. return NULL;
  339. }
  340. }
  341. else
  342. {
  343. return m_PathIDs[i];
  344. }
  345. }
  346. //-----------------------------------------------------------------------------
  347. // CFileInfo implementation.
  348. //-----------------------------------------------------------------------------
  349. CFileInfo::CFileInfo()
  350. {
  351. m_iNeedsVerificationListIndex = -1;
  352. }
  353. CFileInfo::~CFileInfo()
  354. {
  355. }
  356. //-----------------------------------------------------------------------------
  357. // CPathIDFileList implementation..
  358. //-----------------------------------------------------------------------------
  359. CPathIDFileList::CPathIDFileList() : m_Files( k_eDictCompareTypeFilenames )
  360. {
  361. }
  362. CPathIDFileList::~CPathIDFileList()
  363. {
  364. m_Files.PurgeAndDeleteElements();
  365. }
  366. CFileInfo* CPathIDFileList::FindFileInfo( const char *pFilename )
  367. {
  368. Assert( !V_IsAbsolutePath( pFilename ) );
  369. int i = m_Files.Find( pFilename );
  370. if ( i == m_Files.InvalidIndex() )
  371. return NULL;
  372. else
  373. return m_Files[i];
  374. }
  375. CFileInfo* CPathIDFileList::AddFileInfo( const char *pFilename )
  376. {
  377. Assert( !V_IsAbsolutePath( pFilename ) );
  378. Assert( m_Files.Find( pFilename ) == m_Files.InvalidIndex() );
  379. CFileInfo *pFileInfo = new CFileInfo;
  380. pFileInfo->m_pPathIDFileList = this;
  381. pFileInfo->m_PathIDFileListDictIndex = m_Files.Insert( pFilename, pFileInfo );
  382. return pFileInfo;
  383. }
  384. #ifdef SUPPORT_VPK
  385. uintp ThreadStubProcessMD5Requests( void *pParam )
  386. {
  387. return ((CFileTracker2 *)pParam)->ThreadedProcessMD5Requests();
  388. }
  389. //-----------------------------------------------------------------------------
  390. // ThreadedProcessMD5Requests
  391. // Calculate the MD5s of all the blocks submitted to us
  392. //-----------------------------------------------------------------------------
  393. unsigned CFileTracker2::ThreadedProcessMD5Requests()
  394. {
  395. while ( m_bThreadShouldRun )
  396. {
  397. StuffToMD5_t stuff;
  398. while ( m_PendingJobs.PopItem( &stuff ) )
  399. {
  400. SNPROF( "ThreadProcessMD5Requests");
  401. MD5Context_t ctx;
  402. memset(&ctx, 0, sizeof(MD5Context_t));
  403. MD5Init(&ctx);
  404. MD5Update(&ctx, stuff.m_pubBuffer, stuff.m_cubBuffer );
  405. MD5Final( stuff.m_md5Value.bits, &ctx);
  406. TrackedVPKFile_t trackedVPKFileFind;
  407. trackedVPKFileFind.m_nPackFileNumber = stuff.m_nPackFileNumber;
  408. trackedVPKFileFind.m_PackFileID = stuff.m_PackFileID;
  409. trackedVPKFileFind.m_nFileFraction = stuff.m_nPackFileFraction;
  410. {
  411. // update the FileTracker MD5 database
  412. AUTO_LOCK( m_Mutex );
  413. int idxTrackedVPKFile = m_treeTrackedVPKFiles.Find( trackedVPKFileFind );
  414. if ( idxTrackedVPKFile != m_treeTrackedVPKFiles.InvalidIndex() )
  415. {
  416. TrackedVPKFile_t &trackedVPKFile = m_treeTrackedVPKFiles[idxTrackedVPKFile];
  417. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[trackedVPKFile.m_idxAllOpenedFiles];
  418. memcpy( trackedfile.m_filehashFinal.m_md5contents.bits, stuff.m_md5Value.bits, sizeof( trackedfile.m_filehashFinal.m_md5contents.bits ) );
  419. trackedfile.m_filehashFinal.m_crcIOSequence = stuff.m_cubBuffer;
  420. trackedfile.m_filehashFinal.m_cbFileLen = stuff.m_cubBuffer;
  421. trackedfile.m_filehashFinal.m_eFileHashType = FileHash_t::k_EFileHashTypeEntireFile;
  422. trackedfile.m_filehashFinal.m_nPackFileNumber = trackedVPKFileFind.m_nPackFileNumber;
  423. trackedfile.m_filehashFinal.m_PackFileID = trackedVPKFileFind.m_PackFileID;
  424. }
  425. }
  426. m_CompletedJobs.PushItem( stuff );
  427. m_threadEventWorkCompleted.Set();
  428. }
  429. m_threadEventWorkToDo.Wait( 1000 );
  430. }
  431. return 0;
  432. }
  433. //-----------------------------------------------------------------------------
  434. // SubmitThreadedMD5Request
  435. // add pubBuffer,cubBuffer to our queue of stuff to MD5
  436. // caller promises that the memory will remain valid
  437. // until BlockUntilMD5RequestComplete() is called
  438. // returns: request handle
  439. //-----------------------------------------------------------------------------
  440. int CFileTracker2::SubmitThreadedMD5Request( uint8 *pubBuffer, int cubBuffer, int PackFileID, int nPackFileNumber, int nPackFileFraction )
  441. {
  442. StuffToMD5_t stuff;
  443. int idxList;
  444. {
  445. AUTO_LOCK( m_Mutex );
  446. if ( !m_bComputeFileHashes )
  447. return 0;
  448. int idxAllFiles = -1;
  449. TrackedVPKFile_t trackedVPKFileFind;
  450. trackedVPKFileFind.m_nPackFileNumber = nPackFileNumber;
  451. trackedVPKFileFind.m_PackFileID = PackFileID;
  452. trackedVPKFileFind.m_nFileFraction = nPackFileFraction;
  453. int idxTrackedVPKFile = m_treeTrackedVPKFiles.Find( trackedVPKFileFind );
  454. if ( idxTrackedVPKFile != m_treeTrackedVPKFiles.InvalidIndex() )
  455. {
  456. TrackedVPKFile_t &trackedVPKFile = m_treeTrackedVPKFiles[idxTrackedVPKFile];
  457. idxAllFiles = trackedVPKFile.m_idxAllOpenedFiles;
  458. // dont early out if we have already done the MD5, if the caller wants us
  459. // to do it again - then do it again
  460. }
  461. else
  462. {
  463. // this is an error, we should already know about the file
  464. return 0;
  465. }
  466. SubmittedMd5Job_t submittedjob;
  467. submittedjob.m_bFinished = false;
  468. idxList = m_SubmittedJobs.AddToTail( submittedjob );
  469. stuff.m_pubBuffer = pubBuffer;
  470. stuff.m_cubBuffer = cubBuffer;
  471. stuff.m_PackFileID = PackFileID;
  472. stuff.m_nPackFileNumber = nPackFileNumber;
  473. stuff.m_nPackFileFraction = nPackFileFraction;
  474. stuff.m_idxListSubmittedJobs = idxList;
  475. }
  476. // submit the work
  477. m_PendingJobs.PushItem( stuff );
  478. m_threadEventWorkToDo.Set();
  479. return idxList + 1;
  480. }
  481. //-----------------------------------------------------------------------------
  482. // IsMD5RequestComplete
  483. // is request identified by iRequest finished?
  484. // ( the caller wants to free the memory, but now must wait until we finish
  485. // calculating the MD5 )
  486. //-----------------------------------------------------------------------------
  487. bool CFileTracker2::IsMD5RequestComplete( int iRequest, MD5Value_t *pMd5ValueOut )
  488. {
  489. AUTO_LOCK( m_Mutex );
  490. int idxListWaiting = iRequest - 1;
  491. // deal with all completed jobs
  492. StuffToMD5_t stuff;
  493. while ( m_CompletedJobs.PopItem( &stuff ) )
  494. {
  495. int idxList = stuff.m_idxListSubmittedJobs;
  496. Q_memcpy( &m_SubmittedJobs[idxList].m_md5Value, &stuff.m_md5Value, sizeof( MD5Value_t ) );
  497. m_SubmittedJobs[idxList].m_bFinished = true;
  498. }
  499. // did the one we wanted finish?
  500. if ( m_SubmittedJobs[idxListWaiting].m_bFinished )
  501. {
  502. Q_memcpy( pMd5ValueOut, &m_SubmittedJobs[idxListWaiting].m_md5Value, sizeof( MD5Value_t ) );
  503. // you can not ask again, we have removed it from the list
  504. m_SubmittedJobs.Remove(idxListWaiting);
  505. return true;
  506. }
  507. // not done yet
  508. return false;
  509. }
  510. //-----------------------------------------------------------------------------
  511. // BlockUntilMD5RequestComplete
  512. // block until request identified by iRequest is finished
  513. // ( the caller wants to free the memory, but now must wait until we finish
  514. // calculating the MD5 )
  515. //-----------------------------------------------------------------------------
  516. bool CFileTracker2::BlockUntilMD5RequestComplete( int iRequest, MD5Value_t *pMd5ValueOut )
  517. {
  518. while ( 1 )
  519. {
  520. if ( IsMD5RequestComplete( iRequest, pMd5ValueOut ) )
  521. return true;
  522. m_cThreadBlocks++;
  523. m_threadEventWorkCompleted.Wait( 100 );
  524. }
  525. return false;
  526. }
  527. #endif
  528. // CFileTracker2 will replace most of CFileTracker soon
  529. CFileTracker2::CFileTracker2( CBaseFileSystem *pFileSystem ):
  530. m_mapAllOpenFiles( DefLessFunc(FILE *) ),
  531. m_treeAllOpenedFiles( TrackedFile_t::Less ),
  532. m_treeFileInVPK( FileInVPK_t::Less ),
  533. m_treeTrackedVPKFiles( TrackedVPKFile_t::Less )
  534. {
  535. m_pFileSystem = pFileSystem;
  536. m_cMissedReads = 0;
  537. m_bComputeFileHashes = true;
  538. #ifdef SUPPORT_VPK
  539. m_cThreadBlocks = 0;
  540. m_bThreadShouldRun = true;
  541. m_hWorkThread = NULL;
  542. #endif
  543. }
  544. CFileTracker2::~CFileTracker2()
  545. {
  546. #ifdef SUPPORT_VPK
  547. Assert(!m_bThreadShouldRun);
  548. Assert(m_hWorkThread==NULL);
  549. #endif
  550. }
  551. void CFileTracker2::InitAsyncThread()
  552. {
  553. #ifdef SUPPORT_VPK
  554. Assert( m_hWorkThread == NULL );
  555. if ( m_hWorkThread == NULL )
  556. m_hWorkThread = CreateSimpleThread( ThreadStubProcessMD5Requests, this );
  557. #endif
  558. }
  559. void CFileTracker2::ShutdownAsync()
  560. {
  561. #ifdef SUPPORT_VPK
  562. m_bThreadShouldRun = false;
  563. m_threadEventWorkToDo.Set();
  564. if ( m_hWorkThread )
  565. {
  566. // wait for it to die
  567. ThreadJoin( m_hWorkThread );
  568. ReleaseThreadHandle( m_hWorkThread );
  569. m_hWorkThread = NULL;
  570. }
  571. #endif
  572. }
  573. void CFileTracker2::MarkAllCRCsUnverified()
  574. {
  575. AUTO_LOCK( m_Mutex );
  576. m_RecentFileList.Purge();
  577. Assert( m_RecentFileList.Count() == 0 );
  578. // every m_idxRecentFileList in m_treeAllOpenedFiles is bad now
  579. // this is going to hit every file we have *ever* loaded. how do we prune this list?
  580. // lets send VPK files first, then everything else
  581. for ( int i = m_treeAllOpenedFiles.FirstInorder(); i != m_treeAllOpenedFiles.InvalidIndex(); i = m_treeAllOpenedFiles.NextInorder( i ) )
  582. {
  583. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[i];
  584. // skip these for now
  585. if ( trackedfile.m_filehashInProgress.m_cbFileLen == 0 && trackedfile.m_filehashFinal.m_cbFileLen == 0 )
  586. trackedfile.m_idxRecentFileList = m_RecentFileList.InvalidIndex();
  587. else if ( trackedfile.m_bPackOrVPKFile )
  588. trackedfile.m_idxRecentFileList = m_RecentFileList.AddToHead( i );
  589. else if ( !trackedfile.m_bFileInVPK && !trackedfile.m_path.IsEmpty() )
  590. trackedfile.m_idxRecentFileList = m_RecentFileList.AddToTail( i );
  591. else
  592. {
  593. trackedfile.m_idxRecentFileList = m_RecentFileList.InvalidIndex();
  594. }
  595. }
  596. // do it again for any we skipped because we had not yet read the data
  597. for ( int i = m_treeAllOpenedFiles.FirstInorder(); i != m_treeAllOpenedFiles.InvalidIndex(); i = m_treeAllOpenedFiles.NextInorder( i ) )
  598. {
  599. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[i];
  600. if ( trackedfile.m_idxRecentFileList != m_RecentFileList.InvalidIndex() )
  601. continue;
  602. if ( trackedfile.m_bPackOrVPKFile )
  603. trackedfile.m_idxRecentFileList = m_RecentFileList.AddToTail( i );
  604. else if ( !trackedfile.m_bFileInVPK && !trackedfile.m_path.IsEmpty() )
  605. trackedfile.m_idxRecentFileList = m_RecentFileList.AddToTail( i );
  606. else
  607. {
  608. trackedfile.m_idxRecentFileList = m_RecentFileList.InvalidIndex();
  609. }
  610. }
  611. }
  612. int CFileTracker2::GetUnverifiedFileHashes( CUnverifiedFileHash *pFiles, int nMaxFiles )
  613. {
  614. Assert( nMaxFiles > 0 );
  615. AUTO_LOCK( m_Mutex );
  616. // We send all files regardless of the whitelist, let the server figure it out
  617. int iOutFile = 0;
  618. while ( m_RecentFileList.Head() != m_RecentFileList.InvalidIndex() )
  619. {
  620. // pop off the head
  621. int i = m_RecentFileList.Head();
  622. int idx = m_RecentFileList[i];
  623. TrackedFile_t &file = m_treeAllOpenedFiles[idx];
  624. // we could be in the degenerate case of a file that has been opened but we have not yet read any bytes
  625. if ( file.m_filehashInProgress.m_cbFileLen == 0 && file.m_filehashFinal.m_cbFileLen == 0 )
  626. {
  627. // put it back to the end of the line
  628. m_RecentFileList.Remove( i );
  629. file.m_idxRecentFileList = m_RecentFileList.AddToTail( idx );
  630. break;
  631. }
  632. // ok we have a good file - proceed
  633. m_RecentFileList.Remove( i );
  634. // if the file has no "path" associated, then it was some raw file open that we do not care about
  635. if ( file.m_path.IsEmpty() )
  636. continue;
  637. file.m_idxRecentFileList = m_RecentFileList.InvalidIndex();
  638. // Add this file to their list.
  639. CUnverifiedFileHash *pOutFile = &pFiles[iOutFile];
  640. V_strncpy( pOutFile->m_Filename, file.m_filename.String(), sizeof( pOutFile->m_Filename ) );
  641. V_strncpy( pOutFile->m_PathID, file.m_path.String(), sizeof( pOutFile->m_PathID ) );
  642. pOutFile->m_nFileFraction = file.m_nFileFraction;
  643. file.GetCRCValues( &pOutFile->m_FileHash );
  644. ++iOutFile;
  645. if ( iOutFile >= nMaxFiles )
  646. break;
  647. }
  648. return iOutFile;
  649. }
  650. EFileCRCStatus CFileTracker2::CheckCachedFileHash( const char *pPathID, const char *pRelativeFilename, int nFileFraction, FileHash_t *pFileHash )
  651. {
  652. Assert( ThreadInMainThread() );
  653. AUTO_LOCK( m_Mutex );
  654. CRC32_t crcFilename;
  655. CRC32_Init( &crcFilename );
  656. CRC32_ProcessBuffer( &crcFilename, pRelativeFilename, Q_strlen( pRelativeFilename ) );
  657. CRC32_ProcessBuffer( &crcFilename, pPathID, Q_strlen( pPathID ) );
  658. CRC32_Final( &crcFilename );
  659. TrackedFile_t trackedfileFind;
  660. trackedfileFind.m_filename = pRelativeFilename;
  661. trackedfileFind.m_path = pPathID;
  662. trackedfileFind.m_crcIdentifier = crcFilename;
  663. trackedfileFind.m_nFileFraction = nFileFraction;
  664. int idx = m_treeAllOpenedFiles.Find( trackedfileFind );
  665. if ( idx != m_treeAllOpenedFiles.InvalidIndex() )
  666. {
  667. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idx];
  668. if ( trackedfile.m_bFileInVPK )
  669. {
  670. // the FileHash is not meaningful, because the file is in a VPK, we have hashed the entire VPK
  671. // if the user is sending us a hash for this file, it means he has extracted it from the VPK and tricked the client into loading it
  672. // instead of the version in the VPK.
  673. return k_eFileCRCStatus_FileInVPK;
  674. }
  675. trackedfile.GetCRCValues( pFileHash );
  676. return k_eFileCRCStatus_GotCRC;
  677. }
  678. else
  679. {
  680. return k_eFileCRCStatus_CantOpenFile;
  681. }
  682. }
  683. bool TrackedFile_t::GetCRCValues( FileHash_t *pFileHash )
  684. {
  685. if ( m_filehashFinal.m_eFileHashType != FileHash_t::k_EFileHashTypeEntireFile )
  686. {
  687. MD5Context_t ctx;
  688. Q_memcpy( &ctx, &m_md5ctx, sizeof( ctx ));
  689. MD5Final( pFileHash->m_md5contents.bits, &ctx );
  690. CRC32_t crcT = m_filehashInProgress.m_crcIOSequence;
  691. CRC32_Final( &crcT );
  692. pFileHash->m_crcIOSequence = crcT;
  693. pFileHash->m_eFileHashType = FileHash_t::k_EFileHashTypeIncompleteFile;
  694. pFileHash->m_cbFileLen = m_filehashInProgress.m_cbFileLen;
  695. pFileHash->m_nPackFileNumber = m_filehashInProgress.m_nPackFileNumber;
  696. pFileHash->m_PackFileID = m_filehashInProgress.m_PackFileID;
  697. }
  698. else
  699. {
  700. Q_memcpy( pFileHash->m_md5contents.bits, m_filehashFinal.m_md5contents.bits, sizeof(m_filehashFinal.m_md5contents));
  701. pFileHash->m_crcIOSequence = m_filehashFinal.m_crcIOSequence;
  702. pFileHash->m_eFileHashType = m_filehashFinal.m_eFileHashType;
  703. pFileHash->m_cbFileLen = m_filehashFinal.m_cbFileLen;
  704. pFileHash->m_nPackFileNumber = m_filehashFinal.m_nPackFileNumber;
  705. pFileHash->m_PackFileID = m_filehashFinal.m_PackFileID;
  706. }
  707. return true;
  708. }
  709. void TrackedFile_t::ProcessFileRead( void *dest, size_t nBytesRead )
  710. {
  711. if ( m_filehashFinal.m_eFileHashType != FileHash_t::k_EFileHashTypeEntireFile )
  712. {
  713. m_filehashInProgress.m_cbFileLen += nBytesRead;
  714. // if this single read is the entire file - discard any previous partial checksum
  715. if ( m_nFilePos == 0 && nBytesRead == m_nLength )
  716. {
  717. memset(&m_md5ctx, 0, sizeof(m_md5ctx));
  718. MD5Init( &m_md5ctx );
  719. }
  720. MD5Update( &m_md5ctx, (unsigned char *)dest, nBytesRead );
  721. if ( m_nFilePos == m_cubSequentialRead )
  722. m_cubSequentialRead += nBytesRead;
  723. if (( m_nFilePos == 0 && nBytesRead == m_nLength ) ||
  724. ( m_cubSequentialRead == m_nLength && m_filehashInProgress.m_cbFileLen == m_nLength ) )
  725. {
  726. // we have now hashed the entire file - mark it done and never touch it again
  727. MD5Final( m_filehashInProgress.m_md5contents.bits, &m_md5ctx );
  728. // its not a CRC in this case - its the actual length
  729. m_filehashInProgress.m_crcIOSequence = m_nLength;
  730. m_filehashInProgress.m_eFileHashType = FileHash_t::k_EFileHashTypeEntireFile;
  731. Q_memcpy( &m_filehashFinal, &m_filehashInProgress, sizeof( m_filehashFinal ) );
  732. }
  733. else
  734. {
  735. CRC32_ProcessBuffer( &m_filehashInProgress.m_crcIOSequence, &m_nFilePos, sizeof(int64) );
  736. }
  737. m_nFilePos += nBytesRead;
  738. }
  739. }
  740. #ifdef SUPPORT_VPK
  741. void CFileTracker2::NotePackFileAccess( const char *pFilename, const char *pPathID, CPackedStoreFileHandle &VPKHandle )
  742. {
  743. #if !defined( _GAMECONSOLE )
  744. AUTO_LOCK( m_Mutex );
  745. int idxFileVPK = VPKHandle.m_pOwner->m_PackFileID - 1;
  746. // we must have seen the VPK file first
  747. if ( !m_treeAllOpenedFiles.IsValidIndex( idxFileVPK ) )
  748. return;
  749. int idxFile = IdxFileFromName( pFilename, pPathID, 0, VPKHandle.m_nFileSize, false, false );
  750. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idxFile];
  751. // we could use the CRC data from the VPK header - and verify it
  752. // VPKHandle.GetFileCRCFromHeaderData();
  753. // for now all we are going to do is track that this file came from a VPK
  754. trackedfile.m_nLength = VPKHandle.m_nFileSize;
  755. trackedfile.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  756. trackedfile.m_nPackFileNumber = VPKHandle.m_nFileNumber; // this might be useful to send up
  757. trackedfile.m_bFileInVPK = true;
  758. FileInVPK_t fiv;
  759. fiv.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  760. fiv.m_nPackFileNumber = VPKHandle.m_nFileNumber;
  761. fiv.m_nFileOffset = VPKHandle.m_nFileOffset;
  762. fiv.m_idxAllOpenedFiles = idxFile;
  763. int idxFileInVPK = m_treeFileInVPK.Find( fiv );
  764. if ( idxFileInVPK == m_treeFileInVPK.InvalidIndex() )
  765. {
  766. idxFileInVPK = m_treeFileInVPK.Insert( fiv );
  767. }
  768. TrackedVPKFile_t trackedVPKFileFind;
  769. trackedVPKFileFind.m_nPackFileNumber = VPKHandle.m_nFileNumber;
  770. trackedVPKFileFind.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  771. trackedVPKFileFind.m_nFileFraction = VPKHandle.m_nFileOffset & k_nFileFractionMask;
  772. int nFileEnd = ( VPKHandle.m_nFileOffset+VPKHandle.m_nFileSize ) & k_nFileFractionMask;
  773. // if it straddles the 1MB boundary, record all
  774. while ( trackedVPKFileFind.m_nFileFraction <= nFileEnd )
  775. {
  776. int idxAllFiles = m_treeAllOpenedFiles.InvalidIndex();
  777. int idxTrackedVPKFile = m_treeTrackedVPKFiles.Find( trackedVPKFileFind );
  778. if ( idxTrackedVPKFile == m_treeTrackedVPKFiles.InvalidIndex() )
  779. {
  780. char szDataFileName[MAX_PATH];
  781. VPKHandle.GetPackFileName( szDataFileName, sizeof(szDataFileName) );
  782. const char *pszFileName = V_GetFileName( szDataFileName );
  783. idxAllFiles = trackedVPKFileFind.m_idxAllOpenedFiles = IdxFileFromName( pszFileName, "GAME", trackedVPKFileFind.m_nFileFraction, 0, true, true );
  784. idxTrackedVPKFile = m_treeTrackedVPKFiles.Insert( trackedVPKFileFind );
  785. }
  786. trackedVPKFileFind.m_nFileFraction += k_nFileFractionSize;
  787. }
  788. #endif
  789. }
  790. #endif
  791. #ifdef SUPPORT_VPK
  792. // MD5 VPK files in 1MB chunks
  793. void CFileTracker2::NotePackFileRead( CPackedStoreFileHandle &VPKHandle, void *pBuffer, int nReadLength )
  794. {
  795. // This is all a no-op because we are using the VPK SubmitThreadedMD5Request API
  796. #if 0
  797. AUTO_LOCK( m_Mutex );
  798. TrackedVPKFile_t trackedVPKFileFind;
  799. trackedVPKFileFind.m_nPackFileNumber = VPKHandle.m_nFileNumber;
  800. trackedVPKFileFind.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  801. // what should we do about a file that straddles the 1MB boundary?
  802. trackedVPKFileFind.m_nFileFraction = VPKHandle.m_nFileOffset & k_nFileFractionMask;
  803. int idxAllFiles = m_treeAllOpenedFiles.InvalidIndex();
  804. int idxTrackedVPKFile = m_treeTrackedVPKFiles.Find( trackedVPKFileFind );
  805. if ( idxTrackedVPKFile != m_treeTrackedVPKFiles.InvalidIndex() )
  806. {
  807. TrackedVPKFile_t &trackedVPKFile = m_treeTrackedVPKFiles[idxTrackedVPKFile];
  808. idxAllFiles = trackedVPKFile.m_idxAllOpenedFiles;
  809. }
  810. else
  811. {
  812. char szDataFileName[MAX_PATH];
  813. VPKHandle.GetPackFileName( szDataFileName, sizeof(szDataFileName) );
  814. const char *pszFileName = V_GetFileName( szDataFileName );
  815. idxAllFiles = trackedVPKFileFind.m_idxAllOpenedFiles = IdxFileFromName( pszFileName, "GAME", trackedVPKFileFind.m_nFileFraction, 0, true, true );
  816. idxTrackedVPKFile = m_treeTrackedVPKFiles.Insert( trackedVPKFileFind );
  817. }
  818. if ( idxAllFiles != m_treeAllOpenedFiles.InvalidIndex() )
  819. {
  820. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idxAllFiles];
  821. // if we have never hashed this before - do it now
  822. if ( trackedfile.m_filehashFinal.m_eFileHashType != FileHash_t::k_EFileHashTypeEntireFile )
  823. {
  824. int64 fileSize;
  825. VPKHandle.HashEntirePackFile( fileSize, trackedVPKFileFind.m_nFileFraction, k_nFileFractionSize, trackedfile.m_filehashFinal );
  826. trackedfile.m_nLength = fileSize;
  827. }
  828. trackedfile.m_cubTotalRead += nReadLength;
  829. }
  830. #endif
  831. #if 0
  832. // we could verify the CRC from the VPK here.
  833. // we would need to compute a CRC - not an MD5
  834. FileInVPK_t fiv;
  835. fiv.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  836. fiv.m_nPackFileNumber = VPKHandle.m_nFileNumber;
  837. fiv.m_nFileOffset = VPKHandle.m_nFileOffset;
  838. int idxFileInVPK = m_treeFileInVPK.Find( fiv );
  839. if ( idxFileInVPK != m_treeFileInVPK.InvalidIndex() )
  840. {
  841. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[m_treeFileInVPK[idxFileInVPK].m_idxAllOpenedFiles];
  842. // back into the current file position
  843. trackedfile.m_nFilePos = VPKHandle.m_nCurrentFileOffset - nReadLength;
  844. trackedfile.ProcessFileRead( pBuffer, nReadLength );
  845. if ( VPKHandle.m_nMetaDataSize == 0 &&
  846. trackedfile.m_eFileHashType == FileHash_t::k_EFileHashTypeEntireFile &&
  847. trackedfile.m_crcFinal != VPKHandle.GetFileCRCFromHeaderData() )
  848. {
  849. // this should match? why doesn't it match
  850. }
  851. }
  852. #endif
  853. }
  854. #endif
  855. #ifdef SUPPORT_VPK
  856. // This is used by the dedicated server
  857. void CFileTracker2::AddFileHashForVPKFile( int nPackFileNumber, int nFileFraction, int cbFileLen, MD5Value_t &md5, CPackedStoreFileHandle &VPKHandle )
  858. {
  859. AUTO_LOCK( m_Mutex );
  860. m_bComputeFileHashes = false; // since we trust the hashes given here, do not recompute them later
  861. char szDataFileName[MAX_PATH];
  862. VPKHandle.m_nFileNumber = nPackFileNumber;
  863. VPKHandle.GetPackFileName( szDataFileName, sizeof(szDataFileName) );
  864. const char *pszFileName = V_GetFileName( szDataFileName );
  865. TrackedVPKFile_t trackedVPKFile;
  866. trackedVPKFile.m_nPackFileNumber = VPKHandle.m_nFileNumber;
  867. trackedVPKFile.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  868. trackedVPKFile.m_nFileFraction = nFileFraction;
  869. int idxAllFiles = trackedVPKFile.m_idxAllOpenedFiles = IdxFileFromName( pszFileName, "GAME", nFileFraction, 0, true, true );
  870. if ( idxAllFiles != m_treeAllOpenedFiles.InvalidIndex() )
  871. {
  872. m_treeTrackedVPKFiles.Insert( trackedVPKFile );
  873. }
  874. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idxAllFiles];
  875. trackedfile.m_bFileInVPK = false;
  876. trackedfile.m_bPackOrVPKFile = true;
  877. trackedfile.m_nLength = k_nFileFractionSize;
  878. trackedfile.m_filehashFinal.m_cbFileLen = cbFileLen;
  879. trackedfile.m_filehashFinal.m_eFileHashType = FileHash_t::k_EFileHashTypeEntireFile;
  880. trackedfile.m_filehashFinal.m_nPackFileNumber = nPackFileNumber;
  881. trackedfile.m_filehashFinal.m_PackFileID = VPKHandle.m_pOwner->m_PackFileID;
  882. trackedfile.m_filehashFinal.m_crcIOSequence = cbFileLen;
  883. Q_memcpy( trackedfile.m_filehashFinal.m_md5contents.bits, md5.bits, sizeof( md5.bits) );
  884. }
  885. #endif
  886. int CFileTracker2::IdxFileFromName( const char *pFilename, const char *pPathID, int nFileFraction, int64 nLength, bool bPackOrVPKFile, bool bRecordInRecentList )
  887. {
  888. TrackedFile_t trackedfile;
  889. trackedfile.RebuildFileName( pFilename, pPathID );
  890. trackedfile.m_nLength = nLength;
  891. trackedfile.m_bPackOrVPKFile = bPackOrVPKFile;
  892. trackedfile.m_nFileFraction = nFileFraction;
  893. MD5Init( &trackedfile.m_md5ctx );
  894. CRC32_Init( &trackedfile.m_filehashInProgress.m_crcIOSequence );
  895. trackedfile.m_idxRecentFileList = m_RecentFileList.InvalidIndex();
  896. int idxFile = m_treeAllOpenedFiles.Find( trackedfile );
  897. if ( idxFile == m_treeAllOpenedFiles.InvalidIndex() )
  898. {
  899. idxFile = m_treeAllOpenedFiles.Insert( trackedfile );
  900. if ( bRecordInRecentList )
  901. m_treeAllOpenedFiles[idxFile].m_idxRecentFileList = m_RecentFileList.AddToTail( idxFile );
  902. }
  903. else
  904. {
  905. if ( bRecordInRecentList )
  906. {
  907. if ( m_treeAllOpenedFiles[idxFile].m_idxRecentFileList == m_RecentFileList.InvalidIndex() )
  908. m_treeAllOpenedFiles[idxFile].m_idxRecentFileList = m_RecentFileList.AddToTail( idxFile );
  909. }
  910. }
  911. return idxFile;
  912. }
  913. #ifdef SUPPORT_VPK
  914. int CFileTracker2::NotePackFileOpened( const char *pRawFileName, const char *pFilename, const char *pPathID, int64 nLength )
  915. {
  916. #if !defined( _GAMECONSOLE )
  917. AUTO_LOCK( m_Mutex );
  918. TrackedFile_t trackedfileToFind;
  919. trackedfileToFind.RebuildFileName( pRawFileName, NULL );
  920. int idxFile = m_treeAllOpenedFiles.Find( trackedfileToFind );
  921. if ( idxFile != m_treeAllOpenedFiles.InvalidIndex() )
  922. {
  923. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idxFile];
  924. // we have the real name we want to use. correct the name
  925. trackedfile.RebuildFileName( pFilename, pPathID );
  926. trackedfile.m_bPackOrVPKFile = true;
  927. trackedfile.m_PackFileID = idxFile + 1;
  928. trackedfile.m_filehashFinal.m_PackFileID = trackedfile.m_PackFileID;
  929. trackedfile.m_filehashFinal.m_nPackFileNumber = -1;
  930. trackedfile.m_filehashInProgress.m_PackFileID = trackedfile.m_PackFileID;
  931. trackedfile.m_filehashInProgress.m_nPackFileNumber = -1;
  932. m_treeAllOpenedFiles.Reinsert( idxFile );
  933. }
  934. return idxFile + 1;
  935. #else
  936. return 0;
  937. #endif
  938. }
  939. #endif
  940. void CFileTracker2::NoteFileLoadedFromDisk( const char *pFilename, const char *pPathID, FILE *fp, int64 nLength )
  941. {
  942. #if !defined( _GAMECONSOLE )
  943. AUTO_LOCK( m_Mutex );
  944. int idxFile = IdxFileFromName( pFilename, pPathID, 0, nLength, false, true );
  945. m_mapAllOpenFiles.Insert( fp, idxFile );
  946. #endif
  947. }
  948. void CFileTracker2::RecordFileClose( FILE *fp )
  949. {
  950. #if !defined( _GAMECONSOLE )
  951. //VPROF_BUDGET("CFileTracker2::RecordFileClose", "PureFileTracker2");
  952. AUTO_LOCK( m_Mutex );
  953. int idx = m_mapAllOpenFiles.Find( fp );
  954. if ( idx != m_mapAllOpenFiles.InvalidIndex() )
  955. {
  956. int idx2 = m_mapAllOpenFiles[idx];
  957. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idx2];
  958. // don't touch the CRCs, we will just keep CRCing every byte we load, even if we open/close the file multiple times
  959. // note that if we successfully read the entire file - it is in m_crcFinal
  960. trackedfile.m_nFilePos = 0;
  961. trackedfile.m_cubSequentialRead = 0;
  962. m_mapAllOpenFiles.RemoveAt( idx );
  963. }
  964. #endif
  965. }
  966. void CFileTracker2::RecordFileSeek( FILE *fp, int64 pos, int seekType )
  967. {
  968. #if !defined( _GAMECONSOLE )
  969. AUTO_LOCK( m_Mutex );
  970. int idx = m_mapAllOpenFiles.Find( fp );
  971. if ( idx != m_mapAllOpenFiles.InvalidIndex() )
  972. {
  973. int idx2 = m_mapAllOpenFiles[idx];
  974. if ( idx2 != m_treeAllOpenedFiles.InvalidIndex() )
  975. {
  976. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idx2];
  977. // if we have hashed the entire file already - we don't need to do it again
  978. if ( trackedfile.m_filehashFinal.m_eFileHashType != FileHash_t::k_EFileHashTypeEntireFile )
  979. {
  980. if ( seekType == FILESYSTEM_SEEK_HEAD )
  981. trackedfile.m_nFilePos = pos;
  982. else if ( seekType == FILESYSTEM_SEEK_TAIL )
  983. trackedfile.m_nFilePos = trackedfile.m_nLength - pos;
  984. else if ( seekType == FILESYSTEM_SEEK_CURRENT )
  985. trackedfile.m_nFilePos = trackedfile.m_nFilePos + pos;
  986. CRC32_ProcessBuffer( &trackedfile.m_filehashInProgress.m_crcIOSequence, &pos, sizeof(int64) );
  987. CRC32_ProcessBuffer( &trackedfile.m_filehashInProgress.m_crcIOSequence, &seekType, sizeof(int) );
  988. }
  989. }
  990. }
  991. #endif
  992. }
  993. void CFileTracker2::RecordFileRead( void *dest, size_t nBytesRead, size_t nBytesRequested, FILE *fp )
  994. {
  995. #if !defined( _GAMECONSOLE )
  996. //VPROF_BUDGET("CFileTracker2::RecordFileRead", "PureFileTracker2");
  997. AUTO_LOCK( m_Mutex );
  998. int idx = m_mapAllOpenFiles.Find( fp );
  999. if ( idx != m_mapAllOpenFiles.InvalidIndex() )
  1000. {
  1001. int idx2 = m_mapAllOpenFiles[idx];
  1002. if ( idx2 != m_treeAllOpenedFiles.InvalidIndex() )
  1003. {
  1004. TrackedFile_t &trackedfile = m_treeAllOpenedFiles[idx2];
  1005. trackedfile.ProcessFileRead( dest, nBytesRead );
  1006. }
  1007. }
  1008. else
  1009. {
  1010. m_cMissedReads++;
  1011. }
  1012. #endif
  1013. }
  1014. int CFileTracker2::ListOpenedFiles( bool bListAll, const char *pchFilenameFind, bool bRecentFileList )
  1015. {
  1016. AUTO_LOCK( m_Mutex );
  1017. int cPackFiles = 0;
  1018. if ( !bRecentFileList )
  1019. {
  1020. for ( int i = m_treeAllOpenedFiles.FirstInorder(); i != m_treeAllOpenedFiles.InvalidIndex(); i = m_treeAllOpenedFiles.NextInorder( i ) )
  1021. {
  1022. TrackedFile_t &file = m_treeAllOpenedFiles[i];
  1023. if ( file.m_PackFileID )
  1024. cPackFiles++;
  1025. if ( bListAll || ( pchFilenameFind && Q_stristr( file.m_filename, pchFilenameFind ) ) )
  1026. {
  1027. Msg( "FileTracker %s ( %d, %d, %d ) %d: %d %d\n",
  1028. file.m_filename.String(), file.m_PackFileID, file.m_nPackFileNumber, 0,
  1029. file.m_filehashInProgress.m_eFileHashType, file.m_filehashFinal.m_cbFileLen, file.m_cubTotalRead );
  1030. }
  1031. }
  1032. }
  1033. else
  1034. {
  1035. for ( int i = m_RecentFileList.Head(); i != m_RecentFileList.InvalidIndex(); i = m_RecentFileList.Next( i ) )
  1036. {
  1037. int idx = m_RecentFileList[i];
  1038. TrackedFile_t &file = m_treeAllOpenedFiles[idx];
  1039. if ( file.m_PackFileID )
  1040. cPackFiles++;
  1041. if ( bListAll || ( pchFilenameFind && Q_stristr( file.m_filename, pchFilenameFind ) ) )
  1042. {
  1043. Msg( "FileTracker %s ( %d, %d, %d ) %d: %d %d\n",
  1044. file.m_filename.String(), file.m_PackFileID, file.m_nPackFileNumber, 0,
  1045. file.m_filehashInProgress.m_eFileHashType, file.m_filehashFinal.m_cbFileLen, file.m_cubTotalRead );
  1046. }
  1047. }
  1048. }
  1049. Msg( "FileTracker: %d files %d VPK files\n", m_treeAllOpenedFiles.Count(), cPackFiles );
  1050. return m_treeAllOpenedFiles.Count();
  1051. }
  1052. #ifdef DEBUG_FILETRACKER
  1053. void CC_ListTrackedFiles(const CCommand &args)
  1054. {
  1055. BaseFileSystem()->m_FileTracker2.ListOpenedFiles( true, NULL, false );
  1056. }
  1057. static ConCommand trackerlistfiles("listtrackedfiles", CC_ListTrackedFiles, "ListTrackedFiles");
  1058. void CC_ListRecentFiles(const CCommand &args)
  1059. {
  1060. BaseFileSystem()->m_FileTracker2.ListOpenedFiles( true, NULL, true );
  1061. }
  1062. static ConCommand trackerlistrecent("listrecentfiles", CC_ListRecentFiles, "ListRecentFiles");
  1063. #endif