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 <mgba-util/patch/ips.h>
7
8#include <mgba-util/patch.h>
9#include <mgba-util/vfs.h>
10
11static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
12static bool _IPSApplyPatch(struct Patch* patch, const 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, const 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}