mirror of
https://github.com/DerKoun/bsnes-hd.git
synced 2024-06-01 02:48:08 -04:00
142 lines
4.1 KiB
C++
142 lines
4.1 KiB
C++
auto Program::appliedPatch() const -> bool {
|
|
return (
|
|
superFamicom.patched
|
|
|| gameBoy.patched
|
|
|| bsMemory.patched
|
|
|| sufamiTurboA.patched
|
|
|| sufamiTurboB.patched
|
|
);
|
|
}
|
|
|
|
auto Program::applyPatchIPS(vector<uint8_t>& data, string location, string suffix) -> bool {
|
|
vector<uint8_t> patch;
|
|
|
|
if(location.endsWith("/")) {
|
|
patch = file::read({location, "patch.ips", suffix});
|
|
} else if(location.iendsWith(".zip")) {
|
|
Decode::ZIP archive;
|
|
if(archive.open(location)) {
|
|
for(auto& file : archive.file) {
|
|
if(file.name.iendsWith({".ips", suffix})) {
|
|
patch = archive.extract(file);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!patch) patch = file::read(path("Patches", location, {".ips", suffix}));
|
|
} else {
|
|
patch = file::read(path("Patches", location, {".ips", suffix}));
|
|
}
|
|
if(!patch) return false;
|
|
|
|
bool headered = false;
|
|
if(MessageDialog().setAlignment(*presentation).setTitle({Location::prefix(location), ".ips", suffix}).setText({
|
|
"(You're seeing this prompt because IPS is a terrible patch file format,\n"
|
|
" and nobody can agree on whether SNES ROMs should be headered or not.\n"
|
|
" Please consider asking the patch author to use BPS patches instead.)\n\n"
|
|
"Does this IPS patch expect to be applied to a headered ROM?\n"
|
|
"If you're not sure, try 'No', and if it fails to work, try again with 'Yes'."
|
|
}).question() == "Yes") headered = true;
|
|
|
|
//sanity checks
|
|
if(patch.size() < 8) return false;
|
|
if(patch[0] != 'P') return false;
|
|
if(patch[1] != 'A') return false;
|
|
if(patch[2] != 'T') return false;
|
|
if(patch[3] != 'C') return false;
|
|
if(patch[4] != 'H') return false;
|
|
|
|
for(uint index = 5;;) {
|
|
if(index == patch.size() - 6) {
|
|
if(patch[index + 0] == 'E' && patch[index + 1] == 'O' && patch[index + 2] == 'F') {
|
|
uint32_t truncate = 0;
|
|
truncate |= patch[index + 3] << 16;
|
|
truncate |= patch[index + 4] << 8;
|
|
truncate |= patch[index + 5] << 0;
|
|
data.resize(truncate);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(index == patch.size() - 3) {
|
|
if(patch[index + 0] == 'E' && patch[index + 1] == 'O' && patch[index + 2] == 'F') {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(index >= patch.size()) break;
|
|
|
|
int32_t offset = 0;
|
|
offset |= patch(index++, 0) << 16;
|
|
offset |= patch(index++, 0) << 8;
|
|
offset |= patch(index++, 0) << 0;
|
|
if(headered) offset -= 512;
|
|
|
|
uint16_t length = 0;
|
|
length |= patch(index++, 0) << 8;
|
|
length |= patch(index++, 0) << 0;
|
|
|
|
if(length == 0) {
|
|
uint16_t repeat = 0;
|
|
repeat |= patch(index++, 0) << 8;
|
|
repeat |= patch(index++, 0) << 0;
|
|
|
|
uint8_t fill = patch(index++, 0);
|
|
|
|
while(repeat--) {
|
|
if(offset >= 0) data(offset) = fill;
|
|
offset++;
|
|
}
|
|
} else {
|
|
while(length--) {
|
|
if(offset >= 0) data(offset) = patch(index, 0);
|
|
offset++;
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//"EOF" marker not found in correct place
|
|
//technically should return false, but be permissive (data was already modified)
|
|
return true;
|
|
}
|
|
|
|
#include <nall/beat/single/apply.hpp>
|
|
|
|
auto Program::applyPatchBPS(vector<uint8_t>& input, string location, string suffix) -> bool {
|
|
vector<uint8_t> patch;
|
|
|
|
if(location.endsWith("/")) {
|
|
patch = file::read({location, "patch.bps", suffix});
|
|
} else if(location.iendsWith(".zip")) {
|
|
Decode::ZIP archive;
|
|
if(archive.open(location)) {
|
|
for(auto& file : archive.file) {
|
|
if(file.name.iendsWith({".bps", suffix})) {
|
|
patch = archive.extract(file);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!patch) patch = file::read(path("Patches", location, {".bps", suffix}));
|
|
} else {
|
|
patch = file::read(path("Patches", location, {".bps", suffix}));
|
|
}
|
|
if(!patch) return false;
|
|
|
|
string manifest;
|
|
string error;
|
|
if(auto output = Beat::Single::apply(input, patch, manifest, error)) {
|
|
if(!error) {
|
|
input = move(*output);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
MessageDialog({
|
|
error, "\n\n",
|
|
"Please ensure you are using the correct (headerless) ROM for this patch."
|
|
}).setAlignment(*presentation).error();
|
|
return false;
|
|
}
|