2017-03-29 01:54:05 +02:00
|
|
|
#include "bin_patch.h"
|
2018-03-24 22:16:05 +01:00
|
|
|
#include <yaml-cpp/yaml.h>
|
2017-03-29 01:54:05 +02:00
|
|
|
#include "File.h"
|
|
|
|
#include "Config.h"
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
|
|
|
{
|
|
|
|
format_enum(out, arg, [](patch_type value)
|
|
|
|
{
|
|
|
|
switch (value)
|
|
|
|
{
|
2017-09-17 16:33:59 +02:00
|
|
|
case patch_type::load: return "load";
|
2017-03-29 01:54:05 +02:00
|
|
|
case patch_type::byte: return "byte";
|
|
|
|
case patch_type::le16: return "le16";
|
|
|
|
case patch_type::le32: return "le32";
|
|
|
|
case patch_type::le64: return "le64";
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::bef32: return "bef32";
|
|
|
|
case patch_type::bef64: return "bef64";
|
2017-03-29 01:54:05 +02:00
|
|
|
case patch_type::be16: return "be16";
|
|
|
|
case patch_type::be32: return "be32";
|
|
|
|
case patch_type::be64: return "be64";
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::lef32: return "lef32";
|
|
|
|
case patch_type::lef64: return "lef64";
|
2017-03-29 01:54:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return unknown;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void patch_engine::append(const std::string& patch)
|
|
|
|
{
|
|
|
|
if (fs::file f{patch})
|
|
|
|
{
|
2018-01-29 22:26:22 +01:00
|
|
|
YAML::Node root;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
root = YAML::Load(f.to_string());
|
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
|
|
|
LOG_FATAL(GENERAL, "Failed to load patch file %s\n%s thrown: %s", patch, typeid(e).name(), e.what());
|
|
|
|
return;
|
|
|
|
}
|
2017-03-29 01:54:05 +02:00
|
|
|
|
|
|
|
for (auto pair : root)
|
|
|
|
{
|
|
|
|
auto& name = pair.first.Scalar();
|
|
|
|
auto& data = m_map[name];
|
2018-01-29 22:26:22 +01:00
|
|
|
|
2017-03-29 01:54:05 +02:00
|
|
|
for (auto patch : pair.second)
|
|
|
|
{
|
|
|
|
u64 type64 = 0;
|
|
|
|
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
|
|
|
|
|
2017-07-17 15:34:04 +02:00
|
|
|
struct patch info{};
|
2017-03-29 01:54:05 +02:00
|
|
|
info.type = static_cast<patch_type>(type64);
|
2017-09-17 16:33:59 +02:00
|
|
|
info.offset = patch[1].as<u32>(0);
|
2017-07-17 15:34:04 +02:00
|
|
|
|
|
|
|
switch (info.type)
|
|
|
|
{
|
2017-09-17 16:33:59 +02:00
|
|
|
case patch_type::load:
|
|
|
|
{
|
|
|
|
// Special syntax: copy named sequence (must be loaded before)
|
|
|
|
const auto found = m_map.find(patch[1].Scalar());
|
|
|
|
|
|
|
|
if (found != m_map.end())
|
|
|
|
{
|
|
|
|
// Address modifier (optional)
|
|
|
|
const u32 mod = patch[2].as<u32>(0);
|
|
|
|
|
|
|
|
for (const auto& rd : found->second)
|
|
|
|
{
|
|
|
|
info = rd;
|
|
|
|
info.offset += mod;
|
|
|
|
data.emplace_back(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: error
|
|
|
|
break;
|
|
|
|
}
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::bef32:
|
|
|
|
case patch_type::lef32:
|
|
|
|
{
|
2019-06-01 23:12:17 +02:00
|
|
|
info.value = std::bit_cast<u32>(patch[2].as<f32>());
|
2017-07-17 15:34:04 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::bef64:
|
|
|
|
case patch_type::lef64:
|
|
|
|
{
|
2019-06-01 23:12:17 +02:00
|
|
|
info.value = std::bit_cast<u64>(patch[2].as<f64>());
|
2017-07-17 15:34:04 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
info.value = patch[2].as<u64>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 22:26:22 +01:00
|
|
|
|
2017-03-29 01:54:05 +02:00
|
|
|
data.emplace_back(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 15:34:04 +02:00
|
|
|
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
2017-03-29 01:54:05 +02:00
|
|
|
{
|
|
|
|
const auto found = m_map.find(name);
|
|
|
|
|
|
|
|
if (found == m_map.cend())
|
|
|
|
{
|
2017-07-17 15:34:04 +02:00
|
|
|
return 0;
|
2017-03-29 01:54:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply modifications sequentially
|
|
|
|
for (const auto& p : found->second)
|
|
|
|
{
|
|
|
|
auto ptr = dst + p.offset;
|
|
|
|
|
|
|
|
switch (p.type)
|
|
|
|
{
|
2017-09-17 16:33:59 +02:00
|
|
|
case patch_type::load:
|
|
|
|
{
|
|
|
|
// Invalid in this context
|
|
|
|
break;
|
|
|
|
}
|
2017-03-29 01:54:05 +02:00
|
|
|
case patch_type::byte:
|
|
|
|
{
|
|
|
|
*ptr = static_cast<u8>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le16:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le32:
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::lef32:
|
2017-03-29 01:54:05 +02:00
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le64:
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::lef64:
|
2017-03-29 01:54:05 +02:00
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be16:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be32:
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::bef32:
|
2017-03-29 01:54:05 +02:00
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be64:
|
2017-07-17 15:34:04 +02:00
|
|
|
case patch_type::bef64:
|
2017-03-29 01:54:05 +02:00
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-17 15:34:04 +02:00
|
|
|
|
|
|
|
return found->second.size();
|
2017-03-29 01:54:05 +02:00
|
|
|
}
|
2020-01-07 10:10:23 +01:00
|
|
|
|
|
|
|
std::size_t patch_engine::apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr) const
|
|
|
|
{
|
|
|
|
u32 rejected = 0;
|
|
|
|
|
|
|
|
const auto found = m_map.find(name);
|
|
|
|
|
|
|
|
if (found == m_map.cend())
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply modifications sequentially
|
|
|
|
for (const auto& p : found->second)
|
|
|
|
{
|
|
|
|
auto ptr = dst + (p.offset - ls_addr);
|
|
|
|
|
|
|
|
if(p.offset < ls_addr || p.offset >= (ls_addr + filesz))
|
|
|
|
{
|
|
|
|
// This patch is out of range for this segment
|
|
|
|
rejected++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (p.type)
|
|
|
|
{
|
|
|
|
case patch_type::load:
|
|
|
|
{
|
|
|
|
// Invalid in this context
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::byte:
|
|
|
|
{
|
|
|
|
*ptr = static_cast<u8>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le16:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le32:
|
|
|
|
case patch_type::lef32:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::le64:
|
|
|
|
case patch_type::lef64:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be16:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be32:
|
|
|
|
case patch_type::bef32:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case patch_type::be64:
|
|
|
|
case patch_type::bef64:
|
|
|
|
{
|
|
|
|
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (found->second.size() - rejected);
|
|
|
|
}
|