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:
parent
209ad526c6
commit
8026cdd905
43 changed files with 2124 additions and 2427 deletions
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
169
ExternalLibs/ttvfs/VFSDirInternal.cpp
Normal file
169
ExternalLibs/ttvfs/VFSDirInternal.cpp
Normal 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
|
||||
|
55
ExternalLibs/ttvfs/VFSDirInternal.h
Normal file
55
ExternalLibs/ttvfs/VFSDirInternal.h
Normal 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
|
74
ExternalLibs/ttvfs/VFSDirView.cpp
Normal file
74
ExternalLibs/ttvfs/VFSDirView.cpp
Normal 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
|
||||
|
40
ExternalLibs/ttvfs/VFSDirView.h
Normal file
40
ExternalLibs/ttvfs/VFSDirView.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
144
ExternalLibs/ttvfs/VFSRefcounted.h
Normal file
144
ExternalLibs/ttvfs/VFSRefcounted.h
Normal 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
|
270
ExternalLibs/ttvfs/VFSRoot.cpp
Normal file
270
ExternalLibs/ttvfs/VFSRoot.cpp
Normal 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
|
124
ExternalLibs/ttvfs/VFSRoot.h
Normal file
124
ExternalLibs/ttvfs/VFSRoot.h
Normal 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
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
12
ExternalLibs/ttvfs_cfileapi/CMakeLists.txt
Normal file
12
ExternalLibs/ttvfs_cfileapi/CMakeLists.txt
Normal 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)
|
||||
|
137
ExternalLibs/ttvfs_cfileapi/ttvfs_stdio.cpp
Normal file
137
ExternalLibs/ttvfs_cfileapi/ttvfs_stdio.cpp
Normal 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;
|
||||
}
|
||||
|
86
ExternalLibs/ttvfs_cfileapi/ttvfs_stdio.h
Normal file
86
ExternalLibs/ttvfs_cfileapi/ttvfs_stdio.h
Normal 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
|
|
@ -6,6 +6,8 @@ set(ttvfs_zip_SRC
|
|||
VFSFileZip.h
|
||||
VFSZipArchiveLoader.cpp
|
||||
VFSZipArchiveLoader.h
|
||||
VFSZipArchiveRef.cpp
|
||||
VFSZipArchiveRef.h
|
||||
miniz.c
|
||||
miniz.h
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
121
ExternalLibs/ttvfs_zip/VFSZipArchiveRef.cpp
Normal file
121
ExternalLibs/ttvfs_zip/VFSZipArchiveRef.cpp
Normal 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
|
||||
|
28
ExternalLibs/ttvfs_zip/VFSZipArchiveRef.h
Normal file
28
ExternalLibs/ttvfs_zip/VFSZipArchiveRef.h
Normal 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
|
||||
|
Loading…
Add table
Reference in a new issue