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