all repos — mgba @ a95e2c06b73e9023a53d928e047e68bfd050cd2e

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	BUFFER_SIZE = 1024
 13};
 14
 15static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
 16static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
 17static size_t _UPSDecodeLength(struct VFile* vf);
 18
 19bool loadPatchUPS(struct Patch* patch) {
 20	patch->vf->seek(patch->vf, 0, SEEK_SET);
 21
 22	char buffer[BUFFER_SIZE];
 23	if (patch->vf->read(patch->vf, buffer, 4) != 4) {
 24		return false;
 25	}
 26
 27	if (memcmp(buffer, "UPS1", 4) != 0) {
 28		return false;
 29	}
 30
 31	size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
 32
 33	uint32_t goodCrc32;
 34	patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END);
 35	if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) {
 36		return false;
 37	}
 38
 39	size_t blocksize;
 40	size_t alreadyRead = 0;
 41	patch->vf->seek(patch->vf, 0, SEEK_SET);
 42	uint32_t crc = 0;
 43	while (alreadyRead < filesize + PATCH_CHECKSUM) {
 44		size_t toRead = sizeof(buffer);
 45		if (toRead + alreadyRead > filesize + PATCH_CHECKSUM) {
 46			toRead = filesize + PATCH_CHECKSUM - alreadyRead;
 47		}
 48		blocksize = patch->vf->read(patch->vf, buffer, toRead);
 49		alreadyRead += blocksize;
 50		crc = updateCrc32(crc, buffer, blocksize);
 51		if (blocksize < toRead) {
 52			return 0;
 53		}
 54	}
 55
 56	if (crc != goodCrc32) {
 57		return false;
 58	}
 59
 60	patch->outputSize = _UPSOutputSize;
 61	patch->applyPatch = _UPSApplyPatch;
 62	return true;
 63}
 64
 65size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
 66	UNUSED(inSize);
 67	patch->vf->seek(patch->vf, 4, SEEK_SET);
 68	if (_UPSDecodeLength(patch->vf) != inSize) {
 69		return 0;
 70	}
 71	return _UPSDecodeLength(patch->vf);
 72}
 73
 74bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
 75	// TODO: Input checksum
 76
 77	size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
 78	patch->vf->seek(patch->vf, 4, SEEK_SET);
 79	_UPSDecodeLength(patch->vf); // Discard input size
 80	if (_UPSDecodeLength(patch->vf) != outSize) {
 81		return false;
 82	}
 83
 84	size_t offset = 0;
 85	size_t alreadyRead = 0;
 86	uint8_t* buf = out;
 87	while (alreadyRead < filesize + IN_CHECKSUM) {
 88		offset += _UPSDecodeLength(patch->vf);
 89		uint8_t byte;
 90
 91		while (true) {
 92			if (patch->vf->read(patch->vf, &byte, 1) != 1) {
 93				return false;
 94			}
 95			buf[offset] ^= byte;
 96			++offset;
 97			if (!byte) {
 98				break;
 99			}
100		}
101		alreadyRead = patch->vf->seek(patch->vf, 0, SEEK_CUR);
102	}
103
104	uint32_t goodCrc32;
105	patch->vf->seek(patch->vf, OUT_CHECKSUM, SEEK_END);
106	if (patch->vf->read(patch->vf, &goodCrc32, 4) != 4) {
107		return false;
108	}
109
110	patch->vf->seek(patch->vf, 0, SEEK_SET);
111	if (crc32(out, outSize) != goodCrc32) {
112		return false;
113	}
114	return true;
115}
116
117size_t _UPSDecodeLength(struct VFile* vf) {
118	size_t shift = 1;
119	size_t value = 0;
120	uint8_t byte;
121	while (true) {
122		if (vf->read(vf, &byte, 1) != 1) {
123			break;
124		}
125		value += (byte & 0x7f) * shift;
126		if (byte & 0x80) {
127			break;
128		}
129		shift <<= 7;
130		value += shift;
131	}
132	return value;
133}