Windows refactors

This commit is contained in:
Jack Andersen 2015-08-30 17:36:24 -10:00
parent 199b8e7c32
commit e4f53eaa7c
30 changed files with 479 additions and 251 deletions

View File

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

View File

@ -2,7 +2,9 @@
#define BLENDERCONNECTION_HPP
#if _WIN32
#define _WIN32_LEAN_AND_MEAN 1
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#else
#include <unistd.h>
@ -23,17 +25,15 @@ extern class BlenderConnection* SharedBlenderConnection;
class BlenderConnection
{
bool m_lock;
bool m_lock = false;
#if _WIN32
HANDLE m_blenderProc;
HANDLE m_readpipe[2];
HANDLE m_writepipe[2];
#else
pid_t m_blenderProc;
#endif
int m_readpipe[2];
int m_writepipe[2];
#endif
std::string m_loadedBlend;
SystemString m_loadedBlend;
size_t _readLine(char* buf, size_t bufSz);
size_t _writeLine(const char* buf);
size_t _readBuf(char* buf, size_t len);
@ -75,11 +75,12 @@ public:
{
if (!m_parent.m_parent || !m_parent.m_parent->m_lock)
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);
return ch;
}
//printf("FLUSHING %s\n", m_lineBuf.c_str());
m_parent.m_parent->_writeLine(m_lineBuf.c_str());
char readBuf[16];
m_parent.m_parent->_readLine(readBuf, 16);
@ -109,7 +110,8 @@ public:
public:
PyOutStream(const PyOutStream& other) = delete;
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;}
~PyOutStream() {close();}
void close()
@ -131,13 +133,19 @@ public:
va_list ap;
va_start(ap, fmt);
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);
#endif
va_end(ap);
if (length > 0)
this->write(result, length);
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)
{

View File

@ -13,9 +13,20 @@ list(APPEND PY_SOURCES
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
BlenderConnection.cpp
BlenderConnection.hpp
hecl_blendershell.py
hecl_blendershell.c
hecl_addon.c
${PY_SOURCES})

View File

@ -9,6 +9,10 @@ if '--' not in sys.argv:
args = sys.argv[sys.argv.index('--')+1:]
readfd = int(args[0])
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():
retval = bytearray()
@ -29,14 +33,23 @@ def quitblender():
writepipeline(b'QUITTING')
bpy.ops.wm.quit_blender()
# Check that HECL addon is installed/enabled
if 'hecl' not in bpy.context.user_preferences.addons:
if 'FINISHED' not in bpy.ops.wm.addon_enable(module='hecl'):
writepipeline(b'NOADDON')
bpy.ops.wm.quit_blender()
# If there's a third argument, use it as the .zip path containing the addon
if len(args) >= 3:
bpy.ops.wm.addon_install(overwrite=True, target='DEFAULT', filepath=args[2])
bpy.ops.wm.addon_refresh()
# 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
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
ToolBase.hpp
ToolPackage.hpp
@ -24,8 +20,12 @@ list(APPEND DATA_SPEC_LIBS
DNAMP1
DNACommon)
if(NOT WIN32)
list(APPEND PLAT_LIBS pthread)
endif()
target_link_libraries(hecl
${DATA_SPEC_LIBS}
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");
size_t ErrorRef = LogVisor::ErrorCount;
HECL::SystemString rootDir = info.cwd + '/' + baseFile;
HECL::SystemString rootDir = info.cwd + _S('/') + baseFile;
HECL::ProjectRootPath newProjRoot(rootDir);
newProjRoot.makeDir();
m_fallbackProj.reset(new HECL::Database::Project(newProjRoot));

View File

@ -1,7 +1,9 @@
#if _WIN32
#define WIN_PAUSE 1
#include <objbase.h>
#endif
#include <clocale>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -89,6 +91,12 @@ int wmain(int argc, const wchar_t** argv)
int main(int argc, const char** argv)
#endif
{
#if _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
#else
std::setlocale(LC_ALL, "en-US.UTF-8");
#endif
/* Xterm check */
const char* term = getenv("TERM");
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(blowfish)
add_subdirectory(LogVisor)
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 <unistd.h>
#else
#define _WIN32_LEAN_AND_MEAN 1
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <Windows.h>
#include <wchar.h>
#include "winsupport.h"
#define snprintf _snprintf
#include "winsupport.hpp"
#endif
#include <time.h>
@ -29,8 +30,10 @@
#include "../extern/blowfish/blowfish.h"
/* Handy MIN/MAX macros */
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
namespace HECL
{
@ -135,11 +138,11 @@ static inline void Unlink(const SystemChar* file)
#endif
}
static inline void MakeDir(const SystemChar* dir)
static inline void MakeDir(const char* dir)
{
#if _WIN32
HRESULT err;
if (!CreateDirectory(dir, NULL))
if (!CreateDirectoryA(dir, NULL))
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir);
#else
@ -149,11 +152,29 @@ static inline void MakeDir(const SystemChar* dir)
#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)
{
#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
if (symlink(target, linkPath))
if (symlink(target, linkPath)) /* :) */
if (errno != EEXIST)
LogModule.report(LogVisor::FatalError, "MakeLink(%s, %s): %s", target, linkPath, strerror(errno));
#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)
if(WIN32)
list(APPEND PLAT_SRCS winsupport.c ../include/HECL/winsupport.h)
list(APPEND PLAT_SRCS winsupport.cpp ../include/HECL/winsupport.hpp)
endif()
add_library(HECLCommon

View File

@ -5,29 +5,45 @@ namespace HECL
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;
retval.reserve(src.length());
std::mbstate_t state = {};
for (wchar_t ch : src)
{
char mb[4];
int c = std::wctomb(mb, ch);
char mb[MB_LEN_MAX];
int c = std::wcrtomb(mb, ch, &state);
retval.append(mb, c);
}
return retval;
#endif
}
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;
retval.reserve(src.length());
const char* buf = src.c_str();
std::mbstate_t state = {};
while (*buf)
{
wchar_t wc;
buf += std::mbtowc(&wc, buf, MB_CUR_MAX);
buf += std::mbrtowc(&wc, buf, MB_LEN_MAX, &state);
retval += wc;
}
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;
}