//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// // // Purpose: Helper methods + classes for file access. // //===========================================================================// #include "tier1/strtools.h" #include "tier1/utlbuffer.h" #include "tier2/fileutils.h" #include "filesystem/filesystem.h" // NOTE: This has to be the last file included! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: Builds a directory which is a subdirectory of the current mod. // Input : *pSubDir - // *pBuf - // nBufLen - //----------------------------------------------------------------------------- void GetModSubdirectory(const char* pSubDir, char* pBuf, ssize_t nBufLen) { // Compute starting directory. Assert(FileSystem()->GetSearchPath( "MOD", false, NULL, 0) < nBufLen ); FileSystem()->GetSearchPath( "MOD", false, pBuf, nBufLen ); char* pSemi = strchr( pBuf, ';' ); if ( pSemi ) { *pSemi = 0; } V_StripTrailingSlash( pBuf ); size_t currentLength = strlen( pBuf ); if ( pSubDir ) { Q_strncat( pBuf, "\\", nBufLen-currentLength ); currentLength += 1; // Account for the added '\\' character. Q_strncat( pBuf, pSubDir, nBufLen-currentLength ); } V_FixSlashes( pBuf ); } //----------------------------------------------------------------------------- // Purpose: Builds a directory which is a subdirectory of the current mod's *content*. // Input : *pSubDir - // *pBuf - // nBufLen - //----------------------------------------------------------------------------- void GetModContentSubdirectory( const char *pSubDir, char *pBuf, ssize_t nBufLen ) { char pTemp[ MAX_PATH ]; GetModSubdirectory( pSubDir, pTemp, sizeof(pTemp) ); ComputeModContentFilename( pTemp, pBuf, nBufLen ); } //----------------------------------------------------------------------------- // Purpose: Generates a filename under the 'game' subdirectory given a subdirectory of 'content'. // Input : *pContentFileName - // *pBuf - // nBufLen - //----------------------------------------------------------------------------- void ComputeModFilename( const char *pContentFileName, char *pBuf, ssize_t nBufLen ) { char pRelativePath[ MAX_PATH ]; if ( !FileSystem()->FullPathToRelativePath(pContentFileName, pRelativePath, sizeof(pRelativePath))) { Q_strncpy( pBuf, pContentFileName, nBufLen ); return; } char pGameRoot[ MAX_PATH ]; FileSystem()->GetSearchPath("GAME", false, pGameRoot, sizeof(pGameRoot)); char *pSemi = strchr( pGameRoot, ';' ); if ( pSemi ) { *pSemi = 0; } V_ComposeFileName( pGameRoot, pRelativePath, pBuf, nBufLen ); } //----------------------------------------------------------------------------- // Purpose: Generates a filename under the 'content' subdirectory given a subdirectory of 'game'. // Input : *pGameFileName - // *pBuf - // nBufLen - //----------------------------------------------------------------------------- void ComputeModContentFilename( const char *pGameFileName, char *pBuf, ssize_t nBufLen ) { char pRelativePath[ MAX_PATH ]; if ( !FileSystem()->FullPathToRelativePath( pGameFileName, pRelativePath, sizeof(pRelativePath) ) ) { Q_strncpy( pBuf, pGameFileName, nBufLen ); return; } char pContentRoot[ MAX_PATH ]; FileSystem()->GetSearchPath( "CONTENT", false, pContentRoot, sizeof(pContentRoot) ); char *pSemi = strchr( pContentRoot, ';' ); if ( pSemi ) { *pSemi = 0; } V_ComposeFileName( pContentRoot, pRelativePath, pBuf, nBufLen ); } //----------------------------------------------------------------------------- // Purpose: Search start directory, recurse into sub directories collecting all files matching the target name. // Input : &fileList - // *szStartDirectory - // *szTargetFileName - // *pathID - // separator - //----------------------------------------------------------------------------- void RecursiveFindFilesMatchingName( CUtlVector< CUtlString > &fileList, const char* szStartDirectory, const char* szTargetFileName, const char *pPathID, char separator ) { char searchString[MAX_PATH]; Q_snprintf( searchString, sizeof( searchString ), "%s/*.*", szStartDirectory ); V_FixSlashes( searchString ); FileFindHandle_t handle; const char* curFile = FileSystem()->FindFirstEx( searchString, pPathID, &handle ); while ( curFile ) { if ( *curFile != '.' && FileSystem()->FindIsDirectory( handle ) ) { char newSearchPath[MAX_PATH]; Q_snprintf( newSearchPath, sizeof( newSearchPath ), "%s/%s", szStartDirectory, curFile ); RecursiveFindFilesMatchingName( fileList, newSearchPath, szTargetFileName, pPathID, separator); } else if ( V_StringMatchesPattern( curFile, szTargetFileName ) ) { CUtlString outFile; outFile.Format( "%s/%s", szStartDirectory, curFile ); V_FixSlashes( outFile.Get(), separator ); fileList.AddToTail( outFile ); } curFile = FileSystem()->FindNext( handle ); } FileSystem()->FindClose( handle ); } //----------------------------------------------------------------------------- // Builds a list of all files under a directory with a particular extension. // Input : &fileList - // *pDirectory - // *pExtension - // *pPathID - // separator - //----------------------------------------------------------------------------- void AddFilesToList( CUtlVector< CUtlString > &fileList, const char *pDirectory, const char *pExtension, const char* pPathID, char separator ) { char pSearchString[MAX_PATH]; Q_snprintf( pSearchString, MAX_PATH, "%s/*", pDirectory ); bool bIsAbsolute = V_IsAbsolutePath( pDirectory ); // get the list of files. FileFindHandle_t hFind; const char *pFoundFile = FileSystem()->FindFirstEx( pSearchString, pPathID, &hFind ); // add all the items. CUtlVector< CUtlString > subDirs; for ( ; pFoundFile; pFoundFile = FileSystem()->FindNext( hFind ) ) { char pChildPath[MAX_PATH]; Q_snprintf( pChildPath, MAX_PATH, "%s/%s", pDirectory, pFoundFile ); if ( FileSystem()->FindIsDirectory( hFind ) ) { if ( Q_strnicmp( pFoundFile, ".", 2 ) && Q_strnicmp( pFoundFile, "..", 3 ) ) { subDirs.AddToTail( pChildPath ); } continue; } // Check the extension matches. if ( pExtension && Q_stricmp( V_GetFileExtension( pFoundFile ), pExtension ) ) continue; char pFullPathBuf[MAX_PATH]; char *pFullPath = pFullPathBuf; if ( !bIsAbsolute ) { FileSystem()->RelativePathToFullPath( pChildPath, pPathID, pFullPathBuf, sizeof(pFullPathBuf) ); } else { pFullPath = pChildPath; } V_strlower( pFullPath ); V_FixSlashes( pFullPath, separator ); fileList.AddToTail( pFullPath ); } FileSystem()->FindClose(hFind); int nCount = subDirs.Count(); for ( int i = 0; i < nCount; ++i ) { AddFilesToList( fileList, subDirs[i], pExtension, pPathID, separator ); } } //----------------------------------------------------------------------------- // Purpose: Returns the search path as a list of paths. // Input : &pathList - // *pPathID - //----------------------------------------------------------------------------- void GetSearchPath(CUtlVector< CUtlString >& pathList, const char* pPathID) { const ssize_t nMaxLen = FileSystem()->GetSearchPath(pPathID, false, NULL, 0); char* pBuf = (char*)stackalloc(nMaxLen); FileSystem()->GetSearchPath(pPathID, false, pBuf, nMaxLen); char* pSemi; while (NULL != (pSemi = strchr(pBuf, ';'))) { *pSemi = 0; pathList.AddToTail(pBuf); pBuf = pSemi + 1; } pathList.AddToTail(pBuf); } //----------------------------------------------------------------------------- // Purpose: Given file name in the current dir generate a full path to it. // Input : *pFileName - // *pPathID - // *pBuf - // nBufLen - // Output: True on success, false otherwise. //----------------------------------------------------------------------------- bool GenerateFullPath(const char* pFileName, char const* pPathID, char* pBuf, ssize_t nBufLen) { if (V_IsAbsolutePath(pFileName)) { V_strncpy(pBuf, pFileName, nBufLen); return true; } const char* pFullPath = FileSystem()->RelativePathToFullPath(pFileName, pPathID, pBuf, nBufLen); if (pFullPath && V_IsAbsolutePath(pFullPath)) return true; char pDir[MAX_PATH]; if (!FileSystem()->GetCurrentDirectory(pDir, sizeof(pDir))) return false; V_ComposeFileName(pDir, pFileName, pBuf, nBufLen); V_RemoveDotSlashes(pBuf); return true; }