1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-15 14:09:06 +00:00
Aquaria/ExternalLibs/ttvfs/VFSTools.cpp
fgenesis 8f2279e28a Fix problems with loading saves after saving over them.
This was caused by the VFS caching file sizes,
and not noticing that the file size had changed,
which confused zlib because the data stream ended too early.
2012-06-14 17:54:40 +02:00

513 lines
12 KiB
C++

// 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 <cstdlib>
#include <algorithm>
#include <cctype>
#include <stack>
#include <cstdio>
#include "VFSTools.h"
#if _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
# include <sys/dir.h>
# include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
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);
}
#if !_WIN32
static bool _IsFile(const char *path, dirent *dp)
{
switch(dp->d_type)
{
case DT_DIR:
return false;
case DT_LNK:
{
std::string fullname = path;
fullname += '/';
fullname += dp->d_name;
struct stat statbuf;
if(stat(fullname.c_str(), &statbuf))
return false; // error
return !S_ISDIR(statbuf.st_mode);
}
// TODO: for now, we consider other file types as regular files
default:
;
}
return true;
}
static bool _IsDir(const char *path, dirent *dp)
{
switch(dp->d_type)
{
case DT_DIR:
return true;
case DT_LNK:
{
std::string fullname = path;
fullname += '/';
fullname += dp->d_name;
struct stat statbuf;
if(stat(fullname.c_str(), &statbuf))
return false; // error
return S_ISDIR(statbuf.st_mode);
}
default:
;
}
return false;
}
#endif
// 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 (_IsFile(path, dp)) // 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 (_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 (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
void *fp = real_fopen(fn, "rb");
if(fp)
{
real_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)
{
#ifdef VFS_LARGEFILE_SUPPORT
# ifdef _MSC_VER
struct _stat64 st;
if(_stat64(fn, &st))
return 0;
return st.st_size;
# else // _MSC_VER
struct stat64 st;
if(stat64(fn, &st))
return 0;
return st.st_size;
# endif
#else // VFS_LARGEFILE_SUPPORT
struct stat st;
if(stat(fn, &st))
return 0;
return st.st_size;
#endif
}
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)
{
if(s.empty())
return s;
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);
}
return FixSlashes(p);
}
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; // FIXME: what about symlinks here?
}
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.empty())
return "";
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;
}
// 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