mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-10-03 21:01:14 +00:00
added partial VFS support - enough to read static data from any source
This commit is contained in:
parent
0951283b31
commit
fa3e9e7329
56 changed files with 4021 additions and 606 deletions
64
ExternalLibs/ttvfs/CMakeLists.txt
Normal file
64
ExternalLibs/ttvfs/CMakeLists.txt
Normal file
|
@ -0,0 +1,64 @@
|
|||
|
||||
option(TTVFS_LARGEFILE_SUPPORT "Enable support for files > 4 GB? (experimental!)" FALSE)
|
||||
option(TTVFS_IGNORE_CASE "Enable full case-insensitivity even on case-sensitive OSes like Linux and alike?" FALSE)
|
||||
|
||||
# Be sure to copy this part to your root CMakeLists.txt if you prefer to use CMake for configuring
|
||||
# instead of editing the headers directly!
|
||||
# If you edit the headers, this is not necessary.
|
||||
if(TTVFS_LARGEFILE_SUPPORT)
|
||||
add_definitions("-DVFS_LARGEFILE_SUPPORT")
|
||||
endif()
|
||||
if(TTVFS_IGNORE_CASE)
|
||||
add_definitions("-DVFS_IGNORE_CASE")
|
||||
endif()
|
||||
# --snip--
|
||||
|
||||
|
||||
# compiler specific things
|
||||
if(MSVC)
|
||||
# MSVC builds require installed runtime library by default
|
||||
option(TTVFS_STATIC_LIB "Link as static library without runtime dependencies (Note: To get rid of this setting with MSVC, the cmake cache must be cleared)" FALSE)
|
||||
add_definitions("/GR-") # run-time type info (RTTI) not required
|
||||
|
||||
if(TTVFS_STATIC_LIB)
|
||||
# this is ugly - hackfix compiler flags
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if(${flag_var} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endif(${flag_var} MATCHES "/MD")
|
||||
if(${flag_var} MATCHES "/MDd")
|
||||
string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}")
|
||||
endif(${flag_var} MATCHES "/MDd")
|
||||
endforeach()
|
||||
|
||||
# hackfix linker flags - no idea why, but MSVC will produce linker errors otherwise
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:msvcrt.lib,msvcrtd.lib") # not sure if this is correct
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:msvcrt.lib,msvcrtd.lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
set(ttvfs_SRC
|
||||
VFS.h
|
||||
VFSAtomic.cpp
|
||||
VFSAtomic.h
|
||||
VFSDefines.h
|
||||
VFSDir.cpp
|
||||
VFSDir.h
|
||||
VFSFile.cpp
|
||||
VFSFile.h
|
||||
VFSHelper.cpp
|
||||
VFSHelper.h
|
||||
VFSInternal.h
|
||||
VFSLoader.cpp
|
||||
VFSLoader.h
|
||||
VFSSelfRefCounter.h
|
||||
VFSTools.cpp
|
||||
VFSTools.h
|
||||
)
|
||||
|
||||
set(TTVFS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "ttvfs include directory - for external includers" FORCE)
|
||||
set(TTVFS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "ttvfs source directory - for external includers" FORCE)
|
||||
|
||||
add_library(ttvfs ${ttvfs_SRC})
|
78
ExternalLibs/ttvfs/VFS.h
Normal file
78
ExternalLibs/ttvfs/VFS.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* ttvfs -- tiny tree virtual file system
|
||||
|
||||
// VFS.h - all the necessary includes to get a basic VFS working
|
||||
// Only include externally, not inside the library.
|
||||
|
||||
See VFSDefines.h for compile configration.
|
||||
|
||||
|
||||
---------[ License ]----------
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TTVFS_VFS_H
|
||||
#define TTVFS_VFS_H
|
||||
|
||||
#include "VFSDefines.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
bool _checkCompatInternal(bool large, bool nocase, unsigned int vfspos_size);
|
||||
|
||||
/** 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)
|
||||
{
|
||||
#ifdef VFS_LARGEFILE_SUPPORT
|
||||
bool largefile = true;
|
||||
#else
|
||||
bool largefile = false;
|
||||
#endif
|
||||
|
||||
#ifdef VFS_IGNORE_CASE
|
||||
bool nocase = true;
|
||||
#else
|
||||
bool nocase = false;
|
||||
#endif
|
||||
return _checkCompatInternal(largefile, nocase, sizeof(vfspos));
|
||||
}
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "VFSHelper.h"
|
||||
#include "VFSFile.h"
|
||||
#include "VFSDir.h"
|
||||
|
||||
|
||||
// Checks to enforce correct including.
|
||||
// At least on windows, <string> includes <cstdio>,
|
||||
// but that must be included after "VFSInternal.h",
|
||||
// and "VFSInternal.h" may only be used inside the library (or by extensions),
|
||||
// because it redefines fseek and ftell, which would
|
||||
// mess up the ABI if included elsewhere.
|
||||
#ifdef VFS_INTERNAL_H
|
||||
#error Oops, VFS_INTERNAL_H is defined, someone messed up and included VFSInternal.h wrongly.
|
||||
#endif
|
||||
|
||||
#endif
|
103
ExternalLibs/ttvfs/VFSAtomic.cpp
Normal file
103
ExternalLibs/ttvfs/VFSAtomic.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
// 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
|
39
ExternalLibs/ttvfs/VFSAtomic.h
Normal file
39
ExternalLibs/ttvfs/VFSAtomic.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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
|
79
ExternalLibs/ttvfs/VFSDefines.h
Normal file
79
ExternalLibs/ttvfs/VFSDefines.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
// VFSDefines.h - compile config and basic setup
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#ifndef VFS_DEFINES_H
|
||||
#define VFS_DEFINES_H
|
||||
|
||||
/* --- 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 this to make all operations case insensitive.
|
||||
// Windows systems generally don't care much, but for Linux and Mac this can be used
|
||||
// to get the same behavior as on windows.
|
||||
// Additionally, this achieves full case insensitivity within the library,
|
||||
// if the the same files are accessed multiple times by the program, but with not-uniform case.
|
||||
// (no sane programmer should do this, anyway).
|
||||
// However, on non-windows systems this will decrease performance when checking for files
|
||||
// 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!
|
||||
//#define VFS_THREADSAFE
|
||||
|
||||
|
||||
/* --- 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
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
#ifdef VFS_LARGEFILE_SUPPORT
|
||||
# if defined(_MSC_VER)
|
||||
typedef __int64 vfspos;
|
||||
# else
|
||||
typedef long long 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)
|
||||
# define VFS_STRICMP stricmp
|
||||
#else
|
||||
# define VFS_STRICMP strcasecmp
|
||||
#endif
|
||||
|
||||
static const vfspos npos = vfspos(-1);
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
#endif
|
255
ExternalLibs/ttvfs/VFSDir.cpp
Normal file
255
ExternalLibs/ttvfs/VFSDir.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
// VFSDir.cpp - basic directory interface + classes
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#include "VFSInternal.h"
|
||||
#include "VFSTools.h"
|
||||
#include "VFSFile.h"
|
||||
#include "VFSDir.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
VFSDir::VFSDir()
|
||||
: ref(this), _name(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
VFSDir::VFSDir(const char *fullpath)
|
||||
: ref(this)
|
||||
{
|
||||
_setFullName(fullpath);
|
||||
}
|
||||
|
||||
VFSDir::~VFSDir()
|
||||
{
|
||||
for(Files::iterator it = _files.begin(); it != _files.end(); it++)
|
||||
it->second->ref--;
|
||||
for(Dirs::iterator it = _subdirs.begin(); it != _subdirs.end(); it++)
|
||||
it->second->ref--;
|
||||
}
|
||||
|
||||
void VFSDir::_setFullName(const char *fullname)
|
||||
{
|
||||
_fullname = FixPath(fullname);
|
||||
_name = PathToFileName(_fullname.c_str());
|
||||
}
|
||||
|
||||
VFSDir *VFSDir::createNew(void) const
|
||||
{
|
||||
return new VFSDir;
|
||||
}
|
||||
|
||||
unsigned int VFSDir::load(const char *dir /* = NULL */)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool VFSDir::add(VFSFile *f, bool overwrite /* = true */)
|
||||
{
|
||||
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;
|
||||
if(oldf == f)
|
||||
return false;
|
||||
|
||||
oldf->ref--;
|
||||
_files.erase(it);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
f->ref++;
|
||||
_files[f->name()] = f;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VFSDir::addRecursive(VFSFile *f, bool overwrite /* = true */)
|
||||
{
|
||||
if(!f)
|
||||
return false;
|
||||
|
||||
VFS_GUARD_OPT(this);
|
||||
|
||||
// figure out directory from full file name
|
||||
std::string dirname(f->fullname());
|
||||
size_t pathend = dirname.find_last_of("/\\");
|
||||
VFSDir *vdir;
|
||||
if(pathend != std::string::npos)
|
||||
{
|
||||
dirname = dirname.substr(0, pathend);
|
||||
vdir = getDir(dirname.c_str(), true);
|
||||
}
|
||||
else
|
||||
vdir = this;
|
||||
|
||||
return vdir->add(f, true);
|
||||
}
|
||||
|
||||
bool VFSDir::merge(VFSDir *dir, bool overwrite /* = true */)
|
||||
{
|
||||
if(!dir)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
VFS_GUARD_OPT(this);
|
||||
|
||||
for(Files::iterator it = dir->_files.begin(); it != dir->_files.end(); it++)
|
||||
result = add(it->second, overwrite) || result;
|
||||
|
||||
for(Dirs::iterator it = dir->_subdirs.begin(); it != dir->_subdirs.end(); it++)
|
||||
result = insert(it->second, overwrite) || result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VFSDir::insert(VFSDir *subdir, bool overwrite /* = true */)
|
||||
{
|
||||
if(!subdir)
|
||||
return false;
|
||||
|
||||
VFS_GUARD_OPT(this);
|
||||
Dirs::iterator it = _subdirs.find(subdir->name());
|
||||
VFSDir *mydir;
|
||||
if(it != _subdirs.end())
|
||||
{
|
||||
mydir = it->second;
|
||||
//return it->second->merge(subdir, overwrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new subtree, not to pollute the original one with data that may be added later
|
||||
mydir = subdir->createNew(); // create subdir of same type
|
||||
mydir->_setFullName(subdir->fullname());
|
||||
_subdirs[mydir->name()] = mydir;
|
||||
}
|
||||
|
||||
return mydir->merge(subdir, overwrite);
|
||||
}
|
||||
|
||||
VFSFile *VFSDir::getFile(const char *fn)
|
||||
{
|
||||
char *slashpos = (char *)strchr(fn, '/');
|
||||
|
||||
// if there is a '/' in the string, descend into subdir and continue there
|
||||
if(slashpos)
|
||||
{
|
||||
const char *sub = slashpos + 1;
|
||||
std::string t(fn, slashpos - fn);
|
||||
VFS_GUARD_OPT(this);
|
||||
VFSDir *subdir = getDir(t.c_str()); // fn is null-terminated early here
|
||||
return subdir ? subdir->getFile(sub) : NULL;
|
||||
}
|
||||
|
||||
// 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 : NULL;
|
||||
}
|
||||
|
||||
VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */)
|
||||
{
|
||||
if(!subdir[0] || (subdir[0] == '.' && (!subdir[1] || subdir[1] == '/'))) // empty string or "." or "./" ? use this.
|
||||
return this;
|
||||
|
||||
VFSDir *ret = NULL;
|
||||
char *slashpos = (char *)strchr(subdir, '/');
|
||||
|
||||
// if there is a '/' in the string, descend into subdir and continue there
|
||||
if(slashpos)
|
||||
{
|
||||
const char *sub = slashpos + 1;
|
||||
std::string t(subdir, slashpos - subdir);
|
||||
VFS_GUARD_OPT(this);
|
||||
Dirs::iterator it = _subdirs.find(t);
|
||||
if(it != _subdirs.end())
|
||||
{
|
||||
ret = it->second->getDir(sub, forceCreate); // descend into subdirs
|
||||
}
|
||||
else if(forceCreate)
|
||||
{
|
||||
VFSDir *ins = createNew();
|
||||
std::string newname(fullname());
|
||||
newname += '/';
|
||||
newname += t;
|
||||
ins->_setFullName(newname.c_str());
|
||||
_subdirs[ins->name()] = ins;
|
||||
ret = ins->getDir(sub, true); // create remaining structure
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
Dirs::iterator it = _subdirs.find(subdir);
|
||||
if(it != _subdirs.end())
|
||||
ret = it->second;
|
||||
else if(forceCreate)
|
||||
{
|
||||
ret = createNew();
|
||||
std::string newname(fullname());
|
||||
newname += '/';
|
||||
newname += subdir;
|
||||
ret->_setFullName(newname.c_str());
|
||||
_subdirs[ret->name()] = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----- VFSDirReal start here -----
|
||||
|
||||
|
||||
VFSDirReal::VFSDirReal() : VFSDir()
|
||||
{
|
||||
}
|
||||
|
||||
VFSDir *VFSDirReal::createNew(void) const
|
||||
{
|
||||
return new VFSDirReal;
|
||||
}
|
||||
|
||||
unsigned int VFSDirReal::load(const char *dir /* = NULL */)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(dir)
|
||||
_setFullName(dir);
|
||||
|
||||
StringList li;
|
||||
GetFileList(_fullname.c_str(), li);
|
||||
for(StringList::iterator it = li.begin(); it != li.end(); it++)
|
||||
{
|
||||
if(VFSFile *oldf = getFile(it->c_str()))
|
||||
oldf->ref--;
|
||||
VFSFileReal *f = new VFSFileReal((_fullname + '/' + *it).c_str());
|
||||
_files[f->name()] = f;
|
||||
}
|
||||
unsigned int sum = li.size();
|
||||
|
||||
li.clear();
|
||||
GetDirList(_fullname.c_str(), li, false);
|
||||
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); it++)
|
||||
{
|
||||
if(VFSDir *oldd = getDir(it->c_str()))
|
||||
oldd->ref--;
|
||||
VFSDir *d = createNew();
|
||||
std::string full(_fullname);
|
||||
full += '/';
|
||||
full += *it;
|
||||
sum += d->load(full.c_str()); // GetDirList() always returns relative paths
|
||||
_subdirs[d->name()] = d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
VFS_NAMESPACE_END
|
118
ExternalLibs/ttvfs/VFSDir.h
Normal file
118
ExternalLibs/ttvfs/VFSDir.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
// VFSDir.h - basic directory interface + classes
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#ifndef VFSDIR_H
|
||||
#define VFSDIR_H
|
||||
|
||||
#include "VFSDefines.h"
|
||||
#include <map>
|
||||
#include "VFSSelfRefCounter.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
#ifdef VFS_IGNORE_CASE
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
# endif
|
||||
|
||||
struct ci_less
|
||||
{
|
||||
inline bool operator() (const std::string& a, const std::string& b) const
|
||||
{
|
||||
return VFS_STRICMP(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
# ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
class VFSDir;
|
||||
class VFSFile;
|
||||
|
||||
class VFSDir
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef VFS_IGNORE_CASE
|
||||
typedef std::map<std::string, VFSDir*, ci_less> Dirs;
|
||||
typedef std::map<std::string, VFSFile*, ci_less> Files;
|
||||
#else
|
||||
typedef std::map<std::string, VFSDir*> Dirs;
|
||||
typedef std::map<std::string, VFSFile*> Files;
|
||||
#endif
|
||||
|
||||
VFSDir();
|
||||
VFSDir(const char *fullpath);
|
||||
virtual ~VFSDir();
|
||||
|
||||
/* Load directory with given path. If dir is NULL, reload previously loaded directory.
|
||||
If there is no previously loaded directory, load root. */
|
||||
virtual unsigned int load(const char *dir = NULL);
|
||||
virtual VFSFile *getFile(const char *fn);
|
||||
virtual VFSDir *getDir(const char *subdir, bool forceCreate = false);
|
||||
virtual VFSDir *createNew(void) const;
|
||||
virtual const char *getType(void) const { return "VFSDir"; }
|
||||
|
||||
bool insert(VFSDir *subdir, bool overwrite = true);
|
||||
bool merge(VFSDir *dir, bool overwrite = true);
|
||||
bool add(VFSFile *f, bool overwrite = true); // add file directly in this dir
|
||||
bool addRecursive(VFSFile *f, bool overwrite = true); // traverse subdir tree to find correct subdir; create if not existing
|
||||
|
||||
|
||||
inline const char *name() const { VFS_GUARD_OPT(this); return _name; }
|
||||
inline const char *fullname() const { VFS_GUARD_OPT(this); return _fullname.c_str(); }
|
||||
|
||||
// iterators are NOT thread-safe! If you need to iterate over things in a multithreaded environment,
|
||||
// do the locking yourself! (see below)
|
||||
inline Files::iterator fileIter() { return _files.begin(); }
|
||||
inline Files::iterator fileIterEnd() { return _files.end(); }
|
||||
inline Dirs::iterator dirIter() { return _subdirs.begin(); }
|
||||
inline Dirs::iterator dirIterEnd() { return _subdirs.end(); }
|
||||
inline Files::const_iterator fileIter() const { return _files.begin(); }
|
||||
inline Files::const_iterator fileIterEnd() const { return _files.end(); }
|
||||
inline Dirs::const_iterator dirIter() const { return _subdirs.begin(); }
|
||||
inline Dirs::const_iterator dirIterEnd() const { return _subdirs.end(); }
|
||||
|
||||
// std::map<std::string,*> stores for files and subdirs
|
||||
Files _files;
|
||||
Dirs _subdirs;
|
||||
|
||||
// reference counter, does auto-delete holder when it reaches 0. initially 1.
|
||||
SelfRefCounter<VFSDir> ref;
|
||||
|
||||
// the following functions should be used before and after an iteration finishes
|
||||
// alternatively, VFS_GUARD(dir) can be used to create a locking guard on the stack.
|
||||
inline void lock() { _mtx.Lock(); }
|
||||
inline void unlock() { _mtx.Unlock(); }
|
||||
inline Mutex& mutex() const { return _mtx; }
|
||||
|
||||
protected:
|
||||
void _setFullName(const char *fullname);
|
||||
std::string _fullname;
|
||||
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)
|
||||
mutable Mutex _mtx;
|
||||
};
|
||||
|
||||
typedef VFSDir::Files::iterator FileIter;
|
||||
typedef VFSDir::Dirs::iterator DirIter;
|
||||
typedef VFSDir::Files::const_iterator ConstFileIter;
|
||||
typedef VFSDir::Dirs::const_iterator ConstDirIter;
|
||||
|
||||
class VFSDirReal : public VFSDir
|
||||
{
|
||||
public:
|
||||
VFSDirReal();
|
||||
virtual ~VFSDirReal() {};
|
||||
virtual unsigned int load(const char *dir = NULL);
|
||||
virtual VFSDir *createNew(void) const;
|
||||
virtual const char *getType(void) const { return "VFSDirReal"; }
|
||||
};
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
#endif
|
258
ExternalLibs/ttvfs/VFSFile.cpp
Normal file
258
ExternalLibs/ttvfs/VFSFile.cpp
Normal file
|
@ -0,0 +1,258 @@
|
|||
// VFSFile.cpp - basic file interface + classes
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#include "VFSInternal.h"
|
||||
#include "VFSFile.h"
|
||||
#include "VFSTools.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
VFSFile::VFSFile()
|
||||
: ref(this)
|
||||
{
|
||||
}
|
||||
|
||||
VFSFileReal::VFSFileReal(const char *name /* = NULL */)
|
||||
: VFSFile()
|
||||
{
|
||||
_buf = NULL;
|
||||
_setName(name);
|
||||
_fh = NULL;
|
||||
_size = npos;
|
||||
}
|
||||
|
||||
VFSFileReal::~VFSFileReal()
|
||||
{
|
||||
close();
|
||||
dropBuf(true);
|
||||
}
|
||||
|
||||
// call this only with a lock held!
|
||||
void VFSFileReal::_setName(const char *n)
|
||||
{
|
||||
if(n && *n)
|
||||
{
|
||||
_fullname = FixPath(n);
|
||||
_name = PathToFileName(_fullname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool VFSFileReal::open(const char *fn /* = NULL */, const char *mode /* = NULL */)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
|
||||
if(isopen())
|
||||
close();
|
||||
|
||||
dropBuf(true);
|
||||
|
||||
_setName(fn);
|
||||
|
||||
_fh = fopen(_fullname.c_str(), mode ? mode : "rb");
|
||||
if(!_fh)
|
||||
return false;
|
||||
|
||||
fseek((FILE*)_fh, 0, SEEK_END);
|
||||
_size = getpos();
|
||||
fseek((FILE*)_fh, 0, SEEK_SET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VFSFileReal::isopen(void) const
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
return !!_fh;
|
||||
}
|
||||
|
||||
bool VFSFileReal::iseof(void) const
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
return !_fh || feof((FILE*)_fh);
|
||||
}
|
||||
|
||||
const char *VFSFileReal::name(void) const
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
return _name;
|
||||
}
|
||||
|
||||
const char *VFSFileReal::fullname(void) const
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
return _fullname.c_str();
|
||||
}
|
||||
|
||||
bool VFSFileReal::close(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(_fh)
|
||||
{
|
||||
fclose((FILE*)_fh);
|
||||
_fh = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VFSFileReal::seek(vfspos pos)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(!_fh)
|
||||
return false;
|
||||
fseek((FILE*)_fh, pos, SEEK_SET);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VFSFileReal::seekRel(vfspos offs)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(!_fh)
|
||||
return false;
|
||||
fseek((FILE*)_fh, offs, SEEK_CUR);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VFSFileReal::flush(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(_fh)
|
||||
return false;
|
||||
fflush((FILE*)_fh);
|
||||
return true;
|
||||
}
|
||||
|
||||
vfspos VFSFileReal::getpos(void) const
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(!_fh)
|
||||
return npos;
|
||||
return ftell((FILE*)_fh);
|
||||
}
|
||||
|
||||
unsigned int VFSFileReal::read(void *dst, unsigned int bytes)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(!_fh)
|
||||
return npos;
|
||||
return fread(dst, 1, bytes, (FILE*)_fh);
|
||||
}
|
||||
|
||||
unsigned int VFSFileReal::write(const void *src, unsigned int bytes)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(!_fh)
|
||||
return npos;
|
||||
return fwrite(src, 1, bytes, (FILE*)_fh);
|
||||
}
|
||||
|
||||
vfspos VFSFileReal::size(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(_size != npos)
|
||||
return _size;
|
||||
open();
|
||||
close();
|
||||
// now size is known.
|
||||
return _size;
|
||||
}
|
||||
|
||||
const void *VFSFileReal::getBuf(void)
|
||||
{
|
||||
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 = malloc(s + 4); // a bit extra padding
|
||||
if(!_buf)
|
||||
return NULL;
|
||||
|
||||
if(op)
|
||||
{
|
||||
vfspos oldpos = getpos();
|
||||
seek(0);
|
||||
unsigned int offs = read(_buf, s);
|
||||
memset((char*)_buf + offs, 0, 4);
|
||||
seek(oldpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int offs = read(_buf, s);
|
||||
memset((char*)_buf + offs, 0, 4);
|
||||
close();
|
||||
}
|
||||
return _buf;
|
||||
}
|
||||
|
||||
void VFSFileReal::dropBuf(bool del)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(del && _buf)
|
||||
free(_buf);
|
||||
_buf = NULL;
|
||||
}
|
||||
|
||||
// ------------- VFSFileMem -----------------------
|
||||
|
||||
VFSFileMem::VFSFileMem(const char *name, void *buf, unsigned int size, Mode mode /* = COPY */, delete_func delfunc /* = NULL */)
|
||||
: VFSFile(), _pos(0), _size(size), _buf(buf), _delfunc(delfunc), _mybuf(mode == TAKE_OVER || mode == COPY)
|
||||
{
|
||||
_setName(name);
|
||||
if(mode == COPY)
|
||||
{
|
||||
_buf = malloc(size+1);
|
||||
_delfunc = free;
|
||||
memcpy(_buf, buf, size);
|
||||
((char*)_buf)[size] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
VFSFileMem::~VFSFileMem()
|
||||
{
|
||||
if(_mybuf)
|
||||
{
|
||||
if(_delfunc)
|
||||
_delfunc(_buf);
|
||||
else
|
||||
delete [] (char*)_buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VFSFileMem::_setName(const char *n)
|
||||
{
|
||||
if(n && *n)
|
||||
{
|
||||
_fullname = FixPath(n);
|
||||
_name = PathToFileName(_fullname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
memcpy(dst, (char*)_buf + _pos, rem);
|
||||
return rem;
|
||||
}
|
||||
|
||||
unsigned int VFSFileMem::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);
|
||||
|
||||
memcpy((char*)_buf + _pos, src, rem);
|
||||
return rem;
|
||||
}
|
||||
|
||||
VFS_NAMESPACE_END
|
183
ExternalLibs/ttvfs/VFSFile.h
Normal file
183
ExternalLibs/ttvfs/VFSFile.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
// VFSFile.h - basic file interface + classes
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#ifndef VFSFILE_H
|
||||
#define VFSFILE_H
|
||||
|
||||
#include "VFSDefines.h"
|
||||
#include "VFSSelfRefCounter.h"
|
||||
#include <string>
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
|
||||
/** -- VFSFile 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.
|
||||
* (Common sense here!)
|
||||
* An int/vfspos value of 0 indicates failure, except the size/seek/getpos functions, where npos means failure.
|
||||
* Only the functions required or applicable need to be implemented, for unsupported operations
|
||||
* the default implementation should be sufficient.
|
||||
**/
|
||||
class VFSFile
|
||||
{
|
||||
public:
|
||||
|
||||
/** The ctor is expected to set both name() and fullname(). */
|
||||
VFSFile();
|
||||
|
||||
virtual ~VFSFile() {};
|
||||
|
||||
/** Open a file. If fn is NULL (the default), open fullname().
|
||||
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". */
|
||||
virtual bool open(const char *fn = NULL, const char *mode = NULL) { return false; }
|
||||
virtual bool isopen(void) const { return false; }
|
||||
virtual bool iseof(void) const { return true; }
|
||||
|
||||
/** Returns the plain file name. Never NULL. */
|
||||
virtual const char *name(void) const { return ""; }
|
||||
|
||||
/** Returns the file name with full path. Never NULL. */
|
||||
virtual const char *fullname(void) const { return ""; }
|
||||
|
||||
virtual bool close(void) { return true; }
|
||||
virtual bool seek(vfspos pos) { return false; }
|
||||
|
||||
/** 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; }
|
||||
|
||||
/** Current offset in file. Return npos if NA. */
|
||||
virtual vfspos getpos(void) const { return npos; }
|
||||
|
||||
virtual unsigned int read(void *dst, unsigned int bytes) { return 0; }
|
||||
virtual unsigned int write(const void *src, unsigned int bytes) { return 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; }
|
||||
|
||||
/** Attempt to increase file size. Returns new size after completion.
|
||||
May return any size. Failure is indicated by a size() that didn't change. */
|
||||
virtual vfspos size(vfspos newsize) { return size(); }
|
||||
|
||||
/** 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").
|
||||
Addition 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.
|
||||
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.
|
||||
This memory can be freed with free(), after calling dropBuf(false). */
|
||||
virtual const void *getBuf(void) { return NULL; }
|
||||
|
||||
/** If del is true, delete internal buffer. If false, unregister internal buffer from the file,
|
||||
but do not delete. Use free() later. */
|
||||
virtual void dropBuf(bool del) {}
|
||||
|
||||
/** Basic RTTI, for debugging purposes */
|
||||
virtual const char *getType(void) const { return "<BASE>"; }
|
||||
|
||||
|
||||
/** 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<VFSFile> ref;
|
||||
|
||||
|
||||
inline void lock() { _mtx.Lock(); }
|
||||
inline void unlock() { _mtx.Unlock(); }
|
||||
inline Mutex& mutex() const { return _mtx; }
|
||||
|
||||
protected:
|
||||
|
||||
mutable Mutex _mtx;
|
||||
};
|
||||
|
||||
class VFSFileReal : public VFSFile
|
||||
{
|
||||
public:
|
||||
VFSFileReal(const char *name = NULL);
|
||||
virtual ~VFSFileReal();
|
||||
virtual bool open(const char *fn = NULL, const char *mode = NULL);
|
||||
virtual bool isopen(void) const;
|
||||
virtual bool iseof(void) const;
|
||||
virtual const char *name(void) const;
|
||||
virtual const char *fullname(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 void *getBuf(void);
|
||||
virtual void dropBuf(bool del);
|
||||
virtual const char *getType(void) const { return "disk"; }
|
||||
|
||||
inline void *getFP() { return _fh; }
|
||||
|
||||
protected:
|
||||
void _setName(const char *n);
|
||||
std::string _fullname;
|
||||
const char *_name;
|
||||
void *_fh; // FILE*
|
||||
vfspos _size;
|
||||
void *_buf;
|
||||
};
|
||||
|
||||
class VFSFileMem : public VFSFile
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
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.
|
||||
};
|
||||
typedef void (*delete_func)(void*);
|
||||
|
||||
/* 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, delete_func delfunc = NULL);
|
||||
virtual ~VFSFileMem();
|
||||
virtual bool open(const char *fn = NULL, 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 const char *name(void) const { VFS_GUARD_OPT(this); return _name; }
|
||||
virtual const char *fullname(void) const { VFS_GUARD_OPT(this); return _fullname.c_str(); }
|
||||
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(void) { 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 *getSource(void) const { return "mem"; }
|
||||
|
||||
protected:
|
||||
void _setName(const char *n);
|
||||
std::string _fullname;
|
||||
const char *_name;
|
||||
vfspos _pos;
|
||||
vfspos _size;
|
||||
void *_buf;
|
||||
delete_func _delfunc;
|
||||
bool _mybuf;
|
||||
};
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
#endif
|
350
ExternalLibs/ttvfs/VFSHelper.cpp
Normal file
350
ExternalLibs/ttvfs/VFSHelper.cpp
Normal file
|
@ -0,0 +1,350 @@
|
|||
// 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"
|
||||
|
||||
#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, 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
|
||||
|
||||
return (large == largefile_i)
|
||||
&& (nocase == nocase_i)
|
||||
&& (sizeof(vfspos) == vfspos_size);
|
||||
}
|
||||
|
||||
VFSHelper::VFSHelper()
|
||||
: filesysRoot(NULL), merged(NULL)
|
||||
{
|
||||
_ldrDiskId = _AddFixedLoader(); // NULL intentionally. created by LoadFileSysRoot()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < preRoot.size(); ++i)
|
||||
preRoot[i]->ref--;
|
||||
preRoot.clear();
|
||||
|
||||
for(unsigned int i = 0; i < postRoot.size(); ++i)
|
||||
postRoot[i]->ref--;
|
||||
postRoot.clear();
|
||||
|
||||
for(unsigned int i = 0; i < FixedLoadersCount(); ++i)
|
||||
if(fixedLdrs[i])
|
||||
{
|
||||
delete fixedLdrs[i];
|
||||
fixedLdrs[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int VFSHelper::_AddFixedLoader(VFSLoader *ldr /* = NULL */)
|
||||
{
|
||||
fixedLdrs.push_back(ldr);
|
||||
return FixedLoadersCount() - 1;
|
||||
}
|
||||
|
||||
void VFSHelper::_cleanup(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this); // be extra safe and ensure this is locked
|
||||
if(merged)
|
||||
{
|
||||
merged->ref--;
|
||||
merged = NULL;
|
||||
}
|
||||
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it)
|
||||
it->vdir->ref--;
|
||||
vlist.clear();
|
||||
for(LoaderList::iterator it = dynLdrs.begin(); it != dynLdrs.end(); ++it)
|
||||
delete *it;
|
||||
dynLdrs.clear();
|
||||
}
|
||||
|
||||
bool VFSHelper::LoadFileSysRoot(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
VFSDirReal *oldroot = filesysRoot;
|
||||
|
||||
filesysRoot = new VFSDirReal;
|
||||
if(!filesysRoot->load("."))
|
||||
{
|
||||
filesysRoot->ref--;
|
||||
filesysRoot = oldroot;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!fixedLdrs[_ldrDiskId])
|
||||
fixedLdrs[_ldrDiskId] = new VFSLoaderDisk;
|
||||
|
||||
if(oldroot)
|
||||
oldroot->ref--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VFSHelper::Prepare(bool clear /* = true */)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(clear)
|
||||
_cleanup();
|
||||
if(!merged)
|
||||
{
|
||||
merged = new VFSDir("");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < preRoot.size(); ++i)
|
||||
merged->merge(preRoot[i]);
|
||||
if(filesysRoot)
|
||||
merged->merge(filesysRoot);
|
||||
for(size_t i = 0; i < postRoot.size(); ++i)
|
||||
merged->merge(postRoot[i]);
|
||||
}
|
||||
|
||||
void VFSHelper::Reload(bool fromDisk /* = false */)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
if(fromDisk)
|
||||
LoadFileSysRoot();
|
||||
Prepare(false);
|
||||
for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); it++)
|
||||
GetDir(it->mountPoint.c_str(), true)->merge(it->vdir, it->overwrite);
|
||||
}
|
||||
|
||||
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();
|
||||
VFSDir *sd = GetDir(subdir, true);
|
||||
if(!sd) // may be NULL if Prepare() was not called before
|
||||
return false;
|
||||
dir->ref++; // because this is to be added to vlist
|
||||
VDirEntry ve(dir, subdir, overwrite);
|
||||
_StoreMountPoint(ve);
|
||||
sd->merge(dir, overwrite); // 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;
|
||||
|
||||
Reload(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VFSHelper::_StoreMountPoint(const VDirEntry& ve)
|
||||
{
|
||||
// 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
|
||||
&& (ve.overwrite || !oe.overwrite) ) // overwrite definitely, or if other does not overwrite
|
||||
{
|
||||
it = 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)
|
||||
{
|
||||
vlist.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VFSHelper::MountExternalPath(const char *path, const char *where /* = "" */)
|
||||
{
|
||||
// no guard required here, AddVFSDir has one, and the reference count is locked as well.
|
||||
VFSDirReal *vfs = new VFSDirReal;
|
||||
if(vfs->load(path))
|
||||
AddVFSDir(vfs, where);
|
||||
return !!--(vfs->ref); // 0 if deleted
|
||||
}
|
||||
|
||||
void VFSHelper::AddLoader(VFSLoader *ldr)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
dynLdrs.push_back(ldr);
|
||||
}
|
||||
|
||||
inline static VFSFile *VFSHelper_GetFileByLoader(VFSLoader *ldr, const char *fn, VFSDir *root)
|
||||
{
|
||||
if(!ldr)
|
||||
return NULL;
|
||||
VFSFile *vf = ldr->Load(fn);
|
||||
if(vf)
|
||||
{
|
||||
root->addRecursive(vf, true);
|
||||
--(vf->ref);
|
||||
}
|
||||
return vf;
|
||||
}
|
||||
|
||||
VFSFile *VFSHelper::GetFile(const char *fn)
|
||||
{
|
||||
while(fn[0] == '.' && fn[1] == '/')
|
||||
fn += 2;
|
||||
|
||||
VFSFile *vf = NULL;
|
||||
|
||||
// guarded block
|
||||
{
|
||||
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.
|
||||
// constant, no locking required here - also a bad idea in case a loader does heavy I/O
|
||||
if(!vf)
|
||||
for(unsigned int i = 0; i < fixedLdrs.size(); ++i)
|
||||
if((vf = VFSHelper_GetFileByLoader(fixedLdrs[i], fn, GetDirRoot())))
|
||||
break;
|
||||
|
||||
if(!vf)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
for(LoaderList::iterator it = dynLdrs.begin(); it != dynLdrs.end(); ++it)
|
||||
if((vf = VFSHelper_GetFileByLoader(*it, fn, GetDirRoot())))
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("VFS: GetFile '%s' -> '%s' (%p)\n", fn, vf ? vf->fullname() : "NULL", vf);
|
||||
|
||||
return vf;
|
||||
}
|
||||
|
||||
VFSDir *VFSHelper::GetDir(const char* dn, bool create /* = false */)
|
||||
{
|
||||
while(dn[0] == '.' && dn[1] == '/')
|
||||
dn += 2;
|
||||
|
||||
VFS_GUARD_OPT(this);
|
||||
return (merged && *dn) ? merged->getDir(dn, create) : merged;
|
||||
}
|
||||
|
||||
VFSDir *VFSHelper::GetDirRoot(void)
|
||||
{
|
||||
VFS_GUARD_OPT(this);
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
// DEBUG STUFF
|
||||
|
||||
static void _DumpTreeRecursive(std::ostream& os, VFSDir *vd, const std::string& sp, VFSDir *parent)
|
||||
{
|
||||
std::string sub = sp + " ";
|
||||
|
||||
os << sp << "d|" << vd->name() << " [" << vd->getType() << ", ref " << vd->ref.count() << ", 0x" << vd << "]";
|
||||
|
||||
if(parent && strncmp(parent->fullname(), vd->fullname(), strlen(parent->fullname())))
|
||||
os << " <-- {" << vd->fullname() << "} ***********";
|
||||
os << std::endl;
|
||||
|
||||
for(DirIter it = vd->_subdirs.begin(); it != vd->_subdirs.end(); ++it)
|
||||
_DumpTreeRecursive(os, it->second, sub, vd);
|
||||
|
||||
for(FileIter it = vd->_files.begin(); it != vd->_files.end(); ++it)
|
||||
{
|
||||
VFSFile *vf = it->second;
|
||||
// only if refcount and/or mount point differs
|
||||
bool p = false;
|
||||
if(vf->ref.count() != vd->ref.count())
|
||||
{
|
||||
doprint:
|
||||
os << sub << "f|" << vf->name() << " [" << vf->getType() << ", ref " << vf->ref.count() << ", 0x" << vf << "]";
|
||||
p = true;
|
||||
}
|
||||
if(strncmp(vd->fullname(), vf->fullname(), strlen(vd->fullname())))
|
||||
{
|
||||
if(!p)
|
||||
goto doprint;
|
||||
os << " <-- {" << vf->fullname() << "} ***********";
|
||||
}
|
||||
if(p)
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void VFSHelper::debugDumpTree(std::ostream& os, VFSDir *start /* = NULL */)
|
||||
{
|
||||
_DumpTreeRecursive(os, start ? start : GetDirRoot(), "", NULL);
|
||||
}
|
||||
|
||||
|
||||
VFS_NAMESPACE_END
|
192
ExternalLibs/ttvfs/VFSHelper.h
Normal file
192
ExternalLibs/ttvfs/VFSHelper.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
// 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 <set>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "VFSAtomic.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
class VFSDir;
|
||||
class VFSDirReal;
|
||||
class VFSFile;
|
||||
class VFSLoader;
|
||||
|
||||
|
||||
/** VFSHelper - extensible class to simplify working with the VFS tree
|
||||
* Contains a set of useful functions that should be useful for anyone.
|
||||
* This class may be overridden to support adding any source in a comfortable way.
|
||||
*
|
||||
* Note: This class uses VFS_LAST_HELPER_CLASS, which should always store the last
|
||||
* class derived from VFSHelper. This is supposed to make it easy to make extensions like this:
|
||||
|
||||
#include "VFSHelperExtra.h" // defines a VFSHelperExtra class that is somehow derived from VFSHelper
|
||||
// and follows the same rules as explained below.
|
||||
|
||||
class VFSHelperArchive : public VFS_LAST_HELPER_CLASS
|
||||
{
|
||||
private:
|
||||
typedef VFS_LAST_HELPER_CLASS super;
|
||||
public:
|
||||
// .... class members ....
|
||||
};
|
||||
|
||||
#undef VFS_LAST_HELPER_CLASS
|
||||
#define VFS_LAST_HELPER_CLASS VFSHelperArchive
|
||||
|
||||
|
||||
* Used this way, only the order in which VFSHelper extension classes are included matters.
|
||||
* No code changes are required to get a nice inheritance and priority chain working.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef VFS_LAST_HELPER_CLASS
|
||||
# error VFS_LAST_HELPER_CLASS defined before VFSHelper class decl, check your include order!
|
||||
#endif
|
||||
|
||||
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 and loaders removed.
|
||||
(This is the recommended setting.) */
|
||||
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. */
|
||||
virtual void Reload(bool fromDisk = false);
|
||||
|
||||
/** Reset an instance to its initial state */
|
||||
virtual void Clear(void);
|
||||
|
||||
/** Load all files from working directory (into an internal tree) */
|
||||
bool LoadFileSysRoot(void);
|
||||
|
||||
/** 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.
|
||||
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. */
|
||||
bool MountExternalPath(const char *path, const char *where = "");
|
||||
|
||||
/** 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 a loader that can look for files on demand.
|
||||
It will be deleted if Prepare(true) is called.
|
||||
It is possible (but not a good idea) to add a loader multiple times. */
|
||||
inline void AddLoader(VFSLoader *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);
|
||||
|
||||
/** Remove a file or directory from the tree */
|
||||
//bool Remove(VFSFile *vf);
|
||||
//bool Remove(VFSDir *dir);
|
||||
//bool Remove(const char *name); // TODO: CODE ME
|
||||
|
||||
/** This depends on the class type and stays constant. */
|
||||
inline unsigned int FixedLoadersCount(void) const { return (unsigned int)fixedLdrs.size(); }
|
||||
|
||||
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 additional mount points and dynamic loaders.
|
||||
Overload to do additional cleanup if required. Invoked by Clear() and Prepare(true). */
|
||||
virtual void _cleanup(void);
|
||||
|
||||
/** Add a fixed VFSLoader. Returns its array index in fixedLdrs. */
|
||||
unsigned int _AddFixedLoader(VFSLoader *ldr = NULL);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
typedef std::list<VDirEntry> VFSMountList;
|
||||
typedef std::vector<VFSLoader*> LoaderArray;
|
||||
typedef std::deque<VFSLoader*> LoaderList;
|
||||
typedef std::vector<VFSDir*> DirArray;
|
||||
|
||||
|
||||
void _StoreMountPoint(const VDirEntry& ve);
|
||||
|
||||
bool _RemoveMountPoint(const VDirEntry& ve);
|
||||
|
||||
// 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)
|
||||
|
||||
// Additional tree stores, to be filled by subclasses if needed.
|
||||
DirArray preRoot; // VFSDirs in here will be merged in, before the actual disk files.
|
||||
// Means files on disk will overwrite existing entries.
|
||||
DirArray postRoot; // Will be merged after the disk files, and overwrite prev. merged files.
|
||||
// Both may contain NULLs.
|
||||
|
||||
// if files are not in the tree, maybe one of these is able to find it. May contain NULLs.
|
||||
LoaderArray fixedLdrs; // defined by class type, stays constant during object lifetime
|
||||
LoaderList dynLdrs; // dynamically added on demand, deleted on _cleanup()
|
||||
|
||||
VFSDir *merged; // contains the merged virtual/actual file system tree
|
||||
|
||||
mutable Mutex _mtx;
|
||||
|
||||
private:
|
||||
unsigned int _ldrDiskId;
|
||||
VFSMountList vlist; // all other dirs added later, together with path to mount to
|
||||
};
|
||||
|
||||
#undef VFS_LAST_HELPER_CLASS
|
||||
#define VFS_LAST_HELPER_CLASS VFSHelper
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
#endif
|
43
ExternalLibs/ttvfs/VFSInternal.h
Normal file
43
ExternalLibs/ttvfs/VFSInternal.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
// VFSInternal.h - misc things that are not required to be visible outside of the library.
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
// !! this file is supposed to be included ONLY from VFS*.cpp files.
|
||||
|
||||
#ifndef VFS_INTERNAL_H
|
||||
#define VFS_INTERNAL_H
|
||||
|
||||
// checks to enforcecorrect including
|
||||
#ifdef TTVFS_VFS_H
|
||||
#error Oops, TTVFS_VFS_H is defined, someone messed up and included VFS.h wrongly.
|
||||
#endif
|
||||
|
||||
#include "VFSDefines.h"
|
||||
|
||||
#if _MSC_VER
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_DEPRECATE
|
||||
# pragma warning(disable: 4355) // 'this' : used in base member initializer list
|
||||
#endif
|
||||
|
||||
// this is for POSIX - define before including any stdio headers
|
||||
#ifdef VFS_LARGEFILE_SUPPORT
|
||||
# ifndef _MSC_VER
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
// this is for MSVC - re-define functions
|
||||
#ifdef VFS_LARGEFILE_SUPPORT
|
||||
# ifdef _MSC_VER
|
||||
# define fseek _fseeki64
|
||||
# define ftell _ftelli64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
102
ExternalLibs/ttvfs/VFSLoader.cpp
Normal file
102
ExternalLibs/ttvfs/VFSLoader.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
// VFSLoader.cpp - late loading of files not in the tree
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#include "VFSInternal.h"
|
||||
#include "VFSTools.h"
|
||||
#include "VFSFile.h"
|
||||
#include "VFSLoader.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
#if !defined(_WIN32) && defined(VFS_IGNORE_CASE)
|
||||
|
||||
#include <sys/dir.h>
|
||||
|
||||
// based on code in PhysicsFS: http://icculus.org/physfs/
|
||||
static bool locateOneElement(char *buf)
|
||||
{
|
||||
char *ptr;
|
||||
DIR *dirp;
|
||||
|
||||
ptr = strrchr(buf, '/'); // find entry at end of path.
|
||||
if (ptr == NULL)
|
||||
{
|
||||
dirp = opendir(".");
|
||||
ptr = buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr = '\0';
|
||||
dirp = opendir(buf);
|
||||
*ptr = '/';
|
||||
ptr++; // point past dirsep to entry itself.
|
||||
}
|
||||
|
||||
struct dirent *dent;
|
||||
while ((dent = readdir(dirp)) != NULL)
|
||||
{
|
||||
if (strcasecmp(dent->d_name, ptr) == 0)
|
||||
{
|
||||
strcpy(ptr, dent->d_name); // found a match. Overwrite with this case.
|
||||
closedir(dirp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// no match at all...
|
||||
closedir(dirp);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool findFileHarder(char *fn)
|
||||
{
|
||||
char *ptr = fn;
|
||||
bool found = true;
|
||||
while ((ptr = strchr(ptr + 1, '/')) != 0)
|
||||
{
|
||||
*ptr = '\0';
|
||||
found = locateOneElement(fn);
|
||||
*ptr = '/'; // restore path separator
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
|
||||
// check final element...
|
||||
found = found && locateOneElement(fn);
|
||||
|
||||
printf("tt: Fixed case '%s' [%s]\n", fn, found ? "found" : "NOT FOUND"); // TEMP
|
||||
return found;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
VFSFile *VFSLoaderDisk::Load(const char *fn)
|
||||
{
|
||||
if(FileExists(fn))
|
||||
return new VFSFileReal(fn); // must contain full file name
|
||||
|
||||
#if !defined(_WIN32) && defined(VFS_IGNORE_CASE)
|
||||
size_t s = strlen(fn);
|
||||
if(s < 511) // avoid using malloc() and alloca() for short strings
|
||||
{
|
||||
char t[512];
|
||||
memcpy(&t[0], fn, s+1); // copy terminating '\0' as well
|
||||
if(findFileHarder(&t[0])) // fixes the filename on the way
|
||||
return new VFSFileReal(&t[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *t = (char*)malloc(s+1);
|
||||
VFSFileReal *vf = NULL;
|
||||
memcpy(t, fn, s+1);
|
||||
if(findFileHarder(&t[0]))
|
||||
vf = new VFSFileReal(&t[0]);
|
||||
free(t);
|
||||
return vf;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VFS_NAMESPACE_END
|
31
ExternalLibs/ttvfs/VFSLoader.h
Normal file
31
ExternalLibs/ttvfs/VFSLoader.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// VFSLoader.h - late loading of files not in the tree
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#ifndef VFSLOADER_H
|
||||
#define VFSLOADER_H
|
||||
|
||||
#include "VFSDefines.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
class VFSFile;
|
||||
|
||||
// VFSLoader - to be called if a file is not in the tree.
|
||||
class VFSLoader
|
||||
{
|
||||
public:
|
||||
virtual ~VFSLoader() {}
|
||||
virtual VFSFile *Load(const char *fn) = 0;
|
||||
};
|
||||
|
||||
class VFSLoaderDisk : public VFSLoader
|
||||
{
|
||||
public:
|
||||
virtual ~VFSLoaderDisk() {}
|
||||
virtual VFSFile *Load(const char *fn);
|
||||
};
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
|
||||
#endif
|
44
ExternalLibs/ttvfs/VFSSelfRefCounter.h
Normal file
44
ExternalLibs/ttvfs/VFSSelfRefCounter.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef SELFREFCOUNTER_H
|
||||
#define SELFREFCOUNTER_H
|
||||
|
||||
#include "VFSDefines.h"
|
||||
#include "VFSAtomic.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
// self must point to the object that holds the counter.
|
||||
template <class T, bool DELSELF = true> class SelfRefCounter
|
||||
{
|
||||
private:
|
||||
T *self;
|
||||
volatile int c;
|
||||
SelfRefCounter(SelfRefCounter& r); // forbid copy constructor
|
||||
inline unsigned int _deref(void)
|
||||
{
|
||||
unsigned int cc = (unsigned int)Atomic_Decr(c); // copy c, in case we get deleted
|
||||
if(DELSELF && !cc)
|
||||
{
|
||||
delete self;
|
||||
}
|
||||
|
||||
return cc;
|
||||
}
|
||||
|
||||
public:
|
||||
SelfRefCounter(T *p): self(p), c(1) {}
|
||||
~SelfRefCounter() { /* DEBUG(ASSERT(c <= 1)); */ } // its ok if the last reference calls delete instead of _deref()
|
||||
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; _deref(); return cc; }
|
||||
|
||||
// pre-increment
|
||||
inline unsigned int operator++(void) { return (unsigned int)Atomic_Incr(c); }
|
||||
inline unsigned int operator--(void) { return _deref(); }
|
||||
};
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
|
||||
#endif
|
427
ExternalLibs/ttvfs/VFSTools.cpp
Normal file
427
ExternalLibs/ttvfs/VFSTools.cpp
Normal file
|
@ -0,0 +1,427 @@
|
|||
// VFSTools.cpp - useful functions and misc stuff
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#include "VFSInternal.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <stack>
|
||||
#include "VFSTools.h"
|
||||
|
||||
|
||||
#if _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/dir.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
std::string stringToLower(std::string s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), tolower);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string stringToUpper(std::string s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), toupper);
|
||||
return s;
|
||||
}
|
||||
|
||||
void makeLowercase(std::string& s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), tolower);
|
||||
}
|
||||
|
||||
void makeUppercase(std::string& s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), toupper);
|
||||
}
|
||||
|
||||
// returns list of *plain* file names in given directory,
|
||||
// without paths, and without anything else
|
||||
void GetFileList(const char *path, StringList& files)
|
||||
{
|
||||
#if !_WIN32
|
||||
DIR * dirp;
|
||||
struct dirent * dp;
|
||||
dirp = opendir(path);
|
||||
if(dirp)
|
||||
{
|
||||
while((dp=readdir(dirp)) != NULL)
|
||||
{
|
||||
if (dp->d_type != DT_DIR) // only add if it is not a directory
|
||||
{
|
||||
std::string s(dp->d_name);
|
||||
files.push_back(s);
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
# else
|
||||
|
||||
WIN32_FIND_DATA fil;
|
||||
std::string search(path);
|
||||
MakeSlashTerminated(search);
|
||||
search += "*";
|
||||
HANDLE hFil = FindFirstFile(search.c_str(),&fil);
|
||||
if(hFil != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
std::string s(fil.cFileName);
|
||||
files.push_back(s);
|
||||
}
|
||||
}
|
||||
while(FindNextFile(hFil, &fil));
|
||||
|
||||
FindClose(hFil);
|
||||
}
|
||||
|
||||
# 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, bool recursive /* = false */)
|
||||
{
|
||||
#if !_WIN32
|
||||
DIR * dirp;
|
||||
struct dirent * dp;
|
||||
dirp = opendir(path);
|
||||
if(dirp)
|
||||
{
|
||||
while((dp = readdir(dirp))) // assignment is intentional
|
||||
{
|
||||
if (dp->d_type == DT_DIR) // 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 (recursive) // needing a better way to do that
|
||||
{
|
||||
std::deque<std::string> newdirs;
|
||||
GetDirList(dp->d_name, newdirs, true);
|
||||
std::string d(dp->d_name);
|
||||
for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
|
||||
dirs.push_back(d + *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::string search(path);
|
||||
MakeSlashTerminated(search);
|
||||
search += "*";
|
||||
WIN32_FIND_DATA fil;
|
||||
HANDLE hFil = FindFirstFile(search.c_str(),&fil);
|
||||
if(hFil != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
|
||||
{
|
||||
if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, ".."))
|
||||
continue;
|
||||
|
||||
std::string d(fil.cFileName);
|
||||
dirs.push_back(d);
|
||||
|
||||
if (recursive) // need a better way to do that
|
||||
{
|
||||
StringList newdirs;
|
||||
GetDirList(d.c_str(), newdirs, true);
|
||||
|
||||
for(std::deque<std::string>::iterator it = newdirs.begin(); it != newdirs.end(); ++it)
|
||||
dirs.push_back(d + *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
while(FindNextFile(hFil, &fil));
|
||||
|
||||
FindClose(hFil);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FileExists(const char *fn)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
if(fp)
|
||||
{
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return access(fn, F_OK) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// must return true if creating the directory was successful, or already exists
|
||||
bool CreateDir(const char *dir)
|
||||
{
|
||||
if(IsDirectory(dir)) // do not try to create if it already exists
|
||||
return true;
|
||||
bool result;
|
||||
# if _WIN32
|
||||
result = !!::CreateDirectory(dir, NULL);
|
||||
# else
|
||||
result = !mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CreateDirRec(const char *dir)
|
||||
{
|
||||
if(IsDirectory(dir))
|
||||
return true;
|
||||
bool result = true;
|
||||
StringList li;
|
||||
StrSplit(dir, "/\\", li, false);
|
||||
std::string d;
|
||||
d.reserve(strlen(dir));
|
||||
bool last;
|
||||
for(StringList::iterator it = li.begin(); it != li.end(); it++)
|
||||
{
|
||||
d += *it;
|
||||
last = CreateDir(d.c_str());
|
||||
result = last && result;
|
||||
d += '/';
|
||||
}
|
||||
return result || last;
|
||||
}
|
||||
|
||||
vfspos GetFileSize(const char* fn)
|
||||
{
|
||||
if(!fn || !*fn)
|
||||
return 0;
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
if(!fp)
|
||||
return 0;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
vfspos s = (vfspos)ftell(fp);
|
||||
fclose(fp);
|
||||
|
||||
return s == npos ? 0 : s;
|
||||
}
|
||||
|
||||
std::string FixSlashes(const std::string& s)
|
||||
{
|
||||
std::string r;
|
||||
r.reserve(s.length() + 1);
|
||||
char last = 0, cur;
|
||||
for(size_t i = 0; i < s.length(); ++i)
|
||||
{
|
||||
cur = s[i];
|
||||
if(cur == '\\')
|
||||
cur = '/';
|
||||
if(last == '/' && cur == '/')
|
||||
continue;
|
||||
r += cur;
|
||||
last = cur;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string FixPath(const std::string& s)
|
||||
{
|
||||
const char *p = s.c_str();
|
||||
while(p[0] == '.' && p[1] == '/')
|
||||
p += 2;
|
||||
return FixSlashes(p == s.c_str() ? s : p); // avoid hidden re-allocation when pointer was not moved
|
||||
}
|
||||
|
||||
bool IsDirectory(const char *s)
|
||||
{
|
||||
#if _WIN32
|
||||
DWORD dwFileAttr = GetFileAttributes(s);
|
||||
if(dwFileAttr == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
return !!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
if ( access( s, 0 ) == 0 )
|
||||
{
|
||||
struct stat status;
|
||||
stat( s, &status );
|
||||
return status.st_mode & S_IFDIR;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MakeSlashTerminated(std::string& s)
|
||||
{
|
||||
if(s.length() && s[s.length() - 1] != '/')
|
||||
s += '/';
|
||||
}
|
||||
|
||||
// extracts the file name from a given path
|
||||
const char *PathToFileName(const char *str)
|
||||
{
|
||||
const char *p = strrchr(str, '/');
|
||||
return p ? p+1 : str;
|
||||
}
|
||||
|
||||
std::string StripFileExtension(const 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;
|
||||
}
|
||||
|
||||
std::string StripLastPath(const std::string& s)
|
||||
{
|
||||
if(s[s.length() - 1] == '/')
|
||||
return StripLastPath(s.substr(0, s.length() - 1));
|
||||
|
||||
size_t pos = s.find_last_of('/');
|
||||
if(pos == std::string::npos)
|
||||
return ""; // nothing remains
|
||||
|
||||
return s.substr(0, pos);
|
||||
}
|
||||
|
||||
void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir /* = false */)
|
||||
{
|
||||
std::stack<std::string> stk;
|
||||
|
||||
if(withQueriedDir)
|
||||
{
|
||||
stk.push(dir);
|
||||
while(stk.size())
|
||||
{
|
||||
dir = stk.top();
|
||||
stk.pop();
|
||||
MakeSlashTerminated(dir);
|
||||
|
||||
StringList li;
|
||||
GetFileList(dir.c_str(), li);
|
||||
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
|
||||
files.push_back(dir + *it);
|
||||
|
||||
li.clear();
|
||||
GetDirList(dir.c_str(), li, true);
|
||||
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
|
||||
stk.push(dir + *it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string topdir = dir;
|
||||
MakeSlashTerminated(topdir);
|
||||
stk.push("");
|
||||
while(stk.size())
|
||||
{
|
||||
dir = stk.top();
|
||||
stk.pop();
|
||||
MakeSlashTerminated(dir);
|
||||
|
||||
StringList li;
|
||||
dir = topdir + dir;
|
||||
GetFileList(dir.c_str(), li);
|
||||
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
|
||||
files.push_back(dir + *it);
|
||||
|
||||
li.clear();
|
||||
GetDirList(dir.c_str(), li, true);
|
||||
for(std::deque<std::string>::iterator it = li.begin(); it != li.end(); ++it)
|
||||
stk.push(dir + *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15
|
||||
bool WildcardMatch(const char *str, const char *pattern)
|
||||
{
|
||||
const char *cp = 0, *mp = 0;
|
||||
while(*str && *pattern != '*')
|
||||
{
|
||||
if(*pattern != *str && *pattern != '?')
|
||||
return false;
|
||||
pattern++, str++;
|
||||
}
|
||||
|
||||
while(*str)
|
||||
{
|
||||
if(*pattern == '*')
|
||||
{
|
||||
if(!*++pattern)
|
||||
return 1;
|
||||
mp = pattern;
|
||||
cp = str + 1;
|
||||
}
|
||||
else if(*pattern == *str || *pattern == '?')
|
||||
{
|
||||
++pattern;
|
||||
++str;
|
||||
}
|
||||
else
|
||||
{
|
||||
pattern = mp;
|
||||
str = cp++;
|
||||
}
|
||||
}
|
||||
|
||||
while(*pattern++ == '*');
|
||||
|
||||
return !*pattern;
|
||||
}
|
||||
|
||||
// copy strings, mangling newlines to system standard
|
||||
// windows has 13+10
|
||||
// *nix has 10
|
||||
// exotic systems may have 10+13
|
||||
size_t strnNLcpy(char *dst, const char *src, unsigned int n /* = -1 */)
|
||||
{
|
||||
char *olddst = dst;
|
||||
bool had10 = false, had13 = false;
|
||||
|
||||
--n; // reserve 1 for \0 at end
|
||||
|
||||
while(*src && n)
|
||||
{
|
||||
if((had13 && *src == 10) || (had10 && *src == 13))
|
||||
{
|
||||
++src; // last was already mangled
|
||||
had13 = had10 = false; // processed one CRLF pair
|
||||
continue;
|
||||
}
|
||||
had10 = *src == 10;
|
||||
had13 = *src == 13;
|
||||
|
||||
if(had10 || had13)
|
||||
{
|
||||
*dst++ = '\n';
|
||||
++src;
|
||||
}
|
||||
else
|
||||
*dst++ = *src++;
|
||||
|
||||
--n;
|
||||
}
|
||||
|
||||
*dst++ = 0;
|
||||
|
||||
return dst - olddst;
|
||||
}
|
||||
|
||||
VFS_NAMESPACE_END
|
59
ExternalLibs/ttvfs/VFSTools.h
Normal file
59
ExternalLibs/ttvfs/VFSTools.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// VFSTools.h - useful functions and misc stuff
|
||||
// For conditions of distribution and use, see copyright notice in VFS.h
|
||||
|
||||
#ifndef VFS_TOOLS_H
|
||||
#define VFS_TOOLS_H
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "VFSDefines.h"
|
||||
|
||||
VFS_NAMESPACE_START
|
||||
|
||||
typedef std::deque<std::string> StringList;
|
||||
|
||||
std::string stringToUpper(const std::string& s);
|
||||
std::string stringToLower(const std::string& s);
|
||||
void makeUppercase(std::string& s);
|
||||
void makeLowercase(std::string& s);
|
||||
void GetFileList(const char *, StringList& files);
|
||||
void GetDirList(const char *, StringList& dirs, bool recursive = false);
|
||||
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);
|
||||
void MakeSlashTerminated(std::string& s);
|
||||
std::string StripFileExtension(const std::string& s);
|
||||
std::string StripLastPath(const std::string& s);
|
||||
void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir = false);
|
||||
bool WildcardMatch(const char *str, const char *pattern);
|
||||
size_t strnNLcpy(char *dst, const char *src, unsigned int n = -1);
|
||||
|
||||
template <class T> void StrSplit(const std::string &src, const std::string &sep, T& container, bool keepEmpty = false)
|
||||
{
|
||||
std::string s;
|
||||
for (std::string::const_iterator i = src.begin(); i != src.end(); i++)
|
||||
{
|
||||
if (sep.find(*i) != std::string::npos)
|
||||
{
|
||||
if (keepEmpty || s.length())
|
||||
container.push_back(s);
|
||||
s = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
s += *i;
|
||||
}
|
||||
}
|
||||
if (keepEmpty || s.length())
|
||||
container.push_back(s);
|
||||
}
|
||||
|
||||
VFS_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue