|
|
//===== Copyright (c) Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//==================================================================//
#if defined( _WIN32 ) && !defined( _X360 )
#undef PROTECTED_THINGS_ENABLE
#include <windows.h>
#endif
#include "quakedef.h" // for max_ospath
#include <stdlib.h>
#include <assert.h>
#include "filesystem.h"
#include "bitmap/tgawriter.h"
#include <tier2/tier2.h>
#include "filesystem_init.h"
#include "keyvalues.h"
#include "host.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define ADDONLIST_FILENAME "addonlist.txt"
#define ADDONS_DIRNAME "addons"
IFileSystem *g_pFileSystem = NULL;
// This comes is in filesystem_init.cpp
extern KeyValues* ReadKeyValuesFile( const char *pFilename );
void fs_whitelist_spew_flags_changefn( IConVar *pConVar, const char *pOldValue, float flOldValue ) { if ( g_pFileSystem ) { ConVarRef var( pConVar ); g_pFileSystem->SetWhitelistSpewFlags( var.GetInt() ); } }
#if defined( _DEBUG )
ConVar fs_whitelist_spew_flags( "fs_whitelist_spew_flags", "0", 0, "Set whitelist spew flags to a combination of these values:\n" " 0x0001 - list files as they are added to the CRC tracker\n" " 0x0002 - show files the filesystem is telling the engine to reload\n" " 0x0004 - show files the filesystem is NOT telling the engine to reload", fs_whitelist_spew_flags_changefn ); #endif
CON_COMMAND( path, "Show the engine filesystem path." ) { if( g_pFileSystem ) { g_pFileSystem->PrintSearchPaths(); } }
CON_COMMAND( fs_printopenfiles, "Show all files currently opened by the engine." ) { if( g_pFileSystem ) { g_pFileSystem->PrintOpenedFiles(); } }
CON_COMMAND( fs_warning_level, "Set the filesystem warning level." ) { if( args.ArgC() != 2 ) { Warning( "\"fs_warning_level n\" where n is one of:\n" ); Warning( "\t0:\tFILESYSTEM_WARNING_QUIET\n" ); Warning( "\t1:\tFILESYSTEM_WARNING_REPORTUNCLOSED\n" ); Warning( "\t2:\tFILESYSTEM_WARNING_REPORTUSAGE\n" ); Warning( "\t3:\tFILESYSTEM_WARNING_REPORTALLACCESSES\n" ); Warning( "\t4:\tFILESYSTEM_WARNING_REPORTALLACCESSES_READ\n" ); Warning( "\t5:\tFILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE\n" ); Warning( "\t6:\tFILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC\n" ); return; }
int level = atoi( args[ 1 ] ); switch( level ) { case FILESYSTEM_WARNING_QUIET: Warning( "fs_warning_level = FILESYSTEM_WARNING_QUIET\n" ); break; case FILESYSTEM_WARNING_REPORTUNCLOSED: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTUNCLOSED\n" ); break; case FILESYSTEM_WARNING_REPORTUSAGE: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTUSAGE\n" ); break; case FILESYSTEM_WARNING_REPORTALLACCESSES: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES\n" ); break; case FILESYSTEM_WARNING_REPORTALLACCESSES_READ: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_READ\n" ); break; case FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE\n" ); break; case FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC: Warning( "fs_warning_level = FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC\n" ); break;
default: Warning( "fs_warning_level = UNKNOWN!!!!!!!\n" ); return; break; } g_pFileSystem->SetWarningLevel( ( FileWarningLevel_t )level ); }
//-----------------------------------------------------------------------------
// Purpose: Wrap Sys_LoadModule() with a filesystem GetLocalCopy() call to
// ensure have the file to load when running Steam.
//-----------------------------------------------------------------------------
CSysModule *FileSystem_LoadModule(const char *path) { if ( g_pFileSystem ) return g_pFileSystem->LoadModule( path ); else return Sys_LoadModule(path); }
//-----------------------------------------------------------------------------
// Purpose: Provided for symmetry sake with FileSystem_LoadModule()...
//-----------------------------------------------------------------------------
void FileSystem_UnloadModule(CSysModule *pModule) { Sys_UnloadModule(pModule); }
void FileSystem_SetWhitelistSpewFlags() { #if defined( _DEBUG )
if ( !g_pFileSystem ) { Assert( !"FileSystem_InitSpewFlags - no filesystem." ); return; } g_pFileSystem->SetWhitelistSpewFlags( fs_whitelist_spew_flags.GetInt() ); #endif
}
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." ) { if( g_pFileSystem ) { g_pFileSystem->SyncDvdDevCache(); } }
//---------------------------------------------------------------------------------------------------------------------
// Loads the optional addonlist.txt file which lives in the same location as gameinfo.txt and defines additional search
// paths for content add-ons to mods.
//---------------------------------------------------------------------------------------------------------------------
bool LoadAddonListFile( const char *pDirectoryName, KeyValues *&pAddons ) { char addoninfoFilename[MAX_PATH];
V_snprintf( addoninfoFilename, sizeof( addoninfoFilename), "%s%s", pDirectoryName, ADDONLIST_FILENAME ); pAddons = ReadKeyValuesFile( addoninfoFilename );
return ( pAddons != NULL ); }
//---------------------------------------------------------------------------------------------------------------------
// Copies any addons staged under <STEAMDIR>\steamapps\SourceMods\addons\<APPID> to the addons directory
//---------------------------------------------------------------------------------------------------------------------
void CopyStagedAddons( IFileSystem *pFileSystem, const char *pModPath ) { #if (defined( PLATFORM_WINDOWS ) && !defined( _X360 ) ) || defined( PLATFORM_OSX )
#ifdef IS_WINDOWS_PC
HKEY hKey;
// Find the Steam installation path in the registry
if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Steam", 0, KEY_READ, &hKey) ) { DWORD nReadLength = MAX_PATH; char szAddonInstallPath[MAX_PATH];
if ( ERROR_SUCCESS == RegQueryValueEx( hKey, "SourceModInstallPath", NULL, NULL, (LPBYTE)szAddonInstallPath, &nReadLength ) ) { #else
{ { char szAddonInstallPath[MAX_PATH]; char *pszHomeDir = getenv("HOME"); V_snprintf( szAddonInstallPath, sizeof(szAddonInstallPath), "%s/Library/Application Support/Steam/SteamApps/sourcemods", pszHomeDir ); #endif
char szAddonsWildcard[MAX_PATH]; FileFindHandle_t findHandleDir; //
// Loop through the .vpk files in the staged location
//
CUtlVector< CUtlString > vecAddonVPKs; V_snprintf( szAddonsWildcard, sizeof( szAddonsWildcard ), "%s%c%s%c%i%c%s", szAddonInstallPath, CORRECT_PATH_SEPARATOR, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, GetSteamAppID(), CORRECT_PATH_SEPARATOR, "*.vpk" ); const char *pFileName = pFileSystem->FindFirst( szAddonsWildcard, &findHandleDir );
while ( pFileName ) { char szSrcVPKPath[MAX_PATH];
V_snprintf( szSrcVPKPath, sizeof( szSrcVPKPath), "%s%c%s%c%i%c%s", szAddonInstallPath, CORRECT_PATH_SEPARATOR, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, GetSteamAppID(), CORRECT_PATH_SEPARATOR, pFileName); vecAddonVPKs.AddToTail( CUtlString( szSrcVPKPath ) ); pFileName = pFileSystem->FindNext( findHandleDir ); }
pFileSystem->FindClose( findHandleDir );
//
// Copy each of the VPKs to the addons directory
//
FOR_EACH_VEC( vecAddonVPKs, i ) { char szDestPath[MAX_PATH];
V_snprintf( szDestPath, sizeof( szDestPath ),"%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, V_UnqualifiedFileName( vecAddonVPKs[i] ) ); pFileSystem->RemoveFile( szDestPath ); pFileSystem->RenameFile( vecAddonVPKs[i], szDestPath ); } }
#ifdef IS_WINDOWS_PC
RegCloseKey( hKey ); #endif
} #endif
}
//---------------------------------------------------------------------------------------------------------------------
// Reconciles the contents of the addonlist.txt file with the addon folders located under <MODPATH>/addons. If the
// contains directory names that no longer exist they are removed. If there are directories present that are not in
// the file they are added. Added directories default to the disabled state in the file.
//---------------------------------------------------------------------------------------------------------------------
void ReconcileAddonListFile( IFileSystem *pFileSystem, const char *pModPath ) { KeyValues *pAddonList;
// Load the existing addonlist.txt file
LoadAddonListFile( pModPath, pAddonList );
// If there is no addonlist.txt then create an empty KeyValues
if ( !pAddonList ) { pAddonList = new KeyValues( "AddonList" ); }
// Get the list of subdirectories of addons
char addonsWildcard[MAX_PATH];
FileFindHandle_t findHandleDir;
//
// Loop through the .vpk files
//
CUtlVector< CUtlString > vecAddonVPKs; V_snprintf( addonsWildcard, sizeof( addonsWildcard ), "%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, "*.vpk" ); const char *pFileName = pFileSystem->FindFirst( addonsWildcard, &findHandleDir );
while ( pFileName ) { vecAddonVPKs.AddToTail( CUtlString( pFileName ) ); pFileName = pFileSystem->FindNext( findHandleDir ); }
pFileSystem->FindClose( findHandleDir );
//
// Loop through the loose directories
//
CUtlVector< CUtlString > vecAddonDirs; V_snprintf( addonsWildcard, sizeof( addonsWildcard ), "%s%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, "*.*" ); pFileName = pFileSystem->FindFirst( addonsWildcard, &findHandleDir );
while ( pFileName ) { // We only want directories that is not already represented by a .vpk and that contains a valid addoninfo.txt
if ( pFileSystem->FindIsDirectory( findHandleDir ) && ( pFileName[0] != '.' ) ) { char szVPKized[MAX_PATH];
V_snprintf( szVPKized, sizeof( szVPKized ), "%s.vpk", pFileName );
if ( !vecAddonVPKs.IsValidIndex( vecAddonVPKs.Find( CUtlString( szVPKized ) ) ) ) { char addonsInfoFile[MAX_PATH]; FileFindHandle_t findHandleConfig;
V_snprintf( addonsInfoFile, sizeof( addonsInfoFile ), "%s%s%c%s%c%s", pModPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, pFileName, CORRECT_PATH_SEPARATOR, "addoninfo.txt" );
if ( pFileSystem->FindFirst( addonsInfoFile, &findHandleConfig ) ) { vecAddonDirs.AddToTail( CUtlString( pFileName ) ); }
pFileSystem->FindClose( findHandleConfig ); } } pFileName = pFileSystem->FindNext( findHandleDir ); }
pFileSystem->FindClose( findHandleDir );
// Add missing, existing directories to the KeyValues
FOR_EACH_VEC( vecAddonDirs, i ) { // We found a directory that wasn't included in the file - add it
if ( !pAddonList->FindKey( vecAddonDirs[i] ) ) { pAddonList->SetInt( vecAddonDirs[i], 1 ); } }
// Add missing, existing VPKs to the KeyValues
FOR_EACH_VEC( vecAddonVPKs, i ) { // We found a VPK that wasn't included in the file - add it
if ( !pAddonList->FindKey( vecAddonVPKs[i] ) ) { pAddonList->SetInt( vecAddonVPKs[i], 1 ); } }
// Remove any non-existent directories from the KeyValues
KeyValues* pIter = pAddonList->GetFirstSubKey(); CUtlVector<KeyValues*> vecDoomedSubkeys;
while( pIter ) { if ( !vecAddonDirs.IsValidIndex( vecAddonDirs.Find( CUtlString( pIter->GetName() ) ) ) && !vecAddonVPKs.IsValidIndex( vecAddonVPKs.Find( CUtlString( pIter->GetName() ) ) ) ) { vecDoomedSubkeys.AddToTail( pIter ); }
pIter = pIter->GetNextKey(); }
// Now actually delete the missing directories
FOR_EACH_VEC( vecDoomedSubkeys, j ) { pAddonList->RemoveSubKey( vecDoomedSubkeys[j] ); vecDoomedSubkeys[j]->deleteThis(); }
// Persist and dispose
char addoninfoFilename[MAX_PATH];
V_snprintf( addoninfoFilename, sizeof( addoninfoFilename), "%s%s", pModPath, ADDONLIST_FILENAME );
if ( pAddonList->GetFirstSubKey() ) { pAddonList->SaveToFile( pFileSystem, addoninfoFilename ); } else { if ( pFileSystem->FileExists( addoninfoFilename ) ) { pFileSystem->RemoveFile( addoninfoFilename ); } } pAddonList->deleteThis(); }
//---------------------------------------------------------------------------------------------------------------------
// Adds enabled addons to the GAME search path after removing any existing addons from the GAME path.
//---------------------------------------------------------------------------------------------------------------------
void FileSystem_UpdateAddonSearchPaths( IFileSystem *pFileSystem ) { // Get the path to the mod dir
char modPath[MAX_PATH];
pFileSystem->GetSearchPath( "MOD", false, modPath, sizeof( modPath ) );
//
// Remove any existing addons from the search path
//
char gameSearchPath[10*MAX_PATH]; char addonSearchString[MAX_PATH]; CUtlStringList gameSearchPathList;
// Construct the search string for determining whether the search path component is an add-on
V_snprintf( addonSearchString, sizeof( addonSearchString ), "%s%s", modPath, ADDONS_DIRNAME );
pFileSystem->GetSearchPath( "GAME", false, gameSearchPath, sizeof( gameSearchPath ) ); V_SplitString(gameSearchPath, ";", gameSearchPathList );
FOR_EACH_VEC( gameSearchPathList, i ) { if ( V_stristr( gameSearchPathList[i], addonSearchString ) ) { pFileSystem->RemoveSearchPath( gameSearchPathList[i], "GAME" ); } }
// Unmount any VPK addons
CUtlVector<CUtlString> loadedVPKs;
pFileSystem->GetVPKFileNames( loadedVPKs );
FOR_EACH_VEC( loadedVPKs, i ) { if ( V_stristr( loadedVPKs[i], addonSearchString ) ) { pFileSystem->RemoveVPKFile( loadedVPKs[i] ); } }
//
// Copy over any addons that were staged by the addon installer
//
CopyStagedAddons( pFileSystem, modPath );
//
// Reconcile the addons file and add any newly added ones to the list
//
ReconcileAddonListFile( pFileSystem, modPath );
//
// Add any enabled addons to the GAME search path
//
KeyValues *pAddonList; if ( LoadAddonListFile( modPath, pAddonList ) ) { for ( KeyValues *pCur=pAddonList->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) { const char *pszAddonName = pCur->GetName(); const bool bAddonActivated = pCur->GetInt() != 0;
if ( bAddonActivated ) { char addOnPath[MAX_PATH];
V_snprintf( addOnPath, sizeof( addOnPath ), "%s%s%c%s", modPath, ADDONS_DIRNAME, CORRECT_PATH_SEPARATOR, pszAddonName );
if ( V_stristr( pszAddonName, ".vpk" ) ) { pFileSystem->AddVPKFile( addOnPath, PATH_ADD_TO_TAIL ); } else { pFileSystem->AddSearchPath( addOnPath, "GAME", PATH_ADD_TO_TAIL ); } } }
pAddonList->deleteThis(); }
modelloader->Studio_ReloadModels( IModelLoader::RELOAD_EVERYTHING ); materials->UncacheAllMaterials(); } CON_COMMAND( update_addon_paths, "Reloads the search paths for game addons." ) { if( g_pFileSystem ) { FileSystem_UpdateAddonSearchPaths( g_pFileSystem ); } }
CON_COMMAND( unload_all_addons, "Reloads the search paths for game addons." ) { //
// Unmount any VPK addons
//
if( g_pFileSystem ) { char addonSearchString[MAX_PATH]; char modPath[MAX_PATH]; CUtlVector<CUtlString> loadedVPKs;
// Get the path to the mod dir
g_pFileSystem->GetSearchPath( "MOD", false, modPath, sizeof( modPath ) );
// Construct the search string for determining whether the search path component is an add-on
V_snprintf( addonSearchString, sizeof( addonSearchString ), "%s%s", modPath, ADDONS_DIRNAME );
g_pFileSystem->GetVPKFileNames( loadedVPKs );
FOR_EACH_VEC( loadedVPKs, i ) { if ( V_stristr( loadedVPKs[i], addonSearchString ) ) { g_pFileSystem->RemoveVPKFile( loadedVPKs[i] ); } } } }
|