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"
|
2003-12-04 19:57:19 +00:00
|
|
|
#include "RageUtil.h"
|
2003-12-08 00:07:02 +00:00
|
|
|
#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). */
|
2004-11-08 22:13:56 +00:00
|
|
|
static RageEvent *g_Mutex;
|
2004-01-14 23:44:42 +00:00
|
|
|
|
2004-03-04 00:19:42 +00:00
|
|
|
CString InitialWorkingDirectory;
|
|
|
|
|
CString DirOfExecutable;
|
|
|
|
|
|
2004-03-15 02:52:55 +00:00
|
|
|
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;
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
int m_iRefs;
|
|
|
|
|
|
|
|
|
|
LoadedDriver() { driver = NULL; m_iRefs = 0; }
|
2004-06-03 05:15:08 +00:00
|
|
|
CString GetPath( const CString &path );
|
2004-03-15 02:52:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static vector<LoadedDriver> g_Drivers;
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
void ReferenceAllDrivers( vector<LoadedDriver> &aDriverList )
|
|
|
|
|
{
|
|
|
|
|
g_Mutex->Lock();
|
|
|
|
|
aDriverList = g_Drivers;
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
|
|
|
|
++aDriverList[i].m_iRefs;
|
|
|
|
|
g_Mutex->Unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UnreferenceAllDrivers( vector<LoadedDriver> &aDriverList )
|
|
|
|
|
{
|
|
|
|
|
g_Mutex->Lock();
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
|
|
|
|
--aDriverList[i].m_iRefs;
|
|
|
|
|
g_Mutex->Broadcast();
|
|
|
|
|
g_Mutex->Unlock();
|
|
|
|
|
|
|
|
|
|
/* Clear the temporary list, to make it clear that the drivers may no longer be accessed. */
|
|
|
|
|
aDriverList.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for the given driver to become unreferenced, and remove it from the list
|
|
|
|
|
* to get exclusive access to it. Returns false if the driver is no longer available
|
|
|
|
|
* (somebody else got it first). */
|
|
|
|
|
bool GrabDriver( RageFileDriver *pDriver )
|
|
|
|
|
{
|
|
|
|
|
g_Mutex->Lock();
|
|
|
|
|
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
for( i = 0; i < g_Drivers.size(); ++i )
|
|
|
|
|
if( g_Drivers[i].driver == pDriver )
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if( i == g_Drivers.size() )
|
|
|
|
|
{
|
|
|
|
|
g_Mutex->Unlock();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( g_Drivers[i].m_iRefs == 0 )
|
|
|
|
|
{
|
|
|
|
|
g_Drivers.erase( g_Drivers.begin()+i );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The driver is in use. Wait for somebody to release a driver, and
|
|
|
|
|
* try again. */
|
|
|
|
|
g_Mutex->Wait();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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. */
|
2003-12-08 00:07:02 +00:00
|
|
|
class RageFileDriverMountpoints: public RageFileDriver
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
RageFileDriverMountpoints(): RageFileDriver( new FilenameDB ) { }
|
2003-12-21 07:23:29 +00:00
|
|
|
RageFileObj *Open( const CString &path, int mode, RageFile &p, int &err )
|
2003-12-08 00:07:02 +00:00
|
|
|
{
|
2004-04-21 04:16:31 +00:00
|
|
|
err = (mode == RageFile::WRITE)? ERROR_WRITING_NOT_SUPPORTED:ENOENT;
|
2003-12-08 00:07:02 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2004-03-15 02:52:55 +00:00
|
|
|
/* Never flush FDB, except in LoadFromDrivers. */
|
2003-12-08 00:07:02 +00:00
|
|
|
void FlushDirCache( const CString &sPath ) { }
|
2004-03-15 02:52:55 +00:00
|
|
|
|
|
|
|
|
void LoadFromDrivers( const vector<LoadedDriver> &drivers )
|
2004-03-14 01:56:18 +00:00
|
|
|
{
|
2004-03-15 02:52:55 +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
|
|
|
}
|
2003-12-08 00:07:02 +00:00
|
|
|
};
|
|
|
|
|
static RageFileDriverMountpoints *g_Mountpoints = NULL;
|
|
|
|
|
|
2004-04-24 19:22:53 +00:00
|
|
|
static CString GetDirOfExecutable( CString argv0 )
|
2004-03-04 00:19:42 +00:00
|
|
|
{
|
|
|
|
|
#ifdef _XBOX
|
2004-04-24 19:22:53 +00:00
|
|
|
return "D:\\";
|
2004-03-04 00:19:42 +00:00
|
|
|
#else
|
2004-04-24 19:30:49 +00:00
|
|
|
/* 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( "\\", "/" );
|
2004-03-04 00:19:42 +00:00
|
|
|
|
|
|
|
|
bool IsAbsolutePath = false;
|
2004-04-24 19:22:53 +00:00
|
|
|
if( sPath.size() == 0 || sPath[0] == '/' )
|
2004-03-04 00:19:42 +00:00
|
|
|
IsAbsolutePath = true;
|
|
|
|
|
#if defined(_WIN32)
|
2004-04-24 19:22:53 +00:00
|
|
|
if( sPath.size() > 2 && sPath[1] == ':' && sPath[2] == '/' )
|
2004-03-04 00:19:42 +00:00
|
|
|
IsAbsolutePath = true;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// strip off executable name
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t n = sPath.find_last_of("/");
|
2004-04-24 19:22:53 +00:00
|
|
|
if( n != sPath.npos )
|
|
|
|
|
sPath.erase(n);
|
2004-03-04 00:19:42 +00:00
|
|
|
else
|
2004-04-24 19:22:53 +00:00
|
|
|
sPath.erase();
|
2004-03-04 00:19:42 +00:00
|
|
|
|
|
|
|
|
if( !IsAbsolutePath )
|
|
|
|
|
{
|
2004-04-24 19:22:53 +00:00
|
|
|
sPath = GetCwd() + "/" + sPath;
|
|
|
|
|
sPath.Replace( "\\", "/" );
|
2004-03-04 00:19:42 +00:00
|
|
|
}
|
|
|
|
|
|
2004-04-24 19:22:53 +00:00
|
|
|
return sPath;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ChangeToDirOfExecutable( CString argv0 )
|
|
|
|
|
{
|
|
|
|
|
InitialWorkingDirectory = GetCwd();
|
|
|
|
|
DirOfExecutable = GetDirOfExecutable( argv0 );
|
|
|
|
|
|
2004-03-04 00:19:42 +00:00
|
|
|
/* Set the CWD. Any effects of this is platform-specific; most files are read and
|
|
|
|
|
* written through RageFile. See also RageFileManager::RageFileManager. */
|
2004-06-20 01:35:25 +00:00
|
|
|
#if defined(_WINDOWS)
|
2004-03-04 00:19:42 +00:00
|
|
|
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 );
|
2004-03-04 00:19:42 +00:00
|
|
|
ChangeToDirOfExecutable( argv0 );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
g_Mutex = new RageEvent("RageFileManager");
|
2004-01-14 23:44:42 +00:00
|
|
|
|
2003-12-08 00:07:02 +00:00
|
|
|
g_Mountpoints = new RageFileDriverMountpoints;
|
|
|
|
|
LoadedDriver ld;
|
|
|
|
|
ld.driver = g_Mountpoints;
|
|
|
|
|
ld.MountPoint = "";
|
|
|
|
|
g_Drivers.push_back( ld );
|
2004-09-06 09:36:12 +00:00
|
|
|
|
|
|
|
|
/* The mount path is unused, but must be nonempty. */
|
|
|
|
|
RageFileManager::Mount( "mem", "(cache)", "@mem" );
|
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. */
|
2004-08-10 20:56:16 +00:00
|
|
|
#if defined(XBOX)
|
|
|
|
|
RageFileManager::Mount( "dir", "D:\\", "" );
|
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()) );
|
2004-06-16 00:38:31 +00:00
|
|
|
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", ".", "" );
|
2003-12-04 19:57:19 +00:00
|
|
|
#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;
|
2003-12-08 00:07:02 +00:00
|
|
|
g_Drivers.clear();
|
|
|
|
|
|
2004-02-12 06:48:14 +00:00
|
|
|
// delete g_Mountpoints; // g_Mountpoints was in g_Drivers
|
2003-12-08 00:07:02 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
/* path must be normalized (FixSlashesInPlace, CollapsePath). */
|
|
|
|
|
CString LoadedDriver::GetPath( const CString &path )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-02-27 06:20:59 +00:00
|
|
|
/* Default mountpoints: */
|
2003-12-05 00:07:18 +00:00
|
|
|
if( MountPoint.size() == 0 )
|
2004-02-27 06:20:59 +00:00
|
|
|
{
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
static void NormalizePath( CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
FixSlashesInPlace( sPath );
|
2004-06-03 23:17:27 +00:00
|
|
|
CollapsePath( sPath, true );
|
2004-06-03 05:15:08 +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; }
|
2004-06-03 05:15:08 +00:00
|
|
|
void RageFileManager::GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
|
2003-12-05 02:25:32 +00:00
|
|
|
{
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
2004-11-08 22:13:56 +00:00
|
|
|
|
|
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-05 02:25:32 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
LoadedDriver &ld = aDriverList[i];
|
2003-12-05 02:25:32 +00:00
|
|
|
const CString p = ld.GetPath( sPath );
|
|
|
|
|
if( p.size() == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const unsigned OldStart = AddTo.size();
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
ld.driver->GetDirListing( p, AddTo, bOnlyDirs, bReturnPathToo );
|
2003-12-05 02:25:32 +00:00
|
|
|
|
|
|
|
|
/* 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];
|
|
|
|
|
}
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
|
|
|
|
|
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
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
bool RageFileManager::Remove( CString sPath )
|
2003-12-16 07:23:54 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
2004-01-14 23:44:42 +00:00
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2003-12-16 07:23:54 +00:00
|
|
|
/* Multiple drivers may have the same file. */
|
|
|
|
|
bool Deleted = false;
|
2004-11-08 22:13:56 +00:00
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-16 07:23:54 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
const CString p = aDriverList[i].GetPath( sPath );
|
2003-12-16 07:23:54 +00:00
|
|
|
if( p.size() == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
bool ret = aDriverList[i].driver->Remove( p );
|
2004-08-31 06:44:16 +00:00
|
|
|
if( ret )
|
2003-12-16 07:23:54 +00:00
|
|
|
Deleted = true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
|
|
|
|
|
2003-12-16 07:23:54 +00:00
|
|
|
return Deleted;
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-24 07:04:33 +00:00
|
|
|
void RageFileManager::CreateDir( CString sDir )
|
|
|
|
|
{
|
|
|
|
|
CString sTempFile = sDir + "temp";
|
|
|
|
|
RageFile f;
|
|
|
|
|
f.Open( sTempFile, RageFile::WRITE );
|
|
|
|
|
f.Close();
|
|
|
|
|
|
2004-08-31 06:44:16 +00:00
|
|
|
// YUCK: The dir cache doesn't have this new file we just created,
|
|
|
|
|
// so the delete will fail unless we flush.
|
|
|
|
|
FILEMAN->FlushDirCache( sDir );
|
2004-07-24 07:04:33 +00:00
|
|
|
|
2004-08-31 06:44:16 +00:00
|
|
|
FILEMAN->Remove( sTempFile );
|
2004-07-24 07:04:33 +00:00
|
|
|
}
|
2004-08-31 06:44:16 +00:00
|
|
|
|
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-05-23 22:11:33 +00:00
|
|
|
FixSlashesInPlace( Root );
|
|
|
|
|
FixSlashesInPlace( MountPoint );
|
|
|
|
|
|
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() );
|
2004-06-12 05:38:49 +00:00
|
|
|
else
|
|
|
|
|
fprintf( stderr, "Can't mount unknown VFS type \"%s\", root \"%s\"\n", 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;
|
2004-03-15 02:52:55 +00:00
|
|
|
ld.Type = Type;
|
|
|
|
|
ld.Root = Root;
|
2003-12-05 00:07:18 +00:00
|
|
|
ld.MountPoint = MountPoint;
|
2003-12-08 00:07:02 +00:00
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
g_Mutex->Lock();
|
|
|
|
|
g_Drivers.push_back( ld );
|
2004-04-10 21:45:14 +00:00
|
|
|
CHECKPOINT;
|
2004-03-15 02:52:55 +00:00
|
|
|
g_Mountpoints->LoadFromDrivers( g_Drivers );
|
2004-04-10 21:45:14 +00:00
|
|
|
CHECKPOINT;
|
2004-11-08 22:13:56 +00:00
|
|
|
g_Mutex->Unlock();
|
2003-12-04 08:25:59 +00:00
|
|
|
}
|
2003-12-05 00:07:18 +00:00
|
|
|
|
2004-03-15 02:52:55 +00:00
|
|
|
void RageFileManager::Unmount( CString Type, CString Root, CString MountPoint )
|
2004-03-14 01:56:18 +00:00
|
|
|
{
|
2004-05-23 22:11:33 +00:00
|
|
|
FixSlashesInPlace( Root );
|
|
|
|
|
FixSlashesInPlace( MountPoint );
|
|
|
|
|
|
2004-04-10 21:45:14 +00:00
|
|
|
if( MountPoint.size() && MountPoint.Right(1) != "/" )
|
|
|
|
|
MountPoint += '/';
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
/* Find all drivers we want to delete. Remove them from g_Drivers, and move them
|
|
|
|
|
* into aDriverListToUnmount. */
|
|
|
|
|
vector<LoadedDriver> aDriverListToUnmount;
|
|
|
|
|
g_Mutex->Lock();
|
2004-03-14 01:56:18 +00:00
|
|
|
for( unsigned i = 0; i < g_Drivers.size(); ++i )
|
2004-03-15 02:52:55 +00:00
|
|
|
{
|
|
|
|
|
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
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
++g_Drivers[i].m_iRefs;
|
|
|
|
|
aDriverListToUnmount.push_back( g_Drivers[i] );
|
2004-03-15 02:52:55 +00:00
|
|
|
g_Drivers.erase( g_Drivers.begin()+i );
|
2004-11-08 22:13:56 +00:00
|
|
|
--i;
|
2004-03-15 02:52:55 +00:00
|
|
|
}
|
2004-03-14 01:56:18 +00:00
|
|
|
|
2004-03-15 02:52:55 +00:00
|
|
|
g_Mountpoints->LoadFromDrivers( g_Drivers );
|
2004-11-08 22:13:56 +00:00
|
|
|
|
|
|
|
|
g_Mutex->Unlock();
|
|
|
|
|
|
|
|
|
|
/* Now we have a list of drivers to remove. */
|
|
|
|
|
while( aDriverListToUnmount.size() )
|
|
|
|
|
{
|
|
|
|
|
/* If the driver has more than one reference, somebody other than us is
|
|
|
|
|
* using it; wait for that operation to complete. Note that two Unmount()
|
|
|
|
|
* calls that want to remove the same mountpoint will deadlock here. */
|
|
|
|
|
g_Mutex->Lock();
|
|
|
|
|
while( aDriverListToUnmount[0].m_iRefs > 1 )
|
|
|
|
|
g_Mutex->Wait();
|
|
|
|
|
g_Mutex->Unlock();
|
|
|
|
|
|
|
|
|
|
delete aDriverListToUnmount[0].driver;
|
|
|
|
|
aDriverListToUnmount.erase( aDriverListToUnmount.begin() );
|
|
|
|
|
}
|
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-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
2004-01-14 23:44:42 +00:00
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-05 00:07:18 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
if( aDriverList[i].MountPoint.CompareNoCase( MountPoint ) )
|
2003-12-05 00:07:18 +00:00
|
|
|
continue;
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
return aDriverList[i].driver->Ready();
|
2003-12-05 00:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
2003-12-05 00:07:18 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-15 03:05:55 +00:00
|
|
|
void RageFileManager::GetLoadedDrivers( vector<DriverLocation> &Mounts )
|
|
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
LockMut( *g_Mutex );
|
|
|
|
|
|
2004-03-15 03:05:55 +00:00
|
|
|
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 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
void RageFileManager::FlushDirCache( CString sPath )
|
2003-12-07 04:21:04 +00:00
|
|
|
{
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
RageFileManager::FileType RageFileManager::GetFileType( CString sPath )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
const CString p = aDriverList[i].GetPath( sPath );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( p.size() == 0 )
|
|
|
|
|
continue;
|
2004-11-08 22:13:56 +00:00
|
|
|
FileType ret = aDriverList[i].driver->GetFileType( p );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( ret != TYPE_NONE )
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
return TYPE_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
int RageFileManager::GetFileSizeInBytes( CString sPath )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
const CString p = aDriverList[i].GetPath( sPath );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( p.size() == 0 )
|
|
|
|
|
continue;
|
2004-11-08 22:13:56 +00:00
|
|
|
int ret = aDriverList[i].driver->GetFileSizeInBytes( p );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( ret != -1 )
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
2003-12-04 08:25:59 +00:00
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
int RageFileManager::GetFileHash( CString sPath )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
const CString p = aDriverList[i].GetPath( sPath );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( p.size() == 0 )
|
|
|
|
|
continue;
|
2004-11-08 22:13:56 +00:00
|
|
|
int ret = aDriverList[i].driver->GetFileHash( p );
|
2003-12-04 08:25:59 +00:00
|
|
|
if( ret != -1 )
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
2003-12-04 08:25:59 +00:00
|
|
|
|
|
|
|
|
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 );
|
2004-06-16 00:38:31 +00:00
|
|
|
ASSERT_M( ret.second, ssprintf( "RemoveReference: Duplicate reference (%s)", obj->GetDisplayPath().c_str() ) );
|
2003-12-11 05:32:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 );
|
2004-06-16 00:38:31 +00:00
|
|
|
ASSERT_M( it != g_Refs.end(), ssprintf( "RemoveReference: Missing reference (%s)", obj->GetDisplayPath().c_str() ) );
|
2003-12-11 05:32:35 +00:00
|
|
|
g_Refs.erase( it );
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-19 22:16:39 +00:00
|
|
|
/*
|
|
|
|
|
* 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 )
|
2004-04-19 22:16:39 +00:00
|
|
|
if( !strncmp( sPath, FlushPaths[i], strlen(FlushPaths[i]) ) )
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
/* Used only by RageFile: */
|
2004-06-03 05:15:08 +00:00
|
|
|
RageFileObj *RageFileManager::Open( CString sPath, int mode, RageFile &p, int &err )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
|
|
|
|
err = ENOENT;
|
|
|
|
|
|
2004-04-19 22:16:39 +00:00
|
|
|
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. */
|
2004-04-19 22:16:39 +00:00
|
|
|
if( mode & RageFile::WRITE )
|
2003-12-05 02:25:32 +00:00
|
|
|
return OpenForWriting( sPath, mode, p, err );
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
|
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
LoadedDriver &ld = aDriverList[i];
|
2003-12-11 05:32:35 +00:00
|
|
|
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 );
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
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;
|
|
|
|
|
}
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
2003-12-04 08:25:59 +00:00
|
|
|
|
|
|
|
|
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 );
|
2004-08-29 07:05:17 +00:00
|
|
|
ASSERT_M( it != g_Refs.end(), ssprintf( "CopyFileObj: Missing reference (%s)", cpy->GetDisplayPath().c_str() ) );
|
2003-12-11 07:13:20 +00:00
|
|
|
|
|
|
|
|
RageFileObj *ret = cpy->Copy( p );
|
|
|
|
|
|
|
|
|
|
/* It's from the same driver as the original. */
|
|
|
|
|
AddReference( ret, it->second );
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-03 05:15:08 +00:00
|
|
|
RageFileObj *RageFileManager::OpenForWriting( CString sPath, int mode, RageFile &p, int &err )
|
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.
|
|
|
|
|
*/
|
2004-06-03 05:15:08 +00:00
|
|
|
NormalizePath( sPath );
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
vector<LoadedDriver> aDriverList;
|
|
|
|
|
ReferenceAllDrivers( aDriverList );
|
|
|
|
|
|
2003-12-05 02:25:32 +00:00
|
|
|
vector< pair<int,int> > Values;
|
2004-11-08 22:13:56 +00:00
|
|
|
for( unsigned i = 0; i < aDriverList.size(); ++i )
|
2003-12-05 02:25:32 +00:00
|
|
|
{
|
2004-11-08 22:13:56 +00:00
|
|
|
LoadedDriver &ld = aDriverList[i];
|
2003-12-05 02:25:32 +00:00
|
|
|
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;
|
2004-09-21 07:53:39 +00:00
|
|
|
for( unsigned i = 0; i < Values.size(); ++i )
|
2003-12-05 02:25:32 +00:00
|
|
|
{
|
|
|
|
|
const int driver = Values[i].first;
|
2004-11-08 22:13:56 +00:00
|
|
|
LoadedDriver &ld = aDriverList[driver];
|
2003-12-05 02:25:32 +00:00
|
|
|
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 */
|
|
|
|
|
|
2004-11-08 22:13:56 +00:00
|
|
|
UnreferenceAllDrivers( aDriverList );
|
|
|
|
|
|
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 )
|
|
|
|
|
{
|
2004-10-01 08:54:10 +00:00
|
|
|
return FILEMAN->GetFileHash( sPath );
|
2003-12-10 07:07:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int GetHashForDirectory( const CString &sDir )
|
|
|
|
|
{
|
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
|
2003-12-30 02:16:00 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2004-05-06 00:42:06 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2001-2004 Glenn Maynard, Chris Danford
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|