@loaders.gl/zip 4.0.4 → 4.1.0-alpha.10

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.
Files changed (68) hide show
  1. package/dist/dist.dev.js +434 -27
  2. package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
  3. package/dist/filesystems/zip-filesystem.js.map +1 -1
  4. package/dist/hash-file-utility.d.ts +6 -0
  5. package/dist/hash-file-utility.d.ts.map +1 -1
  6. package/dist/hash-file-utility.js +22 -0
  7. package/dist/hash-file-utility.js.map +1 -1
  8. package/dist/index.cjs +462 -32
  9. package/dist/index.d.ts +4 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/lib/tar/header.d.ts.map +1 -1
  14. package/dist/lib/tar/header.js.map +1 -1
  15. package/dist/lib/tar/tar.d.ts.map +1 -1
  16. package/dist/lib/tar/tar.js.map +1 -1
  17. package/dist/lib/tar/types.d.ts.map +1 -1
  18. package/dist/lib/tar/types.js.map +1 -1
  19. package/dist/lib/tar/utils.d.ts.map +1 -1
  20. package/dist/lib/tar/utils.js.map +1 -1
  21. package/dist/parse-zip/cd-file-header.d.ts +18 -0
  22. package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
  23. package/dist/parse-zip/cd-file-header.js +101 -1
  24. package/dist/parse-zip/cd-file-header.js.map +1 -1
  25. package/dist/parse-zip/end-of-central-directory.d.ts +19 -0
  26. package/dist/parse-zip/end-of-central-directory.d.ts.map +1 -1
  27. package/dist/parse-zip/end-of-central-directory.js +41 -8
  28. package/dist/parse-zip/end-of-central-directory.js.map +1 -1
  29. package/dist/parse-zip/local-file-header.d.ts +16 -0
  30. package/dist/parse-zip/local-file-header.d.ts.map +1 -1
  31. package/dist/parse-zip/local-file-header.js +73 -1
  32. package/dist/parse-zip/local-file-header.js.map +1 -1
  33. package/dist/parse-zip/search-from-the-end.d.ts.map +1 -1
  34. package/dist/parse-zip/search-from-the-end.js.map +1 -1
  35. package/dist/parse-zip/zip-compozition.d.ts +8 -0
  36. package/dist/parse-zip/zip-compozition.d.ts.map +1 -0
  37. package/dist/parse-zip/zip-compozition.js +43 -0
  38. package/dist/parse-zip/zip-compozition.js.map +1 -0
  39. package/dist/parse-zip/zip64-info-generation.d.ts +24 -0
  40. package/dist/parse-zip/zip64-info-generation.d.ts.map +1 -0
  41. package/dist/parse-zip/zip64-info-generation.js +50 -0
  42. package/dist/parse-zip/zip64-info-generation.js.map +1 -0
  43. package/dist/tar-builder.d.ts.map +1 -1
  44. package/dist/tar-builder.js.map +1 -1
  45. package/dist/zip-loader.d.ts.map +1 -1
  46. package/dist/zip-loader.js +1 -1
  47. package/dist/zip-loader.js.map +1 -1
  48. package/dist/zip-writer.d.ts +2 -2
  49. package/dist/zip-writer.d.ts.map +1 -1
  50. package/dist/zip-writer.js +22 -7
  51. package/dist/zip-writer.js.map +1 -1
  52. package/package.json +7 -7
  53. package/src/filesystems/zip-filesystem.ts +2 -1
  54. package/src/hash-file-utility.ts +52 -2
  55. package/src/index.ts +8 -4
  56. package/src/lib/tar/header.ts +2 -1
  57. package/src/lib/tar/tar.ts +2 -1
  58. package/src/lib/tar/types.ts +2 -1
  59. package/src/lib/tar/utils.ts +2 -1
  60. package/src/parse-zip/cd-file-header.ts +185 -2
  61. package/src/parse-zip/end-of-central-directory.ts +99 -9
  62. package/src/parse-zip/local-file-header.ts +128 -2
  63. package/src/parse-zip/search-from-the-end.ts +2 -1
  64. package/src/parse-zip/zip-compozition.ts +113 -0
  65. package/src/parse-zip/zip64-info-generation.ts +106 -0
  66. package/src/tar-builder.ts +2 -1
  67. package/src/zip-loader.ts +2 -1
  68. package/src/zip-writer.ts +24 -10
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  // This file is derived from the tar-js code base under MIT license
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  /**
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  // This file is derived from the tar-js code base under MIT license
@@ -1,9 +1,11 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
- import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils';
5
+ import {FileProvider, compareArrayBuffers, concatenateArrayBuffers} from '@loaders.gl/loader-utils';
5
6
  import {parseEoCDRecord} from './end-of-central-directory';
6
7
  import {ZipSignature} from './search-from-the-end';
8
+ import {createZip64Info, setFieldToNumber} from './zip64-info-generation';
7
9
 
8
10
  /**
9
11
  * zip central directory file header info
@@ -188,3 +190,184 @@ const findExpectedData = (zip64data: Zip64Data): {length: number; name: string}[
188
190
 
189
191
  return zip64dataList;
190
192
  };
193
+
194
+ /** info that can be placed into cd header */
195
+ type GenerateCDOptions = {
196
+ /** CRC-32 of uncompressed data */
197
+ crc32: number;
198
+ /** File name */
199
+ fileName: string;
200
+ /** File size */
201
+ length: number;
202
+ /** Relative offset of local file header */
203
+ offset: bigint;
204
+ };
205
+
206
+ /**
207
+ * generates cd header for the file
208
+ * @param options info that can be placed into cd header
209
+ * @returns buffer with header
210
+ */
211
+ export function generateCDHeader(options: GenerateCDOptions): ArrayBuffer {
212
+ const optionsToUse = {
213
+ ...options,
214
+ fnlength: options.fileName.length,
215
+ extraLength: 0
216
+ };
217
+
218
+ let zip64header: ArrayBuffer = new ArrayBuffer(0);
219
+
220
+ const optionsToZip64: any = {};
221
+ if (optionsToUse.offset >= 0xffffffff) {
222
+ optionsToZip64.offset = optionsToUse.offset;
223
+ optionsToUse.offset = BigInt(0xffffffff);
224
+ }
225
+ if (optionsToUse.length >= 0xffffffff) {
226
+ optionsToZip64.size = optionsToUse.length;
227
+ optionsToUse.length = 0xffffffff;
228
+ }
229
+
230
+ if (Object.keys(optionsToZip64).length) {
231
+ zip64header = createZip64Info(optionsToZip64);
232
+ optionsToUse.extraLength = zip64header.byteLength;
233
+ }
234
+ const header = new DataView(new ArrayBuffer(Number(CD_FILE_NAME_OFFSET)));
235
+
236
+ for (const field of ZIP_HEADER_FIELDS) {
237
+ setFieldToNumber(
238
+ header,
239
+ field.size,
240
+ field.offset,
241
+ optionsToUse[field.name ?? ''] ?? field.default ?? 0
242
+ );
243
+ }
244
+
245
+ const encodedName = new TextEncoder().encode(optionsToUse.fileName);
246
+
247
+ const resHeader = concatenateArrayBuffers(header.buffer, encodedName, zip64header);
248
+
249
+ return resHeader;
250
+ }
251
+
252
+ /** Fields map */
253
+ const ZIP_HEADER_FIELDS = [
254
+ // Central directory file header signature = 0x02014b50
255
+ {
256
+ offset: 0,
257
+ size: 4,
258
+ default: new DataView(signature.buffer).getUint32(0, true)
259
+ },
260
+
261
+ // Version made by
262
+ {
263
+ offset: 4,
264
+ size: 2,
265
+ default: 45
266
+ },
267
+
268
+ // Version needed to extract (minimum)
269
+ {
270
+ offset: 6,
271
+ size: 2,
272
+ default: 45
273
+ },
274
+
275
+ // General purpose bit flag
276
+ {
277
+ offset: 8,
278
+ size: 2,
279
+ default: 0
280
+ },
281
+
282
+ // Compression method
283
+ {
284
+ offset: 10,
285
+ size: 2,
286
+ default: 0
287
+ },
288
+
289
+ // File last modification time
290
+ {
291
+ offset: 12,
292
+ size: 2,
293
+ default: 0
294
+ },
295
+
296
+ // File last modification date
297
+ {
298
+ offset: 14,
299
+ size: 2,
300
+ default: 0
301
+ },
302
+
303
+ // CRC-32 of uncompressed data
304
+ {
305
+ offset: 16,
306
+ size: 4,
307
+ name: 'crc32'
308
+ },
309
+
310
+ // Compressed size (or 0xffffffff for ZIP64)
311
+ {
312
+ offset: 20,
313
+ size: 4,
314
+ name: 'length'
315
+ },
316
+
317
+ // Uncompressed size (or 0xffffffff for ZIP64)
318
+ {
319
+ offset: 24,
320
+ size: 4,
321
+ name: 'length'
322
+ },
323
+
324
+ // File name length (n)
325
+ {
326
+ offset: 28,
327
+ size: 2,
328
+ name: 'fnlength'
329
+ },
330
+
331
+ // Extra field length (m)
332
+ {
333
+ offset: 30,
334
+ size: 2,
335
+ default: 0,
336
+ name: 'extraLength'
337
+ },
338
+
339
+ // File comment length (k)
340
+ {
341
+ offset: 32,
342
+ size: 2,
343
+ default: 0
344
+ },
345
+
346
+ // Disk number where file starts (or 0xffff for ZIP64)
347
+ {
348
+ offset: 34,
349
+ size: 2,
350
+ default: 0
351
+ },
352
+
353
+ // Internal file attributes
354
+ {
355
+ offset: 36,
356
+ size: 2,
357
+ default: 0
358
+ },
359
+
360
+ // External file attributes
361
+ {
362
+ offset: 38,
363
+ size: 4,
364
+ default: 0
365
+ },
366
+
367
+ // Relative offset of local file header
368
+ {
369
+ offset: 42,
370
+ size: 4,
371
+ name: 'offset'
372
+ }
373
+ ];
@@ -1,8 +1,10 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils';
5
6
  import {ZipSignature, searchFromTheEnd} from './search-from-the-end';
7
+ import {setFieldToNumber} from './zip64-info-generation';
6
8
 
7
9
  /**
8
10
  * End of central directory info
@@ -13,6 +15,18 @@ export type ZipEoCDRecord = {
13
15
  cdStartOffset: bigint;
14
16
  /** Relative offset of local file header */
15
17
  cdRecordsNumber: bigint;
18
+ offsets: ZipEoCDRecordOffsets;
19
+ };
20
+
21
+ /**
22
+ * End of central directory offsets
23
+ * according to https://en.wikipedia.org/wiki/ZIP_(file_format)
24
+ */
25
+ export type ZipEoCDRecordOffsets = {
26
+ zipEoCDOffset: bigint;
27
+
28
+ zip64EoCDOffset?: bigint;
29
+ zip64EoCDLocatorOffset?: bigint;
16
30
  };
17
31
 
18
32
  const eoCDSignature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x05, 0x06]);
@@ -21,9 +35,13 @@ const zip64EoCDSignature = new Uint8Array([0x50, 0x4b, 0x06, 0x06]);
21
35
 
22
36
  // offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
23
37
  const CD_RECORDS_NUMBER_OFFSET = 8n;
38
+ const CD_RECORDS_NUMBER_ON_DISC_OFFSET = 10n;
39
+ const CD_CD_BYTE_SIZE_OFFSET = 12n;
24
40
  const CD_START_OFFSET_OFFSET = 16n;
25
41
  const ZIP64_EOCD_START_OFFSET_OFFSET = 8n;
26
42
  const ZIP64_CD_RECORDS_NUMBER_OFFSET = 24n;
43
+ const ZIP64_CD_RECORDS_NUMBER_ON_DISC_OFFSET = 32n;
44
+ const ZIP64_CD_CD_BYTE_SIZE_OFFSET = 40n;
27
45
  const ZIP64_CD_START_OFFSET_OFFSET = 48n;
28
46
 
29
47
  /**
@@ -37,14 +55,12 @@ export const parseEoCDRecord = async (file: FileProvider): Promise<ZipEoCDRecord
37
55
  let cdRecordsNumber = BigInt(await file.getUint16(zipEoCDOffset + CD_RECORDS_NUMBER_OFFSET));
38
56
  let cdStartOffset = BigInt(await file.getUint32(zipEoCDOffset + CD_START_OFFSET_OFFSET));
39
57
 
40
- if (cdStartOffset === BigInt(0xffffffff) || cdRecordsNumber === BigInt(0xffffffff)) {
41
- const zip64EoCDLocatorOffset = zipEoCDOffset - 20n;
58
+ let zip64EoCDLocatorOffset = zipEoCDOffset - 20n;
59
+ let zip64EoCDOffset = 0n;
42
60
 
43
- const magicBytes = await file.slice(zip64EoCDLocatorOffset, zip64EoCDLocatorOffset + 4n);
44
- if (!compareArrayBuffers(magicBytes, zip64EoCDLocatorSignature)) {
45
- throw new Error('zip64 EoCD locator not found');
46
- }
47
- const zip64EoCDOffset = await file.getBigUint64(
61
+ const magicBytes = await file.slice(zip64EoCDLocatorOffset, zip64EoCDLocatorOffset + 4n);
62
+ if (compareArrayBuffers(magicBytes, zip64EoCDLocatorSignature)) {
63
+ zip64EoCDOffset = await file.getBigUint64(
48
64
  zip64EoCDLocatorOffset + ZIP64_EOCD_START_OFFSET_OFFSET
49
65
  );
50
66
 
@@ -55,10 +71,84 @@ export const parseEoCDRecord = async (file: FileProvider): Promise<ZipEoCDRecord
55
71
 
56
72
  cdRecordsNumber = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_RECORDS_NUMBER_OFFSET);
57
73
  cdStartOffset = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_START_OFFSET_OFFSET);
74
+ } else {
75
+ zip64EoCDLocatorOffset = 0n;
58
76
  }
59
77
 
60
78
  return {
61
79
  cdRecordsNumber,
62
- cdStartOffset
80
+ cdStartOffset,
81
+ offsets: {
82
+ zip64EoCDOffset,
83
+ zip64EoCDLocatorOffset,
84
+ zipEoCDOffset
85
+ }
63
86
  };
64
87
  };
88
+
89
+ /**
90
+ * updates EoCD record to add more files to the archieve
91
+ * @param eocdBody buffer containing header
92
+ * @param oldEoCDOffsets info read from EoCD record befor updating
93
+ * @param newCDStartOffset CD start offset to be updated
94
+ * @param eocdStartOffset EoCD start offset to be updated
95
+ * @returns new EoCD header
96
+ */
97
+ export async function updateEoCD(
98
+ eocdBody: ArrayBuffer,
99
+ oldEoCDOffsets: ZipEoCDRecordOffsets,
100
+ newCDStartOffset: bigint,
101
+ eocdStartOffset: bigint,
102
+ newCDRecordsNumber: bigint
103
+ ): Promise<Uint8Array> {
104
+ const eocd = new DataView(eocdBody);
105
+
106
+ const classicEoCDOffset = oldEoCDOffsets.zip64EoCDOffset
107
+ ? oldEoCDOffsets.zipEoCDOffset - oldEoCDOffsets.zip64EoCDOffset
108
+ : 0n;
109
+
110
+ // updating classic EoCD record with new CD records number in general and on disc
111
+ if (Number(newCDRecordsNumber) <= 0xffff) {
112
+ setFieldToNumber(eocd, 2, classicEoCDOffset + CD_RECORDS_NUMBER_OFFSET, newCDRecordsNumber);
113
+ setFieldToNumber(
114
+ eocd,
115
+ 2,
116
+ classicEoCDOffset + CD_RECORDS_NUMBER_ON_DISC_OFFSET,
117
+ newCDRecordsNumber
118
+ );
119
+ }
120
+
121
+ // updating zip64 EoCD record with new size of CD
122
+ if (eocdStartOffset - newCDStartOffset <= 0xffffffff) {
123
+ setFieldToNumber(
124
+ eocd,
125
+ 4,
126
+ classicEoCDOffset + CD_CD_BYTE_SIZE_OFFSET,
127
+ eocdStartOffset - newCDStartOffset
128
+ );
129
+ }
130
+
131
+ // updating classic EoCD record with new CD start offset
132
+ if (newCDStartOffset < 0xffffffff) {
133
+ setFieldToNumber(eocd, 4, classicEoCDOffset + CD_START_OFFSET_OFFSET, newCDStartOffset);
134
+ }
135
+
136
+ // updating zip64 EoCD locator and record with new EoCD record start offset and cd records number
137
+ if (oldEoCDOffsets.zip64EoCDLocatorOffset && oldEoCDOffsets.zip64EoCDOffset) {
138
+ // updating zip64 EoCD locator with new EoCD record start offset
139
+ const locatorOffset = oldEoCDOffsets.zip64EoCDLocatorOffset - oldEoCDOffsets.zip64EoCDOffset;
140
+ setFieldToNumber(eocd, 8, locatorOffset + ZIP64_EOCD_START_OFFSET_OFFSET, eocdStartOffset);
141
+
142
+ // updating zip64 EoCD record with new cd start offset
143
+ setFieldToNumber(eocd, 8, ZIP64_CD_START_OFFSET_OFFSET, newCDStartOffset);
144
+
145
+ // updating zip64 EoCD record with new cd records number
146
+ setFieldToNumber(eocd, 8, ZIP64_CD_RECORDS_NUMBER_OFFSET, newCDRecordsNumber);
147
+ setFieldToNumber(eocd, 8, ZIP64_CD_RECORDS_NUMBER_ON_DISC_OFFSET, newCDRecordsNumber);
148
+
149
+ // updating zip64 EoCD record with new size of CD
150
+ setFieldToNumber(eocd, 8, ZIP64_CD_CD_BYTE_SIZE_OFFSET, eocdStartOffset - newCDStartOffset);
151
+ }
152
+
153
+ return new Uint8Array(eocd.buffer);
154
+ }
@@ -1,8 +1,10 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
- import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils';
5
+ import {FileProvider, compareArrayBuffers, concatenateArrayBuffers} from '@loaders.gl/loader-utils';
5
6
  import {ZipSignature} from './search-from-the-end';
7
+ import {createZip64Info, setFieldToNumber} from './zip64-info-generation';
6
8
 
7
9
  /**
8
10
  * zip local file header info
@@ -94,3 +96,127 @@ export const parseZipLocalFileHeader = async (
94
96
  compressionMethod
95
97
  };
96
98
  };
99
+
100
+ /** info that can be placed into cd header */
101
+ type GenerateLocalOptions = {
102
+ /** CRC-32 of uncompressed data */
103
+ crc32: number;
104
+ /** File name */
105
+ fileName: string;
106
+ /** File size */
107
+ length: number;
108
+ };
109
+
110
+ /**
111
+ * generates local header for the file
112
+ * @param options info that can be placed into local header
113
+ * @returns buffer with header
114
+ */
115
+ export function generateLocalHeader(options: GenerateLocalOptions): ArrayBuffer {
116
+ const optionsToUse = {
117
+ ...options,
118
+ extraLength: 0,
119
+ fnlength: options.fileName.length
120
+ };
121
+
122
+ let zip64header: ArrayBuffer = new ArrayBuffer(0);
123
+
124
+ const optionsToZip64: any = {};
125
+ if (optionsToUse.length >= 0xffffffff) {
126
+ optionsToZip64.size = optionsToUse.length;
127
+ optionsToUse.length = 0xffffffff;
128
+ }
129
+
130
+ if (Object.keys(optionsToZip64).length) {
131
+ zip64header = createZip64Info(optionsToZip64);
132
+ optionsToUse.extraLength = zip64header.byteLength;
133
+ }
134
+
135
+ // base length without file name and extra info is static
136
+ const header = new DataView(new ArrayBuffer(Number(FILE_NAME_OFFSET)));
137
+
138
+ for (const field of ZIP_HEADER_FIELDS) {
139
+ setFieldToNumber(
140
+ header,
141
+ field.size,
142
+ field.offset,
143
+ optionsToUse[field.name ?? ''] ?? field.default ?? 0
144
+ );
145
+ }
146
+
147
+ const encodedName = new TextEncoder().encode(optionsToUse.fileName);
148
+
149
+ const resHeader = concatenateArrayBuffers(header.buffer, encodedName, zip64header);
150
+
151
+ return resHeader;
152
+ }
153
+
154
+ const ZIP_HEADER_FIELDS = [
155
+ // Local file header signature = 0x04034b50
156
+ {
157
+ offset: 0,
158
+ size: 4,
159
+ default: new DataView(signature.buffer).getUint32(0, true)
160
+ },
161
+ // Version needed to extract (minimum)
162
+ {
163
+ offset: 4,
164
+ size: 2,
165
+ default: 45
166
+ },
167
+ // General purpose bit flag
168
+ {
169
+ offset: 6,
170
+ size: 2,
171
+ default: 0
172
+ },
173
+ // Compression method
174
+ {
175
+ offset: 8,
176
+ size: 2,
177
+ default: 0
178
+ },
179
+ // File last modification time
180
+ {
181
+ offset: 10,
182
+ size: 2,
183
+ default: 0
184
+ },
185
+ // File last modification date
186
+ {
187
+ offset: 12,
188
+ size: 2,
189
+ default: 0
190
+ },
191
+ // CRC-32 of uncompressed data
192
+ {
193
+ offset: 14,
194
+ size: 4,
195
+ name: 'crc32'
196
+ },
197
+ // Compressed size (or 0xffffffff for ZIP64)
198
+ {
199
+ offset: 18,
200
+ size: 4,
201
+ name: 'length'
202
+ },
203
+ // Uncompressed size (or 0xffffffff for ZIP64)
204
+ {
205
+ offset: 22,
206
+ size: 4,
207
+ name: 'length'
208
+ },
209
+ // File name length (n)
210
+ {
211
+ offset: 26,
212
+ size: 2,
213
+ name: 'fnlength'
214
+ },
215
+ // Extra field length (m)
216
+ {
217
+ offset: 28,
218
+ size: 2,
219
+ default: 0,
220
+ name: 'extraLength'
221
+ }
222
+ ];
@@ -1,4 +1,5 @@
1
- // loaders.gl, MIT license
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
2
3
  // Copyright (c) vis.gl contributors
3
4
 
4
5
  import {FileProvider} from '@loaders.gl/loader-utils';
@@ -0,0 +1,113 @@
1
+ import {FileHandleFile, concatenateArrayBuffers} from '@loaders.gl/loader-utils';
2
+ import {ZipEoCDRecord, parseEoCDRecord, updateEoCD} from './end-of-central-directory';
3
+ import {CRC32Hash} from '@loaders.gl/crypto';
4
+ import {generateLocalHeader} from './local-file-header';
5
+ import {generateCDHeader} from './cd-file-header';
6
+
7
+ /**
8
+ * cut off CD and EoCD records from zip file
9
+ * @param provider zip file
10
+ * @returns tuple with three values: CD, EoCD record, EoCD information
11
+ */
12
+ async function cutTheTailOff(
13
+ provider: FileHandleFile
14
+ ): Promise<[ArrayBuffer, ArrayBuffer, ZipEoCDRecord]> {
15
+ // define where the body ends
16
+ const oldEoCDinfo = await parseEoCDRecord(provider);
17
+ const oldCDStartOffset = oldEoCDinfo.cdStartOffset;
18
+
19
+ // define cd length
20
+ const oldCDLength = Number(
21
+ oldEoCDinfo.offsets.zip64EoCDOffset
22
+ ? oldEoCDinfo.offsets.zip64EoCDOffset - oldCDStartOffset
23
+ : oldEoCDinfo.offsets.zipEoCDOffset - oldCDStartOffset
24
+ );
25
+
26
+ // cut off everything except of archieve body
27
+ const zipEnding = await provider.slice(oldCDStartOffset, provider.length);
28
+ await provider.truncate(Number(oldCDStartOffset));
29
+
30
+ // divide cd body and eocd record
31
+ const oldCDBody = zipEnding.slice(0, oldCDLength);
32
+ const eocdBody = zipEnding.slice(oldCDLength, zipEnding.byteLength);
33
+
34
+ return [oldCDBody, eocdBody, oldEoCDinfo];
35
+ }
36
+
37
+ /**
38
+ * generates CD and local headers for the file
39
+ * @param fileName name of the file
40
+ * @param fileToAdd buffer with the file
41
+ * @param localFileHeaderOffset offset of the file local header
42
+ * @returns tuple with two values: local header and file body, cd header
43
+ */
44
+ async function generateFileHeaders(
45
+ fileName: string,
46
+ fileToAdd: ArrayBuffer,
47
+ localFileHeaderOffset: bigint
48
+ ): Promise<[Uint8Array, Uint8Array]> {
49
+ // generating CRC32 of the content
50
+ const newFileCRC322 = parseInt(await new CRC32Hash().hash(fileToAdd, 'hex'), 16);
51
+
52
+ // generate local header for the file
53
+ const newFileLocalHeader = generateLocalHeader({
54
+ crc32: newFileCRC322,
55
+ fileName,
56
+ length: fileToAdd.byteLength
57
+ });
58
+
59
+ // generate hash file cd header
60
+ const newFileCDHeader = generateCDHeader({
61
+ crc32: newFileCRC322,
62
+ fileName,
63
+ offset: localFileHeaderOffset,
64
+ length: fileToAdd.byteLength
65
+ });
66
+ return [
67
+ new Uint8Array(concatenateArrayBuffers(newFileLocalHeader, fileToAdd)),
68
+ new Uint8Array(newFileCDHeader)
69
+ ];
70
+ }
71
+
72
+ /**
73
+ * adds one file in the end of the archieve
74
+ * @param zipUrl path to the file
75
+ * @param fileToAdd new file body
76
+ * @param fileName new file name
77
+ */
78
+ export async function addOneFile(zipUrl: string, fileToAdd: ArrayBuffer, fileName: string) {
79
+ // init file handler
80
+ const provider = new FileHandleFile(zipUrl, true);
81
+
82
+ const [oldCDBody, eocdBody, oldEoCDinfo] = await cutTheTailOff(provider);
83
+
84
+ // remember the new file local header start offset
85
+ const newFileOffset = provider.length;
86
+
87
+ const [localPart, cdHeaderPart] = await generateFileHeaders(fileName, fileToAdd, newFileOffset);
88
+
89
+ // write down the file local header
90
+ await provider.append(localPart);
91
+
92
+ // add the file CD header to the CD
93
+ const newCDBody = concatenateArrayBuffers(oldCDBody, cdHeaderPart);
94
+
95
+ // remember the CD start offset
96
+ const newCDStartOffset = provider.length;
97
+
98
+ // write down new CD
99
+ await provider.append(new Uint8Array(newCDBody));
100
+
101
+ // remember where eocd starts
102
+ const eocdOffset = provider.length;
103
+
104
+ await provider.append(
105
+ await updateEoCD(
106
+ eocdBody,
107
+ oldEoCDinfo.offsets,
108
+ newCDStartOffset,
109
+ eocdOffset,
110
+ oldEoCDinfo.cdRecordsNumber + 1n
111
+ )
112
+ );
113
+ }