diff --git a/hecl/CMakeLists.txt b/hecl/CMakeLists.txt index 790ad9356..07d5c9360 100644 --- a/hecl/CMakeLists.txt +++ b/hecl/CMakeLists.txt @@ -5,11 +5,10 @@ add_definitions(-DUNICODE=1 -D_UNICODE=1 -D_CRT_SECURE_NO_WARNINGS=1 /wd4267 /wd else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-multichar -fno-exceptions") endif() -set(HECL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +list(APPEND HECL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/blender) set(ATHENA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/Athena/include) set(LOG_VISOR_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/LogVisor/include) set(ANGELSCRIPT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/AngelScript/angelscript/include) -set(LIBPNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/libpng) set(SQUISH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/libSquish) add_definitions(-DAS_USE_NAMESPACE=1 -DAS_NO_EXCEPTIONS=1) add_subdirectory(extern) diff --git a/hecl/blender/BlenderConnection.cpp b/hecl/blender/BlenderConnection.cpp index 1aa45bb5f..86b7fb46e 100644 --- a/hecl/blender/BlenderConnection.cpp +++ b/hecl/blender/BlenderConnection.cpp @@ -9,7 +9,11 @@ #include #include "BlenderConnection.hpp" -static LogVisor::LogModule Log("BlenderConnection"); +namespace HECL +{ + +LogVisor::LogModule BlenderLog("BlenderConnection"); +BlenderConnection* SharedBlenderConnection = nullptr; #ifdef __APPLE__ #define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender" @@ -28,7 +32,7 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz) { if (readBytes >= bufSz) { - Log.report(LogVisor::FatalError, "Pipe buffer overrun\n"); + BlenderLog.report(LogVisor::FatalError, "Pipe buffer overrun\n"); *(buf-1) = '\0'; return bufSz - 1; } @@ -58,7 +62,7 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz) } } err: - Log.report(LogVisor::FatalError, strerror(errno)); + BlenderLog.report(LogVisor::FatalError, strerror(errno)); return 0; } @@ -81,7 +85,7 @@ size_t BlenderConnection::_writeLine(const char* buf) #endif return (size_t)ret; err: - Log.report(LogVisor::FatalError, strerror(errno)); + BlenderLog.report(LogVisor::FatalError, strerror(errno)); return 0; } @@ -98,7 +102,7 @@ size_t BlenderConnection::_readBuf(char* buf, size_t len) #endif return ret; err: - Log.report(LogVisor::FatalError, strerror(errno)); + BlenderLog.report(LogVisor::FatalError, strerror(errno)); return 0; } @@ -115,7 +119,7 @@ size_t BlenderConnection::_writeBuf(const char* buf, size_t len) #endif return ret; err: - Log.report(LogVisor::FatalError, strerror(errno)); + BlenderLog.report(LogVisor::FatalError, strerror(errno)); return 0; } @@ -257,27 +261,27 @@ BlenderConnection::BlenderConnection(bool silenceBlender) if (!strcmp(lineBuf, "NOLAUNCH")) { _closePipe(); - Log.report(LogVisor::FatalError, "Unable to launch blender"); + BlenderLog.report(LogVisor::FatalError, "Unable to launch blender"); } else if (!strcmp(lineBuf, "NOBLENDER")) { _closePipe(); if (blenderBin) - Log.report(LogVisor::FatalError, _S("Unable to find blender at '%s' or '%s'"), + BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s' or '%s'"), blenderBin, DEFAULT_BLENDER_BIN); else - Log.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"), + BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"), DEFAULT_BLENDER_BIN); } else if (!strcmp(lineBuf, "NOADDON")) { _closePipe(); - Log.report(LogVisor::FatalError, "HECL addon not installed within blender"); + BlenderLog.report(LogVisor::FatalError, "HECL addon not installed within blender"); } else if (strcmp(lineBuf, "READY")) { _closePipe(); - Log.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf); + BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf); } _writeLine("ACK"); @@ -288,7 +292,20 @@ BlenderConnection::~BlenderConnection() _closePipe(); } -bool BlenderConnection::openBlend(const std::string& path) +bool BlenderConnection::createBlend(const SystemString& path) +{ + _writeLine(("CREATE" + path).c_str()); + char lineBuf[256]; + _readLine(lineBuf, sizeof(lineBuf)); + if (!strcmp(lineBuf, "FINISHED")) + { + m_loadedBlend = path; + return true; + } + return false; +} + +bool BlenderConnection::openBlend(const SystemString& path) { _writeLine(("OPEN" + path).c_str()); char lineBuf[256]; @@ -313,7 +330,7 @@ bool BlenderConnection::cookBlend(std::function bufGetter, _readLine(lineBuf, sizeof(lineBuf)); if (strcmp(expectedType.c_str(), lineBuf)) { - Log.report(LogVisor::Error, "expected '%s' to contain '%s' not '%s'", + BlenderLog.report(LogVisor::Error, "expected '%s' to contain '%s' not '%s'", m_loadedBlend.c_str(), expectedType.c_str(), lineBuf); return false; } @@ -331,7 +348,7 @@ bool BlenderConnection::cookBlend(std::function bufGetter, if (!strcmp("SUCCESS", lineBuf)) return true; else if (!strcmp("EXCEPTION", lineBuf)) - Log.report(LogVisor::FatalError, "blender script exception"); + BlenderLog.report(LogVisor::FatalError, "blender script exception"); return false; } @@ -342,3 +359,5 @@ void BlenderConnection::quitBlender() char lineBuf[256]; _readLine(lineBuf, sizeof(lineBuf)); } + +} diff --git a/hecl/blender/BlenderConnection.hpp b/hecl/blender/BlenderConnection.hpp index 134361314..8d1253ee9 100644 --- a/hecl/blender/BlenderConnection.hpp +++ b/hecl/blender/BlenderConnection.hpp @@ -13,6 +13,14 @@ #include #include +#include "HECL/HECL.hpp" + +namespace HECL +{ + +extern LogVisor::LogModule BlenderLog; +extern class BlenderConnection* SharedBlenderConnection; + class BlenderConnection { std::mutex m_lock; @@ -35,7 +43,8 @@ public: BlenderConnection(bool silenceBlender=false); ~BlenderConnection(); - bool openBlend(const std::string& path); + bool createBlend(const SystemString& path); + bool openBlend(const SystemString& path); enum CookPlatform { CP_MODERN = 0, @@ -54,23 +63,34 @@ public: struct StreamBuf : std::streambuf { BlenderConnection* m_parent; + std::string m_lineBuf; StreamBuf(BlenderConnection* parent) : m_parent(parent) {} StreamBuf(const StreamBuf& other) = delete; StreamBuf(StreamBuf&& other) = default; int_type overflow(int_type ch) { - if (ch != traits_type::eof()) + if (ch != traits_type::eof() && ch != '\n') { - char_type chi = ch; - m_parent->_writeBuf(&chi, 1); + m_lineBuf += char_type(ch); + return ch; } + m_parent->_writeLine(m_lineBuf.c_str()); + char readBuf[16]; + m_parent->_readLine(readBuf, 16); + if (strcmp(readBuf, "OK")) + 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) { - m_parent->_writeBuf("\033PYBEGIN\n", 9); + m_parent->_writeLine("PYBEGIN"); + char readBuf[16]; + m_parent->_readLine(readBuf, 16); + if (strcmp(readBuf, "READY")) + BlenderLog.report(LogVisor::FatalError, "unable to open PyOutStream with blender"); } public: PyOutStream(const PyOutStream& other) = delete; @@ -80,7 +100,13 @@ public: ~PyOutStream() { if (m_parent) - m_parent->_writeBuf("\033PYEND\n", 7); + { + m_parent->_writeLine("PYEND"); + char readBuf[16]; + m_parent->_readLine(readBuf, 16); + if (strcmp(readBuf, "DONE")) + BlenderLog.report(LogVisor::FatalError, "unable to close PyOutStream with blender"); + } } }; inline PyOutStream beginPythonOut() @@ -89,6 +115,20 @@ public: } void quitBlender(); + + static inline BlenderConnection& SharedConnection() + { + if (!SharedBlenderConnection) + SharedBlenderConnection = new BlenderConnection(); + return *SharedBlenderConnection; + } + + static inline void Shutdown() + { + delete SharedBlenderConnection; + } }; +} + #endif // BLENDERCONNECTION_HPP diff --git a/hecl/blender/CMakeLists.txt b/hecl/blender/CMakeLists.txt index 07fad3d35..4d5b8055e 100644 --- a/hecl/blender/CMakeLists.txt +++ b/hecl/blender/CMakeLists.txt @@ -1,3 +1,17 @@ +list(APPEND PY_SOURCES + addon/__init__.py + addon/hmdl/__init__.py + addon/hmdl/HMDLMesh.py + addon/hmdl/HMDLShader.py + addon/hmdl/HMDLSkin.py + addon/hmdl/HMDLTxtr.py + addon/sact/__init__.py + addon/sact/SACTAction.py + addon/sact/SACTEvent.py + addon/sact/SACTSubtype.py) + add_library(HECLBlender BlenderConnection.cpp - BlenderConnection.hpp) + BlenderConnection.hpp + blendershell.py + ${PY_SOURCES}) diff --git a/hecl/blender/blendershell.py b/hecl/blender/blendershell.py index 909d7f080..1754160e8 100644 --- a/hecl/blender/blendershell.py +++ b/hecl/blender/blendershell.py @@ -55,6 +55,33 @@ while True: else: writepipeline(b'CANCELLED') + elif cmdline[0] == b'CREATE': + bpy.context.user_preferences.filepaths.save_version = 0 + if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdline[1].decode()): + writepipeline(b'FINISHED') + else: + writepipeline(b'CANCELLED') + + elif cmdline[0] == b'PYBEGIN': + writepipeline(b'READY') + globals = dict() + locals = dict() + while True: + try: + line = readpipeline() + if line == b'PYEND': + writepipeline(b'DONE') + break + co = compile(line+'\n', '', 'single') + exec(co, globals, locals) + except Exception as e: + writepipeline(b'EXCEPTION') + break + writepipeline(b'OK') + + elif cmdline[0] == b'PYEND': + writepipeline(b'ERROR') + else: hecl.command(cmdline, writepipeline, writepipebuf) diff --git a/hecl/driver/CMakeLists.txt b/hecl/driver/CMakeLists.txt index 98d8cd61f..2a71927e8 100644 --- a/hecl/driver/CMakeLists.txt +++ b/hecl/driver/CMakeLists.txt @@ -31,5 +31,6 @@ endif() target_link_libraries(hecl ${WHOLE_START} HECLDatabaseInit ${DATA_SPEC_LIBS} ${WHOLE_END} - HECLDatabase HECLCommon AthenaCore AngelScript NOD LogVisor png squish blowfish z lzo2 + HECLDatabase HECLBlender HECLCommon AthenaCore AngelScript NOD + LogVisor png squish blowfish z lzo2 ) diff --git a/hecl/driver/ToolExtract.hpp b/hecl/driver/ToolExtract.hpp index 9e237f77a..60a0a0644 100644 --- a/hecl/driver/ToolExtract.hpp +++ b/hecl/driver/ToolExtract.hpp @@ -22,11 +22,21 @@ public: ToolExtract(const ToolPassInfo& info) : ToolBase(info) { - if (!info.project) - LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory"); if (!m_info.args.size()) LogModule.report(LogVisor::FatalError, "hecl extract needs a source path as its first argument"); + if (!info.project) + { + /* Get name from input file and init project there */ + std::string baseFile = info.args[0]; + size_t slashPos = baseFile.rfind(_S('/')); + if (slashPos == HECL::SystemString::npos) + slashPos = baseFile.rfind(_S('\\')); + if (slashPos != HECL::SystemString::npos) + baseFile.assign(baseFile.g + LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory"); + } + m_einfo.srcpath = m_info.args[0]; m_einfo.extractArgs.reserve(info.args.size() - 1); m_einfo.force = info.force; diff --git a/hecl/driver/main.cpp b/hecl/driver/main.cpp index 8924cc065..190d2aa63 100644 --- a/hecl/driver/main.cpp +++ b/hecl/driver/main.cpp @@ -9,6 +9,7 @@ #include #include #include "HECL/Database.hpp" +#include "../blender/BlenderConnection.hpp" #include "LogVisor/LogVisor.hpp" LogVisor::LogModule LogModule("HECLDriver"); @@ -64,30 +65,13 @@ static const HECL::SystemRegex regOPEN(_S("-o([^\"]*|\\S*)"), std::regex::ECMASc static const HECL::SystemRegex regVERBOSE(_S("-v(v*)"), std::regex::ECMAScript|std::regex::optimize); static const HECL::SystemRegex regFORCE(_S("-f"), std::regex::ECMAScript|std::regex::optimize); -#include "../blender/BlenderConnection.hpp" - static LogVisor::LogModule AthenaLog("Athena"); static void AthenaExc(const Athena::error::Level& level, const char* file, const char*, int line, const char* fmt, ...) { - LogVisor::Level vLevel = LogVisor::Info; - switch (level) - { - case Athena::error::MESSAGE: - vLevel = LogVisor::Info; - break; - case Athena::error::WARNING: - vLevel = LogVisor::Warning; - break; - case Athena::error::ERROR: - vLevel = LogVisor::Error; - break; - case Athena::error::FATAL: - vLevel = LogVisor::FatalError; - } va_list ap; va_start(ap, fmt); - AthenaLog.reportSource(vLevel, file, line, fmt, ap); + AthenaLog.reportSource(LogVisor::Level(level), file, line, fmt, ap); va_end(ap); } @@ -105,9 +89,6 @@ int main(int argc, const char** argv) LogVisor::RegisterConsoleLogger(); atSetExceptionHandler(AthenaExc); - //CBlenderConnection bconn(false); - //return 0; - /* Basic usage check */ if (argc == 1) { @@ -265,12 +246,14 @@ int main(int argc, const char** argv) int retval = tool->run(); if (LogVisor::ErrorCount > ErrorRef) { + HECL::BlenderConnection::Shutdown(); #if WIN_PAUSE system("PAUSE"); #endif return -1; } + HECL::BlenderConnection::Shutdown(); #if WIN_PAUSE system("PAUSE"); #endif diff --git a/hecl/extern/Athena b/hecl/extern/Athena index 99b6df7cc..83cc8fe3c 160000 --- a/hecl/extern/Athena +++ b/hecl/extern/Athena @@ -1 +1 @@ -Subproject commit 99b6df7cc1192f95f64acb531bf6ab7ea2cdabeb +Subproject commit 83cc8fe3c35c23cfdb207a2267cf73a0e7d014da diff --git a/hecl/extern/RetroCommon b/hecl/extern/RetroCommon index 450a53fd2..80cc3cc27 160000 --- a/hecl/extern/RetroCommon +++ b/hecl/extern/RetroCommon @@ -1 +1 @@ -Subproject commit 450a53fd246cb1dd815acf272604137d9a00ac2a +Subproject commit 80cc3cc277c57d86d9bc8ef2784f68ad95028e9c diff --git a/hecl/extern/libpng/CMakeLists.txt b/hecl/extern/libpng/CMakeLists.txt index c651c1e97..61b42c631 100644 --- a/hecl/extern/libpng/CMakeLists.txt +++ b/hecl/extern/libpng/CMakeLists.txt @@ -1,3 +1,9 @@ +if(NOT WIN32) +find_library(PNG_LIB png) +endif() + +if(WIN32 OR PNG_LIB STREQUAL PNG_LIB-NOTFOUND) +message("-- Using HECL's built-in libpng") include_directories(${ZLIB_INCLUDE_DIR}) add_library(png png.h @@ -22,4 +28,7 @@ add_library(png pngwio.c pngwrite.c pngwtran.c - pngwutil.c) \ No newline at end of file + pngwutil.c) + +set(LIBPNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "PNG include path" FORCE) +endif()