//-----------------------------------------------------------------------------
// Loads the file system module
//-----------------------------------------------------------------------------
FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo )
{
	// First, locate the directory with gameinfo.txt.
	FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo );
	if ( ret != FS_OK )
		return ret;

	// Now that the environment is setup, load the filesystem module.
	if ( !Sys_LoadInterface(
		fsInfo.m_pFileSystemDLLName,
		FILESYSTEM_INTERFACE_VERSION,
		&fsInfo.m_pModule,
		(void**)&fsInfo.m_pFileSystem ) )
	{
		return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName );
	}

	if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) )
		return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName );

	if ( fsInfo.m_pFileSystem->Init() != INIT_OK )
		return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName );

	return FS_OK;
}
FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings )
{
	if ( IsConsole() )
	{
		// consoles don't use steam
		return FS_MISSING_STEAM_DLL;
	}

	// Start at our bin directory and move up until we find a directory with steam.dll in it.
	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) )	)
	{
		if ( bErrorsAsWarnings )
		{
			Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" );
			return FS_INVALID_PARAMETERS;
		}
		else
		{
			return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
		}
	}

	Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen );
	while ( 1 )
	{
		// Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username.
		// find 
		if ( DoesFileExistIn( steamInstallPath, "steam.dll" ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) )
			break;
	
		if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) )
		{
			if ( bErrorsAsWarnings )
			{
				Warning( "Can't find steam.dll relative to executable path: %s.\n", executablePath );
				return FS_MISSING_STEAM_DLL;
			}
			else
			{
				return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find steam.dll relative to executable path: %s.", executablePath );
			}
		}			
	}

	// Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll.
	char szPath[ 8192 ];
	steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) );
	if ( !DoesPathExistAlready( szPath, steamInstallPath ) )
	{
		steamEnvVars.m_Path.SetValue( "%s;%s", szPath, steamInstallPath );
	}
	return FS_OK;
}
//-----------------------------------------------------------------------------
// Returns the name of the file system DLL to use
//-----------------------------------------------------------------------------
FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
{
	bSteam = false;

	// Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
	// is in this same directory with us.
	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) )	)
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
	
#if defined( _WIN32 )
	Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR );
#elif defined( POSIX )
	Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio", executablePath, CORRECT_PATH_SEPARATOR );
	struct stat statBuf;
	if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || stat( pFileSystemDLL, &statBuf ) != 0 )
	{
		Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dylib", executablePath, CORRECT_PATH_SEPARATOR );
		bSteam = true;
	}

#else
	#error "define a filesystem dll name"
#endif

	return FS_OK;
}
FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen )
{
	steamCfgPath[0] = 0;
	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
	{
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
	}
	Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen );
	while ( 1 )
	{
		if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) )
			break;
	
		if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) )
		{
			// the file isnt found, thats ok, its not mandatory
			return FS_OK;
		}			
	}
	Q_AppendSlash( steamCfgPath, steamCfgPathLen );
	Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS );

	return FS_OK;
}
//-----------------------------------------------------------------------------
// Returns the name of the file system DLL to use
//-----------------------------------------------------------------------------
FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
{
	bSteam = false;

	// Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
	// is in this same directory with us.
	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) )	)
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
	
#if defined( _WIN32 ) && !defined( _X360 )
	// If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll.
	// There are two command line parameters for Steam:
	//		1) -steam (runs Steam in remote filesystem mode; requires Steam backend)
	//		2) -steamlocal (runs Steam in local filesystem mode (all content off HDD)
	Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR );
	if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || _access( pFileSystemDLL, 0 ) != 0 )
	{
		Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dll", executablePath, CORRECT_PATH_SEPARATOR );
		bSteam = true;
	}
#elif defined( _X360 )
	Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR );
#elif defined( _LINUX )
	Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_i486.so", executablePath, CORRECT_PATH_SEPARATOR );
#else
	#error "define a filesystem dll name"
#endif

	return FS_OK;
}
FSReturnCode_t LoadGameInfoFile( 
	const char *pDirectoryName, 
	KeyValues *&pMainFile, 
	KeyValues *&pFileSystemInfo, 
	KeyValues *&pSearchPaths )
{
	// If GameInfo.txt exists under pBaseDir, then this is their game directory.
	// All the filesystem mappings will be in this file.
	char gameinfoFilename[MAX_PATH];
	Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
	Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
	Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
	Q_FixSlashes( gameinfoFilename );
	pMainFile = ReadKeyValuesFile( gameinfoFilename );
	if ( IsX360() && !pMainFile )
	{
		// try again
		Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
		Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
		Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
		Q_FixSlashes( gameinfoFilename );
		pMainFile = ReadKeyValuesFile( gameinfoFilename );
	}
	if ( !pMainFile )
	{
		return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename );
	}

	pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
	if ( !pFileSystemInfo )
	{
		pMainFile->deleteThis();
		return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
	}

	// Now read in all the search paths.
	pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" );
	if ( !pSearchPaths )
	{
		pMainFile->deleteThis();
		return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
	}
	return FS_OK;
}
FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem )
{
	pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" );

	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) )	)
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );

	pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" );
	return FS_OK;
}
FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
{
	if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );

	KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
	FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
	if ( retVal != FS_OK )
		return retVal;
	
	// All paths except those marked with |gameinfo_path| are relative to the base dir.
	char baseDir[MAX_PATH];
	if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );

	// The MOD directory is always the one that contains gameinfo.txt
	Q_strncpy( initInfo.m_ModPath, initInfo.m_pDirectoryName, sizeof( initInfo.m_ModPath ) );

	#define GAMEINFOPATH_TOKEN		"|gameinfo_path|"
	#define BASESOURCEPATHS_TOKEN	"|all_source_engine_paths|"

	const char *pszExtraSearchPath = CommandLine()->ParmValue( "-insert_search_path" );
	if ( pszExtraSearchPath )
	{
		CUtlStringList vecPaths;
		V_SplitString( pszExtraSearchPath, ",", vecPaths );
		FOR_EACH_VEC( vecPaths, idxExtraPath )
		{
			char szAbsSearchPath[MAX_PATH];
			Q_StripPrecedingAndTrailingWhitespace( vecPaths[ idxExtraPath ] );
			V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), vecPaths[ idxExtraPath ], baseDir );
			V_FixSlashes( szAbsSearchPath );
			if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
				Error( "Bad -insert_search_path - Can't resolve pathname for '%s'", szAbsSearchPath );
			V_StripTrailingSlash( szAbsSearchPath );
			FileSystem_AddLoadedSearchPath( initInfo, "GAME", szAbsSearchPath, false );
			FileSystem_AddLoadedSearchPath( initInfo, "MOD", szAbsSearchPath, false );
		}
	}
//-----------------------------------------------------------------------------
// Mounds a particular steam cache
//-----------------------------------------------------------------------------
FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo )
{
	// This part is Steam-only.
	if ( mountContentInfo.m_pFileSystem->IsSteam() )
	{
		// Find out the "extra app id". This is for tools, which want to mount a base app's filesystem
		// like HL2, then mount the SDK content (tools materials and models, etc) in addition.
		int nExtraAppId = -1;
		if ( mountContentInfo.m_bToolsMode )
		{
			FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId );
			if ( ret != FS_OK )
				return ret;
		}

		// Set our working directory temporarily so Steam can remember it.
		// This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk
		// to get to the relative part of the path.
		char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH];
		if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
			return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );

		Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) );
		_chdir( baseDir );

		// Filesystem_tools needs to add dependencies in here beforehand.
		FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId );
		
		_chdir( oldWorkingDir );

		if ( retVal != FILESYSTEM_MOUNT_OK )
			return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" );
	}

	return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem );
}
FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen )
{
	// Engine and Hammer don't want to search around for it.
	if ( fsInfo.m_bOnlyUseDirectoryName )
	{
		if ( !fsInfo.m_pDirectoryName )
			return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." );

		bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME );
		if ( IsX360() && !bExists )
		{
			bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE );
		}
		if ( !bExists )
		{
			if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
			{
				char basePath[MAX_PATH];
				strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
				Q_AppendSlash( basePath, sizeof( basePath ) );
				Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS );
				if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
				{
					Q_strncpy( pOutDir, basePath, outDirLen );
					return FS_OK;
				}
				if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
				{
					Q_strncpy( pOutDir, basePath, outDirLen );
					return FS_OK;
				}
			}

			return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "Setup file '%s' doesn't exist in subdirectory '%s'.\nCheck your -game parameter or VCONFIG setting.", GAMEINFO_FILENAME, fsInfo.m_pDirectoryName );
		}

		Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen );
		return FS_OK;
	}

	// First, check for overrides on the command line or environment variables.
	const char *pProject = GetVProjectCmdLineValue();

	if ( pProject )
	{
		if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) )
		{
			Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
			return FS_OK;
		}
		if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) )	
		{
			Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
			return FS_OK;
		}

		if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
		{
			char basePath[MAX_PATH];
			strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
			Q_AppendSlash( basePath, sizeof( basePath ) );
			Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS );
			if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
			{
				Q_strncpy( pOutDir, basePath, outDirLen );
				return FS_OK;
			}
			if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
			{
				Q_strncpy( pOutDir, basePath, outDirLen );
				return FS_OK;
			}
		}
		
		if ( fsInfo.m_bNoGameInfo )
		{
			// fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use.
			// Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if
			// -game is supplied on the command line.
			Q_strncpy( pOutDir, "", outDirLen );
			return FS_OK;
		}
		else
		{
			// They either specified vproject on the command line or it's in their registry. Either way,
			// we don't want to continue if they've specified it but it's not valid.
			goto ShowError;
		}
	}

	if ( fsInfo.m_bNoGameInfo )
	{
		Q_strncpy( pOutDir, "", outDirLen );
		return FS_OK;
	}

	// Ask the application if it can provide us with a game info directory
	{
		bool bBubbleDir = true;
		SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn();
		if ( pfnSuggestGameInfoDirFn &&
			( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) &&
			FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) )
			return FS_OK;
	}

	// Try to use the environment variable / registry
	if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL &&
		 ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) &&
		 FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) )
		return FS_OK;

	if ( IsPC() )
	{
		Warning( "Warning: falling back to auto detection of vproject directory.\n" );
		
		// Now look for it in the directory they passed in.
		if ( fsInfo.m_pDirectoryName )
			Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName );
		else
			Q_MakeAbsolutePath( pOutDir, outDirLen, "." );

		if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
			return FS_OK;

		// Use the CWD
		Q_getwd( pOutDir, outDirLen );
		if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
			return FS_OK;
	}

ShowError:
	return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, 
		"Unable to find %s. Solutions:\n\n"
		"1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n"
		"2. Run vconfig to specify which game you're working on.\n"
		"3. Add -game <path> on the command line where <path> is the directory that %s is in.\n",
		GAMEINFO_FILENAME, GAMEINFO_FILENAME );
}
FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
{
	if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );

	KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
	FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
	if ( retVal != FS_OK )
		return retVal;
	
	// All paths except those marked with |gameinfo_path| are relative to the base dir.
	char baseDir[MAX_PATH];
	if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );

	initInfo.m_ModPath[0] = 0;

	#define GAMEINFOPATH_TOKEN		"|gameinfo_path|"
	#define BASESOURCEPATHS_TOKEN	"|all_source_engine_paths|"

	bool bLowViolence = IsLowViolenceBuild();
	bool bFirstGamePath = true;
	
	for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
	{
		const char *pPathID = pCur->GetName();
		const char *pLocation = pCur->GetString();
		
		if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
		{
			pLocation += strlen( GAMEINFOPATH_TOKEN );
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence );
		}
		else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
		{
			// This is a special identifier that tells it to add the specified path for all source engine versions equal to or prior to this version.
			// So in Orange Box, if they specified:
			//		|all_source_engine_paths|hl2
			// it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
			//
			// We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
			// In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots,
			// so we can just use the path as-is.

			// In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder).
												   
			pLocation += strlen( BASESOURCEPATHS_TOKEN );

			// Add the Orange-box path (which also will include whatever the depots mapped in as well if we're 
			// running a Steam-launched app).
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );

			if ( FileSystem_IsHldsUpdateToolDedicatedServer() )
			{			
				// If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too.
				char ep1EraPath[MAX_PATH];
				V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation );
				FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence );
			}
		}
		else
		{
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );
		}
	}

	pMainFile->deleteThis();

	//
	// Set up search paths for add-ons
	//
	if ( IsPC() )
	{
#ifdef ENGINE_DLL
		FileSystem_UpdateAddonSearchPaths( initInfo.m_pFileSystem );
#endif
	}

	// these specialized tool paths are not used on 360 and cause a costly constant perf tax, so inhibited
	if ( IsPC() )
	{
		// Create a content search path based on the game search path
		const char *pGameRoot = getenv( GAMEROOT_TOKEN );
		const char *pContentRoot = getenv( CONTENTROOT_TOKEN );

		if ( pGameRoot && pContentRoot )
		{
			int nLen = initInfo.m_pFileSystem->GetSearchPath( "GAME", false, NULL, 0 );
			char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
			initInfo.m_pFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen );
			char *pPath = pSearchPath;
			while( pPath )
			{
				char *pSemiColon = strchr( pPath, ';' );
				if ( pSemiColon )
				{
					*pSemiColon = 0;
				}

				Q_StripTrailingSlash( pPath );
				Q_FixSlashes( pPath );

				const char *pCurPath = pPath;
				pPath = pSemiColon ? pSemiColon + 1 : NULL;

				char pRelativePath[MAX_PATH];
				char pContentPath[MAX_PATH];
				if ( !Q_MakeRelativePath( pCurPath, pGameRoot, pRelativePath, sizeof(pRelativePath) ) )
					continue;

				Q_ComposeFileName( pContentRoot, pRelativePath, pContentPath, sizeof(pContentPath) );
				initInfo.m_pFileSystem->AddSearchPath( pContentPath, "CONTENT" );
			}

			// Add the "platform" directory as a game searchable path
			char pPlatformPath[MAX_PATH];
			Q_ComposeFileName( pGameRoot, "platform", pPlatformPath, sizeof(pPlatformPath) );
			initInfo.m_pFileSystem->AddSearchPath( pPlatformPath, "GAME", PATH_ADD_TO_TAIL );

			initInfo.m_pFileSystem->AddSearchPath( pContentRoot, "CONTENTROOT" );
			initInfo.m_pFileSystem->AddSearchPath( pGameRoot, "GAMEROOT" );
		}
		else
		{
			// Come up with some reasonable default
			int nLen = initInfo.m_pFileSystem->GetSearchPath( "MOD", false, NULL, 0 );
			char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
			initInfo.m_pFileSystem->GetSearchPath( "MOD", false, pSearchPath, nLen );
			char *pSemiColon = strchr( pSearchPath, ';' );
			if ( pSemiColon )
			{
				*pSemiColon = 0;
			}

			char pGameRootPath[MAX_PATH];
			Q_strncpy( pGameRootPath, pSearchPath, sizeof(pGameRootPath) );
			Q_StripTrailingSlash( pGameRootPath );
			Q_StripFilename( pGameRootPath );

			char pContentRootPath[MAX_PATH];
			Q_strncpy( pContentRootPath, pGameRootPath, sizeof(pContentRootPath) );
			char *pGame = Q_stristr( pContentRootPath, "game" );

			if ( pGame )
			{
				Q_strcpy( pGame, "content" );
			}

			initInfo.m_pFileSystem->AddSearchPath( pContentRootPath, "CONTENTROOT" );
			initInfo.m_pFileSystem->AddSearchPath( pGameRootPath, "GAMEROOT" );
		}

		// Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
		// when people forget to specify a search path.
		initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "contentroot", true );
		initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gameroot", true );
		initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "content", true );
	}

	// Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
	// when people forget to specify a search path.
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );

	// Add the write path last.
	if ( initInfo.m_ModPath[0] != 0 )
	{
		initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL );
	}

#ifdef _DEBUG	
	initInfo.m_pFileSystem->PrintSearchPaths();
#endif

#if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) && !defined( _X360 )
	//copy search paths to stack tools so it can grab pdb's from all over. But only on P4 or Steam Beta builds
	if( (CommandLine()->FindParm( "-steam" ) == 0) || //not steam
		(CommandLine()->FindParm( "-internalbuild" ) != 0) ) //steam beta is ok
	{
		char szSearchPaths[4096];
		//int CBaseFileSystem::GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen )
		int iLength1 = initInfo.m_pFileSystem->GetSearchPath( "EXECUTABLE_PATH", false, szSearchPaths, 4096 );
		if( iLength1 == 1 )
			iLength1 = 0;

		int iLength2 = initInfo.m_pFileSystem->GetSearchPath( "GAMEBIN", false, szSearchPaths + iLength1, 4096 - iLength1 );
		if( (iLength2 > 1) && (iLength1 > 1) )
		{
			szSearchPaths[iLength1 - 1] = ';'; //replace first null terminator
		}

		const char *szAdditionalPath = CommandLine()->ParmValue( "-AdditionalPDBSearchPath" );
		if( szAdditionalPath && szAdditionalPath[0] )
		{
			int iLength = iLength1;
			if( iLength2 > 1 )
				iLength += iLength2;

			if( iLength != 0 )
			{
				szSearchPaths[iLength - 1] = ';'; //replaces null terminator
			}
			V_strncpy( &szSearchPaths[iLength], szAdditionalPath, 4096 - iLength );			
		}

		//Append the perforce symbol server last. Documentation says that "srv*\\perforce\symbols" should work, but it doesn't.
		//"symsrv*symsrv.dll*\\perforce\symbols" which the docs say is the same statement, works.
		{
			V_strncat( szSearchPaths, ";symsrv*symsrv.dll*\\\\perforce\\symbols", 4096 );
		}

		SetStackTranslationSymbolSearchPath( szSearchPaths );
		//MessageBox( NULL, szSearchPaths, "Search Paths", 0 );
	}
#endif

	return FS_OK;
}
FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings )
{
	if ( IsConsole() )
	{
		// consoles don't use steam
		return FS_MISSING_STEAM_DLL;
	}

	if ( IsPosix() )
		return FS_OK; // under posix the content does not live with steam.dll up the path, rely on the environment already being set by steam
	
	// Start at our bin directory and move up until we find a directory with steam.dll in it.
	char executablePath[MAX_PATH];
	if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) )	)
	{
		if ( bErrorsAsWarnings )
		{
			Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" );
			return FS_INVALID_PARAMETERS;
		}
		else
		{
			return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
		}
	}

	Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen );
#ifdef WIN32
	const char *pchSteamDLL = "steam.dll";
#elif defined(OSX)
	// under osx the bin lives in the bin/ folder, so step back one
	Q_StripLastDir( steamInstallPath, steamInstallPathLen );
	const char *pchSteamDLL = "libsteam.dylib";	
#elif defined(LINUX)
	// under linux the bin lives in the bin/ folder, so step back one
	Q_StripLastDir( steamInstallPath, steamInstallPathLen );
	const char *pchSteamDLL = "libsteam.so";
#else
#error
#endif
	while ( 1 )
	{
		// Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username.
		// find 
		if ( DoesFileExistIn( steamInstallPath, pchSteamDLL ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) )
			break;
	
		if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) )
		{
			if ( bErrorsAsWarnings )
			{
				Warning( "Can't find %s relative to executable path: %s.\n", pchSteamDLL, executablePath );
				return FS_MISSING_STEAM_DLL;
			}
			else
			{
				return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find %s relative to executable path: %s.", pchSteamDLL, executablePath );
			}
		}			
	}

	// Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll.
	char szPath[ 8192 ];
	steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) );
	if ( !DoesPathExistAlready( szPath, steamInstallPath ) )
	{
#ifdef WIN32
#define PATH_SEP ";"
#else
#define PATH_SEP ":"
#endif	
		steamEnvVars.m_Path.SetValue( "%s%s%s", szPath, PATH_SEP, steamInstallPath );
	}
	return FS_OK;
}
FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
{
	if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );

	KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
	FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
	if ( retVal != FS_OK )
		return retVal;
	
	// All paths except those marked with |gameinfo_path| are relative to the base dir.
	char baseDir[MAX_PATH];
	if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
		return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );

	initInfo.m_ModPath[0] = 0;

	#define GAMEINFOPATH_TOKEN		"|gameinfo_path|"
	#define BASESOURCEPATHS_TOKEN	"|all_source_engine_paths|"

	bool bLowViolence = IsLowViolenceBuild();
	bool bFirstGamePath = true;
	
	for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
	{
		const char *pPathID = pCur->GetName();
		const char *pLocation = pCur->GetString();
		
		if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
		{
			pLocation += strlen( GAMEINFOPATH_TOKEN );
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence );
		}
		else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
		{
			// This is a special identifier that tells it to add the specified path for all source engine versions equal to or prior to this version.
			// So in Orange Box, if they specified:
			//		|all_source_engine_paths|hl2
			// it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
			//
			// We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
			// In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots,
			// so we can just use the path as-is.

			// In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder).
												   
			pLocation += strlen( BASESOURCEPATHS_TOKEN );

			// Add the Orange-box path (which also will include whatever the depots mapped in as well if we're 
			// running a Steam-launched app).
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );

			if ( FileSystem_IsHldsUpdateToolDedicatedServer() )
			{			
				// If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too.
				char ep1EraPath[MAX_PATH];
				V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation );
				FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence );
			}
		}
		else
		{
			FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );
		}
	}

	pMainFile->deleteThis();

	// Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
	// when people forget to specify a search path.
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
	initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );
	if ( initInfo.m_ModPath[0] != 0 )
	{
		// Add the write path last.
		initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL );
	}

#ifdef _DEBUG	
	initInfo.m_pFileSystem->PrintSearchPaths();
#endif

	return FS_OK;
}