From 524c29c1e2f1b3c0cbaeded99f7305f2884ddb53 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Thu, 3 Jul 2003 02:41:27 +0000 Subject: [PATCH] split off FDB --- stepmania/src/RageUtil.cpp | 453 +----------------------------- stepmania/src/RageUtil.h | 71 ++--- stepmania/src/RageUtil_FileDB.cpp | 451 +++++++++++++++++++++++++++++ stepmania/src/RageUtil_FileDB.h | 12 + stepmania/src/StepMania.dsp | 57 ++-- stepmania/src/StepMania.vcproj | 6 + stepmania/src/ThemeManager.cpp | 2 +- 7 files changed, 544 insertions(+), 508 deletions(-) create mode 100644 stepmania/src/RageUtil_FileDB.cpp create mode 100644 stepmania/src/RageUtil_FileDB.h diff --git a/stepmania/src/RageUtil.cpp b/stepmania/src/RageUtil.cpp index 274cafdece..87f1ea721a 100644 --- a/stepmania/src/RageUtil.cpp +++ b/stepmania/src/RageUtil.cpp @@ -11,22 +11,15 @@ */ #include "RageUtil.h" +#include "RageUtil_FileDB.h" +#include "RageTimer.h" +#include "RageLog.h" #include #include #include -#include -#include #include #include -#include - -#include "RageTimer.h" -#include "RageLog.h" - -#if !defined(WIN32) -#include -#endif unsigned long randseed = time(NULL); @@ -348,15 +341,6 @@ void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bRe } #endif -void GetCwd(CString &s) -{ - char buf[PATH_MAX]; - bool ret = getcwd(buf, PATH_MAX) != NULL; - ASSERT(ret); - - s = buf; -} - /* Reference: http://www.theorem.com/java/CRC32.java, rewritten by Glenn Maynard. * Public domain. */ unsigned int GetHashForString ( CString s ) @@ -385,12 +369,6 @@ unsigned int GetHashForString ( CString s ) return crc; } -bool DoStat(CString sPath, struct stat *st) -{ - TrimRight(sPath, "/\\"); - return stat(sPath.c_str(), st) != -1; -} - unsigned int GetHashForFile( CString sPath ) { unsigned int hash = 0; @@ -423,42 +401,6 @@ unsigned int GetHashForDirectory( CString sDir ) return hash; } -unsigned GetFileSizeInBytes( const CString &sFilePath ) -{ - struct stat st; - if(!DoStat(sFilePath, &st)) - return 0; - - return st.st_size; -} -#if 0 -bool DoesFileExist( const CString &sPath ) { return FDB.DoesFileExist(sPath); } -bool IsAFile( const CString &sPath ) { return FDB.IsAFile(sPath); } -bool IsADirectory( const CString &sPath ) { return FDB.IsADirectory(sPath); } -#else -bool DoesFileExist( const CString &sPath ) -{ - if(sPath.empty()) return false; - struct stat st; - return DoStat(sPath, &st); -} - -bool IsAFile( const CString &sPath ) -{ - return DoesFileExist(sPath) && ! IsADirectory(sPath); -} - -bool IsADirectory( const CString &sPath ) -{ - if(sPath.empty()) return false; - struct stat st; - if (!DoStat(sPath, &st)) - return false; - - return !!(st.st_mode & S_IFDIR); -} -#endif - bool CompareCStringsAsc(const CString &str1, const CString &str2) { return str1.CompareNoCase( str2 ) < 0; @@ -893,41 +835,6 @@ int memicmp(const char *s1, const char *s2, size_t n) } #endif -/* ASCII-only case insensitivity. */ -struct char_traits_char_nocase: public char_traits -{ - static bool eq( char c1, char c2 ) - { return toupper(c1) == toupper(c2); } - - static bool ne( char c1, char c2 ) - { return toupper(c1) != toupper(c2); } - - static bool lt( char c1, char c2 ) - { return toupper(c1) < toupper(c2); } - - static int compare( const char* s1, const char* s2, size_t n ) { - return memicmp( s1, s2, n ); - } - - static inline char fasttoupper(char a) - { - if(a < 'a' || a > 'z') - return a; - return a+('A'-'a'); - } - - static const char *find( const char* s, int n, char a ) { - a = fasttoupper(a); - while( n-- > 0 && fasttoupper(*s) != a ) { - ++s; - } - if(fasttoupper(*s) == a) - return s; - return NULL; - } -}; - - /* Replace &#nnnn; (decimal) &xnnnn; (hex) with corresponding UTF-8 characters. */ void Replace_Unicode_Markers( CString &Text ) { @@ -1002,359 +909,6 @@ CString WcharDisplayText(wchar_t c) return chr; } - - - - - - -typedef basic_string istring; -struct File { - istring name; - bool dir; - - File() { dir=false; } - File(istring fn, bool dir_=false): name(fn), dir(dir) { } - - bool operator== (const File &rhs) const { return name==rhs.name; } - bool operator< (const File &rhs) const { return name files; - RageTimer age; - void LoadFromDir(const CString &dir); - void GetFilesMatching( - const CString &beginning, const CString &containing, const CString &ending, - vector &out, bool bOnlyDirs) const; - void GetFilesEqualTo(const CString &pat, vector &out, bool bOnlyDirs) const; - bool DoesFileExist(const CString &path) const; - bool IsADirectory(const CString &path) const; - bool IsAFile(const CString &path) const; -}; - -void FileSet::LoadFromDir(const CString &dir) -{ - age.GetDeltaTime(); /* reset */ - files.clear(); - - CString oldpath; - GetCwd(oldpath); - if(chdir(dir) == -1) return; - -#if defined(WIN32) - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile( "*", &fd ); - - if( hFind == INVALID_HANDLE_VALUE ) - { - chdir(oldpath); - return; - } - - do { - if(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) - continue; - - File f; - if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - f.dir = true; - f.name=fd.cFileName; - - files.insert(f); - } while( FindNextFile( hFind, &fd ) ); - FindClose(hFind); -#else - DIR *d = opendir("."); - - while(struct dirent *ent = readdir(d)) - { - if(!strcmp(ent->d_name, ".")) continue; - if(!strcmp(ent->d_name, "..")) continue; - - File f; - f.dir = IsADirectory(ent->d_name); - f.name=ent->d_name; - - files.insert(f); - } - - closedir(d); -#endif - chdir(oldpath); -} - -/* Search for "beginning*containing*ending". */ -void FileSet::GetFilesMatching(const CString &beginning, const CString &containing, const CString &ending, vector &out, bool bOnlyDirs) const -{ - set::const_iterator i = files.lower_bound(File(beginning.c_str())); - for( ; i != files.end(); ++i) - { - if(bOnlyDirs && !i->dir) continue; - - /* Check beginning. */ - if(beginning.size() > i->name.size()) continue; /* can't start with it */ - if(strnicmp(i->name.c_str(), beginning.c_str(), beginning.size())) continue; /* doesn't start with it */ - - /* Position the end starts on: */ - int end_pos = int(i->name.size())-int(ending.size()); - - /* Check end. */ - if(end_pos < 0) continue; /* can't end with it */ - if(stricmp(i->name.c_str()+end_pos, ending.c_str())) continue; /* doesn't end with it */ - - /* Check containing. Do this last, since it's the slowest (substring - * search instead of string match). */ - if(containing.size()) - { - unsigned pos = i->name.find(containing, beginning.size()); - if(pos == i->name.npos) continue; /* doesn't contain it */ - if(pos + containing.size() > unsigned(end_pos)) continue; /* found it but it overlaps with the end */ - } - - out.push_back(i->name.c_str()); - } -} - -void FileSet::GetFilesEqualTo(const CString &str, vector &out, bool bOnlyDirs) const -{ - set::const_iterator i = files.find(File(str.c_str())); - if(i == files.end()) - return; - - if(bOnlyDirs && !i->dir) - return; - - out.push_back(i->name.c_str()); -} - -bool FileSet::DoesFileExist(const CString &path) const -{ - return files.find(File(path.c_str())) != files.end(); -} - -bool FileSet::IsADirectory(const CString &path) const -{ - set::const_iterator i = files.find(File(path.c_str())); - if(i == files.end()) - return false; - return i->dir; -} - -bool FileSet::IsAFile(const CString &path) const -{ - set::const_iterator i = files.find(File(path.c_str())); - if(i == files.end()) - return false; - return !i->dir; -} - -/* Given "foo/bar/baz/" or "foo/bar/baz", return "foo/bar/" and "baz". */ -static void SplitPath( CString Path, CString &Dir, CString &Name ) -{ - /* Must always have at least one slash. */ - static Regex split("(.*/)([^/]+)"); - CStringArray match; - if(split.Compare(Path, match)) { - Dir = match[0]; - Name = match[1]; - } else { - /* No slash. */ - Dir = "./"; - Name = Path; - } -} - -bool FilenameDB::DoesFileExist( const CString &sPath ) -{ - CString Dir, Name; - SplitPath(sPath, Dir, Name); - FileSet &fs = GetFileSet(Dir); - return fs.DoesFileExist(Name); -} - -bool FilenameDB::IsAFile( const CString &sPath ) -{ - CString Dir, Name; - SplitPath(sPath, Dir, Name); - FileSet &fs = GetFileSet(Dir); - return fs.IsAFile(Name); -} - -bool FilenameDB::IsADirectory( const CString &sPath ) -{ - CString Dir, Name; - SplitPath(sPath, Dir, Name); - FileSet &fs = GetFileSet(Dir); - return fs.IsADirectory(Name); -} - - -/* XXX: this won't work right for URIs, eg \\foo\bar */ -bool FilenameDB::ResolvePath(CString &path) -{ - if(path == ".") return true; - if(path == "") return true; - - path.Replace("\\", "/"); - - /* Split path into components. */ - vector p; - split(path, "/", p, true); - - /* Resolve each component. Assume the first component is correct. */ - CString ret = p[0]; - for(unsigned i = 1; i < p.size(); ++i) - { - ret += "/"; - - vector lst; - FileSet &fs = GetFileSet(ret); - fs.GetFilesEqualTo(p[i], lst, false); - - /* If there were no matches, the path isn't found. */ - if(lst.empty()) return false; - - if(lst.size() > 1) - LOG->Warn("Ambiguous filenames \"%s\" and \"%s\"", - lst[0].c_str(), lst[1].c_str()); - - ret += lst[0]; - } - - if(path[path.size()-1] == '/') - path = ret + "/"; - else - path = ret; - return true; -} - -void FilenameDB::GetFilesMatching(const CString &dir, const CString &beginning, const CString &containing, const CString &ending, vector &out, bool bOnlyDirs) -{ - FileSet &fs = GetFileSet(dir); - fs.GetFilesMatching(beginning, containing, ending, out, bOnlyDirs); -} - -void FilenameDB::GetFilesEqualTo(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs) -{ - FileSet &fs = GetFileSet(dir); - fs.GetFilesEqualTo(fn, out, bOnlyDirs); -} - - -void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs) -{ - /* Does this contain a wildcard? */ - unsigned first_pos = fn.find_first_of('*'); - if(first_pos == fn.npos) - { - /* No; just do a regular search. */ - GetFilesEqualTo(dir, fn, out, bOnlyDirs); - } else { - unsigned second_pos = fn.find_first_of('*', first_pos+1); - if(second_pos == fn.npos) - { - /* Only one *: "A*B". */ - GetFilesMatching(dir, fn.substr(0, first_pos), "", fn.substr(first_pos+1), out, bOnlyDirs); - } else { - /* Two *s: "A*B*C". */ - GetFilesMatching(dir, - fn.substr(0, first_pos), - fn.substr(first_pos+1, second_pos-first_pos-1), - fn.substr(second_pos+1), out, bOnlyDirs); - } - } -} - -FileSet &FilenameDB::GetFileSet(CString dir, bool ResolveCase) -{ - /* Normalize the path. */ - dir.Replace("\\", "/"); /* foo\bar -> foo/bar */ - dir.Replace("//", "/"); /* foo//bar -> foo/bar */ - - FileSet *ret; - map::iterator i = dirs.find(dir); - bool reload = false; - if(i == dirs.end()) - { - ret = new FileSet; - dirs[dir] = ret; - reload = true; - } - else - { - ret = i->second; - if(ret->age.PeekDeltaTime() > 30) - reload = true; - } - - if(reload) - { - CString RealDir = dir; - if(ResolveCase) - { - /* Resolve path cases (path/Path -> PATH/path). */ - ResolvePath(RealDir); - - /* Alias this name, too. */ - dirs[RealDir] = ret; - } - - ret->LoadFromDir(RealDir); - } - return *ret; -} - -FilenameDB FDB; - -void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo ) -{ - /* If you want the CWD, use ".". */ - ASSERT(!sPath.empty()); - - /* XXX: for case-insensitive resolving, we assume the first element is - * correct (we need a place to start from); so if sPath is relative, - * prepend "./" */ - - /* Strip off the last path element and use it as a mask. */ - unsigned pos = sPath.find_last_of("/\\"); - CString fn; - if(pos != sPath.npos) - { - fn = sPath.substr(pos+1); - sPath = sPath.substr(0, pos+1); - } - - /* If there was only one path element, or if the last element was empty, - * use "*". */ - if(fn.size() == 0) - fn = "*"; - - unsigned start = AddTo.size(); - FDB.GetFilesSimpleMatch(sPath, fn, AddTo, bOnlyDirs); - - if(bReturnPathToo && start < AddTo.size()) - { - FDB.ResolvePath(sPath); - while(start < AddTo.size()) - { - AddTo[start] = sPath + AddTo[start]; - start++; - } - } -} - -void FilenameDB::FlushDirCache() -{ - dirs.clear(); -} - /* Return the last named component of dir: * a/b/c -> c * a/b/c/ -> c @@ -1370,7 +924,6 @@ CString Basename(CString dir) return dir; } - CString Capitalize( CString s ) { if( s.GetLength()==0 ) diff --git a/stepmania/src/RageUtil.h b/stepmania/src/RageUtil.h index d38b2c6214..d153979048 100644 --- a/stepmania/src/RageUtil.h +++ b/stepmania/src/RageUtil.h @@ -13,6 +13,7 @@ */ #include +#include "RageUtil_FileDB.h" //----------------------------------------------------------------------------- // SAFE_ Macros @@ -186,12 +187,6 @@ 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 -bool DoStat(CString sPath, struct stat *st); -bool DoesFileExist( const CString &sPath ); -bool IsAFile( const CString &sPath ); -bool IsADirectory( const CString &sPath ); -unsigned GetFileSizeInBytes( const CString &sFilePath ); - bool CompareCStringsAsc(const CString &str1, const CString &str2); bool CompareCStringsDesc(const CString &str1, const CString &str2); void SortCStringArray( CStringArray &AddTo, const bool bSortAcsending = true ); @@ -223,35 +218,6 @@ public: bool Compare(const CString &str, vector &matches); }; -struct FileSet; -class FilenameDB -{ - FileSet &GetFileSet(CString dir, bool ResolveCase = true); - - /* Directories we have cached: */ - map dirs; - - void GetFilesEqualTo(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs); - void GetFilesMatching(const CString &dir, - const CString &beginning, const CString &containing, const CString &ending, - vector &out, bool bOnlyDirs); - -public: - /* This handles at most one * wildcard. If we need anything more complicated, - * we'll need to use fnmatch or regex. */ - void GetFilesSimpleMatch(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs); - - /* Search for "path" case-insensitively and replace it with the correct - * case. If "path" doesn't exist at all, return false and don't change it. */ - bool ResolvePath(CString &path); - - bool DoesFileExist(const CString &path); - bool IsAFile(const CString &path); - bool IsADirectory(const CString &path); - - void FlushDirCache(); -}; -extern FilenameDB FDB; void Replace_Unicode_Markers( CString &Text ); void ReplaceText( CString &Text, const map &m ); @@ -265,4 +231,39 @@ CString Capitalize( CString s ); #include /* correct place with correct definitions */ #endif +/* ASCII-only case insensitivity. */ +struct char_traits_char_nocase: public char_traits +{ + static bool eq( char c1, char c2 ) + { return toupper(c1) == toupper(c2); } + + static bool ne( char c1, char c2 ) + { return toupper(c1) != toupper(c2); } + + static bool lt( char c1, char c2 ) + { return toupper(c1) < toupper(c2); } + + static int compare( const char* s1, const char* s2, size_t n ) { + return memicmp( s1, s2, n ); + } + + static inline char fasttoupper(char a) + { + if(a < 'a' || a > 'z') + return a; + return a+('A'-'a'); + } + + static const char *find( const char* s, int n, char a ) { + a = fasttoupper(a); + while( n-- > 0 && fasttoupper(*s) != a ) { + ++s; + } + if(fasttoupper(*s) == a) + return s; + return NULL; + } +}; +typedef basic_string istring; + #endif diff --git a/stepmania/src/RageUtil_FileDB.cpp b/stepmania/src/RageUtil_FileDB.cpp new file mode 100644 index 0000000000..a9f3e87550 --- /dev/null +++ b/stepmania/src/RageUtil_FileDB.cpp @@ -0,0 +1,451 @@ +#include "global.h" + +#include "RageUtil_FileDB.h" +#include "RageUtil.h" +#include "RageTimer.h" +#include "RageLog.h" +#include +#include +#include +#include + +#if !defined(WIN32) +#include +#endif + +static void GetCwd(CString &s) +{ + char buf[PATH_MAX]; + bool ret = getcwd(buf, PATH_MAX) != NULL; + ASSERT(ret); + + s = buf; +} + +struct File { + istring name; + bool dir; + + File() { dir=false; } + File(istring fn, bool dir_=false): name(fn), dir(dir) { } + + bool operator== (const File &rhs) const { return name==rhs.name; } + bool operator< (const File &rhs) const { return name files; + RageTimer age; + void LoadFromDir(const CString &dir); + void GetFilesMatching( + const CString &beginning, const CString &containing, const CString &ending, + vector &out, bool bOnlyDirs) const; + void GetFilesEqualTo(const CString &pat, vector &out, bool bOnlyDirs) const; + bool DoesFileExist(const CString &path) const; + bool IsADirectory(const CString &path) const; + bool IsAFile(const CString &path) const; +}; + +void FileSet::LoadFromDir(const CString &dir) +{ + age.GetDeltaTime(); /* reset */ + files.clear(); + + CString oldpath; + GetCwd(oldpath); + if(chdir(dir) == -1) return; + +#if defined(WIN32) + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFile( "*", &fd ); + + if( hFind == INVALID_HANDLE_VALUE ) + { + chdir(oldpath); + return; + } + + do { + if(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) + continue; + + File f; + f.name=fd.cFileName; + f.dir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + + files.insert(f); + } while( FindNextFile( hFind, &fd ) ); + FindClose(hFind); +#else + DIR *d = opendir("."); + + while(struct dirent *ent = readdir(d)) + { + if(!strcmp(ent->d_name, ".")) continue; + if(!strcmp(ent->d_name, "..")) continue; + + File f; + f.name=ent->d_name; + f.dir = IsADirectory(ent->d_name); + + files.insert(f); + } + + closedir(d); +#endif + chdir(oldpath); +} + +/* Search for "beginning*containing*ending". */ +void FileSet::GetFilesMatching(const CString &beginning, const CString &containing, const CString &ending, vector &out, bool bOnlyDirs) const +{ + set::const_iterator i = files.lower_bound(File(beginning.c_str())); + for( ; i != files.end(); ++i) + { + if(bOnlyDirs && !i->dir) continue; + + /* Check beginning. */ + if(beginning.size() > i->name.size()) continue; /* can't start with it */ + if(strnicmp(i->name.c_str(), beginning.c_str(), beginning.size())) continue; /* doesn't start with it */ + + /* Position the end starts on: */ + int end_pos = int(i->name.size())-int(ending.size()); + + /* Check end. */ + if(end_pos < 0) continue; /* can't end with it */ + if(stricmp(i->name.c_str()+end_pos, ending.c_str())) continue; /* doesn't end with it */ + + /* Check containing. Do this last, since it's the slowest (substring + * search instead of string match). */ + if(containing.size()) + { + unsigned pos = i->name.find(containing, beginning.size()); + if(pos == i->name.npos) continue; /* doesn't contain it */ + if(pos + containing.size() > unsigned(end_pos)) continue; /* found it but it overlaps with the end */ + } + + out.push_back(i->name.c_str()); + } +} + +void FileSet::GetFilesEqualTo(const CString &str, vector &out, bool bOnlyDirs) const +{ + set::const_iterator i = files.find(File(str.c_str())); + if(i == files.end()) + return; + + if(bOnlyDirs && !i->dir) + return; + + out.push_back(i->name.c_str()); +} + +bool FileSet::DoesFileExist(const CString &path) const +{ + return files.find(File(path.c_str())) != files.end(); +} + +bool FileSet::IsADirectory(const CString &path) const +{ + set::const_iterator i = files.find(File(path.c_str())); + if(i == files.end()) + return false; + return i->dir; +} + +bool FileSet::IsAFile(const CString &path) const +{ + set::const_iterator i = files.find(File(path.c_str())); + if(i == files.end()) + return false; + return !i->dir; +} + +/* Given "foo/bar/baz/" or "foo/bar/baz", return "foo/bar/" and "baz". */ +static void SplitPath( CString Path, CString &Dir, CString &Name ) +{ + static Regex split("(.*/)([^/]+)"); + CStringArray match; + if(split.Compare(Path, match)) { + /* At least one slash. */ + Dir = match[0]; + Name = match[1]; + } else { + /* No slash. */ + Dir = "./"; + Name = Path; + } +} + + + +class FilenameDB +{ + FileSet &GetFileSet(CString dir, bool ResolveCase = true); + + /* Directories we have cached: */ + map dirs; + + void GetFilesEqualTo(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs); + void GetFilesMatching(const CString &dir, + const CString &beginning, const CString &containing, const CString &ending, + vector &out, bool bOnlyDirs); + +public: + /* This handles at most one * wildcard. If we need anything more complicated, + * we'll need to use fnmatch or regex. */ + void GetFilesSimpleMatch(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs); + + /* Search for "path" case-insensitively and replace it with the correct + * case. If "path" doesn't exist at all, return false and don't change it. */ + bool ResolvePath(CString &path); + + bool DoesFileExist(const CString &path); + bool IsAFile(const CString &path); + bool IsADirectory(const CString &path); + + void FlushDirCache(); +}; + +bool FilenameDB::DoesFileExist( const CString &sPath ) +{ + CString Dir, Name; + SplitPath(sPath, Dir, Name); + FileSet &fs = GetFileSet(Dir.c_str()); + return fs.DoesFileExist(Name); +} + +bool FilenameDB::IsAFile( const CString &sPath ) +{ + CString Dir, Name; + SplitPath(sPath, Dir, Name); + FileSet &fs = GetFileSet(Dir.c_str()); + return fs.IsAFile(Name); +} + +bool FilenameDB::IsADirectory( const CString &sPath ) +{ + CString Dir, Name; + SplitPath(sPath, Dir, Name); + FileSet &fs = GetFileSet(Dir.c_str()); + return fs.IsADirectory(Name); +} + + +/* XXX: this won't work right for URIs, eg \\foo\bar */ +bool FilenameDB::ResolvePath(CString &path) +{ + if(path == ".") return true; + if(path == "") return true; + + path.Replace("\\", "/"); + + /* Split path into components. */ + vector p; + split(path, "/", p, true); + + /* Resolve each component. Assume the first component is correct. XXX + * don't do that! "Songs/" vs "songs/" */ + CString ret = p[0]; + for(unsigned i = 1; i < p.size(); ++i) + { + ret += "/"; + + vector lst; + FileSet &fs = GetFileSet(ret.c_str()); + fs.GetFilesEqualTo(p[i], lst, false); + + /* If there were no matches, the path isn't found. */ + if(lst.empty()) return false; + + if(lst.size() > 1) + LOG->Warn("Ambiguous filenames \"%s\" and \"%s\"", + lst[0].c_str(), lst[1].c_str()); + + ret += lst[0]; + } + + if(path[path.size()-1] == '/') + path = ret + "/"; + else + path = ret; + return true; +} + +void FilenameDB::GetFilesMatching(const CString &dir, const CString &beginning, const CString &containing, const CString &ending, vector &out, bool bOnlyDirs) +{ + FileSet &fs = GetFileSet(dir.c_str()); + fs.GetFilesMatching(beginning, containing, ending, out, bOnlyDirs); +} + +void FilenameDB::GetFilesEqualTo(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs) +{ + FileSet &fs = GetFileSet(dir.c_str()); + fs.GetFilesEqualTo(fn, out, bOnlyDirs); +} + + +void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector &out, bool bOnlyDirs) +{ + /* Does this contain a wildcard? */ + unsigned first_pos = fn.find_first_of('*'); + if(first_pos == fn.npos) + { + /* No; just do a regular search. */ + GetFilesEqualTo(dir, fn, out, bOnlyDirs); + } else { + unsigned second_pos = fn.find_first_of('*', first_pos+1); + if(second_pos == fn.npos) + { + /* Only one *: "A*B". */ + GetFilesMatching(dir, fn.substr(0, first_pos), "", fn.substr(first_pos+1), out, bOnlyDirs); + } else { + /* Two *s: "A*B*C". */ + GetFilesMatching(dir, + fn.substr(0, first_pos), + fn.substr(first_pos+1, second_pos-first_pos-1), + fn.substr(second_pos+1), out, bOnlyDirs); + } + } +} + +FileSet &FilenameDB::GetFileSet(CString dir, bool ResolveCase) +{ + /* Normalize the path. */ + dir.Replace("\\", "/"); /* foo\bar -> foo/bar */ + dir.Replace("//", "/"); /* foo//bar -> foo/bar */ + + FileSet *ret; + map::iterator i = dirs.find(dir.c_str()); + bool reload = false; + if(i == dirs.end()) + { + ret = new FileSet; + dirs[dir.c_str()] = ret; + reload = true; + } + else + { + ret = i->second; + if(ret->age.PeekDeltaTime() > 30) + reload = true; + } + + if(reload) + { + CString RealDir = dir; + if(ResolveCase) + { + /* Resolve path cases (path/Path -> PATH/path). */ + ResolvePath(RealDir); + + /* Alias this name, too. */ + dirs[RealDir.c_str()] = ret; + } + + ret->LoadFromDir(RealDir); + } + return *ret; +} + +void FilenameDB::FlushDirCache() +{ + dirs.clear(); +} + + +FilenameDB FDB; + +void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo ) +{ + /* If you want the CWD, use ".". */ + ASSERT(!sPath.empty()); + + /* XXX: for case-insensitive resolving, we assume the first element is + * correct (we need a place to start from); so if sPath is relative, + * prepend "./" */ + + /* Strip off the last path element and use it as a mask. */ + unsigned pos = sPath.find_last_of("/\\"); + CString fn; + if(pos != sPath.npos) + { + fn = sPath.substr(pos+1); + sPath = sPath.substr(0, pos+1); + } + + /* If there was only one path element, or if the last element was empty, + * use "*". */ + if(fn.size() == 0) + fn = "*"; + + unsigned start = AddTo.size(); + FDB.GetFilesSimpleMatch(sPath, fn, AddTo, bOnlyDirs); + + if(bReturnPathToo && start < AddTo.size()) + { + FDB.ResolvePath(sPath); + while(start < AddTo.size()) + { + AddTo[start] = sPath + AddTo[start]; + start++; + } + } +} + + +#if 0 +bool DoesFileExist( const CString &sPath ) { return FDB.DoesFileExist(sPath); } +bool IsAFile( const CString &sPath ) { return FDB.IsAFile(sPath); } +bool IsADirectory( const CString &sPath ) { return FDB.IsADirectory(sPath); } +#else +bool DoesFileExist( const CString &sPath ) +{ + if(sPath.empty()) return false; + struct stat st; + return DoStat(sPath, &st); +} + +bool IsAFile( const CString &sPath ) +{ + return DoesFileExist(sPath) && !IsADirectory(sPath); +} + +bool IsADirectory( const CString &sPath ) +{ + if(sPath.empty()) return false; + struct stat st; + if (!DoStat(sPath, &st)) + return false; + + return !!(st.st_mode & S_IFDIR); +} +#endif + +/* XXX */ +bool DoStat(CString sPath, struct stat *st) +{ + TrimRight(sPath, "/\\"); + return stat(sPath.c_str(), st) != -1; +} + +unsigned GetFileSizeInBytes( const CString &sFilePath ) +{ + struct stat st; + if(!DoStat(sFilePath, &st)) + return 0; + + return st.st_size; +} + +void FlushDirCache() +{ + FDB.FlushDirCache(); +} diff --git a/stepmania/src/RageUtil_FileDB.h b/stepmania/src/RageUtil_FileDB.h new file mode 100644 index 0000000000..4e14eedd51 --- /dev/null +++ b/stepmania/src/RageUtil_FileDB.h @@ -0,0 +1,12 @@ +#ifndef RAGE_UTIL_FILEDB +#define RAGE_UTIL_FILEDB 1 + +bool DoesFileExist( const CString &sPath ); +bool IsAFile( const CString &sPath ); +bool IsADirectory( const CString &sPath ); +unsigned GetFileSizeInBytes( const CString &sFilePath ); +bool DoStat(CString sPath, struct stat *st); + +void FlushDirCache(); + +#endif diff --git a/stepmania/src/StepMania.dsp b/stepmania/src/StepMania.dsp index dcffefd3fd..e2cc3ea4e4 100644 --- a/stepmania/src/StepMania.dsp +++ b/stepmania/src/StepMania.dsp @@ -1,5 +1,5 @@ # Microsoft Developer Studio Project File - Name="StepMania" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 60000 +# Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 @@ -61,10 +61,10 @@ LINK32=link.exe # SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib # Begin Special Build Tool IntDir=.\../Debug6 -TargetDir=\stepmania\stepmania +TargetDir=\temp\stepmania TargetName=StepMania-debug SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi # End Special Build Tool @@ -82,29 +82,25 @@ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania # PROP Intermediate_Dir "StepMania___Xbox_Debug___VC6" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" -CPP=cl.exe -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "SDL-1.2.5\include" /I "SDL_image-1.2" /I "plib-1.6.0" /D "WIN32" /D "_XBOX" /D "_DEBUG" /Fr /YX"global.h" /FD /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "SDL-1.2.5\include" /I "SDL_image-1.2" /I "plib-1.6.0" /D "WIN32" /D "_XBOX" /D "_DEBUG" /Fr /YX"global.h" /FD /c -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo +XBCP=xbecopy.exe +# ADD BASE XBCP /NOLOGO +# ADD XBCP /NOLOGO +XBE=imagebld.exe +# ADD BASE XBE /nologo /stack:0x10000 /debug +# ADD XBE /nologo /stack:0x10000 /debug LINK32=link.exe # ADD BASE LINK32 $(intdir)\verstub.obj kernel32.lib shell32.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /pdb:"../debug6/StepMania-debug.pdb" /map /debug /machine:IX86 /nodefaultlib:"libcmtd.lib" /out:"../StepMania-debug.exe" # SUBTRACT BASE LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib # ADD LINK32 $(intdir)\verstub.obj kernel32.lib shell32.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /pdb:"../debug6/StepMania-debug.pdb" /map /debug /machine:IX86 /nodefaultlib:"libcmtd.lib" /out:"../StepMania-debug.exe" # SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib -XBE=imagebld.exe -# ADD BASE XBE /nologo /stack:0x10000 /debug -# ADD XBE /nologo /stack:0x10000 /debug -XBCP=xbecopy.exe -# ADD BASE XBCP /NOLOGO -# ADD XBCP /NOLOGO +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "SDL-1.2.5\include" /I "SDL_image-1.2" /I "plib-1.6.0" /D "WIN32" /D "_XBOX" /D "_DEBUG" /Fr /YX"global.h" /FD /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "SDL-1.2.5\include" /I "SDL_image-1.2" /I "plib-1.6.0" /D "WIN32" /D "_XBOX" /D "_DEBUG" /Fr /YX"global.h" /FD /c # Begin Special Build Tool -IntDir=.\StepMania___Xbox_Debug___VC6 -TargetDir=.\StepMania___Xbox_Debug___VC6 -TargetName=StepMania -SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi # End Special Build Tool @@ -141,10 +137,10 @@ LINK32=link.exe # SUBTRACT LINK32 /verbose /pdb:none # Begin Special Build Tool IntDir=.\../Release6 -TargetDir=\stepmania\stepmania +TargetDir=\temp\stepmania TargetName=StepMania SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi # End Special Build Tool @@ -669,6 +665,23 @@ SOURCE=.\RageUtil_CharConversions.cpp SOURCE=.\RageUtil_CharConversions.h # End Source File +# Begin Source File + +SOURCE=.\RageUtil_FileDB.cpp + +!IF "$(CFG)" == "StepMania - Win32 Debug" + +!ELSEIF "$(CFG)" == "StepMania - Xbox Debug" + +!ELSEIF "$(CFG)" == "StepMania - Win32 Release" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\RageUtil_FileDB.h +# End Source File # End Group # Begin Group "Data Structures" diff --git a/stepmania/src/StepMania.vcproj b/stepmania/src/StepMania.vcproj index 741bc66375..4efed0b6db 100644 --- a/stepmania/src/StepMania.vcproj +++ b/stepmania/src/StepMania.vcproj @@ -1605,6 +1605,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\ + + + + diff --git a/stepmania/src/ThemeManager.cpp b/stepmania/src/ThemeManager.cpp index ece62d900f..bdddc17dfa 100644 --- a/stepmania/src/ThemeManager.cpp +++ b/stepmania/src/ThemeManager.cpp @@ -154,7 +154,7 @@ try_element_again: if( asElementPaths.size() > 1 ) { - FDB.FlushDirCache(); + FlushDirCache(); CString message = ssprintf( "There is more than one theme element element that matches "