Add support for IPS patching
Adds support for patching ROMs on the fly with a corresponding IPS patch file. Functions in a similar manner to the pre-existing BPS support. If a *.ips file is found with the same name as the ROM, it attempts to apply the patch. If both a BPS and IPS file are found, the BPS patch takes priority. This patch implements the IPS format as described here: https://zerosoft.zophar.net/ips.php
このコミットが含まれているのは:
コミット
e02938c23f
|
@ -1,5 +1,6 @@
|
|||
#include <nall/nall.hpp>
|
||||
#include <nall/cd.hpp>
|
||||
#include <nall/ips.hpp>
|
||||
#include <nall/vfs.hpp>
|
||||
#include <nall/beat/single/apply.hpp>
|
||||
#include <nall/decode/cue.hpp>
|
||||
|
|
|
@ -24,11 +24,15 @@ auto Pak::read(string location) -> vector<u8> {
|
|||
|
||||
auto Pak::read(string location, vector<string> match) -> vector<u8> {
|
||||
vector<u8> memory;
|
||||
vector<u8> patch;
|
||||
vector<u8> ips_patch;
|
||||
vector<u8> 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<string> match) -> vector<u8> {
|
|||
//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<string> match) -> vector<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
namespace nall::IPS {
|
||||
|
||||
inline auto apply(array_view<u8> source, array_view<u8> patch, maybe<string&> result = {}) -> maybe<vector<u8>> {
|
||||
#define error(text) { if(result) *result = {"error: ", text}; return {}; }
|
||||
#define success() { if(result) *result = ""; return target; }
|
||||
|
||||
vector<u8> 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
|
||||
}
|
||||
}
|
読み込み中…
新しいイシューから参照