Use UTF-8-safe filesystem paths end-to-end

This commit is contained in:
patchzyy
2026-08-28 19:01:14 +02:00
parent 4897e7e27d
commit b5e5858e1d
26 changed files with 334 additions and 306 deletions
+20 -29
View File
@@ -74,23 +74,14 @@ std::string RiivoGameId() {
return id;
}
// Every narrow path string in this file is UTF-8: string()/generic_string()
// would use the ANSI codepage on Windows, and the XML-supplied halves the
// resolved paths are built from are UTF-8 already.
std::string RiivoUtf8(const std::u8string& text) {
return std::string(reinterpret_cast<const char*>(text.c_str()), text.size());
}
std::string RiivoPathText(const fs::path& path) {
return RiivoUtf8(path.u8string());
}
// Every narrow path string here is UTF-8, including the ones the XML halves of
// resolved paths are concatenated with.
using RuntimeConfigFile::PathFromUtf8;
using RuntimeConfigFile::PathToUtf8;
std::string RiivoGenericText(const fs::path& path) {
return RiivoUtf8(path.generic_u8string());
}
fs::path RiivoPathFromUtf8(const std::string& text) {
return fs::path(std::u8string(text.begin(), text.end()));
const std::u8string text = path.generic_u8string();
return std::string(text.begin(), text.end());
}
std::string RiivoComparablePath(const fs::path& path) {
@@ -106,7 +97,7 @@ void RiivoAddRoot(std::vector<RuntimeRiivolution::Overlay>& overlays, fs::path r
std::error_code ec;
if (!fs::is_directory(root, ec)) {
RT_LOG(RT_TAG_RIIVOLUTION) << "rejected overlay root (" << (source ? source : "unknown")
<< "): " << RiivoPathText(root) << " is not a reachable directory" << std::endl;
<< "): " << PathToUtf8(root) << " is not a reachable directory" << std::endl;
return;
}
@@ -125,7 +116,7 @@ void RiivoAddRoot(std::vector<RuntimeRiivolution::Overlay>& overlays, fs::path r
}
RT_LOG(RT_TAG_RIIVOLUTION) << "overlay root (" << (source ? source : "unknown")
<< "): " << RiivoPathText(normalized) << std::endl;
<< "): " << PathToUtf8(normalized) << std::endl;
overlays.push_back({std::move(normalized), std::nullopt});
}
@@ -154,7 +145,7 @@ std::vector<RuntimeRiivolution::Overlay> RiivoDiscoverRoots() {
}
for (const auto& root : RecompMod::DvdOverlayRoots()) {
RiivoAddRoot(overlays, fs::path(root), "recomp mod manifest");
RiivoAddRoot(overlays, root, "recomp mod manifest");
}
return overlays;
@@ -175,7 +166,7 @@ std::optional<RiivoXmlSet> RiivoFindXmls(const fs::path& overlayRoot) {
// <sd>/RetroRewind6), so externals resolve against the root's parent.
const std::string& configured = RecompMod::RiivolutionXml();
if (!configured.empty()) {
const fs::path configuredXml = overlayRoot / RiivoPathFromUtf8(configured);
const fs::path configuredXml = overlayRoot / PathFromUtf8(configured);
if (fs::is_regular_file(configuredXml, ec)) {
return RiivoXmlSet{overlayRoot.parent_path(), {configuredXml}};
}
@@ -232,7 +223,7 @@ void RiivoCollectMappings(const RiivolutionContract::Patch& patch, const std::st
++set.skippedExternals;
continue;
}
const fs::path hostFile = RiivoPathFromUtf8(*resolved);
const fs::path hostFile = PathFromUtf8(*resolved);
if (!fs::is_regular_file(hostFile, ec)) {
++set.skippedExternals;
continue;
@@ -248,7 +239,7 @@ void RiivoCollectMappings(const RiivolutionContract::Patch& patch, const std::st
++set.skippedExternals;
continue;
}
const fs::path hostFolder = RiivoPathFromUtf8(*resolved);
const fs::path hostFolder = PathFromUtf8(*resolved);
if (!fs::is_directory(hostFolder, ec)) {
++set.skippedExternals;
continue;
@@ -282,20 +273,20 @@ std::optional<RuntimeRiivolution::PatchSet> RiivoLoadPatchSet(const fs::path& ov
for (const fs::path& xmlFile : xmlSet->xmlFiles) {
const auto text = RiivoReadFile(xmlFile);
if (!text) {
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: cannot read " << RiivoPathText(xmlFile)
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: cannot read " << PathToUtf8(xmlFile)
<< std::endl;
continue;
}
auto disc = RiivolutionContract::ParseString(*text);
if (!disc) {
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: " << RiivoPathText(xmlFile)
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: " << PathToUtf8(xmlFile)
<< " is not a valid Riivolution XML (version 1 wiidisc); ignoring it"
<< std::endl;
continue;
}
if (!disc->IsValidForGame(gameId, std::nullopt, std::nullopt)) {
RT_LOG(RT_TAG_RIIVOLUTION) << RiivoPathText(xmlFile) << ": not valid for " << gameId
RT_LOG(RT_TAG_RIIVOLUTION) << PathToUtf8(xmlFile) << ": not valid for " << gameId
<< ", skipped" << std::endl;
continue;
}
@@ -319,10 +310,10 @@ std::optional<RuntimeRiivolution::PatchSet> RiivoLoadPatchSet(const fs::path& ov
if (const auto resolvedSave = RiivolutionContract::MakeAbsoluteFromRelative(
sdRootGeneric, xmlDirGeneric, savegame->external)) {
state.saveRedirect =
RuntimeRiivolution::SaveRedirect{RiivoPathFromUtf8(*resolvedSave),
RuntimeRiivolution::SaveRedirect{PathFromUtf8(*resolvedSave),
savegame->clone};
RT_LOG(RT_TAG_RIIVOLUTION) << "savegame redirect: "
<< RiivoPathText(state.saveRedirect->hostDirectory)
<< PathToUtf8(state.saveRedirect->hostDirectory)
<< (savegame->clone ? " (clone)" : "") << std::endl;
}
}
@@ -331,11 +322,11 @@ std::optional<RuntimeRiivolution::PatchSet> RiivoLoadPatchSet(const fs::path& ov
// A pack whose XML parses but activates nothing is the most confusing
// failure this layer has: the game boots, plays, and quietly shows
// vanilla content. Always say what happened.
RT_LOG(RT_TAG_RIIVOLUTION) << RiivoPathText(xmlFile) << ": " << activePatches.size()
RT_LOG(RT_TAG_RIIVOLUTION) << PathToUtf8(xmlFile) << ": " << activePatches.size()
<< " active patch(es), " << (set.mappings.size() - before) << " mapping(s)"
<< std::endl;
if (activePatches.empty()) {
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: " << RiivoPathText(xmlFile)
RT_LOG(RT_TAG_RIIVOLUTION) << "WARNING: " << PathToUtf8(xmlFile)
<< " has no enabled options for " << gameId
<< "; check the riivolution option selections (recomp.yml) or "
<< sdRootGeneric << "/riivolution/config/" << gameId.substr(0, 4) << ".xml"
@@ -344,7 +335,7 @@ std::optional<RuntimeRiivolution::PatchSet> RiivoLoadPatchSet(const fs::path& ov
}
if (set.skippedExternals != 0) {
RT_LOG(RT_TAG_RIIVOLUTION) << RiivoPathText(overlayRoot) << ": skipped "
RT_LOG(RT_TAG_RIIVOLUTION) << PathToUtf8(overlayRoot) << ": skipped "
<< set.skippedExternals << " mapping(s) whose external path does not exist"
<< std::endl;
}