metaforce/hecl/include/HECL.hpp

529 lines
15 KiB
C++
Raw Normal View History

2015-05-20 05:22:32 +00:00
#ifndef HECL_HPP
#define HECL_HPP
2015-05-27 09:09:05 +00:00
#if _WIN32
char* win_realpath(const char* name, char* restrict resolved);
#else
#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
2015-06-10 23:34:14 +00:00
#include <fcntl.h>
2015-05-27 09:09:05 +00:00
#endif
2015-06-10 23:34:14 +00:00
#include <time.h>
2015-06-10 02:40:03 +00:00
#include <stdarg.h>
2015-06-09 22:19:59 +00:00
#include <stdio.h>
2015-05-20 05:22:32 +00:00
#include <functional>
2015-06-09 22:19:59 +00:00
#include <stdexcept>
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>
#include "../extern/blowfish/blowfish.h"
2015-05-20 05:22:32 +00:00
namespace HECL
{
2015-06-10 02:40:03 +00:00
#if _WIN32 && UNICODE
#define HECL_UCS2 1
#endif
2015-06-09 22:19:59 +00:00
std::string WideToUTF8(const std::wstring& src);
std::wstring UTF8ToWide(const std::string& src);
2015-06-10 02:40:03 +00:00
#if HECL_UCS2
2015-06-09 22:19:59 +00:00
typedef wchar_t SystemChar;
typedef std::wstring SystemString;
2015-06-11 04:55:06 +00:00
static inline void ToLower(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), towlower);}
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), towupper);}
2015-06-10 02:40:03 +00:00
class SystemUTF8View
2015-06-09 22:19:59 +00:00
{
std::string m_utf8;
public:
2015-06-10 02:40:03 +00:00
SystemUTF8View(const SystemString& str)
2015-06-09 22:19:59 +00:00
: m_utf8(WideToUTF8(str)) {}
inline const std::string& utf8_str() {return m_utf8;}
};
2015-06-10 02:40:03 +00:00
class SystemStringView
2015-06-09 22:19:59 +00:00
{
std::wstring m_sys;
public:
2015-06-10 02:40:03 +00:00
SystemStringView(const std::string& str)
2015-06-09 22:19:59 +00:00
: m_sys(UTF8ToWide(str)) {}
2015-06-10 02:40:03 +00:00
inline const std::wstring& sys_str() {return m_sys;}
2015-06-09 22:19:59 +00:00
};
#ifndef _S
#define _S(val) L ## val
#endif
#else
typedef char SystemChar;
typedef std::string SystemString;
2015-06-11 04:55:06 +00:00
static inline void ToLower(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), tolower);}
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), toupper);}
2015-06-10 02:40:03 +00:00
class SystemUTF8View
2015-06-09 22:19:59 +00:00
{
const std::string& m_utf8;
public:
2015-06-10 02:40:03 +00:00
SystemUTF8View(const SystemString& str)
2015-06-09 22:19:59 +00:00
: m_utf8(str) {}
inline const std::string& utf8_str() {return m_utf8;}
};
2015-06-10 02:40:03 +00:00
class SystemStringView
2015-06-09 22:19:59 +00:00
{
const std::string& m_sys;
public:
2015-06-10 02:40:03 +00:00
SystemStringView(const std::string& str)
2015-06-09 22:19:59 +00:00
: m_sys(str) {}
inline const std::string& sys_str() {return m_sys;}
};
#ifndef _S
#define _S(val) val
#endif
#endif
2015-06-10 02:40:03 +00:00
class Exception : public std::exception
{
SystemString m_what;
#if HECL_UCS2
std::string m_utf8what;
#endif
public:
Exception(const SystemString& what) noexcept
: m_what(what)
{
#if HECL_UCS2
m_utf8what = WideToUTF8(what);
#endif
}
const char* what() const noexcept
{
#if HECL_UCS2
return m_utf8what.c_str();
#else
return m_what.c_str();
#endif
}
inline const SystemChar* swhat() const noexcept {return m_what.c_str();}
};
2015-06-09 22:19:59 +00:00
static inline void MakeDir(const SystemString& dir)
{
2015-05-27 09:09:05 +00:00
#if _WIN32
2015-06-09 22:19:59 +00:00
HRESULT err;
if (!CreateDirectory(dir.c_str(), NULL))
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
throw std::error_code(err, std::system_category());
2015-05-27 09:09:05 +00:00
#else
2015-06-09 22:19:59 +00:00
if (mkdir(dir.c_str(), 0755))
if (errno != EEXIST)
throw std::error_code(errno, std::system_category());
2015-05-27 09:09:05 +00:00
#endif
2015-06-09 22:19:59 +00:00
}
2015-06-10 02:40:03 +00:00
static inline SystemChar* Getcwd(SystemChar* buf, int maxlen)
{
#if HECL_UCS2
return wgetcwd(buf, maxlen);
#else
return getcwd(buf, maxlen);
#endif
}
2015-06-10 23:34:14 +00:00
enum FileLockType
{
LNONE = 0,
LREAD,
LWRITE
};
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=LNONE)
2015-06-09 22:19:59 +00:00
{
2015-06-10 02:40:03 +00:00
#if HECL_UCS2
2015-06-09 22:19:59 +00:00
FILE* fp = wfopen(path, mode);
#else
FILE* fp = fopen(path, mode);
#endif
if (!fp)
throw std::error_code(errno, std::system_category());
2015-06-10 23:34:14 +00:00
if (lock)
{
#if _WIN32
HANDLE fhandle = (HANDLE)fileno(fp);
OVERLAPPED ov = {};
LockFileEx(fhandle, (lock == LWRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov);
#else
struct flock lk =
{
(short)((lock == LREAD) ? F_RDLCK : F_WRLCK),
SEEK_SET, 0, 0, 0
};
fcntl(fileno(fp), F_SETLK, &lk);
#endif
}
2015-06-09 22:19:59 +00:00
return fp;
}
2015-06-10 02:40:03 +00:00
static inline int Stat(const SystemChar* path, struct stat* statOut)
{
#if HECL_UCS2
return wstat(path, statOut);
#else
return stat(path, statOut);
#endif
}
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);
}
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-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 Severity of a log event
*/
enum LogType
{
LOG_INFO,
LOG_WARN,
LOG_ERROR
};
/**
* @brief Logger callback type
*/
2015-06-10 02:40:03 +00:00
typedef std::function<void(LogType, std::string&)> FLogger;
2015-05-20 05:22:32 +00:00
/**
* @brief FourCC representation used within HECL's database
*
* FourCCs are efficient, mnemonic four-char-sequences used to represent types
* while fitting comfortably in a 32-bit word. HECL uses a four-char array
* to remain endian-independent.
*/
2015-06-10 23:34:14 +00:00
class FourCC final
2015-05-20 05:22:32 +00:00
{
union
{
char fcc[4];
uint32_t num;
};
public:
2015-05-21 02:33:05 +00:00
FourCC() /* Sentinel FourCC */
: num(0) {}
2015-05-20 05:22:32 +00:00
FourCC(const char* name)
: num(*(uint32_t*)name) {}
2015-06-11 04:55:06 +00:00
inline bool operator==(const FourCC& other) const {return num == other.num;}
inline bool operator!=(const FourCC& other) const {return num != other.num;}
inline bool operator==(const char* other) const {return num == *(uint32_t*)other;}
inline bool operator!=(const char* other) const {return num != *(uint32_t*)other;}
inline std::string toString() const {return std::string(fcc, 4);}
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-06-10 23:34:14 +00:00
class Hash final
2015-05-20 05:22:32 +00:00
{
int64_t hash;
public:
2015-06-09 22:19:59 +00:00
Hash(const void* buf, size_t len)
2015-05-20 05:22:32 +00:00
: hash(Blowfish_hash(buf, len)) {}
2015-06-09 22:19:59 +00:00
Hash(const std::string& str)
: hash(Blowfish_hash(str.data(), str.size())) {}
Hash(int64_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;}
inline Hash& operator=(const Hash& other) {hash = other.hash; return *this;}
inline bool operator==(const Hash& other) const {return hash == other.hash;}
inline bool operator!=(const Hash& other) const {return hash != other.hash;}
inline bool operator<(const Hash& other) const {return hash < other.hash;}
inline bool operator>(const Hash& other) const {return hash > other.hash;}
inline bool operator<=(const Hash& other) const {return hash <= other.hash;}
inline bool operator>=(const Hash& other) const {return hash >= other.hash;}
};
/**
* @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-06-11 04:55:06 +00:00
inline time_t getTs() const {return ts;}
2015-06-10 23:34:14 +00:00
inline Time& operator=(const Time& other) {ts = other.ts; return *this;}
inline bool operator==(const Time& other) const {return ts == other.ts;}
inline bool operator!=(const Time& other) const {return ts != other.ts;}
inline bool operator<(const Time& other) const {return ts < other.ts;}
inline bool operator>(const Time& other) const {return ts > other.ts;}
inline bool operator<=(const Time& other) const {return ts <= other.ts;}
inline bool operator>=(const Time& other) const {return ts >= other.ts;}
2015-05-20 05:22:32 +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
{
protected:
2015-06-09 22:19:59 +00:00
SystemString m_absPath;
2015-06-11 04:55:06 +00:00
SystemString m_relPath;
2015-06-10 23:34:14 +00:00
size_t m_hash = 0;
2015-06-10 02:40:03 +00:00
#if HECL_UCS2
std::string m_utf8AbsPath;
const char* m_utf8RelPath;
#endif
2015-05-27 09:09:05 +00:00
ProjectPath() {}
2015-06-09 22:19:59 +00:00
bool _canonAbsPath(const SystemString& path);
2015-05-27 09:09:05 +00:00
public:
/**
* @brief Construct a project subpath representation
* @param rootPath previously constructed ProjectRootPath held by HECLDatabase::IProject
* @param path valid filesystem-path (relative or absolute) to subpath
*/
2015-06-09 22:19:59 +00:00
ProjectPath(const ProjectRootPath& rootPath, const SystemString& path);
2015-05-27 09:09:05 +00:00
/**
* @brief Determine if ProjectPath represents project root directory
* @return true if project root directory
*/
2015-06-11 04:55:06 +00:00
inline bool isRoot() const {return m_relPath.empty();}
2015-05-27 09:09:05 +00:00
/**
* @brief Access fully-canonicalized absolute path
* @return Absolute path reference
*/
2015-06-10 02:40:03 +00:00
inline const SystemString& 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)
*/
2015-06-11 04:55:06 +00:00
inline const SystemString& 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-06-10 02:40:03 +00:00
/**
* @brief Access fully-canonicalized absolute path in UTF-8
* @return Absolute path reference
*/
inline const std::string& getAbsolutePathUTF8() const
{
#if HECL_UCS2
return m_utf8AbsPath;
#else
return m_absPath;
#endif
}
2015-06-11 04:55:06 +00:00
inline const std::string& getRelativePathUTF8() const
2015-06-10 02:40:03 +00:00
{
#if HECL_UCS2
return m_utf8RelPath;
#else
return m_relPath;
#endif
}
2015-05-27 09:09:05 +00:00
/**
* @brief Type of path
*/
enum PathType
{
PT_NONE, /**< If path doesn't reference a valid filesystem entity, this is returned */
PT_FILE, /**< Singular file path (confirmed with filesystem) */
PT_DIRECTORY, /**< Singular directory path (confirmed with filesystem) */
PT_GLOB /**< Glob-path (whenever one or more '*' occurs in syntax) */
};
/**
* @brief Get type of path based on syntax and filesystem queries
* @return Type of path
*/
2015-06-10 02:40:03 +00:00
PathType getPathType() const;
2015-05-27 09:09:05 +00:00
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-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)
*/
2015-06-10 02:40:03 +00:00
void getGlobResults(std::vector<SystemString>& outPaths) const;
2015-06-10 23:34:14 +00:00
/**
* @brief C++11 compatible runtime hash (NOT USED IN PACKAGES!!)
* @return System-specific hash value
*/
inline size_t hash() const {return m_hash;}
inline bool operator==(const ProjectPath& other) const {return m_hash == other.m_hash;}
inline bool operator!=(const ProjectPath& other) const {return m_hash != other.m_hash;}
2015-05-27 09:09:05 +00:00
};
/**
* @brief Special ProjectRootPath subclass for opening HECLDatabase::IProject instances
*
* Constructing a ProjectPath requires supplying a ProjectRootPath to consistently
* resolve canonicalized relative paths.
*/
class ProjectRootPath : public ProjectPath
{
public:
2015-06-09 22:19:59 +00:00
ProjectRootPath(const SystemString& path)
2015-06-11 04:55:06 +00:00
{_canonAbsPath(path);}
2015-05-27 09:09:05 +00:00
};
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
* @return Newly-constructed root path or NULL if not found
*/
ProjectRootPath* SearchForProject(const SystemString& path);
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-09 22:19:59 +00:00
static inline int16_t ToBig(int16_t val) {return bswap16(val);}
static inline uint16_t ToBig(uint16_t val) {return bswap16(val);}
static inline int32_t ToBig(int32_t val) {return bswap32(val);}
static inline uint32_t ToBig(uint32_t val) {return bswap32(val);}
static inline int64_t ToBig(int64_t val) {return bswap64(val);}
static inline uint64_t ToBig(uint64_t val) {return bswap64(val);}
static inline int16_t ToLittle(int16_t val) {return val;}
static inline uint16_t ToLittle(uint16_t val) {return val;}
static inline int32_t ToLittle(int32_t val) {return val;}
static inline uint32_t ToLittle(uint32_t val) {return val;}
static inline int64_t ToLittle(int64_t val) {return val;}
static inline uint64_t ToLittle(uint64_t val) {return val;}
2015-05-27 09:09:05 +00:00
#else
2015-06-09 22:19:59 +00:00
static inline int16_t ToLittle(int16_t val) {return bswap16(val);}
static inline uint16_t ToLittle(uint16_t val) {return bswap16(val);}
static inline int32_t ToLittle(int32_t val) {return bswap32(val);}
static inline uint32_t ToLittle(uint32_t val) {return bswap32(val);}
static inline int64_t ToLittle(int64_t val) {return bswap64(val);}
static inline uint64_t ToLittle(uint64_t val) {return bswap64(val);}
static inline int16_t ToBig(int16_t val) {return val;}
static inline uint16_t ToBig(uint16_t val) {return val;}
static inline int32_t ToBig(int32_t val) {return val;}
static inline uint32_t ToBig(uint32_t val) {return val;}
static inline int64_t ToBig(int64_t val) {return val;}
static inline uint64_t ToBig(uint64_t val) {return val;}
2015-05-27 09:09:05 +00:00
#endif
2015-05-20 05:22:32 +00:00
}
2015-06-10 23:34:14 +00:00
namespace std
{
template <> struct hash<HECL::ProjectPath>
{
size_t operator()(const HECL::ProjectPath& val) const noexcept
{return val.hash();}
};
}
2015-05-20 05:22:32 +00:00
#endif // HECL_HPP