diff --git a/include/helpers/patches.hpp b/include/helpers/patches.hpp index ea35f357b..fbeb6880a 100644 --- a/include/helpers/patches.hpp +++ b/include/helpers/patches.hpp @@ -12,4 +12,6 @@ namespace hex { std::vector generateIPSPatch(const Patches &patches); std::vector generateIPS32Patch(const Patches &patches); + Patches loadIPSPatch(const std::vector &ipsPatch); + Patches loadIPS32Patch(const std::vector &ipsPatch); } \ No newline at end of file diff --git a/include/helpers/utils.hpp b/include/helpers/utils.hpp index 4e80877d0..cd2aba949 100644 --- a/include/helpers/utils.hpp +++ b/include/helpers/utils.hpp @@ -99,6 +99,8 @@ namespace hex { throw std::invalid_argument("Invalid value size!"); } + std::vector readFile(std::string_view path); + class ScopeExit { public: ScopeExit(std::function func) : m_func(func) {} diff --git a/source/helpers/patches.cpp b/source/helpers/patches.cpp index 258a1f97e..5b0277185 100644 --- a/source/helpers/patches.cpp +++ b/source/helpers/patches.cpp @@ -20,7 +20,7 @@ namespace hex { buffer.resize(buffer.size() + sizeof(T)); std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T)); } - + std::vector generateIPSPatch(const Patches &patches) { std::vector result; @@ -113,4 +113,106 @@ namespace hex { return result; } + Patches loadIPSPatch(const std::vector &ipsPatch) { + if (ipsPatch.size() < (5 + 3)) + return { }; + + if (std::memcmp(ipsPatch.data(), "PATCH", 5) != 0) + return { }; + + Patches result; + bool foundEOF = false; + + u32 ipsOffset = 5; + while (ipsOffset < ipsPatch.size() - (5 + 3)) { + u32 offset = ipsPatch[ipsOffset + 2] | (ipsPatch[ipsOffset + 1] << 8) | (ipsPatch[ipsOffset + 0] << 16); + u16 size = ipsPatch[ipsOffset + 4] | (ipsPatch[ipsOffset + 3] << 8); + + ipsOffset += 5; + + // Handle normal record + if (size > 0x0000) { + if (ipsOffset + size > ipsPatch.size() - 3) + return { }; + + for (u16 i = 0; i < size; i++) + result[offset + i] = ipsPatch[ipsOffset + i]; + ipsOffset += size; + } + // Handle RLE record + else { + if (ipsOffset + 3 > ipsPatch.size() - 3) + return { }; + + u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8); + + ipsOffset += 2; + + for (u16 i = 0; i < rleSize; i++) + result[offset + i] = ipsPatch[ipsOffset + 0]; + + ipsOffset += 1; + } + + if (std::memcmp(ipsPatch.data(), "EOF", 3)) + foundEOF = true; + } + + if (foundEOF) + return result; + else + return { }; + } + + Patches loadIPS32Patch(const std::vector &ipsPatch) { + if (ipsPatch.size() < (5 + 4)) + return { }; + + if (std::memcmp(ipsPatch.data(), "IPS32", 5) != 0) + return { }; + + Patches result; + bool foundEEOF = false; + + u32 ipsOffset = 5; + while (ipsOffset < ipsPatch.size() - (5 + 4)) { + u32 offset = ipsPatch[ipsOffset + 3] | (ipsPatch[ipsOffset + 2] << 8) | (ipsPatch[ipsOffset + 1] << 16) | (ipsPatch[ipsOffset + 0] << 24); + u16 size = ipsPatch[ipsOffset + 5] | (ipsPatch[ipsOffset + 4] << 8); + + ipsOffset += 6; + + // Handle normal record + if (size > 0x0000) { + if (ipsOffset + size > ipsPatch.size() - 3) + return { }; + + for (u16 i = 0; i < size; i++) + result[offset + i] = ipsPatch[ipsOffset + i]; + ipsOffset += size; + } + // Handle RLE record + else { + if (ipsOffset + 3 > ipsPatch.size() - 3) + return { }; + + u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8); + + ipsOffset += 2; + + for (u16 i = 0; i < rleSize; i++) + result[offset + i] = ipsPatch[ipsOffset + 0]; + + ipsOffset += 1; + } + + if (std::memcmp(ipsPatch.data(), "EEOF", 4)) + foundEEOF = true; + } + + if (foundEEOF) + return result; + else + return { }; + } + } \ No newline at end of file diff --git a/source/helpers/utils.cpp b/source/helpers/utils.cpp index 7a0460176..e9f872d4a 100644 --- a/source/helpers/utils.cpp +++ b/source/helpers/utils.cpp @@ -1,5 +1,6 @@ #include "helpers/utils.hpp" +#include #include #include @@ -73,4 +74,20 @@ namespace hex { } } + std::vector readFile(std::string_view path) { + FILE *file = fopen(path.data(), "rb"); + + if (file == nullptr) return { }; + + std::vector result; + + fseek(file, 0, SEEK_END); + result.resize(ftell(file)); + rewind(file); + + fread(result.data(), 1, result.size(), file); + + return result; + } + } \ No newline at end of file diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp index 07e13f1c0..1c3c36a32 100644 --- a/source/views/view_hexeditor.cpp +++ b/source/views/view_hexeditor.cpp @@ -142,6 +142,24 @@ namespace hex { this->saveToFile(this->m_fileBrowser.selected_path, this->m_dataToSave); } + if (this->m_fileBrowser.showFileDialog("Apply IPS Patch", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) { + auto patchData = hex::readFile(this->m_fileBrowser.selected_path); + auto patch = hex::loadIPSPatch(patchData); + + for (auto &[address, value] : patch) { + this->m_dataProvider->write(address, &value, 1); + } + } + + if (this->m_fileBrowser.showFileDialog("Apply IPS32 Patch", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) { + auto patchData = hex::readFile(this->m_fileBrowser.selected_path); + auto patch = hex::loadIPS32Patch(patchData); + + for (auto &[address, value] : patch) { + this->m_dataProvider->write(address, &value, 1); + } + } + if (this->m_fileBrowser.showFileDialog("Save As", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) { FILE *file = fopen(this->m_fileBrowser.selected_path.c_str(), "wb"); @@ -187,6 +205,18 @@ namespace hex { View::doLater([]{ ImGui::OpenPopup("Open Base64 File"); }); } + ImGui::Separator(); + + if (ImGui::MenuItem("IPS Patch")) { + this->getWindowOpenState() = true; + View::doLater([]{ ImGui::OpenPopup("Apply IPS Patch"); }); + } + + if (ImGui::MenuItem("IPS32 Patch")) { + this->getWindowOpenState() = true; + View::doLater([]{ ImGui::OpenPopup("Apply IPS32 Patch"); }); + } + ImGui::EndMenu(); }