nitro-patcher 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +5 -2
- package/dist/browser.js +1367 -1418
- package/dist/browser.js.map +4 -4
- package/dist/{chunk-GFF67HX4.js → chunk-GLK3K2HY.js} +221 -282
- package/dist/chunk-GLK3K2HY.js.map +1 -0
- package/dist/{chunk-ZY3OSZCA.js → chunk-IGURDH5L.js} +180 -186
- package/dist/chunk-IGURDH5L.js.map +1 -0
- package/dist/cli.js +5 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/nitro-helper.d.ts +78 -78
- package/dist/nitro-helper.js +1 -1
- package/dist/nitro-patch-helper.d.ts +5 -2
- package/dist/nitro-patch-helper.js +4 -2
- package/package.json +6 -2
- package/src/browser/buffer.ts +1 -1
- package/src/browser.ts +16 -12
- package/src/cli.ts +38 -48
- package/src/index.ts +2 -2
- package/src/nitro/banner.ts +28 -31
- package/src/nitro/binary.ts +6 -16
- package/src/nitro/blz.ts +13 -21
- package/src/nitro/crypto.ts +78 -84
- package/src/nitro/filesystem.ts +36 -39
- package/src/nitro/header.ts +58 -71
- package/src/nitro/nds-file.ts +44 -63
- package/src/nitro/twl.ts +59 -100
- package/src/nitro-helper.ts +14 -14
- package/src/nitro-patch-helper.ts +49 -57
- package/src/patch/xdelta.ts +43 -49
- package/src/patch/zip.ts +47 -56
- package/src/runtime/browser.ts +9 -11
- package/src/runtime/node.ts +14 -15
- package/dist/chunk-GFF67HX4.js.map +0 -1
- package/dist/chunk-ZY3OSZCA.js.map +0 -1
|
@@ -5,158 +5,11 @@ import {
|
|
|
5
5
|
hashDigest,
|
|
6
6
|
inflateRawSync,
|
|
7
7
|
readFile
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
|
|
10
|
-
// src/patch/zip.ts
|
|
11
|
-
var fail = (message) => {
|
|
12
|
-
throw new Error(`ZIP: ${message}`);
|
|
13
|
-
};
|
|
14
|
-
var utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
15
|
-
var cp437 = "\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0";
|
|
16
|
-
function decodeUtf8(bytes2) {
|
|
17
|
-
try {
|
|
18
|
-
return utf8.decode(bytes2);
|
|
19
|
-
} catch {
|
|
20
|
-
return fail("invalid UTF-8 filename");
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function boundOption(value, fallback, label) {
|
|
24
|
-
value ??= fallback;
|
|
25
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
26
|
-
throw new RangeError(`${label} must be a non-negative safe integer`);
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
function checkRange(bytes2, offset, length, end = bytes2.length) {
|
|
30
|
-
if (offset < 0 || offset > end || length > end - offset)
|
|
31
|
-
fail(`truncated or out-of-bounds record at ${offset} (length ${length})`);
|
|
32
|
-
}
|
|
33
|
-
function extras(bytes2) {
|
|
34
|
-
const fields = /* @__PURE__ */ new Map();
|
|
35
|
-
let offset = 0;
|
|
36
|
-
while (offset < bytes2.length) {
|
|
37
|
-
checkRange(bytes2, offset, 4);
|
|
38
|
-
const id = bytes2.readUInt16LE(offset), length = bytes2.readUInt16LE(offset + 2);
|
|
39
|
-
offset += 4;
|
|
40
|
-
checkRange(bytes2, offset, length);
|
|
41
|
-
if (id === 1) fail("ZIP64 archives are unsupported");
|
|
42
|
-
fields.set(id, bytes2.subarray(offset, offset + length));
|
|
43
|
-
offset += length;
|
|
44
|
-
}
|
|
45
|
-
return fields;
|
|
46
|
-
}
|
|
47
|
-
function filename(raw, flags, fields) {
|
|
48
|
-
if (flags & 2048) return decodeUtf8(raw);
|
|
49
|
-
const unicode = fields.get(28789);
|
|
50
|
-
if (unicode && unicode.length >= 5 && unicode[0] === 1 && unicode.readUInt32LE(1) === crc32(raw))
|
|
51
|
-
return decodeUtf8(unicode.subarray(5));
|
|
52
|
-
let result = "";
|
|
53
|
-
for (const byte of raw) result += byte < 128 ? String.fromCharCode(byte) : cp437[byte - 128];
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
var readZip = (input, options = {}) => {
|
|
57
|
-
if (!(input instanceof Uint8Array))
|
|
58
|
-
throw new TypeError("ZIP input must be a Buffer or Uint8Array");
|
|
59
|
-
const bytes2 = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
60
|
-
const maxSize = boundOption(
|
|
61
|
-
options.maxUncompressedSize,
|
|
62
|
-
1024 * 1024 * 1024,
|
|
63
|
-
"maxUncompressedSize"
|
|
64
|
-
);
|
|
65
|
-
const maxEntries = boundOption(options.maxEntries, 1e5, "maxEntries");
|
|
66
|
-
let eocd = -1;
|
|
67
|
-
for (let offset2 = bytes2.length - 22; offset2 >= Math.max(0, bytes2.length - 65557); offset2--) {
|
|
68
|
-
if (bytes2.readUInt32LE(offset2) === 101010256 && offset2 + 22 + bytes2.readUInt16LE(offset2 + 20) === bytes2.length) {
|
|
69
|
-
eocd = offset2;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (eocd < 0) fail("end of central directory not found or truncated");
|
|
74
|
-
const disk = bytes2.readUInt16LE(eocd + 4), centralDisk = bytes2.readUInt16LE(eocd + 6);
|
|
75
|
-
const diskEntries = bytes2.readUInt16LE(eocd + 8), count = bytes2.readUInt16LE(eocd + 10);
|
|
76
|
-
const centralSize = bytes2.readUInt32LE(eocd + 12), centralOffset = bytes2.readUInt32LE(eocd + 16);
|
|
77
|
-
if (count === 65535 || diskEntries === 65535 || centralSize === 4294967295 || centralOffset === 4294967295 || eocd >= 20 && bytes2.readUInt32LE(eocd - 20) === 117853008)
|
|
78
|
-
fail("ZIP64 archives are unsupported");
|
|
79
|
-
if (disk || centralDisk || diskEntries !== count)
|
|
80
|
-
fail("split or multiple-disk archives are unsupported");
|
|
81
|
-
if (count > maxEntries) fail(`entry count exceeds limit ${maxEntries}`);
|
|
82
|
-
checkRange(bytes2, centralOffset, centralSize, eocd);
|
|
83
|
-
if (centralOffset + centralSize !== eocd) fail("central directory size/offset mismatch");
|
|
84
|
-
if (count > Math.floor(centralSize / 46)) fail("entry count exceeds central directory size");
|
|
85
|
-
const result = /* @__PURE__ */ new Map();
|
|
86
|
-
const ranges = [];
|
|
87
|
-
let offset = centralOffset, totalSize = 0;
|
|
88
|
-
for (let index = 0; index < count; index++) {
|
|
89
|
-
checkRange(bytes2, offset, 46, eocd);
|
|
90
|
-
if (bytes2.readUInt32LE(offset) !== 33639248) fail("invalid central directory signature");
|
|
91
|
-
const flags = bytes2.readUInt16LE(offset + 8), method = bytes2.readUInt16LE(offset + 10);
|
|
92
|
-
const checksum = bytes2.readUInt32LE(offset + 16), compressedSize = bytes2.readUInt32LE(offset + 20), size = bytes2.readUInt32LE(offset + 24);
|
|
93
|
-
const nameLength = bytes2.readUInt16LE(offset + 28), extraLength = bytes2.readUInt16LE(offset + 30), commentLength = bytes2.readUInt16LE(offset + 32);
|
|
94
|
-
const startDisk = bytes2.readUInt16LE(offset + 34), localOffset = bytes2.readUInt32LE(offset + 42);
|
|
95
|
-
if (size === 4294967295 || compressedSize === 4294967295 || localOffset === 4294967295 || startDisk === 65535)
|
|
96
|
-
fail("ZIP64 entry is unsupported");
|
|
97
|
-
if (startDisk) fail("split or multiple-disk entry is unsupported");
|
|
98
|
-
if (flags & 8257) fail("encrypted entries are unsupported");
|
|
99
|
-
if (flags & ~2062) fail("unsupported general purpose flags");
|
|
100
|
-
if (method !== 0 && method !== 8) fail(`unsupported compression method ${method}`);
|
|
101
|
-
if (size > maxSize - totalSize) fail(`uncompressed size exceeds limit ${maxSize}`);
|
|
102
|
-
totalSize += size;
|
|
103
|
-
checkRange(bytes2, offset + 46, nameLength + extraLength + commentLength, eocd);
|
|
104
|
-
const rawName = bytes2.subarray(offset + 46, offset + 46 + nameLength);
|
|
105
|
-
const fields = extras(
|
|
106
|
-
bytes2.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength)
|
|
107
|
-
);
|
|
108
|
-
const name = filename(rawName, flags, fields).replaceAll("\\", "/").toLowerCase();
|
|
109
|
-
offset += 46 + nameLength + extraLength + commentLength;
|
|
110
|
-
checkRange(bytes2, localOffset, 30, centralOffset);
|
|
111
|
-
if (bytes2.readUInt32LE(localOffset) !== 67324752) fail("invalid local header signature");
|
|
112
|
-
if (bytes2.readUInt16LE(localOffset + 6) !== flags || bytes2.readUInt16LE(localOffset + 8) !== method)
|
|
113
|
-
fail("local header flags/compression method mismatch");
|
|
114
|
-
const localNameLength = bytes2.readUInt16LE(localOffset + 26), localExtraLength = bytes2.readUInt16LE(localOffset + 28);
|
|
115
|
-
const dataOffset = localOffset + 30 + localNameLength + localExtraLength;
|
|
116
|
-
checkRange(bytes2, localOffset + 30, localNameLength + localExtraLength, centralOffset);
|
|
117
|
-
if (!rawName.equals(bytes2.subarray(localOffset + 30, localOffset + 30 + localNameLength)))
|
|
118
|
-
fail("local filename differs from central directory");
|
|
119
|
-
extras(bytes2.subarray(localOffset + 30 + localNameLength, dataOffset));
|
|
120
|
-
if (!(flags & 8) && (bytes2.readUInt32LE(localOffset + 14) !== checksum || bytes2.readUInt32LE(localOffset + 18) !== compressedSize || bytes2.readUInt32LE(localOffset + 22) !== size))
|
|
121
|
-
fail("local header CRC/size mismatch");
|
|
122
|
-
checkRange(bytes2, dataOffset, compressedSize, centralOffset);
|
|
123
|
-
let dataEnd = dataOffset + compressedSize;
|
|
124
|
-
if (flags & 8) {
|
|
125
|
-
checkRange(bytes2, dataEnd, 12, centralOffset);
|
|
126
|
-
const matches = (at) => at + 12 <= centralOffset && bytes2.readUInt32LE(at) === checksum && bytes2.readUInt32LE(at + 4) === compressedSize && bytes2.readUInt32LE(at + 8) === size;
|
|
127
|
-
if (matches(dataEnd)) dataEnd += 12;
|
|
128
|
-
else if (bytes2.readUInt32LE(dataEnd) === 134695760 && matches(dataEnd + 4)) dataEnd += 16;
|
|
129
|
-
else fail("data descriptor CRC/size mismatch");
|
|
130
|
-
}
|
|
131
|
-
ranges.push({ start: localOffset, end: dataEnd });
|
|
132
|
-
const compressed = bytes2.subarray(dataOffset, dataOffset + compressedSize);
|
|
133
|
-
let data;
|
|
134
|
-
if (method === 0) {
|
|
135
|
-
if (compressedSize !== size) fail("stored entry compressed/uncompressed size mismatch");
|
|
136
|
-
data = Buffer.from(compressed);
|
|
137
|
-
} else {
|
|
138
|
-
try {
|
|
139
|
-
data = inflateRawSync(compressed, { maxOutputLength: Math.max(1, size) });
|
|
140
|
-
} catch (error) {
|
|
141
|
-
return fail(
|
|
142
|
-
`cannot inflate entry ${JSON.stringify(name)} within declared size: ${error instanceof Error ? error.message : String(error)}`
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (data.length !== size) fail(`uncompressed size mismatch for ${JSON.stringify(name)}`);
|
|
147
|
-
if (crc32(data) !== checksum) fail(`CRC32 mismatch for ${JSON.stringify(name)}`);
|
|
148
|
-
if (name && !name.endsWith("/")) result.set(name, data);
|
|
149
|
-
}
|
|
150
|
-
if (offset !== eocd) fail("central directory entry count/length mismatch");
|
|
151
|
-
ranges.sort((a, b) => a.start - b.start);
|
|
152
|
-
for (let i = 1; i < ranges.length; i++)
|
|
153
|
-
if (ranges[i].start < ranges[i - 1].end) fail("overlapping local entry ranges");
|
|
154
|
-
return result;
|
|
155
|
-
};
|
|
8
|
+
} from "./chunk-GLK3K2HY.js";
|
|
156
9
|
|
|
157
10
|
// src/patch/xdelta.ts
|
|
158
11
|
var UINT32_MAX = 4294967295;
|
|
159
|
-
var
|
|
12
|
+
var fail = (message) => {
|
|
160
13
|
throw new Error(`VCDIFF: ${message}`);
|
|
161
14
|
};
|
|
162
15
|
var Reader = class {
|
|
@@ -164,14 +17,12 @@ var Reader = class {
|
|
|
164
17
|
this.bytes = bytes2;
|
|
165
18
|
this.label = label;
|
|
166
19
|
}
|
|
167
|
-
bytes;
|
|
168
|
-
label;
|
|
169
20
|
pos = 0;
|
|
170
21
|
get remaining() {
|
|
171
22
|
return this.bytes.length - this.pos;
|
|
172
23
|
}
|
|
173
24
|
byte() {
|
|
174
|
-
if (!this.remaining)
|
|
25
|
+
if (!this.remaining) fail(`truncated ${this.label}`);
|
|
175
26
|
return this.bytes[this.pos++];
|
|
176
27
|
}
|
|
177
28
|
integer() {
|
|
@@ -179,14 +30,13 @@ var Reader = class {
|
|
|
179
30
|
for (let count = 0; count < 5; count++) {
|
|
180
31
|
const byte = this.byte();
|
|
181
32
|
value = value * 128 + (byte & 127);
|
|
182
|
-
if (value > UINT32_MAX)
|
|
33
|
+
if (value > UINT32_MAX) fail(`integer exceeds 32-bit limit in ${this.label}`);
|
|
183
34
|
if (!(byte & 128)) return value;
|
|
184
35
|
}
|
|
185
|
-
return
|
|
36
|
+
return fail(`invalid variable-length integer in ${this.label}`);
|
|
186
37
|
}
|
|
187
38
|
take(size) {
|
|
188
|
-
if (size > this.remaining)
|
|
189
|
-
fail2(`truncated ${this.label}: requested ${size} bytes, ${this.remaining} remain`);
|
|
39
|
+
if (size > this.remaining) fail(`truncated ${this.label}: requested ${size} bytes, ${this.remaining} remain`);
|
|
190
40
|
const result = this.bytes.subarray(this.pos, this.pos + size);
|
|
191
41
|
this.pos += size;
|
|
192
42
|
return result;
|
|
@@ -237,27 +87,26 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
237
87
|
const reader = new Reader(asBuffer(patch, "patch"));
|
|
238
88
|
const maxOutput = limit(options.maxOutputSize, 1024 * 1024 * 1024, "maxOutputSize");
|
|
239
89
|
const maxWindow = limit(options.maxWindowSize, 16 * 1024 * 1024, "maxWindowSize");
|
|
240
|
-
if (!reader.take(3).equals(Buffer.from([214, 195, 196])))
|
|
241
|
-
if (reader.byte() !== 0)
|
|
90
|
+
if (!reader.take(3).equals(Buffer.from([214, 195, 196]))) fail("invalid file signature");
|
|
91
|
+
if (reader.byte() !== 0) fail("unsupported version (expected 0)");
|
|
242
92
|
const header = reader.byte();
|
|
243
|
-
if (header & ~7)
|
|
244
|
-
if (header & 1)
|
|
245
|
-
|
|
246
|
-
if (header & 2) fail2("custom code table is unsupported");
|
|
93
|
+
if (header & ~7) fail("unrecognized header indicator bits");
|
|
94
|
+
if (header & 1) fail("secondary compression is unsupported; recreate the patch using xdelta3 -S none");
|
|
95
|
+
if (header & 2) fail("custom code table is unsupported");
|
|
247
96
|
if (header & 4) reader.take(reader.integer());
|
|
248
97
|
const windows = [];
|
|
249
98
|
let total = 0;
|
|
250
99
|
while (reader.remaining) {
|
|
251
100
|
const indicator = reader.byte();
|
|
252
101
|
if (indicator & ~7 || (indicator & 3) === 3)
|
|
253
|
-
|
|
102
|
+
fail("invalid window indicator: source and target are mutually exclusive");
|
|
254
103
|
let dictionary = Buffer.alloc(0);
|
|
255
104
|
if (indicator & 3) {
|
|
256
105
|
const length = reader.integer();
|
|
257
106
|
const offset = reader.integer();
|
|
258
107
|
const available = indicator & 1 ? sourceBytes.length : total;
|
|
259
108
|
if (offset > available || length > available - offset)
|
|
260
|
-
|
|
109
|
+
fail("source segment exceeds available source/target bounds");
|
|
261
110
|
if (indicator & 1) dictionary = sourceBytes.subarray(offset, offset + length);
|
|
262
111
|
else {
|
|
263
112
|
dictionary = Buffer.allocUnsafe(length);
|
|
@@ -273,20 +122,18 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
273
122
|
const deltaLength = reader.integer();
|
|
274
123
|
const delta = new Reader(reader.take(deltaLength), "delta window");
|
|
275
124
|
const targetLength = delta.integer();
|
|
276
|
-
if (targetLength > maxWindow)
|
|
277
|
-
|
|
278
|
-
if (targetLength >
|
|
279
|
-
if (dictionary.length + targetLength > UINT32_MAX)
|
|
280
|
-
fail2("window address space exceeds 32-bit limit");
|
|
125
|
+
if (targetLength > maxWindow) fail(`window size ${targetLength} exceeds maximum limit ${maxWindow}`);
|
|
126
|
+
if (targetLength > maxOutput - total) fail(`output size exceeds maximum limit ${maxOutput}`);
|
|
127
|
+
if (dictionary.length + targetLength > UINT32_MAX) fail("window address space exceeds 32-bit limit");
|
|
281
128
|
const compressed = delta.byte();
|
|
282
|
-
if (compressed & ~7)
|
|
283
|
-
if (compressed)
|
|
129
|
+
if (compressed & ~7) fail("unrecognized delta indicator bits");
|
|
130
|
+
if (compressed) fail("secondary compression in delta sections is unsupported");
|
|
284
131
|
const dataLength = delta.integer(), instructionLength = delta.integer(), addressLength = delta.integer();
|
|
285
132
|
const checksum = indicator & 4 ? delta.take(4).readUInt32BE() : null;
|
|
286
133
|
const data = new Reader(delta.take(dataLength), "data section");
|
|
287
134
|
const instructions = new Reader(delta.take(instructionLength), "instruction section");
|
|
288
135
|
const addresses = new Reader(delta.take(addressLength), "address section");
|
|
289
|
-
if (delta.remaining)
|
|
136
|
+
if (delta.remaining) fail("delta encoding length does not match section lengths");
|
|
290
137
|
const target = Buffer.allocUnsafe(targetLength);
|
|
291
138
|
const near = new Uint32Array(4), same = new Uint32Array(768);
|
|
292
139
|
let nearIndex = 0, position = 0;
|
|
@@ -294,7 +141,7 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
294
141
|
const operations = table[instructions.byte()];
|
|
295
142
|
for (const { type, size: fixedSize, mode } of operations) {
|
|
296
143
|
const size = fixedSize || instructions.integer();
|
|
297
|
-
if (size > targetLength - position)
|
|
144
|
+
if (size > targetLength - position) fail(`${type} size exceeds target window length`);
|
|
298
145
|
if (type === "ADD") {
|
|
299
146
|
data.take(size).copy(target, position);
|
|
300
147
|
} else if (type === "RUN") {
|
|
@@ -306,8 +153,7 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
306
153
|
else if (mode === 1) address = here - addresses.integer();
|
|
307
154
|
else if (mode < 6) address = near[mode - 2] + addresses.integer();
|
|
308
155
|
else address = same[(mode - 6) * 256 + addresses.byte()];
|
|
309
|
-
if (address < 0 || address >= here)
|
|
310
|
-
fail2(`COPY address ${address} is outside decoded address space ${here}`);
|
|
156
|
+
if (address < 0 || address >= here) fail(`COPY address ${address} is outside decoded address space ${here}`);
|
|
311
157
|
near[nearIndex] = address;
|
|
312
158
|
nearIndex = (nearIndex + 1) % near.length;
|
|
313
159
|
same[address % same.length] = address;
|
|
@@ -319,7 +165,7 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
319
165
|
const from = Math.max(0, address - dictionary.length);
|
|
320
166
|
while (copied < size) {
|
|
321
167
|
const count = Math.min(size - copied, position + copied - from);
|
|
322
|
-
if (count <= 0)
|
|
168
|
+
if (count <= 0) fail("COPY references unavailable target bytes");
|
|
323
169
|
target.copy(target, position + copied, from, from + count);
|
|
324
170
|
copied += count;
|
|
325
171
|
}
|
|
@@ -327,16 +173,161 @@ var decodeXdelta = (source, patch, options = {}) => {
|
|
|
327
173
|
position += size;
|
|
328
174
|
}
|
|
329
175
|
}
|
|
330
|
-
if (position !== targetLength)
|
|
331
|
-
|
|
332
|
-
if (
|
|
333
|
-
if (checksum !== null && adler32(target) !== checksum) fail2("Adler32 checksum mismatch");
|
|
176
|
+
if (position !== targetLength) fail(`target window length mismatch: decoded ${position}, expected ${targetLength}`);
|
|
177
|
+
if (data.remaining || addresses.remaining) fail("unused bytes in data/address section");
|
|
178
|
+
if (checksum !== null && adler32(target) !== checksum) fail("Adler32 checksum mismatch");
|
|
334
179
|
windows.push(target);
|
|
335
180
|
total += targetLength;
|
|
336
181
|
}
|
|
337
182
|
return Buffer.concat(windows, total);
|
|
338
183
|
};
|
|
339
184
|
|
|
185
|
+
// src/patch/zip.ts
|
|
186
|
+
var fail2 = (message) => {
|
|
187
|
+
throw new Error(`ZIP: ${message}`);
|
|
188
|
+
};
|
|
189
|
+
var utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
190
|
+
var cp437 = "\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0";
|
|
191
|
+
function decodeUtf8(bytes2) {
|
|
192
|
+
try {
|
|
193
|
+
return utf8.decode(bytes2);
|
|
194
|
+
} catch {
|
|
195
|
+
return fail2("invalid UTF-8 filename");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function boundOption(value, fallback, label) {
|
|
199
|
+
value ??= fallback;
|
|
200
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${label} must be a non-negative safe integer`);
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
function checkRange(bytes2, offset, length, end = bytes2.length) {
|
|
204
|
+
if (offset < 0 || offset > end || length > end - offset)
|
|
205
|
+
fail2(`truncated or out-of-bounds record at ${offset} (length ${length})`);
|
|
206
|
+
}
|
|
207
|
+
function extras(bytes2) {
|
|
208
|
+
const fields = /* @__PURE__ */ new Map();
|
|
209
|
+
let offset = 0;
|
|
210
|
+
while (offset < bytes2.length) {
|
|
211
|
+
checkRange(bytes2, offset, 4);
|
|
212
|
+
const id = bytes2.readUInt16LE(offset), length = bytes2.readUInt16LE(offset + 2);
|
|
213
|
+
offset += 4;
|
|
214
|
+
checkRange(bytes2, offset, length);
|
|
215
|
+
if (id === 1) fail2("ZIP64 archives are unsupported");
|
|
216
|
+
fields.set(id, bytes2.subarray(offset, offset + length));
|
|
217
|
+
offset += length;
|
|
218
|
+
}
|
|
219
|
+
return fields;
|
|
220
|
+
}
|
|
221
|
+
function filename(raw, flags, fields) {
|
|
222
|
+
if (flags & 2048) return decodeUtf8(raw);
|
|
223
|
+
const unicode = fields.get(28789);
|
|
224
|
+
if (unicode && unicode.length >= 5 && unicode[0] === 1 && unicode.readUInt32LE(1) === crc32(raw))
|
|
225
|
+
return decodeUtf8(unicode.subarray(5));
|
|
226
|
+
let result = "";
|
|
227
|
+
for (const byte of raw) result += byte < 128 ? String.fromCharCode(byte) : cp437[byte - 128];
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
var readZip = (input, options = {}) => {
|
|
231
|
+
if (!(input instanceof Uint8Array)) throw new TypeError("ZIP input must be a Buffer or Uint8Array");
|
|
232
|
+
const bytes2 = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
233
|
+
const maxSize = boundOption(options.maxUncompressedSize, 1024 * 1024 * 1024, "maxUncompressedSize");
|
|
234
|
+
const maxEntries = boundOption(options.maxEntries, 1e5, "maxEntries");
|
|
235
|
+
let eocd = -1;
|
|
236
|
+
for (let offset2 = bytes2.length - 22; offset2 >= Math.max(0, bytes2.length - 65557); offset2--) {
|
|
237
|
+
if (bytes2.readUInt32LE(offset2) === 101010256 && offset2 + 22 + bytes2.readUInt16LE(offset2 + 20) === bytes2.length) {
|
|
238
|
+
eocd = offset2;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (eocd < 0) fail2("end of central directory not found or truncated");
|
|
243
|
+
const disk = bytes2.readUInt16LE(eocd + 4), centralDisk = bytes2.readUInt16LE(eocd + 6);
|
|
244
|
+
const diskEntries = bytes2.readUInt16LE(eocd + 8), count = bytes2.readUInt16LE(eocd + 10);
|
|
245
|
+
const centralSize = bytes2.readUInt32LE(eocd + 12), centralOffset = bytes2.readUInt32LE(eocd + 16);
|
|
246
|
+
if (count === 65535 || diskEntries === 65535 || centralSize === 4294967295 || centralOffset === 4294967295 || eocd >= 20 && bytes2.readUInt32LE(eocd - 20) === 117853008)
|
|
247
|
+
fail2("ZIP64 archives are unsupported");
|
|
248
|
+
if (disk || centralDisk || diskEntries !== count) fail2("split or multiple-disk archives are unsupported");
|
|
249
|
+
if (count > maxEntries) fail2(`entry count exceeds limit ${maxEntries}`);
|
|
250
|
+
checkRange(bytes2, centralOffset, centralSize, eocd);
|
|
251
|
+
if (centralOffset + centralSize !== eocd) fail2("central directory size/offset mismatch");
|
|
252
|
+
if (count > Math.floor(centralSize / 46)) fail2("entry count exceeds central directory size");
|
|
253
|
+
const result = /* @__PURE__ */ new Map();
|
|
254
|
+
const ranges = [];
|
|
255
|
+
let offset = centralOffset, totalSize = 0;
|
|
256
|
+
for (let index = 0; index < count; index++) {
|
|
257
|
+
checkRange(bytes2, offset, 46, eocd);
|
|
258
|
+
if (bytes2.readUInt32LE(offset) !== 33639248) fail2("invalid central directory signature");
|
|
259
|
+
const flags = bytes2.readUInt16LE(offset + 8), method = bytes2.readUInt16LE(offset + 10);
|
|
260
|
+
const checksum = bytes2.readUInt32LE(offset + 16), compressedSize = bytes2.readUInt32LE(offset + 20), size = bytes2.readUInt32LE(offset + 24);
|
|
261
|
+
const nameLength = bytes2.readUInt16LE(offset + 28), extraLength = bytes2.readUInt16LE(offset + 30), commentLength = bytes2.readUInt16LE(offset + 32);
|
|
262
|
+
const startDisk = bytes2.readUInt16LE(offset + 34), localOffset = bytes2.readUInt32LE(offset + 42);
|
|
263
|
+
if (size === 4294967295 || compressedSize === 4294967295 || localOffset === 4294967295 || startDisk === 65535)
|
|
264
|
+
fail2("ZIP64 entry is unsupported");
|
|
265
|
+
if (startDisk) fail2("split or multiple-disk entry is unsupported");
|
|
266
|
+
if (flags & 8257) fail2("encrypted entries are unsupported");
|
|
267
|
+
if (flags & ~2062) fail2("unsupported general purpose flags");
|
|
268
|
+
if (method !== 0 && method !== 8) fail2(`unsupported compression method ${method}`);
|
|
269
|
+
if (size > maxSize - totalSize) fail2(`uncompressed size exceeds limit ${maxSize}`);
|
|
270
|
+
totalSize += size;
|
|
271
|
+
checkRange(bytes2, offset + 46, nameLength + extraLength + commentLength, eocd);
|
|
272
|
+
const rawName = bytes2.subarray(offset + 46, offset + 46 + nameLength);
|
|
273
|
+
const fields = extras(bytes2.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength));
|
|
274
|
+
const name = filename(rawName, flags, fields).replaceAll("\\", "/").toLowerCase();
|
|
275
|
+
offset += 46 + nameLength + extraLength + commentLength;
|
|
276
|
+
checkRange(bytes2, localOffset, 30, centralOffset);
|
|
277
|
+
if (bytes2.readUInt32LE(localOffset) !== 67324752) fail2("invalid local header signature");
|
|
278
|
+
if (bytes2.readUInt16LE(localOffset + 6) !== flags || bytes2.readUInt16LE(localOffset + 8) !== method)
|
|
279
|
+
fail2("local header flags/compression method mismatch");
|
|
280
|
+
const localNameLength = bytes2.readUInt16LE(localOffset + 26), localExtraLength = bytes2.readUInt16LE(localOffset + 28);
|
|
281
|
+
const dataOffset = localOffset + 30 + localNameLength + localExtraLength;
|
|
282
|
+
checkRange(bytes2, localOffset + 30, localNameLength + localExtraLength, centralOffset);
|
|
283
|
+
if (!rawName.equals(bytes2.subarray(localOffset + 30, localOffset + 30 + localNameLength)))
|
|
284
|
+
fail2("local filename differs from central directory");
|
|
285
|
+
extras(bytes2.subarray(localOffset + 30 + localNameLength, dataOffset));
|
|
286
|
+
if (!(flags & 8) && (bytes2.readUInt32LE(localOffset + 14) !== checksum || bytes2.readUInt32LE(localOffset + 18) !== compressedSize || bytes2.readUInt32LE(localOffset + 22) !== size))
|
|
287
|
+
fail2("local header CRC/size mismatch");
|
|
288
|
+
checkRange(bytes2, dataOffset, compressedSize, centralOffset);
|
|
289
|
+
let dataEnd = dataOffset + compressedSize;
|
|
290
|
+
if (flags & 8) {
|
|
291
|
+
checkRange(bytes2, dataEnd, 12, centralOffset);
|
|
292
|
+
const matches = (at) => at + 12 <= centralOffset && bytes2.readUInt32LE(at) === checksum && bytes2.readUInt32LE(at + 4) === compressedSize && bytes2.readUInt32LE(at + 8) === size;
|
|
293
|
+
if (matches(dataEnd)) dataEnd += 12;
|
|
294
|
+
else if (bytes2.readUInt32LE(dataEnd) === 134695760 && matches(dataEnd + 4)) dataEnd += 16;
|
|
295
|
+
else fail2("data descriptor CRC/size mismatch");
|
|
296
|
+
}
|
|
297
|
+
ranges.push({ start: localOffset, end: dataEnd });
|
|
298
|
+
const compressed = bytes2.subarray(dataOffset, dataOffset + compressedSize);
|
|
299
|
+
let data;
|
|
300
|
+
if (method === 0) {
|
|
301
|
+
if (compressedSize !== size) fail2("stored entry compressed/uncompressed size mismatch");
|
|
302
|
+
data = Buffer.from(compressed);
|
|
303
|
+
} else {
|
|
304
|
+
try {
|
|
305
|
+
data = inflateRawSync(compressed, { maxOutputLength: Math.max(1, size) });
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return fail2(
|
|
308
|
+
`cannot inflate entry ${JSON.stringify(name)} within declared size: ${error instanceof Error ? error.message : String(error)}`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (data.length !== size) fail2(`uncompressed size mismatch for ${JSON.stringify(name)}`);
|
|
313
|
+
if (crc32(data) !== checksum) fail2(`CRC32 mismatch for ${JSON.stringify(name)}`);
|
|
314
|
+
if (name && !name.endsWith("/")) result.set(name, data);
|
|
315
|
+
}
|
|
316
|
+
if (offset !== eocd) fail2("central directory entry count/length mismatch");
|
|
317
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
318
|
+
for (let i = 1; i < ranges.length; i++)
|
|
319
|
+
if (ranges[i].start < ranges[i - 1].end) fail2("overlapping local entry ranges");
|
|
320
|
+
return result;
|
|
321
|
+
};
|
|
322
|
+
var extractZipEntry = (input, entryName, options = {}) => {
|
|
323
|
+
if (typeof entryName !== "string" || !entryName) throw new TypeError("ZIP entry name must be a non-empty string");
|
|
324
|
+
const normalized = entryName.replaceAll("\\", "/").toLowerCase();
|
|
325
|
+
if (normalized.endsWith("/")) fail2(`entry ${JSON.stringify(entryName)} is a directory`);
|
|
326
|
+
const entry = readZip(input, options).get(normalized);
|
|
327
|
+
if (entry === void 0) return fail2(`entry ${JSON.stringify(entryName)} not found`);
|
|
328
|
+
return entry;
|
|
329
|
+
};
|
|
330
|
+
|
|
340
331
|
// src/nitro-patch-helper.ts
|
|
341
332
|
var PatchReturnValue = Object.freeze({
|
|
342
333
|
SUCCESS: "SUCCESS",
|
|
@@ -366,9 +357,12 @@ var readMetadata = (files) => {
|
|
|
366
357
|
const value = metadata;
|
|
367
358
|
const result = {};
|
|
368
359
|
for (const field of ["id", "author", "name", "homepage", "version"]) {
|
|
369
|
-
|
|
360
|
+
const fieldValue = value[field];
|
|
361
|
+
if (fieldValue === void 0) continue;
|
|
362
|
+
if (typeof fieldValue === "string") result[field] = fieldValue;
|
|
370
363
|
else invalidMetadataField(field, "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002");
|
|
371
364
|
}
|
|
365
|
+
if (value.isBeta === void 0) return result;
|
|
372
366
|
if (typeof value.isBeta === "boolean") result.isBeta = value.isBeta;
|
|
373
367
|
else invalidMetadataField("isBeta", "\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002");
|
|
374
368
|
return result;
|
|
@@ -399,6 +393,7 @@ var readPatchInfo = (patch, options = {}) => {
|
|
|
399
393
|
const files = readZip(patch, options);
|
|
400
394
|
return { metadata: readMetadata(files), readme: readReadme(files) };
|
|
401
395
|
};
|
|
396
|
+
var extractZipEntry2 = (archive, entryName, options = {}) => extractZipEntry(archive, entryName, options);
|
|
402
397
|
var readPatchMetadataFile = async (patchPath, options = {}) => readPatchMetadata(await readFile(patchPath), options);
|
|
403
398
|
var readPatchReadmeFile = async (patchPath, options = {}) => readPatchReadme(await readFile(patchPath), options);
|
|
404
399
|
var readPatchInfoFile = async (patchPath, options = {}) => readPatchInfo(await readFile(patchPath), options);
|
|
@@ -407,10 +402,7 @@ var patchBuffer = (original, patch, options = {}) => {
|
|
|
407
402
|
const patches = readZip(patch, options);
|
|
408
403
|
const inputMd5 = md5(input);
|
|
409
404
|
const preprocessing = patches.get(`preprocessing/${inputMd5}.xdelta`);
|
|
410
|
-
const rom = new NDSFile(
|
|
411
|
-
preprocessing ? decodeXdelta(input, preprocessing, options) : input,
|
|
412
|
-
options
|
|
413
|
-
);
|
|
405
|
+
const rom = new NDSFile(preprocessing ? decodeXdelta(input, preprocessing, options) : input, options);
|
|
414
406
|
let matched = true;
|
|
415
407
|
const md5File = patches.get("md5.txt");
|
|
416
408
|
if (md5File) {
|
|
@@ -444,6 +436,7 @@ var patchIt = async (originalPath, patchPath, options = {}) => {
|
|
|
444
436
|
return patchBuffer(original, patch, options);
|
|
445
437
|
};
|
|
446
438
|
var PatchHelper = Object.freeze({
|
|
439
|
+
extractZipEntry: extractZipEntry2,
|
|
447
440
|
patchBuffer,
|
|
448
441
|
patchIt,
|
|
449
442
|
readPatchInfo,
|
|
@@ -460,6 +453,7 @@ export {
|
|
|
460
453
|
readPatchMetadata,
|
|
461
454
|
readPatchReadme,
|
|
462
455
|
readPatchInfo,
|
|
456
|
+
extractZipEntry2 as extractZipEntry,
|
|
463
457
|
readPatchMetadataFile,
|
|
464
458
|
readPatchReadmeFile,
|
|
465
459
|
readPatchInfoFile,
|
|
@@ -467,4 +461,4 @@ export {
|
|
|
467
461
|
patchIt,
|
|
468
462
|
PatchHelper
|
|
469
463
|
};
|
|
470
|
-
//# sourceMappingURL=chunk-
|
|
464
|
+
//# sourceMappingURL=chunk-IGURDH5L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/patch/xdelta.ts","../src/patch/zip.ts","../src/nitro-patch-helper.ts"],"sourcesContent":["/** Native RFC 3284 VCDIFF decoder with the xdelta3 Adler32/app-header extensions.\n * Supports the same default-table, uncompressed format as PleOps.XdeltaSharp 1.3.0.\n * Secondary compression and custom code tables are explicitly unsupported there too.\n */\nconst UINT32_MAX = 0xffffffff;\nconst fail = (message: string): never => {\n throw new Error(`VCDIFF: ${message}`);\n};\n\nclass Reader {\n pos = 0;\n constructor(\n readonly bytes: Buffer,\n readonly label = \"patch\",\n ) {}\n get remaining() {\n return this.bytes.length - this.pos;\n }\n byte() {\n if (!this.remaining) fail(`truncated ${this.label}`);\n return this.bytes[this.pos++]!;\n }\n integer(): number {\n let value = 0;\n for (let count = 0; count < 5; count++) {\n const byte = this.byte();\n value = value * 128 + (byte & 127);\n if (value > UINT32_MAX) fail(`integer exceeds 32-bit limit in ${this.label}`);\n if (!(byte & 128)) return value;\n }\n return fail(`invalid variable-length integer in ${this.label}`);\n }\n take(size: number): Buffer {\n if (size > this.remaining) fail(`truncated ${this.label}: requested ${size} bytes, ${this.remaining} remain`);\n const result = this.bytes.subarray(this.pos, this.pos + size);\n this.pos += size;\n return result;\n }\n}\n\n// RFC 3284 section 5.6: (operation, size, COPY address mode), up to two per opcode.\ninterface Operation {\n type: \"ADD\" | \"RUN\" | \"COPY\";\n size: number;\n mode: number;\n}\nconst table: Operation[][] = [];\nconst op = (type: Operation[\"type\"], size: number, mode = 0): Operation => ({ type, size, mode });\ntable.push([op(\"RUN\", 0)]);\nfor (let size = 0; size <= 17; size++) table.push([op(\"ADD\", size)]);\nfor (let mode = 0; mode <= 8; mode++) {\n table.push([op(\"COPY\", 0, mode)]);\n for (let size = 4; size <= 18; size++) table.push([op(\"COPY\", size, mode)]);\n}\nfor (let mode = 0; mode <= 5; mode++) {\n for (let add = 1; add <= 4; add++) {\n for (let copy = 4; copy <= 6; copy++) table.push([op(\"ADD\", add), op(\"COPY\", copy, mode)]);\n }\n}\nfor (let mode = 6; mode <= 8; mode++) {\n for (let add = 1; add <= 4; add++) table.push([op(\"ADD\", add), op(\"COPY\", 4, mode)]);\n}\nfor (let mode = 0; mode <= 8; mode++) table.push([op(\"COPY\", 4, mode), op(\"ADD\", 1)]);\n\nfunction adler32(bytes: Uint8Array): number {\n let a = 1,\n b = 0;\n for (let start = 0; start < bytes.length; start += 5552) {\n const end = Math.min(start + 5552, bytes.length);\n for (let i = start; i < end; i++) {\n a += bytes[i]!;\n b += a;\n }\n a %= 65521;\n b %= 65521;\n }\n return (b * 65536 + a) >>> 0;\n}\n\nfunction asBuffer(value: Uint8Array, name: string): Buffer {\n if (!(value instanceof Uint8Array)) throw new TypeError(`${name} must be a Buffer or Uint8Array`);\n return Buffer.from(value.buffer, value.byteOffset, value.byteLength);\n}\nfunction limit(value: number | undefined, fallback: number, name: string): number {\n value ??= fallback;\n if (!Number.isSafeInteger(value) || value < 0 || value > UINT32_MAX)\n throw new RangeError(`${name} must be an integer between 0 and ${UINT32_MAX}`);\n return value;\n}\n\n/**\n * @param {Buffer|Uint8Array} source Original bytes.\n * @param {Buffer|Uint8Array} patch VCDIFF bytes.\n * @param {{maxOutputSize?: number, maxWindowSize?: number}} [options]\n * @returns {Buffer} Decoded target, only returned after all windows/checksums validate.\n */\nexport interface XdeltaOptions {\n maxOutputSize?: number;\n maxWindowSize?: number;\n}\n\nexport const decodeXdelta = (source: Uint8Array, patch: Uint8Array, options: XdeltaOptions = {}): Buffer => {\n const sourceBytes = asBuffer(source, \"source\");\n const reader = new Reader(asBuffer(patch, \"patch\"));\n const maxOutput = limit(options.maxOutputSize, 1024 * 1024 * 1024, \"maxOutputSize\");\n const maxWindow = limit(options.maxWindowSize, 16 * 1024 * 1024, \"maxWindowSize\");\n if (!reader.take(3).equals(Buffer.from([0xd6, 0xc3, 0xc4]))) fail(\"invalid file signature\");\n if (reader.byte() !== 0) fail(\"unsupported version (expected 0)\");\n const header = reader.byte();\n if (header & ~7) fail(\"unrecognized header indicator bits\");\n if (header & 1) fail(\"secondary compression is unsupported; recreate the patch using xdelta3 -S none\");\n if (header & 2) fail(\"custom code table is unsupported\");\n if (header & 4) reader.take(reader.integer());\n const windows: Buffer[] = [];\n let total = 0;\n\n while (reader.remaining) {\n const indicator = reader.byte();\n if (indicator & ~7 || (indicator & 3) === 3)\n fail(\"invalid window indicator: source and target are mutually exclusive\");\n let dictionary: Buffer = Buffer.alloc(0);\n if (indicator & 3) {\n const length = reader.integer();\n const offset = reader.integer();\n const available = indicator & 1 ? sourceBytes.length : total;\n if (offset > available || length > available - offset)\n fail(\"source segment exceeds available source/target bounds\");\n if (indicator & 1) dictionary = sourceBytes.subarray(offset, offset + length);\n else {\n dictionary = Buffer.allocUnsafe(length);\n let base = 0;\n for (const previous of windows) {\n const start = Math.max(offset, base),\n end = Math.min(offset + length, base + previous.length);\n if (end > start) previous.copy(dictionary, start - offset, start - base, end - base);\n base += previous.length;\n if (base >= offset + length) break;\n }\n }\n }\n const deltaLength = reader.integer();\n const delta = new Reader(reader.take(deltaLength), \"delta window\");\n const targetLength = delta.integer();\n if (targetLength > maxWindow) fail(`window size ${targetLength} exceeds maximum limit ${maxWindow}`);\n if (targetLength > maxOutput - total) fail(`output size exceeds maximum limit ${maxOutput}`);\n if (dictionary.length + targetLength > UINT32_MAX) fail(\"window address space exceeds 32-bit limit\");\n const compressed = delta.byte();\n if (compressed & ~7) fail(\"unrecognized delta indicator bits\");\n if (compressed) fail(\"secondary compression in delta sections is unsupported\");\n const dataLength = delta.integer(),\n instructionLength = delta.integer(),\n addressLength = delta.integer();\n const checksum = indicator & 4 ? delta.take(4).readUInt32BE() : null;\n const data = new Reader(delta.take(dataLength), \"data section\");\n const instructions = new Reader(delta.take(instructionLength), \"instruction section\");\n const addresses = new Reader(delta.take(addressLength), \"address section\");\n if (delta.remaining) fail(\"delta encoding length does not match section lengths\");\n const target = Buffer.allocUnsafe(targetLength);\n const near = new Uint32Array(4),\n same = new Uint32Array(768);\n let nearIndex = 0,\n position = 0;\n while (instructions.remaining) {\n const operations = table[instructions.byte()]!;\n for (const { type, size: fixedSize, mode } of operations) {\n const size = fixedSize || instructions.integer();\n if (size > targetLength - position) fail(`${type} size exceeds target window length`);\n if (type === \"ADD\") {\n data.take(size).copy(target, position);\n } else if (type === \"RUN\") {\n target.fill(data.byte(), position, position + size);\n } else {\n const here = dictionary.length + position;\n let address;\n if (mode === 0) address = addresses.integer();\n else if (mode === 1) address = here - addresses.integer();\n else if (mode < 6) address = near[mode - 2]! + addresses.integer();\n else address = same[(mode - 6) * 256 + addresses.byte()]!;\n if (address < 0 || address >= here) fail(`COPY address ${address} is outside decoded address space ${here}`);\n near[nearIndex] = address;\n nearIndex = (nearIndex + 1) % near.length;\n same[address % same.length] = address;\n let copied = 0;\n if (address < dictionary.length) {\n copied = Math.min(size, dictionary.length - address);\n dictionary.copy(target, position, address, address + copied);\n }\n // Copy only already available bytes per iteration. Doubling handles overlap\n // (including a copy crossing from dictionary to current target) efficiently.\n const from = Math.max(0, address - dictionary.length);\n while (copied < size) {\n const count = Math.min(size - copied, position + copied - from);\n if (count <= 0) fail(\"COPY references unavailable target bytes\");\n target.copy(target, position + copied, from, from + count);\n copied += count;\n }\n }\n position += size;\n }\n }\n if (position !== targetLength) fail(`target window length mismatch: decoded ${position}, expected ${targetLength}`);\n if (data.remaining || addresses.remaining) fail(\"unused bytes in data/address section\");\n if (checksum !== null && adler32(target) !== checksum) fail(\"Adler32 checksum mismatch\");\n windows.push(target);\n total += targetLength;\n }\n return Buffer.concat(windows, total);\n};\n","import { inflateRawSync } from \"#nitro-runtime\";\n\nimport { crc32 } from \"../nitro/crc\";\n\n/** Resource limits apply to every central-directory entry, including duplicates. */\nexport interface ZipOptions {\n maxUncompressedSize?: number;\n maxEntries?: number;\n}\n\nconst fail = (message: string): never => {\n throw new Error(`ZIP: ${message}`);\n};\nconst utf8 = new TextDecoder(\"utf-8\", { fatal: true });\n// IBM code page 437, byte values 0x80..0xff. ASCII bytes retain their literal values.\nconst cp437 =\n \"ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ \";\n\nfunction decodeUtf8(bytes: Uint8Array): string {\n try {\n return utf8.decode(bytes);\n } catch {\n return fail(\"invalid UTF-8 filename\");\n }\n}\nfunction boundOption(value: number | undefined, fallback: number, label: string): number {\n value ??= fallback;\n if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${label} must be a non-negative safe integer`);\n return value;\n}\nfunction checkRange(bytes: Buffer, offset: number, length: number, end = bytes.length): void {\n if (offset < 0 || offset > end || length > end - offset)\n fail(`truncated or out-of-bounds record at ${offset} (length ${length})`);\n}\nfunction extras(bytes: Buffer): Map<number, Buffer> {\n const fields = new Map<number, Buffer>();\n let offset = 0;\n while (offset < bytes.length) {\n checkRange(bytes, offset, 4);\n const id = bytes.readUInt16LE(offset),\n length = bytes.readUInt16LE(offset + 2);\n offset += 4;\n checkRange(bytes, offset, length);\n if (id === 1) fail(\"ZIP64 archives are unsupported\");\n fields.set(id, bytes.subarray(offset, offset + length));\n offset += length;\n }\n return fields;\n}\nfunction filename(raw: Buffer, flags: number, fields: Map<number, Buffer>): string {\n if (flags & 0x800) return decodeUtf8(raw);\n const unicode = fields.get(0x7075);\n if (unicode && unicode.length >= 5 && unicode[0] === 1 && unicode.readUInt32LE(1) === crc32(raw))\n return decodeUtf8(unicode.subarray(5));\n let result = \"\";\n for (const byte of raw) result += byte < 128 ? String.fromCharCode(byte) : cp437[byte - 128]!;\n return result;\n}\n\n/**\n * Read stored/deflated, single-disk ZIPs entirely in memory. Central-directory\n * sizes support streamed entries with data descriptors. Paths match PatchHelper:\n * backslashes become slashes, names are lowercase, and the final duplicate wins.\n * ZIP64, encryption, split archives and other compression methods fail explicitly.\n * Format reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT\n */\nexport const readZip = (input: Uint8Array, options: ZipOptions = {}): Map<string, Buffer> => {\n if (!(input instanceof Uint8Array)) throw new TypeError(\"ZIP input must be a Buffer or Uint8Array\");\n const bytes = Buffer.from(input.buffer, input.byteOffset, input.byteLength);\n const maxSize = boundOption(options.maxUncompressedSize, 1024 * 1024 * 1024, \"maxUncompressedSize\");\n const maxEntries = boundOption(options.maxEntries, 100000, \"maxEntries\");\n let eocd = -1;\n for (let offset = bytes.length - 22; offset >= Math.max(0, bytes.length - 65557); offset--) {\n if (bytes.readUInt32LE(offset) === 0x06054b50 && offset + 22 + bytes.readUInt16LE(offset + 20) === bytes.length) {\n eocd = offset;\n break;\n }\n }\n if (eocd < 0) fail(\"end of central directory not found or truncated\");\n const disk = bytes.readUInt16LE(eocd + 4),\n centralDisk = bytes.readUInt16LE(eocd + 6);\n const diskEntries = bytes.readUInt16LE(eocd + 8),\n count = bytes.readUInt16LE(eocd + 10);\n const centralSize = bytes.readUInt32LE(eocd + 12),\n centralOffset = bytes.readUInt32LE(eocd + 16);\n if (\n count === 0xffff ||\n diskEntries === 0xffff ||\n centralSize === 0xffffffff ||\n centralOffset === 0xffffffff ||\n (eocd >= 20 && bytes.readUInt32LE(eocd - 20) === 0x07064b50)\n )\n fail(\"ZIP64 archives are unsupported\");\n if (disk || centralDisk || diskEntries !== count) fail(\"split or multiple-disk archives are unsupported\");\n if (count > maxEntries) fail(`entry count exceeds limit ${maxEntries}`);\n checkRange(bytes, centralOffset, centralSize, eocd);\n if (centralOffset + centralSize !== eocd) fail(\"central directory size/offset mismatch\");\n if (count > Math.floor(centralSize / 46)) fail(\"entry count exceeds central directory size\");\n const result = new Map<string, Buffer>();\n const ranges: { start: number; end: number }[] = [];\n let offset = centralOffset,\n totalSize = 0;\n for (let index = 0; index < count; index++) {\n checkRange(bytes, offset, 46, eocd);\n if (bytes.readUInt32LE(offset) !== 0x02014b50) fail(\"invalid central directory signature\");\n const flags = bytes.readUInt16LE(offset + 8),\n method = bytes.readUInt16LE(offset + 10);\n const checksum = bytes.readUInt32LE(offset + 16),\n compressedSize = bytes.readUInt32LE(offset + 20),\n size = bytes.readUInt32LE(offset + 24);\n const nameLength = bytes.readUInt16LE(offset + 28),\n extraLength = bytes.readUInt16LE(offset + 30),\n commentLength = bytes.readUInt16LE(offset + 32);\n const startDisk = bytes.readUInt16LE(offset + 34),\n localOffset = bytes.readUInt32LE(offset + 42);\n if (size === 0xffffffff || compressedSize === 0xffffffff || localOffset === 0xffffffff || startDisk === 0xffff)\n fail(\"ZIP64 entry is unsupported\");\n if (startDisk) fail(\"split or multiple-disk entry is unsupported\");\n if (flags & 0x2041) fail(\"encrypted entries are unsupported\");\n if (flags & ~0x80e) fail(\"unsupported general purpose flags\");\n if (method !== 0 && method !== 8) fail(`unsupported compression method ${method}`);\n if (size > maxSize - totalSize) fail(`uncompressed size exceeds limit ${maxSize}`);\n totalSize += size;\n checkRange(bytes, offset + 46, nameLength + extraLength + commentLength, eocd);\n const rawName = bytes.subarray(offset + 46, offset + 46 + nameLength);\n const fields = extras(bytes.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength));\n const name = filename(rawName, flags, fields).replaceAll(\"\\\\\", \"/\").toLowerCase();\n offset += 46 + nameLength + extraLength + commentLength;\n\n checkRange(bytes, localOffset, 30, centralOffset);\n if (bytes.readUInt32LE(localOffset) !== 0x04034b50) fail(\"invalid local header signature\");\n if (bytes.readUInt16LE(localOffset + 6) !== flags || bytes.readUInt16LE(localOffset + 8) !== method)\n fail(\"local header flags/compression method mismatch\");\n const localNameLength = bytes.readUInt16LE(localOffset + 26),\n localExtraLength = bytes.readUInt16LE(localOffset + 28);\n const dataOffset = localOffset + 30 + localNameLength + localExtraLength;\n checkRange(bytes, localOffset + 30, localNameLength + localExtraLength, centralOffset);\n if (!rawName.equals(bytes.subarray(localOffset + 30, localOffset + 30 + localNameLength)))\n fail(\"local filename differs from central directory\");\n extras(bytes.subarray(localOffset + 30 + localNameLength, dataOffset));\n if (\n !(flags & 8) &&\n (bytes.readUInt32LE(localOffset + 14) !== checksum ||\n bytes.readUInt32LE(localOffset + 18) !== compressedSize ||\n bytes.readUInt32LE(localOffset + 22) !== size)\n )\n fail(\"local header CRC/size mismatch\");\n checkRange(bytes, dataOffset, compressedSize, centralOffset);\n let dataEnd = dataOffset + compressedSize;\n if (flags & 8) {\n // A descriptor may omit its signature. Use the CRC and both sizes to\n // disambiguate the rare case of an unsigned CRC equal to the signature.\n checkRange(bytes, dataEnd, 12, centralOffset);\n const matches = (at: number): boolean =>\n at + 12 <= centralOffset &&\n bytes.readUInt32LE(at) === checksum &&\n bytes.readUInt32LE(at + 4) === compressedSize &&\n bytes.readUInt32LE(at + 8) === size;\n if (matches(dataEnd)) dataEnd += 12;\n else if (bytes.readUInt32LE(dataEnd) === 0x08074b50 && matches(dataEnd + 4)) dataEnd += 16;\n else fail(\"data descriptor CRC/size mismatch\");\n }\n ranges.push({ start: localOffset, end: dataEnd });\n const compressed = bytes.subarray(dataOffset, dataOffset + compressedSize);\n let data: Buffer;\n if (method === 0) {\n if (compressedSize !== size) fail(\"stored entry compressed/uncompressed size mismatch\");\n data = Buffer.from(compressed);\n } else {\n try {\n data = inflateRawSync(compressed, { maxOutputLength: Math.max(1, size) });\n } catch (error) {\n return fail(\n `cannot inflate entry ${JSON.stringify(name)} within declared size: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n if (data.length !== size) fail(`uncompressed size mismatch for ${JSON.stringify(name)}`);\n if (crc32(data) !== checksum) fail(`CRC32 mismatch for ${JSON.stringify(name)}`);\n if (name && !name.endsWith(\"/\")) result.set(name, data);\n }\n if (offset !== eocd) fail(\"central directory entry count/length mismatch\");\n ranges.sort((a, b) => a.start - b.start);\n for (let i = 1; i < ranges.length; i++)\n if (ranges[i]!.start < ranges[i - 1]!.end) fail(\"overlapping local entry ranges\");\n return result;\n};\n\n/** Extract one file using the same normalized, case-insensitive paths as readZip. */\nexport const extractZipEntry = (input: Uint8Array, entryName: string, options: ZipOptions = {}): Buffer => {\n if (typeof entryName !== \"string\" || !entryName) throw new TypeError(\"ZIP entry name must be a non-empty string\");\n const normalized = entryName.replaceAll(\"\\\\\", \"/\").toLowerCase();\n if (normalized.endsWith(\"/\")) fail(`entry ${JSON.stringify(entryName)} is a directory`);\n const entry = readZip(input, options).get(normalized);\n if (entry === undefined) return fail(`entry ${JSON.stringify(entryName)} not found`);\n return entry;\n};\n","// Native TypeScript port of NitroPatchHelper/PatchHelper.cs, GPL-3.0.\nimport { hashDigest, readFile } from \"#nitro-runtime\";\n\nimport { bytes } from \"./nitro/binary\";\nimport { NDSFile } from \"./nitro/nds-file\";\nimport { decodeXdelta } from \"./patch/xdelta\";\nimport { extractZipEntry as extractEntry, readZip } from \"./patch/zip\";\n\nexport const PatchReturnValue = Object.freeze({\n SUCCESS: \"SUCCESS\",\n MD5_MISMATCH: \"MD5_MISMATCH\",\n} as const);\nexport type PatchReturnValue = (typeof PatchReturnValue)[keyof typeof PatchReturnValue];\n\nexport interface PatchResult {\n /** Generated ROM bytes. Also returned when the MD5 does not match, as in the original helper. */\n buffer: Buffer;\n returnValue: PatchReturnValue;\n /** MD5 of the original input, before preprocessing. */\n inputMd5: string;\n outputMd5: string;\n}\n\nexport interface PatchOptions {\n /** Maximum generated ROM/Xdelta target size in bytes, default 1 GiB. */\n maxOutputSize?: number;\n /** Maximum target size of an individual VCDIFF window, default 16 MiB. */\n maxWindowSize?: number;\n /** Maximum total uncompressed ZIP bytes, default 1 GiB. */\n maxUncompressedSize?: number;\n /** Maximum ZIP entry count. */\n maxEntries?: number;\n}\n\nexport interface PatchMetadata {\n id?: string;\n author?: string;\n name?: string;\n homepage?: string;\n version?: string;\n isBeta?: boolean;\n}\n\nexport interface PatchReadme {\n format: \"markdown\" | \"plaintext\";\n content: string;\n}\n\nexport interface PatchInfo {\n metadata: PatchMetadata | null;\n readme: PatchReadme | null;\n}\n\nconst md5 = (data: Uint8Array): string => hashDigest(\"md5\", data).toString(\"hex\");\nconst invalidMetadata = (message: string): null => {\n console.warn(`忽略 metadata.json:${message}`);\n return null;\n};\nconst invalidMetadataField = (field: keyof PatchMetadata, message: string): void => {\n console.warn(`忽略 metadata.json 的 ${field}:${message}`);\n};\n\nconst readMetadata = (files: Map<string, Buffer>): PatchMetadata | null => {\n const file = files.get(\"metadata.json\");\n if (!file) return null;\n\n let metadata: unknown;\n try {\n metadata = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(file));\n } catch (error) {\n return invalidMetadata(\n `格式错误,可能是因为补丁包已损坏:${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (!metadata || typeof metadata !== \"object\" || Array.isArray(metadata))\n return invalidMetadata(\"根节点必须是对象。\");\n\n const value = metadata as Record<string, unknown>;\n const result: PatchMetadata = {};\n for (const field of [\"id\", \"author\", \"name\", \"homepage\", \"version\"] as const) {\n const fieldValue = value[field];\n if (fieldValue === undefined) continue;\n if (typeof fieldValue === \"string\") result[field] = fieldValue;\n else invalidMetadataField(field, \"必须是字符串。\");\n }\n if (value.isBeta === undefined) return result;\n if (typeof value.isBeta === \"boolean\") result.isBeta = value.isBeta;\n else invalidMetadataField(\"isBeta\", \"必须是布尔值。\");\n\n return result;\n};\n\nconst readReadme = (files: Map<string, Buffer>): PatchReadme | null => {\n for (const [filename, format] of [\n [\"README.md\", \"markdown\"],\n [\"README.txt\", \"plaintext\"],\n ] as const) {\n const file = files.get(filename.toLowerCase());\n if (!file) continue;\n try {\n return {\n format,\n content: new TextDecoder(\"utf-8\", { fatal: true }).decode(file),\n };\n } catch (error) {\n console.warn(\n `忽略 ${filename}:不是有效的 UTF-8 文本:${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n return null;\n};\n\n/** Read and validate metadata.json from a NitroPatcher ZIP package. */\nexport const readPatchMetadata = (patch: Uint8Array, options: PatchOptions = {}): PatchMetadata | null =>\n readMetadata(readZip(patch, options));\n\n/** Read a root README.md or README.txt from a NitroPatcher ZIP package. */\nexport const readPatchReadme = (patch: Uint8Array, options: PatchOptions = {}): PatchReadme | null =>\n readReadme(readZip(patch, options));\n\n/** Read metadata and README content while parsing the patch package only once. */\nexport const readPatchInfo = (patch: Uint8Array, options: PatchOptions = {}): PatchInfo => {\n const files = readZip(patch, options);\n return { metadata: readMetadata(files), readme: readReadme(files) };\n};\n\n/** Extract one file from a ZIP archive entirely in memory. */\nexport const extractZipEntry = (archive: Uint8Array, entryName: string, options: PatchOptions = {}): Buffer =>\n extractEntry(archive, entryName, options);\n\n/** Read metadata.json from a NitroPatcher ZIP package on disk. */\nexport const readPatchMetadataFile = async (\n patchPath: string,\n options: PatchOptions = {},\n): Promise<PatchMetadata | null> => readPatchMetadata(await readFile(patchPath), options);\n\n/** Read a patch README from disk. */\nexport const readPatchReadmeFile = async (patchPath: string, options: PatchOptions = {}): Promise<PatchReadme | null> =>\n readPatchReadme(await readFile(patchPath), options);\n\n/** Read patch metadata and README content from disk. */\nexport const readPatchInfoFile = async (patchPath: string, options: PatchOptions = {}): Promise<PatchInfo> =>\n readPatchInfo(await readFile(patchPath), options);\n\n/** Apply an existing NitroPatcher ZIP package entirely in memory. */\nexport const patchBuffer = (original: Uint8Array, patch: Uint8Array, options: PatchOptions = {}): PatchResult => {\n const input = bytes(original, \"original ROM\");\n const patches = readZip(patch, options);\n const inputMd5 = md5(input);\n const preprocessing = patches.get(`preprocessing/${inputMd5}.xdelta`);\n const rom = new NDSFile(preprocessing ? decodeXdelta(input, preprocessing, options) : input, options);\n let matched = true;\n const md5File = patches.get(\"md5.txt\");\n if (md5File) {\n matched = false;\n for (const line of md5File.toString(\"utf8\").split(\"\\n\")) {\n const expected = line.trim();\n if (!expected || expected.startsWith(\"#\")) continue;\n // Keep the original format contract: exactly 32 characters, case-insensitive comparison.\n if (expected.length !== 32) throw new Error(\"md5.txt 格式错误,可能是因为补丁包已损坏。\");\n if (expected.toLowerCase() === inputMd5) {\n matched = true;\n break;\n }\n }\n }\n for (const { path } of rom.listFiles()) {\n const normalized = path.replaceAll(\"\\\\\", \"/\").toLowerCase();\n const delta = patches.get(`xdelta/${normalized}`);\n const replacement = delta ? decodeXdelta(rom.getFile(path), delta, options) : patches.get(normalized);\n if (replacement) rom.replaceFile(path, replacement);\n }\n const buffer = rom.toBuffer();\n return {\n buffer,\n returnValue: matched ? PatchReturnValue.SUCCESS : PatchReturnValue.MD5_MISMATCH,\n inputMd5,\n outputMd5: md5(buffer),\n };\n};\n\n/** Read input files and apply the patch. Does not write an output file. */\nexport const patchIt = async (\n originalPath: string,\n patchPath: string,\n options: PatchOptions = {},\n): Promise<PatchResult> => {\n const [original, patch] = await Promise.all([readFile(originalPath), readFile(patchPath)]);\n return patchBuffer(original, patch, options);\n};\n\n/** Namespaced counterpart of the original C# helper, sharing the same typed functions. */\nexport const PatchHelper = Object.freeze({\n extractZipEntry,\n patchBuffer,\n patchIt,\n readPatchInfo,\n readPatchInfoFile,\n readPatchMetadata,\n readPatchMetadataFile,\n readPatchReadme,\n readPatchReadmeFile,\n});\nexport { decodeXdelta } from \"./patch/xdelta\";\nexport type { XdeltaOptions } from \"./patch/xdelta\";\n"],"mappings":"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","names":["bytes","fail","bytes","offset","filename","extractZipEntry"]}
|
package/dist/cli.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
patchBuffer,
|
|
4
4
|
readPatchInfo
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-IGURDH5L.js";
|
|
6
|
+
import "./chunk-GLK3K2HY.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
|
+
import { render } from "markdansi";
|
|
9
10
|
import { mkdtemp, readFile, realpath, rename, rm, stat, writeFile } from "fs/promises";
|
|
10
11
|
import { basename, dirname, join, resolve } from "path";
|
|
11
12
|
import { parseArgs } from "util";
|
|
12
|
-
import { render } from "markdansi";
|
|
13
13
|
var help = `nitro-patcher \u2014 \u539F\u751F Node.js NDS ROM \u8865\u4E01\u5DE5\u5177
|
|
14
14
|
|
|
15
15
|
\u7528\u6CD5\uFF1A
|
|
@@ -78,9 +78,7 @@ var main = async () => {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
if (values.version) {
|
|
81
|
-
const metadata = JSON.parse(
|
|
82
|
-
await readFile(new URL("../package.json", import.meta.url), "utf8")
|
|
83
|
-
);
|
|
81
|
+
const metadata = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
84
82
|
if (!metadata || typeof metadata !== "object" || !("version" in metadata) || typeof metadata.version !== "string")
|
|
85
83
|
throw new Error("Invalid package version metadata");
|
|
86
84
|
console.log(metadata.version);
|
|
@@ -142,7 +140,7 @@ ${metadata}`);
|
|
|
142
140
|
}) : content;
|
|
143
141
|
sections.push(`\u8865\u4E01\u8BF4\u660E\uFF1A
|
|
144
142
|
|
|
145
|
-
${rendered.
|
|
143
|
+
${rendered.trim()}`);
|
|
146
144
|
}
|
|
147
145
|
if (dryRun) sections.push("\u8BD5\u8FD0\u884C\u5B8C\u6210\uFF0C\u672A\u5199\u5165\u8F93\u51FA ROM\u3002");
|
|
148
146
|
sections.push(`${returnValue}
|