all repos — mgba @ e17e4fd19003209ff9db1f0ac1394e463c7b4a47

mGBA Game Boy Advance Emulator

src/util/patch-ips.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/patch.h"
 9#include "util/vfs.h"
10
11static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
12static bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
13
14bool loadPatchIPS(struct Patch* patch) {
15	patch->vf->seek(patch->vf, 0, SEEK_SET);
16
17	char buffer[5];
18	if (patch->vf->read(patch->vf, buffer, 5) != 5) {
19		return false;
20	}
21
22	if (memcmp(buffer, "PATCH", 5) != 0) {
23		return false;
24	}
25
26	patch->vf->seek(patch->vf, -3, SEEK_END);
27	if (patch->vf->read(patch->vf, buffer, 3) != 3) {
28		return false;
29	}
30
31	if (memcmp(buffer, "EOF", 3) != 0) {
32		return false;
33	}
34
35	patch->outputSize = _IPSOutputSize;
36	patch->applyPatch = _IPSApplyPatch;
37	return true;
38}
39
40size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
41	UNUSED(patch);
42	UNUSED(inSize);
43	return 16 * 1024 * 1024; // IPS patches can grow up to 16MiB, but not beyond
44}
45
46bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
47	if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
48		return false;
49	}
50	memcpy(out, in, inSize > outSize ? outSize : inSize);
51	uint8_t* buf = out;
52
53	while (true) {
54		uint32_t offset = 0;
55		uint16_t size = 0;
56
57		if (patch->vf->read(patch->vf, &offset, 3) != 3) {
58			return false;
59		}
60
61		if (offset == 0x464F45) {
62			return true;
63		}
64
65		offset = (offset >> 16) | (offset & 0xFF00) | ((offset << 16) & 0xFF0000);
66		if (patch->vf->read(patch->vf, &size, 2) != 2) {
67			return false;
68		}
69		if (!size) {
70			// RLE chunk
71			if (patch->vf->read(patch->vf, &size, 2) != 2) {
72				return false;
73			}
74			size = (size >> 8) | (size << 8);
75			uint8_t byte;
76			if (patch->vf->read(patch->vf, &byte, 1) != 1) {
77				return false;
78			}
79			if (offset + size > outSize) {
80				return false;
81			}
82			memset(&buf[offset], byte, size);
83		} else {
84			size = (size >> 8) | (size << 8);
85			if (offset + size > outSize) {
86				return false;
87			}
88			if (patch->vf->read(patch->vf, &buf[offset], size) != size) {
89				return false;
90			}
91		}
92	}
93}