diff --git a/hecl/.gitmodules b/hecl/.gitmodules new file mode 100644 index 000000000..0b5c0b6e3 --- /dev/null +++ b/hecl/.gitmodules @@ -0,0 +1,3 @@ +[submodule "extern/Athena"] + path = extern/Athena + url = https://github.com/Antidote/Athena.git diff --git a/hecl/dataspec/DUMB.hpp b/hecl/dataspec/DUMB.hpp index d652f6193..cfb9edf47 100644 --- a/hecl/dataspec/DUMB.hpp +++ b/hecl/dataspec/DUMB.hpp @@ -3,32 +3,26 @@ #include "HECLDatabase.hpp" -class CDUMBProject : public HECLDatabase::CProjectObject +class CDUMBProject : public HECLDatabase::ProjectObjectBase { - bool _cookObject(TDataAppender dataAppender, + using HECLDatabase::ProjectObjectBase::ProjectObjectBase; + + bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) { return true; } - void _gatherDeps(TDepAdder depAdder) + void _gatherDeps(FDepAdder depAdder) { } -public: - CDUMBProject(const ConstructionInfo& info) - : CProjectObject(info) - { - } - - ~CDUMBProject() - { - } }; -class CDUMBRuntime : public HECLDatabase::CRuntimeObject +class CDUMBRuntime : public HECLDatabase::RuntimeObjectBase { + using HECLDatabase::RuntimeObjectBase::RuntimeObjectBase; bool _objectFinishedLoading(const void* data, size_t len) { @@ -40,14 +34,6 @@ class CDUMBRuntime : public HECLDatabase::CRuntimeObject } -public: - CDUMBRuntime(const ConstructionInfo& info) - : CRuntimeObject(info) - { - } - ~CDUMBRuntime() - { - } }; #endif // DUMB_HPP diff --git a/hecl/dataspec/HMDL.hpp b/hecl/dataspec/HMDL.hpp index f7dcaf791..217043751 100644 --- a/hecl/dataspec/HMDL.hpp +++ b/hecl/dataspec/HMDL.hpp @@ -3,32 +3,25 @@ #include "HECLDatabase.hpp" -class CHMDLProject : public HECLDatabase::CProjectObject +class CHMDLProject : public HECLDatabase::ProjectObjectBase { - bool _cookObject(TDataAppender dataAppender, + using HECLDatabase::ProjectObjectBase::ProjectObjectBase; + + bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) { return true; } - void _gatherDeps(TDepAdder depAdder) + void _gatherDeps(FDepAdder depAdder) { } - -public: - CHMDLProject(const ConstructionInfo& info) - : CProjectObject(info) - { - } - - ~CHMDLProject() - { - } }; -class CHMDLRuntime : public HECLDatabase::CRuntimeObject +class CHMDLRuntime : public HECLDatabase::RuntimeObjectBase { + using HECLDatabase::RuntimeObjectBase::RuntimeObjectBase; bool _objectFinishedLoading(const void* data, size_t len) { @@ -39,15 +32,6 @@ class CHMDLRuntime : public HECLDatabase::CRuntimeObject { } - -public: - CHMDLRuntime(const ConstructionInfo& info) - : CRuntimeObject(info) - { - } - ~CHMDLRuntime() - { - } }; #endif // HMDL_HPP diff --git a/hecl/dataspec/MATR.hpp b/hecl/dataspec/MATR.hpp index ced93728b..04a925348 100644 --- a/hecl/dataspec/MATR.hpp +++ b/hecl/dataspec/MATR.hpp @@ -3,32 +3,25 @@ #include "HECLDatabase.hpp" -class CMATRProject : public HECLDatabase::CProjectObject +class CMATRProject : public HECLDatabase::ProjectObjectBase { - bool _cookObject(TDataAppender dataAppender, + using HECLDatabase::ProjectObjectBase::ProjectObjectBase; + + bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) { return true; } - void _gatherDeps(TDepAdder depAdder) + void _gatherDeps(FDepAdder depAdder) { } - -public: - CMATRProject(const ConstructionInfo& info) - : CProjectObject(info) - { - } - - ~CMATRProject() - { - } }; -class CMATRRuntime : public HECLDatabase::CRuntimeObject +class CMATRRuntime : public HECLDatabase::RuntimeObjectBase { + using HECLDatabase::RuntimeObjectBase::RuntimeObjectBase; bool _objectFinishedLoading(const void* data, size_t len) { @@ -39,15 +32,6 @@ class CMATRRuntime : public HECLDatabase::CRuntimeObject { } - -public: - CMATRRuntime(const ConstructionInfo& info) - : CRuntimeObject(info) - { - } - ~CMATRRuntime() - { - } }; #endif // MATR_HPP diff --git a/hecl/dataspec/STRG.hpp b/hecl/dataspec/STRG.hpp index de2de94c9..90edaa157 100644 --- a/hecl/dataspec/STRG.hpp +++ b/hecl/dataspec/STRG.hpp @@ -3,32 +3,25 @@ #include "HECLDatabase.hpp" -class CSTRGProject : public HECLDatabase::CProjectObject +class CSTRGProject : public HECLDatabase::ProjectObjectBase { - bool _cookObject(TDataAppender dataAppender, + using HECLDatabase::ProjectObjectBase::ProjectObjectBase; + + bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) { return true; } - void _gatherDeps(TDepAdder depAdder) + void _gatherDeps(FDepAdder depAdder) { } - -public: - CSTRGProject(const ConstructionInfo& info) - : CProjectObject(info) - { - } - - ~CSTRGProject() - { - } }; -class CSTRGRuntime : public HECLDatabase::CRuntimeObject +class CSTRGRuntime : public HECLDatabase::RuntimeObjectBase { + using HECLDatabase::RuntimeObjectBase::RuntimeObjectBase; bool _objectFinishedLoading(const void* data, size_t len) { @@ -39,15 +32,6 @@ class CSTRGRuntime : public HECLDatabase::CRuntimeObject { } - -public: - CSTRGRuntime(const ConstructionInfo& info) - : CRuntimeObject(info) - { - } - ~CSTRGRuntime() - { - } }; #endif // STRG_HPP diff --git a/hecl/dataspec/TXTR.hpp b/hecl/dataspec/TXTR.hpp index d8edbbcb6..c37aca0a2 100644 --- a/hecl/dataspec/TXTR.hpp +++ b/hecl/dataspec/TXTR.hpp @@ -4,15 +4,17 @@ #include "HECLDatabase.hpp" #include "helpers.hpp" -class CTXTRProject : public HECLDatabase::CProjectObject +class CTXTRProject : public HECLDatabase::ProjectObjectBase { - bool _cookObject(TDataAppender dataAppender, + using HECLDatabase::ProjectObjectBase::ProjectObjectBase; + + bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) { return true; } - void _gatherDeps(TDepAdder depAdder) + void _gatherDeps(FDepAdder depAdder) { } @@ -26,19 +28,11 @@ public: return false; return true; } - - CTXTRProject(const ConstructionInfo& info) - : CProjectObject(info) - { - } - - ~CTXTRProject() - { - } }; -class CTXTRRuntime : public HECLDatabase::CRuntimeObject +class CTXTRRuntime : public HECLDatabase::RuntimeObjectBase { + using HECLDatabase::RuntimeObjectBase::RuntimeObjectBase; bool _objectFinishedLoading(const void* data, size_t len) { @@ -49,15 +43,6 @@ class CTXTRRuntime : public HECLDatabase::CRuntimeObject { } - -public: - CTXTRRuntime(const ConstructionInfo& info) - : CRuntimeObject(info) - { - } - ~CTXTRRuntime() - { - } }; #endif // TXTR_HPP diff --git a/hecl/driver/CToolHelp.hpp b/hecl/driver/CToolHelp.hpp index b8514381c..912740f3c 100644 --- a/hecl/driver/CToolHelp.hpp +++ b/hecl/driver/CToolHelp.hpp @@ -58,6 +58,8 @@ public: CHelpOutput::THelpFunc helpFunc = NULL; if (toolName == "init") helpFunc = CToolInit::Help; + else if (toolName == "platform") + helpFunc = CToolPlatform::Help; else if (toolName == "add") helpFunc = CToolAdd::Help; else if (toolName == "remove" || toolName == "rm") diff --git a/hecl/driver/CToolPlatform.hpp b/hecl/driver/CToolPlatform.hpp new file mode 100644 index 000000000..f8afe377c --- /dev/null +++ b/hecl/driver/CToolPlatform.hpp @@ -0,0 +1,53 @@ +#ifndef CTOOL_PLATFORM +#define CTOOL_PLATFORM + +#include "CToolBase.hpp" +#include + +class CToolPlatform final : public CToolBase +{ +public: + CToolPlatform(const SToolPassInfo& info) + : CToolBase(info) + { + } + + ~CToolPlatform() + { + } + + static void Help(CHelpOutput& help) + { + help.secHead("NAME"); + help.beginWrap(); + help.wrap("hecl-platform - Configure platform target options\n"); + help.endWrap(); + + help.secHead("SYNOPSIS"); + help.beginWrap(); + help.wrap("hecl platform [enable|disable] [...]\n"); + help.endWrap(); + + help.secHead("DESCRIPTION"); + help.beginWrap(); + help.wrap("This command configures the HECL project with the user's preferred target platforms.\n\n" + "Providing enable/disable argument will bulk-set the enable status of the provided platform" + "list. If enable/disable is not provided, a list of supported platforms is printed.\n\n"); + help.endWrap(); + + help.secHead("OPTIONS"); + help.optionHead("...", "platform name(s)"); + help.beginWrap(); + help.wrap("Specifies platform-names to enable/disable"); + help.endWrap(); + } + + std::string toolName() const {return "platform";} + + int run() + { + return 0; + } +}; + +#endif // CTOOL_PLATFORM diff --git a/hecl/driver/driver.pro b/hecl/driver/driver.pro index c6e1f12a6..930e9b527 100644 --- a/hecl/driver/driver.pro +++ b/hecl/driver/driver.pro @@ -31,5 +31,6 @@ HEADERS += \ CToolCook.hpp \ CToolClean.hpp \ CToolAdd.hpp \ - CToolRemove.hpp + CToolRemove.hpp \ + CToolPlatform.hpp diff --git a/hecl/driver/main.cpp b/hecl/driver/main.cpp index 507d09178..725c90e33 100644 --- a/hecl/driver/main.cpp +++ b/hecl/driver/main.cpp @@ -9,6 +9,7 @@ #include "CToolBase.hpp" #include "CToolInit.hpp" +#include "CToolPlatform.hpp" #include "CToolAdd.hpp" #include "CToolRemove.hpp" #include "CToolGroup.hpp" @@ -150,6 +151,8 @@ int main(int argc, const char** argv) { if (toolName == "init") tool = new CToolInit(info); + else if (toolName == "platform") + tool = new CToolPlatform(info); else if (toolName == "add") tool = new CToolAdd(info); else if (toolName == "remove" || toolName == "rm") diff --git a/hecl/extern/Athena b/hecl/extern/Athena new file mode 160000 index 000000000..778598309 --- /dev/null +++ b/hecl/extern/Athena @@ -0,0 +1 @@ +Subproject commit 7785983093f77e1f967580685f8d155364a7d965 diff --git a/hecl/extra/hecl_autocomplete.sh b/hecl/extra/hecl_autocomplete.sh index 50fbcf850..e35f86a7e 100755 --- a/hecl/extra/hecl_autocomplete.sh +++ b/hecl/extra/hecl_autocomplete.sh @@ -3,7 +3,7 @@ _hecl () { local word=${COMP_WORDS[COMP_CWORD]} - local filecmds=(init add remove group cook clean package) + local filecmds=(init platform add remove group cook clean package) if [ $COMP_CWORD == 1 ] then @@ -15,6 +15,9 @@ _hecl () init|add|remove|group|cook|clean|package) COMPREPLY=($(compgen -f -- "${word}")) ;; + platform) + COMPREPLY=($(compgen -W "enable disable" "${word}")) + ;; help) COMPREPLY=($(compgen -W "${filecmds[*]}" "${word}")) ;; diff --git a/hecl/hecl.pro b/hecl/hecl.pro index 00209f995..837a55d7e 100644 --- a/hecl/hecl.pro +++ b/hecl/hecl.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs +CONFIG += c++11 # Enable building with LLVM dependencies exists ($$PWD/llvm) { diff --git a/hecl/include/HECL.hpp b/hecl/include/HECL.hpp index 572e3e253..2ab29a878 100644 --- a/hecl/include/HECL.hpp +++ b/hecl/include/HECL.hpp @@ -10,25 +10,97 @@ char* win_realpath(const char* name, char* restrict resolved); #include #endif +#include #include +#include #include #include -#include #include "../extern/blowfish/blowfish.h" namespace HECL { -#if _WIN32 -typedef std::basic_string TSystemPath; +std::string WideToUTF8(const std::wstring& src); +std::wstring UTF8ToWide(const std::string& src); + +#if _WIN32 && UNICODE +typedef wchar_t SystemChar; +typedef std::wstring SystemString; +class CSystemUTF8View +{ + std::string m_utf8; +public: + CSystemUTF8View(const SystemString& str) + : m_utf8(WideToUTF8(str)) {} + inline const std::string& utf8_str() {return m_utf8;} +}; +class CSystemStringView +{ + std::wstring m_sys; +public: + CSystemStringView(const std::string& str) + : m_sys(UTF8ToWide(str)) {} + inline const std::string& sys_str() {return m_sys;} +}; +#ifndef _S +#define _S(val) L ## val +#endif #else -typedef std::string TSystemPath; +typedef char SystemChar; +typedef std::string SystemString; +class CSystemUTF8View +{ + const std::string& m_utf8; +public: + CSystemUTF8View(const SystemString& str) + : m_utf8(str) {} + inline const std::string& utf8_str() {return m_utf8;} +}; +class CSystemStringView +{ + const std::string& m_sys; +public: + CSystemStringView(const std::string& str) + : m_sys(str) {} + inline const std::string& sys_str() {return m_sys;} +}; +#ifndef _S +#define _S(val) val +#endif #endif +static inline void MakeDir(const SystemString& dir) +{ +#if _WIN32 + HRESULT err; + if (!CreateDirectory(dir.c_str(), NULL)) + if ((err = GetLastError()) != ERROR_ALREADY_EXISTS) + throw std::error_code(err, std::system_category()); +#else + if (mkdir(dir.c_str(), 0755)) + if (errno != EEXIST) + throw std::error_code(errno, std::system_category()); +#endif +} + +static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode) +{ +#if _WIN32 && UNICODE + FILE* fp = wfopen(path, mode); +#else + FILE* fp = fopen(path, mode); +#endif + if (!fp) + throw std::error_code(errno, std::system_category()); + + return fp; +} + +typedef std::basic_regex SystemRegex; +typedef std::regex_token_iterator SystemRegexTokenIterator; +typedef std::match_results SystemRegexMatch; + class ProjectRootPath; -static const std::regex regGLOB("\\*", std::regex::ECMAScript|std::regex::optimize); -static const std::regex regPATHCOMP("/([^/]+)", std::regex::ECMAScript|std::regex::optimize); -static const std::regex regDRIVELETTER("^([^/]*)/", std::regex::ECMAScript|std::regex::optimize); /** * @brief Severity of a log event @@ -75,20 +147,22 @@ public: * Hashes are used within HECL to avoid redundant storage of objects; * providing a rapid mechanism to compare for equality. */ -class ObjectHash +class Hash { int64_t hash; public: - ObjectHash(const void* buf, size_t len) + Hash(const void* buf, size_t len) : hash(Blowfish_hash(buf, len)) {} - ObjectHash(int64_t hashin) + Hash(const std::string& str) + : hash(Blowfish_hash(str.data(), str.size())) {} + Hash(int64_t hashin) : hash(hashin) {} - inline bool operator==(ObjectHash& other) {return hash == other.hash;} - inline bool operator!=(ObjectHash& other) {return hash != other.hash;} - inline bool operator<(ObjectHash& other) {return hash < other.hash;} - inline bool operator>(ObjectHash& other) {return hash > other.hash;} - inline bool operator<=(ObjectHash& other) {return hash <= other.hash;} - inline bool operator>=(ObjectHash& other) {return hash >= other.hash;} + inline bool operator==(Hash& other) {return hash == other.hash;} + inline bool operator!=(Hash& other) {return hash != other.hash;} + inline bool operator<(Hash& other) {return hash < other.hash;} + inline bool operator>(Hash& other) {return hash > other.hash;} + inline bool operator<=(Hash& other) {return hash <= other.hash;} + inline bool operator>=(Hash& other) {return hash >= other.hash;} }; /** @@ -107,51 +181,18 @@ public: class ProjectPath { protected: - TSystemPath m_absPath; - const char* m_relPath = NULL; + SystemString m_absPath; + const SystemChar* m_relPath = NULL; ProjectPath() {} - bool _canonAbsPath(const TSystemPath& path) - { -#if _WIN32 -#else - char resolvedPath[PATH_MAX]; - if (!realpath(path.c_str(), resolvedPath)) - { - throw std::invalid_argument("Unable to resolve '" + path + "' as a canonicalized path"); - return false; - } - m_absPath = resolvedPath; -#endif - return true; - } + bool _canonAbsPath(const SystemString& path); 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 */ - ProjectPath(const ProjectRootPath& rootPath, const TSystemPath& path) - { - _canonAbsPath(path); - if (m_absPath.size() < ((ProjectPath&)rootPath).m_absPath.size() || - m_absPath.compare(0, ((ProjectPath&)rootPath).m_absPath.size(), - ((ProjectPath&)rootPath).m_absPath)) - { - throw std::invalid_argument("'" + m_absPath + "' is not a subpath of '" + - ((ProjectPath&)rootPath).m_absPath + "'"); - return; - } - if (m_absPath.size() == ((ProjectPath&)rootPath).m_absPath.size()) - { - /* Copies of the project root are permitted */ - return; - } - m_relPath = m_absPath.c_str() + ((ProjectPath&)rootPath).m_absPath.size(); - if (m_relPath[0] == '/') - ++m_relPath; - if (m_relPath[0] == '\0') - m_relPath = NULL; - } + ProjectPath(const ProjectRootPath& rootPath, const SystemString& path); + /** * @brief Determine if ProjectPath represents project root directory * @return true if project root directory @@ -162,17 +203,17 @@ public: * @brief Access fully-canonicalized absolute path * @return Absolute path reference */ - inline const TSystemPath& getAbsolutePath() {return m_absPath;} + inline const SystemString& getAbsolutePath() {return m_absPath;} /** * @brief Access fully-canonicalized project-relative path * @return Relative pointer to within absolute-path or "." for project root-directory (use isRoot to detect) */ - inline const char* getRelativePath() + inline const SystemChar* getRelativePath() { if (m_relPath) return m_relPath; - return "."; + return _S("."); } /** @@ -190,57 +231,13 @@ public: * @brief Get type of path based on syntax and filesystem queries * @return Type of path */ - PathType getPathType() - { - if (std::regex_search(m_absPath, regGLOB)) - return PT_GLOB; -#if _WIN32 -#else - struct stat theStat; - if (stat(m_absPath.c_str(), &theStat)) - return PT_NONE; - if (S_ISDIR(theStat.st_mode)) - return PT_DIRECTORY; - if (S_ISREG(theStat.st_mode)) - return PT_FILE; - return PT_NONE; -#endif - } + PathType getPathType(); - void getGlobResults(std::vector& outPaths) - { -#if _WIN32 - std::string itStr; - std::smatch letterMatch; - if (m_absPath.compare(0, 2, "//")) - itStr = "\\\\"; - else if (std::regex_search(m_absPath, letterMatch, regDRIVELETTER)) - if (letterMatch[1].str().size()) - itStr = letterMatch[1]; -#else - std::string itStr = "/"; -#endif - bool needSlash = false; - - std::sregex_token_iterator pathComps(m_absPath.begin(), m_absPath.end(), regPATHCOMP); - for (; pathComps != std::sregex_token_iterator() ; ++pathComps) - { - const std::string& comp = *pathComps; - if (!std::regex_search(comp, regGLOB)) - { - if (needSlash) - itStr += '/'; - else - needSlash = true; - itStr += comp; - continue; - } -#if _WIN32 -#else - DIR* dir = opendir(""); -#endif - } - } + /** + * @brief Insert glob matches into existing vector + * @param outPaths Vector to add matches to (will not erase existing contents) + */ + void getGlobResults(std::vector& outPaths); }; /** @@ -252,7 +249,7 @@ public: class ProjectRootPath : public ProjectPath { public: - ProjectRootPath(const TSystemPath& path) + ProjectRootPath(const SystemString& path) { _canonAbsPath(path); } @@ -261,7 +258,8 @@ public: /* Type-sensitive byte swappers */ -static inline int16_t bswap(int16_t val) +template +static inline T bswap16(T val) { #if __GNUC__ return __builtin_bswap16(val); @@ -272,18 +270,8 @@ static inline int16_t bswap(int16_t val) #endif } -static inline uint16_t bswap(uint16_t val) -{ -#if __GNUC__ - return __builtin_bswap16(val); -#elif _WIN32 - return _byteswap_ushort(val); -#else - return (val = (val << 8) | ((val >> 8) & 0xFF)); -#endif -} - -static inline int32_t bswap(int32_t val) +template +static inline T bswap32(T val) { #if __GNUC__ return __builtin_bswap32(val); @@ -296,61 +284,54 @@ static inline int32_t bswap(int32_t val) #endif } -static inline uint32_t bswap(uint32_t val) -{ -#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 -} - -static inline int64_t bswap(int64_t val) +template +static inline T bswap64(T val) { #if __GNUC__ return __builtin_bswap64(val); #elif _WIN32 return _byteswap_uint64(val); #else - return (val = ((atInt64)((((atInt64)(val) & 0xFF00000000000000ULL) >> 56) | - (((atInt64)(val) & 0x00FF000000000000ULL) >> 40) | - (((atInt64)(val) & 0x0000FF0000000000ULL) >> 24) | - (((atInt64)(val) & 0x000000FF00000000ULL) >> 8) | - (((atInt64)(val) & 0x00000000FF000000ULL) << 8) | - (((atInt64)(val) & 0x0000000000FF0000ULL) << 24) | - (((atInt64)(val) & 0x000000000000FF00ULL) << 40) | - (((atInt64)(val) & 0x00000000000000FFULL) << 56)))); + 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); #endif } -static inline uint64_t bswap(uint64_t val) -{ -#if __GNUC__ - return __builtin_bswap64(val); -#elif _WIN32 - return _byteswap_uint64(val); -#else - return (val = ((atInt64)((((atInt64)(val) & 0xFF00000000000000ULL) >> 56) | - (((atInt64)(val) & 0x00FF000000000000ULL) >> 40) | - (((atInt64)(val) & 0x0000FF0000000000ULL) >> 24) | - (((atInt64)(val) & 0x000000FF00000000ULL) >> 8) | - (((atInt64)(val) & 0x00000000FF000000ULL) << 8) | - (((atInt64)(val) & 0x0000000000FF0000ULL) << 24) | - (((atInt64)(val) & 0x000000000000FF00ULL) << 40) | - (((atInt64)(val) & 0x00000000000000FFULL) << 56)))); -#endif -} #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define HECLMakeBig(val) HECL::bswap(val) -#define HECLMakeLittle(val) (val) +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;} #else -#define HECLMakeBig(val) (val) -#define HECLMakeLittle(val) HECL::bswap(val) +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;} #endif } diff --git a/hecl/include/HECLDatabase.hpp b/hecl/include/HECLDatabase.hpp index 87384a323..836153053 100644 --- a/hecl/include/HECLDatabase.hpp +++ b/hecl/include/HECLDatabase.hpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #include #include @@ -13,73 +16,6 @@ namespace HECLDatabase { -class IProject; - -/** - * @brief Generic Database Object Class - * - * This abstract base-class is a typeless object node for entities in an - * underlying database. - */ -class IDataObject -{ -public: - /** - * @brief Data-key of object - * @return Primary key - */ - virtual int64_t id() const=0; - - /** - * @brief FourCC type of object - * @return FourCC type - */ - virtual const HECL::FourCC& type() const=0; - - /** - * @brief Data-hash of object - * @return Object hash truncated to system's size-type - */ - virtual const HECL::ObjectHash& hash() const=0; - - /** - * @brief Original path of object - * @return Name - */ - virtual const std::string& path() const=0; - -}; - -/** - * @brief An iterable collection of objects tracked within the database - */ -class IDataDependencyGroup -{ -public: - /** - * @brief Count of objects in the group - * @return object count - */ - virtual size_t length() const=0; - - /** - * @brief Alias of length() - * @return object count - */ - inline size_t size() const {return length();} - - /** - * @brief Retrieve object at specified internal index within the group - * @param idx internal index of object to fetch (range [0,length()-1]) - * @return object or nullptr - */ - virtual const IDataObject* at(size_t idx) const=0; - inline const IDataObject* operator[](size_t idx) {return at(idx);} - - virtual std::vector::const_iterator begin() const=0; - virtual std::vector::const_iterator end() const=0; -}; - /** * @brief Base object to subclass for integrating with key project operations * @@ -89,10 +25,11 @@ public: * * DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!! */ -class CProjectObject +class ProjectObjectBase { -protected: friend class CProject; + std::string m_path; +protected: /** * @brief Byte-order of target system @@ -115,7 +52,7 @@ protected: DP_CAFE /**< Swizzled textures and R700 shader objects */ }; - typedef std::function TDataAppender; + typedef std::function FDataAppender; /** * @brief Optional private method implemented by CProjectObject subclasses to cook objects @@ -128,11 +65,11 @@ protected: * Part of the cooking process may include embedding database-refs to dependencies. * This method should store the 64-bit value provided by IDataObject::id() when doing this. */ - virtual bool _cookObject(TDataAppender dataAppender, + virtual bool _cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) {(void)dataAppender;(void)endianness;(void)platform;return true;} - typedef std::function TDepAdder; + typedef std::function FDepAdder; /** * @brief Optional private method implemented by CProjectObject subclasses to resolve dependencies @@ -142,24 +79,21 @@ protected: * Dependencies registered via this method will eventually have this method called on themselves * as well. This is a non-recursive operation, no need for subclasses to implement recursion-control. */ - virtual void _gatherDeps(TDepAdder depAdder) + virtual void _gatherDeps(FDepAdder depAdder) {(void)depAdder;} -protected: - std::string m_path; - IDataObject* m_mainObj; - IDataObject* m_cookedObj; public: - static bool ClaimPath(const std::string&, const std::string&) {return false;} - virtual ~CProjectObject(); - struct ConstructionInfo - { - IDataObject* mainObj; - IDataObject* cookedObj; - const std::string& path; - }; - CProjectObject(const ConstructionInfo& info) - : m_path(info.path), m_mainObj(info.mainObj), m_cookedObj(info.cookedObj) {} + ProjectObjectBase(const std::string& path) + : m_path(path) {} + + inline const std::string& getPath() const {return m_path;} + + /** + * @brief Overridable function to verify data at provided path + * @return true if ProjectObject subclass handles data at provided path/subpath + */ + static bool ClaimPath(const std::string& /*path*/, const std::string& /*subpath*/) {return false;} + }; @@ -170,7 +104,7 @@ public: * resources in their ideal editor-formats. This interface exposes all * primary operations to perform on a given project. */ -class IProject +class Project { public: virtual ~IProject() {} @@ -226,14 +160,14 @@ public: /** * @brief Remove a given file or file-pattern from the database - * @param path file or pattern within project + * @param paths file(s) or pattern(s) within project * @param recursive traverse into matched subdirectories * @return true on success * * This method will not delete actual working files from the project * directory. It will delete associated cooked objects though. */ - virtual bool removePaths(const std::string& path, bool recursive=false)=0; + virtual bool removePaths(const std::vector& paths, bool recursive=false)=0; /** * @brief Register a working sub-directory as a Dependency Group @@ -249,14 +183,34 @@ public: * This contiguous storage makes for optimal loading from slow block-devices * like optical drives. */ - virtual bool addGroup(const std::string& path)=0; + virtual bool addGroup(const HECL::ProjectPath& path)=0; /** * @brief Unregister a working sub-directory as a dependency group * @param path directory to unregister as Dependency Group * @return true on success */ - virtual bool removeGroup(const std::string& path)=0; + virtual bool removeGroup(const HECL::ProjectPath& path)=0; + + /** + * @brief Return map populated with platforms targetable by this project interface + * @return Platform map with name-string keys and enable-status values + */ + virtual const std::map& listPlatforms()=0; + + /** + * @brief Enable persistent user preference for particular platform string(s) + * @param platforms String(s) representing unique platform(s) from listPlatforms + * @return true on success + */ + virtual bool enablePlatforms(const std::vector& platforms)=0; + + /** + * @brief Disable persistent user preference for particular platform string(s) + * @param platform String(s) representing unique platform(s) from listPlatforms + * @return true on success + */ + virtual bool disablePlatforms(const std::vector& platforms)=0; /** * @brief Begin cook process for specified directory @@ -269,7 +223,7 @@ public: * This method blocks execution during the procedure, with periodic * feedback delivered via feedbackCb. */ - virtual bool cookPath(const std::string& path, + virtual bool cookPath(const HECL::SystemString& path, std::function feedbackCb, bool recursive=false)=0; @@ -294,24 +248,39 @@ public: * Developers understand how useful 'clean' is. While ideally not required, * it's useful for verifying that a rebuild from ground-up is doable. */ - virtual bool cleanPath(const std::string& path, bool recursive=false)=0; + virtual bool cleanPath(const HECL::SystemString& path, bool recursive=false)=0; /** - * @brief Package cooked objects for directory - * @param path directory of intermediates to package - * @param recursive traverse subdirectories to package as well - * @return true on success - * - * Once all dependent resources are cooked, this method archives specified - * intermediates into a packed database file located alongside the specified - * directory. This is a similar process to 'linking' in software development. - * - * Part of this process involves calling CProjectObject::_gatherDeps() to calculate - * object dependencies. This makes package-assembly simple, as dependencies will - * automatically be added as needed. The frontend needn't be concerned about - * gathering leaf-objects buried in corners of the working directory. + * @brief Nodegraph class for gathering dependency-resolved objects for packaging */ - virtual bool packagePath(const std::string& path, bool recursive=false)=0; + class PackageDepsgraph + { + public: + struct Node + { + enum + { + NODE_DATA, + NODE_GROUP + } type; + std::string path; + ProjectObjectBase* projectObj; + Node* sub; + Node* next; + }; + private: + friend class Project; + std::vector m_nodes; + public: + const Node* getRootNode() const {return &m_nodes[0];} + }; + + /** + * @brief Constructs a full depsgraph of the project-subpath provided + * @param path Subpath of project to root depsgraph at + * @return Populated depsgraph ready to traverse + */ + virtual PackageDepsgraph buildPackageDepsgraph(const HECL::ProjectPath& path)=0; }; @@ -324,204 +293,22 @@ public: * All necessary database index files and object directories will be established * within the specified directory path. */ -IProject* OpenProject(const HECL::ProjectRootPath& rootPath); +Project* OpenProject(const HECL::ProjectRootPath& rootPath); /** - * @brief Base object to subclass for integrating with key runtime operations + * @brief Subclassed by dataspec entries to manage per-game aspects of the data pipeline * - * All runtime objects are provided with IDataObject pointers to their database - * entries. Subclasses register themselves with a type registry so instances - * are automatically constructed when performing operations like runtime-integration. - * - * DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!! + * The DataSpec class manages interfaces for unpackaging, cooking, and packaging + * of data for interacting with a specific system/game-engine. */ -class CRuntimeObject -{ - unsigned m_refCount = 0; - bool m_loaded = false; -protected: - - /** - * @brief Optional subclass method called on background thread or in response to interrupt when data is ready - * @param data fully-loaded data buffer - * @param len length of buffer - * @return true when data is successfully integrated into the runtime - */ - virtual bool _objectFinishedLoading(const void* data, size_t len) - {(void)data;(void)len;return true;} - - /** - * @brief Optional subclass method called in response to reference-count dropping to 0 - */ - virtual void _objectWillUnload() {} - -protected: - IDataObject* m_obj; -public: - struct ConstructionInfo - { - IDataObject* obj; - }; - CRuntimeObject(const ConstructionInfo& info) - : m_obj(info.obj) {} - - /** - * @brief Determine if object is fully loaded and constructed - * @return true if so - */ - inline bool isLoaded() const {return m_loaded;} - - /** - * @brief Increment object's reference count - * @return true if already loaded (ready to use) false if load is staged (poll with isLoaded()) - * - * CRuntimeObject instances initially have an internal reference-count of 0. - * By calling this method from 0, an asynchronous load operation takes place. - * Synchronous loads are discouraged by HECL in order to avoid stalling game - * systems. Please poll with isLoaded() to keep things running smoothly! - */ - bool incRef(); - - /** - * @brief Decrement object's reference count - * - * If the internal reference-count reaches 0, the object's unload procedure takes place - */ - void decRef(); - -}; - -/** - * @brief Runtime data-management interface - * - * Interface for controlling runtime data-operations like object lookup - * and burst load-transactions. The runtime's implementation automatically - * constructs CRuntimeObject instances as needed. - */ -class IRuntime +class IDataSpec { public: - virtual ~IRuntime() {} - /** - * @brief Lookup singular object by database ID - * @param id database ID of object - * @return runtime object - */ - virtual CRuntimeObject* lookupObjectById(size_t id); - - /** - * @brief Iterable group view providing a load interface for Dependency Groups - * - * HECL uses a background thread or other asynchronous loading mechanism to - * efficiently load and construct IRuntimeObject instances. - * - * The iterator interface may be used immediately to access contained objects. - */ - class IStagedGroup - { - public: - /** - * @brief Poll to see if transaction complete - * @return true if complete - */ - virtual bool isDone() const=0; - - virtual std::vector::iterator begin() const=0; - virtual std::vector::iterator end() const=0; - }; - - /** - * @brief Begin asynchronously loading a dependency group by id - * @param groupId the id of the dependency group within the database - * @return Staged group interface scheduled to load ASAP - */ - virtual IStagedGroup* loadDependencyGroup(int64_t groupId); - - /** - * @brief Unload a previously-loaded dependency group or cancel a load in-progress - * @param group Staged Group obtained via loadDependencyGroup() - */ - virtual void unloadDependencyGroup(IStagedGroup* group); + virtual packageData(); }; -/** - * @brief Statically-constructed structure registering a FourCC with project - * and runtime factories. This is used for constructing key operational subclasses - * for cooking/packaging during development and runtime-integrating during gameplay. - */ -struct RegistryEntry -{ - typedef std::function TPathClaimer; - typedef std::function TProjectFactory; - typedef std::function TRuntimeFactory; - const HECL::FourCC& fcc; -#ifndef HECL_STRIP_PROJECT - TPathClaimer pathClaimer; - TProjectFactory projectFactory; -#endif -#ifndef HECL_STRIP_RUNTIME - TRuntimeFactory runtimeFactory; -#endif -}; - -static RegistryEntry::TPathClaimer NULL_PATH_CLAIMER = - [](const std::string&, const std::string&) -> bool {return false;}; -static RegistryEntry::TProjectFactory NULL_PROJECT_FACTORY = - [](const HECLDatabase::CProjectObject::ConstructionInfo&) - -> HECLDatabase::CProjectObject* {return nullptr;}; -static RegistryEntry::TRuntimeFactory NULL_RUNTIME_FACTORY = - [](const HECLDatabase::CRuntimeObject::ConstructionInfo&) - -> HECLDatabase::CRuntimeObject* {return nullptr;}; - -#if !defined(HECL_STRIP_PROJECT) && !defined(HECL_STRIP_RUNTIME) - -#define REGISTRY_ENTRY(fourcc, projectClass, runtimeClass) {fourcc, \ -[](const std::string& path, const std::string& subpath) -> \ - bool {return projectClass::ClaimPath(path, subpath);}, \ -[](const HECLDatabase::CProjectObject::ConstructionInfo& info) -> \ - HECLDatabase::CProjectObject* {return new projectClass(info);}, \ -[](const HECLDatabase::CRuntimeObject::ConstructionInfo& info) -> \ - HECLDatabase::CRuntimeObject* {return new runtimeClass(info);}} - -#define REGISTRY_SENTINEL() \ - {HECL::FourCC(), HECLDatabase::NULL_PATH_CLAIMER, \ - HECLDatabase::NULL_PROJECT_FACTORY, HECLDatabase::NULL_RUNTIME_FACTORY} - -#elif !defined(HECL_STRIP_PROJECT) - -#define REGISTRY_ENTRY(fourcc, projectClass, runtimeClass) {fourcc, \ -[](const std::string& path, const std::string& subpath) -> \ - bool {return projectClass::ClaimPath(path, subpath);}, \ -[](const HECLDatabase::CProjectObject::ConstructionInfo& info) -> \ - HECLDatabase::CProjectObject* {return new projectClass(info);}} - -#define REGISTRY_SENTINEL() {HECL::FourCC(), \ - HECLDatabase::NULL_PATH_CLAIMER, HECLDatabase::NULL_PROJECT_FACTORY} - -#elif !defined(HECL_STRIP_RUNTIME) - -#define REGISTRY_ENTRY(fourcc, projectClass, runtimeClass) {fourcc, \ -[](const HECLDatabase::CRuntimeObject::ConstructionInfo& info) -> \ - HECLDatabase::CRuntimeObject* {return new runtimeClass(info);}} - -#define REGISTRY_SENTINEL() {HECL::FourCC(), HECLDatabase::NULL_RUNTIME_FACTORY} - -#endif - -/** - * @brief Statically-constructed table of registered types - * - * Table is defined in dataspec/dataspec.cpp of HECL's codebase. - * Developers are encouraged to modify/extend the default data model as - * required by their project. - * - * The REGISTRY_ENTRY macro is a helper for defining entries. - * The REGISTRY_SENTINEL must be inserted at the end of the table. - */ -extern const RegistryEntry DATASPEC_TYPE_REGISTRY[]; - } #endif // HECLDATABASE_HPP diff --git a/hecl/include/HECLRuntime.hpp b/hecl/include/HECLRuntime.hpp index 9453c0530..6e7e245ed 100644 --- a/hecl/include/HECLRuntime.hpp +++ b/hecl/include/HECLRuntime.hpp @@ -1,4 +1,173 @@ #ifndef HECLRUNTIME_HPP #define HECLRUNTIME_HPP +#include +#include +#include + +#include "HECL.hpp" + + +namespace HECLRuntime +{ + +class RuntimeEntity +{ +public: + enum ERuntimeEntityType + { + ENTITY_NONE, + ENTITY_OBJECT, + ENTITY_GROUP + }; + +private: + ERuntimeEntityType m_type; + const std::string& m_path; + bool m_loaded = false; + + friend class RuntimeGroup; + friend class RuntimeObjectBase; + RuntimeEntity(ERuntimeEntityType type, const std::string& path) + : m_type(type), m_path(path) {} + +public: + /** + * @brief Get type of runtime object + * @return Type enum + */ + inline ERuntimeEntityType getType() const {return m_type;} + + /** + * @brief Get database entity path + * @return Path string + */ + inline const std::string& getPath() const {return m_path;} + + /** + * @brief Determine if object is fully loaded and constructed + * @return true if so + */ + inline bool isLoaded() const {return m_loaded;} +}; + +/** + * @brief Interface representing a load-ordered group of runtime objects + * + * HLPK files perform all data retrieval using the notion of 'groups' + * Groups are a collection of data objects that have been sequentially packed + * in the package file and are constructed in the indexed order of the group. + * + * RuntimeGroup objects are internally created and weakly-referenced by CRuntime. + * RuntimeObject objects are weakly-referenced by RuntimeGroup; they're strongly + * referenced by application systems as long as they're needed. + * + * DO NOT CONSTRUCT THIS DIRECTLY!! + */ +class RuntimeGroup : public RuntimeEntity +{ +public: + typedef std::vector> GroupObjectsVector; +private: + friend class HECLRuntime; + GroupObjectsVector m_objects; + RuntimeGroup(const std::string& path) + : RuntimeEntity(ENTITY_GROUP, path) {} +public: + inline const GroupObjectsVector& getObjects() const {return m_objects;} +}; + +/** + * @brief Base object to subclass for integrating with key runtime operations + * + * All runtime objects are provided with IDataObject pointers to their database + * entries. Subclasses register themselves with a type registry so instances + * are automatically constructed when performing operations like runtime-integration. + * + * DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!! + */ +class RuntimeObjectBase : public RuntimeEntity +{ + std::shared_ptr m_parent; +protected: + + /** + * @brief Optional subclass method called on background thread or in response to interrupt when data is ready + * @param data fully-loaded data buffer + * @param len length of buffer + * @return true when data is successfully integrated into the runtime + */ + virtual bool _objectFinishedLoading(const void* data, size_t len) + {(void)data;(void)len;return true;} + + /** + * @brief Optional subclass method called in response to reference-count dropping to 0 + */ + virtual void _objectWillUnload() {} + +public: + RuntimeObjectBase(const RuntimeGroup* group, const std::string& path) + : RuntimeEntity(ENTITY_OBJECT, path), m_parent(group) {} + + /** + * @brief Get parent group of object + * @return Borrowed pointer of parent RuntimeGroup + */ + inline const RuntimeGroup* getParentGroup() {return m_parent.get();} +}; + +/** + * @brief HLPK Runtime data-management root + * + * Interface for controlling runtime data-operations like object lookup + * and burst load-transactions using HLPK packages. The runtime's + * implementation automatically constructs RuntimeObjectBase and + * RuntimeGroup instances as needed. + */ +class HECLRuntime +{ +public: + /** + * @brief Constructs the HECL runtime root + * @param hlpkDirectory directory to search for .hlpk files + */ + HECLRuntime(const HECL::SystemString& hlpkDirectory); + ~HECLRuntime(); + + /** + * @brief Structure indicating the load status of an object group + */ + struct SGroupLoadStatus + { + std::atomic_bool done; + std::atomic_size_t completedObjects; + std::atomic_size_t totalObjects; + }; + + /** + * @brief Begin a synchronous group-load transaction + * @param pathHash Hashed path string to perform lookup + * @return Shared reference to the loading/loaded object + * + * This method blocks until the entire containing-group is loaded. + * Paths to groups or individual objects are accepted. + */ + std::shared_ptr loadSync(const HECL::Hash& pathHash); + + /** + * @brief Begin an asynchronous group-load transaction + * @param pathHash Hashed path string to perform lookup + * @param statusOut Optional atomically-pollable structure updated with status fields + * @return Shared reference to the loading/loaded object + * + * This method returns once all group entity stubs are constructed. + * Paths to groups or individual objects are accepted. + */ + std::shared_ptr loadAsync(const HECL::Hash& pathHash, + SGroupLoadStatus* statusOut=NULL); + +}; + +} + #endif // HECLRUNTIME_HPP diff --git a/hecl/lib/HECL.cpp b/hecl/lib/HECL.cpp index b8f8dfeb9..4cc368966 100644 --- a/hecl/lib/HECL.cpp +++ b/hecl/lib/HECL.cpp @@ -1,9 +1 @@ #include "HECL.hpp" - -#if _WIN32 -char* win_realpath(const char* name, char* restrict resolved) -{ -} -#endif - - diff --git a/hecl/lib/ProjectPath.cpp b/hecl/lib/ProjectPath.cpp new file mode 100644 index 000000000..bb378062c --- /dev/null +++ b/hecl/lib/ProjectPath.cpp @@ -0,0 +1,145 @@ +#include "HECL.hpp" +#include +#include + +#if _WIN32 +char* win_realpath(const char* name, char* restrict resolved) +{ +} +#endif + +namespace HECL +{ + +static const SystemRegex regGLOB(_S("\\*"), SystemRegex::ECMAScript|SystemRegex::optimize); +static const SystemRegex regPATHCOMP(_S("/([^/]+)"), SystemRegex::ECMAScript|SystemRegex::optimize); +static const SystemRegex regDRIVELETTER(_S("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize); + +bool ProjectPath::_canonAbsPath(const SystemString& path) +{ +#if _WIN32 +#else + SystemChar resolvedPath[PATH_MAX]; + if (!realpath(path.c_str(), resolvedPath)) + { + throw std::invalid_argument("Unable to resolve '" + CSystemUTF8View(path).utf8_str() + + "' as a canonicalized path"); + return false; + } + m_absPath = resolvedPath; +#endif + return true; +} + +ProjectPath::ProjectPath(const ProjectRootPath& rootPath, const SystemString& path) +{ + _canonAbsPath(path); + if (m_absPath.size() < ((ProjectPath&)rootPath).m_absPath.size() || + m_absPath.compare(0, ((ProjectPath&)rootPath).m_absPath.size(), + ((ProjectPath&)rootPath).m_absPath)) + { + throw std::invalid_argument("'" + CSystemUTF8View(m_absPath).utf8_str() + "' is not a subpath of '" + + CSystemUTF8View(((ProjectPath&)rootPath).m_absPath).utf8_str() + "'"); + return; + } + if (m_absPath.size() == ((ProjectPath&)rootPath).m_absPath.size()) + { + /* Copies of the project root are permitted */ + return; + } + m_relPath = m_absPath.c_str() + ((ProjectPath&)rootPath).m_absPath.size(); + if (m_relPath[0] == _S('/')) + ++m_relPath; + if (m_relPath[0] == _S('\0')) + m_relPath = NULL; +} + +ProjectPath::PathType ProjectPath::getPathType() +{ + if (std::regex_search(m_absPath, regGLOB)) + return PT_GLOB; +#if _WIN32 +#else + struct stat theStat; + if (stat(m_absPath.c_str(), &theStat)) + return PT_NONE; + if (S_ISDIR(theStat.st_mode)) + return PT_DIRECTORY; + if (S_ISREG(theStat.st_mode)) + return PT_FILE; + return PT_NONE; +#endif +} + +static void _recursiveGlob(std::vector& outPaths, + size_t level, + const SystemRegexMatch& pathCompMatches, + const SystemString& itStr, + bool needSlash) +{ + if (level >= pathCompMatches.size()) + return; + + SystemString comp = pathCompMatches.str(level); + if (!std::regex_search(comp, regGLOB)) + { + SystemString nextItStr = itStr; + if (needSlash) + nextItStr += _S('/'); + nextItStr += comp; + _recursiveGlob(outPaths, level+1, pathCompMatches, nextItStr, true); + return; + } + + /* Compile component into regex */ + SystemRegex regComp(comp, SystemRegex::ECMAScript); + +#if _WIN32 +#else + DIR* dir = opendir(itStr.c_str()); + if (!dir) + throw std::runtime_error("unable to open directory for traversal at '" + itStr + "'"); + + struct dirent* de; + while ((de = readdir(dir))) + { + if (std::regex_search(de->d_name, regComp)) + { + SystemString nextItStr = itStr; + if (needSlash) + nextItStr += '/'; + nextItStr += de->d_name; + + struct stat theStat; + if (stat(nextItStr.c_str(), &theStat)) + continue; + + if (S_ISDIR(theStat.st_mode)) + _recursiveGlob(outPaths, level+1, pathCompMatches, nextItStr, true); + else if (S_ISREG(theStat.st_mode)) + outPaths.push_back(nextItStr); + } + } +#endif +} + +void ProjectPath::getGlobResults(std::vector& outPaths) +{ +#if _WIN32 + TSystemPath itStr; + SystemRegexMatch letterMatch; + if (m_absPath.compare(0, 2, _S("//"))) + itStr = _S("\\\\"); + else if (std::regex_search(m_absPath, letterMatch, regDRIVELETTER)) + if (letterMatch[1].str().size()) + itStr = letterMatch[1]; +#else + SystemString itStr = _S("/"); +#endif + + SystemRegexMatch pathCompMatches; + if (std::regex_search(m_absPath, pathCompMatches, regPATHCOMP)) + _recursiveGlob(outPaths, 1, pathCompMatches, itStr, false); +} + +} diff --git a/hecl/lib/WideStringConvert.cpp b/hecl/lib/WideStringConvert.cpp new file mode 100644 index 000000000..3f28250e0 --- /dev/null +++ b/hecl/lib/WideStringConvert.cpp @@ -0,0 +1,20 @@ +#include "HECL.hpp" +#include +#include + +namespace HECL +{ + +std::string WideToUTF8(const std::wstring& src) +{ + std::wstring_convert> conv; + return conv.to_bytes(src); +} + +std::wstring UTF8ToWide(const std::string& src) +{ + std::wstring_convert> conv; + return conv.from_bytes(src); +} + +} diff --git a/hecl/lib/database/CProject.cpp b/hecl/lib/database/CProject.cpp index 4936df459..32fd832ca 100644 --- a/hecl/lib/database/CProject.cpp +++ b/hecl/lib/database/CProject.cpp @@ -1,18 +1,125 @@ #include #include +#include #include #include #include "HECLDatabase.hpp" -#include "CSQLiteMain.hpp" namespace HECLDatabase { +static inline bool CheckNewLineAdvance(std::string::const_iterator& it) +{ + if (*it == '\n' || *it == '\0') + { + it += 1; + return true; + } + else if (*it == '\r') + { + if (*(it+1) == '\n') + { + it += 2; + return true; + } + } + return false; +} + class CProject : public IProject { - std::string m_rootPath; - CSQLiteMain* m_db; + HECL::SystemString m_rootPath; + + class ConfigFile + { + const CProject& m_project; + const HECL::SystemString& m_name; + HECL::SystemString m_filepath; + public: + ConfigFile(const CProject& project, const HECL::SystemString& name) + : m_project(project), m_name(name) + { + m_filepath = project.m_rootPath + _S("/.hecl/config/") + name; + } + + std::vector readLines() + { + FILE* fp = HECL::Fopen(m_filepath.c_str(), _S("r")); + + std::string mainString; + char readBuf[1024]; + size_t readSz; + while ((readSz = fread(readBuf, 1, 1024, fp))) + mainString += std::string(readBuf, readSz); + fclose(fp); + + std::string::const_iterator begin = mainString.begin(); + std::string::const_iterator end = mainString.begin(); + + std::vector retval; + while (end != mainString.end()) + { + std::string::const_iterator origEnd = end; + if (CheckNewLineAdvance(end)) + { + if (begin != origEnd) + retval.push_back(std::string(begin, origEnd)); + begin = end; + continue; + } + ++end; + } + if (begin != end) + retval.push_back(std::string(begin, end)); + + return retval; + } + + void addLine(const std::string& line) + { + std::vector curLines = readLines(); + + FILE* fp = HECL::Fopen(m_filepath.c_str(), _S("w")); + for (std::string& line : curLines) + { + fwrite(line.data(), 1, line.length(), fp); + fwrite("\n", 1, 1, fp); + } + fwrite(line.data(), 1, line.length(), fp); + fwrite("\n", 1, 1, fp); + fclose(fp); + } + + void removeLine(const std::string& refLine) + { + std::vector curLines = readLines(); + + FILE* fp = HECL::Fopen(m_filepath.c_str(), _S("w")); + for (std::string& line : curLines) + { + if (line.compare(refLine)) + { + fwrite(line.data(), 1, line.length(), fp); + fwrite("\n", 1, 1, fp); + } + } + fclose(fp); + } + + bool checkForLine(const std::string& refLine) + { + std::vector curLines = readLines(); + for (std::string& line : curLines) + { + if (!line.compare(refLine)) + return true; + } + return false; + } + + }; + public: CProject(const std::string& rootPath) : m_rootPath(rootPath) @@ -25,20 +132,16 @@ public: if (!S_ISDIR(myStat.st_mode)) throw std::invalid_argument("provided path must be a directory; '" + m_rootPath + "' isn't"); - /* Create project directory */ - if (mkdir((m_rootPath + "/.hecl").c_str(), 0755)) - { - if (errno != EEXIST) - throw std::error_code(errno, std::system_category()); - } + /* Create project directory structure */ + HECL::MakeDir(m_rootPath + "/.hecl"); + HECL::MakeDir(m_rootPath + "/.hecl/cooked"); + HECL::MakeDir(m_rootPath + "/.hecl/config"); /* Create or open databases */ - m_db = new CSQLiteMain(m_rootPath + "/.hecl/main.db"); } ~CProject() { - delete m_db; } void registerLogger(HECL::TLogger logger) @@ -49,11 +152,11 @@ public: { } - bool addPath(const std::string& path) + bool addPaths(const std::vector& paths) { } - bool removePath(const std::string& path, bool recursive) + bool removePaths(const std::vector& paths, bool recursive) { } @@ -65,9 +168,21 @@ public: { } + const std::map& listPlatforms() + { + } + + bool enablePlatforms(const std::vector& platforms) + { + } + + bool disablePlatforms(const std::vector& platforms) + { + } + bool cookPath(const std::string& path, - std::function feedbackCb, - bool recursive) + std::function feedbackCb, + bool recursive) { } diff --git a/hecl/lib/database/CRuntime.cpp b/hecl/lib/database/CRuntime.cpp deleted file mode 100644 index 3a6731342..000000000 --- a/hecl/lib/database/CRuntime.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -#include "HECLDatabase.hpp" -#include "CSQLiteMain.hpp" - -namespace HECLDatabase -{ - -class CRuntime : public IRuntime -{ -}; - -} diff --git a/hecl/lib/database/CSQLiteCooked.hpp b/hecl/lib/database/CSQLiteCooked.hpp deleted file mode 100644 index f3ca12747..000000000 --- a/hecl/lib/database/CSQLiteCooked.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef CSQLITECOOKED_HPP -#define CSQLITECOOKED_HPP - -#include -#include -#include - -#include "HECLDatabase.hpp" -#include "sqlite_hecl_vfs.h" - -namespace HECLDatabase -{ - -static const char* skCOOKEDDBINIT = -"PRAGMA foreign_keys = ON;\n" -"CREATE TABLE IF NOT EXISTS cgrps(" - "grpid INTEGER PRIMARY KEY," /* Unique group identifier (from main DB) */ - "offset UNSIGNED INTEGER," /* Group-blob offset within package */ - "compLen UNSIGNED INTEGER," /* Compressed blob-length */ - "decompLen UNSIGNED INTEGER);\n" /* Decompressed blob-length */ -"CREATE TABLE IF NOT EXISTS cobjs(" - "objid INTEGER PRIMARY KEY," /* Unique object identifier (from main DB) */ - "type4cc UNSIGNED INTEGER," /* Type FourCC as claimed by first project class in dataspec */ - "loosegrp REFERENCES cgrps(grpid) ON DELETE SET NULL DEFAULT NULL);\n" /* single-object group of ungrouped object */ -"CREATE TABLE IF NOT EXISTS cgrplinks(" - "grpid REFERENCES cgrps(grpid) ON DELETE CASCADE," /* Group ref */ - "objid REFERENCES cobjs(objid) ON DELETE CASCADE," /* Object ref */ - "offset UNSIGNED INTEGER," /* Offset within decompressed group-blob */ - "decompLen UNSIGNED INTEGER," /* Decompressed object length */ - "UNIQUE (grpid, objid) ON CONFLICT IGNORE);\n" -"CREATE INDEX IF NOT EXISTS grpidx ON cgrplinks(grpid);\n"; - -#define PREPSTMT(stmtSrc, outVar)\ -if (sqlite3_prepare_v2(m_db, stmtSrc, 0, &outVar, NULL) != SQLITE_OK)\ -{\ - throw std::runtime_error(sqlite3_errmsg(m_db));\ - sqlite3_close(m_db);\ - return;\ -} - -class CSQLiteCooked -{ - sqlite3* m_db; - -public: - CSQLiteCooked(const char* path, bool readonly) - { - /* Open database connection */ - int errCode = 0; - if ((errCode = sqlite3_open_v2(path, &m_db, SQLITE_OPEN_READONLY, - "hecl_memlba")) != SQLITE_OK) - { - throw std::runtime_error(sqlite3_errstr(errCode)); - sqlite3_close(m_db); - return; - } - - /* Execute bootstrap statements */ - char* errMsg = NULL; - sqlite3_exec(m_db, skCOOKEDDBINIT, NULL, NULL, &errMsg); - if (errMsg) - { - throw std::runtime_error(errMsg); - sqlite3_free(errMsg); - sqlite3_close(m_db); - return; - } - - /* Precompile statements */ - - } - - ~CSQLiteCooked() - { - sqlite3_close(m_db); - } - -}; - -} - -#endif // CSQLITE_HPP diff --git a/hecl/lib/database/CSQLiteMain.hpp b/hecl/lib/database/CSQLiteMain.hpp deleted file mode 100644 index de232bd27..000000000 --- a/hecl/lib/database/CSQLiteMain.hpp +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef CSQLITEMAIN_HPP -#define CSQLITEMAIN_HPP - -#include -#include -#include - -#include "HECLDatabase.hpp" -#include "sqlite_hecl_vfs.h" - -namespace HECLDatabase -{ - -static const char* skMAINDBINIT = -"PRAGMA foreign_keys = ON;\n" -"CREATE TABLE IF NOT EXISTS grps(" - "grpid INTEGER PRIMARY KEY," /* Unique group identifier (used as in-game ref) */ - "path);\n" /* Directory path collecting working files for group */ -"CREATE TABLE IF NOT EXISTS objs(" - "objid INTEGER PRIMARY KEY," /* Unique object identifier (used as in-game ref) */ - "path," /* Path of working file */ - "subpath DEFAULT NULL," /* String name of sub-object within working file (i.e. blender object) */ - "cookedHash64 INTEGER DEFAULT NULL," /* Hash of last cooking pass */ - "cookedTime64 INTEGER DEFAULT NULL);\n"; /* UTC unix-time of last cooking pass */ - -#define PREPSTMT(stmtSrc, outVar)\ -if (sqlite3_prepare_v2(m_db, stmtSrc, 0, &outVar, NULL) != SQLITE_OK)\ -{\ - throw std::runtime_error(sqlite3_errmsg(m_db));\ - sqlite3_close(m_db);\ - return;\ -} - -class CSQLiteMain -{ - sqlite3* m_db; - - struct SCloseBuf - { - void* buf = NULL; - size_t sz = 0; - }; - static void _vfsClose(void* buf, size_t bufSz, SCloseBuf* ctx) - { - ctx->buf = buf; - ctx->sz = bufSz; - } - -public: - CSQLiteMain(const std::string& path) - { - /* Open database connection */ - int errCode = 0; - if ((errCode = sqlite3_open(path.c_str(), &m_db) != SQLITE_OK)) - { - throw std::runtime_error(sqlite3_errstr(errCode)); - sqlite3_close(m_db); - return; - } - - /* Execute bootstrap statements */ - char* errMsg = NULL; - sqlite3_exec(m_db, skMAINDBINIT, NULL, NULL, &errMsg); - if (errMsg) - { - throw std::runtime_error(errMsg); - sqlite3_free(errMsg); - sqlite3_close(m_db); - return; - } - - /* Precompile statements */ - - } - - ~CSQLiteMain() - { - sqlite3_close(m_db); - } - - - void* fillDBBuffer(size_t& bufSzOut) const - { - /* Instructs vfs that a close operation is premature and buffer should be freed */ - sqlite_hecl_mem_vfs_register(NULL, NULL); - - /* Open pure-memory DB */ - sqlite3* memDb; - int errCode; - if ((errCode = sqlite3_open_v2("", &memDb, SQLITE_OPEN_READWRITE, "hecl_mem")) != SQLITE_OK) - { - throw std::runtime_error(sqlite3_errstr(errCode)); - sqlite3_close(memDb); - return NULL; - } - - /* Perform backup (row copy) */ - sqlite3_backup* backup = sqlite3_backup_init(memDb, "main", m_db, "main"); - if (!backup) - { - throw std::runtime_error(sqlite3_errmsg(memDb)); - sqlite3_close(memDb); - return NULL; - } - sqlite3_backup_step(backup, -1); - sqlite3_backup_finish(backup); - - /* Now a close operation is useful; register close callback */ - SCloseBuf closeBuf; - sqlite_hecl_mem_vfs_register((TCloseCallback)_vfsClose, &closeBuf); - sqlite3_close(memDb); - - /* This should be set by close callback */ - if (!closeBuf.buf) - { - throw std::runtime_error("close operation did not write buffer"); - return NULL; - } - - /* All good! */ - bufSzOut = closeBuf.sz; - return closeBuf.buf; - } - - static void freeDBBuffer(void* buf) - { - sqlite3_free(buf); - } - -}; - -} - -#endif // CSQLITEMAIN_HPP diff --git a/hecl/lib/database/database.pri b/hecl/lib/database/database.pri index 977f71b8d..0b8911d0d 100644 --- a/hecl/lib/database/database.pri +++ b/hecl/lib/database/database.pri @@ -1,11 +1,4 @@ -HEADERS += \ - $$PWD/sqlite_hecl_vfs.h \ - $$PWD/CSQLiteMain.hpp \ - $$PWD/CSQLiteCooked.hpp +HEADERS += SOURCES += \ - $$PWD/CRuntime.cpp \ - $$PWD/CProject.cpp \ - $$PWD/sqlite_hecl_mem_vfs.c \ - $$PWD/sqlite_hecl_memlba_vfs.c \ - $$PWD/sqlite_hecl_memlba_make.c + $$PWD/CProject.cpp diff --git a/hecl/lib/database/sqlite_hecl_mem_vfs.c b/hecl/lib/database/sqlite_hecl_mem_vfs.c deleted file mode 100644 index 0dfa53e1d..000000000 --- a/hecl/lib/database/sqlite_hecl_mem_vfs.c +++ /dev/null @@ -1,386 +0,0 @@ -#include "sqlite_hecl_vfs.h" - -#include "sqlite3.h" -#include -#include -#include -#include -#include - -/* - * Modified test_onefile.c VFS for sqlite3 - * - * This VFS gets registered with sqlite to access an in-memory, - * block-compressed LBA. It's designed for read-only access of - * a packed object-database. - * - * Journal and temp read/write is unsupported and will call abort - * if attempted. - */ - -/* - * App-supplied callback called when file is closed (ready to access) - */ -static TCloseCallback CLOSE_CALLBACK = NULL; -static void* CLOSE_CTX = NULL; - -typedef struct mem_file mem_file; -struct mem_file -{ - sqlite3_file base; - int nSize; - int nAlloc; - char* zAlloc; -}; - -/* -** Method declarations for mem_file. -*/ -static int memClose(sqlite3_file*); -static int memRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -static int memWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); -static int memTruncate(sqlite3_file*, sqlite3_int64 size); -static int memSync(sqlite3_file*, int flags); -static int memFileSize(sqlite3_file*, sqlite3_int64* pSize); -static int memLock(sqlite3_file*, int); -static int memUnlock(sqlite3_file*, int); -static int memCheckReservedLock(sqlite3_file*, int* pResOut); -static int memFileControl(sqlite3_file*, int op, void* pArg); -static int memSectorSize(sqlite3_file*); -static int memDeviceCharacteristics(sqlite3_file*); - -/* -** Method declarations for fs_vfs. -*/ -static int memOpen(sqlite3_vfs*, const char*, sqlite3_file*, int , int*); -static int memDelete(sqlite3_vfs*, const char* zName, int syncDir); -static int memAccess(sqlite3_vfs*, const char* zName, int flags, int*); -static int memFullPathname(sqlite3_vfs*, const char* zName, int nOut, char* zOut); -static void* memDlOpen(sqlite3_vfs*, const char* zFilename); -static void memDlError(sqlite3_vfs*, int nByte, char* zErrMsg); -static void (*memDlSym(sqlite3_vfs*, void*, const char* zSymbol))(void); -static void memDlClose(sqlite3_vfs*, void*); -static int memRandomness(sqlite3_vfs*, int nByte, char* zOut); -static int memSleep(sqlite3_vfs*, int microseconds); -static int memCurrentTime(sqlite3_vfs*, double*); - -static sqlite3_vfs mem_vfs = -{ - 1, /* iVersion */ - 0, /* szOsFile */ - 0, /* mxPathname */ - 0, /* pNext */ - "hecl_mem", /* zName */ - 0, /* pAppData */ - memOpen, /* xOpen */ - memDelete, /* xDelete */ - memAccess, /* xAccess */ - memFullPathname, /* xFullPathname */ - memDlOpen, /* xDlOpen */ - memDlError, /* xDlError */ - memDlSym, /* xDlSym */ - memDlClose, /* xDlClose */ - memRandomness, /* xRandomness */ - memSleep, /* xSleep */ - memCurrentTime, /* xCurrentTime */ - 0 /* xCurrentTimeInt64 */ -}; - -static sqlite3_io_methods mem_io_methods = -{ - 1, /* iVersion */ - memClose, /* xClose */ - memRead, /* xRead */ - memWrite, /* xWrite */ - memTruncate, /* xTruncate */ - memSync, /* xSync */ - memFileSize, /* xFileSize */ - memLock, /* xLock */ - memUnlock, /* xUnlock */ - memCheckReservedLock, /* xCheckReservedLock */ - memFileControl, /* xFileControl */ - memSectorSize, /* xSectorSize */ - memDeviceCharacteristics, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0, /* xShmUnmap */ - 0, - 0 -}; - -/* Useful macros used in several places */ -#define MIN(x,y) ((x)<(y)?(x):(y)) -#define MAX(x,y) ((x)>(y)?(x):(y)) - - -/* -** Close a mem-file. -*/ -static int memClose(sqlite3_file* pFile) -{ - mem_file* pTmp = (mem_file*)pFile; - if(CLOSE_CALLBACK) - CLOSE_CALLBACK(pTmp->zAlloc, pTmp->nSize, CLOSE_CTX); - else - sqlite3_free(pTmp->zAlloc); - return SQLITE_OK; -} - -/* -** Read data from a mem-file. -*/ -static int memRead( - sqlite3_file* pFile, - void* zBuf, - int iAmt, - sqlite_int64 iOfst -) -{ - mem_file* pTmp = (mem_file*)pFile; - if((iAmt + iOfst) > pTmp->nSize) - return SQLITE_IOERR_SHORT_READ; - memcpy(zBuf, &pTmp->zAlloc[iOfst], iAmt); - return SQLITE_OK; -} - -/* -** Write data to a mem-file. -*/ -static int memWrite( - sqlite3_file* pFile, - const void* zBuf, - int iAmt, - sqlite_int64 iOfst -) -{ - mem_file* pTmp = (mem_file*)pFile; - if((iAmt + iOfst) > pTmp->nAlloc) - { - int nNew = (int)(2 * (iAmt + iOfst + pTmp->nAlloc)); - char* zNew = sqlite3_realloc(pTmp->zAlloc, nNew); - if(!zNew) - return SQLITE_NOMEM; - pTmp->zAlloc = zNew; - pTmp->nAlloc = nNew; - } - memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt); - pTmp->nSize = (int)MAX(pTmp->nSize, iOfst + iAmt); - return SQLITE_OK; -} - -/* -** Truncate a mem-file. -*/ -static int memTruncate(sqlite3_file* pFile, sqlite_int64 size) -{ - mem_file* pTmp = (mem_file*)pFile; - pTmp->nSize = (int)MIN(pTmp->nSize, size); - return SQLITE_OK; -} - -/* -** Sync a mem-file. -*/ -static int memSync(sqlite3_file* pFile, int flags) -{ - return SQLITE_OK; -} - -/* -** Return the current file-size of a mem-file. -*/ -static int memFileSize(sqlite3_file* pFile, sqlite_int64* pSize) -{ - mem_file* pTmp = (mem_file*)pFile; - *pSize = pTmp->nSize; - return SQLITE_OK; -} - -/* -** Lock a mem-file. -*/ -static int memLock(sqlite3_file* pFile, int eLock) -{ - return SQLITE_OK; -} - -/* -** Unlock a mem-file. -*/ -static int memUnlock(sqlite3_file* pFile, int eLock) -{ - return SQLITE_OK; -} - -/* -** Check if another file-handle holds a RESERVED lock on a mem-file. -*/ -static int memCheckReservedLock(sqlite3_file* pFile, int* pResOut) -{ - *pResOut = 0; - return SQLITE_OK; -} - -/* -** File control method. For custom operations on a mem-file. -*/ -static int memFileControl(sqlite3_file* pFile, int op, void* pArg) -{ - return SQLITE_OK; -} - -/* -** Return the sector-size in bytes for a mem-file. -*/ -static int memSectorSize(sqlite3_file* pFile) -{ - return 0; -} - -/* -** Return the device characteristic flags supported by a mem-file. -*/ -static int memDeviceCharacteristics(sqlite3_file* pFile) -{ - return 0; -} - -/* -** Open an mem file handle. -*/ -static int memOpen( - sqlite3_vfs* pVfs, - const char* zName, - sqlite3_file* pFile, - int flags, - int* pOutFlags -) -{ - if ((flags & SQLITE_OPEN_MAIN_DB) != SQLITE_OPEN_MAIN_DB) - { - fprintf(stderr, "the sqlite hecl mem VFS only supports main-db reading/writing\n"); - return SQLITE_CANTOPEN; - } - mem_file* p2 = (mem_file*)pFile; - memset(p2, 0, sizeof(*p2)); - p2->base.pMethods = &mem_io_methods; - return SQLITE_OK; -} - -/* -** Delete the file located at zPath. If the dirSync argument is true, -** ensure the file-system modifications are synced to disk before -** returning. -*/ -static int memDelete(sqlite3_vfs* pVfs, const char* zPath, int dirSync) -{ - return SQLITE_OK; -} - -/* -** Test for access permissions. Return true if the requested permission -** is available, or false otherwise. -*/ -static int memAccess( - sqlite3_vfs* pVfs, - const char* zPath, - int flags, - int* pResOut -) -{ - if(flags & SQLITE_ACCESS_READ | SQLITE_ACCESS_READWRITE) - return 1; - return 0; -} - -/* -** Populate buffer zOut with the full canonical pathname corresponding -** to the pathname in zPath. zOut is guaranteed to point to a buffer -** of at least (FS_MAX_PATHNAME+1) bytes. -*/ -static int memFullPathname( - sqlite3_vfs* pVfs, /* Pointer to vfs object */ - const char* zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char* zOut) /* Output buffer */ -{ - strncpy(zOut, zPath, nOut); - return SQLITE_OK; -} - -/* -** Open the dynamic library located at zPath and return a handle. -*/ -static void* memDlOpen(sqlite3_vfs* pVfs, const char* zPath) -{ - return NULL; -} - -/* -** Populate the buffer zErrMsg (size nByte bytes) with a human readable -** utf-8 string describing the most recent error encountered associated -** with dynamic libraries. -*/ -static void memDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) -{ -} - -/* -** Return a pointer to the symbol zSymbol in the dynamic library pHandle. -*/ -static void (*memDlSym(sqlite3_vfs* pVfs, void* pH, const char* zSym))(void) -{ -} - -/* -** Close the dynamic library handle pHandle. -*/ -static void memDlClose(sqlite3_vfs* pVfs, void* pHandle) -{ -} - -/* -** Populate the buffer pointed to by zBufOut with nByte bytes of -** random data. -*/ -static int memRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) -{ - for(int i = 0 ; i < nByte ; ++i) - zBufOut[i] = rand(); - return nByte; -} - -/* -** Sleep for nMicro microseconds. Return the number of microseconds -** actually slept. -*/ -static int memSleep(sqlite3_vfs* pVfs, int nMicro) -{ - int seconds = (nMicro + 999999) / 1000000; - sleep(seconds); - return seconds * 1000000; -} - -/* -** Return the current time as a Julian Day number in *pTimeOut. -*/ -static int memCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) -{ - *pTimeOut = 0.0; - return 0; -} - -/* -** This procedure registers the mem vfs with SQLite. If the argument is -** true, the mem vfs becomes the new default vfs. It is the only publicly -** available function in this file. -*/ -int sqlite_hecl_mem_vfs_register(TCloseCallback closeCb, void* ctx) -{ - CLOSE_CALLBACK = closeCb; - CLOSE_CTX = ctx; - if(mem_vfs.szOsFile) return SQLITE_OK; - mem_vfs.szOsFile = sizeof(mem_file); - return sqlite3_vfs_register(&mem_vfs, 0); -} diff --git a/hecl/lib/database/sqlite_hecl_memlba_make.c b/hecl/lib/database/sqlite_hecl_memlba_make.c deleted file mode 100644 index e222c8b55..000000000 --- a/hecl/lib/database/sqlite_hecl_memlba_make.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "sqlite_hecl_vfs.h" -#include -#include -#include - -/* - * Block-compression LBA generator used for storing packed sqlite3 database - */ - -#define BLOCK_SIZE 0x4000 -#define ROUND_UP_BLOCK(val) (((val) + (BLOCK_SIZE-1)) & ~(BLOCK_SIZE-1)) - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -static inline uint32_t makeu32(uint32_t val, bool bigEndian) -{ - if (!bigEndian) - return val; -#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 -} -#else -static inline uint32_t makeu32(uint32_t val, bool bigEndian) -{ - if (bigEndian) - return val; -#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 -} -#endif - -/* Useful macros used in several places */ -#define MIN(x,y) ((x)<(y)?(x):(y)) -#define MAX(x,y) ((x)>(y)?(x):(y)) - -void sqlite_hecl_memlba_make(FILE* fout, void* bufin, size_t bufinLen, bool bigEndian) -{ - unsigned i; - - /* Magic */ - fwrite("HLPK", 1, 4, fout); - - /* Block size */ - uint32_t blockSize_s = makeu32(BLOCK_SIZE, bigEndian); - fwrite(&blockSize_s, 1, 4, fout); - - /* Block count */ - size_t blockCount = ROUND_UP_BLOCK(bufinLen) / BLOCK_SIZE; - uint32_t blockCount_s = makeu32(blockCount, bigEndian); - fwrite(&blockCount_s, 1, 4, fout); - - /* Header+TOC+DB Size */ - fwrite("\0\0\0\0", 1, 4, fout); - - /* Block TOC buffer and file-space */ - uint32_t* blockTOC = calloc(blockCount+1, 4); - for (i=0 ; i -#include -#include -#include -#include -#include - -#include - -/* - * Modified test_onefile.c VFS for sqlite3 - * - * This VFS gets registered with sqlite to access an in-memory, - * block-compressed LBA. It's designed for read-only access of - * a packed object-database. - * - * Journal and temp read/write is unsupported and will call abort - * if attempted. - */ - -/* - * App-supplied pointer to head buffer (Header+TOC+DB) - */ -static void* HEAD_BUF = NULL; - -#define BLOCK_SLOTS 4 - -typedef struct memlba_file memlba_file; -struct memlba_file -{ - sqlite3_file base; - struct - { - char magic[4]; - uint32_t blockSize; - uint32_t blockCount; - uint32_t headSz; - uint32_t blockTOC[]; - }* headBuf; - void* cachedBlockBufs[BLOCK_SLOTS]; - /* All initialized to -1 */ - int cachedBlockIndices[BLOCK_SLOTS]; - /* Ages start at 0, newly inserted block is 1. - * Non-0 blocks incremented on every insertion. - * If any slot is BLOCK_SLOTS upon insertion, surrounding blocks - * are incremented and that slot is reset to 1 (oldest-block caching) - */ - int cachedBlockAges[BLOCK_SLOTS]; - z_stream zstrm; -}; - -static int newBlockSlot(memlba_file* file) -{ - unsigned i; - for (i=0 ; icachedBlockAges[i] != 0) - ++file->cachedBlockAges[i]; - for (i=0 ; icachedBlockAges[i] == 0) - { - file->cachedBlockAges[i] = 1; - return i; - } - for (i=0 ; icachedBlockAges[i] == BLOCK_SLOTS+1) - { - file->cachedBlockAges[i] = 1; - return i; - } - /* Shouldn't happen (fallback) */ - for (i=1 ; icachedBlockAges[i] = 0; - file->cachedBlockIndices[i] = -1; - } - file->cachedBlockAges[0] = 1; - return 0; -} - -static void decompressBlock(memlba_file* file, unsigned blockIdx, int targetSlot) -{ - if (blockIdx >= file->headBuf->blockCount) - { - fprintf(stderr, "exceeded memlba block range"); - abort(); - } - void* dbBlock = ((void*)file->headBuf) + file->headBuf->blockTOC[blockIdx]; - file->zstrm.next_in = dbBlock; - file->zstrm.avail_in = file->headBuf->blockTOC[blockIdx+1] - file->headBuf->blockTOC[blockIdx]; - file->zstrm.next_out = file->cachedBlockBufs[targetSlot]; - file->zstrm.avail_out = file->headBuf->blockSize; - inflate(&file->zstrm, Z_FINISH); - inflateReset(&file->zstrm); -} - -static int getBlockSlot(memlba_file* file, int blockIdx) -{ - unsigned i; - for (i=0 ; icachedBlockIndices[i] != blockIdx) - return i; - int newSlot = newBlockSlot(file); - file->cachedBlockIndices[newSlot] = blockIdx; - decompressBlock(file, blockIdx, newSlot); - return newSlot; -} - -/* -** Method declarations for memlba_file. -*/ -static int memlbaClose(sqlite3_file*); -static int memlbaRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -static int memlbaWrite(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); -static int memlbaTruncate(sqlite3_file*, sqlite3_int64 size); -static int memlbaSync(sqlite3_file*, int flags); -static int memlbaFileSize(sqlite3_file*, sqlite3_int64* pSize); -static int memlbaLock(sqlite3_file*, int); -static int memlbaUnlock(sqlite3_file*, int); -static int memlbaCheckReservedLock(sqlite3_file*, int* pResOut); -static int memlbaFileControl(sqlite3_file*, int op, void* pArg); -static int memlbaSectorSize(sqlite3_file*); -static int memlbaDeviceCharacteristics(sqlite3_file*); - -/* -** Method declarations for fs_vfs. -*/ -static int memlbaOpen(sqlite3_vfs*, const char*, sqlite3_file*, int , int*); -static int memlbaDelete(sqlite3_vfs*, const char* zName, int syncDir); -static int memlbaAccess(sqlite3_vfs*, const char* zName, int flags, int*); -static int memlbaFullPathname(sqlite3_vfs*, const char* zName, int nOut, char* zOut); -static void* memlbaDlOpen(sqlite3_vfs*, const char* zFilename); -static void memlbaDlError(sqlite3_vfs*, int nByte, char* zErrMsg); -static void (*memlbaDlSym(sqlite3_vfs*, void*, const char* zSymbol))(void); -static void memlbaDlClose(sqlite3_vfs*, void*); -static int memlbaRandomness(sqlite3_vfs*, int nByte, char* zOut); -static int memlbaSleep(sqlite3_vfs*, int microseconds); -static int memlbaCurrentTime(sqlite3_vfs*, double*); - -static sqlite3_vfs memlba_vfs = -{ - 1, /* iVersion */ - 0, /* szOsFile */ - 0, /* mxPathname */ - 0, /* pNext */ - "hecl_memlba", /* zName */ - 0, /* pAppData */ - memlbaOpen, /* xOpen */ - memlbaDelete, /* xDelete */ - memlbaAccess, /* xAccess */ - memlbaFullPathname, /* xFullPathname */ - memlbaDlOpen, /* xDlOpen */ - memlbaDlError, /* xDlError */ - memlbaDlSym, /* xDlSym */ - memlbaDlClose, /* xDlClose */ - memlbaRandomness, /* xRandomness */ - memlbaSleep, /* xSleep */ - memlbaCurrentTime, /* xCurrentTime */ - 0, 0, 0, 0, 0 -}; - -static sqlite3_io_methods memlba_io_methods = -{ - 1, /* iVersion */ - memlbaClose, /* xClose */ - memlbaRead, /* xRead */ - memlbaWrite, /* xWrite */ - memlbaTruncate, /* xTruncate */ - memlbaSync, /* xSync */ - memlbaFileSize, /* xFileSize */ - memlbaLock, /* xLock */ - memlbaUnlock, /* xUnlock */ - memlbaCheckReservedLock, /* xCheckReservedLock */ - memlbaFileControl, /* xFileControl */ - memlbaSectorSize, /* xSectorSize */ - memlbaDeviceCharacteristics, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0, /* xShmUnmap */ - 0, - 0 -}; - -/* Useful macros used in several places */ -#define MIN(x,y) ((x)<(y)?(x):(y)) -#define MAX(x,y) ((x)>(y)?(x):(y)) - - -/* -** Close a memlba-file. -*/ -static int memlbaClose(sqlite3_file* pFile) -{ - memlba_file* pTmp = (memlba_file*)pFile; - free(pTmp->headBuf); - free(pTmp->cachedBlockBufs[0]); - inflateEnd(&pTmp->zstrm); - return SQLITE_OK; -} - -/* -** Read data from a memlba-file. -*/ -static int memlbaRead( - sqlite3_file* pFile, - void* zBuf, - int iAmt, - sqlite_int64 iOfst -) -{ - memlba_file* pTmp = (memlba_file*)pFile; - - unsigned blockIdx = iOfst / pTmp->headBuf->blockSize; - unsigned firstOff = iOfst % pTmp->headBuf->blockSize; - unsigned firstRemBytes = pTmp->headBuf->blockSize - firstOff; - - int slot = getBlockSlot(pTmp, blockIdx); - unsigned toRead = MIN((unsigned)iAmt, firstRemBytes); - memcpy(zBuf, pTmp->cachedBlockBufs[slot] + firstOff, toRead); - iAmt -= toRead; - zBuf += toRead; - - while (iAmt > 0) - { - slot = getBlockSlot(pTmp, ++blockIdx); - toRead = MIN((unsigned)iAmt, pTmp->headBuf->blockSize); - memcpy(zBuf, pTmp->cachedBlockBufs[slot], toRead); - iAmt -= toRead; - zBuf += toRead; - } - - return SQLITE_OK; -} - -/* -** Write data to a memlba-file. -*/ -static int memlbaWrite( - sqlite3_file* pFile, - const void* zBuf, - int iAmt, - sqlite_int64 iOfst -) -{ - (void)pFile; (void)zBuf; (void)iAmt; (void)iOfst; - return SQLITE_OK; -} - -/* -** Truncate a memlba-file. -*/ -static int memlbaTruncate(sqlite3_file* pFile, sqlite_int64 size) -{ - (void)pFile; (void)size; - return SQLITE_OK; -} - -/* -** Sync a memlba-file. -*/ -static int memlbaSync(sqlite3_file* pFile, int flags) -{ - (void)pFile; (void)flags; - return SQLITE_OK; -} - -/* -** Return the current file-size of a memlba-file. -*/ -static int memlbaFileSize(sqlite3_file* pFile, sqlite_int64* pSize) -{ - memlba_file* pTmp = (memlba_file*)pFile; - *pSize = pTmp->headBuf->headSz - pTmp->headBuf->blockTOC[0]; - return SQLITE_OK; -} - -/* -** Lock a memlba-file. -*/ -static int memlbaLock(sqlite3_file* pFile, int eLock) -{ - (void)pFile; (void)eLock; - return SQLITE_OK; -} - -/* -** Unlock a memlba-file. -*/ -static int memlbaUnlock(sqlite3_file* pFile, int eLock) -{ - (void)pFile; (void)eLock; - return SQLITE_OK; -} - -/* -** Check if another file-handle holds a RESERVED lock on a memlba-file. -*/ -static int memlbaCheckReservedLock(sqlite3_file* pFile, int* pResOut) -{ - (void)pFile; - *pResOut = 0; - return SQLITE_OK; -} - -/* -** File control method. For custom operations on a memlba-file. -*/ -static int memlbaFileControl(sqlite3_file* pFile, int op, void* pArg) -{ - (void)pFile; (void)op; (void)pArg; - return SQLITE_OK; -} - -/* -** Return the sector-size in bytes for a memlba-file. -*/ -static int memlbaSectorSize(sqlite3_file* pFile) -{ - (void)pFile; - return 0; -} - -/* -** Return the device characteristic flags supported by a memlba-file. -*/ -static int memlbaDeviceCharacteristics(sqlite3_file* pFile) -{ - (void)pFile; - return 0; -} - -/* -** Open an memlba file handle. -*/ -static int memlbaOpen( - sqlite3_vfs* pVfs, - const char* zName, - sqlite3_file* pFile, - int flags, - int* pOutFlags -) -{ - (void)pVfs; (void)zName; (void)pOutFlags; - if ((flags & SQLITE_OPEN_MAIN_DB) != SQLITE_OPEN_MAIN_DB || - (flags & SQLITE_OPEN_READONLY) != SQLITE_OPEN_READONLY) - { - fprintf(stderr, "the sqlite hecl memlba VFS only supports main-db reading\n"); - return SQLITE_CANTOPEN; - } - memlba_file* p2 = (memlba_file*)pFile; - memset(p2, 0, sizeof(*p2)); - p2->base.pMethods = &memlba_io_methods; - inflateInit(&p2->zstrm); - p2->headBuf = HEAD_BUF; - unsigned i; - void* blockBufs = calloc(BLOCK_SLOTS, p2->headBuf->blockSize); - for (i=0 ; icachedBlockBufs[i] = blockBufs + p2->headBuf->blockSize * i; - p2->cachedBlockIndices[i] = -1; - } - return SQLITE_OK; -} - -/* -** Delete the file located at zPath. If the dirSync argument is true, -** ensure the file-system modifications are synced to disk before -** returning. -*/ -static int memlbaDelete(sqlite3_vfs* pVfs, const char* zPath, int dirSync) -{ - (void)pVfs; (void)zPath; (void)dirSync; - return SQLITE_OK; -} - -/* -** Test for access permissions. Return true if the requested permission -** is available, or false otherwise. -*/ -static int memlbaAccess( - sqlite3_vfs* pVfs, - const char* zPath, - int flags, - int* pResOut -) -{ - (void)pVfs; (void)zPath; (void)pResOut; - if (flags & (SQLITE_ACCESS_READ | SQLITE_ACCESS_READWRITE)) - return 1; - return 0; -} - -/* -** Populate buffer zOut with the full canonical pathname corresponding -** to the pathname in zPath. zOut is guaranteed to point to a buffer -** of at least (FS_MAX_PATHNAME+1) bytes. -*/ -static int memlbaFullPathname( - sqlite3_vfs* pVfs, /* Pointer to vfs object */ - const char* zPath, /* Possibly relative input path */ - int nOut, /* Size of output buffer in bytes */ - char* zOut) /* Output buffer */ -{ - (void)pVfs; - strncpy(zOut, zPath, nOut); - return SQLITE_OK; -} - -/* -** Open the dynamic library located at zPath and return a handle. -*/ -static void* memlbaDlOpen(sqlite3_vfs* pVfs, const char* zPath) -{ - (void)pVfs; (void)zPath; - return NULL; -} - -/* -** Populate the buffer zErrMsg (size nByte bytes) with a human readable -** utf-8 string describing the most recent error encountered associated -** with dynamic libraries. -*/ -static void memlbaDlError(sqlite3_vfs* pVfs, int nByte, char* zErrMsg) -{ - (void)pVfs; (void)nByte; (void)zErrMsg; -} - -/* -** Return a pointer to the symbol zSymbol in the dynamic library pHandle. -*/ -static void (*memlbaDlSym(sqlite3_vfs* pVfs, void* pH, const char* zSym))(void) -{ - (void)pVfs; (void)pH; (void)zSym; - return NULL; -} - -/* -** Close the dynamic library handle pHandle. -*/ -static void memlbaDlClose(sqlite3_vfs* pVfs, void* pHandle) -{ - (void)pVfs; (void)pHandle; -} - -/* -** Populate the buffer pointed to by zBufOut with nByte bytes of -** random data. -*/ -static int memlbaRandomness(sqlite3_vfs* pVfs, int nByte, char* zBufOut) -{ - (void)pVfs; - for(int i = 0 ; i < nByte ; ++i) - zBufOut[i] = rand(); - return nByte; -} - -/* -** Sleep for nMicro microseconds. Return the number of microseconds -** actually slept. -*/ -static int memlbaSleep(sqlite3_vfs* pVfs, int nMicro) -{ - (void)pVfs; - int seconds = (nMicro + 999999) / 1000000; - sleep(seconds); - return seconds * 1000000; -} - -/* -** Return the current time as a Julian Day number in *pTimeOut. -*/ -static int memlbaCurrentTime(sqlite3_vfs* pVfs, double* pTimeOut) -{ - (void)pVfs; - *pTimeOut = 0.0; - return 0; -} - -/* -** This procedure registers the memlba vfs with SQLite. If the argument is -** true, the memlba vfs becomes the new default vfs. It is the only publicly -** available function in this file. -*/ -int sqlite_hecl_memlba_vfs_register(void* headBuf) -{ - HEAD_BUF = headBuf; - if(memlba_vfs.szOsFile) return SQLITE_OK; - memlba_vfs.szOsFile = sizeof(memlba_file); - return sqlite3_vfs_register(&memlba_vfs, 0); -} diff --git a/hecl/lib/database/sqlite_hecl_vfs.h b/hecl/lib/database/sqlite_hecl_vfs.h deleted file mode 100644 index 481a9fbc9..000000000 --- a/hecl/lib/database/sqlite_hecl_vfs.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SQLITE_HECL_VFS -#define SQLITE_HECL_VFS - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -typedef void(*TCloseCallback)(void* buf, size_t bufLen, void* ctx); -int sqlite_hecl_mem_vfs_register(TCloseCallback closeCb, void* ctx); -int sqlite_hecl_memlba_vfs_register(void* headBuf); -void sqlite_hecl_memlba_make(FILE* fout, void* bufin, size_t bufinLen, bool bigEndian); - -#ifdef __cplusplus -} -#endif - -#endif // SQLITE_HECL_VFS diff --git a/hecl/lib/lib.pro b/hecl/lib/lib.pro index ecb6e5aec..f3a7d8c58 100644 --- a/hecl/lib/lib.pro +++ b/hecl/lib/lib.pro @@ -1,5 +1,5 @@ TEMPLATE = lib -CONFIG += staticlib +CONFIG += staticlib c++11 TARGET = hecl CONFIG -= Qt QT = @@ -17,4 +17,6 @@ include (database/database.pri) include (runtime/runtime.pri) SOURCES += \ - HECL.cpp + HECL.cpp \ + ProjectPath.cpp \ + WideStringConvert.cpp diff --git a/hecl/lib/runtime/HECLRuntime.cpp b/hecl/lib/runtime/HECLRuntime.cpp index e69de29bb..a2e868d15 100644 --- a/hecl/lib/runtime/HECLRuntime.cpp +++ b/hecl/lib/runtime/HECLRuntime.cpp @@ -0,0 +1,23 @@ +#include "HECLRuntime.hpp" + +namespace HECLRuntime +{ + +HECLRuntime::HECLRuntime(const HECL::SystemString& hlpkDirectory) +{ +} + +HECLRuntime::~HECLRuntime() +{ +} + +std::shared_ptr HECLRuntime::loadSync(const HECL::Hash& pathHash) +{ +} + +std::shared_ptr HECLRuntime::loadAsync(const HECL::Hash& pathHash, + SGroupLoadStatus* statusOut) +{ +} + +}