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