all repos — mgba @ 422e2e2f62fb536ab8c9a2155999287cef5454ea

mGBA Game Boy Advance Emulator

src/util/patch-ups.c (view raw)

  1#include "util/patch-ips.h"
  2
  3#include "util/crc32.h"
  4#include "util/patch.h"
  5#include "util/vfs.h"
  6
  7enum {
  8	IN_CHECKSUM = -12,
  9	OUT_CHECKSUM = -8,
 10	PATCH_CHECKSUM = -4,
 11};
 12
 13static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
 14static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
 15static size_t _UPSDecodeLength(struct VFile* vf);
 16
 17bool loadPatchUPS(struct Patch* patch) {
 18	patch->vf->seek(patch->vf, 0, SEEK_SET);
 19
 20	char buffer[4];
 21	if (patch->vf->read(patch->vf, buffer, 4) != 4) {
 22		return false;
 23	}
 24
 25	if (memcmp(buffer, "UPS1", 4) != 0) {
 26		return false;
 27	}
 28
 29	size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
 30
 31	uint32_t goodCrc32;
 32	patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END);
 33	if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) {
 34		return false;
 35	}
 36
 37	uint32_t crc = fileCrc32(patch->vf, filesize + PATCH_CHECKSUM);
 38	if (crc != goodCrc32) {
 39		return false;
 40	}
 41
 42	patch->outputSize = _UPSOutputSize;
 43	patch->applyPatch = _UPSApplyPatch;
 44	return true;
 45}
 46
 47size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
 48	UNUSED(inSize);
 49	patch->vf->seek(patch->vf, 4, SEEK_SET);
 50	if (_UPSDecodeLength(patch->vf) != inSize) {
 51		return 0;
 52	}
 53	return _UPSDecodeLength(patch->vf);
 54}
 55
 56bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
 57	// TODO: Input checksum
 58
 59	size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
 60	patch->vf->seek(patch->vf, 4, SEEK_SET);
 61	_UPSDecodeLength(patch->vf); // Discard input size
 62	if (_UPSDecodeLength(patch->vf) != outSize) {
 63		return false;
 64	}
 65
 66	size_t offset = 0;
 67	size_t alreadyRead = 0;
 68	uint8_t* buf = out;
 69	while (alreadyRead < filesize + IN_CHECKSUM) {
 70		offset += _UPSDecodeLength(patch->vf);
 71		uint8_t byte;
 72
 73		while (true) {
 74			if (patch->vf->read(patch->vf, &byte, 1) != 1) {
 75				return false;
 76			}
 77			buf[offset] ^= byte;
 78			++offset;
 79			if (!byte) {
 80				break;
 81			}
 82		}
 83		alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR);
 84	}
 85
 86	uint32_t goodCrc32;
 87	patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END);
 88	if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) {
 89		return false;
 90	}
 91
 92	patch->vf->seek(patch->vf, 0, SEEK_SET);
 93	if (doCrc32(out, outSize) != goodCrc32) {
 94		return false;
 95	}
 96	return true;
 97}
 98
 99size_t _UPSDecodeLength(struct VFile* vf) {
100	size_t shift = 1;
101	size_t value = 0;
102	uint8_t byte;
103	while (true) {
104		if (vf->read(vf, &byte, 1) != 1) {
105			break;
106		}
107		value += (byte & 0x7f) * shift;
108		if (byte & 0x80) {
109			break;
110		}
111		shift <<= 7;
112		value += shift;
113	}
114	return value;
115}