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