1
0
mirror of synced 2024-09-24 03:28:24 +02:00

Added patching system and IPS/IPS32 patch exporting

This commit is contained in:
WerWolv 2020-11-27 09:09:48 +01:00
parent fd2b79bea9
commit ed572ececf
10 changed files with 293 additions and 88 deletions

View File

@ -30,6 +30,7 @@ add_executable(ImHex
source/window.cpp
source/utils.cpp
source/crypto.cpp
source/patches.cpp
source/lang/preprocessor.cpp
source/lang/lexer.cpp
@ -37,7 +38,7 @@ add_executable(ImHex
source/lang/validator.cpp
source/lang/evaluator.cpp
source/provider/file_provider.cpp
source/providers/file_provider.cpp
source/views/view_hexeditor.cpp
source/views/view_pattern.cpp

15
include/patches.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <hex.hpp>
#include <map>
#include <vector>
namespace hex {
using Patches = std::map<u64, u8>;
std::vector<u8> generateIPSPatch(const Patches &patches);
std::vector<u8> generateIPS32Patch(const Patches &patches);
}

View File

@ -19,6 +19,9 @@ namespace hex::prv {
void read(u64 offset, void *buffer, size_t size) override;
void write(u64 offset, void *buffer, size_t size) override;
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, void *buffer, size_t size) override;
size_t getActualSize() override;
std::vector<std::pair<std::string, std::string>> getDataInformation() override;

View File

@ -3,6 +3,8 @@
#include <hex.hpp>
#include <cmath>
#include <concepts>
#include <map>
#include <optional>
#include <string>
#include <vector>
@ -13,17 +15,29 @@ namespace hex::prv {
public:
constexpr static size_t PageSize = 0x1000'0000;
Provider() = default;
Provider() {
this->m_patches.emplace_back();
}
virtual ~Provider() = default;
virtual bool isAvailable() = 0;
virtual bool isReadable() = 0;
virtual bool isWritable() = 0;
virtual void read(u64 offset, void *buffer, size_t size) = 0;
virtual void write(u64 offset, void *buffer, size_t size) = 0;
virtual void read(u64 offset, void *buffer, size_t size) { this->readRaw(offset, buffer, size); }
virtual void write(u64 offset, void *buffer, size_t size) { this->writeRaw(offset, buffer, size); }
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
virtual void writeRaw(u64 offset, void *buffer, size_t size) = 0;
virtual size_t getActualSize() = 0;
const std::map<u64, u8>& getPatches() { return this->m_patches.back(); }
void applyPatches() {
for (auto &[patchAddress, patch] : this->m_patches.back())
this->writeRaw(patchAddress, &patch, 1);
}
u32 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
u32 getCurrentPage() const { return this->m_currPage; }
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
@ -49,6 +63,8 @@ namespace hex::prv {
protected:
u32 m_currPage = 0;
std::vector<std::map<u64, u8>> m_patches;
};
}

View File

@ -33,59 +33,34 @@ namespace hex {
return std::string(buffer.data(), buffer.data() + size);
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
return (value & mask) >> to;
}
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
u64 mask = 1LLU << (currWidth - 1);
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
}
constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
[[nodiscard]] constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x00;
}
constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
[[nodiscard]] constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x01;
}
constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
[[nodiscard]] constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
return (static_cast<u32>(type) & 0x0F) == 0x02;
}
constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
[[nodiscard]] constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
return static_cast<u32>(type) >> 4;
}
inline std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
while (value > 1024) {
value /= 1024;
unitIndex++;
if (unitIndex == 6)
break;
}
std::string result = hex::format("%.2f", value);
switch (unitIndex) {
case 0: result += " Bytes"; break;
case 1: result += " kB"; break;
case 2: result += " MB"; break;
case 3: result += " GB"; break;
case 4: result += " TB"; break;
case 5: result += " PB"; break;
case 6: result += " EB"; break;
default: result = "A lot!";
}
return result;
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
return (value & mask) >> to;
}
std::string toByteString(u64 bytes);
std::string makePrintable(char c);
template<typename T>
struct always_false : std::false_type {};
@ -124,46 +99,6 @@ namespace hex {
throw std::invalid_argument("Invalid value size!");
}
inline std::string makePrintable(char c) {
switch (c) {
case 0: return "NUL";
case 1: return "SOH";
case 2: return "STX";
case 3: return "ETX";
case 4: return "EOT";
case 5: return "ENQ";
case 6: return "ACK";
case 7: return "BEL";
case 8: return "BS";
case 9: return "TAB";
case 10: return "LF";
case 11: return "VT";
case 12: return "FF";
case 13: return "CR";
case 14: return "SO";
case 15: return "SI";
case 16: return "DLE";
case 17: return "DC1";
case 18: return "DC2";
case 19: return "DC3";
case 20: return "DC4";
case 21: return "NAK";
case 22: return "SYN";
case 23: return "ETB";
case 24: return "CAN";
case 25: return "EM";
case 26: return "SUB";
case 27: return "ESC";
case 28: return "FS";
case 29: return "GS";
case 30: return "RS";
case 31: return "US";
case 32: return "Space";
case 127: return "DEL";
default: return std::string() + c;
}
}
class ScopeExit {
public:
ScopeExit(std::function<void()> func) : m_func(func) {}

View File

@ -45,7 +45,7 @@ namespace hex {
s64 m_gotoAddress = 0;
std::vector<u8> m_importData;
std::vector<u8> m_dataToSave;
void drawSearchPopup();
void drawGotoPopup();

116
source/patches.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "patches.hpp"
#include <concepts>
#include <cstring>
#include <string_view>
#include <type_traits>
#include "utils.hpp"
namespace hex {
static void pushBytesBack(std::vector<u8> &buffer, const char* bytes) {
std::string_view string(bytes);
buffer.resize(buffer.size() + string.length());
std::memcpy((&buffer.back() - string.length()) + 1, string.begin(), string.length());
}
template<typename T>
static void pushBytesBack(std::vector<u8> &buffer, T bytes) {
buffer.resize(buffer.size() + sizeof(T));
std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T));
}
std::vector<u8> generateIPSPatch(const Patches &patches) {
std::vector<u8> result;
pushBytesBack(result, "PATCH");
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : patches) {
addresses.push_back(address);
values.push_back(value);
}
std::optional<u64> startAddress;
std::vector<u8> bytes;
for (u32 i = 0; i < addresses.size(); i++) {
if (!startAddress.has_value())
startAddress = addresses[i];
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
bytes.push_back(values[i]);
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFF'FFFF)
return { };
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8*>(&address);
result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
for (auto byte : bytes)
result.push_back(byte);
bytes.clear();
startAddress = { };
}
}
pushBytesBack(result, "EOF");
return result;
}
std::vector<u8> generateIPS32Patch(const Patches &patches) {
std::vector<u8> result;
pushBytesBack(result, "IPS32");
std::vector<u64> addresses;
std::vector<u8> values;
for (const auto &[address, value] : patches) {
addresses.push_back(address);
values.push_back(value);
}
std::optional<u64> startAddress;
std::vector<u8> bytes;
for (u32 i = 0; i < addresses.size(); i++) {
if (!startAddress.has_value())
startAddress = addresses[i];
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
bytes.push_back(values[i]);
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFFFF'FFFF)
return { };
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8*>(&address);
result.push_back(addressBytes[3]); result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
for (auto byte : bytes)
result.push_back(byte);
bytes.clear();
startAddress = { };
}
}
pushBytesBack(result, "EEOF");
return result;
}
}

View File

@ -49,16 +49,39 @@ namespace hex::prv {
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fread(buffer, 1, size, this->m_file);
for (u64 i = 0; i < size; i++)
if (this->m_patches.back().contains(offset + i))
reinterpret_cast<u8*>(buffer)[i] = this->m_patches.back()[offset + i];
}
void FileProvider::write(u64 offset, void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fwrite(buffer, 1, size, this->m_file);
this->m_patches.push_back(this->m_patches.back());
for (u64 i = 0; i < size; i++)
this->m_patches.back()[offset + i] = reinterpret_cast<u8*>(buffer)[i];
}
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fread(buffer, 1, size, this->m_file);
}
void FileProvider::writeRaw(u64 offset, void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, offset, SEEK_SET);
fwrite(&buffer, 1, size, this->m_file);
}
size_t FileProvider::getActualSize() {
fseeko64(this->m_file, 0, SEEK_END);
return ftello64(this->m_file);

View File

@ -5,4 +5,72 @@
namespace hex {
std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
while (value > 1024) {
value /= 1024;
unitIndex++;
if (unitIndex == 6)
break;
}
std::string result = hex::format("%.2f", value);
switch (unitIndex) {
case 0: result += " Bytes"; break;
case 1: result += " kB"; break;
case 2: result += " MB"; break;
case 3: result += " GB"; break;
case 4: result += " TB"; break;
case 5: result += " PB"; break;
case 6: result += " EB"; break;
default: result = "A lot!";
}
return result;
}
std::string makePrintable(char c) {
switch (c) {
case 0: return "NUL";
case 1: return "SOH";
case 2: return "STX";
case 3: return "ETX";
case 4: return "EOT";
case 5: return "ENQ";
case 6: return "ACK";
case 7: return "BEL";
case 8: return "BS";
case 9: return "TAB";
case 10: return "LF";
case 11: return "VT";
case 12: return "FF";
case 13: return "CR";
case 14: return "SO";
case 15: return "SI";
case 16: return "DLE";
case 17: return "DC1";
case 18: return "DC2";
case 19: return "DC3";
case 20: return "DC4";
case 21: return "NAK";
case 22: return "SYN";
case 23: return "ETB";
case 24: return "CAN";
case 25: return "EM";
case 26: return "SUB";
case 27: return "ESC";
case 28: return "FS";
case 29: return "GS";
case 30: return "RS";
case 31: return "US";
case 32: return "Space";
case 127: return "DEL";
default: return std::string() + c;
}
}
}

View File

@ -6,6 +6,7 @@
#include <GLFW/glfw3.h>
#include "crypto.hpp"
#include "patches.hpp"
#undef __STRICT_ANSI__
#include <cstdio>
@ -127,15 +128,14 @@ namespace hex {
this->loadFromFile(this->m_fileBrowser.selected_path, base64);
if (!base64.empty()) {
this->m_importData = decode64(base64);
ImGui::OpenPopup("Save File");
this->m_dataToSave = decode64(base64);
ImGui::OpenPopup("Save Data");
}
}
if (this->m_fileBrowser.showFileDialog("Save File", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
this->saveToFile(this->m_fileBrowser.selected_path, this->m_importData);
this->openFile(this->m_fileBrowser.selected_path);
if (this->m_fileBrowser.showFileDialog("Save Data", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
this->saveToFile(this->m_fileBrowser.selected_path, this->m_dataToSave);
}
}
@ -155,6 +155,33 @@ namespace hex {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Export...")) {
if (ImGui::MenuItem("IPS Patch")) {
Patches patches = this->m_dataProvider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
u8 value = 0;
this->m_dataProvider->read(0x00454F45, &value, sizeof(u8));
patches[0x00454F45] = value;
}
this->m_dataToSave = generateIPSPatch(patches);
View::doLater([]{ ImGui::OpenPopup("Save Data"); });
}
if (ImGui::MenuItem("IPS32 Patch")) {
Patches patches = this->m_dataProvider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
u8 value = 0;
this->m_dataProvider->read(0x45454F45, &value, sizeof(u8));
patches[0x45454F45] = value;
}
this->m_dataToSave = generateIPS32Patch(patches);
View::doLater([]{ ImGui::OpenPopup("Save Data"); });
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Search", "CTRL + F")) {
@ -234,6 +261,7 @@ namespace hex {
delete this->m_dataProvider;
this->m_dataProvider = new prv::FileProvider(path);
this->m_memoryEditor.ReadOnly = !this->m_dataProvider->isWritable();
View::postEvent(Events::DataChanged);
}