Merge pull request #11444 from AdmiralCurtiss/riivolution-linux-host-fs

RiivolutionPatcher: Use capitalization of file that exists in the host file system if possible.
This commit is contained in:
Admiral H. Curtiss 2023-02-28 01:17:05 +01:00 committed by GitHub
commit 73aaa01648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 12 deletions

View File

@ -704,4 +704,12 @@ void ToUpper(std::string* str)
{ {
std::transform(str->begin(), str->end(), str->begin(), [](char c) { return Common::ToUpper(c); }); std::transform(str->begin(), str->end(), str->begin(), [](char c) { return Common::ToUpper(c); });
} }
bool CaseInsensitiveEquals(std::string_view a, std::string_view b)
{
if (a.size() != b.size())
return false;
return std::equal(a.begin(), a.end(), b.begin(),
[](char ca, char cb) { return Common::ToLower(ca) == Common::ToLower(cb); });
}
} // namespace Common } // namespace Common

View File

@ -264,4 +264,5 @@ inline char ToUpper(char ch)
} }
void ToLower(std::string* str); void ToLower(std::string* str);
void ToUpper(std::string* str); void ToUpper(std::string* str);
bool CaseInsensitiveEquals(std::string_view a, std::string_view b);
} // namespace Common } // namespace Common

View File

@ -64,7 +64,8 @@ FileDataLoaderHostFS::MakeAbsoluteFromRelative(std::string_view external_relativ
return std::nullopt; return std::nullopt;
#endif #endif
std::string result = external_relative_path.starts_with('/') ? m_sd_root : m_patch_root; const std::string& root = external_relative_path.starts_with('/') ? m_sd_root : m_patch_root;
std::string result = root;
std::string_view work = external_relative_path; std::string_view work = external_relative_path;
// Strip away all leading and trailing path separators. // Strip away all leading and trailing path separators.
@ -116,6 +117,33 @@ FileDataLoaderHostFS::MakeAbsoluteFromRelative(std::string_view external_relativ
// Append path element to result string. // Append path element to result string.
result += '/'; result += '/';
result += element; result += element;
// Riivolution assumes a case-insensitive file system, which means it's possible that an XML
// file references a 'file.bin' but the actual file is named 'File.bin' or 'FILE.BIN'. To
// preserve this behavior, we modify the file path to match any existing file in the file
// system, if one exists.
if (!::File::Exists(result))
{
// Drop path element again so we can search in the directory.
result.erase(result.size() - element.size(), element.size());
// Re-attach an element that actually matches the capitalization in the host filesystem.
auto possible_files = ::File::ScanDirectoryTree(result, false);
bool found = false;
for (auto& f : possible_files.children)
{
if (Common::CaseInsensitiveEquals(element, f.virtualName))
{
result += f.virtualName;
found = true;
break;
}
}
// If there isn't any file that matches just use the given element.
if (!found)
result += element;
}
} }
// If this was the last path element, we're done. // If this was the last path element, we're done.
@ -320,14 +348,6 @@ static void ApplyPatchToFile(const Patch& patch, const File& file_patch,
file_patch.m_fileoffset, file_patch.m_length, file_patch.m_resize); file_patch.m_fileoffset, file_patch.m_length, file_patch.m_resize);
} }
static bool CaseInsensitiveEquals(std::string_view a, std::string_view b)
{
if (a.size() != b.size())
return false;
return std::equal(a.begin(), a.end(), b.begin(),
[](char ca, char cb) { return Common::ToLower(ca) == Common::ToLower(cb); });
}
static FSTBuilderNode* FindFileNodeInFST(std::string_view path, std::vector<FSTBuilderNode>* fst, static FSTBuilderNode* FindFileNodeInFST(std::string_view path, std::vector<FSTBuilderNode>* fst,
bool create_if_not_exists) bool create_if_not_exists)
{ {
@ -335,7 +355,7 @@ static FSTBuilderNode* FindFileNodeInFST(std::string_view path, std::vector<FSTB
const bool is_file = path_separator == std::string_view::npos; const bool is_file = path_separator == std::string_view::npos;
const std::string_view name = is_file ? path : path.substr(0, path_separator); const std::string_view name = is_file ? path : path.substr(0, path_separator);
const auto it = std::find_if(fst->begin(), fst->end(), [&](const FSTBuilderNode& node) { const auto it = std::find_if(fst->begin(), fst->end(), [&](const FSTBuilderNode& node) {
return CaseInsensitiveEquals(node.m_filename, name); return Common::CaseInsensitiveEquals(node.m_filename, name);
}); });
if (it == fst->end()) if (it == fst->end())
@ -377,7 +397,7 @@ static DiscIO::FSTBuilderNode* FindFilenameNodeInFST(std::string_view filename,
if (result) if (result)
return result; return result;
} }
else if (CaseInsensitiveEquals(node.m_filename, filename)) else if (Common::CaseInsensitiveEquals(node.m_filename, filename))
{ {
return &node; return &node;
} }
@ -398,7 +418,7 @@ static void ApplyFilePatchToFST(const Patch& patch, const File& file,
if (node) if (node)
ApplyPatchToFile(patch, file, node); ApplyPatchToFile(patch, file, node);
} }
else if (dol_node && CaseInsensitiveEquals(file.m_disc, "main.dol")) else if (dol_node && Common::CaseInsensitiveEquals(file.m_disc, "main.dol"))
{ {
// Special case: If the filename is "main.dol", we want to patch the main executable. // Special case: If the filename is "main.dol", we want to patch the main executable.
ApplyPatchToFile(patch, file, dol_node); ApplyPatchToFile(patch, file, dol_node);