Added patching system and IPS/IPS32 patch exporting
This commit is contained in:
parent
fd2b79bea9
commit
ed572ececf
@ -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
15
include/patches.hpp
Normal 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);
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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) {}
|
||||
|
@ -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
116
source/patches.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user