Files
itgmania212121/stepmania/src/RageUtil_FileDB.cpp
T

465 lines
12 KiB
C++
Raw Normal View History

2003-07-03 02:41:27 +00:00
#include "global.h"
#include "RageUtil_FileDB.h"
#include "RageUtil.h"
#include "RageLog.h"
2003-07-22 07:47:27 +00:00
#include "arch/arch.h"
2003-07-03 02:41:27 +00:00
/* Search for "beginning*containing*ending". */
void FileSet::GetFilesMatching(const CString &beginning, const CString &containing, const CString &ending, vector<CString> &out, bool bOnlyDirs) const
{
2003-10-19 22:20:56 +00:00
/* "files" is a case-insensitive mapping, by filename. Use lower_bound to figure
* out where to start. */
CString containing_lower = containing;
containing_lower.ToLower();
2003-12-06 05:50:02 +00:00
set<File>::const_iterator i = files.lower_bound( File(beginning) );
2003-07-03 02:41:27 +00:00
for( ; i != files.end(); ++i)
{
if(bOnlyDirs && !i->dir) continue;
2003-10-19 22:20:56 +00:00
/* Check beginning. Once we hit a filename that no longer matches beginning,
* we're past all possible matches in the sort, so stop. */
if(beginning.size() > i->name.size()) break; /* can't start with it */
2003-12-06 05:50:02 +00:00
if(strnicmp(i->name, beginning, beginning.size())) break; /* doesn't start with it */
2003-07-03 02:41:27 +00:00
/* 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 */
2003-12-06 05:50:02 +00:00
if( stricmp(i->name.c_str()+end_pos, ending) ) continue; /* doesn't end with it */
2003-07-03 02:41:27 +00:00
/* Check containing. Do this last, since it's the slowest (substring
* search instead of string match). */
if(containing.size())
{
CString name = i->name;
name.ToLower();
size_t pos = name.find( containing_lower, beginning.size() );
if(pos == name.npos) continue; /* doesn't contain it */
2003-07-03 02:41:27 +00:00
if(pos + containing.size() > unsigned(end_pos)) continue; /* found it but it overlaps with the end */
}
2003-12-06 05:50:02 +00:00
out.push_back( i->name );
2003-07-03 02:41:27 +00:00
}
}
void FileSet::GetFilesEqualTo(const CString &str, vector<CString> &out, bool bOnlyDirs) const
{
2003-12-06 05:50:02 +00:00
set<File>::const_iterator i = files.find( File(str) );
2003-07-03 02:41:27 +00:00
if(i == files.end())
return;
if(bOnlyDirs && !i->dir)
return;
2003-12-06 05:50:02 +00:00
out.push_back( i->name );
2003-07-03 02:41:27 +00:00
}
2004-01-06 05:44:28 +00:00
RageFileManager::FileType FileSet::GetFileType(const CString &path ) const
2003-07-03 02:41:27 +00:00
{
2003-12-06 05:50:02 +00:00
set<File>::const_iterator i = files.find( File(path) );
2003-07-03 02:41:27 +00:00
if(i == files.end())
2004-01-06 05:44:28 +00:00
return RageFileManager::TYPE_NONE;
2003-07-03 02:41:27 +00:00
2004-01-06 05:44:28 +00:00
return i->dir? RageFileManager::TYPE_DIR:RageFileManager::TYPE_FILE;
2003-07-03 02:41:27 +00:00
}
2003-09-22 05:14:00 +00:00
int FileSet::GetFileSize(const CString &path) const
{
2003-12-06 05:50:02 +00:00
set<File>::const_iterator i = files.find( File(path) );
2003-09-22 05:14:00 +00:00
if(i == files.end())
return -1;
return i->size;
}
2003-12-10 07:07:42 +00:00
int FileSet::GetFileHash(const CString &path) const
2003-09-23 00:09:10 +00:00
{
2003-12-06 05:50:02 +00:00
set<File>::const_iterator i = files.find( File(path) );
2003-09-23 00:09:10 +00:00
if(i == files.end())
return -1;
2003-12-10 07:07:42 +00:00
return i->hash + i->size;
2003-09-23 00:09:10 +00:00
}
2003-12-11 07:24:32 +00:00
/*
* Given "foo/bar/baz/" or "foo/bar/baz", return "foo/bar/" and "baz".
* "foo" -> "", "foo"
*/
2003-07-03 02:41:27 +00:00
static void SplitPath( CString Path, CString &Dir, CString &Name )
{
2003-12-11 07:24:32 +00:00
CollapsePath( Path );
if( Path.Right(1) == "/" )
2003-09-06 22:55:20 +00:00
Path.erase( Path.size()-1 );
2004-06-16 07:01:12 +00:00
size_t sep = Path.find_last_of( '/' );
2003-12-11 07:24:32 +00:00
if( sep == CString::npos )
{
Dir = "";
2003-07-03 02:41:27 +00:00
Name = Path;
}
2003-12-11 07:24:32 +00:00
else
{
Dir = Path.substr( 0, sep+1 );
Name = Path.substr( sep+1 );
}
2003-07-03 02:41:27 +00:00
}
2004-01-06 05:44:28 +00:00
RageFileManager::FileType FilenameDB::GetFileType( const CString &sPath )
2003-07-03 02:41:27 +00:00
{
CString Dir, Name;
2003-12-06 06:17:21 +00:00
SplitPath( sPath, Dir, Name );
2003-12-15 02:40:41 +00:00
if( Name == "." )
2004-01-06 05:44:28 +00:00
return RageFileManager::TYPE_DIR;
2003-12-15 02:40:41 +00:00
2004-08-30 04:49:57 +00:00
const FileSet *fs = GetFileSet( Dir );
2003-12-16 07:22:43 +00:00
return fs->GetFileType( Name );
2003-07-03 02:41:27 +00:00
}
2003-09-22 05:14:00 +00:00
int FilenameDB::GetFileSize( const CString &sPath )
{
CString Dir, Name;
SplitPath(sPath, Dir, Name);
2004-08-30 04:49:57 +00:00
const FileSet *fs = GetFileSet( Dir );
2003-12-16 07:22:43 +00:00
return fs->GetFileSize(Name);
2003-09-22 05:14:00 +00:00
}
2003-12-10 07:07:42 +00:00
int FilenameDB::GetFileHash( const CString &sPath )
2003-09-23 00:09:10 +00:00
{
CString Dir, Name;
SplitPath(sPath, Dir, Name);
2004-08-30 04:49:57 +00:00
const FileSet *fs = GetFileSet( Dir );
2003-12-16 07:22:43 +00:00
return fs->GetFileHash(Name);
2003-09-23 00:09:10 +00:00
}
2003-07-03 02:41:27 +00:00
/* path should be fully collapsed, so we can operate in-place: no . or .. */
2003-07-03 02:41:27 +00:00
bool FilenameDB::ResolvePath(CString &path)
{
2004-02-27 05:49:24 +00:00
if( path == "." || path == "" )
return true;
2003-07-03 02:41:27 +00:00
/* Split path into components. */
2003-12-15 05:36:27 +00:00
int begin = 0, size = -1;
2003-07-03 02:41:27 +00:00
2003-12-15 02:40:41 +00:00
/* Resolve each component. */
CString ret = "";
2004-08-30 04:49:57 +00:00
const FileSet *fs = NULL;
2003-12-15 05:36:27 +00:00
File *prev_file = NULL;
static const CString slash("/");
while( 1 )
2003-07-03 02:41:27 +00:00
{
2003-12-15 05:36:27 +00:00
split( path, slash, begin, size, true );
if( begin == (int) path.size() )
break;
if( fs == NULL )
2003-12-16 07:22:43 +00:00
fs = GetFileSet( ret );
2003-07-03 02:41:27 +00:00
2003-12-15 05:36:27 +00:00
CString p = path.substr( begin, size );
2004-06-16 00:38:31 +00:00
ASSERT_M( p.size() != 1 || p[0] != '.', path ); // no .
ASSERT_M( p.size() != 2 || p[0] != '.' || p[1] != '.', path ); // no ..
2004-08-30 04:49:57 +00:00
set<File>::const_iterator it = fs->files.find( File(p) );
2003-07-03 02:41:27 +00:00
/* If there were no matches, the path isn't found. */
2003-12-15 05:36:27 +00:00
if( it == fs->files.end() )
return false;
2003-07-03 02:41:27 +00:00
2003-12-15 05:36:27 +00:00
if( prev_file )
prev_file->dirp = fs;
2003-12-15 08:13:14 +00:00
prev_file = (File*) &*it;
2003-07-03 02:41:27 +00:00
2003-12-15 05:36:27 +00:00
if( ret.size() != 0 )
ret += "/";
ret += it->name;
2003-07-03 02:41:27 +00:00
2003-12-15 05:36:27 +00:00
fs = it->dirp;
}
if( path.size() && path[path.size()-1] == '/' )
2003-12-10 09:47:36 +00:00
path = ret + "/";
2003-07-03 02:41:27 +00:00
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)
{
2004-08-30 04:49:57 +00:00
const FileSet *fs = GetFileSet( dir );
2003-12-16 07:22:43 +00:00
fs->GetFilesMatching(beginning, containing, ending, out, bOnlyDirs);
2003-07-03 02:41:27 +00:00
}
void FilenameDB::GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs)
{
2004-08-30 04:49:57 +00:00
const FileSet *fs = GetFileSet( dir );
2003-12-16 07:22:43 +00:00
fs->GetFilesEqualTo(fn, out, bOnlyDirs);
2003-07-03 02:41:27 +00:00
}
void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs)
{
/* Does this contain a wildcard? */
2004-06-16 07:01:12 +00:00
size_t first_pos = fn.find_first_of('*');
2003-07-03 02:41:27 +00:00
if(first_pos == fn.npos)
{
/* No; just do a regular search. */
GetFilesEqualTo(dir, fn, out, bOnlyDirs);
} else {
2004-06-16 07:01:12 +00:00
size_t second_pos = fn.find_first_of('*', first_pos+1);
2003-07-03 02:41:27 +00:00
if(second_pos == fn.npos)
{
/* Only one *: "A*B". */
2003-09-03 04:30:29 +00:00
/* XXX: "_blank.png*.png" shouldn't match the file "_blank.png". */
2003-07-03 02:41:27 +00:00
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);
}
}
}
2003-12-16 07:22:43 +00:00
FileSet *FilenameDB::GetFileSet( CString dir, bool create )
2003-07-03 02:41:27 +00:00
{
/* Normalize the path. */
2003-12-10 09:47:36 +00:00
dir.Replace("\\", "/"); /* foo\bar -> foo/bar */
dir.Replace("//", "/"); /* foo//bar -> foo/bar */
2003-12-07 04:14:52 +00:00
2003-12-11 07:24:32 +00:00
if( dir == "" )
dir = ".";
2003-12-06 05:50:02 +00:00
CString lower = dir;
lower.MakeLower();
2003-07-03 02:41:27 +00:00
2003-12-06 08:22:03 +00:00
map<CString, FileSet *>::iterator i = dirs.find( lower );
2003-12-16 07:22:43 +00:00
if( !create )
{
if( i == dirs.end() )
return NULL;
return i->second;
}
2004-08-31 09:35:12 +00:00
FileSet *ret;
if( i != dirs.end() )
2003-07-03 02:41:27 +00:00
{
2004-08-31 09:35:12 +00:00
if( ExpireSeconds != -1 && i->second->age.PeekDeltaTime() >= ExpireSeconds )
{
2004-08-31 09:35:12 +00:00
/* The data has expired. Clear it, but don't delete it, so w
* don't break dirp pointers. */
ret = i->second;
ret->age.Touch();
ret->files.clear();
} else
2003-12-16 07:22:43 +00:00
return i->second;
2004-08-31 09:35:12 +00:00
}
else
{
ret = new FileSet;
AddFileSet( dir, ret );
2003-07-03 02:41:27 +00:00
}
2003-12-07 04:14:52 +00:00
PopulateFileSet( *ret, dir );
2003-12-16 07:22:43 +00:00
return ret;
2003-07-03 02:41:27 +00:00
}
2003-12-07 04:14:52 +00:00
void FilenameDB::AddFileSet( CString sPath, FileSet *fs )
{
sPath.MakeLower();
2003-12-07 06:15:08 +00:00
map<CString, FileSet *>::iterator it = dirs.find( sPath );
if( it != dirs.end() )
delete it->second;
2003-12-07 04:14:52 +00:00
dirs[sPath] = fs;
}
2003-12-08 00:04:47 +00:00
/* Add the file or directory "sPath". sPath is a directory if it ends with
* a slash. */
2003-12-10 07:07:42 +00:00
void FilenameDB::AddFile( const CString &sPath, int size, int hash, void *priv )
2003-12-08 00:04:47 +00:00
{
2004-04-22 20:17:27 +00:00
if( sPath == "" )
return;
2003-12-08 00:04:47 +00:00
vector<CString> parts;
split( sPath, "/", parts, false );
CStringArray::const_iterator begin = parts.begin();
CStringArray::const_iterator end = parts.end();
bool IsDir = true;
if( sPath[sPath.size()-1] != '/' )
IsDir = false;
else
--end;
do
{
/* Combine all but the last part. */
2004-02-11 20:36:48 +00:00
CString dir = join( "/", begin, end-1 );
if( dir != "" )
dir += "/";
2003-12-08 00:04:47 +00:00
const CString &fn = *(end-1);
2003-12-16 07:22:43 +00:00
FileSet *fs = GetFileSet( dir );
2003-12-08 00:04:47 +00:00
File f;
f.SetName( fn );
2003-12-16 07:22:43 +00:00
if( fs->files.find( f ) == fs->files.end() )
2003-12-08 00:04:47 +00:00
{
f.dir = IsDir;
if( !IsDir )
{
f.size = size;
2003-12-10 07:07:42 +00:00
f.hash = hash;
f.priv = priv;
}
2003-12-16 07:22:43 +00:00
fs->files.insert( f );
2003-12-08 00:04:47 +00:00
}
IsDir = true;
--end;
} while( begin != end );
}
2003-12-16 07:22:43 +00:00
void FilenameDB::DelFile( const CString &sPath )
{
CString lower = sPath;
lower.MakeLower();
map<CString, FileSet *>::iterator fsi = dirs.find( lower );
if( fsi != dirs.end() )
{
FileSet *fs = fsi->second;
/* Remove any stale dirp pointers. */
for( map<CString, FileSet *>::iterator it = dirs.begin(); it != dirs.end(); ++it )
{
FileSet *Clean = it->second;
for( set<File>::iterator f = Clean->files.begin(); f != Clean->files.end(); ++f )
2003-12-16 20:35:09 +00:00
{
File &ff = (File &) *f;
if( ff.dirp == fs )
ff.dirp = NULL;
}
2003-12-16 07:22:43 +00:00
}
delete fs;
dirs.erase( fsi );
}
/* Delete sPath from its parent. */
CString Dir, Name;
SplitPath(sPath, Dir, Name);
FileSet *Parent = GetFileSet( Dir, false );
if( Parent )
{
set<File>::iterator i = Parent->files.find( File(Name) );
if( i != Parent->files.end() )
{
Parent->files.erase( i );
}
}
}
2003-07-03 02:41:27 +00:00
void FilenameDB::FlushDirCache()
{
2003-12-06 05:50:02 +00:00
for( map<CString, FileSet *>::iterator i = dirs.begin(); i != dirs.end(); ++i )
2003-11-11 17:51:06 +00:00
delete i->second;
2003-07-03 02:41:27 +00:00
dirs.clear();
}
2004-08-30 04:49:57 +00:00
const File *FilenameDB::GetFile( const CString &sPath )
{
CString Dir, Name;
SplitPath(sPath, Dir, Name);
2003-12-16 07:22:43 +00:00
FileSet *fs = GetFileSet( Dir );
2003-12-10 07:27:18 +00:00
set<File>::iterator it;
2003-12-16 07:22:43 +00:00
it = fs->files.find( File(Name) );
if( it == fs->files.end() )
return NULL;
2003-12-10 07:27:18 +00:00
/* Oops. &*it is a const File &, because you can't change the order
* of something once it's in a list. Cast away the const; we won't
* change the filename (used for the ordering), but the rest of the
* values are non-const. */
return const_cast<File *> (&*it);
}
2004-09-05 03:40:19 +00:00
const void *FilenameDB::GetFilePriv( const CString &path )
{
const File *pFile = GetFile( path );
void *pRet = NULL;
if( pFile != NULL )
pRet = pFile->priv;
return pRet;
}
2003-07-03 02:41:27 +00:00
2003-12-07 04:14:52 +00:00
void FilenameDB::GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
2003-07-03 02:41:27 +00:00
{
2003-07-22 07:47:27 +00:00
// LOG->Trace( "GetDirListing( %s )", sPath.c_str() );
2003-07-03 02:41:27 +00:00
/* If you want the CWD, use ".". */
ASSERT(!sPath.empty());
/* Strip off the last path element and use it as a mask. */
2004-06-16 07:01:12 +00:00
size_t pos = sPath.find_last_of( '/' );
2003-07-03 02:41:27 +00:00
CString fn;
2003-12-18 08:48:36 +00:00
if( pos == sPath.npos )
2003-07-03 02:41:27 +00:00
{
2003-12-18 08:48:36 +00:00
fn = sPath;
sPath = "";
} else {
2003-07-03 02:41:27 +00:00
fn = sPath.substr(pos+1);
sPath = sPath.substr(0, pos+1);
}
2003-12-18 08:48:36 +00:00
/* If the last element was empty, use "*". */
if( fn.size() == 0 )
2003-07-03 02:41:27 +00:00
fn = "*";
unsigned start = AddTo.size();
2003-12-07 04:14:52 +00:00
GetFilesSimpleMatch(sPath, fn, AddTo, bOnlyDirs);
2003-07-03 02:41:27 +00:00
if(bReturnPathToo && start < AddTo.size())
{
2003-12-07 04:14:52 +00:00
ResolvePath(sPath);
2003-07-03 02:41:27 +00:00
while(start < AddTo.size())
{
AddTo[start] = sPath + AddTo[start];
start++;
}
}
}
2004-05-06 02:40:33 +00:00
/*
* Copyright (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/