From 662c2bc689588f7bd64a177fc3d66bfbac0fa95a Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 17 Jul 2015 18:35:01 -1000 Subject: [PATCH] extract behavior tweaks --- hecl/driver/ToolExtract.hpp | 1 + hecl/driver/ToolSpec.hpp | 21 ++++--- hecl/extern/RetroCommon | 2 +- hecl/include/HECL/Database.hpp | 32 ++++++++-- hecl/include/HECL/HECL.hpp | 49 +++++++++++----- hecl/lib/Database/ASEngine.cpp | 1 + hecl/lib/Database/Project.cpp | 32 ++++++---- hecl/lib/ProjectPath.cpp | 103 +++++++++++++++------------------ 8 files changed, 146 insertions(+), 95 deletions(-) diff --git a/hecl/driver/ToolExtract.hpp b/hecl/driver/ToolExtract.hpp index c8ce25353..0f6e46087 100644 --- a/hecl/driver/ToolExtract.hpp +++ b/hecl/driver/ToolExtract.hpp @@ -20,6 +20,7 @@ public: m_einfo.srcpath = m_info.args[0]; m_einfo.extractArgs.reserve(info.args.size() - 1); + m_einfo.force = info.force; for (std::vector::const_iterator it=info.args.begin() + 1; it != info.args.end(); ++it) diff --git a/hecl/driver/ToolSpec.hpp b/hecl/driver/ToolSpec.hpp index f0c74f2e0..760b4f558 100644 --- a/hecl/driver/ToolSpec.hpp +++ b/hecl/driver/ToolSpec.hpp @@ -21,7 +21,8 @@ public: return; if (!info.project) - LogModule.report(LogVisor::FatalError, "hecl spec must be ran within a project directory"); + LogModule.report(LogVisor::FatalError, + "hecl spec must be ran within a project directory"); const auto& specs = info.project->getDataSpecs(); HECL::SystemString firstArg = info.args[0]; @@ -47,14 +48,16 @@ public: bool found = false; for (auto& spec : specs) { - if (!spec.first.m_name.compare(*it)) + if (!spec.spec.m_name.compare(*it)) { found = true; break; } } if (!found) - LogModule.report(LogVisor::FatalError, _S("'%s' is not found in the dataspec registry"), it->c_str()); + LogModule.report(LogVisor::FatalError, + _S("'%s' is not found in the dataspec registry"), + it->c_str()); } } @@ -107,17 +110,17 @@ public: for (auto& spec : specs) { if (XTERM_COLOR) - HECL::Printf(_S("" BOLD CYAN "%s" NORMAL ""), spec.first.m_name.c_str()); + HECL::Printf(_S("" BOLD CYAN "%s" NORMAL ""), spec.spec.m_name.c_str()); else - HECL::Printf(_S("%s"), spec.first.m_name.c_str()); - if (spec.second) + HECL::Printf(_S("%s"), spec.spec.m_name.c_str()); + if (spec.active) { if (XTERM_COLOR) HECL::Printf(_S(" " BOLD GREEN "[ENABLED]" NORMAL "")); else HECL::Printf(_S(" [ENABLED]")); } - HECL::Printf(_S("\n %s\n"), spec.first.m_desc.c_str()); + HECL::Printf(_S("\n %s\n"), spec.spec.m_desc.c_str()); } return 0; } @@ -131,9 +134,9 @@ public: HECL::ToLower(itName); for (auto& spec : specs) { - if (!spec.first.m_name.compare(itName)) + if (!spec.spec.m_name.compare(itName)) { - opSpecs.push_back(spec.first.m_name); + opSpecs.push_back(spec.spec.m_name); break; } } diff --git a/hecl/extern/RetroCommon b/hecl/extern/RetroCommon index 94d84d899..dea341d27 160000 --- a/hecl/extern/RetroCommon +++ b/hecl/extern/RetroCommon @@ -1 +1 @@ -Subproject commit 94d84d8991dd0c6b5fdb5557476eb4c3421d7b09 +Subproject commit dea341d27b52e60ab3b8f4ee7e7b808ef516b39d diff --git a/hecl/include/HECL/Database.hpp b/hecl/include/HECL/Database.hpp index 1f80ac6ae..56ac3301b 100644 --- a/hecl/include/HECL/Database.hpp +++ b/hecl/include/HECL/Database.hpp @@ -200,6 +200,7 @@ public: { SystemString srcpath; std::vector extractArgs; + bool force; }; /** @@ -383,10 +384,17 @@ public: class Project { public: - typedef std::vector> CompiledSpecs; + struct ProjectDataSpec + { + const DataSpecEntry& spec; + ProjectPath cookedPath; + bool active; + }; private: ProjectRootPath m_rootPath; - CompiledSpecs m_compiledSpecs; + ProjectPath m_dotPath; + ProjectPath m_cookedRoot; + std::vector m_compiledSpecs; public: Project(const HECL::ProjectRootPath& rootPath); @@ -440,13 +448,29 @@ public: /** * @brief Get the path of the project's root-directory - * @param absolute return as absolute-path * @return project root path * * Self explanatory */ inline const ProjectRootPath& getProjectRootPath() const {return m_rootPath;} + /** + * @brief Get the path of project's cooked directory for a specific DataSpec + * @param DataSpec to retrieve path for + * @return project cooked path + * + * The cooked path matches the directory layout of the working directory, + * except data is + */ + inline const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const + { + for (const ProjectDataSpec& sp : m_compiledSpecs) + if (&sp.spec == &spec) + return sp.cookedPath; + LogModule.report(LogVisor::FatalError, "Unable to find spec '%s'", spec.m_name.c_str()); + return m_cookedRoot; + } + /** * @brief Add given file(s) to the database * @param path file or pattern within project @@ -502,7 +526,7 @@ public: * @brief Return map populated with dataspecs targetable by this project interface * @return Platform map with name-string keys and enable-status values */ - inline const CompiledSpecs& getDataSpecs() {return m_compiledSpecs;} + inline const std::vector& getDataSpecs() {return m_compiledSpecs;} /** * @brief Enable persistent user preference for particular spec string(s) diff --git a/hecl/include/HECL/HECL.hpp b/hecl/include/HECL/HECL.hpp index 5a5d7c711..15f02cd19 100644 --- a/hecl/include/HECL/HECL.hpp +++ b/hecl/include/HECL/HECL.hpp @@ -1,9 +1,7 @@ #ifndef HECL_HPP #define HECL_HPP -#if _WIN32 -char* win_realpath(const char* name, char* restrict resolved); -#else +#ifndef _WIN32 #include #include #include @@ -50,16 +48,24 @@ class SystemUTF8View public: SystemUTF8View(const SystemString& str) : m_utf8(WideToUTF8(str)) {} - inline const std::string& utf8_str() {return m_utf8;} + inline operator const std::string&() const {return m_utf8;} + inline std::string operator+(const std::string& other) const {return m_utf8 + other;} + inline std::string operator+(const char* other) const {return m_utf8 + other;} }; +inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} class SystemStringView { std::wstring m_sys; public: SystemStringView(const std::string& str) : m_sys(UTF8ToWide(str)) {} - inline const std::wstring& sys_str() {return m_sys;} + inline operator const std::wstring&() const {return m_sys;} + inline std::wstring operator+(const std::wstring& other) const {return m_sys + other;} + inline std::wstring operator+(const wchar_t* other) const {return m_sys + other;} }; +inline std::wstring operator+(const std::wstring& lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} +inline std::wstring operator+(const wchar_t* lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} #ifndef _S #define _S(val) L ## val #endif @@ -76,16 +82,24 @@ class SystemUTF8View public: SystemUTF8View(const SystemString& str) : m_utf8(str) {} - inline const std::string& utf8_str() {return m_utf8;} + inline operator const std::string&() const {return m_utf8;} + inline std::string operator+(const std::string& other) const {return std::string(m_utf8) + other;} + inline std::string operator+(const char* other) const {return std::string(m_utf8) + other;} }; +inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} class SystemStringView { const std::string& m_sys; public: SystemStringView(const std::string& str) : m_sys(str) {} - inline const std::string& sys_str() {return m_sys;} + inline operator const std::string&() const {return m_sys;} + inline std::string operator+(const std::string& other) const {return m_sys + other;} + inline std::string operator+(const char* other) const {return m_sys + other;} }; +inline std::string operator+(const std::string& lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} #ifndef _S #define _S(val) val #endif @@ -318,6 +332,7 @@ public: class ProjectPath { protected: + SystemString m_projRoot; SystemString m_absPath; SystemString m_relPath; Hash m_hash = 0; @@ -325,9 +340,14 @@ protected: std::string m_utf8AbsPath; const char* m_utf8RelPath; #endif - ProjectPath() {} - bool _canonAbsPath(const SystemString& path, bool& needsMake); - inline void _makeDir() const {MakeDir(m_absPath);} + ProjectPath(const SystemString& projRoot) + : m_projRoot(projRoot), m_absPath(projRoot), m_relPath("."), m_hash(m_relPath) + { +#if HECL_UCS2 + m_utf8AbsPath = WideToUTF8(m_absPath); + m_utf8RelPath = m_utf8AbsPath.c_str() + ((ProjectPath&)rootPath).m_utf8AbsPath.size(); +#endif + } public: /** * @brief Construct a project subpath representation within another subpath @@ -415,6 +435,8 @@ public: */ void getGlobResults(std::vector& outPaths) const; + inline void makeDir() const {MakeDir(m_absPath);} + /** * @brief HECL-specific blowfish hash * @return unique hash value @@ -435,12 +457,7 @@ class ProjectRootPath : public ProjectPath { public: ProjectRootPath(const SystemString& path) - { - bool needsMake = false; - _canonAbsPath(path, needsMake); - if (needsMake) - _makeDir(); - } + : ProjectPath(path) {} }; /** diff --git a/hecl/lib/Database/ASEngine.cpp b/hecl/lib/Database/ASEngine.cpp index 4c1fdcf80..2f2739c3a 100644 --- a/hecl/lib/Database/ASEngine.cpp +++ b/hecl/lib/Database/ASEngine.cpp @@ -28,6 +28,7 @@ void InitASEngine() InitEntered = true; assert(asENGINE = AngelScript::asCreateScriptEngine(ANGELSCRIPT_VERSION)); assert(asENGINE->SetEngineProperty(AngelScript::asEP_COPY_SCRIPT_SECTIONS, false) >= 0); + assert(asENGINE->SetEngineProperty(AngelScript::asEP_ALLOW_MULTILINE_STRINGS, true) >= 0); assert(asENGINE->SetMessageCallback(AngelScript::asFUNCTION(MessageCallback), nullptr, AngelScript::asCALL_CDECL) >= 0); } diff --git a/hecl/lib/Database/Project.cpp b/hecl/lib/Database/Project.cpp index c4bfcd0a5..9b7a59a3a 100644 --- a/hecl/lib/Database/Project.cpp +++ b/hecl/lib/Database/Project.cpp @@ -95,7 +95,8 @@ void Project::ConfigFile::removeLine(const std::string& refLine) { if (!m_lockedFile) { - LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called"); + LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, + "Project::ConfigFile::lockAndRead not yet called"); return; } @@ -115,7 +116,8 @@ bool Project::ConfigFile::checkForLine(const std::string& refLine) { if (!m_lockedFile) { - LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called"); + LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, + "Project::ConfigFile::lockAndRead not yet called"); return false; } @@ -131,7 +133,8 @@ void Project::ConfigFile::unlockAndDiscard() { if (!m_lockedFile) { - LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called"); + LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, + "Project::ConfigFile::lockAndRead not yet called"); return; } @@ -144,7 +147,8 @@ bool Project::ConfigFile::unlockAndCommit() { if (!m_lockedFile) { - LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called"); + LogModule.reportSource(LogVisor::FatalError, __FILE__, __LINE__, + "Project::ConfigFile::lockAndRead not yet called"); return false; } @@ -186,6 +190,8 @@ bool Project::ConfigFile::unlockAndCommit() Project::Project(const ProjectRootPath& rootPath) : m_rootPath(rootPath), + m_dotPath(m_rootPath, _S(".hecl")), + m_cookedRoot(m_dotPath, _S("cooked")), m_specs(*this, _S("specs")), m_paths(*this, _S("paths")), m_groups(*this, _S("groups")) @@ -200,11 +206,12 @@ Project::Project(const ProjectRootPath& rootPath) m_rootPath.getAbsolutePathUTF8() + "' isn't"); /* Create project directory structure */ - HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl")); - HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/cooked")); + m_dotPath.makeDir(); + m_cookedRoot.makeDir(); /* Ensure beacon is valid or created */ - FILE* bf = HECL::Fopen((m_rootPath.getAbsolutePath() + _S("/.hecl/beacon")).c_str(), _S("a+b")); + ProjectPath beaconPath(m_dotPath, _S("beacon")); + FILE* bf = HECL::Fopen(beaconPath.getAbsolutePath().c_str(), _S("a+b")); struct BeaconStruct { HECL::FourCC magic; @@ -286,7 +293,8 @@ void Project::rescanDataSpecs() for (const DataSpecEntry* spec : DATA_SPEC_REGISTRY) { SystemUTF8View specUTF8(spec->m_name); - m_compiledSpecs.push_back({*spec, m_specs.checkForLine(specUTF8.utf8_str()) ? true : false}); + m_compiledSpecs.push_back({*spec, ProjectPath(m_cookedRoot, spec->m_name + ".spec"), + m_specs.checkForLine(specUTF8) ? true : false}); } m_specs.unlockAndDiscard(); } @@ -296,7 +304,9 @@ bool Project::enableDataSpecs(const std::vector& specs) m_specs.lockAndRead(); for (const SystemString& spec : specs) m_specs.addLine(spec); - return m_specs.unlockAndCommit(); + bool result = m_specs.unlockAndCommit(); + rescanDataSpecs(); + return result; } bool Project::disableDataSpecs(const std::vector& specs) @@ -304,7 +314,9 @@ bool Project::disableDataSpecs(const std::vector& specs) m_specs.lockAndRead(); for (const SystemString& spec : specs) m_specs.removeLine(spec); - return m_specs.unlockAndCommit(); + bool result = m_specs.unlockAndCommit(); + rescanDataSpecs(); + return result; } bool Project::cookPath(const ProjectPath& path, diff --git a/hecl/lib/ProjectPath.cpp b/hecl/lib/ProjectPath.cpp index 427519f8f..e3606a458 100644 --- a/hecl/lib/ProjectPath.cpp +++ b/hecl/lib/ProjectPath.cpp @@ -2,79 +2,72 @@ #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 regPATHCOMP(_S("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript|SystemRegex::optimize); static const SystemRegex regDRIVELETTER(_S("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize); -inline bool isAbsolute(const SystemString& path) +static SystemString canonRelPath(const SystemString& path) { - if (path.size() && path[0] == '/') - return true; - return false; -} - -bool ProjectPath::_canonAbsPath(const SystemString& path, bool& needsMake) -{ -#if _WIN32 -#else - SystemChar resolvedPath[PATH_MAX]; - if (!realpath(path.c_str(), resolvedPath)) + /* Absolute paths not allowed */ + if (path[0] == _S('/') || path[0] == _S('\\')) { - if (errno != ENOENT) - { - throw std::system_error(errno, std::system_category(), - "Unable to resolve '" + SystemUTF8View(path).utf8_str() + - "' as a canonicalized path"); - return false; - } - else - needsMake = true; + throw std::invalid_argument("Absolute path provided; expected relative: " + path); + return _S("."); } - m_absPath = resolvedPath; -#endif - return true; + + /* Tokenize Path */ + std::vector comps; + HECL::SystemRegexMatch matches; + SystemString in = path; + while (std::regex_search(in, matches, regPATHCOMP)) + { + in = matches.suffix(); + const SystemString& match = matches[1]; + if (!match.compare(_S("."))) + continue; + else if (!match.compare(_S(".."))) + { + if (comps.empty()) + { + /* Unable to resolve outside project */ + SystemUTF8View pathView(path); + throw std::invalid_argument("Unable to resolve outside project root in " + pathView); + return _S("."); + } + comps.pop_back(); + continue; + } + comps.push_back(match); + } + + /* Emit relative path */ + if (comps.size()) + { + auto it = comps.begin(); + SystemString retval = *it; + for (++it ; it != comps.end() ; ++it) + { + retval += _S('/'); + retval += *it; + } + return retval; + } + return "."; } ProjectPath::ProjectPath(const ProjectPath& parentPath, const SystemString& path) +: m_projRoot(parentPath.m_projRoot) { - bool needsMake = false; - if (!_canonAbsPath(parentPath.getRelativePath() + '/' + path, needsMake)) - return; - if (m_absPath.size() < parentPath.m_absPath.size() || - m_absPath.compare(0, parentPath.m_absPath.size(), - parentPath.m_absPath)) - { - throw std::invalid_argument("'" + SystemUTF8View(m_absPath).utf8_str() + "' is not a subpath of '" + - SystemUTF8View(parentPath.m_absPath).utf8_str() + "'"); - return; - } - if (m_absPath.size() == parentPath.m_absPath.size()) - { - /* Copies of the project root are permitted */ - return; - } - SystemString::iterator beginit = m_absPath.begin() + parentPath.m_absPath.size(); - if (*beginit == _S('/')) - ++beginit; - m_relPath = SystemString(beginit, m_absPath.end()); + m_relPath = canonRelPath(parentPath.m_relPath + '/' + path); + m_absPath = parentPath.m_projRoot + '/' + m_relPath; m_hash = Hash(m_relPath); - #if HECL_UCS2 m_utf8AbsPath = WideToUTF8(m_absPath); m_utf8RelPath = m_utf8AbsPath.c_str() + ((ProjectPath&)rootPath).m_utf8AbsPath.size(); #endif - - if (needsMake) - _makeDir(); } ProjectPath::PathType ProjectPath::getPathType() const