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