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
|
- Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter
|
||||||
- Support IPv6
|
- Support IPv6
|
||||||
- Save directory of last loaded file
|
- Save directory of last loaded file
|
||||||
|
- Support BPS patches
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- Qt: Fix issue with set frame sizes being the wrong height
|
- Qt: Fix issue with set frame sizes being the wrong height
|
||||||
- Qt: Fix emulator crashing when full screen if a game is not running
|
- 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;
|
return;
|
||||||
}
|
}
|
||||||
gba->memory.rom = anonymousMemoryMap(patchedSize);
|
gba->memory.rom = anonymousMemoryMap(patchedSize);
|
||||||
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize);
|
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
|
||||||
if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) {
|
|
||||||
mappedMemoryFree(gba->memory.rom, patchedSize);
|
mappedMemoryFree(gba->memory.rom, patchedSize);
|
||||||
gba->memory.rom = gba->pristineRom;
|
gba->memory.rom = gba->pristineRom;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
|
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) {
|
bool loadPatchIPS(struct Patch* patch) {
|
||||||
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
||||||
|
@ -42,10 +42,11 @@ size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
|
||||||
return 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) {
|
if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
memcpy(out, in, inSize > outSize ? outSize : inSize);
|
||||||
uint8_t* buf = out;
|
uint8_t* buf = out;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -16,8 +16,11 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
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) {
|
bool loadPatchUPS(struct Patch* patch) {
|
||||||
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
||||||
|
@ -27,7 +30,11 @@ bool loadPatchUPS(struct Patch* patch) {
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,34 +52,35 @@ bool loadPatchUPS(struct Patch* patch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
patch->outputSize = _UPSOutputSize;
|
patch->outputSize = _UPSOutputSize;
|
||||||
patch->applyPatch = _UPSApplyPatch;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
|
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
|
||||||
UNUSED(inSize);
|
UNUSED(inSize);
|
||||||
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
if (_UPSDecodeLength(patch->vf) != inSize) {
|
if (_decodeLength(patch->vf) != inSize) {
|
||||||
return 0;
|
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
|
// TODO: Input checksum
|
||||||
|
|
||||||
size_t filesize = patch->vf->size(patch->vf);
|
size_t filesize = patch->vf->size(patch->vf);
|
||||||
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
_UPSDecodeLength(patch->vf); // Discard input size
|
_decodeLength(patch->vf); // Discard input size
|
||||||
if (_UPSDecodeLength(patch->vf) != outSize) {
|
if (_decodeLength(patch->vf) != outSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(out, in, inSize > outSize ? outSize : inSize);
|
||||||
|
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
size_t alreadyRead = 0;
|
size_t alreadyRead = 0;
|
||||||
uint8_t* buf = out;
|
uint8_t* buf = out;
|
||||||
while (alreadyRead < filesize + IN_CHECKSUM) {
|
while (alreadyRead < filesize + IN_CHECKSUM) {
|
||||||
offset += _UPSDecodeLength(patch->vf);
|
offset += _decodeLength(patch->vf);
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -101,7 +109,82 @@ bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
|
||||||
return true;
|
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 shift = 1;
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct Patch {
|
||||||
struct VFile* vf;
|
struct VFile* vf;
|
||||||
|
|
||||||
size_t (*outputSize)(struct Patch* patch, size_t inSize);
|
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);
|
bool loadPatch(struct VFile* vf, struct Patch* patch);
|
||||||
|
|
Loading…
Reference in New Issue