summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2019-05-29 09:16:03 +0200
committerJosJuice <josjuice@gmail.com>2019-05-29 14:53:39 +0200
commitf25367a7a274c351da568e1ce1e09b95ae0f1a60 (patch)
tree768e9c35186ef3581b2899419c02374d9f3593ae /Source/Core
parentd4b069f4580b1cde512861cb3bfd3930c3a1e909 (diff)
DiscIO: Avoid copies when comparing FileSystemGCWii file names
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/DiscIO/FileSystemGCWii.cpp43
-rw-r--r--Source/Core/DiscIO/FileSystemGCWii.h1
-rw-r--r--Source/Core/DiscIO/Filesystem.h1
3 files changed, 41 insertions, 4 deletions
diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp
index 3954472465..c4e8d4a48e 100644
--- a/Source/Core/DiscIO/FileSystemGCWii.cpp
+++ b/Source/Core/DiscIO/FileSystemGCWii.cpp
@@ -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);
diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h
index 26ff5d5444..9f7f8f2e6a 100644
--- a/Source/Core/DiscIO/FileSystemGCWii.h
+++ b/Source/Core/DiscIO/FileSystemGCWii.h
@@ -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;
diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h
index b46da7c29b..35b01e17b1 100644
--- a/Source/Core/DiscIO/Filesystem.h
+++ b/Source/Core/DiscIO/Filesystem.h
@@ -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.