split off FDB
This commit is contained in:
+3
-450
@@ -11,22 +11,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
|
#include "RageUtil_FileDB.h"
|
||||||
|
#include "RageTimer.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "RageTimer.h"
|
|
||||||
#include "RageLog.h"
|
|
||||||
|
|
||||||
#if !defined(WIN32)
|
|
||||||
#include <dirent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned long randseed = time(NULL);
|
unsigned long randseed = time(NULL);
|
||||||
|
|
||||||
@@ -348,15 +341,6 @@ void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bRe
|
|||||||
}
|
}
|
||||||
#endif
|
#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.
|
/* Reference: http://www.theorem.com/java/CRC32.java, rewritten by Glenn Maynard.
|
||||||
* Public domain. */
|
* Public domain. */
|
||||||
unsigned int GetHashForString ( CString s )
|
unsigned int GetHashForString ( CString s )
|
||||||
@@ -385,12 +369,6 @@ unsigned int GetHashForString ( CString s )
|
|||||||
return crc;
|
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 GetHashForFile( CString sPath )
|
||||||
{
|
{
|
||||||
unsigned int hash = 0;
|
unsigned int hash = 0;
|
||||||
@@ -423,42 +401,6 @@ unsigned int GetHashForDirectory( CString sDir )
|
|||||||
return hash;
|
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)
|
bool CompareCStringsAsc(const CString &str1, const CString &str2)
|
||||||
{
|
{
|
||||||
return str1.CompareNoCase( str2 ) < 0;
|
return str1.CompareNoCase( str2 ) < 0;
|
||||||
@@ -893,41 +835,6 @@ int memicmp(const char *s1, const char *s2, size_t n)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ASCII-only case insensitivity. */
|
|
||||||
struct char_traits_char_nocase: public char_traits<char>
|
|
||||||
{
|
|
||||||
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. */
|
/* Replace &#nnnn; (decimal) &xnnnn; (hex) with corresponding UTF-8 characters. */
|
||||||
void Replace_Unicode_Markers( CString &Text )
|
void Replace_Unicode_Markers( CString &Text )
|
||||||
{
|
{
|
||||||
@@ -1002,359 +909,6 @@ CString WcharDisplayText(wchar_t c)
|
|||||||
return chr;
|
return chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef basic_string<char,char_traits_char_nocase> 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<rhs.name; }
|
|
||||||
|
|
||||||
bool equal(const File &rhs) const { return name == rhs.name; }
|
|
||||||
bool equal(const CString &rhs) const {
|
|
||||||
return !stricmp(name.c_str(), rhs.c_str());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileSet
|
|
||||||
{
|
|
||||||
set<File> files;
|
|
||||||
RageTimer age;
|
|
||||||
void LoadFromDir(const CString &dir);
|
|
||||||
void GetFilesMatching(
|
|
||||||
const CString &beginning, const CString &containing, const CString &ending,
|
|
||||||
vector<CString> &out, bool bOnlyDirs) const;
|
|
||||||
void GetFilesEqualTo(const CString &pat, vector<CString> &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<CString> &out, bool bOnlyDirs) const
|
|
||||||
{
|
|
||||||
set<File>::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<CString> &out, bool bOnlyDirs) const
|
|
||||||
{
|
|
||||||
set<File>::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<File>::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<File>::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<CString> 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<CString> 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<CString> &out, bool bOnlyDirs)
|
|
||||||
{
|
|
||||||
FileSet &fs = GetFileSet(dir);
|
|
||||||
fs.GetFilesMatching(beginning, containing, ending, out, bOnlyDirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilenameDB::GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs)
|
|
||||||
{
|
|
||||||
FileSet &fs = GetFileSet(dir);
|
|
||||||
fs.GetFilesEqualTo(fn, out, bOnlyDirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector<CString> &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<CString, FileSet *>::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:
|
/* Return the last named component of dir:
|
||||||
* a/b/c -> c
|
* a/b/c -> c
|
||||||
* a/b/c/ -> c
|
* a/b/c/ -> c
|
||||||
@@ -1370,7 +924,6 @@ CString Basename(CString dir)
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CString Capitalize( CString s )
|
CString Capitalize( CString s )
|
||||||
{
|
{
|
||||||
if( s.GetLength()==0 )
|
if( s.GetLength()==0 )
|
||||||
|
|||||||
+36
-35
@@ -13,6 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include "RageUtil_FileDB.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// SAFE_ Macros
|
// SAFE_ Macros
|
||||||
@@ -186,12 +187,6 @@ unsigned int GetHashForString( CString s );
|
|||||||
unsigned int GetHashForFile( CString sPath );
|
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
|
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 CompareCStringsAsc(const CString &str1, const CString &str2);
|
||||||
bool CompareCStringsDesc(const CString &str1, const CString &str2);
|
bool CompareCStringsDesc(const CString &str1, const CString &str2);
|
||||||
void SortCStringArray( CStringArray &AddTo, const bool bSortAcsending = true );
|
void SortCStringArray( CStringArray &AddTo, const bool bSortAcsending = true );
|
||||||
@@ -223,35 +218,6 @@ public:
|
|||||||
bool Compare(const CString &str, vector<CString> &matches);
|
bool Compare(const CString &str, vector<CString> &matches);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileSet;
|
|
||||||
class FilenameDB
|
|
||||||
{
|
|
||||||
FileSet &GetFileSet(CString dir, bool ResolveCase = true);
|
|
||||||
|
|
||||||
/* Directories we have cached: */
|
|
||||||
map<CString, FileSet *> dirs;
|
|
||||||
|
|
||||||
void GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs);
|
|
||||||
void GetFilesMatching(const CString &dir,
|
|
||||||
const CString &beginning, const CString &containing, const CString &ending,
|
|
||||||
vector<CString> &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<CString> &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 Replace_Unicode_Markers( CString &Text );
|
||||||
void ReplaceText( CString &Text, const map<CString,CString> &m );
|
void ReplaceText( CString &Text, const map<CString,CString> &m );
|
||||||
@@ -265,4 +231,39 @@ CString Capitalize( CString s );
|
|||||||
#include <unistd.h> /* correct place with correct definitions */
|
#include <unistd.h> /* correct place with correct definitions */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* ASCII-only case insensitivity. */
|
||||||
|
struct char_traits_char_nocase: public char_traits<char>
|
||||||
|
{
|
||||||
|
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<char,char_traits_char_nocase> istring;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,451 @@
|
|||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include "RageUtil_FileDB.h"
|
||||||
|
#include "RageUtil.h"
|
||||||
|
#include "RageTimer.h"
|
||||||
|
#include "RageLog.h"
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#if !defined(WIN32)
|
||||||
|
#include <dirent.h>
|
||||||
|
#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<rhs.name; }
|
||||||
|
|
||||||
|
bool equal(const File &rhs) const { return name == rhs.name; }
|
||||||
|
bool equal(const CString &rhs) const {
|
||||||
|
return !stricmp(name.c_str(), rhs.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This represents a directory. */
|
||||||
|
struct FileSet
|
||||||
|
{
|
||||||
|
set<File> files;
|
||||||
|
RageTimer age;
|
||||||
|
void LoadFromDir(const CString &dir);
|
||||||
|
void GetFilesMatching(
|
||||||
|
const CString &beginning, const CString &containing, const CString &ending,
|
||||||
|
vector<CString> &out, bool bOnlyDirs) const;
|
||||||
|
void GetFilesEqualTo(const CString &pat, vector<CString> &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<CString> &out, bool bOnlyDirs) const
|
||||||
|
{
|
||||||
|
set<File>::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<CString> &out, bool bOnlyDirs) const
|
||||||
|
{
|
||||||
|
set<File>::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<File>::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<File>::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<istring, FileSet *> dirs;
|
||||||
|
|
||||||
|
void GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs);
|
||||||
|
void GetFilesMatching(const CString &dir,
|
||||||
|
const CString &beginning, const CString &containing, const CString &ending,
|
||||||
|
vector<CString> &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<CString> &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<CString> 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<CString> 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<CString> &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<CString> &out, bool bOnlyDirs)
|
||||||
|
{
|
||||||
|
FileSet &fs = GetFileSet(dir.c_str());
|
||||||
|
fs.GetFilesEqualTo(fn, out, bOnlyDirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector<CString> &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<istring, FileSet *>::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();
|
||||||
|
}
|
||||||
@@ -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
|
||||||
+35
-22
@@ -1,5 +1,5 @@
|
|||||||
# Microsoft Developer Studio Project File - Name="StepMania" - Package Owner=<4>
|
# 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 **
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
# TARGTYPE "Win32 (x86) Application" 0x0101
|
# TARGTYPE "Win32 (x86) Application" 0x0101
|
||||||
@@ -61,10 +61,10 @@ LINK32=link.exe
|
|||||||
# SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib
|
# SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib
|
||||||
# Begin Special Build Tool
|
# Begin Special Build Tool
|
||||||
IntDir=.\../Debug6
|
IntDir=.\../Debug6
|
||||||
TargetDir=\stepmania\stepmania
|
TargetDir=\temp\stepmania
|
||||||
TargetName=StepMania-debug
|
TargetName=StepMania-debug
|
||||||
SOURCE="$(InputPath)"
|
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
|
PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi
|
||||||
# End Special Build Tool
|
# 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 Intermediate_Dir "StepMania___Xbox_Debug___VC6"
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
CPP=cl.exe
|
XBCP=xbecopy.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 BASE XBCP /NOLOGO
|
||||||
# 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
|
# ADD XBCP /NOLOGO
|
||||||
BSC32=bscmake.exe
|
XBE=imagebld.exe
|
||||||
# ADD BASE BSC32 /nologo
|
# ADD BASE XBE /nologo /stack:0x10000 /debug
|
||||||
# ADD BSC32 /nologo
|
# ADD XBE /nologo /stack:0x10000 /debug
|
||||||
LINK32=link.exe
|
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"
|
# 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
|
# 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"
|
# 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
|
# SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib
|
||||||
XBE=imagebld.exe
|
BSC32=bscmake.exe
|
||||||
# ADD BASE XBE /nologo /stack:0x10000 /debug
|
# ADD BASE BSC32 /nologo
|
||||||
# ADD XBE /nologo /stack:0x10000 /debug
|
# ADD BSC32 /nologo
|
||||||
XBCP=xbecopy.exe
|
CPP=cl.exe
|
||||||
# ADD BASE XBCP /NOLOGO
|
# 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 XBCP /NOLOGO
|
# 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
|
# Begin Special Build Tool
|
||||||
IntDir=.\StepMania___Xbox_Debug___VC6
|
PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\
|
||||||
TargetDir=.\StepMania___Xbox_Debug___VC6
|
|
||||||
TargetName=StepMania
|
|
||||||
SOURCE="$(InputPath)"
|
|
||||||
PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\
|
|
||||||
PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi
|
PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi
|
||||||
# End Special Build Tool
|
# End Special Build Tool
|
||||||
|
|
||||||
@@ -141,10 +137,10 @@ LINK32=link.exe
|
|||||||
# SUBTRACT LINK32 /verbose /pdb:none
|
# SUBTRACT LINK32 /verbose /pdb:none
|
||||||
# Begin Special Build Tool
|
# Begin Special Build Tool
|
||||||
IntDir=.\../Release6
|
IntDir=.\../Release6
|
||||||
TargetDir=\stepmania\stepmania
|
TargetDir=\temp\stepmania
|
||||||
TargetName=StepMania
|
TargetName=StepMania
|
||||||
SOURCE="$(InputPath)"
|
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
|
PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi
|
||||||
# End Special Build Tool
|
# End Special Build Tool
|
||||||
|
|
||||||
@@ -669,6 +665,23 @@ SOURCE=.\RageUtil_CharConversions.cpp
|
|||||||
|
|
||||||
SOURCE=.\RageUtil_CharConversions.h
|
SOURCE=.\RageUtil_CharConversions.h
|
||||||
# End Source File
|
# 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
|
# End Group
|
||||||
# Begin Group "Data Structures"
|
# Begin Group "Data Structures"
|
||||||
|
|
||||||
|
|||||||
@@ -1605,6 +1605,12 @@ cl /Zl /nologo /c verstub.cpp /Fo"$(IntDir)"\
|
|||||||
<File
|
<File
|
||||||
RelativePath="RageUtil_CharConversions.h">
|
RelativePath="RageUtil_CharConversions.h">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="RageUtil_FileDB.cpp">
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="RageUtil_FileDB.h">
|
||||||
|
</File>
|
||||||
<Filter
|
<Filter
|
||||||
Name="Helpers"
|
Name="Helpers"
|
||||||
Filter="">
|
Filter="">
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ try_element_again:
|
|||||||
|
|
||||||
if( asElementPaths.size() > 1 )
|
if( asElementPaths.size() > 1 )
|
||||||
{
|
{
|
||||||
FDB.FlushDirCache();
|
FlushDirCache();
|
||||||
|
|
||||||
CString message = ssprintf(
|
CString message = ssprintf(
|
||||||
"There is more than one theme element element that matches "
|
"There is more than one theme element element that matches "
|
||||||
|
|||||||
Reference in New Issue
Block a user