all repos — mgba @ f71edb1684bab1a11c255c0b72dacae987414c07

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