all repos — mgba @ 8c2d3e5d598cd50433e8869c09a54f974af22ab3

mGBA Game Boy Advance Emulator

Util: Add BPS patch support (fixes #150)
Jeffrey Pfau jeffrey@endrift.com
Sun, 11 Jan 2015 01:38:04 -0800
commit

8c2d3e5d598cd50433e8869c09a54f974af22ab3

parent

a6001496bce5ff9c2ef2f93df2ab4fe396f48266

5 files changed, 100 insertions(+), 16 deletions(-)

jump to
M CHANGESCHANGES

@@ -17,6 +17,7 @@ - Rewinding of emulation

- 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
M src/gba/gba.csrc/gba/gba.c

@@ -496,8 +496,7 @@ if (!patchedSize) {

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;
M src/util/patch-ips.csrc/util/patch-ips.c

@@ -9,7 +9,7 @@ #include "util/patch.h"

#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 @@ UNUSED(patch);

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) {
M src/util/patch-ups.csrc/util/patch-ups.c

@@ -16,8 +16,11 @@ PATCH_CHECKSUM = -4,

}; 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 @@ if (patch->vf->read(patch->vf, buffer, 4) != 4) {

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 @@ return false;

} 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 @@ }

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;
M src/util/patch.hsrc/util/patch.h

@@ -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);