Files
itgmania212121/stepmania/src/RageFileManager.cpp
T

699 lines
19 KiB
C++
Raw Normal View History

2003-12-04 08:25:59 +00:00
#include "global.h"
2003-12-12 09:06:31 +00:00
#include "StepMania.h"
2003-12-04 08:25:59 +00:00
#include "RageFileManager.h"
#include "RageFileDriver.h"
#include "RageUtil.h"
#include "RageUtil_FileDB.h"
2003-12-06 05:52:44 +00:00
#include "RageLog.h"
2004-01-14 23:44:42 +00:00
#include "RageThreads.h"
2003-12-12 22:27:21 +00:00
2004-04-05 05:22:32 +00:00
#include <cerrno>
2003-12-12 22:27:21 +00:00
#if defined(LINUX)
#include <sys/stat.h>
#endif
2003-12-04 08:25:59 +00:00
RageFileManager *FILEMAN = NULL;
2004-01-14 23:44:42 +00:00
/* Lock this before touching any of these globals (except FILEMAN itself). */
static RageMutex *g_Mutex;
CString InitialWorkingDirectory;
CString DirOfExecutable;
typedef map< const RageFileObj *, RageFileDriver * > FileReferences;
static FileReferences g_Refs;
struct LoadedDriver
{
/* A loaded driver may have a base path, which modifies the path we
* pass to the driver. For example, if the base is "Songs/", and we
* want to send the path "Songs/Foo/Bar" to it, then we actually
* only send "Foo/Bar". The path "Themes/Foo" is out of the scope
* of the driver, and GetPath returns false. */
RageFileDriver *driver;
CString Type, Root, MountPoint;
LoadedDriver() { driver = NULL; }
CString GetPath( CString path );
};
static vector<LoadedDriver> g_Drivers;
2003-12-12 22:27:21 +00:00
// Mountpoints as directories cause a problem. If "Themes/default" is a mountpoint, and
// doesn't exist anywhere else, then GetDirListing("Themes/*") must return "default". The
// driver containing "Themes/default" won't do this; its world view begins at "BGAnimations"
// (inside "Themes/default"). We need a dummy driver that handles mountpoints. */
class RageFileDriverMountpoints: public RageFileDriver
{
public:
RageFileDriverMountpoints(): RageFileDriver( new FilenameDB ) { }
RageFileObj *Open( const CString &path, int mode, RageFile &p, int &err )
{
2004-04-21 04:16:31 +00:00
err = (mode == RageFile::WRITE)? ERROR_WRITING_NOT_SUPPORTED:ENOENT;
return NULL;
}
/* Never flush FDB, except in LoadFromDrivers. */
void FlushDirCache( const CString &sPath ) { }
void LoadFromDrivers( const vector<LoadedDriver> &drivers )
2004-03-14 01:56:18 +00:00
{
FDB->FlushDirCache();
for( unsigned i = 0; i < drivers.size(); ++i )
FDB->AddFile( drivers[i].MountPoint, 0, 0 );
2004-03-14 01:56:18 +00:00
}
};
static RageFileDriverMountpoints *g_Mountpoints = NULL;
2004-04-24 19:22:53 +00:00
static CString GetDirOfExecutable( CString argv0 )
{
#ifdef _XBOX
2004-04-24 19:22:53 +00:00
return "D:\\";
#else
/* argv[0] can be wrong in most OS's; try to avoid using it. */
CString sPath;
#if defined(_WIN32)
char buf[MAX_PATH];
GetModuleFileName( NULL, buf, sizeof(buf) );
sPath = buf;
#else
sPath = argv0;
#endif
2004-04-24 19:22:53 +00:00
sPath.Replace( "\\", "/" );
bool IsAbsolutePath = false;
2004-04-24 19:22:53 +00:00
if( sPath.size() == 0 || sPath[0] == '/' )
IsAbsolutePath = true;
#if defined(_WIN32)
2004-04-24 19:22:53 +00:00
if( sPath.size() > 2 && sPath[1] == ':' && sPath[2] == '/' )
IsAbsolutePath = true;
#endif
// strip off executable name
2004-04-24 19:22:53 +00:00
unsigned n = sPath.find_last_of("/");
if( n != sPath.npos )
sPath.erase(n);
else
2004-04-24 19:22:53 +00:00
sPath.erase();
if( !IsAbsolutePath )
{
2004-04-24 19:22:53 +00:00
sPath = GetCwd() + "/" + sPath;
sPath.Replace( "\\", "/" );
}
2004-04-24 19:22:53 +00:00
return sPath;
#endif
}
static void ChangeToDirOfExecutable( CString argv0 )
{
InitialWorkingDirectory = GetCwd();
DirOfExecutable = GetDirOfExecutable( argv0 );
/* Set the CWD. Any effects of this is platform-specific; most files are read and
* written through RageFile. See also RageFileManager::RageFileManager. */
#if defined(_WIN32)
chdir( DirOfExecutable + "/.." );
#elif defined(DARWIN)
chdir(DirOfExecutable + "/../../..");
#endif
}
RageFileManager::RageFileManager( CString argv0 )
2003-12-04 08:25:59 +00:00
{
2004-04-10 21:45:14 +00:00
CHECKPOINT_M( argv0 );
ChangeToDirOfExecutable( argv0 );
g_Mutex = new RageMutex("RageFileManager");
2004-01-14 23:44:42 +00:00
g_Mountpoints = new RageFileDriverMountpoints;
LoadedDriver ld;
ld.driver = g_Mountpoints;
ld.MountPoint = "";
g_Drivers.push_back( ld );
2004-02-10 23:39:45 +00:00
}
2004-02-13 08:36:42 +00:00
#ifndef HAVE_EXTRA // set for custom initial mount rules
2004-02-10 23:39:45 +00:00
void RageFileManager::MountInitialFilesystems()
{
2003-12-05 06:12:47 +00:00
/* Add file search paths, higher priority first. */
2003-12-04 08:25:59 +00:00
#if defined(XBOX)
2003-12-07 04:21:04 +00:00
RageFileManager::Mount( "dir", SYS_BASE_PATH, "" );
2003-12-06 05:45:36 +00:00
#elif defined(LINUX)
2003-12-12 22:27:21 +00:00
/* Absolute paths. This is rarely used, eg. by Alsa9Buf::GetSoundCardDebugInfo().
* All paths that start with a slash (eg. "/proc") should use this, so put it
* first. */
RageFileManager::Mount( "dir", "/", "/" );
2003-12-05 06:12:47 +00:00
/* We can almost do this, to have machine profiles be system-global to eg. share
* scores. It would need to handle permissions properly. */
/* RageFileManager::Mount( "dir", "/var/lib/games/stepmania", "Data/Profiles" ); */
2003-12-06 05:45:36 +00:00
// CString Home = getenv( "HOME" ) + "/" + PRODUCT_NAME;
2003-12-05 06:12:47 +00:00
/*
* Next: path to write general mutable user data. If the above path fails (eg.
* wrong permissions, doesn't exist), machine memcard data will also go in here.
* XXX: It seems silly to have two ~ directories. If we're going to create a
* directory on our own, it seems like it should be a dot directory, but it
* seems wrong to put lots of data (eg. music) in one. Hmm.
*/
/* XXX: create */
/* RageFileManager::Mount( "dir", Home + "." PRODUCT_NAME, "Data" ); */
/* Next, search ~/StepMania. This is where users can put music, themes, etc. */
/* RageFileManager::Mount( "dir", Home + PRODUCT_NAME, "" ); */
2003-12-12 22:27:21 +00:00
/* Search for a directory with "Songs" in it. Be careful: the CWD is likely to
* be ~, and it's possible that some users will have a ~/Songs/ directory that
* has nothing to do with us, so check the initial directory last. */
CString Root = "";
struct stat st;
if( Root == "" && !stat( DirOfExecutable + "/Songs", &st ) && st.st_mode&S_IFDIR )
Root = DirOfExecutable;
if( Root == "" && !stat( InitialWorkingDirectory + "/Songs", &st ) && st.st_mode&S_IFDIR )
Root = InitialWorkingDirectory;
if( Root == "" )
RageException::Throw( "Couldn't find \"Songs\"" );
RageFileManager::Mount( "dir", Root, "" );
2003-12-12 09:06:31 +00:00
#elif defined(_WINDOWS)
/* All Windows data goes in the directory one level above the executable. */
2004-04-10 21:45:14 +00:00
CHECKPOINT_M( ssprintf( "DOE \"%s\"", DirOfExecutable.c_str()) );
2003-12-12 09:06:31 +00:00
CStringArray parts;
split( DirOfExecutable, "/", parts );
2004-04-10 21:45:14 +00:00
CHECKPOINT_M( ssprintf( "... %i parts", parts.size()) );
2003-12-28 23:31:36 +00:00
RAGE_ASSERT_M( parts.size() > 1, ssprintf("Strange DirOfExecutable: %s", DirOfExecutable.c_str()) );
2003-12-12 09:06:31 +00:00
CString Dir = join( "/", parts.begin(), parts.end()-1 );
RageFileManager::Mount( "dir", Dir, "" );
2003-12-05 06:12:47 +00:00
#else
/* Paths relative to the CWD: */
RageFileManager::Mount( "dir", ".", "" );
#endif
2003-12-04 08:25:59 +00:00
}
2004-02-13 08:36:42 +00:00
#endif
2003-12-04 08:25:59 +00:00
RageFileManager::~RageFileManager()
{
/* Note that drivers can use previously-loaded drivers, eg. to load a ZIP
* from the FS. Unload drivers in reverse order. */
for( int i = g_Drivers.size()-1; i >= 0; --i )
delete g_Drivers[i].driver;
g_Drivers.clear();
2004-02-12 06:48:14 +00:00
// delete g_Mountpoints; // g_Mountpoints was in g_Drivers
g_Mountpoints = NULL;
2004-01-14 23:44:42 +00:00
delete g_Mutex;
2004-01-15 00:45:53 +00:00
g_Mutex = NULL;
2003-12-04 08:25:59 +00:00
}
CString LoadedDriver::GetPath( CString path )
{
/* Map all slashes to forward slash. */
FixSlashesInPlace( path );
/* Collapse the path, removing any leading dots. (FilenameDB::ResolvePath requires
* this.) */
CollapsePath( path, true );
2003-12-07 04:21:04 +00:00
/* Default mountpoints: */
2003-12-05 00:07:18 +00:00
if( MountPoint.size() == 0 )
{
/* If the path begins with @, default mount points don't count. */
if( path.size() && path[0] == '@' )
return "";
return path;
}
2003-12-04 08:25:59 +00:00
2003-12-05 00:07:18 +00:00
if( path.Left( MountPoint.size() ).CompareNoCase( MountPoint ) )
2003-12-04 08:25:59 +00:00
return ""; /* no match */
2003-12-05 00:07:18 +00:00
return path.Right( path.size() - MountPoint.size() );
2003-12-04 08:25:59 +00:00
}
2003-12-07 06:21:39 +00:00
bool ilt( const CString &a, const CString &b ) { return a.CompareNoCase(b) < 0; }
bool ieq( const CString &a, const CString &b ) { return a.CompareNoCase(b) == 0; }
2003-12-16 07:23:54 +00:00
void RageFileManager::GetDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
2003-12-05 02:25:32 +00:00
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-05 02:25:32 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
LoadedDriver &ld = g_Drivers[i];
const CString p = ld.GetPath( sPath );
if( p.size() == 0 )
continue;
const unsigned OldStart = AddTo.size();
g_Drivers[i].driver->GetDirListing( p, AddTo, bOnlyDirs, bReturnPathToo );
/* If returning the path, prepend the mountpoint name to the files this driver returned. */
if( bReturnPathToo )
for( unsigned j = OldStart; j < AddTo.size(); ++j )
AddTo[j] = ld.MountPoint + AddTo[j];
}
2003-12-07 06:21:39 +00:00
/* More than one driver might return the same file. Remove duplicates (case-
* insensitively). */
sort( AddTo.begin(), AddTo.end(), ilt );
CStringArray::iterator it = unique( AddTo.begin(), AddTo.end(), ieq );
AddTo.erase(it, AddTo.end());
2003-12-05 02:25:32 +00:00
}
2003-12-04 08:25:59 +00:00
2003-12-16 07:23:54 +00:00
bool RageFileManager::Remove( const CString &sPath )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-16 07:23:54 +00:00
/* Multiple drivers may have the same file. */
bool Deleted = false;
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
const CString p = g_Drivers[i].GetPath( sPath );
if( p.size() == 0 )
continue;
bool ret = g_Drivers[i].driver->Remove( p );
if( !ret )
Deleted = true;
}
return Deleted;
}
2003-12-05 00:07:18 +00:00
void RageFileManager::Mount( CString Type, CString Root, CString MountPoint )
2003-12-04 08:25:59 +00:00
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-05 00:07:18 +00:00
if( MountPoint.size() && MountPoint.Right(1) != "/" )
MountPoint += '/';
2003-12-04 08:25:59 +00:00
ASSERT( Root != "" );
2004-04-10 21:45:14 +00:00
CHECKPOINT_M( ssprintf("\"%s\", \"%s\", \"%s\"",
Type.c_str(), Root.c_str(), MountPoint.c_str() ) );
// Unmount anything that was previously mounted here.
Unmount( Type, Root, MountPoint );
CHECKPOINT;
2004-02-10 19:35:15 +00:00
RageFileDriver *driver = MakeFileDriver( Type, Root );
2003-12-04 08:25:59 +00:00
if( !driver )
2003-12-06 05:52:44 +00:00
{
2004-04-10 21:45:14 +00:00
CHECKPOINT;
2004-02-11 04:18:03 +00:00
if( LOG )
LOG->Warn("Can't mount unknown VFS type \"%s\", root \"%s\"", Type.c_str(), Root.c_str() );
2003-12-04 08:25:59 +00:00
return;
2003-12-06 05:52:44 +00:00
}
2004-04-10 21:45:14 +00:00
CHECKPOINT;
2003-12-04 08:25:59 +00:00
LoadedDriver ld;
ld.driver = driver;
ld.Type = Type;
ld.Root = Root;
2003-12-05 00:07:18 +00:00
ld.MountPoint = MountPoint;
2003-12-04 08:25:59 +00:00
g_Drivers.push_back( ld );
2004-04-10 21:45:14 +00:00
CHECKPOINT;
g_Mountpoints->LoadFromDrivers( g_Drivers );
2004-04-10 21:45:14 +00:00
CHECKPOINT;
2003-12-04 08:25:59 +00:00
}
2003-12-05 00:07:18 +00:00
void RageFileManager::Unmount( CString Type, CString Root, CString MountPoint )
2004-03-14 01:56:18 +00:00
{
LockMut( *g_Mutex );
2004-04-10 21:45:14 +00:00
if( MountPoint.size() && MountPoint.Right(1) != "/" )
MountPoint += '/';
2004-03-14 01:56:18 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
if( g_Drivers[i].Type.CompareNoCase( Type ) )
continue;
if( g_Drivers[i].Root.CompareNoCase( Root ) )
continue;
if( g_Drivers[i].MountPoint.CompareNoCase( MountPoint ) )
continue;
2004-04-10 21:45:14 +00:00
delete g_Drivers[i].driver;
g_Drivers.erase( g_Drivers.begin()+i );
}
2004-03-14 01:56:18 +00:00
g_Mountpoints->LoadFromDrivers( g_Drivers );
2004-03-14 01:56:18 +00:00
}
2003-12-05 00:07:18 +00:00
bool RageFileManager::IsMounted( CString MountPoint )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-05 00:07:18 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
if( !g_Drivers[i].MountPoint.CompareNoCase( MountPoint ) )
return true;
return false;
}
/* Return true if the driver with the given root path is ready (eg. CD or memory card
* inserted). */
bool RageFileManager::MountpointIsReady( CString MountPoint )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-05 00:07:18 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
if( g_Drivers[i].MountPoint.CompareNoCase( MountPoint ) )
continue;
return g_Drivers[i].driver->Ready();
}
return false;
}
2004-03-15 03:05:55 +00:00
void RageFileManager::GetLoadedDrivers( vector<DriverLocation> &Mounts )
{
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
DriverLocation l;
l.MountPoint = g_Drivers[i].MountPoint;
l.Type = g_Drivers[i].Type;
l.Root = g_Drivers[i].Root;
Mounts.push_back( l );
}
}
2003-12-07 04:21:04 +00:00
void RageFileManager::FlushDirCache( const CString &sPath )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-07 04:21:04 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
2003-12-07 08:31:53 +00:00
if( sPath.size() == 0 )
{
g_Drivers[i].driver->FlushDirCache( "" );
}
else
{
const CString path = g_Drivers[i].GetPath( sPath );
if( path.size() == 0 )
continue;
g_Drivers[i].driver->FlushDirCache( path );
}
2003-12-07 04:21:04 +00:00
}
}
2003-12-04 08:25:59 +00:00
RageFileManager::FileType RageFileManager::GetFileType( const CString &sPath )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-04 08:25:59 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
const CString p = g_Drivers[i].GetPath( sPath );
if( p.size() == 0 )
continue;
FileType ret = g_Drivers[i].driver->GetFileType( p );
if( ret != TYPE_NONE )
return ret;
}
return TYPE_NONE;
}
int RageFileManager::GetFileSizeInBytes( const CString &sPath )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-04 08:25:59 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
const CString p = g_Drivers[i].GetPath( sPath );
if( p.size() == 0 )
continue;
int ret = g_Drivers[i].driver->GetFileSizeInBytes( p );
if( ret != -1 )
return ret;
}
return -1;
}
2003-12-10 07:07:42 +00:00
int RageFileManager::GetFileHash( const CString &sPath )
2003-12-04 08:25:59 +00:00
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-04 08:25:59 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
const CString p = g_Drivers[i].GetPath( sPath );
if( p.size() == 0 )
continue;
2003-12-10 07:07:42 +00:00
int ret = g_Drivers[i].driver->GetFileHash( p );
2003-12-04 08:25:59 +00:00
if( ret != -1 )
return ret;
}
return -1;
}
2003-12-05 02:25:32 +00:00
static bool SortBySecond( const pair<int,int> &a, const pair<int,int> &b )
{
return a.second < b.second;
}
2003-12-11 05:32:35 +00:00
void AddReference( const RageFileObj *obj, RageFileDriver *driver )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-11 05:32:35 +00:00
pair< const RageFileObj *, RageFileDriver * > ref;
ref.first = obj;
ref.second = driver;
/* map::insert returns an iterator (which we discard) and a bool, indicating whether
* this is a new entry. This should always be new. */
const pair< FileReferences::iterator, bool > ret = g_Refs.insert( ref );
RAGE_ASSERT_M( ret.second, ssprintf( "RemoveReference: Duplicate reference (%s)", obj->GetDisplayPath().c_str() ) );
}
void RemoveReference( const RageFileObj *obj )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-11 05:32:35 +00:00
FileReferences::iterator it = g_Refs.find( obj );
RAGE_ASSERT_M( it != g_Refs.end(), ssprintf( "RemoveReference: Missing reference (%s)", obj->GetDisplayPath().c_str() ) );
g_Refs.erase( it );
}
/*
* Return true if the given path should use slow, reliable writes.
*
* I havn't decided if it's better to do this here, or to specify SLOW_FLUSH
* manually each place we want it. This seems more reliable (we might forget
* somewhere and not notice), and easier (don't have to pass flags down to IniFile::Write,
* etc).
*/
static bool PathUsesSlowFlush( const CString &sPath )
{
static const char *FlushPaths[] =
{
"Data/"
};
2004-04-27 04:55:59 +00:00
for( unsigned i = 0; i < ARRAYSIZE(FlushPaths); ++i )
if( !strncmp( sPath, FlushPaths[i], strlen(FlushPaths[i]) ) )
return true;
return false;
}
2003-12-04 08:25:59 +00:00
/* Used only by RageFile: */
RageFileObj *RageFileManager::Open( const CString &sPath, int mode, RageFile &p, int &err )
2003-12-04 08:25:59 +00:00
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-04 08:25:59 +00:00
err = ENOENT;
if( (mode & RageFile::WRITE) && PathUsesSlowFlush(sPath) )
mode |= RageFile::SLOW_FLUSH;
2003-12-05 02:25:32 +00:00
/* If writing, we need to do a heuristic to figure out which driver to write with--there
* may be several that will work. */
if( mode & RageFile::WRITE )
2003-12-05 02:25:32 +00:00
return OpenForWriting( sPath, mode, p, err );
2003-12-04 08:25:59 +00:00
for( unsigned i = 0; i < g_Drivers.size(); ++i )
{
2003-12-11 05:32:35 +00:00
LoadedDriver &ld = g_Drivers[i];
const CString path = ld.GetPath( sPath );
2003-12-04 08:25:59 +00:00
if( path.size() == 0 )
continue;
int error;
2003-12-11 05:32:35 +00:00
RageFileObj *ret = ld.driver->Open( path, mode, p, error );
2003-12-04 08:25:59 +00:00
if( ret )
2003-12-11 05:32:35 +00:00
{
AddReference( ret, ld.driver );
2003-12-04 08:25:59 +00:00
return ret;
2003-12-11 05:32:35 +00:00
}
2003-12-04 08:25:59 +00:00
/* ENOENT (File not found) is low-priority: if some other error
* was reported, return that instead. */
if( error != ENOENT )
err = error;
}
return NULL;
}
2003-12-11 07:13:20 +00:00
/* Copy a RageFileObj for a new RageFile. */
RageFileObj *RageFileManager::CopyFileObj( const RageFileObj *cpy, RageFile &p )
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-11 07:13:20 +00:00
FileReferences::const_iterator it = g_Refs.find( cpy );
RAGE_ASSERT_M( it != g_Refs.end(), ssprintf( "RemoveReference: Missing reference (%s)", cpy->GetDisplayPath().c_str() ) );
RageFileObj *ret = cpy->Copy( p );
/* It's from the same driver as the original. */
AddReference( ret, it->second );
return ret;
}
RageFileObj *RageFileManager::OpenForWriting( const CString &sPath, int mode, RageFile &p, int &err )
2003-12-05 02:25:32 +00:00
{
2004-01-14 23:44:42 +00:00
LockMut( *g_Mutex );
2003-12-05 02:25:32 +00:00
/*
* The value for a driver to open a file is the number of directories and/or files
* that would have to be created in order to write it, or 0 if the file already exists.
* For example, if we're opening "foo/bar/baz.txt", and only "foo/" exists in a
* driver, we'd have to create the "bar" directory and the "baz.txt" file, so the
* value is 2. If "foo/bar/" exists, we'd only have to create the file, so the
* value is 1. Create the file with the driver that returns the lowest value;
* in case of a tie, earliest-loaded driver wins.
*
* The purpose of this is to create files in the expected place. For example, if we
* have both C:/games/StepMania and C:/games/DWI loaded, and we're writing
* "Songs/Music/Waltz/waltz.sm", and the song was loaded out of
* "C:/games/DWI/Songs/Music/Waltz/waltz.dwi", we want to write the new SM into the
* same directory (if possible). Don't split up files in the same directory any
* more than we have to.
*
* If the given path can not be created, return -1. This happens if a path
* that needs to be a directory is a file, or vice versa.
*/
vector< pair<int,int> > Values;
unsigned i;
for( i = 0; i < g_Drivers.size(); ++i )
{
LoadedDriver &ld = g_Drivers[i];
const CString path = ld.GetPath( sPath );
if( path.size() == 0 )
continue;
const int value = ld.driver->GetPathValue( path );
if( value == -1 )
continue;
Values.push_back( pair<int,int>( i, value ) );
}
stable_sort( Values.begin(), Values.end(), SortBySecond );
2004-04-21 04:16:31 +00:00
err = 0;
2003-12-05 02:25:32 +00:00
for( i = 0; i < Values.size(); ++i )
{
const int driver = Values[i].first;
LoadedDriver &ld = g_Drivers[driver];
const CString path = ld.GetPath( sPath );
ASSERT( path.size() );
2004-04-21 04:16:31 +00:00
int error;
2003-12-05 02:25:32 +00:00
RageFileObj *ret = ld.driver->Open( path, mode, p, error );
if( ret )
2003-12-11 05:32:35 +00:00
{
AddReference( ret, ld.driver );
2003-12-05 02:25:32 +00:00
return ret;
2003-12-11 05:32:35 +00:00
}
2004-04-21 04:16:31 +00:00
/* The drivers are in order of priority; if they all return error, return the
* first. Never return ERROR_WRITING_NOT_SUPPORTED. */
if( !err && error != RageFileDriver::ERROR_WRITING_NOT_SUPPORTED )
err = error;
2003-12-05 02:25:32 +00:00
}
2004-04-21 04:16:31 +00:00
if( !err )
err = EEXIST; /* no driver could write */
2003-12-05 02:25:32 +00:00
return NULL;
}
2003-12-11 05:32:35 +00:00
void RageFileManager::Close( RageFileObj *obj )
{
if( obj == NULL )
return;
RemoveReference( obj );
delete obj;
}
2003-12-04 08:25:59 +00:00
bool RageFileManager::IsAFile( const CString &sPath ) { return GetFileType(sPath) == TYPE_FILE; }
bool RageFileManager::IsADirectory( const CString &sPath ) { return GetFileType(sPath) == TYPE_DIR; }
bool RageFileManager::DoesFileExist( const CString &sPath ) { return GetFileType(sPath) != TYPE_NONE; }
2003-12-07 04:21:04 +00:00
bool DoesFileExist( const CString &sPath )
{
return FILEMAN->DoesFileExist( sPath );
}
bool IsAFile( const CString &sPath )
{
return FILEMAN->IsAFile( sPath );
}
bool IsADirectory( const CString &sPath )
{
return FILEMAN->IsADirectory( sPath );
}
unsigned GetFileSizeInBytes( const CString &sPath )
{
return FILEMAN->GetFileSizeInBytes( sPath );
}
2003-12-07 22:55:55 +00:00
void GetDirListing( const CString &sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
2003-12-07 04:21:04 +00:00
{
FILEMAN->GetDirListing( sPath, AddTo, bOnlyDirs, bReturnPathToo );
}
void FlushDirCache()
{
FILEMAN->FlushDirCache( "" );
}
2003-12-10 07:07:42 +00:00
unsigned int GetHashForFile( const CString &sPath )
{
return GetHashForString( sPath ) + FILEMAN->GetFileHash( sPath );
}
unsigned int GetHashForDirectory( const CString &sDir )
{
unsigned int hash = 0;
hash += GetHashForString( sDir );
2003-12-10 07:07:42 +00:00
CStringArray arrayFiles;
GetDirListing( sDir+"*", arrayFiles, false );
for( unsigned i=0; i<arrayFiles.size(); i++ )
{
const CString sFilePath = sDir + arrayFiles[i];
hash += GetHashForFile( sFilePath );
}
return hash;
}