all repos — mgba @ a450b0470ac44fd0714872bc7602b1339e31b175

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