From dfb3fd2b1635a1c7849b6c1e3a6b82503a316b61 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 1 Sep 2002 00:37:10 +0000 Subject: [PATCH] Clean up and optimize splitrelpath. Add GetExtDirListing and GetExtDirListingV, for fast searches for files by directory, filename prefix and extension. Note a change in behavior due to this: all theme files will load as prefixes, not just graphics. (eg. "title sound (abc)" will load for "title sound", just like "title graphic (abc)" loads for "title graphic".) This seems more consistent anyway, but if it's a problem it's easily fixed. (Profiling showed that we were spending a decent amount of time reading directories; this is because we were making separate calls for each theme extension.) --- stepmania/src/RageUtil.cpp | 195 +++++++++++++++++++++++++++---------- stepmania/src/RageUtil.h | 6 +- 2 files changed, 146 insertions(+), 55 deletions(-) diff --git a/stepmania/src/RageUtil.cpp b/stepmania/src/RageUtil.cpp index 8c49518989..eaec12b285 100644 --- a/stepmania/src/RageUtil.cpp +++ b/stepmania/src/RageUtil.cpp @@ -12,9 +12,6 @@ */ #include "RageUtil.h" -#if 0 -#include "fnmatch.h" -#endif ULONG randseed = time(NULL); @@ -202,37 +199,24 @@ void splitpath( const bool UsingDirsOnly, const CString &Path, CString& Drive, C //----------------------------------------------------------------------------- void splitrelpath( const CString &Path, CString& Dir, CString& FName, CString& Ext ) { - // need to split on both forward slashes and back slashes - CStringArray sPathBits; + /* Find the last slash or backslash. */ + int Last = max(Path.ReverseFind('/'), Path.ReverseFind('\\')); - CStringArray sBackShashPathBits; - split( Path, "\\", sBackShashPathBits, true ); - - for( int i=0; i files; + CArray exts; + CArray Attributes; + + CString dir; + }; + + const CacheEntry *SearchDirCache( const CString &sPath ); + + void FlushCache(); + ~DirCache() { FlushCache(); } +private: + CacheEntry *LoadDirCache( const CString &sPath ); + + /* We don't have too many directories ... XXX - glenn */ + CArray directory_cache; +} static DirectoryCache; + +void DirCache::FlushCache() +{ + for(int i = 0; i < directory_cache.GetSize(); ++i) + delete directory_cache[i]; + directory_cache.RemoveAll(); +} + +void FlushDirCache() { DirectoryCache.FlushCache(); } + +/* Read a directory and return a CacheEntry object for it. */ +DirCache::CacheEntry *DirCache::LoadDirCache( const CString &sPath ) +{ + CString sFile, sDir, sThrowAway; + splitrelpath( sPath, sDir, sThrowAway, sThrowAway ); + + CString oldpath; + GetCwd(oldpath); + if(chdir(sDir) == -1) return NULL; + + WIN32_FIND_DATA fd; + HANDLE hFind = ::FindFirstFile( "*", &fd ); + + if( hFind == INVALID_HANDLE_VALUE ) + { + chdir(oldpath); + return NULL; + } + + DirCache::CacheEntry *dir = new DirCache::CacheEntry; + dir->dir = sPath; + + do { + if(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) + continue; + + CString sExt, sThrowAway; + splitrelpath( fd.cFileName, sThrowAway, sThrowAway, sExt ); + + dir->files.Add(fd.cFileName); + dir->exts.Add(sExt); + dir->Attributes.Add(fd.dwFileAttributes); + } while( ::FindNextFile( hFind, &fd ) ); + directory_cache.Add(dir); + + chdir(oldpath); + return dir; +} + +/* Return a CacheEntry object for a directory, reading it if necessary. */ +const DirCache::CacheEntry *DirCache::SearchDirCache( const CString &sPath ) +{ + int i; + for(i = 0; i < directory_cache.GetSize(); ++i) + if(directory_cache[i]->dir == sPath) break; + + if(i == directory_cache.GetSize()) + /* Didn't find it. */ + return LoadDirCache( sPath ); + + return directory_cache[i]; +} + +/* GetExtDirListing(V): + * sPath is a path, which may include a filename. If a filename + * portion is included, return only files which have that as a + * prefix. (If you don't use this, make sure the path ends in a + * backslash, to prevent the last element of the directory from + * looking like a filename.) + * + * Each argument (or each element in the array) is an extension to + * return. If no arguments are provided, return all files. */ -#if 0 -bool GetFnmDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, ... ) +bool GetExtDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, ... ) { const char *masks[32]; int nmasks = 0; @@ -328,44 +409,50 @@ bool GetFnmDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs va_start(va, bReturnPathToo); while(const char *next = va_arg(va, const char *)) - masks[nmasks++] = next; - va_end(va); - - CString oldpath; - GetCwd(oldpath); - if(chdir(sPath) == -1) return false; - - WIN32_FIND_DATA fd; - HANDLE hFind = ::FindFirstFile( ".", &fd ); - - if( hFind != INVALID_HANDLE_VALUE ) - do { - if( bOnlyDirs && !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) + masks[nmasks++] = next; + ASSERT(nmasks+1 < sizeof(masks)/sizeof(*masks)); + } + va_end(va); + masks[nmasks++] = NULL; + + return GetExtDirListingV(sPath, AddTo, bOnlyDirs, bReturnPathToo, masks ); +} + +bool GetExtDirListingV( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, const char *masks[] ) +{ + CString sFile, sDir, sThrowAway; + splitrelpath( sPath, sDir, sFile, sThrowAway ); + + const DirCache::CacheEntry *cache = DirectoryCache.SearchDirCache(sDir); + if(!cache) return false; + + for(int i = 0; i < cache->files.GetSize(); ++i) + { + if( bOnlyDirs && !(cache->Attributes[i] & FILE_ATTRIBUTE_DIRECTORY) ) continue; // skip - if(!strcmp(fd.cFileName, ".") || - !strcmp(fd.cFileName, "..")) + if(strnicmp(sFile, cache->files[i], sFile.GetLength())) continue; bool matched = false; - for(int i = 0; !matched && i < nmasks; ++i) - if(!fnmatch(masks[i], fd.cFileName, FNM_CASEFOLD)) - matched = true; + for(int j = 0; !matched && masks[j]; ++j) + { + if(stricmp(masks[j], cache->exts[i])) + continue; + matched = true; + } if(!matched) continue; if( bReturnPathToo ) - AddTo.Add( sPath + fd.cFileName ); + AddTo.Add( sDir + cache->files[i] ); else - AddTo.Add( fd.cFileName ); - } while( ::FindNextFile( hFind, &fd ) ); - ::FindClose( hFind ); + AddTo.Add( cache->files[i] ); + } - chdir(oldpath); return true; } -#endif //----------------------------------------------------------------------------- // Name: GetHashForString( CString s ) diff --git a/stepmania/src/RageUtil.h b/stepmania/src/RageUtil.h index 56b4bb5b2c..f04d5e521e 100644 --- a/stepmania/src/RageUtil.h +++ b/stepmania/src/RageUtil.h @@ -164,7 +164,11 @@ CString join( bool CreateDirectories( CString Path ); void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs=false, bool bReturnPathToo=false ); -bool GetFnmDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, ... ); + +bool GetExtDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, ... ); +bool GetExtDirListingV( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo, const char *masks[] ); +void FlushDirCache(); + unsigned int GetHashForString( CString s ); unsigned int GetHashForFile( CString sPath ); unsigned int GetHashForDirectory( CString sDir ); // a hash value that remains the same as long as nothing in the directory has changed