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.

532 lines
13 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "basefilesystem.h"
  7. // NOTE: This has to be the last file included!
  8. #include "tier0/memdbgon.h"
  9. bool CompareFilenames( const char *pa, const char *pb )
  10. {
  11. // Case-insensitive and path separator-insensitive compare.
  12. const char *a = pa;
  13. const char *b = pb;
  14. while ( *a && *b )
  15. {
  16. char ca = *a;
  17. char cb = *b;
  18. if ( ca >= 'a' && ca <= 'z' )
  19. ca = 'A' + (ca - 'a');
  20. else if ( ca == '/' )
  21. ca = '\\';
  22. if ( cb >= 'a' && cb <= 'z' )
  23. cb = 'A' + (cb - 'a');
  24. else if ( cb == '/' )
  25. cb = '\\';
  26. if ( ca != cb )
  27. return false;
  28. ++a;
  29. ++b;
  30. }
  31. // Filenames also must be the same length.
  32. if ( *a != *b )
  33. return false;
  34. return true;
  35. }
  36. //-----------------------------------------------------------------------------
  37. // CFileTracker.
  38. //-----------------------------------------------------------------------------
  39. CFileTracker::CFileTracker( CBaseFileSystem *pFileSystem )
  40. {
  41. m_pFileSystem = pFileSystem;
  42. }
  43. CFileTracker::~CFileTracker()
  44. {
  45. Clear();
  46. }
  47. void CFileTracker::NoteFileLoadedFromDisk( const char *pFilename, const char *pPathID, FileHandle_t fp )
  48. {
  49. AUTO_LOCK( m_Mutex );
  50. if ( !pPathID )
  51. pPathID = "";
  52. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  53. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  54. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  55. {
  56. if ( pInfo )
  57. Warning( "(Duplicate): [%s]\\%s", pPathID, pFilename );
  58. else
  59. Warning( "(Unique ): [%s]\\%s", pPathID, pFilename );
  60. }
  61. if ( pInfo )
  62. {
  63. // Clear all the flags, but remember if we ever had a CRC.
  64. pInfo->m_Flags &= k_eFileFlagsGotCRCOnce;
  65. pInfo->m_Flags &= ~k_eFileFlagsFailedToLoadLastTime;
  66. }
  67. else
  68. {
  69. pInfo = pPath->AddFileInfo( pFilename );
  70. pInfo->m_Flags = (EFileFlags)0;
  71. }
  72. if ( !fp )
  73. {
  74. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  75. {
  76. Warning( "\n" );
  77. }
  78. return;
  79. }
  80. // Remember that we calculated the CRC and that it is unverified.
  81. pInfo->m_CRC = CalculateCRCForFile( fp );
  82. pInfo->m_Flags |= k_eFileFlagsHasCRC | k_eFileFlagsGotCRCOnce;
  83. if ( pInfo->m_iNeedsVerificationListIndex == -1 )
  84. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  85. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  86. {
  87. Warning( " - %u\n", pInfo->m_CRC );
  88. }
  89. }
  90. void CFileTracker::NoteFileFailedToLoad( const char *pFilename, const char *pPathID )
  91. {
  92. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  93. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  94. if ( pInfo )
  95. {
  96. pInfo->m_Flags |= k_eFileFlagsFailedToLoadLastTime;
  97. }
  98. }
  99. CRC32_t CFileTracker::CalculateCRCForFile( FileHandle_t fp )
  100. {
  101. CRC32_t crc;
  102. // Calculate the CRC.
  103. unsigned int initialFilePos = m_pFileSystem->Tell( fp );
  104. m_pFileSystem->Seek( fp, 0, FILESYSTEM_SEEK_HEAD );
  105. #define CRC_CHUNK_SIZE (32*1024)
  106. char tempBuf[CRC_CHUNK_SIZE];
  107. CRC32_Init( &crc );
  108. unsigned int fileLength = m_pFileSystem->Size( fp );
  109. int nChunks = fileLength / CRC_CHUNK_SIZE + 1;
  110. unsigned int curStartByte = 0;
  111. for ( int iChunk=0; iChunk < nChunks; iChunk++ )
  112. {
  113. int curEndByte = min( curStartByte + CRC_CHUNK_SIZE, fileLength );
  114. int chunkLen = curEndByte - curStartByte;
  115. if ( chunkLen == 0 )
  116. break;
  117. m_pFileSystem->Read( tempBuf, chunkLen, fp ); // TODO: handle errors here..
  118. CRC32_ProcessBuffer( &crc, tempBuf, chunkLen );
  119. curStartByte += CRC_CHUNK_SIZE;
  120. }
  121. CRC32_Final( &crc );
  122. // Go back to where we started.
  123. m_pFileSystem->Seek( fp, initialFilePos, FILESYSTEM_SEEK_HEAD );
  124. return crc;
  125. }
  126. CFileInfo* CFileTracker::GetFileInfo( const char *pFilename, const char *pPathID )
  127. {
  128. AUTO_LOCK( m_Mutex );
  129. CPathIDFileList *pPath = GetPathIDFileList( pPathID, false );
  130. if ( !pPath )
  131. return NULL;
  132. return pPath->FindFileInfo( pFilename );
  133. }
  134. int CFileTracker::GetFileInfos( CFileInfo **ppFileInfos, int nMaxFileInfos, const char *pFilename )
  135. {
  136. AUTO_LOCK( m_Mutex );
  137. int nOut = 0;
  138. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  139. {
  140. CFileInfo *pCur = m_PathIDs[i]->FindFileInfo( pFilename );
  141. if ( pCur )
  142. {
  143. if ( nOut < nMaxFileInfos )
  144. {
  145. ppFileInfos[nOut++] = pCur;
  146. }
  147. else
  148. {
  149. Assert( !"CFileTracker::GetFileInfos - overflowed list!" );
  150. }
  151. }
  152. }
  153. return nOut;
  154. }
  155. void CFileTracker::NoteFileLoadedFromSteam( const char *pFilename, const char *pPathID, bool bForcedLoadFromSteam )
  156. {
  157. AUTO_LOCK( m_Mutex );
  158. if ( !pPathID )
  159. pPathID = "";
  160. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  161. CFileInfo *pInfo = pPath->FindFileInfo( pFilename );
  162. if ( !pInfo )
  163. pInfo = pPath->AddFileInfo( pFilename );
  164. if ( m_pFileSystem->m_WhitelistSpewFlags & WHITELIST_SPEW_WHILE_LOADING )
  165. {
  166. Warning( "From Steam: [%s]\\%s\n", pPathID, pFilename );
  167. }
  168. pInfo->m_Flags = k_eFileFlagsLoadedFromSteam;
  169. if ( bForcedLoadFromSteam )
  170. pInfo->m_Flags |= k_eFileFlagsForcedLoadFromSteam;
  171. }
  172. void CFileTracker::CalculateMissingCRCs( IFileList *pWantCRCList )
  173. {
  174. // First build a list of files that need a CRC and don't have one.
  175. m_Mutex.Lock();
  176. CUtlLinkedList<CFileInfo*,int> needCRCList;
  177. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  178. {
  179. CPathIDFileList *pPath = m_PathIDs[i];
  180. FOR_EACH_LL( pPath->m_Files, j )
  181. {
  182. CFileInfo *pInfo = pPath->m_Files[j];
  183. if ( !( pInfo->m_Flags & k_eFileFlagsLoadedFromSteam ) && !( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  184. {
  185. // If the new "force match" list doesn't care whether the file has a CRC or not, then don't bother to calculate it.
  186. if ( !pWantCRCList->IsFileInList( pInfo->m_pFilename ) )
  187. continue;
  188. needCRCList.AddToTail( pInfo );
  189. }
  190. }
  191. }
  192. m_Mutex.Unlock();
  193. // Then, when the mutex is not locked, go generate the CRCs for them.
  194. FOR_EACH_LL( needCRCList, i )
  195. {
  196. CFileInfo *pInfo = needCRCList[i];
  197. CalculateMissingCRC( pInfo->m_pFilename, pInfo->GetPathIDString() );
  198. }
  199. }
  200. void CFileTracker::CacheFileCRC( const char *pPathID, const char *pRelativeFilename )
  201. {
  202. Assert( ThreadInMainThread() );
  203. // Get the file's info. Load the file if necessary.
  204. CFileInfo *pInfo = GetFileInfo( pRelativeFilename, pPathID );
  205. if ( !pInfo )
  206. {
  207. CalculateMissingCRC( pRelativeFilename, pPathID );
  208. pInfo = GetFileInfo( pRelativeFilename, pPathID );
  209. }
  210. if ( !pInfo )
  211. return;
  212. // Already cached a CRC for this file?
  213. if ( !( pInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  214. {
  215. // Ok, it's from disk but we don't have the CRC.
  216. CalculateMissingCRC( pInfo->GetFilename(), pInfo->GetPathIDString() );
  217. }
  218. }
  219. void CFileTracker::CacheFileCRC_Copy( const char *pPathID, const char *pRelativeFilename, const char *pPathIDToCopyFrom )
  220. {
  221. Assert( ThreadInMainThread() );
  222. // Get the file's info. Load the file if necessary.
  223. CFileInfo *pSourceInfo = GetFileInfo( pRelativeFilename, pPathIDToCopyFrom );
  224. if ( !pSourceInfo || !( pSourceInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  225. {
  226. // Strange, we don't have a CRC for the one they wanted to copy from, so calculate that CRC.
  227. CacheFileCRC( pPathIDToCopyFrom, pRelativeFilename );
  228. if ( !( pSourceInfo->m_Flags & k_eFileFlagsGotCRCOnce ) )
  229. {
  230. // Still didn't get it. Ok.. well get a CRC for the target one anyway.
  231. CacheFileCRC( pPathID, pRelativeFilename );
  232. return;
  233. }
  234. }
  235. // Setup a CFileInfo for the target..
  236. CPathIDFileList *pPath = GetPathIDFileList( pPathID );
  237. CFileInfo *pDestInfo = pPath->FindFileInfo( pRelativeFilename );
  238. if ( !pDestInfo )
  239. pDestInfo = pPath->AddFileInfo( pRelativeFilename );
  240. pDestInfo->m_CRC = pSourceInfo->m_CRC;
  241. pDestInfo->m_Flags = pSourceInfo->m_Flags;
  242. }
  243. EFileCRCStatus CFileTracker::CheckCachedFileCRC( const char *pPathID, const char *pRelativeFilename, CRC32_t *pCRC )
  244. {
  245. Assert( ThreadInMainThread() );
  246. // Get the file's info. Load the file if necessary.
  247. CFileInfo *pInfo = GetFileInfo( pRelativeFilename, pPathID );
  248. if ( pInfo && (pInfo->m_Flags & k_eFileFlagsGotCRCOnce) )
  249. {
  250. *pCRC = pInfo->m_CRC;
  251. return k_eFileCRCStatus_GotCRC;
  252. }
  253. else
  254. {
  255. return k_eFileCRCStatus_CantOpenFile;
  256. }
  257. }
  258. void CFileTracker::CalculateMissingCRC( const char *pFilename, const char *pPathID )
  259. {
  260. // Force it to make a CRC of disk files.
  261. FileHandle_t fh = m_pFileSystem->FindFileInSearchPaths( pFilename, "rb", pPathID, FSOPEN_FORCE_TRACK_CRC, NULL, true );
  262. if ( !fh )
  263. return;
  264. CFileInfo *pInfo = GetFileInfo( pFilename, pPathID );
  265. if ( pInfo )
  266. {
  267. // Now we're about to modify the file itself.. lock the mutex.
  268. AUTO_LOCK( m_Mutex );
  269. // The FindFileInSearchPaths call might have done the CRC for us.
  270. if ( !( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  271. {
  272. pInfo->m_CRC = CalculateCRCForFile( fh );
  273. pInfo->m_Flags |= k_eFileFlagsHasCRC | k_eFileFlagsGotCRCOnce;
  274. if ( pInfo->m_iNeedsVerificationListIndex == -1 )
  275. {
  276. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  277. }
  278. }
  279. }
  280. else
  281. {
  282. Assert( false );
  283. }
  284. m_pFileSystem->Close( fh );
  285. }
  286. void CFileTracker::MarkAllCRCsUnverified()
  287. {
  288. AUTO_LOCK( m_Mutex );
  289. // First clear the 'needs verification' list.
  290. MarkAllCRCsVerified();
  291. Assert( m_NeedsVerificationList.Count() == 0 );
  292. for ( int i=m_PathIDs.First(); i != m_PathIDs.InvalidIndex(); i=m_PathIDs.Next( i ) )
  293. {
  294. CPathIDFileList *pPath = m_PathIDs[i];
  295. FOR_EACH_LL( pPath->m_Files, j )
  296. {
  297. CFileInfo *pInfo = pPath->m_Files[j];
  298. if ( !(pInfo->m_Flags & k_eFileFlagsLoadedFromSteam) && ( pInfo->m_Flags & k_eFileFlagsHasCRC ) )
  299. {
  300. pInfo->m_iNeedsVerificationListIndex = m_NeedsVerificationList.AddToTail( pInfo );
  301. }
  302. }
  303. }
  304. }
  305. void CFileTracker::MarkAllCRCsVerified( bool bLockMutex )
  306. {
  307. if ( bLockMutex )
  308. m_Mutex.Lock();
  309. FOR_EACH_LL( m_NeedsVerificationList, i )
  310. {
  311. m_NeedsVerificationList[i]->m_iNeedsVerificationListIndex = -1;
  312. }
  313. m_NeedsVerificationList.Purge();
  314. if ( bLockMutex )
  315. m_Mutex.Unlock();
  316. }
  317. int CFileTracker::GetUnverifiedCRCFiles( CUnverifiedCRCFile *pFiles, int nMaxFiles )
  318. {
  319. Assert( nMaxFiles > 0 );
  320. AUTO_LOCK( m_Mutex );
  321. int iOutFile = 0;
  322. int iNext = 0;
  323. for ( int i=m_NeedsVerificationList.Head(); i != m_NeedsVerificationList.InvalidIndex(); i=iNext )
  324. {
  325. iNext = m_NeedsVerificationList.Next( i );
  326. CFileInfo *pInfo = m_NeedsVerificationList[i];
  327. // Remove this entry from the list.
  328. m_NeedsVerificationList.Remove( i );
  329. pInfo->m_iNeedsVerificationListIndex = -1;
  330. // This can happen if a file that was in this list was loaded from Steam since it got added to the list.
  331. // In that case, just act like it's not in the list.
  332. if ( pInfo->m_Flags & k_eFileFlagsLoadedFromSteam )
  333. continue;
  334. Assert( pInfo->m_Flags & k_eFileFlagsHasCRC );
  335. // Add this file to their list.
  336. CUnverifiedCRCFile *pOutFile = &pFiles[iOutFile];
  337. V_strncpy( pOutFile->m_Filename, pInfo->m_pFilename, sizeof( pOutFile->m_Filename ) );
  338. V_strncpy( pOutFile->m_PathID, pInfo->m_pPathIDFileList->m_PathID.String(), sizeof( pOutFile->m_PathID ) );
  339. pOutFile->m_CRC = pInfo->m_CRC;
  340. ++iOutFile;
  341. if ( iOutFile >= nMaxFiles )
  342. return iOutFile;
  343. }
  344. return iOutFile;
  345. }
  346. void CFileTracker::Clear()
  347. {
  348. AUTO_LOCK( m_Mutex );
  349. m_PathIDs.PurgeAndDeleteElements();
  350. }
  351. CPathIDFileList* CFileTracker::GetPathIDFileList( const char *pPathID, bool bAutoAdd )
  352. {
  353. AUTO_LOCK( m_Mutex );
  354. if ( !pPathID )
  355. pPathID = "";
  356. int i = m_PathIDs.Find( pPathID );
  357. if ( i == m_PathIDs.InvalidIndex() )
  358. {
  359. if ( bAutoAdd )
  360. {
  361. CPathIDFileList *pPath = new CPathIDFileList;
  362. pPath->m_PathID = pPathID;
  363. m_PathIDs.Insert( pPathID, pPath );
  364. return pPath;
  365. }
  366. else
  367. {
  368. return NULL;
  369. }
  370. }
  371. else
  372. {
  373. return m_PathIDs[i];
  374. }
  375. }
  376. //-----------------------------------------------------------------------------
  377. // CFileInfo implementation.
  378. //-----------------------------------------------------------------------------
  379. CFileInfo::CFileInfo( const char *pFilename )
  380. {
  381. int len = V_strlen( pFilename ) + 1;
  382. m_pFilename = new char[ len ];
  383. Q_strncpy( m_pFilename, pFilename, len );
  384. m_iNeedsVerificationListIndex = -1;
  385. }
  386. CFileInfo::~CFileInfo()
  387. {
  388. delete [] m_pFilename;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // CPathIDFileList implementation..
  392. //-----------------------------------------------------------------------------
  393. CPathIDFileList::~CPathIDFileList()
  394. {
  395. m_Files.PurgeAndDeleteElements();
  396. }
  397. CFileInfo* CPathIDFileList::FindFileInfo( const char *pFilename )
  398. {
  399. Assert( !V_IsAbsolutePath( pFilename ) );
  400. FOR_EACH_LL( m_Files, i )
  401. {
  402. CFileInfo *pFileInfo = m_Files[i];
  403. if ( CompareFilenames( pFilename, pFileInfo->GetFilename() ) )
  404. return m_Files[i];
  405. }
  406. return NULL;
  407. }
  408. CFileInfo* CPathIDFileList::AddFileInfo( const char *pFilename )
  409. {
  410. Assert( !V_IsAbsolutePath( pFilename ) );
  411. CFileInfo *pFileInfo = new CFileInfo( pFilename );
  412. pFileInfo->m_pPathIDFileList = this;
  413. m_Files.AddToTail( pFileInfo );
  414. return pFileInfo;
  415. }