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.

500 lines
15 KiB

  1. //===== Copyright (c) Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //==================================================================//
  7. #if defined( _WIN32 ) && !defined( _X360 )
  8. #undef PROTECTED_THINGS_ENABLE
  9. #include <windows.h>
  10. #endif
  11. #include "quakedef.h" // for max_ospath
  12. #include <stdlib.h>
  13. #include <assert.h>
  14. #include "filesystem.h"
  15. #include "bitmap/tgawriter.h"
  16. #include <tier2/tier2.h>
  17. #include "filesystem_init.h"
  18. #include "keyvalues.h"
  19. #include "host.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. #define ADDONLIST_FILENAME "addonlist.txt"
  23. #define ADDONS_DIRNAME "addons"
  24. IFileSystem *g_pFileSystem = NULL;
  25. // This comes is in filesystem_init.cpp
  26. extern KeyValues* ReadKeyValuesFile( const char *pFilename );
  27. void fs_whitelist_spew_flags_changefn( IConVar *pConVar, const char *pOldValue, float flOldValue )
  28. {
  29. if ( g_pFileSystem )
  30. {
  31. ConVarRef var( pConVar );
  32. g_pFileSystem->SetWhitelistSpewFlags( var.GetInt() );
  33. }
  34. }
  35. #if defined( _DEBUG )
  36. ConVar fs_whitelist_spew_flags( "fs_whitelist_spew_flags", "0", 0,
  37. "Set whitelist spew flags to a combination of these values:\n"
  38. " 0x0001 - list files as they are added to the CRC tracker\n"
  39. " 0x0002 - show files the filesystem is telling the engine to reload\n"
  40. " 0x0004 - show files the filesystem is NOT telling the engine to reload",
  41. fs_whitelist_spew_flags_changefn );
  42. #endif
  43. CON_COMMAND( path, "Show the engine filesystem path." )
  44. {
  45. if( g_pFileSystem )
  46. {
  47. g_pFileSystem->PrintSearchPaths();
  48. }
  49. }
  50. CON_COMMAND( fs_printopenfiles, "Show all files currently opened by the engine." )
  51. {
  52. if( g_pFileSystem )
  53. {
  54. g_pFileSystem->PrintOpenedFiles();
  55. }
  56. }
  57. CON_COMMAND( fs_warning_level, "Set the filesystem warning level." )
  58. {
  59. if( args.ArgC() != 2 )
  60. {
  61. Warning( "\"fs_warning_level n\" where n is one of:\n" );
  62. Warning( "\t0:\tFILESYSTEM_WARNING_QUIET\n" );
  63. Warning( "\t1:\tFILESYSTEM_WARNING_REPORTUNCLOSED\n" );
  64. Warning( "\t2:\tFILESYSTEM_WARNING_REPORTUSAGE\n" );
  65. Warning( "\t3:\tFILESYSTEM_WARNING_REPORTALLACCESSES\n" );
  66. Warning( "\t4:\tFILESYSTEM_WARNING_REPORTALLACCESSES_READ\n" );
  67. Warning( "\t5:\tFILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE\n" );
  68. Warning( "\t6:\tFILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC\n" );
  69. return;
  70. }
  71. int level = atoi( args[ 1 ] );
  72. switch( level )
  73. {
  74. case FILESYSTEM_WARNING_QUIET:
  75. Warning( "fs_warning_level = FILESYSTEM_WARNING_QUIET\n" );
  76. break;
  77. case FILESYSTEM_WARNING_REPORTUNCLOSED:
  78. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTUNCLOSED\n" );
  79. break;
  80. case FILESYSTEM_WARNING_REPORTUSAGE:
  81. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTUSAGE\n" );
  82. break;
  83. case FILESYSTEM_WARNING_REPORTALLACCESSES:
  84. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES\n" );
  85. break;
  86. case FILESYSTEM_WARNING_REPORTALLACCESSES_READ:
  87. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_READ\n" );
  88. break;
  89. case FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE:
  90. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE\n" );
  91. break;
  92. case FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC:
  93. Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC\n" );
  94. break;
  95. default:
  96. Warning( "fs_warning_level = UNKNOWN!!!!!!!\n" );
  97. return;
  98. break;
  99. }
  100. g_pFileSystem->SetWarningLevel( ( FileWarningLevel_t )level );
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Wrap Sys_LoadModule() with a filesystem GetLocalCopy() call to
  104. // ensure have the file to load when running Steam.
  105. //-----------------------------------------------------------------------------
  106. CSysModule *FileSystem_LoadModule(const char *path)
  107. {
  108. if ( g_pFileSystem )
  109. return g_pFileSystem->LoadModule( path );
  110. else
  111. return Sys_LoadModule(path);
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Provided for symmetry sake with FileSystem_LoadModule()...
  115. //-----------------------------------------------------------------------------
  116. void FileSystem_UnloadModule(CSysModule *pModule)
  117. {
  118. Sys_UnloadModule(pModule);
  119. }
  120. void FileSystem_SetWhitelistSpewFlags()
  121. {
  122. #if defined( _DEBUG )
  123. if ( !g_pFileSystem )
  124. {
  125. Assert( !"FileSystem_InitSpewFlags - no filesystem." );
  126. return;
  127. }
  128. g_pFileSystem->SetWhitelistSpewFlags( fs_whitelist_spew_flags.GetInt() );
  129. #endif
  130. }
  131. CON_COMMAND( fs_syncdvddevcache, "Force the 360 to get updated files that are in your p4 changelist(s) from the host PC when running with -dvddev." )
  132. {
  133. if( g_pFileSystem )
  134. {
  135. g_pFileSystem->SyncDvdDevCache();
  136. }
  137. }
  138. //---------------------------------------------------------------------------------------------------------------------
  139. // Loads the optional addonlist.txt file which lives in the same location as gameinfo.txt and defines additional search
  140. // paths for content add-ons to mods.
  141. //---------------------------------------------------------------------------------------------------------------------
  142. bool LoadAddonListFile( const char *pDirectoryName, KeyValues *&pAddons )
  143. {
  144. char addoninfoFilename[MAX_PATH];
  145. V_snprintf( addoninfoFilename, sizeof( addoninfoFilename), "%s%s", pDirectoryName, ADDONLIST_FILENAME );
  146. pAddons = ReadKeyValuesFile( addoninfoFilename );
  147. return ( pAddons != NULL );
  148. }
  149. //---------------------------------------------------------------------------------------------------------------------
  150. // Copies any addons staged under <STEAMDIR>\steamapps\SourceMods\addons\<APPID> to the addons directory
  151. //---------------------------------------------------------------------------------------------------------------------
  152. void CopyStagedAddons( IFileSystem *pFileSystem, const char *pModPath )
  153. {
  154. #if (defined( PLATFORM_WINDOWS ) && !defined( _X360 ) ) || defined( PLATFORM_OSX )
  155. #ifdef IS_WINDOWS_PC
  156. HKEY hKey;
  157. // Find the Steam installation path in the registry
  158. if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Steam", 0, KEY_READ, &hKey) )
  159. {
  160. DWORD nReadLength = MAX_PATH;
  161. char szAddonInstallPath[MAX_PATH];
  162. if ( ERROR_SUCCESS == RegQueryValueEx( hKey, "SourceModInstallPath", NULL, NULL, (LPBYTE)szAddonInstallPath, &nReadLength ) )
  163. {
  164. #else
  165. {
  166. {
  167. char szAddonInstallPath[MAX_PATH];
  168. char *pszHomeDir = getenv("HOME");
  169. V_snprintf( szAddonInstallPath, sizeof(szAddonInstallPath), "%s/Library/Application Support/Steam/SteamApps/sourcemods", pszHomeDir );
  170. #endif
  171. char szAddonsWildcard[MAX_PATH];
  172. FileFindHandle_t findHandleDir;
  173. //
  174. // Loop through the .vpk files in the staged location
  175. //
  176. CUtlVector< CUtlString > vecAddonVPKs;
  177. V_snprintf( szAddonsWildcard, sizeof( szAddonsWildcard ), "%s%c%s%c%i%c%s", szAddonInstallPath, CORRECT_PATH_SEPARATOR, ADDONS_DIRNAME,
  178. CORRECT_PATH_SEPARATOR, GetSteamAppID(), CORRECT_PATH_SEPARATOR, "*.vpk" );
  179. const char *pFileName = pFileSystem->FindFirst( szAddonsWildcard, &findHandleDir );
  180. while ( pFileName )
  181. {
  182. char szSrcVPKPath[MAX_PATH];
  183. V_snprintf( szSrcVPKPath, sizeof( szSrcVPKPath), "%s%c%s%c%i%c%s", szAddonInstallPath, CORRECT_PATH_SEPARATOR, ADDONS_DIRNAME,
  184. CORRECT_PATH_SEPARATOR, GetSteamAppID(), CORRECT_PATH_SEPARATOR, pFileName);
  185. vecAddonVPKs.AddToTail( CUtlString( szSrcVPKPath ) );
  186. pFileName = pFileSystem->FindNext( findHandleDir );
  187. }
  188. pFileSystem->FindClose( findHandleDir );
  189. //
  190. // Copy each of the VPKs to the addons directory
  191. //
  192. FOR_EACH_VEC( vecAddonVPKs, i )
  193. {
  194. char szDestPath[MAX_PATH];
  195. V_snprintf( szDestPath, sizeof( szDestPath ),"%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, V_UnqualifiedFileName( vecAddonVPKs[i] ) );
  196. pFileSystem->RemoveFile( szDestPath );
  197. pFileSystem->RenameFile( vecAddonVPKs[i], szDestPath );
  198. }
  199. }
  200. #ifdef IS_WINDOWS_PC
  201. RegCloseKey( hKey );
  202. #endif
  203. }
  204. #endif
  205. }
  206. //---------------------------------------------------------------------------------------------------------------------
  207. // Reconciles the contents of the addonlist.txt file with the addon folders located under <MODPATH>/addons. If the
  208. // contains directory names that no longer exist they are removed. If there are directories present that are not in
  209. // the file they are added. Added directories default to the disabled state in the file.
  210. //---------------------------------------------------------------------------------------------------------------------
  211. void ReconcileAddonListFile( IFileSystem *pFileSystem, const char *pModPath )
  212. {
  213. KeyValues *pAddonList;
  214. // Load the existing addonlist.txt file
  215. LoadAddonListFile( pModPath, pAddonList );
  216. // If there is no addonlist.txt then create an empty KeyValues
  217. if ( !pAddonList )
  218. {
  219. pAddonList = new KeyValues( "AddonList" );
  220. }
  221. // Get the list of subdirectories of addons
  222. char addonsWildcard[MAX_PATH];
  223. FileFindHandle_t findHandleDir;
  224. //
  225. // Loop through the .vpk files
  226. //
  227. CUtlVector< CUtlString > vecAddonVPKs;
  228. V_snprintf( addonsWildcard, sizeof( addonsWildcard ), "%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, "*.vpk" );
  229. const char *pFileName = pFileSystem->FindFirst( addonsWildcard, &findHandleDir );
  230. while ( pFileName )
  231. {
  232. vecAddonVPKs.AddToTail( CUtlString( pFileName ) );
  233. pFileName = pFileSystem->FindNext( findHandleDir );
  234. }
  235. pFileSystem->FindClose( findHandleDir );
  236. //
  237. // Loop through the loose directories
  238. //
  239. CUtlVector< CUtlString > vecAddonDirs;
  240. V_snprintf( addonsWildcard, sizeof( addonsWildcard ), "%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, "*.*" );
  241. pFileName = pFileSystem->FindFirst( addonsWildcard, &findHandleDir );
  242. while ( pFileName )
  243. {
  244. // We only want directories that is not already represented by a .vpk and that contains a valid addoninfo.txt
  245. if ( pFileSystem->FindIsDirectory( findHandleDir ) && ( pFileName[0] != '.' ) )
  246. {
  247. char szVPKized[MAX_PATH];
  248. V_snprintf( szVPKized, sizeof( szVPKized ), "%s.vpk", pFileName );
  249. if ( !vecAddonVPKs.IsValidIndex( vecAddonVPKs.Find( CUtlString( szVPKized ) ) ) )
  250. {
  251. char addonsInfoFile[MAX_PATH];
  252. FileFindHandle_t findHandleConfig;
  253. V_snprintf( addonsInfoFile, sizeof( addonsInfoFile ), "%s%s%c%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, pFileName, CORRECT_PATH_SEPARATOR, "addoninfo.txt" );
  254. if ( pFileSystem->FindFirst( addonsInfoFile, &findHandleConfig ) )
  255. {
  256. vecAddonDirs.AddToTail( CUtlString( pFileName ) );
  257. }
  258. pFileSystem->FindClose( findHandleConfig );
  259. }
  260. }
  261. pFileName = pFileSystem->FindNext( findHandleDir );
  262. }
  263. pFileSystem->FindClose( findHandleDir );
  264. // Add missing, existing directories to the KeyValues
  265. FOR_EACH_VEC( vecAddonDirs, i )
  266. {
  267. // We found a directory that wasn't included in the file - add it
  268. if ( !pAddonList->FindKey( vecAddonDirs[i] ) )
  269. {
  270. pAddonList->SetInt( vecAddonDirs[i], 1 );
  271. }
  272. }
  273. // Add missing, existing VPKs to the KeyValues
  274. FOR_EACH_VEC( vecAddonVPKs, i )
  275. {
  276. // We found a VPK that wasn't included in the file - add it
  277. if ( !pAddonList->FindKey( vecAddonVPKs[i] ) )
  278. {
  279. pAddonList->SetInt( vecAddonVPKs[i], 1 );
  280. }
  281. }
  282. // Remove any non-existent directories from the KeyValues
  283. KeyValues* pIter = pAddonList->GetFirstSubKey();
  284. CUtlVector<KeyValues*> vecDoomedSubkeys;
  285. while( pIter )
  286. {
  287. if ( !vecAddonDirs.IsValidIndex( vecAddonDirs.Find( CUtlString( pIter->GetName() ) ) ) &&
  288. !vecAddonVPKs.IsValidIndex( vecAddonVPKs.Find( CUtlString( pIter->GetName() ) ) ) )
  289. {
  290. vecDoomedSubkeys.AddToTail( pIter );
  291. }
  292. pIter = pIter->GetNextKey();
  293. }
  294. // Now actually delete the missing directories
  295. FOR_EACH_VEC( vecDoomedSubkeys, j )
  296. {
  297. pAddonList->RemoveSubKey( vecDoomedSubkeys[j] );
  298. vecDoomedSubkeys[j]->deleteThis();
  299. }
  300. // Persist and dispose
  301. char addoninfoFilename[MAX_PATH];
  302. V_snprintf( addoninfoFilename, sizeof( addoninfoFilename), "%s%s", pModPath, ADDONLIST_FILENAME );
  303. if ( pAddonList->GetFirstSubKey() )
  304. {
  305. pAddonList->SaveToFile( pFileSystem, addoninfoFilename );
  306. }
  307. else
  308. {
  309. if ( pFileSystem->FileExists( addoninfoFilename ) )
  310. {
  311. pFileSystem->RemoveFile( addoninfoFilename );
  312. }
  313. }
  314. pAddonList->deleteThis();
  315. }
  316. //---------------------------------------------------------------------------------------------------------------------
  317. // Adds enabled addons to the GAME search path after removing any existing addons from the GAME path.
  318. //---------------------------------------------------------------------------------------------------------------------
  319. void FileSystem_UpdateAddonSearchPaths( IFileSystem *pFileSystem )
  320. {
  321. // Get the path to the mod dir
  322. char modPath[MAX_PATH];
  323. pFileSystem->GetSearchPath( "MOD", false, modPath, sizeof( modPath ) );
  324. //
  325. // Remove any existing addons from the search path
  326. //
  327. char gameSearchPath[10*MAX_PATH];
  328. char addonSearchString[MAX_PATH];
  329. CUtlStringList gameSearchPathList;
  330. // Construct the search string for determining whether the search path component is an add-on
  331. V_snprintf( addonSearchString, sizeof( addonSearchString ), "%s%s", modPath, ADDONS_DIRNAME );
  332. pFileSystem->GetSearchPath( "GAME", false, gameSearchPath, sizeof( gameSearchPath ) );
  333. V_SplitString(gameSearchPath, ";", gameSearchPathList );
  334. FOR_EACH_VEC( gameSearchPathList, i )
  335. {
  336. if ( V_stristr( gameSearchPathList[i], addonSearchString ) )
  337. {
  338. pFileSystem->RemoveSearchPath( gameSearchPathList[i], "GAME" );
  339. }
  340. }
  341. // Unmount any VPK addons
  342. CUtlVector<CUtlString> loadedVPKs;
  343. pFileSystem->GetVPKFileNames( loadedVPKs );
  344. FOR_EACH_VEC( loadedVPKs, i )
  345. {
  346. if ( V_stristr( loadedVPKs[i], addonSearchString ) )
  347. {
  348. pFileSystem->RemoveVPKFile( loadedVPKs[i] );
  349. }
  350. }
  351. //
  352. // Copy over any addons that were staged by the addon installer
  353. //
  354. CopyStagedAddons( pFileSystem, modPath );
  355. //
  356. // Reconcile the addons file and add any newly added ones to the list
  357. //
  358. ReconcileAddonListFile( pFileSystem, modPath );
  359. //
  360. // Add any enabled addons to the GAME search path
  361. //
  362. KeyValues *pAddonList;
  363. if ( LoadAddonListFile( modPath, pAddonList ) )
  364. {
  365. for ( KeyValues *pCur=pAddonList->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
  366. {
  367. const char *pszAddonName = pCur->GetName();
  368. const bool bAddonActivated = pCur->GetInt() != 0;
  369. if ( bAddonActivated )
  370. {
  371. char addOnPath[MAX_PATH];
  372. V_snprintf( addOnPath, sizeof( addOnPath ), "%s%s%c%s", modPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, pszAddonName );
  373. if ( V_stristr( pszAddonName, ".vpk" ) )
  374. {
  375. pFileSystem->AddVPKFile( addOnPath, PATH_ADD_TO_TAIL );
  376. }
  377. else
  378. {
  379. pFileSystem->AddSearchPath( addOnPath, "GAME", PATH_ADD_TO_TAIL );
  380. }
  381. }
  382. }
  383. pAddonList->deleteThis();
  384. }
  385. modelloader->Studio_ReloadModels( IModelLoader::RELOAD_EVERYTHING );
  386. materials->UncacheAllMaterials();
  387. }
  388. CON_COMMAND( update_addon_paths, "Reloads the search paths for game addons." )
  389. {
  390. if( g_pFileSystem )
  391. {
  392. FileSystem_UpdateAddonSearchPaths( g_pFileSystem );
  393. }
  394. }
  395. CON_COMMAND( unload_all_addons, "Reloads the search paths for game addons." )
  396. {
  397. //
  398. // Unmount any VPK addons
  399. //
  400. if( g_pFileSystem )
  401. {
  402. char addonSearchString[MAX_PATH];
  403. char modPath[MAX_PATH];
  404. CUtlVector<CUtlString> loadedVPKs;
  405. // Get the path to the mod dir
  406. g_pFileSystem->GetSearchPath( "MOD", false, modPath, sizeof( modPath ) );
  407. // Construct the search string for determining whether the search path component is an add-on
  408. V_snprintf( addonSearchString, sizeof( addonSearchString ), "%s%s", modPath, ADDONS_DIRNAME );
  409. g_pFileSystem->GetVPKFileNames( loadedVPKs );
  410. FOR_EACH_VEC( loadedVPKs, i )
  411. {
  412. if ( V_stristr( loadedVPKs[i], addonSearchString ) )
  413. {
  414. g_pFileSystem->RemoveVPKFile( loadedVPKs[i] );
  415. }
  416. }
  417. }
  418. }