diff --git a/hecl/blender/BlenderConnection.cpp b/hecl/blender/BlenderConnection.cpp index 77d7def2f..1a9fa8384 100644 --- a/hecl/blender/BlenderConnection.cpp +++ b/hecl/blender/BlenderConnection.cpp @@ -313,6 +313,13 @@ BlenderConnection::~BlenderConnection() bool BlenderConnection::createBlend(const SystemString& path) { + std::unique_lock lk(m_lock, std::try_to_lock); + if (!lk) + { + BlenderLog.report(LogVisor::FatalError, + "BlenderConnection::createBlend() musn't be called with stream active"); + return false; + } HECL::SystemUTF8View pathView(path); _writeLine(("CREATE \"" + pathView.str() + "\"").c_str()); char lineBuf[256]; @@ -327,6 +334,13 @@ bool BlenderConnection::createBlend(const SystemString& path) bool BlenderConnection::openBlend(const SystemString& path) { + std::unique_lock lk(m_lock, std::try_to_lock); + if (!lk) + { + BlenderLog.report(LogVisor::FatalError, + "BlenderConnection::openBlend() musn't be called with stream active"); + return false; + } HECL::SystemUTF8View pathView(path); _writeLine(("OPEN \"" + pathView.str() + "\"").c_str()); char lineBuf[256]; @@ -341,6 +355,13 @@ bool BlenderConnection::openBlend(const SystemString& path) bool BlenderConnection::saveBlend() { + std::unique_lock lk(m_lock, std::try_to_lock); + if (!lk) + { + BlenderLog.report(LogVisor::FatalError, + "BlenderConnection::saveBlend() musn't be called with stream active"); + return false; + } _writeLine("SAVE"); char lineBuf[256]; _readLine(lineBuf, sizeof(lineBuf)); @@ -349,12 +370,20 @@ bool BlenderConnection::saveBlend() return false; } -void BlenderConnection::PyOutStream::linkBlend(const SystemString& relPath, const std::string& objName, +void BlenderConnection::deleteBlend() +{ + if (m_loadedBlend.size()) + { + HECL::Unlink(m_loadedBlend.c_str()); + m_loadedBlend.clear(); + } +} + +void BlenderConnection::PyOutStream::linkBlend(const SystemString& target, const std::string& objName, bool link) { - HECL::SystemUTF8View relView(relPath); format("if '%s' not in bpy.data.scenes:\n" - " with bpy.data.libraries.load('//%s', link=%s, relative=True) as (data_from, data_to):\n" + " with bpy.data.libraries.load('%s', link=%s, relative=True) as (data_from, data_to):\n" " data_to.scenes = data_from.scenes\n" " obj_scene = None\n" " for scene in data_to.scenes:\n" @@ -368,7 +397,7 @@ void BlenderConnection::PyOutStream::linkBlend(const SystemString& relPath, cons "else:\n" " obj = bpy.data.objects['%s']\n" "\n", - objName.c_str(), relView.str().c_str(), link?"True":"False", + objName.c_str(), target.c_str(), link?"True":"False", objName.c_str(), objName.c_str()); } diff --git a/hecl/blender/BlenderConnection.hpp b/hecl/blender/BlenderConnection.hpp index c669ba466..beee77ae1 100644 --- a/hecl/blender/BlenderConnection.hpp +++ b/hecl/blender/BlenderConnection.hpp @@ -47,6 +47,7 @@ public: bool createBlend(const SystemString& path); bool openBlend(const SystemString& path); bool saveBlend(); + void deleteBlend(); enum CookPlatform { CP_MODERN = 0, @@ -62,31 +63,43 @@ public: friend class BlenderConnection; std::unique_lock m_lk; BlenderConnection* m_parent; + bool m_deleteOnError; struct StreamBuf : std::streambuf { - BlenderConnection* m_parent; + PyOutStream& m_parent; std::string m_lineBuf; - StreamBuf(BlenderConnection* parent) : m_parent(parent) {} + bool m_deleteOnError; + StreamBuf(PyOutStream& parent, bool deleteOnError) + : m_parent(parent), m_deleteOnError(deleteOnError) {} StreamBuf(const StreamBuf& other) = delete; StreamBuf(StreamBuf&& other) = default; int_type overflow(int_type ch) { + if (!m_parent.m_lk) + BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream writing"); if (ch != traits_type::eof() && ch != '\n') { m_lineBuf += char_type(ch); return ch; } - m_parent->_writeLine(m_lineBuf.c_str()); + m_parent.m_parent->_writeLine(m_lineBuf.c_str()); char readBuf[16]; - m_parent->_readLine(readBuf, 16); + m_parent.m_parent->_readLine(readBuf, 16); if (strcmp(readBuf, "OK")) + { + if (m_deleteOnError) + m_parent.m_parent->deleteBlend(); BlenderLog.report(LogVisor::FatalError, "error sending '%s' to blender", m_lineBuf.c_str()); + } m_lineBuf.clear(); return ch; } } m_sbuf; - PyOutStream(BlenderConnection* parent) - : m_lk(parent->m_lock), m_parent(parent), m_sbuf(parent), std::ostream(&m_sbuf) + PyOutStream(BlenderConnection* parent, bool deleteOnError) + : m_lk(parent->m_lock), m_parent(parent), + m_sbuf(*this, deleteOnError), + m_deleteOnError(deleteOnError), + std::ostream(&m_sbuf) { m_parent->_writeLine("PYBEGIN"); char readBuf[16]; @@ -99,9 +112,10 @@ public: PyOutStream(PyOutStream&& other) : m_lk(std::move(other.m_lk)), m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)) {other.m_parent = nullptr;} - ~PyOutStream() + ~PyOutStream() {close();} + void close() { - if (m_parent) + if (m_parent && m_lk) { m_parent->_writeLine("PYEND"); char readBuf[16]; @@ -109,9 +123,12 @@ public: if (strcmp(readBuf, "DONE")) BlenderLog.report(LogVisor::FatalError, "unable to close PyOutStream with blender"); } + m_lk.unlock(); } void format(const char* fmt, ...) { + if (!m_lk) + BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream::format()"); va_list ap; va_start(ap, fmt); char* result = nullptr; @@ -123,9 +140,9 @@ public: } void linkBlend(const SystemString& target, const std::string& objName, bool link=true); }; - inline PyOutStream beginPythonOut() + inline PyOutStream beginPythonOut(bool deleteOnError=false) { - return PyOutStream(this); + return PyOutStream(this, deleteOnError); } void quitBlender(); diff --git a/hecl/blender/addon/Nodegrid.py b/hecl/blender/addon/Nodegrid.py index 78f530bab..badfc988d 100644 --- a/hecl/blender/addon/Nodegrid.py +++ b/hecl/blender/addon/Nodegrid.py @@ -15,7 +15,7 @@ class Nodegrid: self.col_roffs = [[0.0,0.0]] * self.ncol for i in range(self.ncol): self.heights.append(0.0) - frame_node = new_nodetree.nodes.new('NodeFrame') + frame_node = nodetree.nodes.new('NodeFrame') frame_node.label = FRAME_NAMES[i] frame_node.use_custom_color = True frame_node.color = FRAME_COLORS[i] diff --git a/hecl/blender/addon/__init__.py b/hecl/blender/addon/__init__.py index 96c47d41b..bd388ff23 100644 --- a/hecl/blender/addon/__init__.py +++ b/hecl/blender/addon/__init__.py @@ -6,14 +6,15 @@ bl_info = { "name": "HECL", "author": "Jack Andersen ", "version": (1, 0), - "blender": (2, 69), + "blender": (2, 74), "tracker_url": "https://github.com/RetroView/hecl/issues/new", "location": "Properties > Scene > HECL", "description": "Enables blender to gather meshes, materials, and textures for hecl", "category": "System"} # Package import -from . import hmdl, sact +from . import hmdl, sact, Nodegrid +Nodegrid = Nodegrid.Nodegrid import bpy, os, sys from bpy.app.handlers import persistent diff --git a/hecl/blender/blendershell.py b/hecl/blender/blendershell.py index 798c7f2ab..8f9f02702 100644 --- a/hecl/blender/blendershell.py +++ b/hecl/blender/blendershell.py @@ -1,4 +1,4 @@ -import bpy, sys, os, re, code +import bpy, sys, os, re ARGS_PATTERN = re.compile(r'''(?:"([^"]+)"|'([^']+)'|(\S+))''') @@ -44,6 +44,22 @@ ackbytes = readpipeline() if ackbytes != b'ACK': quitblender() +# Count brackets +def count_brackets(linestr): + bracket_count = 0 + for ch in linestr: + if ch in {'[','{','('}: + bracket_count += 1 + elif ch in {']','}',')'}: + bracket_count -= 1 + return bracket_count + +# Complete sequences of statements compiled/executed here +def exec_compbuf(compbuf, globals): + #print('EXEC', compbuf) + co = compile(compbuf, '', 'exec') + exec(co, globals) + # Command loop while True: cmdline = readpipeline() @@ -80,34 +96,46 @@ while True: elif cmdargs[0] == 'PYBEGIN': writepipeline(b'READY') - globals = dict() + globals = {'hecl':hecl} compbuf = str() - prev_leading_spaces = 0 + bracket_count = 0 while True: try: line = readpipeline() + + # End check if line == b'PYEND': + # Ensure remaining block gets executed + if len(compbuf): + exec_compbuf(compbuf, globals) + compbuf = str() writepipeline(b'DONE') break - linestr = line.decode() - if linestr.isspace() or not len(linestr): + + # Syntax filter + linestr = line.decode().rstrip() + if not len(linestr) or linestr.lstrip()[0] == '#': writepipeline(b'OK') continue leading_spaces = len(linestr) - len(linestr.lstrip()) - if prev_leading_spaces and not leading_spaces: - compbuf += '\n' - co = code.compile_command(compbuf, filename='') - if co is not None: - exec(co, globals) - compbuf = str() - prev_leading_spaces = leading_spaces + + # Block lines always get appended right away + if linestr.endswith(':') or leading_spaces or bracket_count: + if len(compbuf): + compbuf += '\n' + compbuf += linestr + bracket_count += count_brackets(linestr) + writepipeline(b'OK') + continue + + # Complete non-block statement in compbuf if len(compbuf): - compbuf += '\n' - compbuf += linestr - co = code.compile_command(compbuf, filename='') - if co is not None: - exec(co, globals) - compbuf = str() + exec_compbuf(compbuf, globals) + + # Establish new compbuf + compbuf = linestr + bracket_count += count_brackets(linestr) + except Exception as e: writepipeline(b'EXCEPTION') raise diff --git a/hecl/driver/ToolExtract.hpp b/hecl/driver/ToolExtract.hpp index ba40b8485..7a8255d65 100644 --- a/hecl/driver/ToolExtract.hpp +++ b/hecl/driver/ToolExtract.hpp @@ -44,12 +44,13 @@ public: LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory"); size_t ErrorRef = LogVisor::ErrorCount; - HECL::ProjectRootPath newProjRoot(baseFile); + HECL::SystemString rootDir = info.cwd + '/' + baseFile; + HECL::ProjectRootPath newProjRoot(rootDir); newProjRoot.makeDir(); m_fallbackProj.reset(new HECL::Database::Project(newProjRoot)); if (LogVisor::ErrorCount > ErrorRef) - LogModule.report(LogVisor::FatalError, "unable to init project at '%s'", baseFile.c_str()); - LogModule.report(LogVisor::Info, _S("initialized project at '%s/.hecl'"), baseFile.c_str()); + LogModule.report(LogVisor::FatalError, "unable to init project at '%s'", rootDir.c_str()); + LogModule.report(LogVisor::Info, _S("initialized project at '%s/.hecl'"), rootDir.c_str()); m_useProj = m_fallbackProj.get(); } else @@ -68,7 +69,7 @@ public: HECL::Database::IDataSpec* ds = entry->m_factory(*m_useProj, HECL::Database::TOOL_EXTRACT); if (ds) { - if (ds->canExtract(*m_useProj, m_einfo, m_reps)) + if (ds->canExtract(m_einfo, m_reps)) m_specPasses.emplace_back(entry, ds); else delete ds; @@ -184,7 +185,7 @@ public: #endif int lineIdx = 0; - ds.m_instance->doExtract(*m_useProj, m_einfo, + ds.m_instance->doExtract(m_einfo, [&lineIdx](const HECL::SystemChar* message, int lidx, float factor) { #ifndef _WIN32 diff --git a/hecl/extern/Athena b/hecl/extern/Athena index f06afb429..9ed090b12 160000 --- a/hecl/extern/Athena +++ b/hecl/extern/Athena @@ -1 +1 @@ -Subproject commit f06afb429ccddad1be6878d1b9d6ffebb245909b +Subproject commit 9ed090b12629ddb8a431b871340276da61e88442 diff --git a/hecl/extern/RetroCommon b/hecl/extern/RetroCommon index 8e89d7efd..aeb608905 160000 --- a/hecl/extern/RetroCommon +++ b/hecl/extern/RetroCommon @@ -1 +1 @@ -Subproject commit 8e89d7efd060d756bf756c09c0d285eb944f6610 +Subproject commit aeb6089053a46c51b55727e68406bb7577c2e60e diff --git a/hecl/include/HECL/Database.hpp b/hecl/include/HECL/Database.hpp index 0e584986c..74f2d91d7 100644 --- a/hecl/include/HECL/Database.hpp +++ b/hecl/include/HECL/Database.hpp @@ -217,9 +217,9 @@ public: typedef std::function FExtractProgress; - virtual bool canExtract(Project&, const ExtractPassInfo& info, std::vector& reps) + virtual bool canExtract(const ExtractPassInfo& info, std::vector& reps) {(void)info;LogModule.report(LogVisor::Error, "not implemented");return false;} - virtual void doExtract(Project&, const ExtractPassInfo& info, FExtractProgress progress) + virtual void doExtract(const ExtractPassInfo& info, FExtractProgress progress) {(void)info;(void)progress;} /** @@ -233,10 +233,10 @@ public: ProjectPath path; ProjectPath cookedPath; }; - virtual bool canCook(const Project&, const CookTaskInfo& info, + virtual bool canCook(const CookTaskInfo& info, SystemString& reasonNo) {(void)info;reasonNo=_S("not implemented");return false;} - virtual void doCook(const Project&, const CookTaskInfo& info) + virtual void doCook(const CookTaskInfo& info) {(void)info;} /** @@ -252,13 +252,13 @@ public: ProjectPath subpath; ProjectPath outpath; }; - virtual bool canPackage(const Project&, const PackagePassInfo& info, + virtual bool canPackage(const PackagePassInfo& info, SystemString& reasonNo) {(void)info;reasonNo=_S("not implemented");return false;} - virtual void gatherDependencies(const Project&, const PackagePassInfo& info, + virtual void gatherDependencies(const PackagePassInfo& info, std::unordered_set& implicitsOut) {(void)info;(void)implicitsOut;} - virtual void doPackage(const Project&, const PackagePassInfo& info) + virtual void doPackage(const PackagePassInfo& info) {(void)info;} }; diff --git a/hecl/include/HECL/HECL.hpp b/hecl/include/HECL/HECL.hpp index 1906f9d5e..6d51afc67 100644 --- a/hecl/include/HECL/HECL.hpp +++ b/hecl/include/HECL/HECL.hpp @@ -122,6 +122,15 @@ inline std::string operator+(const char* lhs, const SystemStringView& rhs) {retu typedef struct stat Sstat; #endif +static inline void Unlink(const SystemChar* file) +{ +#if _WIN32 + _wunlink(file); +#else + unlink(file); +#endif +} + static inline void MakeDir(const SystemChar* dir) { #if _WIN32