From df7dfde1d61b1da21550c8974a76bd2cc5d7031e Mon Sep 17 00:00:00 2001 From: shweet Date: Thu, 18 Dec 2025 00:36:19 -0500 Subject: [PATCH] this is insane but sure --- src/log.cpp | 45 +++++++++++++++++++++++++++++++++- src/render.cpp | 65 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 88bcd80..f289912 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,5 +1,7 @@ #include "log.h" +#include +#include #include #include "sdl.h" @@ -9,6 +11,37 @@ using namespace anm2ed::util; namespace anm2ed { + namespace + { + bool ensure_utf8_bom(const std::filesystem::path& path) + { + std::error_code ec{}; + if (!std::filesystem::exists(path, ec)) return false; + + std::ifstream existing(path, std::ios::binary); + if (!existing) return false; + + char bom[3]{}; + existing.read(bom, sizeof(bom)); + auto bytesRead = existing.gcount(); + auto hasBom = bytesRead == sizeof(bom) && bom[0] == '\xEF' && bom[1] == '\xBB' && bom[2] == '\xBF'; + if (hasBom) return true; + + existing.clear(); + existing.seekg(0, std::ios::beg); + std::string contents((std::istreambuf_iterator(existing)), std::istreambuf_iterator()); + existing.close(); + + std::ofstream output(path, std::ios::binary | std::ios::trunc); + if (!output) return false; + output << "\xEF\xBB\xBF"; + output.write(contents.data(), static_cast(contents.size())); + output.close(); + + return true; + } + } + void Logger::write_raw(const std::string& message) { std::println("{}", message); @@ -26,7 +59,17 @@ namespace anm2ed void Logger::error(const std::string& message) { write(ERROR, message); } void Logger::fatal(const std::string& message) { write(FATAL, message); } void Logger::command(const std::string& message) { write(COMMAND, message); } - void Logger::open(const std::filesystem::path& path) { file.open(path, std::ios::out | std::ios::app); } + void Logger::open(const std::filesystem::path& path) + { + std::error_code ec{}; + auto exists = std::filesystem::exists(path, ec); + if (exists) ensure_utf8_bom(path); + + file.open(path, std::ios::out | std::ios::app); + if (!file.is_open()) return; + + if (!exists) file << "\xEF\xBB\xBF"; + } std::filesystem::path Logger::path() { return sdl::preferences_directory_get() / "log.txt"; } diff --git a/src/render.cpp b/src/render.cpp index 9912dc5..77fd96e 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -1,11 +1,17 @@ #include "render.h" +#include +#include #include #include #include #include #include +#if _WIN32 + #include +#endif + #include "log.h" #include "process_.h" #include "sdl.h" @@ -109,28 +115,55 @@ namespace anm2ed command += " 2>&1"; #if _WIN32 - auto escape_double_quotes_for_cmd = [](const std::string& input) + auto logCommand = + std::string("& ") + command + " | Tee-Object -FilePath " + string::quote(loggerPathString) + " -Append"; + logger.command(logCommand); + + auto utf8_to_wstring = [](const std::string& value) { - std::string escaped{}; - escaped.reserve(input.size() * 2); - for (char character : input) - { - if (character == '"') - escaped += "\"\""; - else - escaped += character; - } - return escaped; + if (value.empty()) return std::wstring{}; + auto length = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0); + if (length <= 0) return std::wstring{}; + std::wstring wide(static_cast(length - 1), L'\0'); + MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, wide.data(), length); + return wide; }; - auto powershellScript = - std::string("& { & ") + command + " | Tee-Object -FilePath " + string::quote(loggerPathString) + " -Append }"; - command = "powershell -Command \"" + escape_double_quotes_for_cmd(powershellScript) + "\""; + auto base64_encode_utf16 = [](const std::wstring& input) + { + const auto* bytes = reinterpret_cast(input.c_str()); + auto length = input.size() * sizeof(wchar_t); + static constexpr char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string encoded{}; + encoded.reserve(((length + 2) / 3) * 4); + + for (std::size_t i = 0; i < length;) + { + auto remaining = length - i; + auto octetA = bytes[i++]; + auto octetB = remaining > 1 ? bytes[i++] : 0; + auto octetC = remaining > 2 ? bytes[i++] : 0; + + auto triple = (static_cast(octetA) << 16) | (static_cast(octetB) << 8) | + static_cast(octetC); + + encoded.push_back(alphabet[(triple >> 18) & 0x3F]); + encoded.push_back(alphabet[(triple >> 12) & 0x3F]); + encoded.push_back(remaining > 1 ? alphabet[(triple >> 6) & 0x3F] : '='); + encoded.push_back(remaining > 2 ? alphabet[triple & 0x3F] : '='); + } + + return encoded; + }; + + auto script = std::wstring(L"& { ") + utf8_to_wstring(logCommand) + L" }"; + auto encodedCommand = base64_encode_utf16(script); + command = "powershell -NoProfile -EncodedCommand " + string::quote(encodedCommand); #else command += " | tee -a " + string::quote(loggerPathString); -#endif - logger.command(command); +#endif Process process(command.c_str(), "w");