feat: Added better error messages for generating and importing ips patches
This commit is contained in:
parent
111eabb84c
commit
fee1b985c0
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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!",
|
||||
|
@ -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());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user