From e9b24642541f0203d369fedd74900cbbaddeac1e Mon Sep 17 00:00:00 2001 From: Gliniak Date: Wed, 6 Aug 2025 22:22:01 +0200 Subject: [PATCH] [XAM] Fixed issue with Content not being close due to upper-lower characters difference. - Added string_key_insensitive --- src/xenia/base/string_key.h | 43 +++++++++++++++++++++++++ src/xenia/kernel/xam/content_manager.cc | 23 +++++++------ src/xenia/kernel/xam/content_manager.h | 2 +- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/xenia/base/string_key.h b/src/xenia/base/string_key.h index 9b4576ded..a1adabd55 100644 --- a/src/xenia/base/string_key.h +++ b/src/xenia/base/string_key.h @@ -10,6 +10,7 @@ #ifndef XENIA_BASE_STRING_KEY_H_ #define XENIA_BASE_STRING_KEY_H_ +#include #include #include @@ -58,6 +59,41 @@ struct string_key : internal::string_key_base { }; }; +struct string_key_insensitive : internal::string_key_base { + public: + explicit string_key_insensitive(const std::string_view value) + : string_key_base(value) {} + explicit string_key_insensitive(std::string value) : string_key_base(value) {} + + static string_key_insensitive create(const std::string_view value) { + return string_key_insensitive(std::string(value)); + } + + static string_key_insensitive create(std::string value) { + return string_key_insensitive(value); + } + + bool operator==(const string_key_insensitive& other) const { + return other.view().size() == view().size() && + std::ranges::equal( + other.view(), view(), {}, + [](char ch) { + return std::tolower(static_cast(ch)); + }, + [](char ch) { + return std::tolower(static_cast(ch)); + }); + } + + size_t hash() const { return utf8::hash_fnv1a(view()); } + + struct Hash { + size_t operator()(const string_key_insensitive& t) const { + return t.hash(); + } + }; +}; + struct string_key_case : internal::string_key_base { public: explicit string_key_case(const std::string_view value) @@ -91,6 +127,13 @@ struct hash { std::size_t operator()(const xe::string_key& t) const { return t.hash(); } }; +template <> +struct hash { + std::size_t operator()(const xe::string_key_insensitive& t) const { + return t.hash(); + } +}; + template <> struct hash { std::size_t operator()(const xe::string_key_case& t) const { diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index a7dfed38f..c29879fbb 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -366,7 +366,7 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data) { auto global_lock = global_critical_region_.Acquire(); - if (open_packages_.count(string_key(root_name))) { + if (open_packages_.count(string_key_insensitive(root_name))) { // Already content open with this root name. return X_ERROR_ALREADY_EXISTS; } @@ -384,7 +384,8 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name, auto package = ResolvePackage(root_name, xuid, data); assert_not_null(package); - open_packages_.insert({string_key::create(root_name), package.release()}); + open_packages_.insert( + {string_key_insensitive::create(root_name), package.release()}); return X_ERROR_SUCCESS; } @@ -396,7 +397,7 @@ X_RESULT ContentManager::OpenContent(const std::string_view root_name, const uint32_t disc_number) { auto global_lock = global_critical_region_.Acquire(); - if (open_packages_.count(string_key(root_name))) { + if (open_packages_.count(string_key_insensitive(root_name))) { // Already content open with this root name. return X_ERROR_ALREADY_EXISTS; } @@ -425,7 +426,8 @@ X_RESULT ContentManager::OpenContent(const std::string_view root_name, } } - open_packages_.insert({string_key::create(root_name), package.release()}); + open_packages_.insert( + {string_key_insensitive::create(root_name), package.release()}); return X_ERROR_SUCCESS; } @@ -433,7 +435,9 @@ X_RESULT ContentManager::OpenContent(const std::string_view root_name, X_RESULT ContentManager::CloseContent(const std::string_view root_name) { auto global_lock = global_critical_region_.Acquire(); - auto it = open_packages_.find(string_key(root_name)); + // 415607D6 - Uses XamContentCreate with name "save", but XamContentClose with + // "SAVE". + auto it = open_packages_.find(string_key_insensitive(root_name)); if (it == open_packages_.end()) { return X_ERROR_FILE_NOT_FOUND; } @@ -509,10 +513,11 @@ std::filesystem::path ContentManager::ResolveGameUserContentPath( } bool ContentManager::IsContentOpen(const XCONTENT_AGGREGATE_DATA& data) const { - return std::any_of(open_packages_.cbegin(), open_packages_.cend(), - [data](std::pair content) { - return data == content.second->GetPackageContentData(); - }); + return std::any_of( + open_packages_.cbegin(), open_packages_.cend(), + [data](std::pair content) { + return data == content.second->GetPackageContentData(); + }); } void ContentManager::CloseOpenedFilesFromContent( diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 7e9d71ea3..f617d8e52 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -211,7 +211,7 @@ class ContentManager { // TODO(benvanik): remove use of global lock, it's bad here! xe::global_critical_region global_critical_region_; - std::unordered_map open_packages_; + std::unordered_map open_packages_; }; } // namespace xam