1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-06-07 17:11:56 +00:00

Update ttvfs to new version

This commit is contained in:
fgenesis 2014-04-06 19:19:33 +02:00
parent 209ad526c6
commit 8026cdd905
43 changed files with 2124 additions and 2427 deletions

View file

@ -1,25 +1,26 @@
set(ttvfs_SRC
VFS.h
ttvfs.h
VFSArchiveLoader.h
VFSAtomic.cpp
VFSAtomic.h
VFSBase.cpp
VFSBase.h
VFSDefines.h
VFSDir.cpp
VFSDir.h
VFSDirInternal.cpp
VFSDirInternal.h
VFSDirView.cpp
VFSDirView.h
VFSFile.cpp
VFSFile.h
VFSFileFuncs.cpp
VFSFileFuncs.h
VFSHashmap.h
VFSHelper.cpp
VFSHelper.h
VFSInternal.h
VFSLoader.cpp
VFSLoader.h
VFSSelfRefCounter.h
VFSRefcounted.h
VFSRoot.h
VFSRoot.cpp
VFSSystemPaths.cpp
VFSSystemPaths.h
VFSTools.cpp

View file

@ -2,14 +2,15 @@
#define VFS_ARCHIVE_LOADER_H
#include "VFSDefines.h"
#include "VFSRefcounted.h"
VFS_NAMESPACE_START
class VFSDir;
class VFSFile;
class Dir;
class File;
class VFSLoader;
// Generic Archive loader interface that is supposed to return a valid VFSDir pointer when it
// Generic Archive loader interface that is supposed to return a valid Dir pointer when it
// was able to load 'arch' as an archive, and NULL if there was an error or the loader is
// unable to load that file type.
// 'asSubdir' - if this is true, the archive will be accessible as a folder (as in "test.zip/file.dat"),
@ -24,12 +25,12 @@ class VFSLoader;
// modify this object.
// The const char parameter is a string unique for each loader (to prevent accessing the pointer
// in a wrong way by the wrong loader). Example below.
class VFSArchiveLoader
class VFSArchiveLoader : public Refcounted
{
public:
virtual ~VFSArchiveLoader() {}
virtual VFSDir *Load(VFSFile *arch, VFSLoader **ldr, void *opaque = NULL) = 0;
virtual Dir *Load(File *arch, VFSLoader **ldr, void *opaque = NULL) = 0;
};
/* A possible struct for 'opaque' would be:

View file

@ -1,103 +0,0 @@
// VFSAtomic.cpp - atomic operations and thread locking
// For conditions of distribution and use, see copyright notice in VFS.h
/** --- Atomic operations and thread safety ---
* You may want to add your own implementation if thread safety is needed.
* If not, just leave everything like it is.
* If you are on windows, Interlocked[In/De]crement is faster than
explicit mutex locking for integer operations.
* TODO: The actual locking that is done in the tree when VFS_THREADSAFE is defined
is rather crude for the time beeing; a somewhat more efficient ReadWriteLock
implementation would be nice to have, someday.
* If you can, leave VFS_THREADSAFE undefined and do the locking externally,
it will probably have much better performance than if each and every operation
does a lock and unlock call.
(For a rather I/O based library this should not really make a difference, anyway.
But don't say you haven't been warned :) )
*/
#include "VFSInternal.h"
#include "VFSAtomic.h"
// for Interlocked[In/De]crement, if required
#if defined(_WIN32) && defined(VFS_THREADSAFE)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
VFS_NAMESPACE_START
#ifdef VFS_THREADSAFE
static Mutex mtx;
#endif
int Atomic_Incr(volatile int &i)
{
#ifdef VFS_THREADSAFE
# ifdef _WIN32
volatile LONG* dp = (volatile LONG*) &i;
return InterlockedIncrement( dp );
# else
Guard g(mtx);
# endif
#endif
return ++i;
}
int Atomic_Decr(volatile int &i)
{
#ifdef VFS_THREADSAFE
# ifdef _WIN32
volatile LONG* dp = (volatile LONG*) &i;
return InterlockedDecrement( dp );
# else
Guard g(mtx);
# endif
#endif
return --i;
}
/* Implement your Mutex class here.
Important: The mutex must be re-entrant/recursive,
means it must be possible to lock it from the same thread multiple times.
*/
Mutex::Mutex()
{
// implement your own if needed. Remove the trap below when you are done.
// This is to prevent people from defining VFS_THREADSAFE and expecting everything to work just like that :)
#ifdef VFS_THREADSAFE
#error VFSAtomic: Hey, you forgot to implement the mutex class, cant guarantee thread safety! Either undef VFS_THREADSAFE or read the docs and get your hands dirty.
#endif
}
Mutex::~Mutex()
{
// implement your own if needed
}
void Mutex::Lock(void)
{
// implement your own if needed
}
void Mutex::Unlock(void)
{
// implement your own if needed
}
Guard::Guard(Mutex& m)
: _m(m)
{
_m.Lock();
}
Guard::~Guard()
{
_m.Unlock();
}
VFS_NAMESPACE_END

View file

@ -1,39 +0,0 @@
// VFSAtomic.h - atomic operations and thread locking
// For conditions of distribution and use, see copyright notice in VFS.h
#ifndef VFS_ATOMIC_H
#define VFS_ATOMIC_H
#include "VFSDefines.h"
VFS_NAMESPACE_START
int Atomic_Incr(volatile int &i);
int Atomic_Decr(volatile int &i);
// generic Mutex class, needs to be reentrant/recursive.
class Mutex
{
public:
Mutex();
~Mutex();
void Lock();
void Unlock();
protected:
// add own stuff if needed
};
class Guard
{
public:
Guard(Mutex& m);
~Guard();
protected:
Mutex& _m;
};
VFS_NAMESPACE_END
#endif

View file

@ -8,24 +8,16 @@
VFS_NAMESPACE_START
VFSBase::VFSBase()
: ref(this)
#ifdef VFS_USE_HASHMAP
, _hash(0)
#endif
, _origin(NULL)
{
}
// call this only with a lock held!
void VFSBase::_setName(const char *n)
{
if(!n)
return;
_fullname = FixPath(n);
_name = PathToFileName(_fullname.c_str());
#ifdef VFS_USE_HASHMAP
_hash = STRINGHASH(_name);
#endif
_fullname = n;
FixPath(_fullname);
_name = GetBaseNameFromPath(_fullname.c_str());
}

View file

@ -6,50 +6,39 @@
#include <string>
#include "VFSDefines.h"
#include "VFSSelfRefCounter.h"
#include "VFSRefcounted.h"
VFS_NAMESPACE_START
// Used internally. No special properties, just holds some common code.
class VFSBase
class VFSBase : public Refcounted
{
public:
virtual ~VFSBase() {}
/** Returns the plain file name. Never NULL. */
inline const char *name() const { VFS_GUARD_OPT(this); return _name; }
inline const char *name() const { return _name; }
/** Returns the file name with full path. Never NULL. */
inline const char *fullname() const { VFS_GUARD_OPT(this); return _fullname.c_str(); }
inline const char *fullname() const { return _fullname.c_str(); }
/** To avoid strlen() */
inline size_t fullnameLen() const { VFS_GUARD_OPT(this); return _fullname.length(); }
inline size_t fullnameLen() const { return _fullname.length(); }
// We know that mem addr of _name > _fullname:
// _fullname: "abc/def/ghi/hjk.txt" (length = 19)
// _name: "hjk.txt" <-- want that length
// ptr diff: 12
// so in total: 19 - 12 == 7
inline size_t nameLen() const { VFS_GUARD_OPT(this); return _fullname.length() - (_name - _fullname.c_str()); }
inline size_t nameLen() const { return _fullname.length() - (_name - _fullname.c_str()); }
/** Basic RTTI, for debugging purposes */
virtual const char *getType() const { return "<BASE>"; }
virtual const char *getType() const = 0;
/** Can be overloaded to close resources this object keeps open */
virtual bool close() { return true; }
virtual void close() {}
/** Returns an object this object depends on. (used internally, by extensions) */
inline VFSBase *getOrigin() const { return _origin; }
inline void lock() const { _mtx.Lock(); }
inline void unlock() const { _mtx.Unlock(); }
inline Mutex& mutex() const { return _mtx; }
#ifdef VFS_USE_HASHMAP
inline size_t hash() const { return _hash; }
#endif
// For internal use
inline void _setOrigin(VFSBase *origin) { _origin = origin; }
/** Can be overloaded if necessary. Called by VFSHelper::ClearGarbage() */
virtual void clearGarbage() {}
protected:
VFSBase();
@ -57,23 +46,9 @@ protected:
private:
#ifdef VFS_USE_HASHMAP
size_t _hash;
#endif
const char *_name; // must point to an address constant during object lifetime (like _fullname.c_str() + N)
// (not necessary to have an additional string copy here, just wastes memory)
std::string _fullname;
mutable Mutex _mtx;
VFSBase *_origin; // May store a pointer if necessary. NOT ref-counted, because this would create cycles in almost all cases.
public:
/** Reference count, if the pointer to this file is stored somewhere it is advisable to increase
(ref++) it. If it reaches 0, this file is deleted automatically. */
SelfRefCounter<VFSBase> ref;
};

View file

@ -6,12 +6,9 @@
/* --- Config section -- modify as needed --- */
// choose a namespace name, or comment out to disable namespacing completely (not recommended)
#define VFS_NAMESPACE ttvfs
// Define this to allow dealing with files > 4 GB, using non-standard functions.
// This may or may not work with your platform/compiler, good luck.
//#define VFS_LARGEFILE_SUPPORT
#define VFS_LARGEFILE_SUPPORT
// Define this to make all operations case insensitive.
// Windows systems generally don't care much, but for Linux and Mac this can be used
@ -23,25 +20,10 @@
// on disk (see VFSLoader.cpp).
#define VFS_IGNORE_CASE
// Define this to make all VFSFile, VFSDir, VFSHelper operations thread-safe.
// If you do, do not forget to add your own implementation to VFSAtomic.cpp/.h !
// If this is not defined, you can still do manual locking if you know what you're doing,
// performance matters, and you implemented actual locking into the Mutex class.
// If no Mutex implementation is provided, its operations are no-ops, beware!
// Note: This adds a *lot* of overhead. Better ensure thread safety yourself, externally. Really!
// (Also note that this feature is *UNTESTED*. Don't activate.)
//#define VFS_THREADSAFE
// By default, ttvfs uses a std::map to store stuff.
// Uncomment the line below to use an (experimental!) hashmap.
// With std::map, iterating over entries will always deliver them in sorted order.
// The hashmap will deliver entries in random order, but lookup will be much faster
// (especially if VFS_IGNORE_CASE is set!)
//#define VFS_USE_HASHMAP
// These are used for small, temporary memory allocations that can remain on the stack.
// If alloca is available, this is the preferred way.
#include <stdlib.h>
#include <cstdlib>
#ifdef _WIN32
# include <malloc.h> // MSVC/MinGW still need this for alloca. Seems to be windows-specific failure
#endif
@ -54,15 +36,8 @@
/* --- End of config section --- */
#ifdef VFS_NAMESPACE
# define VFS_NAMESPACE_START namespace VFS_NAMESPACE {
# define VFS_NAMESPACE_END }
# define VFS_NAMESPACE_IMPL VFS_NAMESPACE::
#else
# define VFS_NAMESPACE_START
# define VFS_NAMESPACE_END
# define VFS_NAMESPACE_IMPL
#endif
#define VFS_NAMESPACE_START namespace ttvfs {
#define VFS_NAMESPACE_END }
VFS_NAMESPACE_START
@ -70,32 +45,31 @@ VFS_NAMESPACE_START
# if defined(_MSC_VER)
typedef __int64 vfspos;
# else
typedef long long vfspos;
# include <stdint.h>
typedef int64_t vfspos;
# endif
#else
typedef unsigned int vfspos;
#endif
// simple guard wrapper, works also if VFS_THREADSAFE is not defined
#define VFS_GUARD(obj) VFS_NAMESPACE_IMPL Guard __vfs_stack_guard((obj)->mutex())
// defines for optional auto-locking; only if VFS_THREADSAFE is defined
#ifdef VFS_THREADSAFE
# define VFS_GUARD_OPT(obj) VFS_GUARD(obj)
#else
# define VFS_GUARD_OPT(obj)
#endif
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
# define VFS_STRICMP stricmp
# define VFS_STRICMP _stricmp
static const vfspos npos = vfspos(-1i64);
#else
# define VFS_STRICMP strcasecmp
static const vfspos npos = vfspos(-1LL);
#endif
static const vfspos npos = vfspos(-1);
typedef void (*delete_func)(void *);
struct _AbiCheck
{
int structSize;
int vfsposSize;
int largefile;
int nocase;
};
typedef void *(*allocator_func)(size_t);
typedef void (*delete_func)(void*);
VFS_NAMESPACE_END

View file

@ -1,4 +1,4 @@
// VFSDir.cpp - basic directory interface + classes
// Dir.cpp - basic directory interface + classes
// For conditions of distribution and use, see copyright notice in VFS.h
#include <set>
@ -7,136 +7,21 @@
#include "VFSTools.h"
#include "VFSFile.h"
#include "VFSDir.h"
#include "VFSDirView.h"
#include "VFSLoader.h"
VFS_NAMESPACE_START
VFSDir::VFSDir(const char *fullpath)
DirBase::DirBase(const char *fullpath)
{
_setName(fullpath);
}
VFSDir::~VFSDir()
DirBase::~DirBase()
{
for(Files::iterator it = _files.begin(); it != _files.end(); ++it)
it->second.ptr->ref--;
for(Dirs::iterator it = _subdirs.begin(); it != _subdirs.end(); ++it)
it->second.ptr->ref--;
}
unsigned int VFSDir::load(bool recursive)
{
return 0;
}
VFSDir *VFSDir::createNew(const char *dir) const
{
VFSDir *vd = new VFSDir(dir);
vd->_setOrigin(getOrigin());
return vd;
}
bool VFSDir::add(VFSFile *f, bool overwrite, EntryFlags flag)
{
if(!f)
return false;
VFS_GUARD_OPT(this);
Files::iterator it = _files.find(f->name());
if(it != _files.end())
{
if(overwrite)
{
VFSFile *oldf = it->second.ptr;
if(oldf == f)
return false;
_files.erase(it);
oldf->ref--;
}
else
return false;
}
f->ref++;
_files[f->name()] = MapEntry<VFSFile>(f, flag);
return true;
}
bool VFSDir::addRecursive(VFSFile *f, bool overwrite, EntryFlags flag)
{
if(!f)
return false;
VFS_GUARD_OPT(this);
// figure out directory from full file name
VFSDir *vdir;
size_t prefixLen = f->fullnameLen() - f->nameLen();
if(prefixLen)
{
char *dirname = (char*)VFS_STACK_ALLOC(prefixLen);
--prefixLen; // -1 to strip the trailing '/'. That's the position where to put the terminating null byte.
memcpy(dirname, f->fullname(), prefixLen); // copy trailing null byte
dirname[prefixLen] = 0;
vdir = getDir(dirname, true);
VFS_STACK_FREE(dirname);
}
else
vdir = this;
return vdir->add(f, true, flag);
}
bool VFSDir::merge(VFSDir *dir, bool overwrite, EntryFlags flag)
{
if(!dir)
return false;
if(dir == this)
return true; // nothing to do then
// HACK: make sure the files are there before merging
this->load(false);
dir->load(false);
bool result = false;
VFS_GUARD_OPT(this);
for(Files::iterator it = dir->_files.begin(); it != dir->_files.end(); ++it)
result = add(it->second.ptr, overwrite, flag) || result;
for(Dirs::iterator it = dir->_subdirs.begin(); it != dir->_subdirs.end(); ++it)
result = insert(it->second.ptr, overwrite, flag) || result;
return result;
}
bool VFSDir::insert(VFSDir *subdir, bool overwrite, EntryFlags flag)
{
if(!subdir)
return false;
VFS_GUARD_OPT(this);
// With load() cleaning up the tree, it is ok to add subsequent VFSDirs directly.
// This will be very useful at some point when files can be mounted into a directory
// belonging to an archive, and this way adding files to the archive.
Dirs::iterator it = _subdirs.find(subdir->name());
if(it == _subdirs.end())
{
subdir->ref++;
_subdirs[subdir->name()] = MapEntry<VFSDir>(subdir, flag);
return true;
}
else
{
it->second.ptr->merge(subdir, overwrite, flag);
return false;
}
}
VFSFile *VFSDir::getFile(const char *fn)
File *DirBase::getFile(const char *fn, bool lazyLoad /* = true */)
{
while(fn[0] == '.' && fn[1] == '/') // skip ./
fn += 2;
@ -155,10 +40,8 @@ VFSFile *VFSDir::getFile(const char *fn)
char *dup = (char*)VFS_STACK_ALLOC(len + 1);
memcpy(dup, fn, len + 1); // also copy the null-byte
slashpos = dup + (slashpos - fn); // use direct offset, not to have to recount again the first time
VFSDir *subdir = this;
DirBase *subdir = this;
const char *ptr = dup;
Dirs::iterator it;
VFS_GUARD_OPT(this);
goto pos_known;
do
@ -172,11 +55,7 @@ VFSFile *VFSDir::getFile(const char *fn)
pos_known:
*slashpos = 0;
it = subdir->_subdirs.find(ptr);
if(it != subdir->_subdirs.end())
subdir = it->second.ptr; // found it
else
subdir = NULL; // bail out
subdir = subdir->getDirByName(ptr, true, true);
}
while(subdir);
VFS_STACK_FREE(dup);
@ -184,23 +63,21 @@ VFSFile *VFSDir::getFile(const char *fn)
return NULL;
// restore pointer into original string, by offset
ptr = fn + (ptr - dup);
Files::iterator ft = subdir->_files.find(ptr);
return ft != subdir->_files.end() ? ft->second.ptr : NULL;
return subdir->getFileByName(ptr, lazyLoad);
}
// no subdir? file must be in this dir now.
VFS_GUARD_OPT(this);
Files::iterator it = _files.find(fn);
return it != _files.end() ? it->second.ptr : NULL;
return getFileByName(fn, lazyLoad);
}
VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */)
DirBase *DirBase::getDir(const char *subdir, bool forceCreate /* = false */, bool lazyLoad /* = true */, bool useSubtrees /* = true */)
{
if(!subdir[0] || (subdir[0] == '.' && (!subdir[1] || subdir[1] == '/'))) // empty string or "." or "./" ? use this.
SkipSelfPath(subdir);
if(!subdir[0])
return this;
VFSDir *ret = NULL;
DirBase *ret = NULL;
char *slashpos = (char *)strchr(subdir, '/');
// if there is a '/' in the string, descend into subdir and continue there
@ -212,17 +89,17 @@ VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */)
char * const t = (char*)VFS_STACK_ALLOC(copysize + 1);
memcpy(t, subdir, copysize);
t[copysize] = 0;
VFS_GUARD_OPT(this);
Dirs::iterator it = _subdirs.find(t);
if(it != _subdirs.end())
if(DirBase *dir = getDirByName(t, lazyLoad, useSubtrees))
{
ret = it->second.ptr->getDir(sub, forceCreate); // descend into subdirs
// TODO: get rid of recursion
ret = dir->getDir(sub, forceCreate, lazyLoad); // descend into subdirs
}
else if(forceCreate)
{
// -> newname = fullname() + '/' + t
size_t fullLen = fullnameLen();
VFSDir *ins;
DirBase *ins;
if(fullLen)
{
char * const newname = (char*)VFS_STACK_ALLOC(fullLen + copysize + 2);
@ -239,15 +116,13 @@ VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */)
ins = createNew(t);
_subdirs[ins->name()] = ins;
ret = ins->getDir(sub, true); // create remaining structure
ret = ins->getDir(sub, true, lazyLoad); // create remaining structure
}
}
else
{
VFS_GUARD_OPT(this);
Dirs::iterator it = _subdirs.find(subdir);
if(it != _subdirs.end())
ret = it->second.ptr;
if(DirBase *dir = getDirByName(subdir, lazyLoad, useSubtrees))
ret = dir;
else if(forceCreate)
{
size_t fullLen = fullnameLen();
@ -278,168 +153,211 @@ VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */)
return ret;
}
void VFSDir::clearMounted()
static void _iterDirs(Dirs &m, DirEnumCallback f, void *user)
{
for(FileIter it = _files.begin(); it != _files.end(); )
{
MapEntry<VFSFile>& e = it->second;
if(e.isMounted())
{
e.ptr->ref--;
_files.erase(it++);
}
else
++it;
}
for(DirIter it = _subdirs.begin(); it != _subdirs.end(); )
{
MapEntry<VFSDir>& e = it->second;
if(e.isMounted())
{
e.ptr->ref--;
_subdirs.erase(it++);
}
else
{
it->second.ptr->clearMounted();
++it;
}
}
for(Dirs::iterator it = m.begin(); it != m.end(); ++it)
f(it->second.content(), user);
}
template<typename T> static void iterIncref(T *b, void*) { ++(b->ref); }
template<typename T> static void iterDecref(T *b, void*) { --(b->ref); }
static void _iterDirs(VFSDir::Dirs &m, DirEnumCallback f, void *user)
void DirBase::forEachDir(DirEnumCallback f, void *user /* = NULL */, bool safe /* = false */)
{
for(DirIter it = m.begin(); it != m.end(); ++it)
f(it->second.ptr, user);
}
void VFSDir::forEachDir(DirEnumCallback f, void *user /* = NULL */, bool safe /* = false */)
{
VFS_GUARD_OPT(this);
if(safe)
{
Dirs cp = _subdirs;
_iterDirs(cp, iterIncref<VFSDir>, NULL);
_iterDirs(cp, f, user);
_iterDirs(cp, iterDecref<VFSDir>, NULL);
}
else
_iterDirs(_subdirs, f, user);
}
static void _iterFiles(VFSDir::Files &m, FileEnumCallback f, void *user)
DirBase *DirBase::getDirByName(const char *dn, bool /* unused: lazyLoad = true */, bool useSubtrees /* = true */)
{
for(FileIter it = m.begin(); it != m.end(); ++it)
f(it->second.ptr, user);
Dirs::const_iterator it = _subdirs.find(dn);
return it != _subdirs.end() ? it->second : NULL;
}
void VFSDir::forEachFile(FileEnumCallback f, void *user /* = NULL */, bool safe /* = false */)
void DirBase::clearGarbage()
{
VFS_GUARD_OPT(this);
for(Dirs::iterator it = _subdirs.begin(); it != _subdirs.end(); ++it)
it->second->clearGarbage();
}
Dir::Dir(const char *fullpath, VFSLoader *ldr)
: DirBase(fullpath), _loader(ldr)
{
}
Dir::~Dir()
{
}
File *Dir::getFileByName(const char *fn, bool lazyLoad /* = true */)
{
Files::iterator it = _files.find(fn);
if(it != _files.end())
return it->second;
if(!lazyLoad || !_loader)
return NULL;
// Lazy-load file if it's not in the tree yet
// TODO: get rid of alloc
std::string fn2 = joinPath(fullname(), GetBaseNameFromPath(fn));
File *f = _loader->Load(fn2.c_str(), fn2.c_str());
if(f)
_files[f->name()] = f;
return f;
}
static void _iterFiles(Files &m, FileEnumCallback f, void *user)
{
for(Files::iterator it = m.begin(); it != m.end(); ++it)
f(it->second.content(), user);
}
void Dir::forEachFile(FileEnumCallback f, void *user /* = NULL */, bool safe /* = false */)
{
load();
if(safe)
{
Files cp = _files;
_iterFiles(cp, iterIncref<VFSFile>, NULL);
_iterFiles(cp, f, user);
_iterFiles(cp, iterDecref<VFSFile>, NULL);
}
else
_iterFiles(_files, f, user);
}
// ----- VFSDirReal start here -----
bool Dir::add(File *f)
{
if(!f)
return false;
Files::iterator it = _files.find(f->name());
if(it != _files.end())
{
File *oldf = it->second;
if(oldf == f)
return false;
_files.erase(it);
}
_files[f->name()] = f;
return true;
}
bool Dir::addRecursive(File *f, size_t skip /* = 0 */)
{
if(!f)
return false;
Dir *vdir = this;
if(f->fullnameLen() - f->nameLen() > skip)
{
// figure out directory from full file name
size_t prefixLen = f->fullnameLen() - f->nameLen() - skip;
// prefixLen == 0 is invalid, prefixLen == 1 is just a single '/', which will be stripped away below.
// in both cases, just use 'this'.
if(prefixLen > 1)
{
char *dirname = (char*)VFS_STACK_ALLOC(prefixLen);
--prefixLen; // -1 to strip the trailing '/'. That's the position where to put the terminating null byte.
++skip;
memcpy(dirname, f->fullname() + skip, prefixLen); // copy trailing null byte
dirname[prefixLen] = 0;
vdir = safecastNonNull<Dir*>(getDir(dirname, true));
VFS_STACK_FREE(dirname);
}
}
return vdir->add(f);
}
void Dir::clearGarbage()
{
DirBase::clearGarbage();
for(Files::iterator it = _files.begin(); it != _files.end(); ++it)
it->second->clearGarbage();
}
bool Dir::_addToView(char *path, DirView& view)
{
if(Dir *dir = safecast<Dir*>(getDir(path)))
{
view.add(dir);
return true;
}
return false;
}
DirBase *Dir::getDirByName(const char *dn, bool lazyLoad /* = true */, bool useSubtrees /* = true */)
{
DirBase *sub;
if((sub = DirBase::getDirByName(dn, lazyLoad, useSubtrees)))
return sub;
if(!lazyLoad || !_loader)
return NULL;
// Lazy-load file if it's not in the tree yet
// TODO: get rid of alloc
std::string fn2 = joinPath(fullname(), dn);
sub = _loader->LoadDir(fn2.c_str(), fn2.c_str());
if(sub)
_subdirs[sub->name()] = sub;
return sub;
}
VFSDirReal::VFSDirReal(const char *dir) : VFSDir(dir)
// ----- DiskDir start here -----
DiskDir::DiskDir(const char *path, VFSLoader *ldr) : Dir(path, ldr)
{
}
VFSDir *VFSDirReal::createNew(const char *dir) const
DiskDir *DiskDir::createNew(const char *dir) const
{
return new VFSDirReal(dir);
return new DiskDir(dir, getLoader());
}
unsigned int VFSDirReal::load(bool recursive)
void DiskDir::load()
{
VFS_GUARD_OPT(this);
Files remainF;
Dirs remainD;
remainF.swap(_files);
remainD.swap(_subdirs);
// _files, _subdirs now empty
_files.clear();
_subdirs.clear();
// TODO: cache existing files and keep them unless they do no longer exist
StringList li;
GetFileList(fullname(), li);
for(StringList::iterator it = li.begin(); it != li.end(); ++it)
{
// file was already present, move over and erase
FileIter fi = remainF.find(it->c_str());
if(fi != remainF.end())
{
_files[fi->first] = fi->second;
remainF.erase(fi);
continue;
}
// TODO: use stack alloc
std::string tmp = fullname();
tmp += '/';
tmp += *it;
VFSFileReal *f = new VFSFileReal(tmp.c_str());
DiskFile *f = new DiskFile(tmp.c_str());
_files[f->name()] = f;
}
unsigned int sum = li.size();
li.clear();
GetDirList(fullname(), li, 0);
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
for(StringList::iterator it = li.begin(); it != li.end(); ++it)
{
// subdir was already present, move over and erase
DirIter fi = remainD.find(it->c_str());
if(fi != remainD.end())
{
if(recursive)
sum += fi->second.ptr->load(true);
++sum;
_subdirs[fi->first] = fi->second;
remainD.erase(fi);
continue;
}
// TODO: use stack alloc
std::string full(fullname());
full += '/';
full += *it; // GetDirList() always returns relative paths
VFSDir *d = createNew(full.c_str());
if(recursive)
sum += d->load(true);
++sum;
Dir *d = createNew(full.c_str());
_subdirs[d->name()] = d;
}
// clean up & remove no longer existing files & dirs,
// and move over entries mounted here.
for(FileIter it = remainF.begin(); it != remainF.end(); ++it)
if(it->second.isMounted())
_files[it->first] = it->second;
else
it->second.ptr->ref--;
for(DirIter it = remainD.begin(); it != remainD.end(); ++it)
if(it->second.isMounted())
_subdirs[it->first] = it->second;
else
it->second.ptr->ref--;
return sum;
}
VFS_NAMESPACE_END

View file

@ -17,10 +17,6 @@ VFS_NAMESPACE_START
#ifdef VFS_IGNORE_CASE
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996)
# endif
struct ci_less
{
@ -29,24 +25,13 @@ struct ci_less
return VFS_STRICMP(a, b) < 0;
}
};
struct ci_equal
{
inline bool operator() (const char *a, const char *b) const
{
return !VFS_STRICMP(a, b);
}
};
typedef ci_less map_compare;
inline int casecmp(const char *a, const char *b)
{
return VFS_STRICMP(a, b);
}
# ifdef _MSC_VER
# pragma warning(pop)
# endif
#else // VFS_IGNORE_CASE
struct cs_less
@ -56,6 +41,7 @@ struct cs_less
return strcmp(a, b) < 0;
}
};
typedef cs_less map_compare;
inline int casecmp(const char *a, const char *b)
{
@ -66,146 +52,109 @@ inline int casecmp(const char *a, const char *b)
#endif // VFS_IGNORE_CASE
#ifdef VFS_USE_HASHMAP
struct hashmap_eq
{
inline bool operator() (const char *a, const char *b, size_t h, const VFSBase *itm) const
{
// quick check - instead of just comparing the strings,
// check the hashes first. If they don't match there is no
// need to check the strings at all.
return itm->hash() == h && !casecmp(a, b);
}
};
class Dir;
class DirBase;
class DirView;
class File;
class VFSLoader;
struct charptr_hash
{
inline size_t operator()(const char *s)
{
// case sensitive or in-sensitive, depending on config
return STRINGHASH(s);
}
};
#endif // VFS_USE_HASHMAP
typedef void (*FileEnumCallback)(File *vf, void *user);
typedef void (*DirEnumCallback)(DirBase *vd, void *user);
class VFSDir;
class VFSFile;
typedef void (*FileEnumCallback)(VFSFile *vf, void *user);
typedef void (*DirEnumCallback)(VFSDir *vd, void *user);
// Avoid using std::string as key.
// The file names are known to remain constant during each object's lifetime,
// so just keep the pointers and use an appropriate comparator function.
typedef std::map<const char *, CountedPtr<DirBase>, map_compare> Dirs;
typedef std::map<const char *, CountedPtr<File>, map_compare> Files;
class VFSDir : public VFSBase
class DirBase : public VFSBase
{
public:
// bitmask
enum EntryFlags
{
NONE = 0,
MOUNTED = 1
};
template<typename T> struct MapEntry
{
MapEntry() {}
MapEntry(T *p, EntryFlags flg = NONE) : ptr(p), flags(flg) {}
inline bool isMounted() { return flags & MOUNTED; }
T *ptr;
EntryFlags flags;
};
// Avoid using std::string as key.
// The file names are known to remain constant during each object's lifetime,
// so just keep the pointers and use an appropriate comparator function.
#ifdef VFS_USE_HASHMAP
// VFS_IGNORE_CASE already handled in hash generation
typedef HashMap<const char *, MapEntry<VFSDir>, charptr_hash, hashmap_eq> Dirs;
typedef HashMap<const char *, MapEntry<VFSFile>, charptr_hash, hashmap_eq> Files;
#else
# ifdef VFS_IGNORE_CASE
typedef std::map<const char *, MapEntry<VFSDir>, ci_less> Dirs;
typedef std::map<const char *, MapEntry<VFSFile>, ci_less> Files;
# else
typedef std::map<const char *, MapEntry<VFSDir>, cs_less> Dirs;
typedef std::map<const char *, MapEntry<VFSFile>, cs_less> Files;
# endif
#endif
VFSDir(const char *fullpath);
virtual ~VFSDir();
/** Enumerate directory with given path. Keeps previously loaded entries.
Returns the amount of files found. */
virtual unsigned int load(bool recursive);
/** Creates a new virtual directory of an internally specified type. */
virtual VFSDir *createNew(const char *dir) const;
/** For debugging. Does never return NULL. */
virtual const char *getType() const { return "VFSDir"; }
/** Can be overloaded if necessary. Called by VFSHelper::ClearGarbage() */
virtual void clearGarbage() {}
/** Can be overloaded to close resources this dir keeps open */
virtual bool close() { return true; }
DirBase(const char *fullpath);
virtual ~DirBase();
/** Returns a file for this dir's subtree. Descends if necessary.
Returns NULL if the file is not found. */
VFSFile *getFile(const char *fn);
File *getFile(const char *fn, bool lazyLoad = true);
/** Returns a subdir, descends if necessary. If forceCreate is true,
create directory tree if it does not exist, and return the originally requested
subdir. Otherwise return NULL if not found. */
VFSDir *getDir(const char *subdir, bool forceCreate = false);
DirBase *getDir(const char *subdir, bool forceCreate = false, bool lazyLoad = true, bool useSubtrees = true);
/** Recursively drops all files/dirs that were mounted into this directory (and subdirs) */
void clearMounted();
/** Returns a file from this dir's file map.
Expects the actual file name without path - does NOT descend. */
virtual File *getFileByName(const char *fn, bool lazyLoad = true) = 0;
virtual DirBase *getDirByName(const char *fn, bool lazyLoad = true, bool useSubtrees = true);
/** Iterate over all files or directories, calling a callback function,
optionally with additional userdata. If safe is true, iterate over a copy.
This is useful if the callback function modifies the tree, e.g.
adds or removes files. */
void forEachFile(FileEnumCallback f, void *user = NULL, bool safe = false);
void forEachDir(DirEnumCallback f, void *user = NULL, bool safe = false);
optionally with additional userdata. If safe is true, iterate over a copy.
This is useful if the callback function modifies the tree, e.g.
adds or removes files. */
virtual void forEachDir(DirEnumCallback f, void *user = NULL, bool safe = false);
virtual void forEachFile(FileEnumCallback f, void *user = NULL, bool safe = false) = 0;
virtual void clearGarbage();
/* Below is for internal use -- take care if using these externally! */
bool insert(VFSDir *subdir, bool overwrite, EntryFlags flag);
bool merge(VFSDir *dir, bool overwrite, EntryFlags flag);
/** Adds a file directly to this directory, allows any name.
If another file with this name already exists, optionally drop the old one out.
Returns whether the file was actually added. */
bool add(VFSFile *f, bool overwrite, EntryFlags flag);
/** Like add(), but if the file name contains a path, descend the tree to the target dir.
Not-existing subdirs are created on the way. */
bool addRecursive(VFSFile *f, bool overwrite, EntryFlags flag);
virtual bool _addToView(char *path, DirView& view) = 0;
protected:
// std::map<const char*,X> or ttvfs::HashMap<const char*, X> stores for files and subdirs.
Files _files;
/** Creates a new dir of the same type to be used as child of this. */
virtual DirBase *createNew(const char *dir) const = 0;
Dirs _subdirs;
};
typedef VFSDir::Files::iterator FileIter;
typedef VFSDir::Dirs::iterator DirIter;
class VFSDirReal : public VFSDir
class Dir : public DirBase
{
public:
VFSDirReal(const char *dir);
virtual ~VFSDirReal() {};
virtual unsigned int load(bool recursive);
virtual VFSDir *createNew(const char *dir) const;
virtual const char *getType(void) const { return "VFSDirReal"; }
Dir(const char *fullpath, VFSLoader *ldr);
virtual ~Dir();
/** Adds a file directly to this directory, allows any name.
If another file with this name already exists, drop the old one out.
Returns whether the file was actually added (false if the same file already existed) */
bool add(File *f);
/** Like add(), but if the file name contains a path, descend the tree to the target dir.
Not-existing subdirs are created on the way. */
bool addRecursive(File *f, size_t skip = 0);
/** Enumerate directory with given path. Clears previously loaded entries. */
virtual void load() = 0;
void forEachFile(FileEnumCallback f, void *user = NULL, bool safe = false);
virtual void clearGarbage();
bool _addToView(char *path, DirView& view);
DirBase *getDirByName(const char *dn, bool lazyLoad = true, bool useSubtrees = true);
File *getFileByName(const char *fn, bool lazyLoad = true);
protected:
inline VFSLoader *getLoader() const { return _loader; }
Files _files;
private:
VFSLoader *_loader;
};
class DiskDir : public Dir
{
public:
DiskDir(const char *path, VFSLoader *ldr);
virtual ~DiskDir() {};
virtual void load();
virtual DiskDir *createNew(const char *dir) const;
virtual const char *getType() const { return "DiskDir"; }
};
VFS_NAMESPACE_END

View file

@ -0,0 +1,169 @@
#include "VFSDirInternal.h"
#include "VFSDirView.h"
#include "VFSInternal.h"
#include "VFSFile.h"
#include "VFSTools.h"
#include <algorithm>
VFS_NAMESPACE_START
// Internal class, not to be used outside
InternalDir::InternalDir(const char *fullpath)
: DirBase(fullpath)
{
}
InternalDir::~InternalDir()
{
}
void InternalDir::_clearDirs()
{
_subdirs.clear();
}
void InternalDir::_clearMounts()
{
_mountedDirs.clear();
}
InternalDir *InternalDir::createNew(const char *dir) const
{
return new InternalDir(dir);
}
void InternalDir::close()
{
for(MountedDirs::iterator it = _mountedDirs.begin(); it != _mountedDirs.end(); ++it)
(*it)->close();
}
void InternalDir::_addMountDir(CountedPtr<DirBase> d)
{
// move to end of vector if already mounted
for(MountedDirs::iterator it = _mountedDirs.begin(); it != _mountedDirs.end(); ++it)
if(*it == d)
{
_mountedDirs.erase(it);
break;
}
_mountedDirs.push_back(d);
}
void InternalDir::_removeMountDir(DirBase *d)
{
for(MountedDirs::iterator it = _mountedDirs.begin(); it != _mountedDirs.end(); ++it)
if(it->content() == d)
{
_mountedDirs.erase(it);
return; // pointers are unique
}
}
File *InternalDir::getFileByName(const char *fn, bool lazyLoad /* = true */)
{
if(_mountedDirs.size())
for(MountedDirs::reverse_iterator it = _mountedDirs.rbegin(); it != _mountedDirs.rend(); ++it)
if(File *f = (*it)->getFileByName(fn, lazyLoad))
return f;
return NULL;
}
DirBase *InternalDir::getDirByName(const char *dn, bool lazyLoad /* = true */, bool useSubtrees /* = true */)
{
DirBase *sub;
if((sub = DirBase::getDirByName(dn, lazyLoad)))
return sub;
if(useSubtrees)
for(MountedDirs::reverse_iterator it = _mountedDirs.rbegin(); it != _mountedDirs.rend(); ++it)
if((sub = (*it)->getDirByName(dn, lazyLoad)))
return sub;
return NULL;
}
static void _addFileCallback(File *f, void *p)
{
((Files*)p)->insert(std::make_pair(f->name(), f)); // only inserts if not exist
}
void InternalDir::forEachFile(FileEnumCallback f, void *user /* = NULL */, bool /*ignored*/)
{
Files flist; // TODO: optimize allocation
for(MountedDirs::reverse_iterator it = _mountedDirs.rbegin(); it != _mountedDirs.rend(); ++it)
(*it)->forEachFile(_addFileCallback, &flist);
for(Files::iterator it = flist.begin(); it != flist.end(); ++it)
f(it->second, user);
}
static void _iterDirs(Dirs &m, DirEnumCallback f, void *user)
{
for(Dirs::iterator it = m.begin(); it != m.end(); ++it)
f(it->second.content(), user);
}
void InternalDir::forEachDir(DirEnumCallback f, void *user /* = NULL */, bool safe /* = false */)
{
for(MountedDirs::reverse_iterator it = _mountedDirs.rbegin(); it != _mountedDirs.rend(); ++it)
(*it)->forEachDir(f, user, safe);
}
bool InternalDir::fillView(const char *path, DirView& view)
{
SkipSelfPath(path);
view.init(path);
size_t len = strlen(path) + 1;
char *pathcopy = (char*)VFS_STACK_ALLOC(len);
memcpy(pathcopy, path, len);
bool added = _addToView(pathcopy, view);
VFS_STACK_FREE(pathcopy);
return added;
}
bool InternalDir::_addToView(char *path, DirView& view)
{
bool added = false;
if(!*path)
{
for(MountedDirs::iterator it = _mountedDirs.begin(); it != _mountedDirs.end(); ++it)
{
added = true;
view.add(it->content());
}
}
else
{
SkipSelfPath(path);
char dummy = 0;
char *slashpos = strchr(path, '/');
char *tail = slashpos ? slashpos+1 : &dummy;
if(slashpos)
*slashpos = 0;
for(MountedDirs::iterator it = _mountedDirs.begin(); it != _mountedDirs.end(); ++it)
if(DirBase *subdir = (*it)->getDirByName(path))
added = subdir->_addToView(tail, view) || added;
if(InternalDir *subdir = safecast<InternalDir*>(getDirByName(path, true, false)))
added = subdir->_addToView(tail, view) || added;
if(slashpos)
*slashpos = '/';
}
return added;
}
VFS_NAMESPACE_END

View file

@ -0,0 +1,55 @@
#ifndef VFS_DIR_INTERNAL_H
#define VFS_DIR_INTERNAL_H
#include "VFSDir.h"
#include <vector>
VFS_NAMESPACE_START
class Root;
class DirView;
// Internal class, not to be used outside
class InternalDir : public DirBase
{
friend class Root;
public:
bool fillView(const char *path, DirView& view);
// virtual overrides (final)
const char *getType() const { return "InternalDir"; }
void forEachFile(FileEnumCallback f, void *user = NULL, bool safe = false);
void forEachDir(DirEnumCallback f, void *user = NULL, bool safe = false);
File *getFileByName(const char *fn, bool lazyLoad = true);
DirBase *getDirByName(const char *fn, bool lazyLoad = true, bool useSubtrees = true);
void close();
protected:
// virtual overrides(final)
InternalDir *createNew(const char *dir) const;
bool _addToView(char *path, DirView& view);
private:
InternalDir(const char *);
virtual ~InternalDir();
typedef std::vector<CountedPtr<DirBase> > MountedDirs;
MountedDirs _mountedDirs;
void _clearDirs();
void _clearMounts();
void _addMountDir(CountedPtr<DirBase> d);
void _removeMountDir(DirBase *d);
};
VFS_NAMESPACE_END
#endif

View file

@ -0,0 +1,74 @@
#include "VFSDirView.h"
#include "VFSFile.h"
VFS_NAMESPACE_START
DirView::DirView()
: DirBase("")
{
}
DirView::~DirView()
{
}
void DirView::init(const char *name)
{
_setName(name);
_view.clear();
}
void DirView::add(DirBase *dir)
{
for(ViewList::iterator it = _view.begin(); it != _view.end(); ++it)
if(it->content() == dir)
return;
_view.push_back(dir);
}
File *DirView::getFileByName(const char *fn, bool lazyLoad /* = true */)
{
for(ViewList::reverse_iterator it = _view.rbegin(); it != _view.rend(); ++it)
if(File *f = (*it)->getFileByName(fn))
return f;
return NULL;
}
void DirView::forEachDir(DirEnumCallback f, void *user, bool safe)
{
for(ViewList::reverse_iterator it = _view.rbegin(); it != _view.rend(); ++it)
(*it)->forEachDir(f, user, safe);
}
static void _addFileCallback(File *f, void *p)
{
((Files*)p)->insert(std::make_pair(f->name(), f)); // only inserts if not exist
}
void DirView::forEachFile(FileEnumCallback f, void *user, bool /*ignored*/)
{
Files flist; // TODO: optimize allocation
for(ViewList::reverse_iterator it = _view.rbegin(); it != _view.rend(); ++it)
(*it)->forEachFile(_addFileCallback, &flist);
for(Files::iterator it = flist.begin(); it != flist.end(); ++it)
f(it->second, user);
}
bool DirView::_addToView(char *path, DirView& view)
{
if(_view.empty())
return false;
for(ViewList::const_iterator it = _view.begin(); it != _view.end(); ++it) // not reverse!
view.add(const_cast<DirBase*>(it->content()));
return true;
}
static void __test__()
{
new DirView;
}
VFS_NAMESPACE_END

View file

@ -0,0 +1,40 @@
#ifndef VFS_DIR_VIEW_H
#define VFS_DIR_VIEW_H
#include <vector>
#include "VFSDir.h"
VFS_NAMESPACE_START
class InternalDir;
class File;
class DirView : public DirBase
{
public:
DirView();
~DirView();
void init(const char *);
void add(DirBase *);
virtual File *getFileByName(const char *fn, bool lazyLoad = true);
virtual void forEachDir(DirEnumCallback f, void *user = NULL, bool safe = false);
virtual void forEachFile(FileEnumCallback f, void *user = NULL, bool safe = false);
virtual const char *getType() const { return "DirView"; }
virtual DirBase *createNew(const char *dir) const { return NULL; }
bool _addToView(char *path, DirView& view);
protected:
typedef std::vector<CountedPtr<DirBase> > ViewList;
ViewList _view;
};
VFS_NAMESPACE_END
#endif

View file

@ -6,203 +6,152 @@
#include "VFSTools.h"
#include "VFSFileFuncs.h"
#include <cstdio>
#include <cstdio> // for SEEK_* constants
VFS_NAMESPACE_START
VFSFile::VFSFile(const char *name)
: _buf(NULL), _delfunc(NULL)
File::File(const char *name)
{
_setName(name);
}
VFSFile::~VFSFile()
File::~File()
{
}
DiskFile::DiskFile(const char *name /* = NULL */)
: File(name), _fh(NULL), _buf(NULL)
{
}
DiskFile::~DiskFile()
{
close();
dropBuf(true);
}
void VFSFile::delBuf(void *mem)
bool DiskFile::open(const char *mode /* = NULL */)
{
deleteHelper(_delfunc, (char*) mem);
}
const void *VFSFile::getBuf(allocator_func alloc /* = NULL */, delete_func del /* = NULL */)
{
assert(!alloc == !del); // either both or none may be defined. Checked extra early to prevent possible errors later.
VFS_GUARD_OPT(this);
if(_buf)
return _buf;
bool op = isopen();
if(!op && !open()) // open with default params if not open
return NULL;
unsigned int s = (unsigned int)size();
_buf = allocHelper(alloc, s + 4); // a bit extra padding
if(!_buf)
return NULL;
_delfunc = del;
vfspos offs;
if(op)
{
vfspos oldpos = getpos();
seek(0);
offs = read(_buf, s);
seek(oldpos);
}
else
{
offs = read(_buf, s);
close();
}
// Might as well be text mode reading, which means less actual bytes than size() said,
// so this can't be done earlier.
memset((char*)_buf + offs, 0, 4);
return _buf;
}
void VFSFile::dropBuf(bool del)
{
VFS_GUARD_OPT(this);
if(del)
delBuf(_buf);
_buf = NULL;
}
VFSFileReal::VFSFileReal(const char *name /* = NULL */)
: VFSFile(name), _fh(NULL), _buf(NULL)
{
}
VFSFileReal::~VFSFileReal()
{
}
bool VFSFileReal::open(const char *mode /* = NULL */)
{
VFS_GUARD_OPT(this);
if(isopen())
close();
dropBuf(true);
_fh = real_fopen(fullname(), mode ? mode : "rb");
return !!_fh;
}
bool VFSFileReal::isopen(void) const
bool DiskFile::isopen() const
{
VFS_GUARD_OPT(this);
return !!_fh;
}
bool VFSFileReal::iseof(void) const
bool DiskFile::iseof() const
{
VFS_GUARD_OPT(this);
return !_fh || real_feof((FILE*)_fh);
}
bool VFSFileReal::close(void)
void DiskFile::close()
{
VFS_GUARD_OPT(this);
if(_fh)
{
real_fclose((FILE*)_fh);
_fh = NULL;
}
return true;
}
bool VFSFileReal::seek(vfspos pos)
bool DiskFile::seek(vfspos pos, int whence)
{
VFS_GUARD_OPT(this);
if(!_fh)
return false;
return real_fseek((FILE*)_fh, pos, SEEK_SET) == 0;
return _fh && real_fseek((FILE*)_fh, pos, whence) == 0;
}
bool VFSFileReal::seekRel(vfspos offs)
bool DiskFile::flush()
{
VFS_GUARD_OPT(this);
if(!_fh)
return false;
return real_fseek((FILE*)_fh, offs, SEEK_CUR) == 0;
return _fh && real_fflush((FILE*)_fh) == 0;
}
bool VFSFileReal::flush(void)
vfspos DiskFile::getpos() const
{
VFS_GUARD_OPT(this);
if(!_fh)
return false;
return real_fflush((FILE*)_fh) == 0;
return _fh ? real_ftell((FILE*)_fh) : npos;
}
vfspos VFSFileReal::getpos(void) const
unsigned int DiskFile::read(void *dst, size_t bytes)
{
VFS_GUARD_OPT(this);
if(!_fh)
return npos;
return real_ftell((FILE*)_fh);
return _fh ? real_fread(dst, 1, bytes, (FILE*)_fh) : 0;
}
unsigned int VFSFileReal::read(void *dst, unsigned int bytes)
unsigned int DiskFile::write(const void *src, size_t bytes)
{
VFS_GUARD_OPT(this);
if(!_fh)
return npos;
return real_fread(dst, 1, bytes, (FILE*)_fh);
return _fh ? real_fwrite(src, 1, bytes, (FILE*)_fh) : 0;
}
unsigned int VFSFileReal::write(const void *src, unsigned int bytes)
vfspos DiskFile::size()
{
VFS_GUARD_OPT(this);
if(!_fh)
return npos;
return real_fwrite(src, 1, bytes, (FILE*)_fh);
vfspos sz = 0;
bool ok = GetFileSize(fullname(), sz);
return ok ? sz : npos;
}
vfspos VFSFileReal::size(void)
// ------------- MemFile -----------------------
MemFile::MemFile(const char *name, void *buf, unsigned int size, delete_func delfunc /* = NULL */, DeleteMode delmode /* = ON_CLOSE */)
: File(name), _pos(0), _size(size), _buf(buf), _delfunc(delfunc), _delmode(delmode)
{
return GetFileSize(fullname());
}
// ------------- VFSFileMem -----------------------
VFSFileMem::VFSFileMem(const char *name, void *buf, unsigned int size, Mode mode /* = COPY */,
allocator_func alloc /* = NULL */, delete_func delfunc /* = NULL */)
: VFSFile(name), _pos(0), _size(size), _mybuf(mode == TAKE_OVER || mode == COPY)
MemFile::~MemFile()
{
if(mode == COPY)
if(_delmode == ON_DESTROY)
_clearMem();
}
void MemFile::_clearMem()
{
if(_delfunc)
_delfunc(_buf);
_delfunc = NULL;
_buf = NULL;
_size = 0;
_pos = 0;
}
void MemFile::close()
{
if(_delmode == ON_CLOSE)
_clearMem();
}
bool MemFile::seek(vfspos pos, int whence)
{
switch(whence)
{
assert(!alloc == !delfunc);
_buf = alloc ? alloc(size+1) : (void*)(new char[size+1]);
memcpy(_buf, buf, size);
((char*)_buf)[size] = 0;
case SEEK_SET:
if(pos < _size)
{
_pos = pos;
return true;
}
break;
case SEEK_CUR:
if(_pos + pos < _size)
{
_pos += pos;
return true;
}
break;
case SEEK_END:
if(pos < _size)
{
_pos = _size - pos;
return true;
}
}
else
{
_buf = buf;
}
_delfunc = delfunc;
return false;
}
VFSFileMem::~VFSFileMem()
unsigned int MemFile::read(void *dst, unsigned int bytes)
{
if(_mybuf)
VFSFile::dropBuf(true);
}
unsigned int VFSFileMem::read(void *dst, unsigned int bytes)
{
VFS_GUARD_OPT(this);
if(iseof())
return 0;
unsigned int rem = std::min<unsigned int>((unsigned int)(_size - _pos), bytes);
@ -211,9 +160,8 @@ unsigned int VFSFileMem::read(void *dst, unsigned int bytes)
return rem;
}
unsigned int VFSFileMem::write(const void *src, unsigned int bytes)
unsigned int MemFile::write(const void *src, unsigned int bytes)
{
VFS_GUARD_OPT(this);
if(iseof())
return 0;
unsigned int rem = std::min<unsigned int>((unsigned int)(_size - _pos), bytes);

View file

@ -5,12 +5,11 @@
#define VFSFILE_H
#include "VFSBase.h"
#include <string>
VFS_NAMESPACE_START
/** -- VFSFile basic interface --
/** -- File basic interface --
* All functions that return bool should return true on success and false on failure.
* If an operation is not necessary or irrelevant (for example, files in memory can't be closed),
* it is useful to return true anyways, because this operation did not fail, technically.
@ -19,98 +18,60 @@ VFS_NAMESPACE_START
* Only the functions required or applicable need to be implemented, for unsupported operations
* the default implementation should be sufficient.
**/
class VFSFile : public VFSBase
class File : public VFSBase
{
public:
/** The ctor is expected to set both name() and fullname();
The name must remain static throughout the object's lifetime. */
VFSFile(const char *fn);
virtual ~VFSFile();
virtual ~File();
/** Open a file.
Mode can be "r", "w", "rb", "rb", and possibly other things that fopen supports.
It is the subclass's choice to support other modes. Default is "rb".
If the file still has a buffer (as returned by getBuf()), it is deleted.
Closes and reopens if already open (even in the same mode). */
virtual bool open(const char *mode = NULL) { return false; }
virtual bool open(const char *mode = NULL) = 0;
virtual bool isopen(void) const { return false; }
virtual bool iseof(void) const { return true; }
virtual bool close(void) { return true; }
virtual bool seek(vfspos pos) { return false; }
virtual bool isopen() const = 0;
virtual bool iseof() const = 0;
virtual void close() = 0;
virtual bool seek(vfspos pos, int whence) = 0;
/** Seek relative to current position. Negative numbers will seek backwards.
(In most cases, the default implementation does not have to be changed) */
virtual bool seekRel(vfspos offs) { VFS_GUARD_OPT(this); return seek(getpos() + offs); }
virtual bool flush(void) { return true; }
virtual bool flush() = 0;
/** Current offset in file. Return npos if NA. */
virtual vfspos getpos(void) const { return npos; }
virtual vfspos getpos() const = 0;
virtual unsigned int read(void *dst, unsigned int bytes) { return 0; }
virtual unsigned int write(const void *src, unsigned int bytes) { return 0; }
virtual size_t read(void *dst, size_t bytes) = 0;
virtual size_t write(const void *src, size_t bytes) = 0;
/** Return file size. If NA, return npos. If size is not yet known,
open() and close() may be called (with default args) to find out the size.
The file is supposed to be in its old state when the function returns,
that is in the same open state and seek position.
The pointer returned by getBuf() must not change. */
virtual vfspos size(void) { return npos; }
/** Return full file content in memory. Like size(), this may do other operations on the file,
but after the function returns the file is expected to be in the same state it was before.
If the file is not open before the call, it will be opened with default parameters (that is, "rb").
Additional EOL mangling my happen if the file is opened in text mode before (= not binary).
Calls to open() should delete this memory if the file was previously opened in a different mode.
In the default implementation, the returned memory is not guaranteed to be writable without problems,
so don't do it. Don't cast the const away. You have been warned.
Supply your own allocator and deletor functions if required.
NULL means new[] and delete[], respectively.
Either both must be a valid function, or both NULL. Only one of them NULL will cause assertion fail. */
virtual const void *getBuf(allocator_func alloc = NULL, delete_func del = NULL);
/** If del is true, delete internal buffer. If false, unregister internal buffer from the file,
but do not delete. Use delete[] or an appropriate deletion function later. */
virtual void dropBuf(bool del);
/** Basic RTTI, for debugging purposes */
virtual const char *getType(void) const { return "virtual"; }
// ---- non-virtual part ----
/** Uses the deletion function earlier given to getBuf() to free the given memory,
or delete [] if the function is NULL. Useful if the original function
that was used to allocate the buffer is no longer known. */
void delBuf(void *mem);
that is in the same open state and seek position. */
virtual vfspos size() = 0;
protected:
void *_buf;
delete_func _delfunc;
/** The ctor is expected to set both name() and fullname();
The name must remain static throughout the object's lifetime. */
File(const char *fn);
};
class VFSFileReal : public VFSFile
class DiskFile : public File
{
public:
VFSFileReal(const char *name);
virtual ~VFSFileReal();
DiskFile(const char *name);
virtual ~DiskFile();
virtual bool open(const char *mode = NULL);
virtual bool isopen(void) const;
virtual bool iseof(void) const;
virtual bool close(void);
virtual bool seek(vfspos pos);
virtual bool seekRel(vfspos offs);
virtual bool flush(void);
virtual vfspos getpos(void) const;
virtual unsigned int read(void *dst, unsigned int bytes);
virtual unsigned int write(const void *src, unsigned int bytes);
virtual vfspos size(void);
virtual const char *getType(void) const { return "disk"; }
virtual bool isopen() const;
virtual bool iseof() const;
virtual void close();
virtual bool seek(vfspos pos, int whence);
virtual bool flush();
virtual vfspos getpos() const;
virtual size_t read(void *dst, size_t bytes);
virtual size_t write(const void *src, size_t bytes);
virtual vfspos size();
virtual const char *getType() const { return "DiskFile"; }
inline void *getFP() { return _fh; }
@ -120,43 +81,41 @@ protected:
void *_buf;
};
class VFSFileMem : public VFSFile
class MemFile : public File
{
public:
enum Mode
enum DeleteMode
{
COPY, //- Make a copy of the buffer (default action).
REUSE, //- Use the passed-in buffer as is. Requires the pointer
// to remain valid over the life of this object.
TAKE_OVER, //- Take over the passed-in buffer; it will be deleted on object destruction.
ON_CLOSE,
ON_DESTROY
};
/* Creates a virtual file from a memory buffer. By default, the memory is copied.
A deletor function can be passed optionally, if its NULL (the default),
delete[] (char*)buf will be used. For malloc()'d memory, pass free. (Only used if mode is TAKE_OVER) */
VFSFileMem(const char *name, void *buf, unsigned int size, Mode m = COPY,
allocator_func alloc = NULL, delete_func delfunc = NULL);
virtual ~VFSFileMem();
/* Creates a virtual file from a memory buffer. The buffer is passed as-is,
so for text files you should make sure it ends with a \0 character.
A deletor function can be passed optionally, that the buffer will be passed to
when the memory file is destroyed. Pass NULL or leave away to keep the buffer alive. */
MemFile(const char *name, void *buf, unsigned int size, delete_func delfunc = NULL, DeleteMode delmode = ON_CLOSE);
virtual ~MemFile();
virtual bool open(const char *mode = NULL) { return true; }
virtual bool isopen(void) const { return true; } // always open
virtual bool iseof(void) const { VFS_GUARD_OPT(this); return _pos >= _size; }
virtual bool close(void) { return true; } // cant close, but not a problem
virtual bool seek(vfspos pos) { VFS_GUARD_OPT(this); _pos = pos; return true; }
virtual bool seekRel(vfspos offs) { VFS_GUARD_OPT(this); _pos += offs; return true; }
virtual bool flush(void) { return false; } // can't flush, if a successful file write is expected, this IS a problem.
virtual vfspos getpos(void) const { VFS_GUARD_OPT(this); return _pos; }
virtual unsigned int read(void *dst, unsigned int bytes);
virtual unsigned int write(const void *src, unsigned int bytes);
virtual vfspos size(void) { VFS_GUARD_OPT(this); return _size; }
virtual const void *getBuf(allocator_func alloc = NULL, delete_func del = NULL) { VFS_GUARD_OPT(this); return _buf; }
virtual void dropBuf(bool) {} // we can't simply drop the internal buffer, as the file is entirely memory based
virtual const char *getType(void) const { return "mem"; }
virtual bool isopen() const { return !!_buf; } // always open
virtual bool iseof() const { return _pos >= _size; }
virtual void close();
virtual bool seek(vfspos pos, int whence);
virtual bool flush() { return true; }
virtual vfspos getpos() const { return _pos; }
virtual size_t read(void *dst, size_t bytes);
virtual size_t write(const void *src, size_t bytes);
virtual vfspos size() { return _size; }
virtual const char *getType() const { return "MemFile"; }
protected:
void _clearMem();
void *_buf;
vfspos _pos;
vfspos _size;
bool _mybuf;
delete_func _delfunc;
DeleteMode _delmode;
};
VFS_NAMESPACE_END

View file

@ -1,4 +1,8 @@
#include "VFSFileFuncs.h"
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "VFSDefines.h"
// this is for POSIX - define before including any stdio headers
#ifdef VFS_LARGEFILE_SUPPORT
@ -7,6 +11,7 @@
# endif
#endif
#include "VFSFileFuncs.h"
#include "VFSInternal.h"
#include <cstdio>

View file

@ -1,3 +1,6 @@
#ifndef VFS_FILE_FUNCS_H
#define VFS_FILE_FUNCS_H
#include "VFSDefines.h"
@ -13,3 +16,5 @@ int real_feof(void *fh);
int real_fflush(void *fh);
VFS_NAMESPACE_END
#endif

View file

@ -1,394 +0,0 @@
// VFSHashmap.h - minimalist but fast STL compatible hashmap implementation.
// For conditions of distribution and use, see copyright notice in VFS.h
#ifndef VFS_HASHMAP_H
#define VFS_HASHMAP_H
#include <vector>
#include "VFSDefines.h"
VFS_NAMESPACE_START
template<typename KEY> struct hash_cast
{
inline size_t operator()(const KEY& t) const
{
return (size_t)t;
}
inline size_t operator()(const KEY *t) const
{
return (size_t)*t;
}
};
template<typename KEY, typename T> struct equal
{
inline bool operator()(const KEY& a, const KEY& b, size_t /*hash*/, const T& /*value*/) const
{
return a == b;
}
};
// (http://graphics.stanford.edu/~seander/bithacks.html)
inline size_t nextPowerOf2(size_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
v += (v == 0);
return v;
}
template
<
typename KEY,
typename T,
typename HASH,
typename CMP,
typename BucketType
>
class _HashMapBase
{
// ---- supplemental stuff ----
public:
typedef T value_type;
typedef std::size_t size_type;
typedef typename BucketType::iterator ITR;
class iterator : public std::iterator<std::bidirectional_iterator_tag, typename ITR::value_type>
{
public:
typedef typename ITR::value_type value_type;
typedef typename ITR::reference reference;
typedef typename ITR::pointer pointer;
iterator()
: _bucket(-1), _hm(NULL)
{
}
iterator(size_t bucket, const ITR& it, _HashMapBase *hm)
: _bucket(bucket), _it(it), _hm(hm)
{
_nextbucket();
}
iterator(const iterator& it)
: _bucket(it._bucket), _it(it._it), _hm(it._hm)
{
_nextbucket();
}
iterator& operator=(const iterator& o)
{
if(this == &o)
return *this;
_bucket = o._bucket;
_it = o._it;
_hm = o._hm;
return *this;
}
void _nextbucket()
{
while(_bucket+1 < _hm->v.size() && _it == _hm->v[_bucket].end())
_it = _hm->v[++_bucket].begin();
}
void _prevbucket()
{
while(_bucket > 0 && _it == _hm->v[_bucket].begin())
_it = _hm->v[--_bucket].back();
}
iterator& operator++(void) // pre-increment
{
++_it;
_nextbucket();
return *this;
}
iterator operator++(int) // post-increment
{
iterator it = *this;
++*this;
return it;
}
iterator& operator--(void) // pre-decrement
{
--_it;
_prevbucket();
return *this;
}
iterator operator--(int) // post-decrement
{
iterator it = *this;
--*this;
return it;
}
bool operator==(const iterator& o) const
{
return _hm == o._hm && _bucket == o._bucket && _it == o._it;
}
bool operator!=(const iterator& o) const
{
return !(*this == o);
}
bool operator<(const iterator& o) const
{
return _bucket < o._bucket || (_bucket == o._bucket && _it < o._it);
}
bool operator<=(const iterator& o) const
{
return !(*this > o);
}
bool operator>(const iterator& o) const
{
return _bucket > o._bucket || (_bucket == o._bucket && _it > o._it);
}
bool operator>=(const iterator& o) const
{
return !(*this < o);
}
reference operator*()
{
return _it.operator*();
}
pointer operator->()
{
return _it.operator->();
}
size_t _bucket;
ITR _it;
_HashMapBase *_hm;
};
protected:
// ---- Main class start ----
_HashMapBase(size_t buckets, int loadFactor, const CMP& c, const HASH& h)
: v(nextPowerOf2(buckets)), _size(0), _loadFactor(loadFactor), cmp(c), hash(h)
{}
_HashMapBase(const _HashMapBase& o)
: v(o.v), _size(o._size), _loadFactor(o._loadFactor), cmp(o.cmp), hash(o.hash)
{}
public:
_HashMapBase& operator=(const _HashMapBase& o)
{
if(this == &o)
return *this;
v = o.v;
cmp = o.cmp;
hash = o.hash;
_size = o._size;
_loadFactor = o._loadFactor;
}
inline iterator begin()
{
return _MakeIter(0, v[0].begin());
}
inline iterator end()
{
return _MakeIter(v.size()-1, v[v.size()-1].end());
}
iterator find(const KEY& k)
{
size_t h = hash(k);
size_t i = h & (v.size()-1); // assume power of 2
BucketType& b = v[i];
for(typename BucketType::iterator it = b.begin(); it != b.end(); ++it)
if(cmp(k, it->first, h, it->second))
return _MakeIter(i, it);
return end();
}
void erase(const KEY& k)
{
size_t h = hash(k);
BucketType& b = v[h & (v.size()-1)]; // assume power of 2
for(typename BucketType::iterator it = b.begin(); it != b.end(); ++it)
{
if(cmp(k, it->first, h, it->second))
{
b.erase(it);
--_size;
return;
}
}
}
inline iterator erase(const iterator& it)
{
--_size;
return _MakeIter(it._bucket, v[it._bucket].erase(it._it));
}
T& operator[] (const KEY& k)
{
size_t h = hash(k);
size_t i = h & (v.size()-1); // assume power of 2
{
BucketType& b = v[i];
for(typename BucketType::iterator it = b.begin(); it != b.end(); ++it)
if(cmp(k, it->first, h, it->second))
return it->second;
}
++_size;
if(_enlargeIfNecessary())
i = h & (v.size()-1);
v[i].push_back(std::make_pair(k, T()));
return v[i].back().second;
}
inline size_t size() const
{
return _size;
}
/* "Because map containers do not allow for duplicate key values, the insertion operation
checks for each element inserted whether another element exists already in the container
with the same key value, if so, the element is not inserted and its mapped value
is not changed in any way." */ // Oh well.
/*void insert(std::pair<KEY, T>& p)
{
size_t h = hash(p.first);
size_t i = h & (v.size()-1);
{
BucketType& b = v[i]; // assume power of 2
for(typename BucketType::iterator it = b.begin(); it != b.end(); ++it)
if(cmp(p.first, it->first, h, it->second))
return _MakeIter(i, it);
}
++_size;
if(_enlargeIfNecessary())
i = h & (v.size()-1);
v[i].push_back(std::make_pair(k, T()));
return _MakeIter(i, b.end()-1);
}*/ // -- not used in ttvfs currently
private:
inline iterator _MakeIter(size_t bucket, const typename BucketType::iterator& it)
{
return iterator(bucket, it, this);
}
inline bool _enlargeIfNecessary()
{
if(_loadFactor < 0)
return false;
if(_size > v.size() * _loadFactor)
{
_enlarge();
return true;
}
return false;
}
void _enlarge()
{
size_t oldsize = v.size();
v.resize(oldsize * 2);
BucketType cp;
for(size_t i = 0; i < oldsize; ++i)
{
cp.clear();
std::swap(cp, v[i]); // use efficient swap
// v[i] is now empty
// this way can possibly copy elements 2 times, but means less container copying overall
for(typename BucketType::iterator it = cp.begin(); it != cp.end(); ++it)
v[hash(it->first) & (v.size()-1)].push_back(*it); // assume power of 2
}
}
inline void swap(_HashMapBase& hm)
{
if(this == &hm)
return;
std::swap(v, hm.v);
std::swap(_size, hm._size);
std::swap(_loadFactor, hm._loadFactor);
std::swap(hash, hm.hash);
std::swap(cmp, hm.cmp);
}
std::vector<BucketType> v;
size_t _size;
int _loadFactor;
HASH hash; // hash functor
CMP cmp; // compare functor
};
template
<
typename KEY,
typename T,
typename HASH = hash_cast<KEY>,
typename CMP = equal<KEY, T>,
typename BucketType = std::vector<std::pair<KEY,T> >
>
class HashMap : public _HashMapBase<KEY, T, HASH, CMP, BucketType>
{
public:
HashMap(size_t buckets = 16, int loadFactor = 5)
: _HashMapBase<KEY, T, HASH, CMP, BucketType>(buckets, loadFactor, CMP(), HASH())
{
}
};
VFS_NAMESPACE_END
namespace std
{
template
<
typename KEY,
typename T,
typename HASH,
typename CMP,
typename BucketType
>
inline static void swap(VFS_NAMESPACE_IMPL HashMap<KEY, T, HASH, CMP, BucketType>& a,
VFS_NAMESPACE_IMPL HashMap<KEY, T, HASH, CMP, BucketType>& b)
{
a.swap(b);
}
};
#endif

View file

@ -1,483 +0,0 @@
// VFSHelper.cpp - glues it all together and makes use simple
// For conditions of distribution and use, see copyright notice in VFS.h
#include <iostream> // for debug only, see EOF
#include "VFSInternal.h"
#include "VFSHelper.h"
#include "VFSAtomic.h"
#include "VFSTools.h"
#include "VFSDir.h"
#include "VFSFile.h"
#include "VFSLoader.h"
#include "VFSArchiveLoader.h"
#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(bool large, bool nocase, bool hashmap, unsigned int vfspos_size)
{
#ifdef VFS_LARGEFILE_SUPPORT
bool largefile_i = true;
#else
bool largefile_i = false;
#endif
#ifdef VFS_IGNORE_CASE
bool nocase_i = true;
#else
bool nocase_i = false;
#endif
#ifdef VFS_USE_HASHMAP
bool hashmap_i = true;
#else
bool hashmap_i = false;
#endif
return (large == largefile_i)
&& (nocase == nocase_i)
&& (hashmap == hashmap_i)
&& (sizeof(vfspos) == vfspos_size);
}
VFSHelper::VFSHelper()
: filesysRoot(NULL), merged(NULL)
{
}
VFSHelper::~VFSHelper()
{
Clear();
}
void VFSHelper::Clear(void)
{
VFS_GUARD_OPT(this);
_cleanup();
if(filesysRoot)
{
filesysRoot->ref--; // this should always delete it...
filesysRoot = NULL; // ...but it may be referenced elsewhere, just in case
}
_ClearMountPoints();
for(LoaderArray::iterator it = loaders.begin(); it != loaders.end(); ++it)
delete *it;
loaders.clear();
for(DirArray::iterator it = trees.begin(); it != trees.end(); ++it)
it->dir->ref--;
trees.clear();
for(ArchiveLoaderArray::iterator it = archLdrs.begin(); it != archLdrs.end(); ++it)
delete *it;
archLdrs.clear();
}
void VFSHelper::_ClearMountPoints(void)
{
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it)
it->vdir->ref--;
vlist.clear();
}
void VFSHelper::_cleanup(void)
{
VFS_GUARD_OPT(this); // be extra safe and ensure this is locked
if(merged)
{
merged->ref--;
merged = NULL;
}
}
bool VFSHelper::LoadFileSysRoot(bool recursive)
{
VFS_GUARD_OPT(this);
if(filesysRoot)
return !!filesysRoot->load(recursive);
filesysRoot = new VFSDirReal(".");
if(!filesysRoot->load(recursive))
{
filesysRoot->ref--;
filesysRoot = NULL;
return false;
}
loaders.push_back(new VFSLoaderDisk);
BaseTreeEntry bt;
bt.source = "";
bt.dir = filesysRoot;
trees.push_back(bt);
filesysRoot->ref++;
AddVFSDir(filesysRoot, "");
return true;
}
// TODO: deprecate this
void VFSHelper::Prepare(bool clear /* = true */)
{
VFS_GUARD_OPT(this);
Reload(false, clear, false); // HACK
//for(DirArray::iterator it = trees.begin(); it != trees.end(); ++it)
// merged->getDir((*it)->fullname(), true)->merge(*it);
}
void VFSHelper::Reload(bool fromDisk /* = false */, bool clear /* = false */, bool clearMountPoints /* = false */)
{
VFS_GUARD_OPT(this);
if(clearMountPoints)
_ClearMountPoints();
if(fromDisk && filesysRoot)
LoadFileSysRoot(true);
if(clear)
_cleanup();
if(!merged && trees.size())
{
for(DirArray::iterator it = trees.begin(); it != trees.end(); ++it)
it->dir->clearMounted();
// FIXME: not sure if really correct
merged = trees[0].dir;
merged->ref++;
// FIXME: this is too hogging
//merged->load(true);
}
if(merged)
{
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it)
{
//printf("VFS: mount {%s} [%s] -> [%s] (overwrite: %d)\n", it->vdir->getType(), it->vdir->fullname(), it->mountPoint.c_str(), it->overwrite);
GetDir(it->mountPoint.c_str(), true)->merge(it->vdir, it->overwrite, VFSDir::MOUNTED);
}
}
}
bool VFSHelper::Mount(const char *src, const char *dest, bool overwrite /* = true*/)
{
VFS_GUARD_OPT(this);
return AddVFSDir(GetDir(src, false), dest, overwrite);
}
bool VFSHelper::AddVFSDir(VFSDir *dir, const char *subdir /* = NULL */, bool overwrite /* = true */)
{
if(!dir)
return false;
VFS_GUARD_OPT(this);
if(!subdir)
subdir = dir->fullname();
VDirEntry ve(dir, subdir, overwrite);
_StoreMountPoint(ve);
VFSDir *sd = GetDir(subdir, true);
if(!sd) // may be NULL if Prepare() was not called before
return false;
sd->merge(dir, overwrite, VFSDir::MOUNTED); // merge into specified subdir. will be (virtually) created if not existing
return true;
}
bool VFSHelper::Unmount(const char *src, const char *dest)
{
VFSDir *vd = GetDir(src, false);
if(!vd)
return false;
VDirEntry ve(vd, dest, true); // last is dummy
if(!_RemoveMountPoint(ve))
return false;
// FIXME: this could be done more efficiently by just reloading parts of the tree that were involved.
Reload(false, true, false);
//vd->load(true);
//vd->load(false);
/*VFSDir *dstdir = GetDir(dest, false);
if(dstdir)
dstdir->load(false);*/
return true;
}
void VFSHelper::_StoreMountPoint(const VDirEntry& ve)
{
// increase ref already before it will be added
ve.vdir->ref++;
// scan through and ensure only one mount point with the same data is present.
// if present, remove and re-add, this ensures the mount point is at the end of the list
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); )
{
const VDirEntry& oe = *it;
if (ve.mountPoint == oe.mountPoint
&& (ve.vdir == oe.vdir || !casecmp(ve.vdir->fullname(), oe.vdir->fullname()))
&& (ve.overwrite || !oe.overwrite) ) // overwrite definitely, or if other does not overwrite
{
it->vdir->ref--;
vlist.erase(it++); // do not break; just in case there are more (fixme?)
}
else
++it;
}
vlist.push_back(ve);
}
bool VFSHelper::_RemoveMountPoint(const VDirEntry& ve)
{
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it)
{
const VDirEntry& oe = *it;
if(ve.mountPoint == oe.mountPoint
&& (ve.vdir == oe.vdir || !casecmp(ve.vdir->fullname(), oe.vdir->fullname())) )
{
it->vdir->ref--;
vlist.erase(it);
return true;
}
}
return false;
}
bool VFSHelper::MountExternalPath(const char *path, const char *where /* = "" */, bool loadRec /* = false */, bool overwrite /* = true */)
{
VFS_GUARD_OPT(this);
VFSDirReal *vfs = new VFSDirReal(path);
if(vfs->load(loadRec))
AddVFSDir(vfs, where, overwrite);
return !!--(vfs->ref); // 0 if deleted
}
void VFSHelper::AddLoader(VFSLoader *ldr)
{
VFS_GUARD_OPT(this);
loaders.push_back(ldr);
}
void VFSHelper::AddArchiveLoader(VFSArchiveLoader *ldr)
{
VFS_GUARD_OPT(this);
archLdrs.push_back(ldr);
}
VFSDir *VFSHelper::AddArchive(const char *arch, bool asSubdir /* = true */, const char *subdir /* = NULL */, void *opaque /* = NULL */)
{
VFSFile *af = GetFile(arch);
if(!af)
return NULL;
VFSDir *ad = NULL;
VFSLoader *fileLdr = NULL;
for(ArchiveLoaderArray::iterator it = archLdrs.begin(); it != archLdrs.end(); ++it)
if((ad = (*it)->Load(af, &fileLdr, opaque)))
break;
if(!ad)
return NULL;
if(fileLdr)
loaders.push_back(fileLdr);
BaseTreeEntry bt;
bt.source = arch;
bt.dir = ad;
trees.push_back(bt);
AddVFSDir(ad, subdir, true);
return ad;
}
inline static VFSFile *VFSHelper_GetFileByLoader(VFSLoader *ldr, const char *fn, const char *unmangled, VFSDir *root)
{
if(!ldr)
return NULL;
VFSFile *vf = ldr->Load(fn, unmangled);
if(vf)
{
VFS_GUARD_OPT(vf);
root->addRecursive(vf, true, VFSDir::NONE);
--(vf->ref);
}
return vf;
}
VFSFile *VFSHelper::GetFile(const char *fn)
{
const char *unmangled = fn;
std::string fixed = FixPath(fn);
fn = fixed.c_str();
VFSFile *vf = NULL;
VFS_GUARD_OPT(this);
if(!merged) // Prepare() called?
return NULL;
vf = merged->getFile(fn);
// nothing found? maybe a loader has something.
// if so, add the newly created VFSFile to the tree.
if(!vf)
for(LoaderArray::iterator it = loaders.begin(); it != loaders.end(); ++it)
if((vf = VFSHelper_GetFileByLoader(*it, fn, unmangled, GetDirRoot())))
break;
//printf("VFS: GetFile '%s' -> '%s' (%s:%p)\n", fn, vf ? vf->fullname() : "NULL", vf ? vf->getType() : "?", vf);
return vf;
}
inline static VFSDir *VFSHelper_GetDirByLoader(VFSLoader *ldr, const char *fn, const char *unmangled, VFSDir *root)
{
if(!ldr)
return NULL;
VFSDir *vd = ldr->LoadDir(fn, unmangled);
if(vd)
{
std::string parentname = StripLastPath(fn);
VFS_GUARD_OPT(this);
VFSDir *parent = parentname.empty() ? root : root->getDir(parentname.c_str(), true);
parent->insert(vd, true, VFSDir::NONE);
--(vd->ref); // should delete it
vd = root->getDir(fn); // can't return vd directly because it is cloned on insert+merge, and already deleted here
}
return vd;
}
VFSDir *VFSHelper::GetDir(const char* dn, bool create /* = false */)
{
const char *unmangled = dn;
std::string fixed = FixPath(dn);
dn = fixed.c_str();
VFS_GUARD_OPT(this);
if(!merged)
return NULL;
if(!*dn)
return merged;
VFSDir *vd = merged->getDir(dn);
if(!vd && create)
{
if(!vd)
for(LoaderArray::iterator it = loaders.begin(); it != loaders.end(); ++it)
if((vd = VFSHelper_GetDirByLoader(*it, dn, unmangled, GetDirRoot())))
break;
if(!vd)
vd = merged->getDir(dn, true);
}
//printf("VFS: GetDir '%s' -> '%s' (%s:%p)\n", dn, vd ? vd->fullname() : "NULL", vd ? vd->getType() : "?", vd);
return vd;
}
VFSDir *VFSHelper::GetDirRoot(void)
{
VFS_GUARD_OPT(this);
return merged;
}
VFSDir *VFSHelper::GetBaseTree(const char *path)
{
for(DirArray::iterator it = trees.begin(); it != trees.end(); ++it)
if(!casecmp(it->source.c_str(), path))
return it->dir;
return NULL;
}
VFSDir *VFSHelper::GetMountPoint(const char *path)
{
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it)
if(!casecmp(it->mountPoint.c_str(), path))
return it->vdir;
return NULL;
}
void VFSHelper::ClearGarbage(void)
{
for(DirArray::iterator it = trees.begin(); it != trees.end(); ++it)
it->dir->clearGarbage();
}
// DEBUG STUFF
struct _DbgParams
{
_DbgParams(std::ostream& os_, VFSDir *parent_, const std::string& sp_)
: os(os_), parent(parent_), sp(sp_) {}
std::ostream& os;
VFSDir *parent;
const std::string& sp;
};
static void _DumpFile(VFSFile *vf, void *user)
{
_DbgParams& p = *((_DbgParams*)user);
p.os << p.sp << "f|" << vf->name() << " [" << vf->getType() << ", ref " << vf->ref.count() << ", 0x" << vf << "]";
if(strncmp(p.parent->fullname(), vf->fullname(), p.parent->fullnameLen()))
p.os << " <-- {" << vf->fullname() << "} ***********";
p.os << std::endl;
}
static void _DumpTreeRecursive(VFSDir *vd, void *user)
{
_DbgParams& p = *((_DbgParams*)user);
std::string sub = p.sp + " ";
p.os << p.sp << "d|" << vd->name() << " [" << vd->getType() << ", ref " << vd->ref.count() << ", 0x" << vd << "]";
if(p.parent && strncmp(p.parent->fullname(), vd->fullname(), strlen(p.parent->fullname())))
p.os << " <-- {" << vd->fullname() << "} ***********";
p.os << std::endl;
_DbgParams recP(p.os, vd, sub);
vd->forEachDir(_DumpTreeRecursive, &recP);
vd->forEachFile(_DumpFile, &recP);
}
void VFSHelper::debugDumpTree(std::ostream& os, VFSDir *start /* = NULL */)
{
_DbgParams recP(os, NULL, "");
VFSDir *d = start ? start : GetDirRoot();
_DumpTreeRecursive(d, &recP);
}
VFS_NAMESPACE_END

View file

@ -1,191 +0,0 @@
// VFSHelper.h - glues it all together and makes use simple
// For conditions of distribution and use, see copyright notice in VFS.h
#ifndef VFSHELPER_H
#define VFSHELPER_H
#include <vector>
#include <list>
#include <string>
#include <iostream>
#include "VFSAtomic.h"
VFS_NAMESPACE_START
class VFSDir;
class VFSDirReal;
class VFSFile;
class VFSLoader;
class VFSArchiveLoader;
/** VFSHelper - extensible class to simplify working with the VFS tree */
class VFSHelper
{
public:
VFSHelper();
virtual ~VFSHelper();
/** Creates the working tree. Required before any files or directories can be accessed.
Internally, it merges all individual VFS trees into one. If clear is true (default),
an existing merged tree is dropped, and old and previously added files removed.
(This is the recommended setting.)
Mount points and loaders are kept.*/
virtual void Prepare(bool clear = true);
/** Re-merges any files in the tree, and optionally reloads files on disk.
This is useful if files on disk were created or removed, and the tree needs to reflect these changes.
Calls Prepare(clear) internally. */
virtual void Reload(bool fromDisk = false, bool clear = false, bool clearMountPoints = false);
/** Reset an instance to its initial state.
Drops all archives, loaders, archive loaders, mount points, internal trees, ...*/
virtual void Clear(void);
/** Do cleanups from time to time. In base VFSHelper, this is a no-op.
Extensions may wish to override this method do do cleanup jobs. */
virtual void ClearGarbage(void);
/** Load all files from working directory (into an internal tree) */
bool LoadFileSysRoot(bool recursive);
/** Mount a directory in the tree to a different location. Requires a previous call to Prepare().
This can be imagined like a symlink pointing to a different location.
Be careful not to create circles, this might technically work,
but confuses the reference counting, causing memory leaks. */
bool Mount(const char *src, const char *dest, bool overwrite = true);
/** Drops a directory from the tree. Internally, this calls Reload(false),
which is a heavy operation compared to Mount(). Be warned. */
bool Unmount(const char *src, const char *dest);
/** Merges a path into the tree. Requires a previous call to Prepare().
By default the directory is added into the root directory of the merged tree.
Pass NULL to add the directory to its original location,
or any other path to add it to that explicit location.
If loadRec is true, load all subdirs recursively.
It is advised not to use this to re-add parts already in the tree; use Mount() instead.
Rule of thumb: If you called LoadFileSysRoot(), do not use this for subdirs.
Note: Directories mounted with this will return `where` as their full path if it was set.
Use GetMountPoint() to retrieve the underlying VFSDir object. */
bool MountExternalPath(const char *path, const char *where = "", bool loadRec = false, bool overwrite = true);
/** Adds a VFSDir object into the merged tree. If subdir is NULL (the default),
add into the subdir stored in the VFSDir object. The tree will be extended if target dir does not exist.
If overwrite is true (the default), files in the tree will be replaced if already existing.
Requires a previous call to Prepare().
Like with Mount(); be careful not to create cycles. */
bool AddVFSDir(VFSDir *dir, const char *subdir = NULL, bool overwrite = true);
/** Add the contents of an archive file to the tree. By default, the archive can be addressed
like a folder, e.g. "path/to/example.zip/file.txt".
Set asSubdir to false to "unpack" the contents of the archive to the containing folder.
Optionally, the target subdir to mount into can be specified. (See AddVFSDir().)
Returns a pointer to the actual VFSDir object that represents the added archive, or NULL if failed.
The opaque pointer is passed directly to each loader and can contain additional parameters,
such as a password to open the file.
Read the comments in VFSArchiveLoader.h for an explanation how it works. If you have no idea, leave it NULL,
because it can easily cause a crash if not used carefully. */
VFSDir *AddArchive(const char *arch, bool asSubdir = true, const char *subdir = NULL, void *opaque = NULL);
/** Add a loader that can look for files on demand.
It is possible (but not a good idea) to add a loader multiple times. */
void AddLoader(VFSLoader *ldr);
/** Add an archive loader that can open archives of various types.
Whenever an archive file is requested to be opened by AddArchive(),
it is sent through each registered loader until one of them can recognize
the format and open it. An archive loader stays once registered. */
void AddArchiveLoader(VFSArchiveLoader *ldr);
/** Get a file from the merged tree. Requires a previous call to Prepare().
Asks loaders if the file is not in the tree. If found by a loader, the file will be added to the tree.
The returned pointer is reference counted. In case the file pointer is stored elsewhere,
do ptr->ref++, and later ptr->ref--. This is to prevent the VFS tree from deleting the file when cleaning up.
Not necessary if the pointer is just retrieved and used, or temp. stored while the VFS tree is not modified. */
VFSFile *GetFile(const char *fn);
/** Get a directory from the merged tree. If create is true and the directory does not exist,
build the tree structure and return the newly created dir. NULL otherwise.
Requires a previous call to Prepare().
Reference counted, same as GetFile(), look there for more info. */
VFSDir *GetDir(const char* dn, bool create = false);
/** Returns the tree root, which is usually the working directory. */
VFSDir *GetDirRoot(void);
/** Returns one of the root tree sources by their internal name. */
VFSDir *GetBaseTree(const char *path);
/** Returns one of the mount points' base directory
(The one that is normally not acessible) */
VFSDir *GetMountPoint(const char *path);
/** Remove a file or directory from the tree */
//bool Remove(VFSFile *vf);
//bool Remove(VFSDir *dir);
//bool Remove(const char *name); // TODO: CODE ME
inline void lock() { _mtx.Lock(); }
inline void unlock() { _mtx.Unlock(); }
inline Mutex& mutex() const { return _mtx; }
// DEBUG STUFF
void debugDumpTree(std::ostream& os, VFSDir *start = NULL);
protected:
/** Drops the merged tree and allows fully re-creating it.
Overload to do additional cleanup if required. Invoked by Clear() and Prepare(true). */
virtual void _cleanup(void);
struct VDirEntry
{
VDirEntry() : vdir(NULL), overwrite(false) {}
VDirEntry(VFSDir *v, std::string mp, bool ow) : vdir(v), mountPoint(mp), overwrite(ow) {}
VFSDir *vdir;
std::string mountPoint;
bool overwrite;
};
struct BaseTreeEntry
{
std::string source;
VFSDir *dir;
};
typedef std::list<VDirEntry> VFSMountList;
typedef std::vector<VFSLoader*> LoaderArray;
typedef std::vector<VFSArchiveLoader*> ArchiveLoaderArray;
typedef std::vector<BaseTreeEntry> DirArray;
void _StoreMountPoint(const VDirEntry& ve);
bool _RemoveMountPoint(const VDirEntry& ve);
void _ClearMountPoints(void);
// the VFSDirs are merged in their declaration order.
// when merging, files already contained can be overwritten by files merged in later.
VFSDirReal *filesysRoot; // local files on disk (root dir)
// VFSDirs from various sources are stored here, and will be merged into one final tree
// by Prepare().
DirArray trees;
// If files are not in the tree, maybe one of these is able to find it.
LoaderArray loaders;
VFSDir *merged; // contains the merged virtual/actual file system tree
mutable Mutex _mtx;
private:
VFSMountList vlist; // all other trees added later, together with path to mount to
ArchiveLoaderArray archLdrs;
};
VFS_NAMESPACE_END
#endif

View file

@ -8,7 +8,7 @@
// checks to enforce correct including
#ifdef TTVFS_VFS_H
#error Oops, TTVFS_VFS_H is defined, someone messed up and included VFS.h wrongly.
#error Oops, TTVFS_VFS_H is defined, someone messed up and included ttvfs.h wrongly.
#endif
#include "VFSDefines.h"
@ -18,30 +18,6 @@
#include <string>
#include <cassert>
VFS_NAMESPACE_START
inline char *allocHelper(allocator_func alloc, size_t size)
{
return alloc ? (char*)alloc(size) : new char[size];
}
inline char *allocHelperExtra(allocator_func alloc, size_t size, size_t extra)
{
char *p = (char*)allocHelper(alloc, size + extra);
memset(p + size, 0, extra);
return p;
}
template <typename T> inline void deleteHelper(delete_func deletor, T *mem)
{
if(deletor)
deletor(mem);
else
delete [] mem;
}
VFS_NAMESPACE_END
#if _MSC_VER
# ifndef _CRT_SECURE_NO_WARNINGS
@ -53,5 +29,21 @@ VFS_NAMESPACE_END
# pragma warning(disable: 4355) // 'this' : used in base member initializer list
#endif
template <typename DST, typename SRC> inline DST safecast(SRC p)
{
#ifndef NDEBUG
assert(!p || static_cast<DST>(p) == dynamic_cast<DST>(p));
#endif
return static_cast<DST>(p);
}
template <typename DST, typename SRC> inline DST safecastNonNull(SRC p)
{
#ifndef NDEBUG
assert(p && static_cast<DST>(p) == dynamic_cast<DST>(p));
#endif
return static_cast<DST>(p);
}
#endif

View file

@ -9,6 +9,12 @@
VFS_NAMESPACE_START
VFSLoader::VFSLoader()
: root(NULL)
{
}
#if !defined(_WIN32) && defined(VFS_IGNORE_CASE)
#include <dirent.h>
@ -78,31 +84,37 @@ static bool findFileHarder(char *fn)
#endif
VFSFile *VFSLoaderDisk::Load(const char *fn, const char * /*ignored*/)
DiskLoader::DiskLoader()
{
root = new DiskDir("", this);
}
File *DiskLoader::Load(const char *fn, const char * /*ignored*/)
{
if(FileExists(fn))
return new VFSFileReal(fn); // must contain full file name
return new DiskFile(fn); // must contain full file name
VFSFileReal *vf = NULL;
DiskFile *vf = NULL;
#if !defined(_WIN32) && defined(VFS_IGNORE_CASE)
size_t s = strlen(fn);
char *t = (char*)VFS_STACK_ALLOC(s+1);
memcpy(t, fn, s+1); // copy terminating '\0' as well
if(findFileHarder(&t[0])) // fixes the filename on the way
vf = new VFSFileReal(&t[0]);
vf = new DiskFile(&t[0]);
VFS_STACK_FREE(t);
#endif
return vf;
}
VFSDir *VFSLoaderDisk::LoadDir(const char *fn, const char * /*ignored*/)
Dir *DiskLoader::LoadDir(const char *fn, const char * /*ignored*/)
{
if(IsDirectory(fn))
return new VFSDirReal(fn); // must contain full file name
if(!IsDirectory(fn))
return NULL;
VFSDirReal *ret = NULL;
DiskDir *ret = NULL;
#if !defined(_WIN32) && defined(VFS_IGNORE_CASE)
size_t s = strlen(fn);
@ -110,12 +122,12 @@ VFSDir *VFSLoaderDisk::LoadDir(const char *fn, const char * /*ignored*/)
memcpy(t, fn, s+1); // copy terminating '\0' as well
if(findFileHarder(&t[0])) // fixes the filename on the way
{
ret = new VFSDirReal(&t[0]);
fn = &t[0];
}
VFS_STACK_FREE(t);
#endif
return ret;
return safecastNonNull<DiskDir*>(getRoot()->getDir(fn, true, false));
}
VFS_NAMESPACE_END

View file

@ -6,27 +6,34 @@
#include <cstddef>
#include "VFSDefines.h"
#include "VFSRefcounted.h"
VFS_NAMESPACE_START
class VFSFile;
class VFSDir;
class File;
class Dir;
// VFSLoader - to be called if a file is not in the tree.
class VFSLoader
class VFSLoader : public Refcounted
{
public:
VFSLoader();
virtual ~VFSLoader() {}
virtual VFSFile *Load(const char *fn, const char *unmangled) = 0;
virtual VFSDir *LoadDir(const char *fn, const char *unmangled) { return NULL; }
virtual File *Load(const char *fn, const char *unmangled) = 0;
virtual Dir *LoadDir(const char *fn, const char *unmangled) { return NULL; }
inline Dir *getRoot() const { return root; }
protected:
Dir *root;
};
class VFSLoaderDisk : public VFSLoader
class DiskLoader : public VFSLoader
{
public:
virtual ~VFSLoaderDisk() {}
virtual VFSFile *Load(const char *fn, const char *unmangled);
virtual VFSDir *LoadDir(const char *fn, const char *unmangled);
DiskLoader();
virtual ~DiskLoader() {}
virtual File *Load(const char *fn, const char *unmangled);
virtual Dir *LoadDir(const char *fn, const char *unmangled);
};
VFS_NAMESPACE_END

View file

@ -0,0 +1,144 @@
#ifndef VFS_REFCOUNTED_H
#define VFS_REFCOUNTED_H
#include <algorithm>
#include <cassert>
VFS_NAMESPACE_START
template <typename T> class RefcountedT
{
public:
// Static methods for refcounting, could overload with atomics if there is the need
inline static void s_incRef(T& ref)
{
++ref;
}
inline static bool s_decRef(T& ref) // returns true if refcount is zero afterwards
{
return (--ref) == 0;
}
inline static void s_setRef(T& ref, int val)
{
ref = val;
}
inline static int s_getRef(const T& ref)
{
return ref;
}
RefcountedT()
{
s_setRef(_refcount, 0);
}
virtual ~RefcountedT()
{
int val = s_getRef(_refcount);
assert(val == 0 && "Object was deleted with refcount != 0");
}
inline void incref()
{
s_incRef(_refcount);
}
inline void decref()
{
if (s_decRef(_refcount))
{
// if the refcount is now zero, it will stay zero forever as nobody has a reference anymore
delete this;
}
}
inline int getRefCount() const
{
return s_getRef(_refcount);
}
private:
T _refcount;
};
// This is the typedef used for VFSBase
typedef RefcountedT<int> Refcounted;
template<typename T> class CountedPtr
{
public:
inline ~CountedPtr()
{
if(_p)
_p->decref();
}
inline CountedPtr() : _p(NULL)
{}
inline CountedPtr(T* p) : _p(p)
{
if(p)
p->incref();
}
inline CountedPtr(const CountedPtr& ref) : _p(ref._p)
{
if (_p)
_p->incref();
}
#ifdef HAVE_CXX_11
// C++11 move constructor
CountedPtr(CountedPtr&& ref) : CountedPtr() // initialize via default constructor
{
CountedPtr::swap(*this, ref);
}
#endif
// intentionally not a reference -- see http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
CountedPtr& operator=(CountedPtr ref)
{
CountedPtr::swap(*this, ref);
return *this;
}
const T* operator->() const { return _p; }
T* operator->() { return _p; }
const T* operator*() const { return *_p; }
T* operator*() { return *_p; }
bool operator!() const { return !_p; }
// if you use these, make sure you also keep a counted reference to the object!
inline operator T* () { return _p; }
inline operator const T* () const { return _p; }
inline T* content() { return _p; }
inline const T* content() const { return _p; }
bool operator<(const CountedPtr& ref) const { return _p < ref._p; }
bool operator<=(const CountedPtr& ref) const { return _p <= ref._p; }
bool operator==(const CountedPtr& ref) const { return _p == ref._p; }
bool operator!=(const CountedPtr& ref) const { return _p != ref._p; }
bool operator>=(const CountedPtr& ref) const { return _p >= ref._p; }
bool operator>(const CountedPtr& ref) const { return _p > ref._p; }
bool operator<(const T* ptr) const { return _p < ptr; }
bool operator<=(const T* ptr) const { return _p <= ptr; }
bool operator==(const T* ptr) const { return _p == ptr; }
bool operator!=(const T* ptr) const { return _p != ptr; }
bool operator>=(const T* ptr) const { return _p >= ptr; }
bool operator>(const T* ptr) const { return _p > ptr; }
inline static void swap(CountedPtr& a, CountedPtr& b)
{
std::swap(a._p, b._p);
}
private:
T *_p;
};
VFS_NAMESPACE_END
#endif

View file

@ -0,0 +1,270 @@
// VFSRoot.cpp - glues it all together and makes use simple
// For conditions of distribution and use, see copyright notice in VFS.h
#include <iostream> // for debug only, see EOF
#include "VFSInternal.h"
#include "VFSRoot.h"
#include "VFSTools.h"
#include "VFSDirInternal.h"
#include "VFSFile.h"
#include "VFSLoader.h"
#include "VFSArchiveLoader.h"
#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()
{
Clear();
}
void Root::Clear()
{
merged->_clearDirs();
merged->_clearMounts();
loaders.clear();
archLdrs.clear();
}
bool Root::Mount(const char *src, const char *dest)
{
return AddVFSDir(GetDir(src, true), dest);
}
bool Root::AddVFSDir(DirBase *dir, const char *subdir /* = NULL */)
{
if(!dir)
return false;
if(!subdir)
subdir = dir->fullname();
InternalDir *into = safecastNonNull<InternalDir*>(merged->getDir(subdir, true, true, false));
into->_addMountDir(dir);
return true;
}
bool Root::Unmount(const char *src, const char *dest)
{
DirBase *vdsrc = GetDir(src, false);
InternalDir *vddest = safecastNonNull<InternalDir*>(GetDir(dest, false));
if(!vdsrc || !vddest)
return false;
vddest->_removeMountDir(vdsrc); // FIXME: verify this works
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);
}
Dir *Root::AddArchive(const char *arch, void *opaque /* = NULL */)
{
File *af = GetFile(arch);
if(!af)
return NULL;
Dir *ad = NULL;
VFSLoader *fileLdr = NULL;
for(ArchiveLoaderArray::iterator it = archLdrs.begin(); it != archLdrs.end(); ++it)
if((ad = (*it)->Load(af, &fileLdr, opaque)))
break;
if(!ad)
return NULL;
if(fileLdr)
loaders.push_back(fileLdr);
AddVFSDir(ad, arch);
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)
{
ret = safecastNonNull<InternalDir*>(merged->getDir(fn, true, true, false));
ret->_addMountDir(realdir);
}
return ret;
}
DirBase *Root::GetDir(const char* dn, bool create /* = false */)
{
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)
vd = safecastNonNull<InternalDir*>(merged->getDir(dn, true)); // typecast is for debug checking only
}
//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)
{
return merged->fillView(path, view);
}
void Root::ClearGarbage()
{
merged->clearGarbage();
}
// DEBUG STUFF
struct _DbgParams
{
_DbgParams(std::ostream& os_, DirBase *parent_, const std::string& sp_)
: os(os_), parent(parent_), sp(sp_) {}
std::ostream& os;
DirBase *parent;
const std::string& sp;
};
static void _DumpFile(File *vf, void *user)
{
_DbgParams& p = *((_DbgParams*)user);
p.os << p.sp << "f|" << vf->name() << " [" << vf->getType() << ", ref " << vf->getRefCount() << ", 0x" << vf << "]";
if(strncmp(p.parent->fullname(), vf->fullname(), p.parent->fullnameLen()))
p.os << " <-- {" << vf->fullname() << "} ***********";
p.os << std::endl;
}
static void _DumpTreeRecursive(DirBase *vd, void *user)
{
_DbgParams& p = *((_DbgParams*)user);
std::string sub = p.sp + " ";
p.os << p.sp << "d|" << vd->name() << " [" << vd->getType() << ", ref " << vd->getRefCount() << ", 0x" << vd << "]";
if(p.parent && strncmp(p.parent->fullname(), vd->fullname(), strlen(p.parent->fullname())))
p.os << " <-- {" << vd->fullname() << "} ***********";
p.os << std::endl;
_DbgParams recP(p.os, vd, sub);
vd->forEachDir(_DumpTreeRecursive, &recP);
vd->forEachFile(_DumpFile, &recP);
}
void Root::debugDumpTree(std::ostream& os, Dir *start /* = NULL */)
{
_DbgParams recP(os, NULL, "");
DirBase *d = start ? start : GetDirRoot();
_DumpTreeRecursive(d, &recP);
}
VFS_NAMESPACE_END

View file

@ -0,0 +1,124 @@
// VFSRoot.h - glues it all together and makes use simple
// For conditions of distribution and use, see copyright notice in VFS.h
#ifndef VFSHELPER_H
#define VFSHELPER_H
#include <vector>
#include <list>
#include <string>
#include <iostream>
#include "VFSRefcounted.h"
VFS_NAMESPACE_START
class DirBase;
class Dir;
class InternalDir;
class File;
class VFSLoader;
class VFSArchiveLoader;
class DirView;
/** Root - extensible class to simplify working with the VFS tree */
class Root
{
public:
Root();
virtual ~Root();
/** Reset an instance to its initial state.
Drops all archives, loaders, archive loaders, mount points, internal trees, ...*/
virtual void Clear();
/** Do cleanups from time to time. For internal classes, this is a no-op.
Extensions may wish to override this method do do cleanup jobs. */
virtual void ClearGarbage();
/** Mount a directory in the tree to a different location. Requires a previous call to Prepare().
This can be imagined like the contents of a directory appearing in a different location.
Be careful not to create circles! */
bool Mount(const char *src, const char *dest);
/** Drops a directory from the tree. Internally, this calls Reload(false),
which is a heavy operation compared to Mount(). Be warned. */
bool Unmount(const char *src, const char *dest);
/** Adds a Dir object into the merged tree. If subdir is NULL (the default),
add into the subdir stored in the Dir object. The tree will be extended if target dir does not exist.
Files in the tree will be replaced if already existing.
Like with Mount(); be careful not to create cycles. */
bool AddVFSDir(DirBase *dir, const char *subdir = NULL);
/** Add an archive file to the tree, which can then be addressed like a folder,
e.g. "path/to/example.zip/file.txt".
Returns a pointer to the actual Dir object that represents the added archive, or NULL if failed.
The opaque pointer is passed directly to each loader and can contain additional parameters,
such as a password to open the file.
Read the comments in VFSArchiveLoader.h for an explanation how it works.
If you have no idea, leave it NULL, because it can easily cause a crash if not used carefully. */
Dir *AddArchive(const char *arch, void *opaque = NULL);
/** Add a loader that can look for files on demand.
Do not add more then once instance of a loader type. */
void AddLoader(VFSLoader *ldr, const char *path = NULL);
/** Add an archive loader that can open archives of various types.
Whenever an archive file is requested to be opened by AddArchive(),
it is sent through each registered loader until one of them can recognize
the format and open it. */
void AddArchiveLoader(VFSArchiveLoader *ldr);
/** Get a file from the merged tree. Asks loaders if the file is not in the tree.
If found by a loader, the file will be added to the tree. */
File *GetFile(const char *fn);
/** Fills a DirView object with a list of directories that match the specified path.
This is the preferred way to enumerate directories, as it respects and collects
mount points during traversal. The DirView instance can be re-used until an (un-)mounting
operation takes place. If the content of directories changes, this is reflected in the view.
(Added dirs or files will appear, removed ones disappear).
Use DirView::forEachFile() or DirView::forEachDir() to iterate. */
bool FillDirView(const char *path, DirView& view);
/** Remove a file or directory from the tree */
//bool Remove(File *vf);
//bool Remove(Dir *dir);
//bool Remove(const char *name); // TODO: CODE ME
/** Returns the tree root, which is usually the working directory.
Same as GetDir("").
You will most likely not need this function. */
DirBase *GetDirRoot();
/** Get a directory from the merged tree. If create is true and the directory does not exist,
build the tree structure and return the newly created dir. NULL otherwise.
You will most likely not need this function.
Use FillDirView() on a DirView object to iterate over directory contents. */
DirBase *GetDir(const char* dn, bool create = false);
// DEBUG STUFF
void debugDumpTree(std::ostream& os, Dir *start = NULL);
protected:
InternalDir *_GetDirByLoader(VFSLoader *ldr, const char *fn, const char *unmangled);
typedef std::vector<CountedPtr<VFSLoader> > LoaderArray;
typedef std::vector<CountedPtr<VFSArchiveLoader> > ArchiveLoaderArray;
// If files are not in the tree, maybe one of these is able to find it.
LoaderArray loaders;
CountedPtr<InternalDir> merged; // contains the merged virtual/actual file system tree
ArchiveLoaderArray archLdrs;
};
VFS_NAMESPACE_END
#endif

View file

@ -2,7 +2,6 @@
#define SELFREFCOUNTER_H
#include "VFSDefines.h"
#include "VFSAtomic.h"
VFS_NAMESPACE_START
@ -11,11 +10,11 @@ template <class T, bool DELSELF = true> class SelfRefCounter
{
private:
T *self;
volatile int c;
int c;
SelfRefCounter(SelfRefCounter& r); // forbid copy constructor
inline unsigned int _deref(void)
{
volatile unsigned int cc = (unsigned int)Atomic_Decr(c); // copy c, in case we get deleted
unsigned int cc = (unsigned int)(c - 1); // copy c, in case we get deleted
if(DELSELF && !cc)
{
delete self;
@ -30,11 +29,11 @@ public:
inline unsigned int count(void) { return c; }
// post-increment (dummy int)
inline unsigned int operator++(int) { unsigned int cc = c; Atomic_Incr(c); return cc; }
inline unsigned int operator++(int) { unsigned int cc = c; ++c; return cc; }
inline unsigned int operator--(int) { unsigned int cc = c; _deref(); return cc; }
// pre-increment
inline unsigned int operator++(void) { return (unsigned int)Atomic_Incr(c); }
inline unsigned int operator++(void) { return (unsigned int)++c; }
inline unsigned int operator--(void) { return _deref(); }
};

View file

@ -1,36 +1,39 @@
#include "VFSInternal.h"
#ifdef _WIN32
# include <shlobj.h>
#endif
#include "VFSSystemPaths.h"
#include "VFSTools.h"
#include "VFSInternal.h"
VFS_NAMESPACE_START
std::string GetUserDir()
{
const char *user;
#ifdef _WIN32
TCHAR szPath[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, szPath)))
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, szPath)))
{
return szPath;
}
// Fallback
const char *user = getenv("USERPROFILE");
user = getenv("USERPROFILE");
if(user)
return user;
// Sorry, windoze :(
return "";
#else // Assume POSIX compliance
const char *user = getenv("HOME");
if(user)
return user;
#endif
// Assume POSIX compliance
user = getenv("HOME");
if(user)
return user;
return "";
}
std::string GetAppDir(const char *appname)
@ -55,7 +58,7 @@ std::string GetAppDir(const char *appname)
ret = "."; // Seems we have no other choice
}
return FixPath(ret + '/' + appname);
ret += '/';
#else // Assume POSIX compliance
@ -65,9 +68,14 @@ std::string GetAppDir(const char *appname)
else
ret = ".";
return FixPath(ret + "/." + appname); // just in case
ret += "/.";
#endif
ret += appname;
FixPath(ret);
return ret;
}

View file

@ -1,21 +1,16 @@
// VFSTools.cpp - useful functions and misc stuff
// For conditions of distribution and use, see copyright notice in VFS.h
#include "VFSInternal.h"
#include "VFSFileFuncs.h"
#include "VFSTools.h"
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <stack>
#include <cstdio>
#include "VFSTools.h"
#if _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <io.h>
#else
# ifdef __HAIKU__
# include <dirent.h>
@ -30,15 +25,6 @@
VFS_NAMESPACE_START
void stringToLower(std::string& s)
{
std::transform(s.begin(), s.end(), s.begin(), tolower);
}
void stringToUpper(std::string& s)
{
std::transform(s.begin(), s.end(), s.begin(), toupper);
}
#if !_WIN32
#ifdef DT_DIR
@ -97,7 +83,7 @@ static bool _IsDir(const char *path, dirent *dp)
char *pathname = (char*)alloca(len1 + 1 + len2 + 1 + 13);
strcpy (pathname, path);
/* Avoid UNC-path "//name" on Cygwin. */
/* Avoid UNC-path "//name" on Cygwin. */
if (len1 > 0 && pathname[len1 - 1] != '/')
strcat (pathname, "/");
@ -119,104 +105,75 @@ static bool _IsFile(const char *path, dirent *dp)
// returns list of *plain* file names in given directory,
// without paths, and without anything else
void GetFileList(const char *path, StringList& files)
bool GetFileList(const char *path, StringList& files)
{
#if !_WIN32
DIR * dirp;
struct dirent * dp;
dirp = opendir(path);
if(dirp)
{
while((dp=readdir(dirp)) != NULL)
{
if (_IsFile(path, dp)) // only add if it is not a directory
{
std::string s(dp->d_name);
files.push_back(s);
}
}
closedir(dirp);
}
if(!dirp)
return false;
# else
while((dp=readdir(dirp)) != NULL)
{
if (_IsFile(path, dp)) // only add if it is not a directory
{
std::string s(dp->d_name);
files.push_back(s);
}
}
closedir(dirp);
return true;
#else
WIN32_FIND_DATA fil;
std::string search(path);
MakeSlashTerminated(search);
search += "*";
HANDLE hFil = FindFirstFile(search.c_str(),&fil);
if(hFil != INVALID_HANDLE_VALUE)
if(hFil == INVALID_HANDLE_VALUE)
return false;
do
{
do
if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
std::string s(fil.cFileName);
files.push_back(s);
}
std::string s(fil.cFileName);
files.push_back(s);
}
while(FindNextFile(hFil, &fil));
FindClose(hFil);
}
while(FindNextFile(hFil, &fil));
# endif
FindClose(hFil);
return true;
#endif
}
// returns a list of directory names in the given directory, *without* the source dir.
// if getting the dir list recursively, all paths are added, except *again* the top source dir beeing queried.
void GetDirList(const char *path, StringList &dirs, int depth /* = 0 */)
bool GetDirList(const char *path, StringList &dirs, int depth /* = 0 */)
{
#if !_WIN32
DIR * dirp;
struct dirent * dp;
dirp = opendir(path);
if(dirp)
{
std::string pathstr(path);
MakeSlashTerminated(pathstr);
while((dp = readdir(dirp))) // assignment is intentional
{
if (_IsDir(path, dp)) // only add if it is a directory
{
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
dirs.push_back(dp->d_name);
if (depth) // needing a better way to do that
{
std::string d = dp->d_name;
std::string subdir = pathstr + d;
MakeSlashTerminated(d);
StringList newdirs;
GetDirList(subdir.c_str(), newdirs, depth - 1);
for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
dirs.push_back(d + *it);
}
}
}
}
closedir(dirp);
}
if(!dirp)
return false;
#else
std::string pathstr(path);
MakeSlashTerminated(pathstr);
WIN32_FIND_DATA fil;
HANDLE hFil = FindFirstFile((pathstr + '*').c_str(),&fil);
if(hFil != INVALID_HANDLE_VALUE)
while((dp = readdir(dirp))) // assignment is intentional
{
do
if (_IsDir(path, dp)) // only add if it is a directory
{
if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
continue;
dirs.push_back(fil.cFileName);
if (depth) // need a better way to do that
dirs.push_back(dp->d_name);
if (depth) // needing a better way to do that
{
std::string d = fil.cFileName;
std::string d = dp->d_name;
std::string subdir = pathstr + d;
MakeSlashTerminated(d);
StringList newdirs;
@ -226,24 +183,52 @@ void GetDirList(const char *path, StringList &dirs, int depth /* = 0 */)
}
}
}
while(FindNextFile(hFil, &fil));
FindClose(hFil);
}
closedir(dirp);
return true;
#else
std::string pathstr(path);
MakeSlashTerminated(pathstr);
WIN32_FIND_DATA fil;
HANDLE hFil = FindFirstFile((pathstr + '*').c_str(),&fil);
if(hFil == INVALID_HANDLE_VALUE)
return false;
do
{
if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
continue;
dirs.push_back(fil.cFileName);
if (depth) // need a better way to do that
{
std::string d = fil.cFileName;
std::string subdir = pathstr + d;
MakeSlashTerminated(d);
StringList newdirs;
GetDirList(subdir.c_str(), newdirs, depth - 1);
for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
dirs.push_back(d + *it);
}
}
}
while(FindNextFile(hFil, &fil));
FindClose(hFil);
return true;
#endif
}
bool FileExists(const char *fn)
{
#ifdef _WIN32
void *fp = real_fopen(fn, "rb");
if(fp)
{
real_fclose(fp);
return true;
}
return false;
#if _WIN32
return _access(fn, 0) == 0;
#else
return access(fn, F_OK) == 0;
#endif
@ -285,33 +270,35 @@ bool CreateDirRec(const char *dir)
return result || last;
}
vfspos GetFileSize(const char* fn)
bool GetFileSize(const char* fn, vfspos& size)
{
vfspos sz = 0;
#ifdef VFS_LARGEFILE_SUPPORT
# ifdef _MSC_VER
struct _stat64 st;
if(_stat64(fn, &st))
return 0;
return st.st_size;
return false;
sz = st.st_size;
# else // _MSC_VER
struct stat64 st;
if(stat64(fn, &st))
return 0;
return st.st_size;
return false;
sz = st.st_size;
# endif
#else // VFS_LARGEFILE_SUPPORT
struct stat st;
if(stat(fn, &st))
return 0;
return st.st_size;
return false;
sz = st.st_size;
#endif
size = sz;
return true;
}
std::string FixSlashes(const std::string& s)
void FixSlashes(std::string& s)
{
std::string r;
r.reserve(s.length() + 1);
char last = 0, cur;
size_t wpos = 0;
for(size_t i = 0; i < s.length(); ++i)
{
cur = s[i];
@ -319,29 +306,35 @@ std::string FixSlashes(const std::string& s)
cur = '/';
if(last == '/' && cur == '/')
continue;
r += cur;
s[wpos++] = cur;
last = cur;
}
return r;
s.resize(wpos);
}
std::string FixPath(const std::string& s)
void FixPath(std::string& s)
{
if(s.empty())
return s;
return;
const char *p = s.c_str();
while(p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
p += 2;
if(!*p)
return "";
char end = s[s.length() - 1];
if(end == '/' || end == '\\')
{
std::string r(p);
r.erase(r.length() - 1); // strip trailing '/'
return FixSlashes(r);
s.clear();
return;
}
return FixSlashes(p);
size_t len = s.length();
while(len)
{
char end = s[len - 1];
if(end == '/' || end == '\\') // strip trailing '/'
--len;
else
break;
}
s.resize(len);
FixSlashes(s);
}
bool IsDirectory(const char *s)
@ -369,37 +362,47 @@ void MakeSlashTerminated(std::string& s)
}
// extracts the file name from a given path
const char *PathToFileName(const char *str)
const char *GetBaseNameFromPath(const char *str)
{
const char *p = strrchr(str, '/');
return p ? p+1 : str;
}
std::string StripFileExtension(const std::string& s)
void StripFileExtension(std::string& s)
{
size_t pos = s.find_last_of('.');
size_t pos2 = s.find_last_of('/');
if(pos != std::string::npos && (pos2 < pos || pos2 == std::string::npos))
return s.substr(0, pos);
return s;
if(pos != std::string::npos && (pos2 == std::string::npos || pos2 < pos))
s.resize(pos+1);
}
std::string StripLastPath(const std::string& s)
void StripLastPath(std::string& s)
{
if(s.empty())
return "";
size_t len = s.length();
while(len)
{
char end = s[len - 1];
if(end == '/' || end == '\\') // strip trailing '/'
--len;
else
break;
}
s.resize(len);
if(s[s.length() - 1] == '/')
return StripLastPath(s.substr(0, s.length() - 1));
if(s.empty())
return;
size_t pos = s.find_last_of('/');
if(pos == std::string::npos)
return ""; // nothing remains
{
s.clear();
return; // nothing remains
}
return s.substr(0, pos);
s.resize(pos+1);
}
// from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15
bool WildcardMatch(const char *str, const char *pattern)
{
@ -477,17 +480,6 @@ size_t strnNLcpy(char *dst, const char *src, unsigned int n /* = -1 */)
return dst - olddst;
}
// Directly appends 'add' to 's', ensuring that 's' is null-terminated.
// Returns the next write position for fastcat (the null byte at the end).
char *fastcat(char *s, const char *add)
{
size_t len = strlen(add);
memcpy(s, add, len);
s += len;
*(s + 1) = 0;
return s;
}
VFS_NAMESPACE_END

View file

@ -7,6 +7,7 @@
#ifndef VFS_TOOLS_H
#define VFS_TOOLS_H
#include <cstdlib>
#include <deque>
#include <string>
@ -16,24 +17,23 @@ VFS_NAMESPACE_START
typedef std::deque<std::string> StringList;
void stringToUpper(std::string& s);
void stringToLower(std::string& s);
void GetFileList(const char *, StringList& files);
void GetDirList(const char *, StringList& dirs, int depth = 0); // recursion depth: 0 = subdirs of current, 1 = subdirs one level down, ..., -1 = deep recursion
// these return false if the queried dir does not exist
bool GetFileList(const char *, StringList& files);
bool GetDirList(const char *, StringList& dirs, int depth = 0); // recursion depth: 0 = subdirs of current, 1 = subdirs one level down, ..., -1 = deep recursion
bool FileExists(const char *);
bool IsDirectory(const char *);
bool CreateDir(const char*);
bool CreateDirRec(const char*);
vfspos GetFileSize(const char*);
std::string FixSlashes(const std::string& s);
std::string FixPath(const std::string& s);
const char *PathToFileName(const char *str);
bool GetFileSize(const char*, vfspos&);
void FixSlashes(std::string& s);
void FixPath(std::string& s);
const char *GetBaseNameFromPath(const char *str);
void MakeSlashTerminated(std::string& s);
std::string StripFileExtension(const std::string& s);
std::string StripLastPath(const std::string& s);
void StripFileExtension(std::string& s);
void StripLastPath(std::string& s);
bool WildcardMatch(const char *str, const char *pattern);
size_t strnNLcpy(char *dst, const char *src, unsigned int n = -1);
char *fastcat(char *s, const char *add);
template <class T> void StrSplit(const std::string &src, const std::string &sep, T& container, bool keepEmpty = false)
{
@ -55,46 +55,23 @@ template <class T> void StrSplit(const std::string &src, const std::string &sep,
container.push_back(s);
}
inline static size_t stringhash(const char *s)
template <typename T> void SkipSelfPath(T *& s)
{
size_t h = 0;
for( ; *s; ++s)
{
h += *s;
h += ( h << 10 );
h ^= ( h >> 6 );
}
h += ( h << 3 );
h ^= ( h >> 11 );
h += ( h << 15 );
return h;
while(s[0] == '.' && s[1] == '/')
s += 2;
if(s[0] == '.' && !s[1])
++s;
}
inline static size_t stringhash_nocase(const char *s)
inline std::string joinPath(std::string base, const char *sub)
{
size_t h = 0;
for( ; *s; ++s)
{
h += tolower(*s);
h += ( h << 10 );
h ^= ( h >> 6 );
}
h += ( h << 3 );
h ^= ( h >> 11 );
h += ( h << 15 );
return h;
if(!*sub)
return base;
if(*sub != '/' && base.length() && base[base.length()-1] != '/')
base += '/';
return base + sub;
}
#ifdef VFS_IGNORE_CASE
# define STRINGHASH(s) stringhash_nocase(s)
#else
# define STRINGHASH(s) stringhash(s)
#endif
VFS_NAMESPACE_END
#endif

View file

@ -1,6 +1,6 @@
/* ttvfs -- tiny tree virtual file system
// VFS.h - all the necessary includes to get a basic VFS working
// ttvfs.h - all the necessary includes to get a basic VFS working
// Only include externally, not inside the library.
See VFSDefines.h for compile configration.
@ -33,43 +33,43 @@ THE SOFTWARE.
#define TTVFS_VFS_H
#include "VFSDefines.h"
#include <cstring>
VFS_NAMESPACE_START
bool _checkCompatInternal(bool large, bool nocase, bool hashmap, unsigned int vfspos_size);
bool _checkCompatInternal(_AbiCheck *abi);
/** It is recommended to call this function early in your code
and ensure it returns true - if it does not, compiler settings
are inconsistent, which may cause otherwise hard to detect problems. */
inline static bool checkCompat(void)
inline static bool checkCompat()
{
_AbiCheck abi;
memset(&abi, 0, sizeof(abi));
abi.structSize = sizeof(abi);
abi.vfsposSize = sizeof(vfspos);
#ifdef VFS_LARGEFILE_SUPPORT
bool largefile = true;
#else
bool largefile = false;
abi.largefile = 1;
#endif
#ifdef VFS_IGNORE_CASE
bool nocase = true;
#else
bool nocase = false;
abi.nocase = 1;
#endif
#ifdef VFS_USE_HASHMAP
bool hashmap = true;
#else
bool hashmap = false;
#endif
return _checkCompatInternal(largefile, nocase, hashmap, sizeof(vfspos));
return _checkCompatInternal(&abi);
}
VFS_NAMESPACE_END
#include <cstring>
#include <string>
#include "VFSHelper.h"
#include "VFSRoot.h"
#include "VFSFile.h"
#include "VFSDir.h"
#include "VFSDirView.h"
#include "VFSSystemPaths.h"
#include "VFSTools.h"
#include "VFSLoader.h"
// Check to enforce correct including.

View file

@ -0,0 +1,12 @@
set(cfileapi_SRC
ttvfs_stdio.cpp
ttvfs_stdio.h
)
include_directories(${TTVFS_INCLUDE_DIRS})
add_library(ttvfs_cfileapi ${cfileapi_SRC})
target_link_libraries(ttvfs_cfileapi ttvfs)

View file

@ -0,0 +1,137 @@
#define VFS_ENABLE_C_API 1
#include "ttvfs.h"
#include "ttvfs_stdio.h"
#include <cassert>
#include <sstream>
static ttvfs::Root *vfs = NULL;
void ttvfs_setroot(ttvfs::Root *root)
{
vfs = root;
}
VFILE *vfopen(const char *fn, const char *mode)
{
if (strchr(mode, 'w'))
{
assert(0 && "ttvfs_stdio: File writing via VFS not yet supported!");
return NULL;
}
VFILE *vf = vfs->GetFile(fn);
if (!vf || !vf->open(mode))
return NULL;
vf->incref(); // keep the file alive until closed.
return vf;
}
size_t vfread(void *ptr, size_t size, size_t count, VFILE *vf)
{
return vf->read(ptr, size * count) / size;
}
int vfclose(VFILE *vf)
{
vf->close();
vf->decref();
return 0;
}
size_t vfwrite(const void *ptr, size_t size, size_t count, VFILE *vf)
{
return vf->write(ptr, size * count) / size;
}
// return 0 on success, -1 on error
int vfseek(VFILE *vf, long int offset, int origin)
{
return vf->seek(offset, origin) ? 0 : -1;
}
// warning: slow
char *vfgets(char *str, int num, VFILE *vf)
{
char *s = str;
if (vf->iseof())
return NULL;
unsigned int remain = int(vf->size() - vf->getpos());
if (remain < (unsigned int)num)
num = remain;
else
--num; // be sure to keep space for the final null char
for(int i = 0; i < num; ++i)
{
char c;
if(!vf->read(&c, 1))
break;
*s++ = c;
if(c == '\n' || c == '\r')
break;
}
*s++ = 0;
return str;
}
int vfsize(VFILE *f, size_t *sizep)
{
ttvfs::vfspos sz = f->size();
if(sz == ttvfs::npos)
return -1;
*sizep = (size_t)sz;
return 0;
}
long int vftell(VFILE *vf)
{
return (long int)vf->getpos();
}
InStream::InStream(const std::string& fn)
: std::istringstream()
{
open(fn.c_str());
}
InStream::InStream(const char *fn)
: std::istringstream()
{
open(fn);
}
bool InStream::open(const char *fn)
{
ttvfs::File *vf = vfs->GetFile(fn);
if(vf && vf->open("r"))
{
size_t sz = (size_t)vf->size();
std::string s;
s.resize(sz);
vf->read(&s[0], sz);
str(s);
vf->close();
return true;
}
setstate(std::ios::failbit);
return false;
}
int ttvfs_stdio_fsize(VFILE *f, size_t *sizep)
{
size_t sz = 0;
if ( vfseek(f, 0, SEEK_END) != 0
|| (sz = vftell(f)) < 0
|| vfseek(f, 0, SEEK_SET) != 0)
{
return -1;
}
*sizep = sz;
return 0;
}

View file

@ -0,0 +1,86 @@
#ifndef TTVFS_STDIO_OVERRIDE_H
#define TTVFS_STDIO_OVERRIDE_H
// Before including this file:
// * Define VFS_ENABLE_C_API to 0 to use the normal functions (fopen() and friends, std::ifstream).
// * Define VFS_ENABLE_C_API to 1 to use ttvfs overrides.
/*
This file is a minimal wrapper to replace the C API and std::ifstream.
Note that if you have an advanced needs, this wrapper API is not for you.
To use it, go through your code and rename all FILE* to VFILE*,
and fopen() and related to vfopen() (so just put a 'v' in front).
Instead of std::ifstream, use InStream. If you use std::fstream for reading ONLY,
also use InStream.
Note that the seek and tell functions do not offer 64 bit offsets in this API.
*/
//-------------------------------------------------
#if VFS_ENABLE_C_API-0 == 1
#include <iostream>
#include <sstream>
#include <ttvfs.h>
typedef ttvfs::File VFILE;
void ttvfs_setroot(ttvfs::Root *root);
VFILE *vfopen(const char *fn, const char *mode);
size_t vfread(void *ptr, size_t size, size_t count, VFILE *vf);
int vfclose(VFILE *vf);
size_t vfwrite(const void *ptr, size_t size, size_t count, VFILE *vf);
int vfseek(VFILE *vf, long int offset, int origin);
char *vfgets(char *str, int num, VFILE *vf);
long int vftell(VFILE *vf);
int vfsize(VFILE *vf, size_t *sizep); // extension
// This class is a minimal adapter to support STL-like read-only file streams for VFS files, using std::istringstream.
// It loads the whole file content at once. If this is a problem, roll your own, or use the lower-level ttvfs::File functions.
class InStream : public std::istringstream
{
public:
InStream(const char *fn);
InStream(const std::string& fn);
bool open(const char *fn);
inline bool is_open() { return good(); }
void close() {}
private:
void _init(const char *fn);
};
#define VFS_USING_C_API 1
//--------------------------------------------------
#elif VFS_ENABLE_C_API-0 == 0
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
typedef std::ifstream InStream;
typedef FILE VFILE;
int ttvfs_stdio_fsize(VFILE *f, size_t *sizep); // extension
#define vfopen fopen
#define vfread fread
#define vfclose fclose
#define vfwrite fwrite
#define vfseek fseek
#define vfgets fgets
#define vftell ftell
#define vfsize ttvfs_stdio_fsize
//-------------------------------------------------
#else // VFS_ENABLE_C_API
// Help catch errors or forgotten defines if this file is included
# error #define VFS_ENABLE_C_API to either 0 or 1
#endif
#endif // FILE_API_H

View file

@ -6,6 +6,8 @@ set(ttvfs_zip_SRC
VFSFileZip.h
VFSZipArchiveLoader.cpp
VFSZipArchiveLoader.h
VFSZipArchiveRef.cpp
VFSZipArchiveRef.h
miniz.c
miniz.h
)

View file

@ -9,99 +9,68 @@
VFS_NAMESPACE_START
static size_t zip_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
ZipDir::ZipDir(ZipArchiveRef *handle, const char *fullpath, bool canLoad)
: Dir(fullpath, NULL)
, _archiveHandle(handle)
, _canLoad(canLoad)
, _couldLoad(canLoad)
{
VFSFile *vf = (VFSFile*)pOpaque;
mz_int64 cur_ofs = vf->getpos();
if((mz_int64)file_ofs < 0)
return 0;
if(cur_ofs != (mz_int64)file_ofs && !vf->seek((vfspos)file_ofs))
return 0;
return vf->read(pBuf, n);
}
static bool zip_reader_init_vfsfile(mz_zip_archive *pZip, VFSFile *vf, mz_uint32 flags)
{
if(!(pZip || vf))
return false;
vf->open("rb");
mz_uint64 file_size = vf->size();
if(!file_size)
{
vf->close();
return false;
}
pZip->m_pRead = zip_read_func;
pZip->m_pIO_opaque = vf;
if (!mz_zip_reader_init(pZip, file_size, flags))
{
vf->close();
return false;
}
return true;
}
VFSDirZip::VFSDirZip(VFSFile *zf)
: VFSDir(zf->fullname()), _zf(zf)
{
_zf->ref++;
_setOrigin(this);
memset(&_zip, 0, sizeof(_zip));
}
VFSDirZip::~VFSDirZip()
ZipDir::~ZipDir()
{
close();
_zf->ref--;
}
bool VFSDirZip::close(void)
void ZipDir::close()
{
mz_zip_reader_end(&_zip);
_zf->close();
return true;
_archiveHandle->close();
_canLoad = _couldLoad; // allow loading again after re-opening (in case archive was replaced)
}
VFSDir *VFSDirZip::createNew(const char *dir) const
DirBase *ZipDir::createNew(const char *fullpath) const
{
return VFSDir::createNew(dir); // inside a Zip file; only the base dir can be a real VFSDirZip.
const ZipArchiveRef *czref = _archiveHandle;
ZipArchiveRef *zref = const_cast<ZipArchiveRef*>(czref);
return new ZipDir(zref, fullpath, false);
}
unsigned int VFSDirZip::load(bool /*ignored*/)
#define MZ ((mz_zip_archive*)_archiveHandle->mz)
void ZipDir::load()
{
close();
if(!_canLoad)
return;
if(!zip_reader_init_vfsfile(&_zip, _zf, 0))
return 0;
_archiveHandle->openRead();
unsigned int files = mz_zip_reader_get_num_files(&_zip);
const unsigned int files = mz_zip_reader_get_num_files(MZ);
const size_t len = fullnameLen();
mz_zip_archive_file_stat fs;
for (unsigned int i = 0; i < files; ++i)
{
// FIXME: do we want empty dirs in the tree?
if(mz_zip_reader_is_file_a_directory(&_zip, i))
if(mz_zip_reader_is_file_encrypted(MZ, i))
continue;
if(mz_zip_reader_is_file_encrypted(&_zip, i))
if(!mz_zip_reader_file_stat(MZ, i, &fs))
continue;
if(!mz_zip_reader_file_stat(&_zip, i, &fs))
if(mz_zip_reader_is_file_a_directory(MZ, i))
{
getDir(fs.m_filename, true);
continue;
}
if(getFile(fs.m_filename))
continue;
VFSFileZip *vf = new VFSFileZip(&_zip);
vf->_setOrigin(this);
memcpy(vf->getZipFileStat(), &fs, sizeof(mz_zip_archive_file_stat));
vf->_init();
addRecursive(vf, true, VFSDir::NONE);
vf->ref--;
ZipFile *vf = new ZipFile(fs.m_filename, _archiveHandle, (vfspos)fs.m_uncomp_size, fs.m_file_index);
addRecursive(vf, len);
}
// Not necessary to keep open all the time, VFSFileZip will re-open the archive if needed
//close();
return files;
_canLoad = false;
}
VFS_NAMESPACE_END
VFS_NAMESPACE_END

View file

@ -2,31 +2,28 @@
#define VFSDIR_ZIP_H
#include "VFSDir.h"
#include "miniz.h"
#include "VFSZipArchiveRef.h"
VFS_NAMESPACE_START
class VFSFile;
class VFSDirZip : public VFSDir
class ZipDir : public Dir
{
public:
VFSDirZip(VFSFile *zf);
virtual ~VFSDirZip();
virtual unsigned int load(bool recusive);
virtual VFSDir *createNew(const char *dir) const;
virtual const char *getType() const { return "VFSDirZip"; }
virtual bool close();
inline mz_zip_archive *getZip() { return &_zip; }
ZipDir(ZipArchiveRef *handle, const char *subpath, bool canLoad);
virtual ~ZipDir();
virtual void load();
virtual const char *getType() const { return "ZipDir"; }
virtual void close();
virtual DirBase *createNew(const char *dir) const;
protected:
VFSFile *_zf;
mz_zip_archive _zip;
std::string zipfilename;
CountedPtr<ZipArchiveRef> _archiveHandle;
bool _canLoad;
const bool _couldLoad;
};
VFS_NAMESPACE_END
#endif

View file

@ -2,196 +2,161 @@
#include "VFSInternal.h"
#include "VFSTools.h"
#include "VFSDir.h"
#include <stdio.h>
#include "miniz.h"
VFS_NAMESPACE_START
// From miniz.c
//#define MZ_ZIP_MODE_READING 2
static bool zip_reader_reopen_vfsfile(mz_zip_archive *pZip, mz_uint32 flags)
ZipFile::ZipFile(const char *name, ZipArchiveRef *zref, vfspos uncompSize, unsigned int fileIdx)
: File(joinPath(zref->fullname(), name).c_str())
, _buf(NULL)
, _pos(0)
, _archiveHandle(zref)
, _uncompSize(uncompSize)
, _fileIdx(fileIdx)
, _mode("rb") // binary mode by default
{
if(!(pZip && pZip->m_pIO_opaque && pZip->m_pRead))
return false;
VFSFile *vf = (VFSFile*)pZip->m_pIO_opaque;
if(!vf->isopen())
if(!vf->open("rb"))
return false;
if(pZip->m_zip_mode == MZ_ZIP_MODE_READING)
return true;
mz_uint64 file_size = vf->size();
if(!file_size)
{
vf->close();
return false;
}
if (!mz_zip_reader_init(pZip, file_size, flags))
{
vf->close();
return false;
}
return true;
}
ZipFile::~ZipFile()
{
close();
}
VFSFileZip::VFSFileZip(mz_zip_archive *zip)
: VFSFile(NULL), _fixedStr(NULL), _zip(zip)
bool ZipFile::open(const char *mode /* = NULL */)
{
_mode = "b"; // binary mode by default
_pos = 0;
}
void VFSFileZip::_init()
{
_setName(_zipstat.m_filename);
}
VFSFileZip::~VFSFileZip()
{
dropBuf(true);
}
bool VFSFileZip::open(const char *mode /* = NULL */)
{
VFS_GUARD_OPT(this);
_pos = 0;
if(mode)
if(!mode)
mode = "rb";
if(_mode != mode)
{
if(_fixedStr && _mode != mode)
{
delete [] _fixedStr;
_fixedStr = NULL;
}
delete [] _buf;
_buf = NULL;
_mode = mode;
}
return true; // does not have to be opened
}
bool VFSFileZip::isopen(void) const
bool ZipFile::isopen() const
{
return true; // is always open
}
bool VFSFileZip::iseof(void) const
bool ZipFile::iseof() const
{
VFS_GUARD_OPT(this);
return _pos >= _zipstat.m_uncomp_size;
return _pos >= _uncompSize;
}
bool VFSFileZip::close(void)
void ZipFile::close()
{
//return flush(); // TODO: write to zip file on close
//flush(); // TODO: write to zip file on close
delete []_buf;
_buf = NULL;
}
bool ZipFile::seek(vfspos pos, int whence)
{
const vfspos end = 0xFFFFFFFF;
switch(whence)
{
case SEEK_SET:
if(pos >= end) // zip files have uint32 range only
return false;
_pos = pos;
break;
case SEEK_CUR:
if(_pos + pos >= end)
return false;
_pos += pos;
break;
case SEEK_END:
if(pos >= _uncompSize)
return false;
_pos = _uncompSize - pos;
break;
default:
return false;
}
return true;
}
bool VFSFileZip::seek(vfspos pos)
{
if(pos >= 0xFFFFFFFF) // zip files have uint32 range only
return false;
VFS_GUARD_OPT(this);
_pos = (unsigned int)pos;
return true;
}
bool VFSFileZip::flush(void)
bool ZipFile::flush()
{
// FIXME: use this to actually write to zip file?
return false;
}
vfspos VFSFileZip::getpos(void) const
vfspos ZipFile::getpos() const
{
VFS_GUARD_OPT(this);
return _pos;
}
unsigned int VFSFileZip::read(void *dst, unsigned int bytes)
unsigned int ZipFile::read(void *dst, unsigned int bytes)
{
VFS_GUARD_OPT(this);
char *mem = (char*)getBuf();
char *startptr = mem + _pos;
char *endptr = mem + size();
if(!_buf && !unpack())
return 0;
char *startptr = _buf + _pos;
char *endptr = _buf + size();
bytes = std::min((unsigned int)(endptr - startptr), bytes); // limit in case reading over buffer size
if(_mode.find('b') == std::string::npos)
strnNLcpy((char*)dst, (const char*)startptr, bytes); // non-binary == text mode
else
//if(_mode.find('b') == std::string::npos)
// strnNLcpy((char*)dst, (const char*)startptr, bytes); // non-binary == text mode
//else
memcpy(dst, startptr, bytes); // binary copy
_pos += bytes;
return bytes;
}
unsigned int VFSFileZip::write(const void *src, unsigned int bytes)
unsigned int ZipFile::write(const void *src, unsigned int bytes)
{
/*VFS_GUARD_OPT(this);
if(getpos() + bytes >= size())
size(getpos() + bytes); // enlarge if necessary
// TODO: implement actually writing to the underlying Zip file.
memcpy(_buf + getpos(), src, bytes);
// TODO: implement actually writing to the Zip file.
return bytes;*/
return VFSFile::write(src, bytes);
return 0;
}
vfspos VFSFileZip::size(void)
vfspos ZipFile::size()
{
VFS_GUARD_OPT(this);
return (vfspos)_zipstat.m_uncomp_size;
return (vfspos)_uncompSize;
}
const void *VFSFileZip::getBuf(allocator_func alloc /* = NULL */, delete_func del /* = NULL */)
#define MZ ((mz_zip_archive*)_archiveHandle->mz)
bool ZipFile::unpack()
{
assert(!alloc == !del); // either both or none may be defined. Checked extra early to prevent possible errors later.
delete [] _buf;
VFS_GUARD_OPT(this);
// _fixedStr gets deleted on mode change, so doing this check here is fine
if(_fixedStr)
return _fixedStr;
if(!_archiveHandle->openRead())
return false; // can happen if the underlying zip file was deleted
// In case of text data, make sure the buffer is always terminated with '\0'.
// Won't hurt for binary data, so just do it in all cases.
size_t sz = (size_t)size();
_buf = new char[sz + 1];
if(!_buf)
return false;
if(!mz_zip_reader_extract_to_mem(MZ, _fileIdx, _buf, sz, 0))
{
size_t sz = (size_t)size();
_buf = allocHelperExtra(alloc, sz, 4);
if(!_buf)
return NULL;
_delfunc = del;
if(!zip_reader_reopen_vfsfile(_zip, 0))
return NULL; // can happen if the underlying zip file was deleted
if(!mz_zip_reader_extract_to_mem(_zip, _zipstat.m_file_index, _buf, sz, 0))
return NULL; // this should not happen
if(_mode.find("b") == std::string::npos) // text mode?
{
_fixedStr = allocHelperExtra(alloc, sz, 4);
strnNLcpy(_fixedStr, (const char*)_buf);
// FIXME: is this really correct?
VFSFile::dropBuf(true);
return _fixedStr;
}
delete [] _buf;
_buf = NULL;
return false; // this should not happen
}
return _buf;
_buf[sz] = 0;
if(_mode.find("b") == std::string::npos) // text mode?
{
_uncompSize = strnNLcpy(_buf, _buf);
}
return true;
}
void VFSFileZip::dropBuf(bool del)
{
VFSFile::dropBuf(del);
VFS_GUARD_OPT(this);
if(del)
delBuf(_fixedStr);
_fixedStr = NULL;
}
VFS_NAMESPACE_END

View file

@ -2,38 +2,36 @@
#define VFSFILE_ZIP_H
#include "VFSFile.h"
#include "miniz.h"
#include "VFSZipArchiveRef.h"
VFS_NAMESPACE_START
class VFSFileZip : public VFSFile
class ZipFile : public File
{
public:
VFSFileZip(mz_zip_archive *zip);
virtual ~VFSFileZip();
ZipFile(const char *name, ZipArchiveRef *zref, vfspos uncompSize, unsigned int fileIdx);
virtual ~ZipFile();
virtual bool open(const char *mode = NULL);
virtual bool isopen(void) const;
virtual bool iseof(void) const;
virtual bool close(void);
virtual bool seek(vfspos pos);
virtual bool flush(void);
virtual vfspos getpos(void) const;
virtual bool isopen() const;
virtual bool iseof() const;
virtual void close();
virtual bool seek(vfspos pos, int whence);
virtual bool flush();
virtual vfspos getpos() const;
virtual unsigned int read(void *dst, unsigned int bytes);
virtual unsigned int write(const void *src, unsigned int bytes);
virtual vfspos size(void);
virtual const void *getBuf(allocator_func alloc = NULL, delete_func del = NULL);
virtual void dropBuf(bool del);
virtual const char *getType(void) const { return "Zip"; }
inline mz_zip_archive_file_stat *getZipFileStat(void) { return &_zipstat; }
void _init();
virtual vfspos size();
virtual const char *getType() const { return "ZipFile"; }
protected:
unsigned int _pos;
bool unpack();
char *_buf;
vfspos _pos;
CountedPtr<ZipArchiveRef> _archiveHandle;
vfspos _uncompSize;
unsigned int _fileIdx;
std::string _mode;
mz_zip_archive_file_stat _zipstat;
mz_zip_archive *_zip;
char *_fixedStr; // for \n fixed string in text mode. cleared when mode is changed
};
VFS_NAMESPACE_END

View file

@ -1,17 +1,18 @@
#include "VFSInternal.h"
#include "VFSZipArchiveLoader.h"
#include "VFSDirZip.h"
#include "VFSZipArchiveRef.h"
VFS_NAMESPACE_START
VFSDir *VFSZipArchiveLoader::Load(VFSFile *arch, VFSLoader ** /*unused*/, void * /*unused*/)
Dir *VFSZipArchiveLoader::Load(File *arch, VFSLoader ** /*unused*/, void * /*unused*/)
{
VFSDirZip *vd = new VFSDirZip(arch);
if(vd->load(true))
return vd;
vd->ref--;
return NULL;
CountedPtr<ZipArchiveRef> zref = new ZipArchiveRef(arch);
if(!zref->init() || !zref->openRead())
return NULL;
ZipDir *vd = new ZipDir(zref, arch->fullname(), true);
vd->load();
return vd;
}
VFS_NAMESPACE_END

View file

@ -5,11 +5,12 @@
VFS_NAMESPACE_START
class VFSZipArchiveLoader : public VFSArchiveLoader
{
public:
virtual ~VFSZipArchiveLoader() {}
virtual VFSDir *Load(VFSFile *arch, VFSLoader **ldr, void *opaque = NULL);
virtual Dir *Load(File *arch, VFSLoader **ldr, void *opaque = NULL);
};
VFS_NAMESPACE_END

View file

@ -0,0 +1,121 @@
#include "VFSZipArchiveRef.h"
#include "miniz.h"
VFS_NAMESPACE_START
static size_t zip_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
File *vf = (File*)pOpaque;
mz_int64 cur_ofs = vf->getpos();
if((mz_int64)file_ofs < 0)
return 0;
if(cur_ofs != (mz_int64)file_ofs && !vf->seek((vfspos)file_ofs, SEEK_SET))
return 0;
return vf->read(pBuf, n);
}
static bool zip_reader_init_vfsfile(mz_zip_archive *pZip, File *vf, mz_uint32 flags)
{
mz_uint64 file_size = vf->size();
if(!file_size || !vf->open("rb"))
{
vf->close();
return false;
}
pZip->m_pRead = zip_read_func;
pZip->m_pIO_opaque = vf;
if (!mz_zip_reader_init(pZip, file_size, flags))
{
vf->close();
return false;
}
return true;
}
static bool zip_reader_reopen_vfsfile(mz_zip_archive *pZip, mz_uint32 flags)
{
if(!(pZip && pZip->m_pIO_opaque && pZip->m_pRead))
return false;
File *vf = (File*)pZip->m_pIO_opaque;
mz_uint64 file_size = vf->size();
if(!file_size)
{
vf->close();
return false;
}
if(!vf->isopen())
if(!vf->open("rb"))
return false;
if(pZip->m_zip_mode == MZ_ZIP_MODE_READING)
return true;
if (!mz_zip_reader_init(pZip, file_size, flags))
{
vf->close();
return false;
}
return true;
}
ZipArchiveRef::ZipArchiveRef(File *file)
: archiveFile(file)
{
mz = new mz_zip_archive;
memset(mz, 0, sizeof(mz_zip_archive));
}
#define MZ ((mz_zip_archive*)mz)
ZipArchiveRef::~ZipArchiveRef()
{
close();
delete MZ;
}
bool ZipArchiveRef::init()
{
return zip_reader_init_vfsfile(MZ, archiveFile, 0);
}
bool ZipArchiveRef::openRead()
{
return zip_reader_reopen_vfsfile(MZ, 0);
}
void ZipArchiveRef::close()
{
switch(MZ->m_zip_mode)
{
case MZ_ZIP_MODE_READING:
mz_zip_reader_end(MZ);
break;
case MZ_ZIP_MODE_WRITING:
mz_zip_writer_finalize_archive(MZ);
mz_zip_writer_end(MZ);
break;
case MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED:
mz_zip_writer_end(MZ);
break;
case MZ_ZIP_MODE_INVALID:
break; // nothing to do
}
archiveFile->close();
}
const char *ZipArchiveRef::fullname() const
{
return archiveFile->fullname();
}
VFS_NAMESPACE_END

View file

@ -0,0 +1,28 @@
#ifndef VFS_ZIP_ARCHIVE_REF
#define VFS_ZIP_ARCHIVE_REF
#include "VFSFile.h"
VFS_NAMESPACE_START
class ZipArchiveRef : public Refcounted
{
public:
ZipArchiveRef(File *archive);
~ZipArchiveRef();
bool openRead();
void close();
bool init();
void *mz;
const char *fullname() const;
protected:
CountedPtr<File> archiveFile;
};
VFS_NAMESPACE_END
#endif