3#include "MemoryBufferBackedFileContents.h"
7#include "../Logging/Logger.h"
8#include "../StringUtilities.h"
9#include "../StringViewFormat.h"
22 void logError(HRESULT hr,
const char* prefix =
"") {
23 thread_local static char err[2000];
26 if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
29 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
34 for(
size_t i = 0; i < std::size(err); i++) {
35 if(err[i] ==
'\r' || err[i] ==
'\n') err[i] =
' ';
37 LOG_ERROR(envLogger,
"%s: %s", prefix, err);
40 LOG_ERROR(envLogger,
"%s: Failed to retrieve error message for error code %08x.", prefix, hr);
45void Cogs::IO::readFileAsync(
const std::string & fileName,
const FileContents::Callback& callback, uint64_t offset, uint64_t size, FileContentsHints hints)
47 if (size == 0 && offset != 0) {
48 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: Illegal range, trying to read zero bytes from offset (%" PRIu64
")", offset);
49 callback(std::unique_ptr<FileContents>());
53 std::ifstream file(fileName, std::ios::binary | std::ios::in | std::ios::ate);
54 if (!file.is_open()) {
55 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: Failed to open file %s", fileName.c_str());
56 callback(std::unique_ptr<FileContents>());
60 file.seekg(0, std::ios::end);
62 std::streampos fileSize = file.tellg();
64 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: tellg returned an error.");
67 uint64_t readOffset = 0;
68 uint64_t readSize =
static_cast<uint64_t
>(fileSize);
70 if (readSize < offset) {
71 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: offset (%" PRIu64
") is greater than filesize (%" PRIu64
")", offset,
static_cast<uint64_t
>(fileSize));
75 readSize -= readOffset;
76 if (readSize < size) {
77 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: offset (%" PRIu64
") + size(%" PRIu64
") is greater than filesize (%" PRIu64
")", offset, size,
static_cast<uint64_t
>(fileSize));
83 if (
static_cast<uint64_t
>(std::numeric_limits<size_t>::max()) <
static_cast<uint64_t
>(readSize)) {
84 LOG_ERROR(envLogger,
"Cogs::IO::readFileAsync: offset (%" PRIu64
") + size(%" PRIu64
") is greater than max size_t (>4GB read on 32bit?)", offset, size);
88 auto contents = std::make_unique<MemoryBufferBackedFileContents>(readSize, fileName, hints);
89 file.seekg(
static_cast<std::streamoff
>(readOffset), std::ios::beg);
90 file.read((
char*)contents->bytes.data(), contents->bytes.size());
92 callback(std::move(contents));
98 callback(std::unique_ptr<FileContents>());
102Cogs::FileContents::Ptr Cogs::IO::readFileSync(
const std::string & fileName, uint64_t offset, uint64_t size, FileContentsHints hints)
104 if (size == 0 && offset != 0) {
105 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: Illegal range, trying to read zero bytes from offset (%" PRIu64
")", offset);
106 return std::unique_ptr<FileContents>();
109 std::ifstream file(fileName, std::ios::binary | std::ios::in | std::ios::ate);
110 if (!file.is_open()) {
111 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: Failed to open file %s", fileName.c_str());
112 return std::unique_ptr<FileContents>();
115 file.seekg(0, std::ios::end);
116 std::streampos fileSize = file.tellg();
118 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: tellg returned an error.");
119 return std::unique_ptr<FileContents>();
121 uint64_t readOffset = 0;
122 uint64_t readSize =
static_cast<uint64_t
>(fileSize);
124 if (readSize < offset) {
125 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: offset (%" PRIu64
") is greater than filesize (%" PRIu64
")", offset,
static_cast<uint64_t
>(fileSize));
126 return std::unique_ptr<FileContents>();
129 readSize -= readOffset;
130 if (readSize < size) {
131 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: offset (%" PRIu64
") + size(%" PRIu64
") is greater than filesize (%" PRIu64
")", offset, size,
static_cast<uint64_t
>(fileSize));
132 return std::unique_ptr<FileContents>();
137 if (
static_cast<uint64_t
>(std::numeric_limits<size_t>::max()) < readSize) {
138 LOG_ERROR(envLogger,
"Cogs::IO::readFileSync: offset (%" PRIu64
") + size(%" PRIu64
") is greater than max size_t (>4GB read on 32bit?)", offset, size);
139 return std::unique_ptr<FileContents>();
142 auto contents = std::make_unique<MemoryBufferBackedFileContents>(readSize, fileName, hints);
143 file.seekg(
static_cast<std::streamoff
>(readOffset), std::ios::beg);
144 file.read((
char*)contents->bytes.data(), contents->bytes.size());
149bool Cogs::IO::readTextFile(std::string& content,
const std::string & fileName) {
150 std::ifstream in(fileName);
153 content = std::string((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
156 LOG_ERROR(envLogger,
"Cogs::IO::readTextFile(%s): Failed to open file.", fileName.c_str());
160void Cogs::IO::autoExpandEnvironmentVariables(std::string & text) {
161 static std::regex env(
"%([0-9A-Za-z\\/]*)%");
164 while (std::regex_search(text, match, env)) {
165 const char * s = std::getenv(match[1].str().c_str());
166 const std::string environmentVariable(s ==
nullptr ?
"" : s);
168 text.replace(match[0].first, match[0].second, environmentVariable);
172std::string Cogs::IO::expandEnvironmentVariables(
const std::string & input) {
173 std::string text = input;
174 autoExpandEnvironmentVariables(text);
178std::string Cogs::IO::expandEnvironmentVariable(
const std::string & name) {
179 const std::string decoratedName =
"%" + name +
"%";
182 ExpandEnvironmentStringsA(decoratedName.c_str(), buffer, 2048);
186char Cogs::IO::pathSeparator() {
190bool Cogs::IO::exists(std::string_view path) {
191 return fs::exists(path);
194bool Cogs::IO::isFile(std::string_view path) {
195 return fs::is_regular_file(path);
198bool Cogs::IO::isDirectory(std::string_view path) {
199 return fs::is_directory(path);
202std::string Cogs::IO::absolute(std::string_view path) {
203 return fs::absolute(fs::path(path)).string();
206std::string Cogs::IO::canonical(std::string_view path) {
208 return fs::canonical(fs::path(path), ec).string();
211bool Cogs::IO::remove(std::string_view path) {
212 return fs::remove(path);
215std::string Cogs::IO::combine(std::string_view path, std::string_view extensionPath) {
216 return (fs::path(path).append(extensionPath)).string();
219std::string Cogs::IO::expandPath(std::string_view fileName) {
220 return expandEnvironmentVariables(fs::path(fileName).
string());
223std::string Cogs::IO::parentPath(std::string_view str) {
224 return fs::path(str.begin(), str.end()).parent_path().string();
227std::string Cogs::IO::relativePath(std::string_view str) {
228 return fs::path(str).relative_path().string();
231std::string Cogs::IO::relativePath(std::string_view str, std::string_view base) {
232 auto dir = fs::path(base);
233 auto p = fs::path(str);
235 dir = fs::absolute(dir);
238 auto beginA = p.begin();
239 auto beginB = dir.begin();
241 while (beginA != p.end() && beginB != dir.end()) {
242 if (*beginA != *beginB)
break;
249 for (; beginB != dir.end(); ++beginB) {
253 for (; beginA != p.end(); ++beginA) {
257 return relative.string();
260std::string Cogs::IO::getPathToExe() {
261 char path[_MAX_PATH];
263 if (GetModuleFileNameA(
nullptr, path,
sizeof(path))) {
266 logError(GetLastError(),
"Cogs::UI::getPathToExe()");
270std::string Cogs::IO::getPathRelativeToDll(
const std::string& fileName,
void* handle) {
271 wchar_t dllPath[MAX_PATH];
272 wchar_t fullPath[MAX_PATH];
273 HMODULE hm =
nullptr;
275 if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
276 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
277 static_cast<LPCSTR
>(handle),
279 std::string prefix =
"Cogs::UI::getPathRelativeToDll(" + fileName +
")";
280 logError(GetLastError(), prefix.c_str());
284 GetModuleFileNameW(hm, dllPath, MAX_PATH);
285 PathRemoveFileSpecW(dllPath);
286 PathCombineW(fullPath, dllPath, Cogs::widen(fileName).c_str());
288 return narrow(fullPath);
291std::string Cogs::IO::getTempPath() {
294 HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
nullptr, &str);
297 char converted[_MAX_PATH];
299 if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, converted,
sizeof(converted),
nullptr,
nullptr)) {
300 logError(GetLastError(),
"Cogs::IO::getTempPath()");
304 result +=
"\\Temp\\";
308 logError(hr,
"Cogs::IO::getTempPath()");
313std::string Cogs::IO::getAppDataPath() {
314 return getOrgLocalDataPath() +
"\\Cogs\\";
317std::string Cogs::IO::getOrgLocalDataPath() {
321 if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT,
nullptr, &ptr) == S_OK) {
322 char converted[_MAX_PATH];
324 if (!WideCharToMultiByte(CP_UTF8, 0, ptr, -1, converted,
sizeof(converted),
nullptr,
nullptr)) {
325 logError(GetLastError(),
"Cogs::IO::getAppDataPath()");
329 path +=
"\\Kongsberg Digital\\";
335bool Cogs::IO::isAbsolute(std::string_view str) {
336 return !str.empty() && ((str.size() >= 1 && (str[0] ==
'/' || str[0] ==
'\\')) || (str.size() >= 2 && str[1] ==
':'));
339bool Cogs::IO::isRelative(std::string_view str) {
340 return !isAbsolute(str);
343std::string Cogs::IO::fileName(std::string_view str) {
344 return fs::path(str).filename().string();
347std::string Cogs::IO::extension(std::string_view str) {
348 return fs::path(str).extension().string();
351std::string Cogs::IO::stem(std::string_view str) {
352 return fs::path(str.begin(), str.end()).stem().string();
355void Cogs::IO::replaceAll(std::string& str, std::string_view from, std::string_view to) {
356 if (from.empty())
return;
359 while ((startPos = str.find(from, startPos)) != std::string::npos) {
360 str.replace(startPos, from.length(), to);
361 startPos += to.length();
365Cogs::IO::FileTimeType Cogs::IO::getFileTime(std::string_view path) {
367 return fs::last_write_time(fs::path(path.begin(), path.end()), ec);
370uint64_t Cogs::IO::getFileSize(std::string_view path) {
371 WIN32_FILE_ATTRIBUTE_DATA fad;
372 if (!GetFileAttributesExW(Cogs::widen(path).c_str(), GetFileExInfoStandard, &fad)) {
373 std::string prefix = Cogs::stringConcatenate({
"Cogs::IO::getFileSize(", path,
")" });
374 logError(GetLastError(), prefix.c_str());
378 size.HighPart = fad.nFileSizeHigh;
379 size.LowPart = fad.nFileSizeLow;
380 return static_cast<uint64_t
>(size.QuadPart);
383bool Cogs::IO::writeBinaryFile(
const std::string& fileName,
const void* content,
size_t contentSize) {
384 if (!createDirectories(fileName)) {
388 HANDLE hFile = CreateFileW(Cogs::widen(fs::path(fileName).
string()).c_str(),
393 FILE_ATTRIBUTE_NORMAL,
396 if (hFile == INVALID_HANDLE_VALUE) {
397 std::string prefix =
"Cogs::IO::writeBinaryFile(" + fileName +
")";
398 logError(GetLastError(), prefix.c_str());
402 const uint8_t *src =
static_cast<const uint8_t*
>(content);
403 size_t remaining = contentSize;
405 DWORD write_size =
static_cast<DWORD
>(std::min(remaining,
static_cast<size_t>(std::numeric_limits<DWORD>::max())));
408 if (!WriteFile(hFile, src, write_size, &written,
nullptr)) {
409 std::string prefix =
"Cogs::IO::writeBinaryFile(" + fileName +
")";
410 logError(GetLastError(), prefix.c_str());
415 remaining -= written;
421bool Cogs::IO::createDirectories(std::string_view fileName) {
422 fs::path path(fileName);
424 if (path.has_parent_path() && !fs::exists(path.parent_path()) && !fs::create_directories(path.parent_path())) {
425 LOG_ERROR(envLogger,
"Cogs::IO::createDirectories(%.*s) failed.", StringViewFormat(fileName));
431Cogs::IO::RecursiveDirIterator Cogs::IO::directoryIterator(std::string_view directory) {
432 return RecursiveDirIterator(directory);
435std::string Cogs::IO::getPath(
const DirectoryEntry& entry) {
436 return entry.path().string();
439Cogs::FileHandle::Ptr Cogs::IO::openFile(std::string_view path, FileHandle::OpenMode openMode, FileHandle::AccessMode accessMode) {
440 return FileHandle::open(path, openMode, accessMode);
443void Cogs::IO::closeFile(
const FileHandle::Ptr& handle) {
449size_t Cogs::IO::fileSize(
const FileHandle::Ptr& handle) {
450 return handle ? handle->getSize() : 0;
453void* Cogs::IO::mapFile(
const FileHandle::Ptr& handle, FileHandle::ProtectionMode protectionMode,
size_t offset,
size_t size) {
454 return handle ? handle->map(protectionMode, offset, size) :
nullptr;
457void Cogs::IO::unmapFile(
const FileHandle::Ptr& handle) {
463bool Cogs::IO::copyFile(std::string_view src, std::string_view dest) {
464 BOOL bFailIfExists = FALSE;
466 if (!CopyFileW(Cogs::widen(src).c_str(), Cogs::widen(dest).c_str(), bFailIfExists)) {
467 std::string prefix = Cogs::stringConcatenate({
"Cogs::IO::copyFile(", src,
", ", dest,
")" });
468 logError(GetLastError(), prefix.c_str());
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept