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