metaforce/hecl/include/hecl/hecl.hpp

1483 lines
41 KiB
C++
Raw Normal View History

2015-05-20 05:22:32 +00:00
#ifndef HECL_HPP
#define HECL_HPP
2015-07-18 04:35:01 +00:00
#ifndef _WIN32
2017-12-29 07:56:31 +00:00
#include <cstdlib>
2015-05-27 09:09:05 +00:00
#include <sys/stat.h>
2015-06-11 09:41:10 +00:00
#include <sys/file.h>
2015-07-20 23:27:22 +00:00
#include <sys/ioctl.h>
2015-05-27 09:09:05 +00:00
#include <dirent.h>
2015-06-10 23:34:14 +00:00
#include <fcntl.h>
2015-07-01 23:53:05 +00:00
#include <unistd.h>
2016-01-25 02:16:40 +00:00
#include <sys/statvfs.h>
2015-07-22 19:14:50 +00:00
#else
2015-08-31 03:36:24 +00:00
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
2015-09-27 04:35:36 +00:00
#ifndef NOMINMAX
#define NOMINMAX
#endif
2015-07-22 19:14:50 +00:00
#include <Windows.h>
2017-12-29 07:56:31 +00:00
#include <cwchar>
2017-02-25 07:58:36 +00:00
#include <Shlwapi.h>
2015-08-31 03:36:24 +00:00
#include "winsupport.hpp"
2015-05-27 09:09:05 +00:00
#endif
2017-12-29 07:56:31 +00:00
#include <cinttypes>
#include <ctime>
#include <cstdarg>
#include <cstdio>
2015-05-20 05:22:32 +00:00
#include <functional>
2015-05-27 09:09:05 +00:00
#include <string>
2015-06-11 04:55:06 +00:00
#include <algorithm>
2015-05-27 09:09:05 +00:00
#include <regex>
2015-09-30 06:23:07 +00:00
#include <list>
2015-10-14 23:06:47 +00:00
#include <map>
2016-03-04 23:02:44 +00:00
#include "logvisor/logvisor.hpp"
2017-12-29 07:56:31 +00:00
#include "athena/Global.hpp"
2017-03-05 07:55:32 +00:00
#include "../extern/boo/xxhash/xxhash.h"
#include "SystemChar.hpp"
2017-12-29 07:56:31 +00:00
#include "FourCC.hpp"
2015-05-20 05:22:32 +00:00
2016-03-04 23:02:44 +00:00
namespace hecl
2015-05-20 05:22:32 +00:00
{
2015-09-30 06:23:07 +00:00
namespace Database
{
class Project;
2015-10-12 04:38:49 +00:00
struct DataSpecEntry;
2015-09-30 06:23:07 +00:00
}
2017-12-29 07:56:31 +00:00
namespace blender
{
enum class BlendType
{
None,
Mesh,
ColMesh,
Actor,
Area,
World,
MapArea,
MapUniverse,
2018-02-24 06:15:12 +00:00
Frame,
PathMesh
2017-12-29 07:56:31 +00:00
};
class Connection;
class Token;
class DataStream;
class PyOutStream;
class ANIMOutStream;
struct Mesh;
struct Material;
struct ColMesh;
struct World;
struct Light;
struct MapArea;
struct MapUniverse;
struct Actor;
struct Armature;
struct Action;
struct Bone;
2018-02-24 06:15:12 +00:00
struct PathMesh;
2017-12-29 07:56:31 +00:00
struct Matrix3f;
struct PoolSkinIndex;
extern class Token SharedBlenderToken;
}
2015-08-06 19:10:12 +00:00
extern unsigned VerbosityLevel;
extern bool GuiMode;
2016-03-04 23:02:44 +00:00
extern logvisor::Module LogModule;
2017-11-13 06:13:53 +00:00
std::string WideToUTF8(std::wstring_view src);
std::string Char16ToUTF8(std::u16string_view src);
std::wstring Char16ToWide(std::u16string_view src);
std::wstring UTF8ToWide(std::string_view src);
std::u16string UTF8ToChar16(std::string_view src);
2015-06-09 22:19:59 +00:00
/* humanize_number port from FreeBSD's libutil */
enum class HNFlags
{
None = 0,
Decimal = 0x01,
NoSpace = 0x02,
B = 0x04,
Divisor1000 = 0x08,
IECPrefixes = 0x10
};
ENABLE_BITWISE_ENUM(HNFlags)
enum class HNScale
{
None = 0,
AutoScale = 0x20
};
ENABLE_BITWISE_ENUM(HNScale)
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags);
2017-06-10 18:40:27 +00:00
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
class SystemUTF8Conv
2017-06-10 18:40:27 +00:00
{
std::string m_utf8;
public:
2017-11-13 06:13:53 +00:00
explicit SystemUTF8Conv(SystemStringView str)
2017-06-10 18:40:27 +00:00
: m_utf8(WideToUTF8(str)) {}
2017-11-13 06:13:53 +00:00
std::string_view str() const {return m_utf8;}
2017-06-10 18:40:27 +00:00
const char* c_str() const {return m_utf8.c_str();}
2017-11-14 03:34:05 +00:00
std::string operator+(std::string_view other) const {return m_utf8 + other.data();}
2017-06-10 18:40:27 +00:00
};
2017-11-14 03:34:05 +00:00
inline std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) {return std::string(lhs) + rhs.c_str();}
2017-11-13 06:13:53 +00:00
class SystemStringConv
2017-06-10 18:40:27 +00:00
{
std::wstring m_sys;
public:
2017-11-13 06:13:53 +00:00
explicit SystemStringConv(std::string_view str)
2017-06-10 18:40:27 +00:00
: m_sys(UTF8ToWide(str)) {}
2017-11-13 06:13:53 +00:00
SystemStringView sys_str() const {return m_sys;}
2017-06-10 18:40:27 +00:00
const SystemChar* c_str() const {return m_sys.c_str();}
2017-11-14 03:34:05 +00:00
std::wstring operator+(const std::wstring_view other) const {return m_sys + other.data();}
2017-06-10 18:40:27 +00:00
};
2017-11-14 03:34:05 +00:00
inline std::wstring operator+(std::wstring_view lhs, const SystemStringConv& rhs) {return std::wstring(lhs) + rhs.c_str();}
2017-06-10 18:40:27 +00:00
#else
2017-11-13 06:13:53 +00:00
class SystemUTF8Conv
2017-06-10 18:40:27 +00:00
{
2017-11-13 06:13:53 +00:00
std::string_view m_utf8;
2017-06-10 18:40:27 +00:00
public:
2017-11-13 06:13:53 +00:00
explicit SystemUTF8Conv(SystemStringView str)
2017-06-10 18:40:27 +00:00
: m_utf8(str) {}
2017-11-13 06:13:53 +00:00
std::string_view str() const {return m_utf8;}
const char* c_str() const {return m_utf8.data();}
std::string operator+(std::string_view other) const {return std::string(m_utf8) + other.data();}
2017-06-10 18:40:27 +00:00
};
2017-11-13 06:13:53 +00:00
inline std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) {return std::string(lhs) + rhs.c_str();}
class SystemStringConv
2017-06-10 18:40:27 +00:00
{
2017-11-13 06:13:53 +00:00
std::string_view m_sys;
2017-06-10 18:40:27 +00:00
public:
2017-11-13 06:13:53 +00:00
explicit SystemStringConv(std::string_view str)
2017-06-10 18:40:27 +00:00
: m_sys(str) {}
2017-11-13 06:13:53 +00:00
SystemStringView sys_str() const {return m_sys;}
const SystemChar* c_str() const {return m_sys.data();}
std::string operator+(std::string_view other) const {return std::string(m_sys) + other.data();}
2017-06-10 18:40:27 +00:00
};
2017-11-13 06:13:53 +00:00
inline std::string operator+(std::string_view lhs, const SystemStringConv& rhs) {return std::string(lhs) + rhs.c_str();}
2017-06-10 18:40:27 +00:00
#endif
2015-08-05 01:54:35 +00:00
void SanitizePath(std::string& path);
void SanitizePath(std::wstring& path);
2015-08-05 22:59:59 +00:00
static inline void Unlink(const SystemChar* file)
{
#if _WIN32
_wunlink(file);
#else
unlink(file);
#endif
}
2015-08-31 03:36:24 +00:00
static inline void MakeDir(const char* dir)
2015-06-09 22:19:59 +00:00
{
2015-05-27 09:09:05 +00:00
#if _WIN32
2015-06-09 22:19:59 +00:00
HRESULT err;
2015-08-31 03:36:24 +00:00
if (!CreateDirectoryA(dir, NULL))
2015-06-09 22:19:59 +00:00
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
2017-02-04 03:45:39 +00:00
LogModule.report(logvisor::Fatal, "MakeDir(%s)", dir);
2015-05-27 09:09:05 +00:00
#else
2015-07-28 23:54:54 +00:00
if (mkdir(dir, 0755))
2015-06-09 22:19:59 +00:00
if (errno != EEXIST)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "MakeDir(%s): %s", dir, strerror(errno));
2015-07-28 23:54:54 +00:00
#endif
}
2015-08-31 03:36:24 +00:00
#if _WIN32
static inline void MakeDir(const wchar_t* dir)
{
HRESULT err;
if (!CreateDirectoryW(dir, NULL))
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, _S("MakeDir(%s)"), dir);
2015-08-31 03:36:24 +00:00
}
#endif
2017-02-04 03:45:39 +00:00
int RecursiveMakeDir(const SystemChar* dir);
2016-03-28 21:38:31 +00:00
static inline const SystemChar* GetEnv(const SystemChar* name)
{
2017-12-06 03:22:31 +00:00
#if WINDOWS_STORE
return nullptr;
#else
2016-03-28 21:38:31 +00:00
#if HECL_UCS2
return _wgetenv(name);
#else
return getenv(name);
#endif
2017-12-06 03:22:31 +00:00
#endif
2016-03-28 21:38:31 +00:00
}
2015-06-10 02:40:03 +00:00
static inline SystemChar* Getcwd(SystemChar* buf, int maxlen)
{
#if HECL_UCS2
2015-07-22 19:14:50 +00:00
return _wgetcwd(buf, maxlen);
2015-06-10 02:40:03 +00:00
#else
return getcwd(buf, maxlen);
#endif
}
2015-12-15 21:55:50 +00:00
static SystemString GetcwdStr()
{
/* http://stackoverflow.com/a/2869667 */
2015-12-18 04:55:46 +00:00
//const size_t ChunkSize=255;
//const int MaxChunks=10240; // 2550 KiBs of current path are more than enough
2015-12-15 21:55:50 +00:00
2015-12-18 04:55:46 +00:00
SystemChar stackBuffer[255]; // Stack buffer for the "normal" case
if (Getcwd(stackBuffer, 255) != nullptr)
return SystemString(stackBuffer);
2015-12-15 21:55:50 +00:00
if (errno != ERANGE)
{
// It's not ERANGE, so we don't know how to handle it
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "Cannot determine the current path.");
2015-12-15 21:55:50 +00:00
// Of course you may choose a different error reporting method
}
// Ok, the stack buffer isn't long enough; fallback to heap allocation
2015-12-18 04:55:46 +00:00
for (int chunks=2 ; chunks<10240 ; chunks++)
2015-12-15 21:55:50 +00:00
{
// With boost use scoped_ptr; in C++0x, use unique_ptr
// If you want to be less C++ but more efficient you may want to use realloc
2015-12-18 04:55:46 +00:00
std::unique_ptr<SystemChar[]> cwd(new SystemChar[255*chunks]);
if (Getcwd(cwd.get(), 255*chunks) != nullptr)
return SystemString(cwd.get());
2015-12-15 21:55:50 +00:00
if (errno != ERANGE)
{
// It's not ERANGE, so we don't know how to handle it
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "Cannot determine the current path.");
2015-12-15 21:55:50 +00:00
// Of course you may choose a different error reporting method
}
}
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "Cannot determine the current path; the path is apparently unreasonably long");
2015-12-15 21:55:50 +00:00
return SystemString();
}
2017-11-13 06:13:53 +00:00
static inline bool IsAbsolute(SystemStringView path)
2015-12-15 21:55:50 +00:00
{
2015-12-18 04:55:46 +00:00
#if _WIN32
2015-12-15 21:55:50 +00:00
if (path.size() && (path[0] == _S('\\') || path[0] == _S('/')))
return true;
if (path.size() >= 2 && iswalpha(path[0]) && path[1] == _S(':'))
return true;
#else
if (path.size() && path[0] == _S('/'))
return true;
#endif
return false;
}
2017-02-24 08:27:07 +00:00
const SystemChar* GetTmpDir();
2017-12-06 03:22:31 +00:00
#if !WINDOWS_STORE
2017-02-24 08:27:07 +00:00
int RunProcess(const SystemChar* path, const SystemChar* const args[]);
2017-12-06 03:22:31 +00:00
#endif
2017-02-24 08:27:07 +00:00
2015-11-21 01:13:06 +00:00
enum class FileLockType
2015-06-10 23:34:14 +00:00
{
2015-11-21 01:13:06 +00:00
None = 0,
Read,
Write
2015-06-10 23:34:14 +00:00
};
2015-11-21 01:13:06 +00:00
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None)
2015-06-09 22:19:59 +00:00
{
2015-06-10 02:40:03 +00:00
#if HECL_UCS2
2015-07-22 19:14:50 +00:00
FILE* fp = _wfopen(path, mode);
if (!fp)
2016-01-05 09:51:15 +00:00
return nullptr;
2015-06-09 22:19:59 +00:00
#else
FILE* fp = fopen(path, mode);
if (!fp)
2016-01-05 09:51:15 +00:00
return nullptr;
#endif
2015-06-09 22:19:59 +00:00
2015-11-21 01:13:06 +00:00
if (lock != FileLockType::None)
2015-06-10 23:34:14 +00:00
{
#if _WIN32
OVERLAPPED ov = {};
2015-11-21 02:59:43 +00:00
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov);
2015-06-10 23:34:14 +00:00
#else
2015-11-21 01:13:06 +00:00
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Error, "flock %s: %s", path, strerror(errno));
2015-06-10 23:34:14 +00:00
#endif
}
2015-06-09 22:19:59 +00:00
return fp;
}
2016-02-04 00:55:13 +00:00
static inline int FSeek(FILE* fp, int64_t offset, int whence)
{
#if _WIN32
return _fseeki64(fp, offset, whence);
#elif __APPLE__ || __FreeBSD__
return fseeko(fp, offset, whence);
#else
return fseeko64(fp, offset, whence);
#endif
}
static inline int64_t FTell(FILE* fp)
{
#if _WIN32
return _ftelli64(fp);
#elif __APPLE__ || __FreeBSD__
return ftello(fp);
#else
return ftello64(fp);
#endif
}
2016-01-05 00:00:34 +00:00
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath)
{
#if HECL_UCS2
2017-10-30 07:29:07 +00:00
//return _wrename(oldpath, newpath);
2017-11-06 06:56:17 +00:00
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
2016-01-05 00:00:34 +00:00
#else
return rename(oldpath, newpath);
#endif
}
2015-07-22 19:14:50 +00:00
static inline int Stat(const SystemChar* path, Sstat* statOut)
2015-06-10 02:40:03 +00:00
{
#if HECL_UCS2
2016-01-02 20:39:14 +00:00
size_t pos;
for (pos=0 ; pos<3 && path[pos] != L'\0' ; ++pos) {}
if (pos == 2 && path[1] == L':')
{
SystemChar fixPath[4] = {path[0], L':', L'/', L'\0'};
return _wstat(fixPath, statOut);
}
2015-07-22 19:14:50 +00:00
return _wstat(path, statOut);
2015-06-10 02:40:03 +00:00
#else
return stat(path, statOut);
#endif
}
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 1, 2)))
#endif
2015-06-10 02:40:03 +00:00
static inline void Printf(const SystemChar* format, ...)
{
va_list va;
va_start(va, format);
#if HECL_UCS2
vwprintf(format, va);
#else
vprintf(format, va);
#endif
va_end(va);
}
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 2, 3)))
#endif
2015-06-10 02:40:03 +00:00
static inline void FPrintf(FILE* fp, const SystemChar* format, ...)
{
va_list va;
va_start(va, format);
#if HECL_UCS2
vfwprintf(fp, format, va);
#else
vfprintf(fp, format, va);
#endif
va_end(va);
}
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 3, 4)))
#endif
2015-07-16 02:03:38 +00:00
static inline void SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...)
{
va_list va;
va_start(va, format);
#if HECL_UCS2
2015-07-22 19:14:50 +00:00
_vsnwprintf(str, maxlen, format, va);
2015-07-16 02:03:38 +00:00
#else
vsnprintf(str, maxlen, format, va);
#endif
va_end(va);
}
2015-10-01 00:40:06 +00:00
static inline int StrCmp(const SystemChar* str1, const SystemChar* str2)
{
2017-12-02 05:49:45 +00:00
if (!str1 || !str2)
return str1 != str2;
2015-10-01 00:40:06 +00:00
#if HECL_UCS2
return wcscmp(str1, str2);
#else
return strcmp(str1, str2);
#endif
}
2017-10-27 10:10:08 +00:00
static inline int StrNCmp(const SystemChar* str1, const SystemChar* str2, size_t count)
{
2017-12-02 05:49:45 +00:00
if (!str1 || !str2)
return str1 != str2;
2017-10-27 10:10:08 +00:00
#if HECL_UCS2
return wcsncmp(str1, str2, count);
#else
return strncmp(str1, str2, count);
#endif
}
2016-01-23 03:38:51 +00:00
static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2)
{
2017-12-02 05:49:45 +00:00
if (!str1 || !str2)
return str1 != str2;
2016-01-23 03:38:51 +00:00
#if HECL_UCS2
return _wcsicmp(str1, str2);
#else
return strcasecmp(str1, str2);
#endif
}
2016-10-01 23:18:52 +00:00
static inline unsigned long StrToUl(const SystemChar* str, SystemChar** endPtr, int base)
{
#if HECL_UCS2
return wcstoul(str, endPtr, base);
#else
return strtoul(str, endPtr, base);
#endif
}
2015-06-24 20:56:52 +00:00
#define FORMAT_BUF_SZ 1024
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 1, 2)))
#endif
SystemString SysFormat(const SystemChar* format, ...);
2015-06-24 20:56:52 +00:00
2015-09-24 01:00:30 +00:00
#if __GNUC__
__attribute__((__format__ (__printf__, 1, 2)))
#endif
std::string Format(const char* format, ...);
2015-06-24 20:56:52 +00:00
std::wstring WideFormat(const wchar_t* format, ...);
2016-03-21 22:00:45 +00:00
2017-01-24 07:40:09 +00:00
std::u16string Char16Format(const wchar_t* format, ...);
2016-01-25 02:16:40 +00:00
static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz)
{
#if _WIN32
ULARGE_INTEGER freeBytes;
2016-01-25 05:56:33 +00:00
wchar_t buf[1024];
wchar_t* end;
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
if (!ret || ret > 1024)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, _S("GetFullPathNameW %s"), path);
2016-01-25 05:56:33 +00:00
if (end)
end[0] = L'\0';
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr))
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, _S("GetDiskFreeSpaceExW %s: %d"), path, GetLastError());
2016-01-25 05:56:33 +00:00
return reqSz < freeBytes.QuadPart;
2016-01-25 02:16:40 +00:00
#else
struct statvfs svfs;
if (statvfs(path, &svfs))
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "statvfs %s: %s", path, strerror(errno));
2016-01-26 02:02:57 +00:00
return reqSz < svfs.f_frsize * svfs.f_bavail;
2016-01-25 02:16:40 +00:00
#endif
}
2017-02-25 07:58:36 +00:00
static inline bool PathRelative(const SystemChar* path)
{
if (!path || !path[0])
return false;
2017-12-06 03:22:31 +00:00
#if _WIN32 && !WINDOWS_STORE
2017-02-25 07:58:36 +00:00
return PathIsRelative(path);
#else
return path[0] != '/';
#endif
}
2015-07-20 23:27:22 +00:00
static inline int ConsoleWidth()
{
int retval = 80;
#if _WIN32
2017-12-06 03:22:31 +00:00
#if !WINDOWS_STORE
2015-07-20 23:27:22 +00:00
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
2015-07-22 19:14:50 +00:00
retval = info.dwSize.X;
2017-12-06 03:22:31 +00:00
#endif
2015-07-20 23:27:22 +00:00
#else
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
retval = w.ws_col;
#endif
if (retval < 10)
return 10;
return retval;
}
2015-06-09 22:19:59 +00:00
typedef std::basic_regex<SystemChar> SystemRegex;
typedef std::regex_token_iterator<SystemString::const_iterator> SystemRegexTokenIterator;
typedef std::match_results<SystemString::const_iterator> SystemRegexMatch;
2015-05-27 09:09:05 +00:00
class ProjectRootPath;
2015-05-20 05:22:32 +00:00
/**
* @brief Hash representation used for all storable and comparable objects
*
* Hashes are used within HECL to avoid redundant storage of objects;
* providing a rapid mechanism to compare for equality.
*/
2015-11-13 02:12:09 +00:00
class Hash
2015-05-20 05:22:32 +00:00
{
2015-11-16 04:30:06 +00:00
protected:
uint64_t hash = 0;
2015-05-20 05:22:32 +00:00
public:
2015-11-13 02:12:09 +00:00
Hash() = default;
operator bool() const {return hash != 0;}
2015-06-09 22:19:59 +00:00
Hash(const void* buf, size_t len)
2015-09-22 01:41:38 +00:00
: hash(XXH64((uint8_t*)buf, len, 0)) {}
2017-11-13 06:13:53 +00:00
Hash(std::string_view str)
2015-09-22 01:41:38 +00:00
: hash(XXH64((uint8_t*)str.data(), str.size(), 0)) {}
2017-11-13 06:13:53 +00:00
Hash(std::wstring_view str)
2015-09-22 01:41:38 +00:00
: hash(XXH64((uint8_t*)str.data(), str.size()*2, 0)) {}
2015-11-16 04:30:06 +00:00
Hash(uint64_t hashin)
2015-05-21 02:33:05 +00:00
: hash(hashin) {}
2015-06-10 23:34:14 +00:00
Hash(const Hash& other) {hash = other.hash;}
uint32_t val32() const {return uint32_t(hash) ^ uint32_t(hash >> 32);}
2015-11-09 19:44:47 +00:00
uint64_t val64() const {return uint64_t(hash);}
size_t valSizeT() const {return size_t(hash);}
2015-09-22 01:41:38 +00:00
Hash& operator=(const Hash& other) {hash = other.hash; return *this;}
bool operator==(const Hash& other) const {return hash == other.hash;}
bool operator!=(const Hash& other) const {return hash != other.hash;}
bool operator<(const Hash& other) const {return hash < other.hash;}
bool operator>(const Hash& other) const {return hash > other.hash;}
bool operator<=(const Hash& other) const {return hash <= other.hash;}
bool operator>=(const Hash& other) const {return hash >= other.hash;}
2015-06-10 23:34:14 +00:00
};
/**
* @brief Timestamp representation used for comparing modtimes of cooked resources
*/
class Time final
{
2015-06-11 04:55:06 +00:00
time_t ts;
2015-06-10 23:34:14 +00:00
public:
Time() : ts(time(NULL)) {}
2015-06-11 04:55:06 +00:00
Time(time_t ti) : ts(ti) {}
2015-06-10 23:34:14 +00:00
Time(const Time& other) {ts = other.ts;}
2015-09-22 01:41:38 +00:00
time_t getTs() const {return ts;}
Time& operator=(const Time& other) {ts = other.ts; return *this;}
bool operator==(const Time& other) const {return ts == other.ts;}
bool operator!=(const Time& other) const {return ts != other.ts;}
bool operator<(const Time& other) const {return ts < other.ts;}
bool operator>(const Time& other) const {return ts > other.ts;}
bool operator<=(const Time& other) const {return ts <= other.ts;}
bool operator>=(const Time& other) const {return ts >= other.ts;}
2015-05-20 05:22:32 +00:00
};
/**
* @brief Case-insensitive comparator for std::map sorting
*/
struct CaseInsensitiveCompare
{
2017-11-13 06:13:53 +00:00
bool operator()(std::string_view lhs, std::string_view rhs) const
{
#if _WIN32
2017-11-13 06:13:53 +00:00
if (_stricmp(lhs.data(), rhs.data()) < 0)
#else
2017-11-13 06:13:53 +00:00
if (strcasecmp(lhs.data(), rhs.data()) < 0)
#endif
return true;
return false;
}
#if _WIN32
2017-11-13 06:13:53 +00:00
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const
{
2017-11-13 06:13:53 +00:00
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
return true;
return false;
}
#endif
};
/**
* @brief Directory traversal tool for accessing sorted directory entries
*/
class DirectoryEnumerator
{
public:
enum class Mode
{
Native,
DirsSorted,
FilesSorted,
DirsThenFilesSorted
};
struct Entry
{
2016-03-04 23:02:44 +00:00
hecl::SystemString m_path;
hecl::SystemString m_name;
size_t m_fileSz;
bool m_isDir;
private:
friend class DirectoryEnumerator;
2016-03-04 23:02:44 +00:00
Entry(hecl::SystemString&& path, const hecl::SystemChar* name, size_t sz, bool isDir)
: m_path(std::move(path)), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
};
private:
std::vector<Entry> m_entries;
public:
2017-11-13 06:13:53 +00:00
DirectoryEnumerator(SystemStringView path, Mode mode=Mode::DirsThenFilesSorted,
bool sizeSort=false, bool reverse=false, bool noHidden=false);
operator bool() const {return m_entries.size() != 0;}
size_t size() const {return m_entries.size();}
std::vector<Entry>::const_iterator begin() const {return m_entries.cbegin();}
std::vector<Entry>::const_iterator end() const {return m_entries.cend();}
};
/**
* @brief Build list of common OS-specific directories
*/
2016-03-04 23:02:44 +00:00
std::vector<std::pair<hecl::SystemString, std::string>> GetSystemLocations();
2015-09-29 21:50:07 +00:00
/**
2016-09-25 01:57:43 +00:00
* @brief Special ProjectRootPath class for opening Database::Project instances
2015-09-29 21:50:07 +00:00
*
* Constructing a ProjectPath requires supplying a ProjectRootPath to consistently
* resolve canonicalized relative paths.
*/
class ProjectRootPath
{
SystemString m_projRoot;
2015-09-30 06:23:07 +00:00
Hash m_hash = 0;
2015-09-29 21:50:07 +00:00
public:
2015-09-30 06:23:07 +00:00
/**
* @brief Empty constructor
*
* Used to preallocate ProjectPath for later population using assign()
*/
ProjectRootPath() = default;
/**
* @brief Tests for non-empty project root path
*/
operator bool() const {return m_projRoot.size() != 0;}
/**
* @brief Construct a representation of a project root path
* @param path valid filesystem-path (relative or absolute) to project root
*/
2017-11-13 06:13:53 +00:00
ProjectRootPath(SystemStringView path) : m_projRoot(path)
2015-09-30 06:23:07 +00:00
{
SanitizePath(m_projRoot);
m_hash = Hash(m_projRoot);
}
/**
* @brief Access fully-canonicalized absolute path
* @return Absolute path reference
*/
2017-11-13 06:13:53 +00:00
SystemStringView getAbsolutePath() const {return m_projRoot;}
2015-09-29 21:50:07 +00:00
2015-10-06 01:49:23 +00:00
/**
* @brief Make absolute path project relative
2015-10-07 01:16:54 +00:00
* @param absPath Absolute path
* @return SystemString of path relative to project root
2015-10-06 01:49:23 +00:00
*/
2017-11-13 06:13:53 +00:00
SystemString getProjectRelativeFromAbsolute(SystemStringView absPath) const
2015-10-06 01:49:23 +00:00
{
if (absPath.size() > m_projRoot.size())
{
2016-04-03 03:31:50 +00:00
SystemString absPathForward(absPath);
for (SystemChar& ch : absPathForward)
if (ch == _S('\\'))
ch = _S('/');
if (!absPathForward.compare(0, m_projRoot.size(), m_projRoot))
2015-10-06 01:49:23 +00:00
{
2016-04-03 03:31:50 +00:00
auto beginIt = absPathForward.cbegin() + m_projRoot.size();
while (*beginIt == _S('/'))
2015-10-06 01:49:23 +00:00
++beginIt;
2016-04-03 03:31:50 +00:00
return SystemString(beginIt, absPathForward.cend());
2015-10-06 01:49:23 +00:00
}
}
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "unable to resolve '%s' as project relative '%s'",
2017-11-13 06:13:53 +00:00
absPath.data(), m_projRoot.c_str());
2015-10-06 01:49:23 +00:00
return SystemString();
}
2015-09-29 21:50:07 +00:00
/**
* @brief Create directory at path
*
* Fatal log report is issued if directory is not able to be created or doesn't already exist.
* If directory already exists, no action taken.
*/
void makeDir() const { MakeDir(m_projRoot.c_str()); }
2015-09-30 06:23:07 +00:00
/**
* @brief HECL-specific xxhash
* @return unique hash value
*/
2015-11-09 19:44:47 +00:00
Hash hash() const {return m_hash;}
2015-09-30 06:23:07 +00:00
bool operator==(const ProjectRootPath& other) const {return m_hash == other.m_hash;}
bool operator!=(const ProjectRootPath& other) const {return m_hash != other.m_hash;}
2016-01-05 00:00:34 +00:00
/**
* @brief Obtain c-string of final path component
* @return Final component c-string (may be empty)
*/
2017-11-13 06:13:53 +00:00
SystemStringView getLastComponent() const
2016-01-05 00:00:34 +00:00
{
size_t pos = m_projRoot.rfind(_S('/'));
if (pos == SystemString::npos)
2017-11-13 06:13:53 +00:00
return {};
return {m_projRoot.c_str() + pos + 1, size_t(m_projRoot.size() - pos - 1)};
2016-01-05 00:00:34 +00:00
}
2015-09-29 21:50:07 +00:00
};
2015-05-27 09:09:05 +00:00
/**
* @brief Canonicalized project path representation using POSIX conventions
*
* HECL uses POSIX-style paths (with '/' separator) and directory tokens
* ('.','..') to resolve files within a project. The database internally
* uses this representation to track working files.
*
* This class provides a convenient way to resolve paths relative to the
* project root. Part of this representation involves resolving symbolic
* links to regular file/directory paths and determining its type.
*
* NOTE THAT PROJECT PATHS ARE TREATED AS CASE SENSITIVE!!
*/
class ProjectPath
{
2015-09-30 06:23:07 +00:00
Database::Project* m_proj = nullptr;
2015-06-09 22:19:59 +00:00
SystemString m_absPath;
2015-06-11 04:55:06 +00:00
SystemString m_relPath;
2016-04-05 01:49:42 +00:00
SystemString m_auxInfo;
2015-06-12 09:08:49 +00:00
Hash m_hash = 0;
2015-06-10 02:40:03 +00:00
#if HECL_UCS2
std::string m_utf8AbsPath;
2015-07-22 19:14:50 +00:00
std::string m_utf8RelPath;
2016-04-05 01:49:42 +00:00
std::string m_utf8AuxInfo;
2015-06-10 02:40:03 +00:00
#endif
2016-08-29 00:28:24 +00:00
void ComputeHash()
{
#if HECL_UCS2
m_utf8AbsPath = WideToUTF8(m_absPath);
m_utf8RelPath = WideToUTF8(m_relPath);
m_utf8AuxInfo = WideToUTF8(m_auxInfo);
if (m_utf8AuxInfo.size())
m_hash = Hash(m_utf8RelPath + '|' + m_utf8AuxInfo);
else
m_hash = Hash(m_utf8RelPath);
#else
if (m_auxInfo.size())
m_hash = Hash(m_relPath + '|' + m_auxInfo);
else
m_hash = Hash(m_relPath);
#endif
}
2015-05-27 09:09:05 +00:00
public:
2015-07-28 23:54:54 +00:00
/**
* @brief Empty constructor
*
* Used to preallocate ProjectPath for later population using assign()
*/
2015-09-29 21:50:07 +00:00
ProjectPath() = default;
2015-07-28 23:54:54 +00:00
/**
* @brief Tests for non-empty project path
*/
operator bool() const {return m_absPath.size() != 0;}
2015-11-09 19:44:47 +00:00
/**
* @brief Clears path
*/
void clear()
{
m_proj = nullptr;
m_absPath.clear();
m_relPath.clear();
m_hash = 0;
#if HECL_UCS2
m_utf8AbsPath.clear();
m_utf8RelPath.clear();
#endif
}
2015-09-29 21:50:07 +00:00
/**
2015-09-30 06:23:07 +00:00
* @brief Construct a project subpath representation within a project's root path
* @param project previously constructed Project to use root path of
2015-09-29 21:50:07 +00:00
* @param path valid filesystem-path (relative or absolute) to subpath
*/
2017-11-13 06:13:53 +00:00
ProjectPath(Database::Project& project, SystemStringView path) {assign(project, path);}
void assign(Database::Project& project, SystemStringView path);
2015-09-29 21:50:07 +00:00
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
ProjectPath(Database::Project& project, std::string_view path) {assign(project, path);}
void assign(Database::Project& project, std::string_view path);
2015-09-29 21:50:07 +00:00
#endif
2015-05-27 09:09:05 +00:00
/**
2015-07-16 02:03:38 +00:00
* @brief Construct a project subpath representation within another subpath
* @param parentPath previously constructed ProjectPath which ultimately connects to a ProjectRootPath
2015-05-27 09:09:05 +00:00
* @param path valid filesystem-path (relative or absolute) to subpath
*/
2017-11-13 06:13:53 +00:00
ProjectPath(const ProjectPath& parentPath, SystemStringView path) {assign(parentPath, path);}
void assign(const ProjectPath& parentPath, SystemStringView path);
2015-06-09 22:19:59 +00:00
2015-08-05 23:19:28 +00:00
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
ProjectPath(const ProjectPath& parentPath, std::string_view path) {assign(parentPath, path);}
void assign(const ProjectPath& parentPath, std::string_view path);
2015-07-22 19:14:50 +00:00
#endif
2015-05-27 09:09:05 +00:00
/**
* @brief Determine if ProjectPath represents project root directory
* @return true if project root directory
*/
2015-09-22 01:41:38 +00:00
bool isRoot() const {return m_relPath.empty();}
2015-05-27 09:09:05 +00:00
2015-08-14 03:03:00 +00:00
/**
* @brief Return new ProjectPath with extension added
2015-10-04 04:35:18 +00:00
* @param ext file extension to add (nullptr may be passed to remove the extension)
2015-10-02 04:06:45 +00:00
* @param replace remove existing extension (if any) before appending new extension
2015-08-14 03:03:00 +00:00
* @return new path with extension
*/
2015-10-02 04:06:45 +00:00
ProjectPath getWithExtension(const SystemChar* ext, bool replace=false) const
2015-08-14 03:03:00 +00:00
{
ProjectPath pp(*this);
2015-10-02 04:06:45 +00:00
if (replace)
{
auto relIt = pp.m_relPath.end();
2016-01-19 22:13:12 +00:00
if (relIt != pp.m_relPath.begin())
--relIt;
2015-10-02 04:06:45 +00:00
auto absIt = pp.m_absPath.end();
2016-01-19 22:13:12 +00:00
if (absIt != pp.m_absPath.begin())
--absIt;
2015-10-02 04:06:45 +00:00
while (relIt != pp.m_relPath.begin() && *relIt != _S('.') && *relIt != _S('/'))
{
--relIt;
--absIt;
}
2015-10-04 04:35:18 +00:00
if (*relIt == _S('.') && relIt != pp.m_relPath.begin())
2015-10-02 04:06:45 +00:00
{
pp.m_relPath.resize(relIt - pp.m_relPath.begin());
pp.m_absPath.resize(absIt - pp.m_absPath.begin());
}
}
2015-10-04 04:35:18 +00:00
if (ext)
{
pp.m_relPath += ext;
pp.m_absPath += ext;
}
2016-08-29 00:28:24 +00:00
pp.ComputeHash();
2015-08-14 03:03:00 +00:00
return pp;
}
2015-05-27 09:09:05 +00:00
/**
* @brief Access fully-canonicalized absolute path
* @return Absolute path reference
*/
2017-11-13 06:13:53 +00:00
SystemStringView getAbsolutePath() const {return m_absPath;}
2015-05-27 09:09:05 +00:00
/**
* @brief Access fully-canonicalized project-relative path
* @return Relative pointer to within absolute-path or "." for project root-directory (use isRoot to detect)
*/
2017-11-13 06:13:53 +00:00
SystemStringView getRelativePath() const
2015-05-27 09:09:05 +00:00
{
2015-06-11 04:55:06 +00:00
if (m_relPath.size())
2015-05-27 09:09:05 +00:00
return m_relPath;
2015-06-11 04:55:06 +00:00
static const SystemString dot = _S(".");
return dot;
2015-05-27 09:09:05 +00:00
}
2015-09-30 06:23:07 +00:00
/**
* @brief Obtain cooked equivalent of this ProjectPath
* @param spec DataSpec to get path against
* @return Cooked representation path
*/
ProjectPath getCookedPath(const Database::DataSpecEntry& spec) const;
/**
* @brief Obtain path of parent entity (a directory for file paths)
* @return Parent Path
*
* This will not resolve outside the project root (error in that case)
*/
ProjectPath getParentPath() const
{
if (m_relPath == _S("."))
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "attempted to resolve parent of root project path");
2015-09-30 06:23:07 +00:00
size_t pos = m_relPath.rfind(_S('/'));
if (pos == SystemString::npos)
return ProjectPath(*m_proj, _S(""));
return ProjectPath(*m_proj, SystemString(m_relPath.begin(), m_relPath.begin() + pos));
}
/**
* @brief Obtain c-string of final path component (stored within relative path)
2015-10-01 00:40:06 +00:00
* @return Final component c-string (may be empty)
2015-09-30 06:23:07 +00:00
*/
2017-11-13 06:13:53 +00:00
SystemStringView getLastComponent() const
2015-09-30 06:23:07 +00:00
{
size_t pos = m_relPath.rfind(_S('/'));
if (pos == SystemString::npos)
2017-12-02 05:49:45 +00:00
return m_relPath;
2017-11-13 06:13:53 +00:00
return {m_relPath.c_str() + pos + 1, m_relPath.size() - pos - 1};
2015-09-30 06:23:07 +00:00
}
2017-11-14 03:34:05 +00:00
std::string_view getLastComponentUTF8() const
2015-10-14 23:06:47 +00:00
{
size_t pos = m_relPath.rfind(_S('/'));
#if HECL_UCS2
if (pos == SystemString::npos)
2017-12-02 05:49:45 +00:00
return m_utf8RelPath;
2017-11-13 06:13:53 +00:00
return {m_utf8RelPath.c_str() + pos + 1, size_t(m_utf8RelPath.size() - pos - 1)};
2015-10-14 23:06:47 +00:00
#else
if (pos == SystemString::npos)
2017-12-02 05:49:45 +00:00
return m_relPath;
2017-11-13 06:13:53 +00:00
return {m_relPath.c_str() + pos + 1, size_t(m_relPath.size() - pos - 1)};
2015-10-14 23:06:47 +00:00
#endif
}
2015-09-30 06:23:07 +00:00
2015-10-01 00:40:06 +00:00
/**
* @brief Obtain c-string of extension of final path component (stored within relative path)
2017-11-13 06:13:53 +00:00
* @return Final component extension c-string (may be empty)
2015-10-01 00:40:06 +00:00
*/
2017-11-13 06:13:53 +00:00
SystemStringView getLastComponentExt() const
2015-10-01 00:40:06 +00:00
{
2017-11-13 06:13:53 +00:00
SystemStringView lastCompOrig = getLastComponent().data();
const SystemChar* end = lastCompOrig.data() + lastCompOrig.size();
const SystemChar* lastComp = end;
while (lastComp != lastCompOrig.data())
2015-10-01 00:40:06 +00:00
{
if (*lastComp == _S('.'))
2017-11-13 06:13:53 +00:00
return {lastComp + 1, size_t(end - lastComp - 1)};
2015-10-01 00:40:06 +00:00
--lastComp;
}
2017-11-13 06:13:53 +00:00
return {};
2015-10-01 00:40:06 +00:00
}
2016-01-06 21:03:41 +00:00
/**
* @brief Build vector of project-relative directory/file components
* @return Vector of path components
*/
2016-03-04 23:02:44 +00:00
std::vector<hecl::SystemString> getPathComponents() const
2016-01-06 21:03:41 +00:00
{
2016-03-04 23:02:44 +00:00
std::vector<hecl::SystemString> ret;
2016-01-06 21:03:41 +00:00
if (m_relPath.empty())
return ret;
auto it = m_relPath.cbegin();
if (*it == _S('/'))
{
ret.push_back(_S("/"));
++it;
}
2016-03-04 23:02:44 +00:00
hecl::SystemString comp;
2016-01-06 21:03:41 +00:00
for (; it != m_relPath.cend() ; ++it)
{
if (*it == _S('/'))
{
if (comp.empty())
continue;
ret.push_back(std::move(comp));
comp.clear();
continue;
}
comp += *it;
}
if (comp.size())
ret.push_back(std::move(comp));
return ret;
}
/**
* @brief Build vector of project-relative directory/file components
* @return Vector of path components encoded as UTF8
*/
std::vector<std::string> getPathComponentsUTF8() const
{
#if HECL_UCS2
const std::string& relPath = m_utf8RelPath;
#else
const std::string& relPath = m_relPath;
#endif
std::vector<std::string> ret;
if (relPath.empty())
return ret;
auto it = relPath.cbegin();
if (*it == '/')
{
2016-01-08 00:50:44 +00:00
ret.push_back("/");
2016-01-06 21:03:41 +00:00
++it;
}
std::string comp;
for (; it != relPath.cend() ; ++it)
{
2016-01-08 00:50:44 +00:00
if (*it == '/')
2016-01-06 21:03:41 +00:00
{
if (comp.empty())
continue;
ret.push_back(std::move(comp));
comp.clear();
continue;
}
comp += *it;
}
if (comp.size())
ret.push_back(std::move(comp));
return ret;
}
2015-06-10 02:40:03 +00:00
/**
* @brief Access fully-canonicalized absolute path in UTF-8
* @return Absolute path reference
*/
2017-11-13 06:13:53 +00:00
std::string_view getAbsolutePathUTF8() const
2015-06-10 02:40:03 +00:00
{
#if HECL_UCS2
return m_utf8AbsPath;
#else
return m_absPath;
#endif
}
2017-11-13 06:13:53 +00:00
std::string_view getRelativePathUTF8() const
2015-06-10 02:40:03 +00:00
{
#if HECL_UCS2
return m_utf8RelPath;
#else
return m_relPath;
#endif
}
2017-11-13 06:13:53 +00:00
SystemStringView getAuxInfo() const
2016-04-05 01:49:42 +00:00
{
return m_auxInfo;
}
2017-11-13 06:13:53 +00:00
std::string_view getAuxInfoUTF8() const
2016-04-05 01:49:42 +00:00
{
#if HECL_UCS2
return m_utf8AuxInfo;
#else
return m_auxInfo;
#endif
}
2016-04-06 01:43:16 +00:00
/**
* @brief Construct a path with the aux info overwritten with specified string
* @param auxStr string to replace existing auxInfo with
*/
2017-11-13 06:13:53 +00:00
ProjectPath ensureAuxInfo(SystemStringView auxStr) const
2016-04-06 01:43:16 +00:00
{
2017-11-13 06:13:53 +00:00
return ProjectPath(getProject(), SystemString(getRelativePath()) + _S('|') + auxStr.data());
}
2016-09-19 01:02:57 +00:00
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
ProjectPath ensureAuxInfo(std::string_view auxStr) const
2016-09-19 01:02:57 +00:00
{
2017-11-13 06:13:53 +00:00
return ProjectPath(getProject(), SystemString(getRelativePath()) + _S('|') + UTF8ToWide(auxStr));
2016-09-19 01:02:57 +00:00
}
#endif
2015-05-27 09:09:05 +00:00
/**
* @brief Type of path
*/
2015-11-21 01:13:06 +00:00
enum class Type
2015-05-27 09:09:05 +00:00
{
2015-11-21 01:13:06 +00:00
None, /**< If path doesn't reference a valid filesystem entity, this is returned */
File, /**< Singular file path (confirmed with filesystem) */
Directory, /**< Singular directory path (confirmed with filesystem) */
Glob, /**< Glob-path (whenever one or more '*' occurs in syntax) */
2015-05-27 09:09:05 +00:00
};
/**
* @brief Get type of path based on syntax and filesystem queries
* @return Type of path
*/
2015-11-21 01:13:06 +00:00
Type getPathType() const;
2015-05-27 09:09:05 +00:00
/**
* @brief Test if nothing exists at path
* @return True if nothing exists at path
*/
bool isNone() const
{
return getPathType() == Type::None;
}
/**
* @brief Test if regular file exists at path
* @return True if regular file exists at path
*/
bool isFile() const
{
return getPathType() == Type::File;
}
/**
* @brief Test if directory exists at path
* @return True if directory exists at path
*/
bool isDirectory() const
{
return getPathType() == Type::Directory;
}
/**
* @brief Certain singular resource targets are cooked based on this test
* @return True if file or glob
*/
bool isFileOrGlob() const
{
Type type = getPathType();
return (type == Type::File || type == Type::Glob);
}
2015-06-10 23:34:14 +00:00
/**
* @brief Get time of last modification with special behaviors for directories and glob-paths
* @return Time object representing entity's time of last modification
*
* Regular files simply return their modtime as queried from the OS
* Directories return the latest modtime of all first-level regular files
* Glob-paths return the latest modtime of all matched regular files
*/
Time getModtime() const;
2015-09-30 06:23:07 +00:00
/**
* @brief Insert directory children into list
* @param outPaths list to append children to
*/
2015-10-14 23:06:47 +00:00
void getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const;
2015-09-30 06:23:07 +00:00
/**
* @brief Construct DirectoryEnumerator set to project path
*/
2016-03-04 23:02:44 +00:00
hecl::DirectoryEnumerator enumerateDir() const;
2015-06-09 22:19:59 +00:00
/**
* @brief Insert glob matches into existing vector
* @param outPaths Vector to add matches to (will not erase existing contents)
*/
2017-10-25 07:46:32 +00:00
void getGlobResults(std::vector<ProjectPath>& outPaths) const;
2015-06-10 23:34:14 +00:00
/**
* @brief Count how many directory levels deep in project path is
* @return Level Count
*/
2015-09-22 01:41:38 +00:00
size_t levelCount() const
{
size_t count = 0;
for (SystemChar ch : m_relPath)
if (ch == _S('/') || ch == _S('\\'))
++count;
return count;
}
2015-07-28 23:54:54 +00:00
/**
* @brief Create directory at path
*
* Fatal log report is issued if directory is not able to be created or doesn't already exist.
* If directory already exists, no action taken.
*/
void makeDir() const { MakeDir(m_absPath.c_str()); }
2015-07-28 23:54:54 +00:00
2016-03-31 18:56:19 +00:00
/**
* @brief Create directory chain leading up to path
* @param includeLastComp if set, the ProjectPath is assumed to be a
* directory, creating the final component
*/
void makeDirChain(bool includeLastComp) const
{
std::vector<hecl::SystemString> comps = getPathComponents();
auto end = comps.cend();
if (end != comps.cbegin() && !includeLastComp)
--end;
ProjectPath compPath(*m_proj, _S("."));
for (auto it=comps.cbegin() ; it != end ; ++it)
{
compPath = ProjectPath(compPath, *it);
compPath.makeDir();
}
}
2015-10-07 01:16:54 +00:00
/**
* @brief Fetch project that contains path
* @return Project
*/
Database::Project& getProject() const
{
if (!m_proj)
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, "ProjectPath::getProject() called on unqualified path");
2015-10-07 01:16:54 +00:00
return *m_proj;
}
2015-06-10 23:34:14 +00:00
/**
2015-09-30 06:23:07 +00:00
* @brief HECL-specific xxhash
2015-06-12 09:08:49 +00:00
* @return unique hash value
2015-06-10 23:34:14 +00:00
*/
2015-11-09 19:44:47 +00:00
Hash hash() const {return m_hash;}
2015-09-22 01:41:38 +00:00
bool operator==(const ProjectPath& other) const {return m_hash == other.m_hash;}
bool operator!=(const ProjectPath& other) const {return m_hash != other.m_hash;}
2015-06-10 23:34:14 +00:00
2015-05-27 09:09:05 +00:00
};
/**
2016-08-31 23:07:40 +00:00
* @brief Handy functions not directly provided via STL strings
*/
class StringUtils
{
public:
2017-11-13 06:13:53 +00:00
static bool BeginsWith(SystemStringView str, SystemStringView test)
{
2017-11-13 06:13:53 +00:00
if (test.size() > str.size())
return false;
2017-11-13 06:13:53 +00:00
return !StrNCmp(str.data(), test.data(), test.size());
}
2017-11-13 06:13:53 +00:00
static bool EndsWith(SystemStringView str, SystemStringView test)
{
2017-11-13 06:13:53 +00:00
if (test.size() > str.size())
return false;
2017-11-13 06:13:53 +00:00
return !StrNCmp(&*(str.end() - test.size()), test.data(), test.size());
}
2017-11-13 06:13:53 +00:00
static std::string TrimWhitespace(std::string_view str)
2016-10-01 23:18:52 +00:00
{
auto bit = str.begin();
while (bit != str.cend() && isspace(*bit))
++bit;
auto eit = str.end();
while (eit != str.cbegin() && isspace(*(eit-1)))
--eit;
return {bit, eit};
}
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
static bool BeginsWith(std::string_view str, std::string_view test)
{
2017-11-13 06:13:53 +00:00
if (test.size() > str.size())
return false;
2017-11-13 06:13:53 +00:00
return !strncmp(str.data(), test.data(), test.size());
}
2017-11-13 06:13:53 +00:00
static bool EndsWith(std::string_view str, std::string_view test)
{
2017-11-13 06:13:53 +00:00
if (test.size() > str.size())
return false;
2017-11-13 06:13:53 +00:00
return !strncmp(&*(str.end() - test.size()), test.data(), test.size());
}
2016-10-01 23:18:52 +00:00
2017-11-13 06:13:53 +00:00
static SystemString TrimWhitespace(SystemStringView str)
2016-10-01 23:18:52 +00:00
{
auto bit = str.begin();
while (bit != str.cend() && iswspace(*bit))
++bit;
auto eit = str.end();
while (eit != str.cbegin() && iswspace(*(eit-1)))
--eit;
return {bit, eit};
}
#endif
};
/**
* @brief Mutex-style centralized resource-path tracking
*
* Provides a means to safely parallelize resource processing; detecting when another
* thread is working on the same resource.
*/
class ResourceLock
{
static bool SetThreadRes(const ProjectPath& path);
static void ClearThreadRes();
bool good;
public:
operator bool() const { return good; }
static bool InProgress(const ProjectPath& path);
ResourceLock(const ProjectPath& path) { good = SetThreadRes(path); }
~ResourceLock() { if (good) ClearThreadRes(); }
ResourceLock(const ResourceLock&) = delete;
ResourceLock& operator=(const ResourceLock&) = delete;
ResourceLock(ResourceLock&&) = delete;
ResourceLock& operator=(ResourceLock&&) = delete;
};
2015-06-11 04:55:06 +00:00
/**
* @brief Search from within provided directory for the project root
* @param path absolute or relative file path to search from
2015-09-30 06:23:07 +00:00
* @return Newly-constructed root path (bool-evaluating to false if not found)
*/
2017-11-13 06:13:53 +00:00
ProjectRootPath SearchForProject(SystemStringView path);
2015-09-30 06:23:07 +00:00
/**
* @brief Search from within provided directory for the project root
* @param path absolute or relative file path to search from
* @param subpathOut remainder of provided path assigned to this ProjectPath
* @return Newly-constructed root path (bool-evaluating to false if not found)
2015-06-11 04:55:06 +00:00
*/
2017-11-13 06:13:53 +00:00
ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut);
2015-05-27 09:09:05 +00:00
2015-10-01 00:40:06 +00:00
/**
* @brief Test if given path is a PNG (based on file header)
* @param path Path to test
* @return true if PNG
*/
2016-03-04 23:02:44 +00:00
bool IsPathPNG(const hecl::ProjectPath& path);
2015-10-01 00:40:06 +00:00
/**
* @brief Test if given path is a blend (based on file header)
* @param path Path to test
* @return true if blend
*/
2016-03-04 23:02:44 +00:00
bool IsPathBlend(const hecl::ProjectPath& path);
2015-10-01 00:40:06 +00:00
/**
* @brief Test if given path is a yaml (based on file extension)
* @param path Path to test
* @return true if yaml
*/
2016-03-04 23:02:44 +00:00
bool IsPathYAML(const hecl::ProjectPath& path);
2015-10-01 00:40:06 +00:00
2015-09-13 03:40:22 +00:00
#undef bswap16
#undef bswap32
#undef bswap64
2015-05-27 09:09:05 +00:00
/* Type-sensitive byte swappers */
2015-06-09 22:19:59 +00:00
template <typename T>
static inline T bswap16(T val)
2015-05-22 08:21:44 +00:00
{
#if __GNUC__
return __builtin_bswap16(val);
#elif _WIN32
return _byteswap_ushort(val);
#else
return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
2015-06-09 22:19:59 +00:00
template <typename T>
static inline T bswap32(T val)
2015-05-22 08:21:44 +00:00
{
#if __GNUC__
return __builtin_bswap32(val);
#elif _WIN32
return _byteswap_ulong(val);
#else
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
#endif
}
2015-06-09 22:19:59 +00:00
template <typename T>
static inline T bswap64(T val)
2015-05-22 08:21:44 +00:00
{
#if __GNUC__
return __builtin_bswap64(val);
#elif _WIN32
return _byteswap_uint64(val);
#else
2015-06-09 22:19:59 +00:00
return ((val & 0xFF00000000000000ULL) >> 56) |
((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) |
((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) |
((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) |
((val & 0x00000000000000FFULL) << 56);
2015-05-22 08:21:44 +00:00
#endif
}
2015-05-27 09:09:05 +00:00
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
2015-06-12 04:02:23 +00:00
static inline int16_t SBig(int16_t val) {return bswap16(val);}
static inline uint16_t SBig(uint16_t val) {return bswap16(val);}
static inline int32_t SBig(int32_t val) {return bswap32(val);}
static inline uint32_t SBig(uint32_t val) {return bswap32(val);}
static inline int64_t SBig(int64_t val) {return bswap64(val);}
static inline uint64_t SBig(uint64_t val) {return bswap64(val);}
2016-03-06 01:19:32 +00:00
static inline float SBig(float val)
{
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
}
static inline double SBig(double val)
{
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
}
2016-12-16 23:05:11 +00:00
#ifndef SBIG
2015-07-19 20:52:22 +00:00
#define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \
| ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 )
2016-12-16 23:05:11 +00:00
#endif
2015-06-12 04:02:23 +00:00
static inline int16_t SLittle(int16_t val) {return val;}
static inline uint16_t SLittle(uint16_t val) {return val;}
static inline int32_t SLittle(int32_t val) {return val;}
static inline uint32_t SLittle(uint32_t val) {return val;}
static inline int64_t SLittle(int64_t val) {return val;}
static inline uint64_t SLittle(uint64_t val) {return val;}
2016-03-06 01:19:32 +00:00
static inline float SLittle(float val) {return val;}
static inline double SLittle(double val) {return val;}
2016-12-16 23:05:11 +00:00
#ifndef SLITTLE
2015-07-19 20:52:22 +00:00
#define SLITTLE(q) (q)
2016-12-16 23:05:11 +00:00
#endif
2015-05-27 09:09:05 +00:00
#else
2015-06-12 04:02:23 +00:00
static inline int16_t SLittle(int16_t val) {return bswap16(val);}
static inline uint16_t SLittle(uint16_t val) {return bswap16(val);}
static inline int32_t SLittle(int32_t val) {return bswap32(val);}
static inline uint32_t SLittle(uint32_t val) {return bswap32(val);}
static inline int64_t SLittle(int64_t val) {return bswap64(val);}
static inline uint64_t SLittle(uint64_t val) {return bswap64(val);}
2016-03-06 01:19:32 +00:00
static inline float SLittle(float val)
{
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
}
static inline double SLittle(double val)
{
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
}
2016-12-16 23:05:11 +00:00
#ifndef SLITTLE
2015-07-19 20:52:22 +00:00
#define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \
| ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 )
2016-12-16 23:05:11 +00:00
#endif
2015-06-12 04:02:23 +00:00
static inline int16_t SBig(int16_t val) {return val;}
static inline uint16_t SBig(uint16_t val) {return val;}
static inline int32_t SBig(int32_t val) {return val;}
static inline uint32_t SBig(uint32_t val) {return val;}
static inline int64_t SBig(int64_t val) {return val;}
static inline uint64_t SBig(uint64_t val) {return val;}
2016-03-06 01:19:32 +00:00
static inline float SBig(float val) {return val;}
static inline double SBig(double val) {return val;}
2016-12-16 23:05:11 +00:00
#ifndef SBIG
2015-07-19 20:52:22 +00:00
#define SBIG(q) (q)
2015-05-27 09:09:05 +00:00
#endif
2016-12-16 23:05:11 +00:00
#endif
2015-05-27 09:09:05 +00:00
2015-05-20 05:22:32 +00:00
}
2015-06-10 23:34:14 +00:00
namespace std
{
2016-03-04 23:02:44 +00:00
template <> struct hash<hecl::ProjectPath>
2015-06-10 23:34:14 +00:00
{
2017-12-29 07:56:31 +00:00
size_t operator()(const hecl::ProjectPath& val) const noexcept
2015-11-09 19:44:47 +00:00
{return val.hash().valSizeT();}
2015-06-10 23:34:14 +00:00
};
2016-03-04 23:02:44 +00:00
template <> struct hash<hecl::Hash>
2015-11-13 02:12:09 +00:00
{
2017-12-29 07:56:31 +00:00
size_t operator()(const hecl::Hash& val) const noexcept
2015-11-13 02:12:09 +00:00
{return val.valSizeT();}
};
2015-06-10 23:34:14 +00:00
}
2015-05-20 05:22:32 +00:00
#endif // HECL_HPP