1
0
mirror of synced 2024-11-12 02:00:52 +01:00

feat: Added better error messages for generating and importing ips patches

This commit is contained in:
WerWolv 2023-01-25 10:38:04 +01:00
parent 111eabb84c
commit fee1b985c0
4 changed files with 90 additions and 33 deletions

View File

@ -4,14 +4,23 @@
#include <map>
#include <vector>
#include <expected>
namespace hex {
using Patches = std::map<u64, u8>;
std::vector<u8> generateIPSPatch(const Patches &patches);
std::vector<u8> generateIPS32Patch(const Patches &patches);
enum class IPSError {
AddressOutOfRange,
PatchTooLarge,
InvalidPatchHeader,
InvalidPatchFormat,
MissingEOF
};
Patches loadIPSPatch(const std::vector<u8> &ipsPatch);
Patches loadIPS32Patch(const std::vector<u8> &ipsPatch);
std::expected<std::vector<u8>, IPSError> generateIPSPatch(const Patches &patches);
std::expected<std::vector<u8>, IPSError> generateIPS32Patch(const Patches &patches);
std::expected<Patches, IPSError> loadIPSPatch(const std::vector<u8> &ipsPatch);
std::expected<Patches, IPSError> loadIPS32Patch(const std::vector<u8> &ipsPatch);
}

View File

@ -18,7 +18,7 @@ namespace hex {
std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T));
}
std::vector<u8> generateIPSPatch(const Patches &patches) {
std::expected<std::vector<u8>, IPSError> generateIPSPatch(const Patches &patches) {
std::vector<u8> result;
pushStringBack(result, "PATCH");
@ -42,8 +42,10 @@ namespace hex {
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFF'FFFF)
return {};
if (bytes.size() > 0xFFFF)
return std::unexpected(IPSError::PatchTooLarge);
if (startAddress > 0xFFFF'FFFF)
return std::unexpected(IPSError::AddressOutOfRange);
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8 *>(&address);
@ -66,7 +68,7 @@ namespace hex {
return result;
}
std::vector<u8> generateIPS32Patch(const Patches &patches) {
std::expected<std::vector<u8>, IPSError> generateIPS32Patch(const Patches &patches) {
std::vector<u8> result;
pushStringBack(result, "IPS32");
@ -90,8 +92,10 @@ namespace hex {
} else {
bytes.push_back(values[i]);
if (bytes.size() > 0xFFFF || startAddress > 0xFFFF'FFFF)
return {};
if (bytes.size() > 0xFFFF)
return std::unexpected(IPSError::PatchTooLarge);
if (startAddress > 0xFFFF'FFFF)
return std::unexpected(IPSError::AddressOutOfRange);
u32 address = startAddress.value();
auto addressBytes = reinterpret_cast<u8 *>(&address);
@ -115,12 +119,12 @@ namespace hex {
return result;
}
Patches loadIPSPatch(const std::vector<u8> &ipsPatch) {
std::expected<Patches, IPSError> loadIPSPatch(const std::vector<u8> &ipsPatch) {
if (ipsPatch.size() < (5 + 3))
return {};
return std::unexpected(IPSError::InvalidPatchHeader);
if (std::memcmp(ipsPatch.data(), "PATCH", 5) != 0)
return {};
return std::unexpected(IPSError::InvalidPatchHeader);
Patches result;
bool foundEOF = false;
@ -135,7 +139,7 @@ namespace hex {
// Handle normal record
if (size > 0x0000) {
if (ipsOffset + size > ipsPatch.size() - 3)
return {};
return std::unexpected(IPSError::InvalidPatchFormat);
for (u16 i = 0; i < size; i++)
result[offset + i] = ipsPatch[ipsOffset + i];
@ -144,7 +148,7 @@ namespace hex {
// Handle RLE record
else {
if (ipsOffset + 3 > ipsPatch.size() - 3)
return {};
return std::unexpected(IPSError::InvalidPatchFormat);
u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);
@ -163,15 +167,15 @@ namespace hex {
if (foundEOF)
return result;
else
return {};
return std::unexpected(IPSError::MissingEOF);
}
Patches loadIPS32Patch(const std::vector<u8> &ipsPatch) {
std::expected<Patches, IPSError> loadIPS32Patch(const std::vector<u8> &ipsPatch) {
if (ipsPatch.size() < (5 + 4))
return {};
return std::unexpected(IPSError::InvalidPatchHeader);
if (std::memcmp(ipsPatch.data(), "IPS32", 5) != 0)
return {};
return std::unexpected(IPSError::InvalidPatchHeader);
Patches result;
bool foundEEOF = false;
@ -186,7 +190,7 @@ namespace hex {
// Handle normal record
if (size > 0x0000) {
if (ipsOffset + size > ipsPatch.size() - 3)
return {};
return std::unexpected(IPSError::InvalidPatchFormat);
for (u16 i = 0; i < size; i++)
result[offset + i] = ipsPatch[ipsOffset + i];
@ -195,7 +199,7 @@ namespace hex {
// Handle RLE record
else {
if (ipsOffset + 3 > ipsPatch.size() - 3)
return {};
return std::unexpected(IPSError::InvalidPatchFormat);
u16 rleSize = ipsPatch[ipsOffset + 0] | (ipsPatch[ipsOffset + 1] << 8);
@ -214,7 +218,7 @@ namespace hex {
if (foundEEOF)
return result;
else
return {};
return std::unexpected(IPSError::MissingEOF);
}
}

View File

@ -136,7 +136,12 @@
"hex.builtin.menu.file.close": "Close",
"hex.builtin.menu.file.create_file": "New File...",
"hex.builtin.menu.file.export": "Export...",
"hex.builtin.menu.file.export.base64.popup.export_error": "File is not in a valid Base64 format!",
"hex.builtin.menu.file.export.ips.popup.export_error": "Failed to create new IPS file!",
"hex.builtin.menu.file.export.ips.popup.invalid_patch_header_error": "Invalid IPS patch header!",
"hex.builtin.menu.file.export.ips.popup.address_out_of_range_error": "A patch tried to patch an address that is out of range!",
"hex.builtin.menu.file.export.ips.popup.patch_too_large_error": "A patch was larger than the maximally allowed size!",
"hex.builtin.menu.file.export.ips.popup.invalid_patch_format_error": "Invalid IPS patch format!",
"hex.builtin.menu.file.export.ips.popup.missing_eof_error": "Missing IPS EOF record!",
"hex.builtin.menu.file.export.ips": "IPS Patch",
"hex.builtin.menu.file.export.ips32": "IPS32 Patch",
"hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!",

View File

@ -17,6 +17,26 @@ namespace hex::plugin::builtin {
static bool g_demoWindowOpen = false;
void handleIPSError(IPSError error) {
switch (error) {
case IPSError::InvalidPatchHeader:
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.invalid_patch_header_error"_lang);
break;
case IPSError::AddressOutOfRange:
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.address_out_of_range_error"_lang);
break;
case IPSError::PatchTooLarge:
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.patch_too_large_error"_lang);
break;
case IPSError::InvalidPatchFormat:
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.invalid_patch_format_error"_lang);
break;
case IPSError::MissingEOF:
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.missing_eof_error"_lang);
break;
}
}
static void createFileMenu() {
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.file", 1000);
@ -137,13 +157,17 @@ namespace hex::plugin::builtin {
TaskManager::createTask("hex.builtin.common.processing", TaskManager::NoProgress, [path](auto &task) {
auto patchData = fs::File(path, fs::File::Mode::Read).readBytes();
auto patch = hex::loadIPSPatch(patchData);
if (!patch.has_value()) {
handleIPSError(patch.error());
return;
}
task.setMaxValue(patch.size());
task.setMaxValue(patch->size());
auto provider = ImHexApi::Provider::get();
u64 progress = 0;
for (auto &[address, value] : patch) {
for (auto &[address, value] : *patch) {
provider->addPatch(address, &value, 1);
progress++;
task.update(progress);
@ -158,14 +182,18 @@ namespace hex::plugin::builtin {
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
TaskManager::createTask("hex.builtin.common.processing", TaskManager::NoProgress, [path](auto &task) {
auto patchData = fs::File(path, fs::File::Mode::Read).readBytes();
auto patch = hex::loadIPS32Patch(patchData);
auto patch = hex::loadIPS32Patch(patchData);
if (!patch.has_value()) {
handleIPSError(patch.error());
return;
}
task.setMaxValue(patch.size());
task.setMaxValue(patch->size());
auto provider = ImHexApi::Provider::get();
u64 progress = 0;
for (auto &[address, value] : patch) {
for (auto &[address, value] : *patch) {
provider->addPatch(address, &value, 1);
progress++;
task.update(progress);
@ -184,6 +212,8 @@ namespace hex::plugin::builtin {
if (ImGui::BeginMenu("hex.builtin.menu.file.export"_lang, providerValid && provider->isWritable())) {
if (ImGui::MenuItem("hex.builtin.menu.file.export.ips"_lang, nullptr, false)) {
Patches patches = provider->getPatches();
// Make sure there's no patch at address 0x00454F46 because that would cause the patch to contain the sequence "EOF" which signals the end of the patch
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
u8 value = 0;
provider->read(0x00454F45, &value, sizeof(u8));
@ -197,11 +227,15 @@ namespace hex::plugin::builtin {
fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) {
auto file = fs::File(path, fs::File::Mode::Create);
if (!file.isValid()) {
View::showErrorPopup("hex.builtin.menu.file.export.base64.popup.export_error"_lang);
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.export_error"_lang);
return;
}
file.write(data);
if (data.has_value())
file.write(data.value());
else {
handleIPSError(data.error());
}
});
});
});
@ -209,7 +243,9 @@ namespace hex::plugin::builtin {
if (ImGui::MenuItem("hex.builtin.menu.file.export.ips32"_lang, nullptr, false)) {
Patches patches = provider->getPatches();
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
// Make sure there's no patch at address 0x45454F46 because that would cause the patch to contain the sequence "*EOF" which signals the end of the patch
if (!patches.contains(0x45454F45) && patches.contains(0x45454F46)) {
u8 value = 0;
provider->read(0x45454F45, &value, sizeof(u8));
patches[0x45454F45] = value;
@ -222,11 +258,14 @@ namespace hex::plugin::builtin {
fs::openFileBrowser(fs::DialogMode::Save, {}, [&data](const auto &path) {
auto file = fs::File(path, fs::File::Mode::Create);
if (!file.isValid()) {
View::showErrorPopup("hex.builtin.menu.file.export.base64.popup.export_error"_lang);
View::showErrorPopup("hex.builtin.menu.file.export.ips.popup.export_error"_lang);
return;
}
file.write(data);
if (data.has_value())
file.write(data.value());
else
handleIPSError(data.error());
});
});
});