mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 13:57:05 +02:00
Write paths as UTF-8, not the ANSI code page
A player whose game sat under a folder named U+200E (left-to-right mark, an invisible directory on their Desktop) could not launch: the status tile read "No mapping for the Unicode character exists in the target multi-byte code page". Nothing was wrong with their mods, and the merge in their log had just succeeded. fs::path::string() converts to the process's ANSI code page and throws exactly that when a character has no mapping there. initfs.cpp called it on the normal path, not in an error handler, so exporting loose files threw on every launch. Their log stops right where that export begins. Cyrillic, CJK or accented characters in an install path do the same on a code page that cannot hold them. - path_utf8() in Engine/Core/Platform/path_text.h: UTF-8 represents every path Windows allows, so it never throws. - Every path that becomes a narrow string uses it -- 35 of them across 13 files. Most are log lines and error messages, which were a second trap: reporting a problem with a path threw this instead of the problem. - The ~45 .string() calls on Json values are untouched; those are not paths. Nothing changes for an ASCII path, where both encodings agree, and a path that is not ASCII had no working behaviour to preserve. Reported with a log by a player on Discord. Moving the game to an ordinary folder works around it on an unpatched build.
This commit is contained in:
1 parent
201571c225
commit
1004e012bf
14 files changed
+81
-43
No files matched your search
@@ -2,6 +2,7 @@
|
||||
#include "Engine/Game/Build/supported_build.h"
|
||||
|
||||
#include "Engine/Core/Json/json.h"
|
||||
#include "Engine/Core/Platform/path_text.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -35,15 +36,15 @@ std::string lower(std::string_view text) {
|
||||
|
||||
std::vector<unsigned char> read_file(const fs::path& path, std::size_t maximum) {
|
||||
std::ifstream input(path, std::ios::binary | std::ios::ate);
|
||||
if (!input) throw std::runtime_error("Cannot open " + path.string());
|
||||
if (!input) throw std::runtime_error("Cannot open " + path_utf8(path));
|
||||
const auto length = input.tellg();
|
||||
if (length < 0 || static_cast<std::uint64_t>(length) > maximum)
|
||||
throw std::runtime_error("File exceeds size limit: " + path.string());
|
||||
throw std::runtime_error("File exceeds size limit: " + path_utf8(path));
|
||||
std::vector<unsigned char> bytes(static_cast<std::size_t>(length));
|
||||
input.seekg(0);
|
||||
if (!bytes.empty() && !input.read(reinterpret_cast<char*>(bytes.data()),
|
||||
static_cast<std::streamsize>(bytes.size())))
|
||||
throw std::runtime_error("Cannot read " + path.string());
|
||||
throw std::runtime_error("Cannot read " + path_utf8(path));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -420,12 +421,12 @@ void save_mod_order(const std::filesystem::path& mods_root, const std::vector<Mo
|
||||
{
|
||||
std::ofstream output(temporary, std::ios::binary | std::ios::trunc);
|
||||
if (!output || !output.write(text.data(), static_cast<std::streamsize>(text.size())) || !output.flush())
|
||||
throw std::runtime_error("Cannot write " + temporary.string());
|
||||
throw std::runtime_error("Cannot write " + path_utf8(temporary));
|
||||
}
|
||||
if (!MoveFileExW(temporary.c_str(), target.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
|
||||
std::error_code ignored;
|
||||
fs::remove(temporary, ignored);
|
||||
throw std::runtime_error("Cannot replace " + target.string() + " (Windows error " +
|
||||
throw std::runtime_error("Cannot replace " + path_utf8(target) + " (Windows error " +
|
||||
std::to_string(GetLastError()) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user