mirror of https://github.com/mgba-emu/mgba.git
Util: Improve speed of UPS patch loading
This commit is contained in:
parent
3809876179
commit
29115e711b
1
CHANGES
1
CHANGES
|
@ -13,6 +13,7 @@ Other fixes:
|
||||||
- Util: Fix loading UPS patches that affect the last byte of the file
|
- Util: Fix loading UPS patches that affect the last byte of the file
|
||||||
Misc:
|
Misc:
|
||||||
- Qt: Rearrange menus some
|
- Qt: Rearrange menus some
|
||||||
|
- Util: Improve speed of UPS patch loading
|
||||||
|
|
||||||
0.9.1: (2021-04-18)
|
0.9.1: (2021-04-18)
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba-util/patch/ips.h>
|
#include <mgba-util/patch/ips.h>
|
||||||
|
|
||||||
|
#include <mgba-util/circle-buffer.h>
|
||||||
#include <mgba-util/crc32.h>
|
#include <mgba-util/crc32.h>
|
||||||
#include <mgba-util/patch.h>
|
#include <mgba-util/patch.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
@ -13,6 +14,8 @@ enum {
|
||||||
IN_CHECKSUM = -12,
|
IN_CHECKSUM = -12,
|
||||||
OUT_CHECKSUM = -8,
|
OUT_CHECKSUM = -8,
|
||||||
PATCH_CHECKSUM = -4,
|
PATCH_CHECKSUM = -4,
|
||||||
|
|
||||||
|
BUFFER_SIZE = 128
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
||||||
|
@ -20,7 +23,7 @@ static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
||||||
static bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize);
|
static bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize);
|
||||||
static bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize);
|
static bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize);
|
||||||
|
|
||||||
static size_t _decodeLength(struct VFile* vf);
|
static size_t _decodeLength(struct VFile* vf, struct CircleBuffer* buffer);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -58,10 +61,10 @@ bool loadPatchUPS(struct Patch* patch) {
|
||||||
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 (_decodeLength(patch->vf) != inSize) {
|
if (_decodeLength(patch->vf, NULL) != inSize) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return _decodeLength(patch->vf);
|
return _decodeLength(patch->vf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize) {
|
bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* out, size_t outSize) {
|
||||||
|
@ -69,36 +72,47 @@ bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
|
|
||||||
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);
|
||||||
_decodeLength(patch->vf); // Discard input size
|
_decodeLength(patch->vf, NULL); // Discard input size
|
||||||
if (_decodeLength(patch->vf) != outSize) {
|
if (_decodeLength(patch->vf, NULL) != outSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CircleBuffer buffer;
|
||||||
memcpy(out, in, inSize > outSize ? outSize : inSize);
|
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;
|
||||||
|
CircleBufferInit(&buffer, BUFFER_SIZE);
|
||||||
while (alreadyRead < filesize + IN_CHECKSUM) {
|
while (alreadyRead < filesize + IN_CHECKSUM) {
|
||||||
offset += _decodeLength(patch->vf);
|
offset += _decodeLength(patch->vf, &buffer);
|
||||||
uint8_t byte;
|
int8_t byte;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (patch->vf->read(patch->vf, &byte, 1) != 1) {
|
if (!CircleBufferSize(&buffer)) {
|
||||||
return false;
|
uint8_t block[BUFFER_SIZE];
|
||||||
|
ssize_t read = patch->vf->read(patch->vf, block, sizeof(block));
|
||||||
|
if (read < 1) {
|
||||||
|
CircleBufferDeinit(&buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CircleBufferWrite(&buffer, block, read);
|
||||||
}
|
}
|
||||||
|
CircleBufferRead8(&buffer, &byte);
|
||||||
if (!byte) {
|
if (!byte) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (offset >= outSize) {
|
if (offset >= outSize) {
|
||||||
|
CircleBufferDeinit(&buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buf[offset] ^= byte;
|
buf[offset] ^= byte;
|
||||||
++offset;
|
++offset;
|
||||||
}
|
}
|
||||||
++offset;
|
++offset;
|
||||||
alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR);
|
alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR) - CircleBufferSize(&buffer);
|
||||||
}
|
}
|
||||||
|
CircleBufferDeinit(&buffer);
|
||||||
|
|
||||||
uint32_t goodCrc32;
|
uint32_t goodCrc32;
|
||||||
patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END);
|
patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END);
|
||||||
|
@ -129,14 +143,14 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
|
|
||||||
ssize_t filesize = patch->vf->size(patch->vf);
|
ssize_t filesize = patch->vf->size(patch->vf);
|
||||||
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
_decodeLength(patch->vf); // Discard input size
|
_decodeLength(patch->vf, NULL); // Discard input size
|
||||||
if (_decodeLength(patch->vf) != outSize) {
|
if (_decodeLength(patch->vf, NULL) != outSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (inSize > SSIZE_MAX || outSize > SSIZE_MAX) {
|
if (inSize > SSIZE_MAX || outSize > SSIZE_MAX) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t metadataLength = _decodeLength(patch->vf);
|
size_t metadataLength = _decodeLength(patch->vf, NULL);
|
||||||
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
|
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
|
||||||
size_t writeLocation = 0;
|
size_t writeLocation = 0;
|
||||||
ssize_t readSourceLocation = 0;
|
ssize_t readSourceLocation = 0;
|
||||||
|
@ -145,7 +159,7 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
uint8_t* writeBuffer = out;
|
uint8_t* writeBuffer = out;
|
||||||
const uint8_t* readBuffer = in;
|
const uint8_t* readBuffer = in;
|
||||||
while (patch->vf->seek(patch->vf, 0, SEEK_CUR) < filesize + IN_CHECKSUM) {
|
while (patch->vf->seek(patch->vf, 0, SEEK_CUR) < filesize + IN_CHECKSUM) {
|
||||||
size_t command = _decodeLength(patch->vf);
|
size_t command = _decodeLength(patch->vf, NULL);
|
||||||
size_t length = (command >> 2) + 1;
|
size_t length = (command >> 2) + 1;
|
||||||
if (writeLocation + length > outSize) {
|
if (writeLocation + length > outSize) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -168,7 +182,7 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
break;
|
break;
|
||||||
case 0x2:
|
case 0x2:
|
||||||
// SourceCopy
|
// SourceCopy
|
||||||
readOffset = _decodeLength(patch->vf);
|
readOffset = _decodeLength(patch->vf, NULL);
|
||||||
if (readOffset & 1) {
|
if (readOffset & 1) {
|
||||||
readSourceLocation -= readOffset >> 1;
|
readSourceLocation -= readOffset >> 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,7 +198,7 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
break;
|
break;
|
||||||
case 0x3:
|
case 0x3:
|
||||||
// TargetCopy
|
// TargetCopy
|
||||||
readOffset = _decodeLength(patch->vf);
|
readOffset = _decodeLength(patch->vf, NULL);
|
||||||
if (readOffset & 1) {
|
if (readOffset & 1) {
|
||||||
readTargetLocation -= readOffset >> 1;
|
readTargetLocation -= readOffset >> 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -209,13 +223,25 @@ bool _BPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _decodeLength(struct VFile* vf) {
|
size_t _decodeLength(struct VFile* vf, struct CircleBuffer* buffer) {
|
||||||
size_t shift = 1;
|
size_t shift = 1;
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (vf->read(vf, &byte, 1) != 1) {
|
if (buffer) {
|
||||||
break;
|
if (!CircleBufferSize(buffer)) {
|
||||||
|
uint8_t block[BUFFER_SIZE];
|
||||||
|
ssize_t read = vf->read(vf, block, sizeof(block));
|
||||||
|
if (read < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CircleBufferWrite(buffer, block, read);
|
||||||
|
}
|
||||||
|
CircleBufferRead8(buffer, (int8_t*) &byte);
|
||||||
|
} else {
|
||||||
|
if (vf->read(vf, &byte, 1) != 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value += (byte & 0x7f) * shift;
|
value += (byte & 0x7f) * shift;
|
||||||
if (byte & 0x80) {
|
if (byte & 0x80) {
|
||||||
|
|
Loading…
Reference in New Issue