1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.ZipperEntryExtraField = exports.ZipperEntry = exports.Zipper = void 0;
|
7 | var _nodeZlib = require("node:zlib");
|
8 | var _iconEncoder = require("@shockpkg/icon-encoder");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | class ZipperEntryExtraField {
|
20 | |
21 |
|
22 |
|
23 | type = 0;
|
24 |
|
25 | |
26 |
|
27 |
|
28 | data = new Uint8Array(0);
|
29 |
|
30 | |
31 |
|
32 |
|
33 | constructor() {}
|
34 |
|
35 | |
36 |
|
37 |
|
38 |
|
39 |
|
40 | sizeof() {
|
41 | return 4 + this.data.length;
|
42 | }
|
43 |
|
44 | |
45 |
|
46 |
|
47 |
|
48 |
|
49 | encode() {
|
50 | const {
|
51 | data,
|
52 | type
|
53 | } = this;
|
54 | const d = new Uint8Array(this.sizeof());
|
55 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
56 | v.setUint16(0, type, true);
|
57 | v.setUint16(2, data.length, true);
|
58 | d.set(data, 4);
|
59 | return d;
|
60 | }
|
61 |
|
62 | |
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | initInfoZipUnix2Local(uid = 0, gid = 0) {
|
69 | this._initInfoZipUnix2(true, uid, gid);
|
70 | }
|
71 |
|
72 | |
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | initInfoZipUnix2Central(uid = 0, gid = 0) {
|
79 | this._initInfoZipUnix2(false, uid, gid);
|
80 | }
|
81 |
|
82 | |
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | initExtendedTimestampLocal(mtime = null, atime = null, ctime = null) {
|
90 | this._initExtendedTimestamp(true, mtime, atime, ctime);
|
91 | }
|
92 |
|
93 | |
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | initExtendedTimestampCentral(mtime = null, atime = null, ctime = null) {
|
101 | this._initExtendedTimestamp(false, mtime, atime, ctime);
|
102 | }
|
103 |
|
104 | |
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | _initInfoZipUnix2(local, uid, gid) {
|
112 | const d = local ? new Uint8Array(4) : new Uint8Array(0);
|
113 | if (local) {
|
114 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
115 | v.setUint16(0, uid, true);
|
116 | v.setUint16(2, gid, true);
|
117 | }
|
118 |
|
119 |
|
120 | this.type = 0x7855;
|
121 | this.data = d;
|
122 | }
|
123 |
|
124 | |
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | _initExtendedTimestamp(local, mtime, atime, ctime) {
|
133 | let flags = 0;
|
134 | const times = [];
|
135 | [mtime, atime, ctime].forEach((v, i) => {
|
136 | if (v === null) {
|
137 | return;
|
138 | }
|
139 |
|
140 |
|
141 | flags |= 1 << i;
|
142 | if (local || i) {
|
143 | times.push(typeof v === 'number' ? v : Math.round(v.getTime() / 1000));
|
144 | }
|
145 | });
|
146 | const d = new Uint8Array(1 + times.length * 4);
|
147 | let i = 0;
|
148 | d[i++] = flags;
|
149 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
150 | for (const time of times) {
|
151 | v.setUint32(i, time, true);
|
152 | i += 4;
|
153 | }
|
154 |
|
155 |
|
156 | this.type = 0x5455;
|
157 | this.data = d;
|
158 | }
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | exports.ZipperEntryExtraField = ZipperEntryExtraField;
|
165 | class ZipperEntry {
|
166 | |
167 |
|
168 |
|
169 | signatureLocal = 0x4034b50;
|
170 |
|
171 | |
172 |
|
173 |
|
174 | signatureCentral = 0x2014b50;
|
175 |
|
176 | |
177 |
|
178 |
|
179 | extractVersion = 0x14;
|
180 |
|
181 | |
182 |
|
183 |
|
184 | extractHostOS = 0;
|
185 |
|
186 | |
187 |
|
188 |
|
189 | createVersion = 0x14;
|
190 |
|
191 | |
192 |
|
193 |
|
194 | createHostOS = 0;
|
195 |
|
196 | |
197 |
|
198 |
|
199 | flags = 0;
|
200 |
|
201 | |
202 |
|
203 |
|
204 | compression = 0;
|
205 |
|
206 | |
207 |
|
208 |
|
209 | time = 0;
|
210 |
|
211 | |
212 |
|
213 |
|
214 | date = 0;
|
215 |
|
216 | |
217 |
|
218 |
|
219 | crc32 = 0;
|
220 |
|
221 | |
222 |
|
223 |
|
224 | sizeCompressed = 0;
|
225 |
|
226 | |
227 |
|
228 |
|
229 | sizeUncompressed = 0;
|
230 |
|
231 | |
232 |
|
233 |
|
234 | diskNumberStart = 0;
|
235 |
|
236 | |
237 |
|
238 |
|
239 | internalAttributes = 0;
|
240 |
|
241 | |
242 |
|
243 |
|
244 | externalAttributes = 0;
|
245 |
|
246 | |
247 |
|
248 |
|
249 | headerOffsetLocal = 0;
|
250 |
|
251 | |
252 |
|
253 |
|
254 | path = new Uint8Array(0);
|
255 |
|
256 | |
257 |
|
258 |
|
259 | comment = new Uint8Array(0);
|
260 |
|
261 | |
262 |
|
263 |
|
264 | extraFieldsLocal = [];
|
265 |
|
266 | |
267 |
|
268 |
|
269 | extraFieldsCentral = [];
|
270 |
|
271 | |
272 |
|
273 |
|
274 | constructor() {}
|
275 |
|
276 | |
277 |
|
278 |
|
279 |
|
280 |
|
281 | sizeofExtraFieldsLocal() {
|
282 | let r = 0;
|
283 | for (const ef of this.extraFieldsLocal) {
|
284 | r += ef.sizeof();
|
285 | }
|
286 | return r;
|
287 | }
|
288 |
|
289 | |
290 |
|
291 |
|
292 |
|
293 |
|
294 | sizeofLocal() {
|
295 | return 30 + this.path.length + this.sizeofExtraFieldsLocal();
|
296 | }
|
297 |
|
298 | |
299 |
|
300 |
|
301 |
|
302 |
|
303 | sizeofExtraFieldsCentral() {
|
304 | let r = 0;
|
305 | for (const ef of this.extraFieldsCentral) {
|
306 | r += ef.sizeof();
|
307 | }
|
308 | return r;
|
309 | }
|
310 |
|
311 | |
312 |
|
313 |
|
314 |
|
315 |
|
316 | sizeofCentral() {
|
317 | return 46 + this.path.length + this.comment.length + this.sizeofExtraFieldsCentral();
|
318 | }
|
319 |
|
320 | |
321 |
|
322 |
|
323 |
|
324 |
|
325 | createExtraField() {
|
326 | return new ZipperEntryExtraField();
|
327 | }
|
328 |
|
329 | |
330 |
|
331 |
|
332 |
|
333 |
|
334 | setDate(date) {
|
335 | const dosTime = this._dateToDosTime(date);
|
336 | this.date = dosTime.date;
|
337 | this.time = dosTime.time;
|
338 | }
|
339 |
|
340 | |
341 |
|
342 |
|
343 |
|
344 |
|
345 | encodeLocal() {
|
346 | const {
|
347 | path,
|
348 | extraFieldsLocal
|
349 | } = this;
|
350 | const d = new Uint8Array(this.sizeofLocal());
|
351 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
352 | let i = 0;
|
353 | v.setUint32(i, this.signatureLocal, true);
|
354 | i += 4;
|
355 | v.setUint8(i++, this.extractVersion);
|
356 | v.setUint8(i++, this.extractHostOS);
|
357 | v.setUint16(i, this.flags, true);
|
358 | i += 2;
|
359 | v.setUint16(i, this.compression, true);
|
360 | i += 2;
|
361 | v.setUint16(i, this.time, true);
|
362 | i += 2;
|
363 | v.setUint16(i, this.date, true);
|
364 | i += 2;
|
365 | v.setUint32(i, this.crc32, true);
|
366 | i += 4;
|
367 | v.setUint32(i, this.sizeCompressed, true);
|
368 | i += 4;
|
369 | v.setUint32(i, this.sizeUncompressed, true);
|
370 | i += 4;
|
371 | v.setUint16(i, path.length, true);
|
372 | i += 2;
|
373 | v.setUint16(i, this.sizeofExtraFieldsLocal(), true);
|
374 | i += 2;
|
375 | d.set(path, i);
|
376 | i += path.length;
|
377 | for (const ef of extraFieldsLocal) {
|
378 | const e = ef.encode();
|
379 | d.set(e, i);
|
380 | i += e.length;
|
381 | }
|
382 | return d;
|
383 | }
|
384 |
|
385 | |
386 |
|
387 |
|
388 |
|
389 |
|
390 | encodeCentral() {
|
391 | const {
|
392 | path,
|
393 | comment,
|
394 | extraFieldsCentral
|
395 | } = this;
|
396 | const d = new Uint8Array(this.sizeofCentral());
|
397 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
398 | let i = 0;
|
399 | v.setUint32(i, this.signatureCentral, true);
|
400 | i += 4;
|
401 | v.setUint8(i++, this.createVersion);
|
402 | v.setUint8(i++, this.createHostOS);
|
403 | v.setUint8(i++, this.extractVersion);
|
404 | v.setUint8(i++, this.extractHostOS);
|
405 | v.setUint16(i, this.flags, true);
|
406 | i += 2;
|
407 | v.setUint16(i, this.compression, true);
|
408 | i += 2;
|
409 | v.setUint16(i, this.time, true);
|
410 | i += 2;
|
411 | v.setUint16(i, this.date, true);
|
412 | i += 2;
|
413 | v.setUint32(i, this.crc32, true);
|
414 | i += 4;
|
415 | v.setUint32(i, this.sizeCompressed, true);
|
416 | i += 4;
|
417 | v.setUint32(i, this.sizeUncompressed, true);
|
418 | i += 4;
|
419 | v.setUint16(i, path.length, true);
|
420 | i += 2;
|
421 | v.setUint16(i, this.sizeofExtraFieldsCentral(), true);
|
422 | i += 2;
|
423 | v.setUint16(i, comment.length, true);
|
424 | i += 2;
|
425 | v.setUint16(i, this.diskNumberStart, true);
|
426 | i += 2;
|
427 | v.setUint16(i, this.internalAttributes, true);
|
428 | i += 2;
|
429 | v.setUint32(i, this.externalAttributes, true);
|
430 | i += 4;
|
431 | v.setUint32(i, this.headerOffsetLocal, true);
|
432 | i += 4;
|
433 | d.set(path, i);
|
434 | i += path.length;
|
435 | for (const ef of extraFieldsCentral) {
|
436 | const e = ef.encode();
|
437 | d.set(e, i);
|
438 | i += e.length;
|
439 | }
|
440 | d.set(comment, i);
|
441 | return d;
|
442 | }
|
443 |
|
444 | |
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 | async initData(data, compress = null) {
|
452 | this.compression = 0;
|
453 | this.crc32 = 0;
|
454 | this.sizeCompressed = 0;
|
455 | this.sizeUncompressed = 0;
|
456 | if (!data) {
|
457 | return null;
|
458 | }
|
459 | const crc32 = this._crc32(data);
|
460 | if (compress === false) {
|
461 | this.crc32 = crc32;
|
462 | this.sizeCompressed = this.sizeUncompressed = data.length;
|
463 | this.compression = 0;
|
464 | return data;
|
465 | }
|
466 | if (compress === true) {
|
467 | const comp = await this._zlibDeflateRaw(data);
|
468 | this.crc32 = crc32;
|
469 | this.sizeUncompressed = data.length;
|
470 | this.sizeCompressed = comp.length;
|
471 | this.compression = 8;
|
472 | return comp;
|
473 | }
|
474 | const comp = await this._zlibDeflateRaw(data);
|
475 | const r = comp.length < data.length ? comp : data;
|
476 | this.crc32 = crc32;
|
477 | this.sizeUncompressed = data.length;
|
478 | this.sizeCompressed = r.length;
|
479 | this.compression = r === data ? 0 : 8;
|
480 | return r;
|
481 | }
|
482 |
|
483 | |
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 | addExtraFieldsExtendedTimestamp(mtime = null, atime = null, ctime = null) {
|
491 | const efl = this.createExtraField();
|
492 | efl.initExtendedTimestampLocal(mtime, atime, ctime);
|
493 | this.extraFieldsLocal.push(efl);
|
494 | const efc = this.createExtraField();
|
495 | efc.initExtendedTimestampCentral(mtime, atime, ctime);
|
496 | this.extraFieldsCentral.push(efc);
|
497 | }
|
498 |
|
499 | |
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 | addExtraFieldsInfoZipUnix2(uid = 0, gid = 0) {
|
506 | const efl = this.createExtraField();
|
507 | efl.initInfoZipUnix2Local(uid, gid);
|
508 | this.extraFieldsLocal.push(efl);
|
509 | const efc = this.createExtraField();
|
510 | efc.initInfoZipUnix2Central(uid, gid);
|
511 | this.extraFieldsCentral.push(efc);
|
512 | }
|
513 |
|
514 | |
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 | _dateToDosTime(date) {
|
521 | const d = typeof date === 'number' ? new Date(date * 1000) : date;
|
522 | return {
|
523 | date:
|
524 |
|
525 | d.getDate() & 0x1f |
|
526 |
|
527 | (d.getMonth() + 1 & 0xf) << 5 |
|
528 |
|
529 | (d.getFullYear() - 1980 & 0x7f) << 9,
|
530 | time:
|
531 |
|
532 | Math.floor(d.getSeconds() / 2) |
|
533 |
|
534 | (d.getMinutes() & 0x3f) << 5 |
|
535 |
|
536 | (d.getHours() & 0x1f) << 11
|
537 | };
|
538 | }
|
539 |
|
540 | |
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 | _crc32(data) {
|
547 |
|
548 | return (0, _iconEncoder.crc32)(data) >>> 0;
|
549 | }
|
550 |
|
551 | |
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 | async _zlibDeflateRaw(data) {
|
558 | return new Promise((resolve, reject) => {
|
559 | (0, _nodeZlib.deflateRaw)(data, (err, d) => {
|
560 | if (err) {
|
561 | reject(err);
|
562 | return;
|
563 | }
|
564 | resolve(new Uint8Array(d.buffer, d.byteOffset, d.byteLength));
|
565 | });
|
566 | });
|
567 | }
|
568 | }
|
569 |
|
570 |
|
571 |
|
572 |
|
573 | exports.ZipperEntry = ZipperEntry;
|
574 | class Zipper {
|
575 | |
576 |
|
577 |
|
578 | signature = 0x6054b50;
|
579 |
|
580 | |
581 |
|
582 |
|
583 | comment = new Uint8Array(0);
|
584 |
|
585 | |
586 |
|
587 |
|
588 | entries = [];
|
589 |
|
590 | |
591 |
|
592 |
|
593 | _offset = 0;
|
594 |
|
595 | |
596 |
|
597 |
|
598 |
|
599 | |
600 |
|
601 |
|
602 |
|
603 |
|
604 | constructor(output) {
|
605 | this._output = output;
|
606 | }
|
607 |
|
608 | |
609 |
|
610 |
|
611 |
|
612 |
|
613 | createEntry() {
|
614 | return new ZipperEntry();
|
615 | }
|
616 |
|
617 | |
618 |
|
619 |
|
620 |
|
621 |
|
622 |
|
623 | async addEntry(entry, data = null) {
|
624 | const {
|
625 | _offset
|
626 | } = this;
|
627 | const {
|
628 | sizeCompressed
|
629 | } = entry;
|
630 | if (data) {
|
631 | if (data.length !== sizeCompressed) {
|
632 | throw new Error('Data length and compressed size must match');
|
633 | }
|
634 | } else if (sizeCompressed) {
|
635 | throw new Error('Data required when compressed size not zero');
|
636 | }
|
637 | entry.headerOffsetLocal = _offset;
|
638 | this.entries.push(entry);
|
639 | await this._writeOutput(entry.encodeLocal());
|
640 | if (data) {
|
641 | await this._writeOutput(data);
|
642 | }
|
643 | }
|
644 |
|
645 | |
646 |
|
647 |
|
648 | async close() {
|
649 | const {
|
650 | _offset,
|
651 | entries,
|
652 | comment
|
653 | } = this;
|
654 | let size = 0;
|
655 | for (const e of entries) {
|
656 | const d = e.encodeCentral();
|
657 |
|
658 | await this._writeOutput(d);
|
659 | size += d.length;
|
660 | }
|
661 | const d = new Uint8Array(22 + comment.length);
|
662 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
663 | let i = 0;
|
664 | v.setUint32(i, this.signature, true);
|
665 | i += 4;
|
666 | v.setUint16(i, 0, true);
|
667 | i += 2;
|
668 | v.setUint16(i, 0, true);
|
669 | i += 2;
|
670 | v.setUint16(i, entries.length, true);
|
671 | i += 2;
|
672 | v.setUint16(i, entries.length, true);
|
673 | i += 2;
|
674 | v.setUint32(i, size, true);
|
675 | i += 4;
|
676 | v.setUint32(i, _offset, true);
|
677 | i += 4;
|
678 | v.setUint16(i, comment.length, true);
|
679 | i += 2;
|
680 | d.set(comment, i);
|
681 | await this._writeOutput(d);
|
682 | await new Promise((resolve, reject) => {
|
683 | this._output.end(err => {
|
684 | if (err) {
|
685 | reject(err);
|
686 | return;
|
687 | }
|
688 | resolve();
|
689 | });
|
690 | });
|
691 | }
|
692 |
|
693 | |
694 |
|
695 |
|
696 |
|
697 |
|
698 | async _writeOutput(data) {
|
699 | await new Promise((resolve, reject) => {
|
700 | this._output.write(data, err => {
|
701 | if (err) {
|
702 | reject(err);
|
703 | return;
|
704 | }
|
705 | resolve();
|
706 | });
|
707 | });
|
708 | this._offset += data.length;
|
709 | }
|
710 | }
|
711 | exports.Zipper = Zipper;
|
712 |
|
\ | No newline at end of file |