mirror of https://github.com/mgba-emu/mgba.git
Util: Add BPS patch support (fixes #150)
This commit is contained in:
parent
a6001496bc
commit
8c2d3e5d59
1
CHANGES
1
CHANGES
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue