Support for patch types:
- float - double - string - u16string - byte_array Plus some smaller changes
This commit is contained in:
parent
c73cdb506a
commit
fc16e3dc40
|
@ -111,6 +111,32 @@ inline size_t copy_and_swap_maybe_truncating(char16_t* dest,
|
||||||
return chars_copied;
|
return chars_copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool hex_string_to_array(std::vector<uint8_t>& 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) {
|
inline std::string to_hex_string(uint32_t value) {
|
||||||
return fmt::format("{:08X}", value);
|
return fmt::format("{:08X}", value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
return LoadXexContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ void PatchDB::LoadPatches() {
|
||||||
const std::vector<xe::filesystem::FileInfo>& patch_files =
|
const std::vector<xe::filesystem::FileInfo>& patch_files =
|
||||||
filesystem::ListFiles(patches_directory);
|
filesystem::ListFiles(patches_directory);
|
||||||
const std::regex file_name_regex_match =
|
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) {
|
for (const xe::filesystem::FileInfo& patch_file : patch_files) {
|
||||||
// Skip files that doesn't have only title_id as name and .patch as
|
// 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;
|
patch.is_enabled = is_enabled;
|
||||||
|
|
||||||
// Iterate through all available data sizes
|
// Iterate through all available data sizes
|
||||||
for (const auto& type : patch_data_types_size) {
|
for (const auto& patch_data_type : patch_data_types_size) {
|
||||||
auto patch_entries = ReadPatchData(type.first, patch_table_entry);
|
bool success =
|
||||||
|
ReadPatchData(patch.patch_data, patch_data_type, patch_table_entry);
|
||||||
|
|
||||||
for (const PatchDataEntry& patch_entry : *patch_entries) {
|
if (!success) {
|
||||||
patch.patch_data.push_back(patch_entry);
|
XELOGE("PatchDB: Cannot read patch {}", patch_name);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
patchFile.patch_info.push_back(patch);
|
patchFile.patch_info.push_back(patch);
|
||||||
|
@ -105,32 +107,64 @@ PatchFileEntry PatchDB::ReadPatchFile(const std::filesystem::path& file_path) {
|
||||||
return patchFile;
|
return patchFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PatchDataEntry>* PatchDB::ReadPatchData(
|
bool PatchDB::ReadPatchData(
|
||||||
const std::string size_type,
|
std::vector<PatchDataEntry>& patch_data,
|
||||||
|
const std::pair<std::string, PatchData> data_type,
|
||||||
const std::shared_ptr<cpptoml::table>& patch_table) {
|
const std::shared_ptr<cpptoml::table>& patch_table) {
|
||||||
std::vector<PatchDataEntry>* patch_data = new std::vector<PatchDataEntry>();
|
auto patch_data_tarr = patch_table->get_table_array(data_type.first);
|
||||||
auto patch_data_tarr = patch_table->get_table_array(size_type);
|
|
||||||
|
|
||||||
if (!patch_data_tarr) {
|
if (!patch_data_tarr) {
|
||||||
return patch_data;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& patch_data_table : *patch_data_tarr) {
|
for (const auto& patch_data_table : *patch_data_tarr) {
|
||||||
uint32_t address = *patch_data_table->get_as<std::uint32_t>("address");
|
uint32_t address = *patch_data_table->get_as<std::uint32_t>("address");
|
||||||
uint64_t value = *patch_data_table->get_as<std::uint64_t>("value");
|
size_t alloc_size = (size_t)data_type.second.size;
|
||||||
|
|
||||||
if (size_type == "f32") {
|
switch (data_type.second.type) {
|
||||||
float fvalue = float(*patch_data_table->get_as<double>("value"));
|
case PatchDataType::f64: {
|
||||||
value = *(reinterpret_cast<uint32_t*>(&fvalue));
|
double val = *patch_data_table->get_as<double>("value");
|
||||||
} else if (size_type == "f64") {
|
uint64_t value = *reinterpret_cast<uint64_t*>(&val);
|
||||||
double dvalue = *patch_data_table->get_as<double>("value");
|
patch_data.push_back({address, PatchDataValue(alloc_size, value)});
|
||||||
value = *(reinterpret_cast<uint64_t*>(&dvalue));
|
break;
|
||||||
|
}
|
||||||
|
case PatchDataType::f32: {
|
||||||
|
float value = float(*patch_data_table->get_as<double>("value"));
|
||||||
|
patch_data.push_back({address, PatchDataValue(alloc_size, value)});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PatchDataType::string: {
|
||||||
|
std::string value = *patch_data_table->get_as<std::string>("value");
|
||||||
|
patch_data.push_back({address, PatchDataValue(value)});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PatchDataType::u16string: {
|
||||||
|
std::u16string value =
|
||||||
|
xe::to_utf16(*patch_data_table->get_as<std::string>("value"));
|
||||||
|
patch_data.push_back({address, PatchDataValue(value)});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PatchDataType::byte_array: {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
const std::string value =
|
||||||
|
*patch_data_table->get_as<std::string>("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<uint64_t>("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<PatchFileEntry> PatchDB::GetTitlePatches(uint32_t title_id,
|
std::vector<PatchFileEntry> PatchDB::GetTitlePatches(uint32_t title_id,
|
||||||
|
@ -147,18 +181,5 @@ std::vector<PatchFileEntry> PatchDB::GetTitlePatches(uint32_t title_id,
|
||||||
return title_patches;
|
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 patcher
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -16,16 +16,46 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace patcher {
|
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,
|
struct PatchDataValue {
|
||||||
const uint64_t new_value)
|
const size_t alloc_size_;
|
||||||
: alloc_size_(alloc_size),
|
const uint8_t* patch_data_ptr_;
|
||||||
memory_address_(memory_address),
|
|
||||||
new_value_(new_value){};
|
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<uint8_t> 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 {
|
struct PatchInfoEntry {
|
||||||
|
@ -44,6 +74,25 @@ struct PatchFileEntry {
|
||||||
std::vector<PatchInfoEntry> patch_info;
|
std::vector<PatchInfoEntry> 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 {
|
class PatchDB {
|
||||||
public:
|
public:
|
||||||
PatchDB(const std::filesystem::path patches_root);
|
PatchDB(const std::filesystem::path patches_root);
|
||||||
|
@ -52,9 +101,9 @@ class PatchDB {
|
||||||
void LoadPatches();
|
void LoadPatches();
|
||||||
|
|
||||||
PatchFileEntry ReadPatchFile(const std::filesystem::path& file_path);
|
PatchFileEntry ReadPatchFile(const std::filesystem::path& file_path);
|
||||||
std::vector<PatchDataEntry>* ReadPatchData(
|
bool ReadPatchData(std::vector<PatchDataEntry>& patch_data,
|
||||||
const std::string size_type,
|
const std::pair<std::string, PatchData> data_type,
|
||||||
const std::shared_ptr<cpptoml::table>& patch_table);
|
const std::shared_ptr<cpptoml::table>& patch_table);
|
||||||
|
|
||||||
std::vector<PatchFileEntry> GetTitlePatches(uint32_t title_id,
|
std::vector<PatchFileEntry> GetTitlePatches(uint32_t title_id,
|
||||||
const uint64_t hash);
|
const uint64_t hash);
|
||||||
|
@ -64,12 +113,16 @@ class PatchDB {
|
||||||
std::vector<PatchFileEntry> loaded_patches;
|
std::vector<PatchFileEntry> loaded_patches;
|
||||||
std::filesystem::path patches_root_;
|
std::filesystem::path patches_root_;
|
||||||
|
|
||||||
uint8_t GetAllocSize(const std::string provided_size);
|
const std::map<std::string, PatchData> patch_data_types_size = {
|
||||||
|
{"string", PatchData(0, PatchDataType::string)},
|
||||||
const std::map<std::string, size_t> patch_data_types_size = {
|
{"u16string", PatchData(0, PatchDataType::u16string)},
|
||||||
{"f64", sizeof(uint64_t)}, {"f32", sizeof(uint32_t)},
|
{"array", PatchData(0, PatchDataType::byte_array)},
|
||||||
{"be64", sizeof(uint64_t)}, {"be32", sizeof(uint32_t)},
|
{"f64", PatchData(sizeof(uint64_t), PatchDataType::f64)},
|
||||||
{"be16", sizeof(uint16_t)}, {"be8", sizeof(uint8_t)}};
|
{"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
|
} // namespace patcher
|
||||||
|
|
|
@ -48,30 +48,17 @@ void Patcher::ApplyPatch(Memory* memory, const PatchInfoEntry* patch) {
|
||||||
heap->QueryProtect(patch_data_entry.memory_address_, &old_address_protect);
|
heap->QueryProtect(patch_data_entry.memory_address_, &old_address_protect);
|
||||||
|
|
||||||
heap->Protect(patch_data_entry.memory_address_,
|
heap->Protect(patch_data_entry.memory_address_,
|
||||||
patch_data_entry.alloc_size_,
|
(uint32_t)patch_data_entry.new_data_.alloc_size_,
|
||||||
kMemoryProtectRead | kMemoryProtectWrite);
|
kMemoryProtectRead | kMemoryProtectWrite);
|
||||||
|
|
||||||
switch (patch_data_entry.alloc_size_) {
|
xe::copy_and_swap(address,
|
||||||
case 1:
|
(uint8_t*)patch_data_entry.new_data_.patch_data_ptr_,
|
||||||
xe::store_and_swap(address, uint8_t(patch_data_entry.new_value_));
|
patch_data_entry.new_data_.alloc_size_);
|
||||||
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;
|
|
||||||
}
|
|
||||||
// Restore previous protection
|
// Restore previous protection
|
||||||
heap->Protect(patch_data_entry.memory_address_,
|
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;
|
is_any_patch_applied_ = true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue