DiscIO: Avoid copies when comparing FileSystemGCWii file names
This commit is contained in:
parent
d4b069f458
commit
f25367a7a2
|
@ -6,6 +6,7 @@
|
|||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -119,6 +120,43 @@ std::string FileInfoGCWii::GetName() const
|
|||
return SHIFTJISToUTF8(reinterpret_cast<const char*>(m_fst + GetNameOffset()));
|
||||
}
|
||||
|
||||
bool FileInfoGCWii::NameCaseInsensitiveEquals(std::string_view other) const
|
||||
{
|
||||
// For speed, this function avoids allocating new strings, except when we are comparing
|
||||
// non-ASCII characters with non-ASCII characters, which is a rare case.
|
||||
|
||||
const char* this_ptr = reinterpret_cast<const char*>(m_fst + GetNameOffset());
|
||||
const char* other_ptr = other.data();
|
||||
|
||||
for (size_t i = 0; i < other.size(); ++i, ++this_ptr, ++other_ptr)
|
||||
{
|
||||
if (*this_ptr == '\0')
|
||||
{
|
||||
// A null byte in this is always a terminator and a null byte in other is never a terminator,
|
||||
// so if we reach this case, this is shorter than other
|
||||
return false;
|
||||
}
|
||||
else if (static_cast<unsigned char>(*this_ptr) >= 0x80 &&
|
||||
static_cast<unsigned char>(*other_ptr) >= 0x80)
|
||||
{
|
||||
// other is in UTF-8 and this is in Shift-JIS, so we convert so that we can compare correctly
|
||||
const std::string this_utf8 = SHIFTJISToUTF8(this_ptr);
|
||||
return std::equal(this_utf8.cbegin(), this_utf8.cend(), other.cbegin() + i, other.cend(),
|
||||
[](char a, char b) {
|
||||
return std::tolower(a, std::locale::classic()) ==
|
||||
std::tolower(b, std::locale::classic());
|
||||
});
|
||||
}
|
||||
else if (std::tolower(*this_ptr, std::locale::classic()) !=
|
||||
std::tolower(*other_ptr, std::locale::classic()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return *this_ptr == '\0'; // If we're not at a null byte, this is longer than other
|
||||
}
|
||||
|
||||
std::string FileInfoGCWii::GetPath() const
|
||||
{
|
||||
// The root entry doesn't have a name
|
||||
|
@ -283,11 +321,8 @@ std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(std::string_view path,
|
|||
|
||||
for (const FileInfo& child : file_info)
|
||||
{
|
||||
const std::string child_name = child.GetName();
|
||||
|
||||
// We need case insensitive comparison since some games have OPENING.BNR instead of opening.bnr
|
||||
if (child_name.size() == name.size() &&
|
||||
!strncasecmp(child_name.data(), name.data(), name.size()))
|
||||
if (child.NameCaseInsensitiveEquals(name))
|
||||
{
|
||||
// A match is found. The rest of the path is passed on to finish the search.
|
||||
std::unique_ptr<FileInfo> result = FindFileInfo(rest_of_path, child);
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
bool IsDirectory() const override;
|
||||
u32 GetTotalChildren() const override;
|
||||
std::string GetName() const override;
|
||||
bool NameCaseInsensitiveEquals(std::string_view other) const override;
|
||||
std::string GetPath() const override;
|
||||
|
||||
bool IsValid(u64 fst_size, const FileInfoGCWii& parent_directory) const;
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
// Not guaranteed to return a meaningful value for files.
|
||||
virtual u32 GetTotalChildren() const = 0;
|
||||
virtual std::string GetName() const = 0;
|
||||
virtual bool NameCaseInsensitiveEquals(std::string_view other) const = 0;
|
||||
// GetPath will find the parents of the current object and call GetName on them,
|
||||
// so it's slower than other functions. If you're traversing through folders
|
||||
// to get a file and its path, building the path while traversing is faster.
|
||||
|
|
Loading…
Reference in New Issue