From fc16e3dc4075bb09962236181acc76f26ee23865 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Fri, 4 Feb 2022 12:15:47 +0100 Subject: [PATCH] Support for patch types: - float - double - string - u16string - byte_array Plus some smaller changes --- src/xenia/base/string_util.h | 26 ++++++++++ src/xenia/kernel/user_module.cc | 2 +- src/xenia/patcher/patch_db.cc | 91 ++++++++++++++++++++------------- src/xenia/patcher/patch_db.h | 89 +++++++++++++++++++++++++------- src/xenia/patcher/patcher.cc | 27 +++------- 5 files changed, 161 insertions(+), 74 deletions(-) diff --git a/src/xenia/base/string_util.h b/src/xenia/base/string_util.h index 09575ff56..4cbbcc6b3 100644 --- a/src/xenia/base/string_util.h +++ b/src/xenia/base/string_util.h @@ -111,6 +111,32 @@ inline size_t copy_and_swap_maybe_truncating(char16_t* dest, return chars_copied; } +inline bool hex_string_to_array(std::vector& output_array, + const std::string_view value) { + output_array.reserve((value.size() + 1) / 2); + + size_t remaining_length = value.size(); + while (remaining_length > 0) { + uint8_t chars_to_read = remaining_length > 1 ? 2 : 1; + const char* substring_pointer = + value.data() + value.size() - remaining_length; + + uint8_t string_value = 0; + std::from_chars_result result = std::from_chars( + substring_pointer, substring_pointer + chars_to_read, string_value, 16); + + if (result.ec != std::errc() || + result.ptr != substring_pointer + chars_to_read) { + output_array.clear(); + return false; + } + + output_array.push_back(string_value); + remaining_length -= chars_to_read; + } + return true; +} + inline std::string to_hex_string(uint32_t value) { return fmt::format("{:08X}", value); } diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 407922833..e9ed11e2b 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -141,7 +141,7 @@ X_STATUS UserModule::LoadFromFile(const std::string_view path) { } } - XELOGI("Module hash: {:16X} for {}", hash_, name_); + XELOGI("Module hash: {:016X} for {}", hash_, name_); return LoadXexContinue(); } diff --git a/src/xenia/patcher/patch_db.cc b/src/xenia/patcher/patch_db.cc index 9135e254c..ac3c19b0b 100644 --- a/src/xenia/patcher/patch_db.cc +++ b/src/xenia/patcher/patch_db.cc @@ -36,7 +36,7 @@ void PatchDB::LoadPatches() { const std::vector& patch_files = filesystem::ListFiles(patches_directory); const std::regex file_name_regex_match = - std::regex("^[A-Fa-f0-9]{8}.*\\.patch$"); + std::regex("^[A-Fa-f0-9]{8}.*\\.patch\\.toml$"); for (const xe::filesystem::FileInfo& patch_file : patch_files) { // Skip files that doesn't have only title_id as name and .patch as @@ -93,11 +93,13 @@ PatchFileEntry PatchDB::ReadPatchFile(const std::filesystem::path& file_path) { patch.is_enabled = is_enabled; // Iterate through all available data sizes - for (const auto& type : patch_data_types_size) { - auto patch_entries = ReadPatchData(type.first, patch_table_entry); + for (const auto& patch_data_type : patch_data_types_size) { + bool success = + ReadPatchData(patch.patch_data, patch_data_type, patch_table_entry); - for (const PatchDataEntry& patch_entry : *patch_entries) { - patch.patch_data.push_back(patch_entry); + if (!success) { + XELOGE("PatchDB: Cannot read patch {}", patch_name); + break; } } patchFile.patch_info.push_back(patch); @@ -105,32 +107,64 @@ PatchFileEntry PatchDB::ReadPatchFile(const std::filesystem::path& file_path) { return patchFile; } -std::vector* PatchDB::ReadPatchData( - const std::string size_type, +bool PatchDB::ReadPatchData( + std::vector& patch_data, + const std::pair data_type, const std::shared_ptr& patch_table) { - std::vector* patch_data = new std::vector(); - auto patch_data_tarr = patch_table->get_table_array(size_type); - + auto patch_data_tarr = patch_table->get_table_array(data_type.first); if (!patch_data_tarr) { - return patch_data; + return true; } for (const auto& patch_data_table : *patch_data_tarr) { uint32_t address = *patch_data_table->get_as("address"); - uint64_t value = *patch_data_table->get_as("value"); + size_t alloc_size = (size_t)data_type.second.size; - if (size_type == "f32") { - float fvalue = float(*patch_data_table->get_as("value")); - value = *(reinterpret_cast(&fvalue)); - } else if (size_type == "f64") { - double dvalue = *patch_data_table->get_as("value"); - value = *(reinterpret_cast(&dvalue)); + switch (data_type.second.type) { + case PatchDataType::f64: { + double val = *patch_data_table->get_as("value"); + uint64_t value = *reinterpret_cast(&val); + patch_data.push_back({address, PatchDataValue(alloc_size, value)}); + break; + } + case PatchDataType::f32: { + float value = float(*patch_data_table->get_as("value")); + patch_data.push_back({address, PatchDataValue(alloc_size, value)}); + break; + } + case PatchDataType::string: { + std::string value = *patch_data_table->get_as("value"); + patch_data.push_back({address, PatchDataValue(value)}); + break; + } + case PatchDataType::u16string: { + std::u16string value = + xe::to_utf16(*patch_data_table->get_as("value")); + patch_data.push_back({address, PatchDataValue(value)}); + break; + } + case PatchDataType::byte_array: { + std::vector data; + const std::string value = + *patch_data_table->get_as("value"); + + bool success = string_util::hex_string_to_array(data, value); + if (!success) { + XELOGE("PatchDB: Cannot load patch due to invalid data!"); + return false; + } + + patch_data.push_back({address, PatchDataValue(value.size() / 2, data)}); + break; + } + default: { + uint64_t value = *patch_data_table->get_as("value"); + patch_data.push_back({address, PatchDataValue(alloc_size, value)}); + break; + } } - - PatchDataEntry patchData = {GetAllocSize(size_type), address, value}; - patch_data->push_back(patchData); } - return patch_data; + return true; } std::vector PatchDB::GetTitlePatches(uint32_t title_id, @@ -147,18 +181,5 @@ std::vector PatchDB::GetTitlePatches(uint32_t title_id, return title_patches; } -uint8_t PatchDB::GetAllocSize(const std::string provided_size) { - uint8_t alloc_size = 0; - - auto itr = patch_data_types_size.find(provided_size); - if (itr == patch_data_types_size.cend()) { - XELOGW("PatchDB: Unsupported patch type!"); - return alloc_size; - } - - alloc_size = uint8_t(itr->second); - return alloc_size; -} - } // namespace patcher } // namespace xe diff --git a/src/xenia/patcher/patch_db.h b/src/xenia/patcher/patch_db.h index 585336e7f..5d237d1d4 100644 --- a/src/xenia/patcher/patch_db.h +++ b/src/xenia/patcher/patch_db.h @@ -16,16 +16,46 @@ namespace xe { namespace patcher { -struct PatchDataEntry { - const uint8_t alloc_size_; - const uint32_t memory_address_; - const uint64_t new_value_; - PatchDataEntry(const uint8_t alloc_size, const uint32_t memory_address, - const uint64_t new_value) - : alloc_size_(alloc_size), - memory_address_(memory_address), - new_value_(new_value){}; +struct PatchDataValue { + const size_t alloc_size_; + const uint8_t* patch_data_ptr_; + + PatchDataValue(const size_t alloc_size, const uint64_t value) + : alloc_size_(alloc_size) { + patch_data_ptr_ = new uint8_t[alloc_size_]; + memcpy((void*)patch_data_ptr_, &value, alloc_size); + }; + + PatchDataValue(const size_t alloc_size, const float value) + : alloc_size_(alloc_size) { + patch_data_ptr_ = new uint8_t[alloc_size_]; + memcpy((void*)patch_data_ptr_, &value, alloc_size); + }; + + PatchDataValue(const size_t alloc_size, const std::vector value) + : alloc_size_(alloc_size) { + patch_data_ptr_ = new uint8_t[alloc_size_]; + memcpy((void*)patch_data_ptr_, value.data(), alloc_size); + }; + + PatchDataValue(const std::string value) : alloc_size_(value.size()) { + patch_data_ptr_ = new uint8_t[alloc_size_]; + memcpy((void*)patch_data_ptr_, value.c_str(), alloc_size_); + }; + + PatchDataValue(const std::u16string value) : alloc_size_(value.size() * 2) { + patch_data_ptr_ = new uint8_t[alloc_size_]; + memcpy((void*)patch_data_ptr_, value.c_str(), alloc_size_); + }; +}; + +struct PatchDataEntry { + const uint32_t memory_address_; + const PatchDataValue new_data_; + + PatchDataEntry(const uint32_t memory_address, const PatchDataValue new_data) + : memory_address_(memory_address), new_data_(new_data){}; }; struct PatchInfoEntry { @@ -44,6 +74,25 @@ struct PatchFileEntry { std::vector patch_info; }; +enum class PatchDataType { + be8, + be16, + be32, + be64, + f32, + f64, + string, + u16string, + byte_array +}; + +struct PatchData { + uint8_t size; + PatchDataType type; + + PatchData(uint8_t size_, PatchDataType type_) : size(size_), type(type_){}; +}; + class PatchDB { public: PatchDB(const std::filesystem::path patches_root); @@ -52,9 +101,9 @@ class PatchDB { void LoadPatches(); PatchFileEntry ReadPatchFile(const std::filesystem::path& file_path); - std::vector* ReadPatchData( - const std::string size_type, - const std::shared_ptr& patch_table); + bool ReadPatchData(std::vector& patch_data, + const std::pair data_type, + const std::shared_ptr& patch_table); std::vector GetTitlePatches(uint32_t title_id, const uint64_t hash); @@ -64,12 +113,16 @@ class PatchDB { std::vector loaded_patches; std::filesystem::path patches_root_; - uint8_t GetAllocSize(const std::string provided_size); - - const std::map patch_data_types_size = { - {"f64", sizeof(uint64_t)}, {"f32", sizeof(uint32_t)}, - {"be64", sizeof(uint64_t)}, {"be32", sizeof(uint32_t)}, - {"be16", sizeof(uint16_t)}, {"be8", sizeof(uint8_t)}}; + const std::map patch_data_types_size = { + {"string", PatchData(0, PatchDataType::string)}, + {"u16string", PatchData(0, PatchDataType::u16string)}, + {"array", PatchData(0, PatchDataType::byte_array)}, + {"f64", PatchData(sizeof(uint64_t), PatchDataType::f64)}, + {"f32", PatchData(sizeof(uint32_t), PatchDataType::f32)}, + {"be64", PatchData(sizeof(uint64_t), PatchDataType::be64)}, + {"be32", PatchData(sizeof(uint32_t), PatchDataType::be32)}, + {"be16", PatchData(sizeof(uint16_t), PatchDataType::be16)}, + {"be8", PatchData(sizeof(uint8_t), PatchDataType::be8)}}; }; } // namespace patcher diff --git a/src/xenia/patcher/patcher.cc b/src/xenia/patcher/patcher.cc index 76bfadc65..b35e92534 100644 --- a/src/xenia/patcher/patcher.cc +++ b/src/xenia/patcher/patcher.cc @@ -48,30 +48,17 @@ void Patcher::ApplyPatch(Memory* memory, const PatchInfoEntry* patch) { heap->QueryProtect(patch_data_entry.memory_address_, &old_address_protect); heap->Protect(patch_data_entry.memory_address_, - patch_data_entry.alloc_size_, + (uint32_t)patch_data_entry.new_data_.alloc_size_, kMemoryProtectRead | kMemoryProtectWrite); - switch (patch_data_entry.alloc_size_) { - case 1: - xe::store_and_swap(address, uint8_t(patch_data_entry.new_value_)); - break; - case 2: - xe::store_and_swap(address, uint16_t(patch_data_entry.new_value_)); - break; - case 4: - xe::store_and_swap(address, uint32_t(patch_data_entry.new_value_)); - break; - case 8: - xe::store_and_swap(address, uint64_t(patch_data_entry.new_value_)); - break; - default: - XELOGE("Patcher: Unsupported patch allocation size - {}", - patch_data_entry.alloc_size_); - break; - } + xe::copy_and_swap(address, + (uint8_t*)patch_data_entry.new_data_.patch_data_ptr_, + patch_data_entry.new_data_.alloc_size_); + // Restore previous protection heap->Protect(patch_data_entry.memory_address_, - patch_data_entry.alloc_size_, old_address_protect); + (uint32_t)patch_data_entry.new_data_.alloc_size_, + old_address_protect); is_any_patch_applied_ = true; }