all repos — mgba @ c8be60f88b651539a224f35054b0856a08d1a76f

mGBA Game Boy Advance Emulator

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

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