diff --git a/hecl/driver/main.cpp b/hecl/driver/main.cpp index 6a607c7ca..9c4c3363a 100644 --- a/hecl/driver/main.cpp +++ b/hecl/driver/main.cpp @@ -255,7 +255,7 @@ static std::unique_ptr MakeSelectedTool(hecl::SystemString toolName, T return std::make_unique(info); } - std::unique_ptr fp{hecl::Fopen(toolName.c_str(), _SYS_STR("rb")), std::fclose}; + auto fp = hecl::FopenUnique(toolName.c_str(), _SYS_STR("rb")); if (fp == nullptr) { LogModule.report(logvisor::Error, fmt(_SYS_STR("unrecognized tool '{}'")), toolNameLower); return nullptr; diff --git a/hecl/include/hecl/Database.hpp b/hecl/include/hecl/Database.hpp index 2b70e4b0a..5f7dee115 100644 --- a/hecl/include/hecl/Database.hpp +++ b/hecl/include/hecl/Database.hpp @@ -257,7 +257,7 @@ public: class ConfigFile { SystemString m_filepath; std::vector m_lines; - FILE* m_lockedFile = nullptr; + UniqueFilePtr m_lockedFile; public: ConfigFile(const Project& project, SystemStringView name, SystemStringView subdir = _SYS_STR("/.hecl/")); diff --git a/hecl/include/hecl/hecl.hpp b/hecl/include/hecl/hecl.hpp index 4ab571852..97a46bb5c 100644 --- a/hecl/include/hecl/hecl.hpp +++ b/hecl/include/hecl/hecl.hpp @@ -275,6 +275,16 @@ inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType return fp; } +struct UniqueFileDeleter { + void operator()(FILE* file) const noexcept { std::fclose(file); } +}; +using UniqueFilePtr = std::unique_ptr; + +inline UniqueFilePtr FopenUnique(const SystemChar* path, const SystemChar* mode, + FileLockType lock = FileLockType::None) { + return UniqueFilePtr{Fopen(path, mode, lock)}; +} + inline int FSeek(FILE* fp, int64_t offset, int whence) { #if _WIN32 return _fseeki64(fp, offset, whence); diff --git a/hecl/lib/Blender/Connection.cpp b/hecl/lib/Blender/Connection.cpp index f7b5a1cc0..f62bea7a1 100644 --- a/hecl/lib/Blender/Connection.cpp +++ b/hecl/lib/Blender/Connection.cpp @@ -60,19 +60,23 @@ extern "C" uint8_t HECL_STARTUP[]; extern "C" size_t HECL_STARTUP_SZ; static void InstallBlendershell(const SystemChar* path) { - FILE* fp = hecl::Fopen(path, _SYS_STR("w")); - if (!fp) + auto fp = hecl::FopenUnique(path, _SYS_STR("w")); + + if (fp == nullptr) { BlenderLog.report(logvisor::Fatal, fmt(_SYS_STR("unable to open {} for writing")), path); - fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp); - fclose(fp); + } + + std::fwrite(HECL_BLENDERSHELL, 1, HECL_BLENDERSHELL_SZ, fp.get()); } static void InstallAddon(const SystemChar* path) { - FILE* fp = hecl::Fopen(path, _SYS_STR("wb")); - if (!fp) + auto fp = hecl::FopenUnique(path, _SYS_STR("wb")); + + if (fp == nullptr) { BlenderLog.report(logvisor::Fatal, fmt(_SYS_STR("Unable to install blender addon at '{}'")), path); - fwrite(HECL_ADDON, 1, HECL_ADDON_SZ, fp); - fclose(fp); + } + + std::fwrite(HECL_ADDON, 1, HECL_ADDON_SZ, fp.get()); } static int Read(int fd, void* buf, size_t size) { @@ -224,18 +228,20 @@ void Connection::_closePipe() { void Connection::_blenderDied() { std::this_thread::sleep_for(std::chrono::milliseconds(100)); - FILE* errFp = hecl::Fopen(m_errPath.c_str(), _SYS_STR("r")); - if (errFp) { - fseek(errFp, 0, SEEK_END); - int64_t len = hecl::FTell(errFp); - if (len) { - fseek(errFp, 0, SEEK_SET); - std::unique_ptr buf(new char[len + 1]); - memset(buf.get(), 0, len + 1); - fread(buf.get(), 1, len, errFp); + auto errFp = hecl::FopenUnique(m_errPath.c_str(), _SYS_STR("r")); + + if (errFp != nullptr) { + std::fseek(errFp.get(), 0, SEEK_END); + const int64_t len = hecl::FTell(errFp.get()); + + if (len != 0) { + std::fseek(errFp.get(), 0, SEEK_SET); + const auto buf = std::make_unique(len + 1); + std::fread(buf.get(), 1, len, errFp.get()); BlenderLog.report(logvisor::Fatal, fmt("\n{:.{}s}"), buf.get(), len); } } + BlenderLog.report(logvisor::Fatal, fmt("Blender Exception")); } diff --git a/hecl/lib/Project.cpp b/hecl/lib/Project.cpp index 8040ec7e9..961e47566 100644 --- a/hecl/lib/Project.cpp +++ b/hecl/lib/Project.cpp @@ -43,17 +43,19 @@ Project::ConfigFile::ConfigFile(const Project& project, SystemStringView name, S } std::vector& Project::ConfigFile::lockAndRead() { - if (m_lockedFile) + if (m_lockedFile != nullptr) { return m_lines; + } - m_lockedFile = hecl::Fopen(m_filepath.c_str(), _SYS_STR("a+"), FileLockType::Write); - hecl::FSeek(m_lockedFile, 0, SEEK_SET); + m_lockedFile = hecl::FopenUnique(m_filepath.c_str(), _SYS_STR("a+"), FileLockType::Write); + hecl::FSeek(m_lockedFile.get(), 0, SEEK_SET); std::string mainString; char readBuf[1024]; size_t readSz; - while ((readSz = fread(readBuf, 1, 1024, m_lockedFile))) + while ((readSz = std::fread(readBuf, 1, sizeof(readBuf), m_lockedFile.get()))) { mainString += std::string(readBuf, readSz); + } std::string::const_iterator begin = mainString.begin(); std::string::const_iterator end = mainString.begin(); @@ -110,14 +112,13 @@ bool Project::ConfigFile::checkForLine(std::string_view refLine) { } void Project::ConfigFile::unlockAndDiscard() { - if (!m_lockedFile) { + if (m_lockedFile == nullptr) { LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, fmt("Project::ConfigFile::lockAndRead not yet called")); return; } m_lines.clear(); - fclose(m_lockedFile); - m_lockedFile = NULL; + m_lockedFile.reset(); } bool Project::ConfigFile::unlockAndCommit() { @@ -126,23 +127,22 @@ bool Project::ConfigFile::unlockAndCommit() { return false; } - SystemString newPath = m_filepath + _SYS_STR(".part"); - FILE* newFile = hecl::Fopen(newPath.c_str(), _SYS_STR("w"), FileLockType::Write); + const SystemString newPath = m_filepath + _SYS_STR(".part"); + auto newFile = hecl::FopenUnique(newPath.c_str(), _SYS_STR("w"), FileLockType::Write); bool fail = false; for (const std::string& line : m_lines) { - if (fwrite(line.c_str(), 1, line.size(), newFile) != line.size()) { + if (std::fwrite(line.c_str(), 1, line.size(), newFile.get()) != line.size()) { fail = true; break; } - if (fwrite("\n", 1, 1, newFile) != 1) { + if (std::fputc('\n', newFile.get()) == EOF) { fail = true; break; } } m_lines.clear(); - fclose(newFile); - fclose(m_lockedFile); - m_lockedFile = NULL; + newFile.reset(); + m_lockedFile.reset(); if (fail) { #if HECL_UCS2 _wunlink(newPath.c_str()); @@ -191,20 +191,21 @@ Project::Project(const ProjectRootPath& rootPath) m_cookedRoot.makeDir(); /* Ensure beacon is valid or created */ - ProjectPath beaconPath(m_dotPath, _SYS_STR("beacon")); - FILE* bf = hecl::Fopen(beaconPath.getAbsolutePath().data(), _SYS_STR("a+b")); + const ProjectPath beaconPath(m_dotPath, _SYS_STR("beacon")); + auto bf = hecl::FopenUnique(beaconPath.getAbsolutePath().data(), _SYS_STR("a+b")); struct BeaconStruct { hecl::FourCC magic; uint32_t version; } beacon; -#define DATA_VERSION 1 - if (fread(&beacon, 1, sizeof(beacon), bf) != sizeof(beacon)) { - fseek(bf, 0, SEEK_SET); + constexpr uint32_t DATA_VERSION = 1; + if (std::fread(&beacon, 1, sizeof(beacon), bf.get()) != sizeof(beacon)) { + std::fseek(bf.get(), 0, SEEK_SET); beacon.magic = HECLfcc; beacon.version = SBig(DATA_VERSION); - fwrite(&beacon, 1, sizeof(beacon), bf); + std::fwrite(&beacon, 1, sizeof(beacon), bf.get()); } - fclose(bf); + bf.reset(); + if (beacon.magic != HECLfcc || SBig(beacon.version) != DATA_VERSION) { LogModule.report(logvisor::Fatal, fmt("incompatible project version")); return; diff --git a/hecl/lib/ProjectPath.cpp b/hecl/lib/ProjectPath.cpp index 8a13842eb..0eca7f826 100644 --- a/hecl/lib/ProjectPath.cpp +++ b/hecl/lib/ProjectPath.cpp @@ -256,17 +256,22 @@ ProjectRootPath SearchForProject(SystemStringView path) { Sstat theStat; if (!hecl::Stat(testIndexPath.c_str(), &theStat)) { if (S_ISREG(theStat.st_mode)) { - FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb")); - if (!fp) + const auto fp = hecl::FopenUnique(testIndexPath.c_str(), _SYS_STR("rb")); + if (fp == nullptr) { continue; + } + char magic[4]; - size_t readSize = fread(magic, 1, 4, fp); - fclose(fp); - if (readSize != 4) + const size_t readSize = std::fread(magic, 1, sizeof(magic), fp.get()); + if (readSize != sizeof(magic)) { continue; + } + static constexpr hecl::FourCC hecl("HECL"); - if (hecl::FourCC(magic) != hecl) + if (hecl::FourCC(magic) != hecl) { continue; + } + return ProjectRootPath(testPath); } } @@ -280,40 +285,51 @@ ProjectRootPath SearchForProject(SystemStringView path) { } ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut) { - ProjectRootPath testRoot(path); + const ProjectRootPath testRoot(path); auto begin = testRoot.getAbsolutePath().begin(); auto end = testRoot.getAbsolutePath().end(); + while (begin != end) { SystemString testPath(begin, end); SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon"); Sstat theStat; + if (!hecl::Stat(testIndexPath.c_str(), &theStat)) { if (S_ISREG(theStat.st_mode)) { - FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb")); - if (!fp) + const auto fp = hecl::FopenUnique(testIndexPath.c_str(), _SYS_STR("rb")); + if (fp == nullptr) { continue; + } + char magic[4]; - size_t readSize = fread(magic, 1, 4, fp); - fclose(fp); - if (readSize != 4) + const size_t readSize = std::fread(magic, 1, sizeof(magic), fp.get()); + if (readSize != sizeof(magic)) { continue; - if (hecl::FourCC(magic) != FOURCC('HECL')) + } + if (hecl::FourCC(magic) != FOURCC('HECL')) { continue; - ProjectRootPath newRootPath = ProjectRootPath(testPath); - auto origEnd = testRoot.getAbsolutePath().end(); - while (end != origEnd && *end != _SYS_STR('/') && *end != _SYS_STR('\\')) + } + + const ProjectRootPath newRootPath = ProjectRootPath(testPath); + const auto origEnd = testRoot.getAbsolutePath().end(); + while (end != origEnd && *end != _SYS_STR('/') && *end != _SYS_STR('\\')) { ++end; - if (end != origEnd && (*end == _SYS_STR('/') || *end == _SYS_STR('\\'))) + } + if (end != origEnd && (*end == _SYS_STR('/') || *end == _SYS_STR('\\'))) { ++end; + } + subpathOut.assign(end, origEnd); return newRootPath; } } - while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\')) + while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\')) { --end; - if (begin != end) + } + if (begin != end) { --end; + } } return ProjectRootPath(); } diff --git a/hecl/lib/SteamFinder.cpp b/hecl/lib/SteamFinder.cpp index e9dccea47..a930e5d5f 100644 --- a/hecl/lib/SteamFinder.cpp +++ b/hecl/lib/SteamFinder.cpp @@ -57,44 +57,45 @@ hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name) { #endif - hecl::SystemString appPath = hecl::SystemString(_SYS_STR("common")) + PATH_SEP + name; + const hecl::SystemString appPath = hecl::SystemString(_SYS_STR("common")) + PATH_SEP + name; /* Try main steam install directory first */ - hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps"); - hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath; - if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) + const hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps"); + const hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath; + if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) { return mainAppPath; + } /* Iterate alternate steam install dirs */ - hecl::SystemString libraryFoldersVdfPath = steamAppsMain + PATH_SEP + _SYS_STR("libraryfolders.vdf"); - FILE* fp = hecl::Fopen(libraryFoldersVdfPath.c_str(), _SYS_STR("r")); - if (!fp) + const hecl::SystemString libraryFoldersVdfPath = steamAppsMain + PATH_SEP + _SYS_STR("libraryfolders.vdf"); + auto fp = hecl::FopenUnique(libraryFoldersVdfPath.c_str(), _SYS_STR("r")); + if (fp == nullptr) { return {}; - hecl::FSeek(fp, 0, SEEK_END); - int64_t fileLen = hecl::FTell(fp); + } + hecl::FSeek(fp.get(), 0, SEEK_END); + const int64_t fileLen = hecl::FTell(fp.get()); if (fileLen <= 0) { - fclose(fp); return {}; } - hecl::FSeek(fp, 0, SEEK_SET); - std::string fileBuf; - fileBuf.resize(fileLen); - if (fread(&fileBuf[0], 1, fileLen, fp) != fileLen) { - fclose(fp); + hecl::FSeek(fp.get(), 0, SEEK_SET); + std::string fileBuf(fileLen, '\0'); + if (std::fread(fileBuf.data(), 1, fileLen, fp.get()) != fileLen) { return {}; } - fclose(fp); std::smatch dirMatch; auto begin = fileBuf.cbegin(); - auto end = fileBuf.cend(); + const auto end = fileBuf.cend(); while (std::regex_search(begin, end, dirMatch, regSteamPath)) { - std::string match = dirMatch[1].str(); - hecl::SystemStringConv otherInstallDir(match); - hecl::SystemString otherAppPath = + const std::string match = dirMatch[1].str(); + const hecl::SystemStringConv otherInstallDir(match); + const auto otherAppPath = hecl::SystemString(otherInstallDir.sys_str()) + PATH_SEP + _SYS_STR("steamapps") + PATH_SEP + appPath; - if (!hecl::Stat(otherAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) + + if (!hecl::Stat(otherAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) { return otherAppPath; + } + begin = dirMatch.suffix().first; } diff --git a/hecl/lib/hecl.cpp b/hecl/lib/hecl.cpp index 99ca54c84..cda41de67 100644 --- a/hecl/lib/hecl.cpp +++ b/hecl/lib/hecl.cpp @@ -144,38 +144,38 @@ void ResourceLock::ClearThreadRes() { } bool IsPathPNG(const hecl::ProjectPath& path) { - FILE* fp = hecl::Fopen(path.getAbsolutePath().data(), _SYS_STR("rb")); - if (!fp) - return false; - uint32_t buf = 0; - if (fread(&buf, 1, 4, fp) != 4) { - fclose(fp); + const auto fp = hecl::FopenUnique(path.getAbsolutePath().data(), _SYS_STR("rb")); + if (fp == nullptr) { return false; } - fclose(fp); + + uint32_t buf = 0; + if (std::fread(&buf, 1, sizeof(buf), fp.get()) != sizeof(buf)) { + return false; + } + buf = hecl::SBig(buf); - if (buf == 0x89504e47) - return true; - return false; + return buf == 0x89504e47; } bool IsPathBlend(const hecl::ProjectPath& path) { - auto lastCompExt = path.getLastComponentExt(); - if (lastCompExt.empty() || hecl::StrCmp(lastCompExt.data(), _SYS_STR("blend"))) - return false; - FILE* fp = hecl::Fopen(path.getAbsolutePath().data(), _SYS_STR("rb")); - if (!fp) - return false; - uint32_t buf = 0; - if (fread(&buf, 1, 4, fp) != 4) { - fclose(fp); + const auto lastCompExt = path.getLastComponentExt(); + if (lastCompExt.empty() || hecl::StrCmp(lastCompExt.data(), _SYS_STR("blend"))) { return false; } - fclose(fp); + + const auto fp = hecl::FopenUnique(path.getAbsolutePath().data(), _SYS_STR("rb")); + if (fp == nullptr) { + return false; + } + + uint32_t buf = 0; + if (std::fread(&buf, 1, sizeof(buf), fp.get()) != sizeof(buf)) { + return false; + } + buf = hecl::SLittle(buf); - if (buf == 0x4e454c42 || buf == 0x88b1f) - return true; - return false; + return buf == 0x4e454c42 || buf == 0x88b1f; } bool IsPathYAML(const hecl::ProjectPath& path) {