Merge pull request #1 from RetroView/windows

Windows refactors
This commit is contained in:
Jack Andersen 2015-08-30 20:33:15 -10:00
commit 284455df8b
30 changed files with 490 additions and 262 deletions

View File

@ -9,6 +9,11 @@
#include <LogVisor/LogVisor.hpp> #include <LogVisor/LogVisor.hpp>
#include "BlenderConnection.hpp" #include "BlenderConnection.hpp"
#if _WIN32
#include <io.h>
#include <fcntl.h>
#endif
namespace HECL namespace HECL
{ {
@ -17,8 +22,6 @@ BlenderConnection* SharedBlenderConnection = nullptr;
#ifdef __APPLE__ #ifdef __APPLE__
#define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender" #define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender"
#elif _WIN32
#define DEFAULT_BLENDER_BIN _S("%ProgramFiles%\\Blender Foundation\\Blender\\blender.exe")
#else #else
#define DEFAULT_BLENDER_BIN "blender" #define DEFAULT_BLENDER_BIN "blender"
#endif #endif
@ -26,6 +29,19 @@ BlenderConnection* SharedBlenderConnection = nullptr;
extern "C" uint8_t HECL_BLENDERSHELL[]; extern "C" uint8_t HECL_BLENDERSHELL[];
extern "C" size_t HECL_BLENDERSHELL_SZ; extern "C" size_t HECL_BLENDERSHELL_SZ;
extern "C" uint8_t HECL_ADDON[];
extern "C" size_t HECL_ADDON_SZ;
static bool InstallAddon(const SystemChar* path)
{
FILE* fp = HECL::Fopen(path, _S("wb"));
if (!fp)
BlenderLog.report(LogVisor::FatalError, _S("Unable to install blender addon at '%s'"), path);
fwrite(HECL_ADDON, 1, HECL_ADDON_SZ, fp);
fclose(fp);
return true;
}
size_t BlenderConnection::_readLine(char* buf, size_t bufSz) size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
{ {
size_t readBytes = 0; size_t readBytes = 0;
@ -37,15 +53,9 @@ size_t BlenderConnection::_readLine(char* buf, size_t bufSz)
*(buf-1) = '\0'; *(buf-1) = '\0';
return bufSz - 1; return bufSz - 1;
} }
#if _WIN32 int ret = read(m_readpipe[0], buf, 1);
DWORD ret = 0;
if (!ReadFile(m_readpipe[0], buf, 1, &ret, NULL))
goto err;
#else
ssize_t ret = read(m_readpipe[0], buf, 1);
if (ret < 0) if (ret < 0)
goto err; goto err;
#endif
else if (ret == 1) else if (ret == 1)
{ {
if (*buf == '\n') if (*buf == '\n')
@ -69,21 +79,13 @@ err:
size_t BlenderConnection::_writeLine(const char* buf) size_t BlenderConnection::_writeLine(const char* buf)
{ {
#if _WIN32 int ret, nlerr;
DWORD ret = 0;
if (!WriteFile(m_writepipe[1], buf, strlen(buf), &ret, NULL))
goto err;
if (!WriteFile(m_writepipe[1], "\n", 1, NULL, NULL))
goto err;
#else
ssize_t ret, nlerr;
ret = write(m_writepipe[1], buf, strlen(buf)); ret = write(m_writepipe[1], buf, strlen(buf));
if (ret < 0) if (ret < 0)
goto err; goto err;
nlerr = write(m_writepipe[1], "\n", 1); nlerr = write(m_writepipe[1], "\n", 1);
if (nlerr < 0) if (nlerr < 0)
goto err; goto err;
#endif
return (size_t)ret; return (size_t)ret;
err: err:
BlenderLog.report(LogVisor::FatalError, strerror(errno)); BlenderLog.report(LogVisor::FatalError, strerror(errno));
@ -92,15 +94,9 @@ err:
size_t BlenderConnection::_readBuf(char* buf, size_t len) size_t BlenderConnection::_readBuf(char* buf, size_t len)
{ {
#if _WIN32 int ret = read(m_readpipe[0], buf, len);
DWORD ret = 0;
if (!ReadFile(m_readpipe[0], buf, len, &ret, NULL))
goto err;
#else
ssize_t ret = read(m_readpipe[0], buf, len);
if (ret < 0) if (ret < 0)
goto err; goto err;
#endif
return ret; return ret;
err: err:
BlenderLog.report(LogVisor::FatalError, strerror(errno)); BlenderLog.report(LogVisor::FatalError, strerror(errno));
@ -109,15 +105,9 @@ err:
size_t BlenderConnection::_writeBuf(const char* buf, size_t len) size_t BlenderConnection::_writeBuf(const char* buf, size_t len)
{ {
#if _WIN32 int ret = write(m_writepipe[1], buf, len);
DWORD ret = 0;
if (!WriteFile(m_writepipe[1], buf, len, &ret, NULL))
goto err;
#else
ssize_t ret = write(m_writepipe[1], buf, len);
if (ret < 0) if (ret < 0)
goto err; goto err;
#endif
return ret; return ret;
err: err:
BlenderLog.report(LogVisor::FatalError, strerror(errno)); BlenderLog.report(LogVisor::FatalError, strerror(errno));
@ -126,13 +116,8 @@ err:
void BlenderConnection::_closePipe() void BlenderConnection::_closePipe()
{ {
#if _WIN32
CloseHandle(m_readpipe[0]);
CloseHandle(m_writepipe[1]);
#else
close(m_readpipe[0]); close(m_readpipe[0]);
close(m_writepipe[1]); close(m_writepipe[1]);
#endif
} }
BlenderConnection::BlenderConnection(bool silenceBlender) BlenderConnection::BlenderConnection(bool silenceBlender)
@ -155,156 +140,181 @@ BlenderConnection::BlenderConnection(bool silenceBlender)
fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp); fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp);
fclose(fp); fclose(fp);
/* Construct communication pipes */ HECL::SystemString blenderAddonPath(TMPDIR);
#if _WIN32 blenderAddonPath += _S("/hecl_blenderaddon.zip");
SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
CreatePipe(&m_readpipe[0], &m_readpipe[1], &sattrs, 0);
CreatePipe(&m_writepipe[0], &m_writepipe[1], &sattrs, 0);
#else
pipe(m_readpipe);
pipe(m_writepipe);
#endif
/* User-specified blender path */ int installAttempt = 0;
#if _WIN32 while (true)
wchar_t BLENDER_BIN_BUF[2048];
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
#else
char* blenderBin = getenv("BLENDER_BIN");
#endif
/* Child process of blender */
#if _WIN32
if (!blenderBin)
{ {
/* Environment not set; use registry */
HKEY blenderKey; /* Construct communication pipes */
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\BlenderFoundation", 0, KEY_READ, &blenderKey) == ERROR_SUCCESS) #if _WIN32
_pipe(m_readpipe, 2048, _O_BINARY);
_pipe(m_writepipe, 2048, _O_BINARY);
HANDLE writehandle = HANDLE(_get_osfhandle(m_writepipe[0]));
SetHandleInformation(writehandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
HANDLE readhandle = HANDLE(_get_osfhandle(m_readpipe[1]));
SetHandleInformation(readhandle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
#else
pipe(m_readpipe);
pipe(m_writepipe);
#endif
/* User-specified blender path */
#if _WIN32
wchar_t BLENDER_BIN_BUF[2048];
wchar_t* blenderBin = _wgetenv(L"BLENDER_BIN");
#else
char* blenderBin = getenv("BLENDER_BIN");
#endif
/* Child process of blender */
#if _WIN32
if (!blenderBin)
{ {
DWORD bufSz = sizeof(BLENDER_BIN_BUF); /* Environment not set; use default */
if (RegGetValueW(blenderKey, NULL, L"Install_Dir", REG_SZ, NULL, BLENDER_BIN_BUF, &bufSz) == ERROR_SUCCESS) wchar_t progFiles[256];
{ if (!GetEnvironmentVariableW(L"ProgramFiles", progFiles, 256))
wcscat_s(BLENDER_BIN_BUF, 2048, L"\\blender.exe"); BlenderLog.report(LogVisor::FatalError, L"unable to determine 'Program Files' path");
blenderBin = BLENDER_BIN_BUF; _snwprintf(BLENDER_BIN_BUF, 2048, L"%s\\Blender Foundation\\Blender\\blender.exe", progFiles);
} blenderBin = BLENDER_BIN_BUF;
RegCloseKey(blenderKey);
} }
}
if (!blenderBin)
{
Log.report(LogVisor::FatalError, "unable to find blender");
return;
}
wchar_t cmdLine[2048]; wchar_t cmdLine[2048];
_snwprintf(cmdLine, 2048, L" --background -P shellscript.py -- %08X %08X", if (installAttempt == 1)
(uint32_t)m_writepipe[0], (uint32_t)m_readpipe[1]); _snwprintf(cmdLine, 2048, L" --background -P \"%s\" -- %d %d \"%s\"",
blenderShellPath.c_str(), int(writehandle), int(readhandle), blenderAddonPath.c_str());
STARTUPINFO sinfo = {sizeof(STARTUPINFO)}; else
HANDLE nulHandle = NULL; _snwprintf(cmdLine, 2048, L" --background -P \"%s\" -- %d %d",
if (silenceBlender) blenderShellPath.c_str(), int(writehandle), int(readhandle));
{
nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
sinfo.hStdError = nulHandle;
sinfo.hStdOutput = nulHandle;
sinfo.dwFlags = STARTF_USESTDHANDLES;
}
PROCESS_INFORMATION pinfo;
if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
Log.report(LogVisor::FatalError, "unable to launch blender");
CloseHandle(m_writepipe[1]);
CloseHandle(m_readpipe[0]);
if (nulHandle)
CloseHandle(nulHandle);
#else
pid_t pid = fork();
if (!pid)
{
close(m_writepipe[1]);
close(m_readpipe[0]);
STARTUPINFO sinfo = {sizeof(STARTUPINFO)};
HANDLE nulHandle = NULL;
if (silenceBlender) if (silenceBlender)
{ {
int devNull = open("/dev/null", O_WRONLY); SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
dup2(devNull, STDOUT_FILENO); nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
dup2(devNull, STDERR_FILENO); sinfo.hStdError = nulHandle;
sinfo.hStdOutput = nulHandle;
sinfo.dwFlags = STARTF_USESTDHANDLES;
} }
char errbuf[256]; PROCESS_INFORMATION pinfo;
char readfds[32]; if (!CreateProcessW(blenderBin, cmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo))
snprintf(readfds, 32, "%d", m_writepipe[0]);
char writefds[32];
snprintf(writefds, 32, "%d", m_readpipe[1]);
/* Try user-specified blender first */
if (blenderBin)
{ {
execlp(blenderBin, blenderBin, LPWSTR messageBuffer = nullptr;
"--background", "-P", blenderShellPath.c_str(), size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
"--", readfds, writefds, NULL); NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
BlenderLog.report(LogVisor::FatalError, L"unable to launch blender from %s: %s", blenderBin, messageBuffer);
}
close(m_writepipe[0]);
close(m_readpipe[1]);
if (nulHandle)
CloseHandle(nulHandle);
#else
pid_t pid = fork();
if (!pid)
{
close(m_writepipe[1]);
close(m_readpipe[0]);
if (silenceBlender)
{
int devNull = open("/dev/null", O_WRONLY);
dup2(devNull, STDOUT_FILENO);
dup2(devNull, STDERR_FILENO);
}
char errbuf[256];
char readfds[32];
snprintf(readfds, 32, "%d", m_writepipe[0]);
char writefds[32];
snprintf(writefds, 32, "%d", m_readpipe[1]);
/* Try user-specified blender first */
if (blenderBin)
{
if (installAttempt == 1)
execlp(blenderBin, blenderBin,
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, blenderAddonPath.c_str(), NULL);
else
execlp(blenderBin, blenderBin,
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, NULL);
if (errno != ENOENT)
{
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
write(m_writepipe[1], errbuf, strlen(errbuf));
exit(1);
}
}
/* Otherwise default blender */
if (installAttempt == 1)
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, blenderAddonPath.c_str(), NULL);
else
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN,
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, NULL);
if (errno != ENOENT) if (errno != ENOENT)
{ {
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno)); snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
write(m_writepipe[1], errbuf, strlen(errbuf)); write(m_writepipe[1], errbuf, strlen(errbuf));
exit(1); exit(1);
} }
}
/* Otherwise default blender */ /* Unable to find blender */
execlp(DEFAULT_BLENDER_BIN, DEFAULT_BLENDER_BIN, write(m_writepipe[1], "NOBLENDER\n", 10);
"--background", "-P", blenderShellPath.c_str(),
"--", readfds, writefds, NULL);
if (errno != ENOENT)
{
snprintf(errbuf, 256, "NOLAUNCH %s\n", strerror(errno));
write(m_writepipe[1], errbuf, strlen(errbuf));
exit(1); exit(1);
} }
close(m_writepipe[0]);
/* Unable to find blender */ close(m_readpipe[1]);
write(m_writepipe[1], "NOBLENDER\n", 10); m_blenderProc = pid;
exit(1);
}
close(m_writepipe[0]);
close(m_readpipe[1]);
m_blenderProc = pid;
#endif #endif
/* Handle first response */ /* Handle first response */
char lineBuf[256]; char lineBuf[256];
_readLine(lineBuf, sizeof(lineBuf)); _readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "NOLAUNCH")) if (!strcmp(lineBuf, "NOLAUNCH"))
{ {
_closePipe(); _closePipe();
BlenderLog.report(LogVisor::FatalError, "Unable to launch blender"); BlenderLog.report(LogVisor::FatalError, "Unable to launch blender");
} }
else if (!strcmp(lineBuf, "NOBLENDER")) else if (!strcmp(lineBuf, "NOBLENDER"))
{ {
_closePipe(); _closePipe();
if (blenderBin) if (blenderBin)
BlenderLog.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); blenderBin, DEFAULT_BLENDER_BIN);
else else
BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"), BlenderLog.report(LogVisor::FatalError, _S("Unable to find blender at '%s'"),
DEFAULT_BLENDER_BIN); DEFAULT_BLENDER_BIN);
} }
else if (!strcmp(lineBuf, "NOADDON")) else if (!strcmp(lineBuf, "NOADDON"))
{ {
_closePipe(); _closePipe();
BlenderLog.report(LogVisor::FatalError, "HECL addon not installed within blender"); InstallAddon(blenderAddonPath.c_str());
} ++installAttempt;
else if (strcmp(lineBuf, "READY")) if (installAttempt >= 2)
{ BlenderLog.report(LogVisor::FatalError, "unable to install blender addon using '%s'", blenderAddonPath.c_str());
_closePipe(); continue;
BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf); }
} else if (strcmp(lineBuf, "READY"))
_writeLine("ACK"); {
_closePipe();
BlenderLog.report(LogVisor::FatalError, "read '%s' from blender; expected 'READY'", lineBuf);
}
_writeLine("ACK");
break;
}
} }
BlenderConnection::~BlenderConnection() BlenderConnection::~BlenderConnection()
@ -326,7 +336,7 @@ bool BlenderConnection::createBlend(const SystemString& path)
_readLine(lineBuf, sizeof(lineBuf)); _readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED")) if (!strcmp(lineBuf, "FINISHED"))
{ {
m_loadedBlend = pathView.str(); m_loadedBlend = path;
return true; return true;
} }
return false; return false;
@ -346,7 +356,7 @@ bool BlenderConnection::openBlend(const SystemString& path)
_readLine(lineBuf, sizeof(lineBuf)); _readLine(lineBuf, sizeof(lineBuf));
if (!strcmp(lineBuf, "FINISHED")) if (!strcmp(lineBuf, "FINISHED"))
{ {
m_loadedBlend = pathView.str(); m_loadedBlend = path;
return true; return true;
} }
return false; return false;
@ -373,12 +383,12 @@ void BlenderConnection::deleteBlend()
if (m_loadedBlend.size()) if (m_loadedBlend.size())
{ {
HECL::Unlink(m_loadedBlend.c_str()); HECL::Unlink(m_loadedBlend.c_str());
BlenderLog.report(LogVisor::Info, "Deleted '%s'", m_loadedBlend.c_str()); BlenderLog.report(LogVisor::Info, _S("Deleted '%s'"), m_loadedBlend.c_str());
m_loadedBlend.clear(); m_loadedBlend.clear();
} }
} }
void BlenderConnection::PyOutStream::linkBlend(const SystemString& target, const std::string& objName, void BlenderConnection::PyOutStream::linkBlend(const std::string& target, const std::string& objName,
bool link) bool link)
{ {
format("if '%s' not in bpy.data.scenes:\n" format("if '%s' not in bpy.data.scenes:\n"

View File

@ -2,7 +2,9 @@
#define BLENDERCONNECTION_HPP #define BLENDERCONNECTION_HPP
#if _WIN32 #if _WIN32
#define _WIN32_LEAN_AND_MEAN 1 #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h> #include <windows.h>
#else #else
#include <unistd.h> #include <unistd.h>
@ -23,17 +25,15 @@ extern class BlenderConnection* SharedBlenderConnection;
class BlenderConnection class BlenderConnection
{ {
bool m_lock; bool m_lock = false;
#if _WIN32 #if _WIN32
HANDLE m_blenderProc; HANDLE m_blenderProc;
HANDLE m_readpipe[2];
HANDLE m_writepipe[2];
#else #else
pid_t m_blenderProc; pid_t m_blenderProc;
#endif
int m_readpipe[2]; int m_readpipe[2];
int m_writepipe[2]; int m_writepipe[2];
#endif SystemString m_loadedBlend;
std::string m_loadedBlend;
size_t _readLine(char* buf, size_t bufSz); size_t _readLine(char* buf, size_t bufSz);
size_t _writeLine(const char* buf); size_t _writeLine(const char* buf);
size_t _readBuf(char* buf, size_t len); size_t _readBuf(char* buf, size_t len);
@ -75,11 +75,12 @@ public:
{ {
if (!m_parent.m_parent || !m_parent.m_parent->m_lock) if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream writing"); BlenderLog.report(LogVisor::FatalError, "lock not held for PyOutStream writing");
if (ch != traits_type::eof() && ch != '\n') if (ch != traits_type::eof() && ch != '\n' && ch != '\0')
{ {
m_lineBuf += char_type(ch); m_lineBuf += char_type(ch);
return ch; return ch;
} }
//printf("FLUSHING %s\n", m_lineBuf.c_str());
m_parent.m_parent->_writeLine(m_lineBuf.c_str()); m_parent.m_parent->_writeLine(m_lineBuf.c_str());
char readBuf[16]; char readBuf[16];
m_parent.m_parent->_readLine(readBuf, 16); m_parent.m_parent->_readLine(readBuf, 16);
@ -109,7 +110,8 @@ public:
public: public:
PyOutStream(const PyOutStream& other) = delete; PyOutStream(const PyOutStream& other) = delete;
PyOutStream(PyOutStream&& other) PyOutStream(PyOutStream&& other)
: m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)) : m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)),
std::ostream(&m_sbuf)
{other.m_parent = nullptr;} {other.m_parent = nullptr;}
~PyOutStream() {close();} ~PyOutStream() {close();}
void close() void close()
@ -131,13 +133,19 @@ public:
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
char* result = nullptr; char* result = nullptr;
#ifdef _WIN32
int length = _vscprintf(fmt, ap);
result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap);
#else
int length = vasprintf(&result, fmt, ap); int length = vasprintf(&result, fmt, ap);
#endif
va_end(ap);
if (length > 0) if (length > 0)
this->write(result, length); this->write(result, length);
free(result); free(result);
va_end(ap);
} }
void linkBlend(const SystemString& target, const std::string& objName, bool link=true); void linkBlend(const std::string& target, const std::string& objName, bool link=true);
}; };
inline PyOutStream beginPythonOut(bool deleteOnError=false) inline PyOutStream beginPythonOut(bool deleteOnError=false)
{ {

View File

@ -1,21 +1,32 @@
list(APPEND PY_SOURCES list(APPEND PY_SOURCES
addon/__init__.py hecl/__init__.py
addon/Nodegrid.py hecl/Nodegrid.py
addon/hmdl/__init__.py hecl/hmdl/__init__.py
addon/hmdl/HMDLMesh.py hecl/hmdl/HMDLMesh.py
addon/hmdl/HMDLShader.py hecl/hmdl/HMDLShader.py
addon/hmdl/HMDLSkin.py hecl/hmdl/HMDLSkin.py
addon/hmdl/HMDLTxtr.py hecl/hmdl/HMDLTxtr.py
addon/sact/__init__.py hecl/sact/__init__.py
addon/sact/SACTAction.py hecl/sact/SACTAction.py
addon/sact/SACTEvent.py hecl/sact/SACTEvent.py
addon/sact/SACTSubtype.py) hecl/sact/SACTSubtype.py)
bintoc(hecl_blendershell.c hecl_blendershell.py HECL_BLENDERSHELL) bintoc(hecl_blendershell.c hecl_blendershell.py HECL_BLENDERSHELL)
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip)
message("-- Generating addon package")
execute_process(COMMAND python zip_package.py
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip)
message(FATAL_ERROR "Unable to generate ${CMAKE_CURRENT_SOURCE_DIR}/hecl.zip; is python installed?")
endif()
endif()
bintoc(hecl_addon.c hecl.zip HECL_ADDON)
add_library(HECLBlender add_library(HECLBlender
BlenderConnection.cpp BlenderConnection.cpp
BlenderConnection.hpp BlenderConnection.hpp
hecl_blendershell.py hecl_blendershell.py
hecl_blendershell.c hecl_blendershell.c
hecl_addon.c
${PY_SOURCES}) ${PY_SOURCES})

View File

@ -9,6 +9,10 @@ if '--' not in sys.argv:
args = sys.argv[sys.argv.index('--')+1:] args = sys.argv[sys.argv.index('--')+1:]
readfd = int(args[0]) readfd = int(args[0])
writefd = int(args[1]) writefd = int(args[1])
if sys.platform == "win32":
import msvcrt
readfd = msvcrt.open_osfhandle(readfd, os.O_RDONLY | os.O_BINARY)
writefd = msvcrt.open_osfhandle(writefd, os.O_WRONLY | os.O_BINARY)
def readpipeline(): def readpipeline():
retval = bytearray() retval = bytearray()
@ -29,14 +33,23 @@ def quitblender():
writepipeline(b'QUITTING') writepipeline(b'QUITTING')
bpy.ops.wm.quit_blender() bpy.ops.wm.quit_blender()
# Check that HECL addon is installed/enabled # If there's a third argument, use it as the .zip path containing the addon
if 'hecl' not in bpy.context.user_preferences.addons: if len(args) >= 3:
if 'FINISHED' not in bpy.ops.wm.addon_enable(module='hecl'): bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[2])
writepipeline(b'NOADDON') bpy.ops.wm.addon_refresh()
bpy.ops.wm.quit_blender()
# Make addon available to commands # Make addon available to commands
import hecl if bpy.context.user_preferences.addons.find('hecl') == -1:
try:
bpy.ops.wm.addon_enable(module='hecl')
bpy.ops.wm.save_userpref()
except:
pass
try:
import hecl
except:
writepipeline(b'NOADDON')
bpy.ops.wm.quit_blender()
# Intro handshake # Intro handshake
writepipeline(b'READY') writepipeline(b'READY')

View File

@ -0,0 +1,18 @@
import sys, os
import zipfile
def zipdir(path, ziph):
# ziph is zipfile handle
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.join(root, file))
package_path = 'hecl'
target_zip = 'hecl.zip'
zf = zipfile.ZipFile(target_zip, mode='w', compression=zipfile.ZIP_DEFLATED)
print('GENERATING', target_zip)
if os.path.isdir(package_path):
zipdir(package_path, zf)
zf.close()

View File

@ -1,7 +1,3 @@
if(WIN32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /INCLUDE:HECLDataSpecs")
endif()
add_executable(hecl main.cpp add_executable(hecl main.cpp
ToolBase.hpp ToolBase.hpp
ToolPackage.hpp ToolPackage.hpp
@ -24,8 +20,12 @@ list(APPEND DATA_SPEC_LIBS
DNAMP1 DNAMP1
DNACommon) DNACommon)
if(NOT WIN32)
list(APPEND PLAT_LIBS pthread)
endif()
target_link_libraries(hecl target_link_libraries(hecl
${DATA_SPEC_LIBS} ${DATA_SPEC_LIBS}
HECLDatabase HECLBlender HECLCommon AthenaCore NOD HECLDatabase HECLBlender HECLCommon AthenaCore NOD
LogVisor AthenaLibYaml png squish blowfish z lzo2 pthread LogVisor AthenaLibYaml png squish blowfish z lzo2 ${PLAT_LIBS}
) )

View File

@ -44,7 +44,7 @@ public:
LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory"); LogModule.report(LogVisor::FatalError, "hecl extract must be ran within a project directory");
size_t ErrorRef = LogVisor::ErrorCount; size_t ErrorRef = LogVisor::ErrorCount;
HECL::SystemString rootDir = info.cwd + '/' + baseFile; HECL::SystemString rootDir = info.cwd + _S('/') + baseFile;
HECL::ProjectRootPath newProjRoot(rootDir); HECL::ProjectRootPath newProjRoot(rootDir);
newProjRoot.makeDir(); newProjRoot.makeDir();
m_fallbackProj.reset(new HECL::Database::Project(newProjRoot)); m_fallbackProj.reset(new HECL::Database::Project(newProjRoot));

View File

@ -1,7 +1,9 @@
#if _WIN32 #if _WIN32
#define WIN_PAUSE 1 #define WIN_PAUSE 1
#include <objbase.h>
#endif #endif
#include <clocale>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -89,6 +91,12 @@ int wmain(int argc, const wchar_t** argv)
int main(int argc, const char** argv) int main(int argc, const char** argv)
#endif #endif
{ {
#if _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
#else
std::setlocale(LC_ALL, "en-US.UTF-8");
#endif
/* Xterm check */ /* Xterm check */
const char* term = getenv("TERM"); const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5)) if (term && !strncmp(term, "xterm", 5))

2
hecl/extern/Athena vendored

@ -1 +1 @@
Subproject commit 66cb6c982e8824fa887807bf8ba42376b301478b Subproject commit 488a0100e2e6044361a494a280c7f1a28911762e

View File

@ -1,5 +1,5 @@
add_subdirectory(libpng)
add_subdirectory(libSquish) add_subdirectory(libSquish)
add_subdirectory(blowfish) add_subdirectory(blowfish)
add_subdirectory(LogVisor) add_subdirectory(LogVisor)
add_subdirectory(Athena EXCLUDE_FROM_ALL) add_subdirectory(Athena EXCLUDE_FROM_ALL)
add_subdirectory(libpng)

@ -1 +1 @@
Subproject commit 154b84413050cdb03f281c42861b9af6eaef7d14 Subproject commit 828db515ba316903c367b9dc7acf9d0bec0969e7

View File

@ -10,11 +10,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#else #else
#define _WIN32_LEAN_AND_MEAN 1 #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <Windows.h> #include <Windows.h>
#include <wchar.h> #include <wchar.h>
#include "winsupport.h" #include "winsupport.hpp"
#define snprintf _snprintf
#endif #endif
#include <time.h> #include <time.h>
@ -29,8 +30,10 @@
#include "../extern/blowfish/blowfish.h" #include "../extern/blowfish/blowfish.h"
/* Handy MIN/MAX macros */ /* Handy MIN/MAX macros */
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
namespace HECL namespace HECL
{ {
@ -135,11 +138,11 @@ static inline void Unlink(const SystemChar* file)
#endif #endif
} }
static inline void MakeDir(const SystemChar* dir) static inline void MakeDir(const char* dir)
{ {
#if _WIN32 #if _WIN32
HRESULT err; HRESULT err;
if (!CreateDirectory(dir, NULL)) if (!CreateDirectoryA(dir, NULL))
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS) if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir); LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir);
#else #else
@ -149,11 +152,29 @@ static inline void MakeDir(const SystemChar* dir)
#endif #endif
} }
#if _WIN32
static inline void MakeDir(const wchar_t* dir)
{
HRESULT err;
if (!CreateDirectoryW(dir, NULL))
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir);
}
#endif
static inline void MakeLink(const SystemChar* target, const SystemChar* linkPath) static inline void MakeLink(const SystemChar* target, const SystemChar* linkPath)
{ {
#if _WIN32 #if _WIN32
HRESULT res = CreateShellLink(target, linkPath, _S("HECL Link")); /* :(( */
if (!SUCCEEDED(res))
{
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); /* :((( */
LogModule.report(LogVisor::FatalError, _S("MakeLink(%s, %s): %s"), target, linkPath, messageBuffer);
}
#else #else
if (symlink(target, linkPath)) if (symlink(target, linkPath)) /* :) */
if (errno != EEXIST) if (errno != EEXIST)
LogModule.report(LogVisor::FatalError, "MakeLink(%s, %s): %s", target, linkPath, strerror(errno)); LogModule.report(LogVisor::FatalError, "MakeLink(%s, %s): %s", target, linkPath, strerror(errno));
#endif #endif

View File

@ -1,14 +0,0 @@
#ifndef _HECL_WINSUPPORT_H_
#define _HECL_WINSUPPORT_H_
#if __cplusplus
extern "C" {
#endif
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
#if __cplusplus
}
#endif
#endif // _HECL_WINSUPPORT_H_

View File

@ -0,0 +1,13 @@
#ifndef _HECL_WINSUPPORT_H_
#define _HECL_WINSUPPORT_H_
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include "windows.h"
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
HRESULT CreateShellLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszDesc);
HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize);
#endif // _HECL_WINSUPPORT_H_

View File

@ -4,7 +4,7 @@ add_subdirectory(Frontend)
add_subdirectory(Runtime) add_subdirectory(Runtime)
if(WIN32) if(WIN32)
list(APPEND PLAT_SRCS winsupport.c ../include/HECL/winsupport.h) list(APPEND PLAT_SRCS winsupport.cpp ../include/HECL/winsupport.hpp)
endif() endif()
add_library(HECLCommon add_library(HECLCommon

View File

@ -5,29 +5,45 @@ namespace HECL
std::string WideToUTF8(const std::wstring& src) std::string WideToUTF8(const std::wstring& src)
{ {
#if _WIN32
int len = WideCharToMultiByte(CP_UTF8, 0, src.c_str(), src.size(), nullptr, 0, nullptr, nullptr);
std::string retval(len, '\0');
WideCharToMultiByte(CP_UTF8, 0, src.c_str(), src.size(), &retval[0], len, nullptr, nullptr);
return retval;
#else
std::string retval; std::string retval;
retval.reserve(src.length()); retval.reserve(src.length());
std::mbstate_t state = {};
for (wchar_t ch : src) for (wchar_t ch : src)
{ {
char mb[4]; char mb[MB_LEN_MAX];
int c = std::wctomb(mb, ch); int c = std::wcrtomb(mb, ch, &state);
retval.append(mb, c); retval.append(mb, c);
} }
return retval; return retval;
#endif
} }
std::wstring UTF8ToWide(const std::string& src) std::wstring UTF8ToWide(const std::string& src)
{ {
#if _WIN32
int len = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), src.size(), nullptr, 0);
std::wstring retval(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, src.c_str(), src.size(), &retval[0], len);
return retval;
#else
std::wstring retval; std::wstring retval;
retval.reserve(src.length()); retval.reserve(src.length());
const char* buf = src.c_str(); const char* buf = src.c_str();
std::mbstate_t state = {};
while (*buf) while (*buf)
{ {
wchar_t wc; wchar_t wc;
buf += std::mbtowc(&wc, buf, MB_CUR_MAX); buf += std::mbrtowc(&wc, buf, MB_LEN_MAX, &state);
retval += wc; retval += wc;
} }
return retval; return retval;
#endif
} }
} }

View File

@ -1,34 +0,0 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
/*
* The memmem() function finds the start of the first occurrence of the
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
* length 'hlen'.
*
* The return value is a pointer to the beginning of the sub-string, or
* NULL if the substring is not found.
*/
void *memmem(const uint8_t *haystack, size_t hlen, const void *needle, size_t nlen)
{
int needle_first;
const uint8_t *p = haystack;
size_t plen = hlen;
if (!nlen)
return NULL;
needle_first = *(unsigned char *)needle;
while (plen >= nlen && (p = memchr(p, needle_first, plen - nlen + 1)))
{
if (!memcmp(p, needle, nlen))
return (void *)p;
p++;
plen = hlen - (p - haystack);
}
return NULL;
}

158
hecl/lib/winsupport.cpp Normal file
View File

@ -0,0 +1,158 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <string>
#include "HECL/winsupport.hpp"
/*
* The memmem() function finds the start of the first occurrence of the
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
* length 'hlen'.
*
* The return value is a pointer to the beginning of the sub-string, or
* NULL if the substring is not found.
*/
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
{
int needle_first;
const uint8_t *p = static_cast<const uint8_t*>(haystack);
size_t plen = hlen;
if (!nlen)
return NULL;
needle_first = *(unsigned char *)needle;
while (plen >= nlen && (p = static_cast<const uint8_t*>(memchr(p, needle_first, plen - nlen + 1))))
{
if (!memcmp(p, needle, nlen))
return (void *)p;
p++;
plen = hlen - (p - static_cast<const uint8_t*>(haystack));
}
return NULL;
}
/* Clearly, MS doesn't require enough headers for this */
#include "winnls.h"
#include "shobjidl.h"
#include "objbase.h"
#include "objidl.h"
#include "shlguid.h"
#include "strsafe.h"
HRESULT CreateShellLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszDesc)
{
std::wstring targetStr(lpszPathObj);
for (wchar_t& ch : targetStr)
if (ch == L'/')
ch = L'\\';
std::wstring linkStr(lpszPathLink);
linkStr += L".lnk";
for (wchar_t& ch : linkStr)
if (ch == L'/')
ch = L'\\';
HRESULT hres;
IShellLink* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
WCHAR targetBuf[MAX_PATH];
WCHAR linkBuf[MAX_PATH];
WCHAR* linkFinalPart = nullptr;
GetFullPathNameW(linkStr.c_str(), MAX_PATH, linkBuf, &linkFinalPart);
if (linkFinalPart != linkBuf)
*(linkFinalPart-1) = L'\0';
StringCbPrintfW(targetBuf, MAX_PATH, L"%s\\%s", linkBuf, targetStr.c_str());
if (linkFinalPart != linkBuf)
*(linkFinalPart - 1) = L'\\';
psl->SetPath(targetBuf);
psl->SetRelativePath(linkBuf, 0);
psl->SetDescription(lpszDesc);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(linkBuf, TRUE);
ppf->Release();
}
psl->Release();
}
return hres;
}
HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize)
{
HRESULT hres;
IShellLink* psl;
WCHAR szGotPath[MAX_PATH];
WCHAR szDescription[MAX_PATH];
WIN32_FIND_DATA wfd;
*lpszPath = 0; // Assume failure
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Get a pointer to the IPersistFile interface.
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres))
{
// Load the shortcut.
hres = ppf->Load(lpszLinkFile, STGM_READ);
if (SUCCEEDED(hres))
{
// Resolve the link.
HWND hwnd = GetConsoleWindow();
if (!hwnd)
hwnd = GetTopWindow(nullptr);
hres = psl->Resolve(hwnd, 0);
if (SUCCEEDED(hres))
{
// Get the path to the link target.
hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH);
if (SUCCEEDED(hres))
{
// Get the description of the target.
hres = psl->GetDescription(szDescription, MAX_PATH);
if (SUCCEEDED(hres))
{
hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
}
}
}
}
// Release the pointer to the IPersistFile interface.
ppf->Release();
}
// Release the pointer to the IShellLink interface.
psl->Release();
}
return hres;
}