diff --git a/mia/mia.hpp b/mia/mia.hpp index 36696aeda..a2c7a5939 100644 --- a/mia/mia.hpp +++ b/mia/mia.hpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/mia/pak/pak.cpp b/mia/pak/pak.cpp index 4ee143a58..d2d322078 100644 --- a/mia/pak/pak.cpp +++ b/mia/pak/pak.cpp @@ -24,11 +24,15 @@ auto Pak::read(string location) -> vector { auto Pak::read(string location, vector match) -> vector { vector memory; - vector patch; + vector ips_patch; + vector bps_patch; if(file::exists(location)) { - //support BPS patches next to the file - patch = file::read({Location::notsuffix(location), ".bps"}); + //support IPS or BPS patches next to the file + bps_patch = file::read({Location::notsuffix(location), ".bps"}); + if (!bps_patch) { + ips_patch = file::read({Location::notsuffix(location), ".ips"}); + } if(location.iendsWith(".zip")) { Decode::ZIP archive; @@ -46,7 +50,10 @@ auto Pak::read(string location, vector match) -> vector { //support BPS patches inside the ZIP archive for(auto& file : archive.file) { if(file.name.imatch("*.bps")) { - patch = archive.extract(file); + bps_patch = archive.extract(file); + break; + } else if (file.name.imatch("*.ips")) { + ips_patch = archive.extract(file); break; } } @@ -62,9 +69,13 @@ auto Pak::read(string location, vector match) -> vector { } } - //attempt to apply BPS patch if one was found - if(patch) { - if(auto output = Beat::Single::apply(memory, patch)) { + //attempt to apply IPS or BPS patch if one was found, favoring BPS + if(bps_patch) { + if(auto output = Beat::Single::apply(memory, bps_patch)) { + memory = std::move(*output); + } + } else if (ips_patch) { + if (auto output = IPS::apply(memory, ips_patch)) { memory = std::move(*output); } } diff --git a/nall/ips.hpp b/nall/ips.hpp new file mode 100644 index 000000000..3bc480025 --- /dev/null +++ b/nall/ips.hpp @@ -0,0 +1,63 @@ +#pragma once + +namespace nall::IPS { + +inline auto apply(array_view source, array_view patch, maybe result = {}) -> maybe> { + #define error(text) { if(result) *result = {"error: ", text}; return {}; } + #define success() { if(result) *result = ""; return target; } + + vector target; + for (u32 i : range(source.size())) { + target.append(source[i]); + } + + u32 patchOffset = 0; + auto read = [&]() -> u8 { + return patch[patchOffset++]; + }; + auto readOffset = [&]() -> u32 { + u32 result = read() << 16; + result |= read() << 8; + result |= read(); + return result; + }; + auto readLength = [&]() -> u32 { + u32 result = read() << 8; + result |= read(); + return result; + }; + auto write = [&](u32 index, u8 data) { + target[index] = data; + }; + + if(read() != 'P') error("IPS header invalid"); + if(read() != 'A') error("IPS header invalid"); + if(read() != 'T') error("IPS header invalid"); + if(read() != 'C') error("IPS header invalid"); + if(read() != 'H') error("IPS header invalid"); + + u32 patchSize = patch.size(); + while (patchOffset < patchSize - 3) { + u32 offset = readOffset(); + u32 length = readLength(); + + if(target.size() < offset + length) error("Invalid IPS patch file"); + + if (length == 0) { + length = readLength(); + u8 data = read(); + for(u32 i : range(length)) write(offset + i, data); + } else { + for (u32 i : range(length)) write(offset + i, read()); + } + } + + if(read() != 'E') error("IPS footer invalid"); + if(read() != 'O') error("IPS footer invalid"); + if(read() != 'F') error("IPS footer invalid"); + + success(); + #undef error + #undef success +} +}