Util: Add BPS patch support (fixes #150)

This commit is contained in:
Jeffrey Pfau 2015-01-11 01:38:04 -08:00
parent a6001496bc
commit 8c2d3e5d59
5 changed files with 100 additions and 16 deletions

View File

@ -17,6 +17,7 @@ Features:
- Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter
- Support IPv6
- Save directory of last loaded file
- Support BPS patches
Bugfixes:
- Qt: Fix issue with set frame sizes being the wrong height
- Qt: Fix emulator crashing when full screen if a game is not running

View File

@ -496,8 +496,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
return;
}
gba->memory.rom = anonymousMemoryMap(patchedSize);
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize);
if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) {
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
mappedMemoryFree(gba->memory.rom, patchedSize);
gba->memory.rom = gba->pristineRom;
return;

View File

@ -9,7 +9,7 @@
#include "util/vfs.h"
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
static bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
static bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
bool loadPatchIPS(struct Patch* patch) {
patch->vf->seek(patch->vf, 0, SEEK_SET);
@ -42,10 +42,11 @@ size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
return inSize;
}
bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
return false;
}
memcpy(out, in, inSize > outSize ? outSize : inSize);
uint8_t* buf = out;
while (true) {

View File

@ -16,8 +16,11 @@ enum {
};
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
static size_t _UPSDecodeLength(struct VFile* vf);
static bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
static bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
static size_t _decodeLength(struct VFile* vf);
bool loadPatchUPS(struct Patch* patch) {
patch->vf->seek(patch->vf, 0, SEEK_SET);
@ -27,7 +30,11 @@ bool loadPatchUPS(struct Patch* patch) {
return false;
}
if (memcmp(buffer, "UPS1", 4) != 0) {
if (memcmp(buffer, "UPS1", 4) == 0) {
patch->applyPatch = _UPSApplyPatch;
} else if (memcmp(buffer, "BPS1", 4) == 0) {
patch->applyPatch = _BPSApplyPatch;
} else {
return false;
}
@ -45,34 +52,35 @@ bool loadPatchUPS(struct Patch* patch) {
}
patch->outputSize = _UPSOutputSize;
patch->applyPatch = _UPSApplyPatch;
return true;
}
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
UNUSED(inSize);
patch->vf->seek(patch->vf, 4, SEEK_SET);
if (_UPSDecodeLength(patch->vf) != inSize) {
if (_decodeLength(patch->vf) != inSize) {
return 0;
}
return _UPSDecodeLength(patch->vf);
return _decodeLength(patch->vf);
}
bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
// TODO: Input checksum
size_t filesize = patch->vf->size(patch->vf);
patch->vf->seek(patch->vf, 4, SEEK_SET);
_UPSDecodeLength(patch->vf); // Discard input size
if (_UPSDecodeLength(patch->vf) != outSize) {
_decodeLength(patch->vf); // Discard input size
if (_decodeLength(patch->vf) != outSize) {
return false;
}
memcpy(out, in, inSize > outSize ? outSize : inSize);
size_t offset = 0;
size_t alreadyRead = 0;
uint8_t* buf = out;
while (alreadyRead < filesize + IN_CHECKSUM) {
offset += _UPSDecodeLength(patch->vf);
offset += _decodeLength(patch->vf);
uint8_t byte;
while (true) {
@ -101,7 +109,82 @@ bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
return true;
}
size_t _UPSDecodeLength(struct VFile* vf) {
bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
// TODO: Input checksum
ssize_t filesize = patch->vf->size(patch->vf);
patch->vf->seek(patch->vf, 4, SEEK_SET);
_decodeLength(patch->vf); // Discard input size
if (_decodeLength(patch->vf) != outSize) {
return false;
}
size_t metadataLength = _decodeLength(patch->vf);
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
size_t writeLocation = 0;
ssize_t readSourceLocation = 0;
ssize_t readTargetLocation = 0;
size_t readOffset;
uint8_t* writeBuffer = out;
uint8_t* readBuffer = in;
while (patch->vf->seek(patch->vf, 0, SEEK_CUR) < filesize + IN_CHECKSUM) {
size_t command = _decodeLength(patch->vf);
size_t length = (command >> 2) + 1;
if (writeLocation + length > outSize) {
return false;
}
size_t i;
switch (command & 0x3) {
case 0x0:
// SourceRead
memmove(&writeBuffer[writeLocation], &readBuffer[writeLocation], length);
writeLocation += length;
break;
case 0x1:
// TargetRead
if (patch->vf->read(patch->vf, &writeBuffer[writeLocation], length) != length) {
return false;
}
writeLocation += length;
break;
case 0x2:
// SourceCopy
readOffset = _decodeLength(patch->vf);
if (readOffset & 1) {
readSourceLocation -= readOffset >> 1;
} else {
readSourceLocation += readOffset >> 1;
}
if (readSourceLocation < 0 || readSourceLocation > inSize) {
return false;
}
memmove(&writeBuffer[writeLocation], &readBuffer[readSourceLocation], length);
writeLocation += length;
readSourceLocation += length;
break;
case 0x3:
// TargetCopy
readOffset = _decodeLength(patch->vf);
if (readOffset & 1) {
readTargetLocation -= readOffset >> 1;
} else {
readTargetLocation += readOffset >> 1;
}
if (readTargetLocation < 0 || readTargetLocation > outSize) {
return false;
}
for (i = 0; i < length; ++i) {
// This needs to be bytewise as it can overlap
writeBuffer[writeLocation] = writeBuffer[readTargetLocation];
++writeLocation;
++readTargetLocation;
}
break;
}
}
return true;
}
size_t _decodeLength(struct VFile* vf) {
size_t shift = 1;
size_t value = 0;
uint8_t byte;

View File

@ -14,7 +14,7 @@ struct Patch {
struct VFile* vf;
size_t (*outputSize)(struct Patch* patch, size_t inSize);
bool (*applyPatch)(struct Patch* patch, void* out, size_t outSize);
bool (*applyPatch)(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
};
bool loadPatch(struct VFile* vf, struct Patch* patch);