2014-04-06 17:19:33 +00:00
|
|
|
// VFSRoot.cpp - glues it all together and makes use simple
|
|
|
|
// For conditions of distribution and use, see copyright notice in VFS.h
|
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
#include <set>
|
2014-04-06 17:19:33 +00:00
|
|
|
|
|
|
|
#include "VFSInternal.h"
|
|
|
|
#include "VFSRoot.h"
|
|
|
|
#include "VFSTools.h"
|
|
|
|
|
|
|
|
#include "VFSDirInternal.h"
|
|
|
|
#include "VFSFile.h"
|
|
|
|
#include "VFSLoader.h"
|
|
|
|
#include "VFSArchiveLoader.h"
|
2014-04-15 13:04:33 +00:00
|
|
|
#include "VFSDirView.h"
|
|
|
|
|
|
|
|
//#include <stdio.h>
|
2014-04-06 17:19:33 +00:00
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
# include <cassert>
|
|
|
|
# define DEBUG_ASSERT(x) assert(x)
|
|
|
|
#else
|
|
|
|
# define DEBUG_ASSERT(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
VFS_NAMESPACE_START
|
|
|
|
|
|
|
|
// predecl is in VFS.h
|
|
|
|
bool _checkCompatInternal(_AbiCheck *used)
|
|
|
|
{
|
|
|
|
if(sizeof(_AbiCheck) != used->structSize)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
_AbiCheck here;
|
|
|
|
memset(&here, 0, sizeof(here));
|
|
|
|
here.structSize = sizeof(here);
|
|
|
|
here.vfsposSize = sizeof(vfspos);
|
|
|
|
|
|
|
|
#ifdef VFS_LARGEFILE_SUPPORT
|
|
|
|
here.largefile = 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef VFS_IGNORE_CASE
|
|
|
|
here.nocase = 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return !memcmp(&here, used, sizeof(here));
|
|
|
|
}
|
|
|
|
|
|
|
|
Root::Root()
|
|
|
|
: merged(new InternalDir(""))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Root::~Root()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Root::Clear()
|
|
|
|
{
|
|
|
|
merged->_clearDirs();
|
|
|
|
merged->_clearMounts();
|
|
|
|
|
|
|
|
loaders.clear();
|
|
|
|
archLdrs.clear();
|
|
|
|
}
|
|
|
|
|
2014-04-07 00:25:58 +00:00
|
|
|
void Root::Mount(const char *src, const char *dest)
|
2014-04-06 17:19:33 +00:00
|
|
|
{
|
2014-04-15 17:48:06 +00:00
|
|
|
AddVFSDir(GetDir(src, true), dest);
|
2014-04-06 17:19:33 +00:00
|
|
|
}
|
|
|
|
|
2014-04-07 00:25:58 +00:00
|
|
|
void Root::AddVFSDir(DirBase *dir, const char *subdir /* = NULL */)
|
2014-04-06 17:19:33 +00:00
|
|
|
{
|
|
|
|
if(!subdir)
|
|
|
|
subdir = dir->fullname();
|
2014-04-15 13:04:33 +00:00
|
|
|
InternalDir *into = safecastNonNull<InternalDir*>(merged->_getDirEx(subdir, subdir, true, true, false).first);
|
2014-04-06 17:19:33 +00:00
|
|
|
into->_addMountDir(dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Root::Unmount(const char *src, const char *dest)
|
|
|
|
{
|
|
|
|
DirBase *vdsrc = GetDir(src, false);
|
2014-04-07 00:25:58 +00:00
|
|
|
InternalDir *vddest = safecast<InternalDir*>(GetDir(dest, false));
|
2014-04-06 17:19:33 +00:00
|
|
|
if(!vdsrc || !vddest)
|
|
|
|
return false;
|
|
|
|
|
2014-04-07 00:25:58 +00:00
|
|
|
vddest->_removeMountDir(vdsrc);
|
2014-04-06 17:19:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Root::AddLoader(VFSLoader *ldr, const char *path /* = NULL */)
|
|
|
|
{
|
|
|
|
loaders.push_back(ldr);
|
|
|
|
AddVFSDir(ldr->getRoot(), path);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Root::AddArchiveLoader(VFSArchiveLoader *ldr)
|
|
|
|
{
|
|
|
|
archLdrs.push_back(ldr);
|
|
|
|
}
|
2014-04-15 13:04:33 +00:00
|
|
|
|
2014-04-06 17:19:33 +00:00
|
|
|
Dir *Root::AddArchive(const char *arch, void *opaque /* = NULL */)
|
|
|
|
{
|
2014-04-07 00:25:58 +00:00
|
|
|
return AddArchive(GetFile(arch), arch, opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
Dir *Root::AddArchive(File *file, const char *path /* = NULL */, void *opaque /* = NULL */)
|
|
|
|
{
|
|
|
|
if(!file || !file->open("rb"))
|
2014-04-06 17:19:33 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
Dir *ad = NULL;
|
|
|
|
VFSLoader *fileLdr = NULL;
|
|
|
|
for(ArchiveLoaderArray::iterator it = archLdrs.begin(); it != archLdrs.end(); ++it)
|
2014-04-07 00:25:58 +00:00
|
|
|
if((ad = (*it)->Load(file, &fileLdr, opaque)))
|
2014-04-06 17:19:33 +00:00
|
|
|
break;
|
|
|
|
if(!ad)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if(fileLdr)
|
|
|
|
loaders.push_back(fileLdr);
|
|
|
|
|
2014-04-07 00:25:58 +00:00
|
|
|
AddVFSDir(ad, path ? path : file->fullname());
|
2014-04-06 17:19:33 +00:00
|
|
|
|
|
|
|
return ad;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline static File *VFSHelper_GetFileByLoader(VFSLoader *ldr, const char *fn, const char *unmangled)
|
|
|
|
{
|
|
|
|
if(!ldr)
|
|
|
|
return NULL;
|
|
|
|
File *vf = ldr->Load(fn, unmangled);
|
|
|
|
if(vf)
|
|
|
|
ldr->getRoot()->addRecursive(vf);
|
|
|
|
return vf;
|
|
|
|
}
|
|
|
|
|
|
|
|
File *Root::GetFile(const char *fn)
|
|
|
|
{
|
|
|
|
const char *unmangled = fn;
|
|
|
|
std::string fixed = fn; // TODO: get rid of allocation here
|
|
|
|
FixPath(fixed);
|
|
|
|
fn = fixed.c_str();
|
|
|
|
|
|
|
|
File *vf = NULL;
|
|
|
|
|
|
|
|
vf = merged->getFile(fn);
|
|
|
|
|
|
|
|
// nothing found? maybe a loader has something.
|
|
|
|
// if so, add the newly created File to the tree.
|
|
|
|
if(!vf)
|
|
|
|
for(LoaderArray::iterator it = loaders.begin(); it != loaders.end(); ++it)
|
|
|
|
if((vf = VFSHelper_GetFileByLoader(*it, fn, unmangled)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
//printf("VFS: GetFile '%s' -> '%s' (%s:%p)\n", fn, vf ? vf->fullname() : "NULL", vf ? vf->getType() : "?", vf);
|
|
|
|
|
|
|
|
return vf;
|
|
|
|
}
|
|
|
|
|
|
|
|
InternalDir *Root::_GetDirByLoader(VFSLoader *ldr, const char *fn, const char *unmangled)
|
|
|
|
{
|
|
|
|
Dir *realdir = ldr->LoadDir(fn, unmangled);
|
|
|
|
InternalDir *ret = NULL;
|
|
|
|
if(realdir)
|
|
|
|
{
|
2014-04-15 13:04:33 +00:00
|
|
|
//ret = safecastNonNull<InternalDir*>(merged->_getDirEx(fn, fn, true, true, false).first);
|
|
|
|
ret = safecastNonNull<InternalDir*>(merged->_createAndInsertSubtree(fn));
|
2014-04-06 17:19:33 +00:00
|
|
|
ret->_addMountDir(realdir);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
DirBase *Root::GetDir(const char* dn, bool create /* = false */)
|
|
|
|
{
|
2014-04-15 13:04:33 +00:00
|
|
|
//printf("Root ::getDir [%s]\n", dn);
|
2014-04-06 17:19:33 +00:00
|
|
|
const char *unmangled = dn;
|
|
|
|
std::string fixed = dn; // TODO: get rid of alloc
|
|
|
|
FixPath(fixed);
|
|
|
|
dn = fixed.c_str();
|
|
|
|
|
|
|
|
if(!*dn)
|
|
|
|
return merged;
|
|
|
|
DirBase *vd = merged->getDir(dn);
|
|
|
|
|
|
|
|
if(!vd)
|
|
|
|
{
|
|
|
|
for(LoaderArray::iterator it = loaders.begin(); it != loaders.end(); ++it)
|
|
|
|
if((vd = _GetDirByLoader(*it, dn, unmangled)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(!vd && create)
|
2014-04-15 13:04:33 +00:00
|
|
|
vd = safecastNonNull<InternalDir*>(merged->_createAndInsertSubtree(dn)); // typecast is for debug checking only
|
2014-04-06 17:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//printf("VFS: GetDir '%s' -> '%s' (%s:%p)\n", dn, vd ? vd->fullname() : "NULL", vd ? vd->getType() : "?", vd);
|
|
|
|
|
|
|
|
return vd;
|
|
|
|
}
|
|
|
|
|
|
|
|
DirBase *Root::GetDirRoot()
|
|
|
|
{
|
|
|
|
return merged;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Root::FillDirView(const char *path, DirView& view)
|
|
|
|
{
|
2014-04-15 13:04:33 +00:00
|
|
|
//printf("Root::FillDirView [%s]\n", path);
|
2014-04-06 17:19:33 +00:00
|
|
|
return merged->fillView(path, view);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Root::ClearGarbage()
|
|
|
|
{
|
|
|
|
merged->clearGarbage();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DEBUG STUFF
|
|
|
|
|
|
|
|
struct _DbgParams
|
|
|
|
{
|
2014-04-15 13:04:33 +00:00
|
|
|
_DbgParams(std::ostream& os_, const std::string& path, const std::string& sp_)
|
|
|
|
: os(os_), mypath(path), sp(sp_) {}
|
2014-04-06 17:19:33 +00:00
|
|
|
|
|
|
|
std::ostream& os;
|
2014-04-15 13:04:33 +00:00
|
|
|
std::string mypath;
|
2014-04-06 17:19:33 +00:00
|
|
|
const std::string& sp;
|
2014-04-15 13:04:33 +00:00
|
|
|
std::set<std::string> dirnames;
|
2014-04-06 17:19:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void _DumpFile(File *vf, void *user)
|
|
|
|
{
|
|
|
|
_DbgParams& p = *((_DbgParams*)user);
|
2014-04-15 13:04:33 +00:00
|
|
|
p.os << p.sp << " F:" << vf->fullname() << " [" << vf->getType() << ", ref " << vf->getRefCount() << ", 0x" << vf << "]" << std::endl;
|
2014-04-06 17:19:33 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
|
|
|
|
static void _DumpDir(DirBase *vd, void *user)
|
2014-04-06 17:19:33 +00:00
|
|
|
{
|
|
|
|
_DbgParams& p = *((_DbgParams*)user);
|
2014-04-15 13:04:33 +00:00
|
|
|
if(!(vd->fullname()[0] == '/' && vd->fullnameLen() == 1)) // don't recurse down the root dir.
|
|
|
|
p.dirnames.insert(vd->name());
|
|
|
|
p.os << p.sp << "D : " << vd->fullname() << " [" << vd->getType() << ", ref " << vd->getRefCount() << ", 0x" << vd << "]" << std::endl;
|
|
|
|
}
|
2014-04-06 17:19:33 +00:00
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
static void _DumpTree(_DbgParams& p, Root& vfs, int level)
|
|
|
|
{
|
|
|
|
p.os << ">> [" << p.mypath << "]" << std::endl;
|
|
|
|
DirView view;
|
|
|
|
vfs.FillDirView(p.mypath.c_str(), view);
|
2014-04-06 17:19:33 +00:00
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
view.forEachDir(_DumpDir, &p);
|
|
|
|
view.forEachFile(_DumpFile, &p);
|
2014-04-06 17:19:33 +00:00
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
if(!level)
|
|
|
|
return;
|
2014-04-06 17:19:33 +00:00
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
std::string sub = p.sp + " ";
|
|
|
|
for(std::set<std::string>::iterator it = p.dirnames.begin(); it != p.dirnames.end(); ++it)
|
|
|
|
{
|
|
|
|
_DbgParams recP(p.os, joinPath(p.mypath, it->c_str()), sub);
|
|
|
|
_DumpTree(recP, vfs, level - 1);
|
|
|
|
}
|
2014-04-06 17:19:33 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 13:04:33 +00:00
|
|
|
void Root::debugDumpTree(std::ostream& os, const char *path, int level)
|
2014-04-06 17:19:33 +00:00
|
|
|
{
|
2014-04-15 13:04:33 +00:00
|
|
|
os << ">>> FILE TREE DUMP <<<" << std::endl;
|
|
|
|
_DbgParams recP(os, path, "");
|
|
|
|
_DumpTree(recP, *this, level);
|
2014-04-06 17:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VFS_NAMESPACE_END
|