@loaders.gl/zip 4.1.0-alpha.2 → 4.1.0-alpha.4

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 (66) hide show
  1. package/dist/dist.dev.js +155 -51
  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.map +1 -1
  5. package/dist/hash-file-utility.js.map +1 -1
  6. package/dist/index.cjs +171 -49
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/tar/header.d.ts.map +1 -1
  12. package/dist/lib/tar/header.js.map +1 -1
  13. package/dist/lib/tar/tar.d.ts.map +1 -1
  14. package/dist/lib/tar/tar.js.map +1 -1
  15. package/dist/lib/tar/types.d.ts.map +1 -1
  16. package/dist/lib/tar/types.js.map +1 -1
  17. package/dist/lib/tar/utils.d.ts.map +1 -1
  18. package/dist/lib/tar/utils.js.map +1 -1
  19. package/dist/parse-zip/cd-file-header.d.ts +1 -1
  20. package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
  21. package/dist/parse-zip/cd-file-header.js +4 -4
  22. package/dist/parse-zip/cd-file-header.js.map +1 -1
  23. package/dist/parse-zip/end-of-central-directory.d.ts +19 -0
  24. package/dist/parse-zip/end-of-central-directory.d.ts.map +1 -1
  25. package/dist/parse-zip/end-of-central-directory.js +42 -8
  26. package/dist/parse-zip/end-of-central-directory.js.map +1 -1
  27. package/dist/parse-zip/local-file-header.d.ts +16 -0
  28. package/dist/parse-zip/local-file-header.d.ts.map +1 -1
  29. package/dist/parse-zip/local-file-header.js +73 -1
  30. package/dist/parse-zip/local-file-header.js.map +1 -1
  31. package/dist/parse-zip/search-from-the-end.d.ts.map +1 -1
  32. package/dist/parse-zip/search-from-the-end.js.map +1 -1
  33. package/dist/parse-zip/zip-compozition.d.ts +8 -0
  34. package/dist/parse-zip/zip-compozition.d.ts.map +1 -0
  35. package/dist/parse-zip/zip-compozition.js +43 -0
  36. package/dist/parse-zip/zip-compozition.js.map +1 -0
  37. package/dist/parse-zip/zip64-info-generation.d.ts +5 -8
  38. package/dist/parse-zip/zip64-info-generation.d.ts.map +1 -1
  39. package/dist/parse-zip/zip64-info-generation.js +6 -3
  40. package/dist/parse-zip/zip64-info-generation.js.map +1 -1
  41. package/dist/tar-builder.d.ts.map +1 -1
  42. package/dist/tar-builder.js.map +1 -1
  43. package/dist/zip-loader.d.ts.map +1 -1
  44. package/dist/zip-loader.js +1 -1
  45. package/dist/zip-loader.js.map +1 -1
  46. package/dist/zip-writer.d.ts +2 -2
  47. package/dist/zip-writer.d.ts.map +1 -1
  48. package/dist/zip-writer.js +22 -7
  49. package/dist/zip-writer.js.map +1 -1
  50. package/package.json +5 -5
  51. package/src/filesystems/zip-filesystem.ts +2 -1
  52. package/src/hash-file-utility.ts +2 -1
  53. package/src/index.ts +4 -2
  54. package/src/lib/tar/header.ts +2 -1
  55. package/src/lib/tar/tar.ts +2 -1
  56. package/src/lib/tar/types.ts +2 -1
  57. package/src/lib/tar/utils.ts +2 -1
  58. package/src/parse-zip/cd-file-header.ts +8 -6
  59. package/src/parse-zip/end-of-central-directory.ts +97 -9
  60. package/src/parse-zip/local-file-header.ts +128 -2
  61. package/src/parse-zip/search-from-the-end.ts +2 -1
  62. package/src/parse-zip/zip-compozition.ts +113 -0
  63. package/src/parse-zip/zip64-info-generation.ts +20 -4
  64. package/src/tar-builder.ts +2 -1
  65. package/src/zip-loader.ts +2 -1
  66. package/src/zip-writer.ts +24 -10
@@ -1,29 +1,44 @@
1
1
  import JSZip from 'jszip';
2
+ const VERSION = typeof "4.1.0-alpha.4" !== 'undefined' ? "4.1.0-alpha.4" : 'latest';
2
3
  export const ZipWriter = {
3
4
  name: 'Zip Archive',
5
+ id: 'zip',
6
+ module: 'zip',
7
+ version: VERSION,
4
8
  extensions: ['zip'],
5
9
  category: 'archive',
6
10
  mimeTypes: ['application/zip'],
11
+ options: {
12
+ zip: {
13
+ onUpdate: () => {}
14
+ },
15
+ jszip: {}
16
+ },
7
17
  encode: encodeZipAsync
8
18
  };
9
19
  async function encodeZipAsync(fileMap) {
20
+ var _ZipWriter$options;
10
21
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
11
22
  const jsZip = new JSZip();
12
23
  for (const subFileName in fileMap) {
13
24
  const subFileData = fileMap[subFileName];
14
25
  jsZip.file(subFileName, subFileData, (options === null || options === void 0 ? void 0 : options.jszip) || {});
15
26
  }
27
+ const zipOptions = {
28
+ ...ZipWriter.options.zip,
29
+ ...(options === null || options === void 0 ? void 0 : options.zip)
30
+ };
16
31
  const jszipOptions = {
17
- ...(options === null || options === void 0 ? void 0 : options.jszip),
18
- type: 'arraybuffer'
32
+ ...((_ZipWriter$options = ZipWriter.options) === null || _ZipWriter$options === void 0 ? void 0 : _ZipWriter$options.jszip),
33
+ ...options.jszip
19
34
  };
20
- const {
21
- onUpdate = () => {}
22
- } = options;
23
35
  try {
24
- return await jsZip.generateAsync(jszipOptions, onUpdate);
36
+ return await jsZip.generateAsync({
37
+ ...jszipOptions,
38
+ type: 'arraybuffer'
39
+ }, zipOptions.onUpdate);
25
40
  } catch (error) {
26
- options.log.error(`Unable to write zip archive: ${error}`);
41
+ options.log.error(`Unable to encode zip archive: ${error}`);
27
42
  throw error;
28
43
  }
29
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"zip-writer.js","names":["JSZip","ZipWriter","name","extensions","category","mimeTypes","encode","encodeZipAsync","fileMap","options","arguments","length","undefined","jsZip","subFileName","subFileData","file","jszip","jszipOptions","type","onUpdate","generateAsync","error","log"],"sources":["../src/zip-writer.ts"],"sourcesContent":["// loaders.gl, MIT license\n// Copyright (c) vis.gl contributors\n\nimport type {Writer, WriterOptions} from '@loaders.gl/loader-utils';\nimport JSZip, {JSZipGeneratorOptions} from 'jszip';\n\nexport type ZipWriterOptions = WriterOptions & {\n zip?: {\n onUpdate?: (metadata: {percent: number}) => void;\n };\n /** Passthrough options to jszip */\n jszip?: JSZipGeneratorOptions;\n};\n\n/**\n * Zip exporter\n */\nexport const ZipWriter: Writer<FileReaderEventMap, never, ZipWriterOptions> = {\n name: 'Zip Archive',\n extensions: ['zip'],\n category: 'archive',\n mimeTypes: ['application/zip'],\n // @ts-ignore\n encode: encodeZipAsync\n};\n\nasync function encodeZipAsync(\n fileMap: Record<string, ArrayBuffer>,\n options: ZipWriterOptions = {}\n) {\n const jsZip = new JSZip();\n // add files to the zip\n for (const subFileName in fileMap) {\n const subFileData = fileMap[subFileName];\n\n // jszip supports both arraybuffer and string data (the main loaders.gl types)\n // https://stuk.github.io/jszip/documentation/api_zipobject/async.html\n jsZip.file(subFileName, subFileData, options?.jszip || {});\n }\n\n // always generate the full zip as an arraybuffer\n const jszipOptions: JSZipGeneratorOptions = {...options?.jszip, type: 'arraybuffer'};\n const {onUpdate = () => {}} = options;\n\n try {\n return await jsZip.generateAsync(jszipOptions, onUpdate);\n } catch (error) {\n options.log.error(`Unable to write zip archive: ${error}`);\n throw error;\n }\n}\n"],"mappings":"AAIA,OAAOA,KAAK,MAA+B,OAAO;AAalD,OAAO,MAAMC,SAA8D,GAAG;EAC5EC,IAAI,EAAE,aAAa;EACnBC,UAAU,EAAE,CAAC,KAAK,CAAC;EACnBC,QAAQ,EAAE,SAAS;EACnBC,SAAS,EAAE,CAAC,iBAAiB,CAAC;EAE9BC,MAAM,EAAEC;AACV,CAAC;AAED,eAAeA,cAAcA,CAC3BC,OAAoC,EAEpC;EAAA,IADAC,OAAyB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC,CAAC;EAE9B,MAAMG,KAAK,GAAG,IAAIb,KAAK,CAAC,CAAC;EAEzB,KAAK,MAAMc,WAAW,IAAIN,OAAO,EAAE;IACjC,MAAMO,WAAW,GAAGP,OAAO,CAACM,WAAW,CAAC;IAIxCD,KAAK,CAACG,IAAI,CAACF,WAAW,EAAEC,WAAW,EAAE,CAAAN,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAEQ,KAAK,KAAI,CAAC,CAAC,CAAC;EAC5D;EAGA,MAAMC,YAAmC,GAAG;IAAC,IAAGT,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAEQ,KAAK;IAAEE,IAAI,EAAE;EAAa,CAAC;EACpF,MAAM;IAACC,QAAQ,GAAGA,CAAA,KAAM,CAAC;EAAC,CAAC,GAAGX,OAAO;EAErC,IAAI;IACF,OAAO,MAAMI,KAAK,CAACQ,aAAa,CAACH,YAAY,EAAEE,QAAQ,CAAC;EAC1D,CAAC,CAAC,OAAOE,KAAK,EAAE;IACdb,OAAO,CAACc,GAAG,CAACD,KAAK,CAAE,gCAA+BA,KAAM,EAAC,CAAC;IAC1D,MAAMA,KAAK;EACb;AACF"}
1
+ {"version":3,"file":"zip-writer.js","names":["JSZip","VERSION","ZipWriter","name","id","module","version","extensions","category","mimeTypes","options","zip","onUpdate","jszip","encode","encodeZipAsync","fileMap","_ZipWriter$options","arguments","length","undefined","jsZip","subFileName","subFileData","file","zipOptions","jszipOptions","generateAsync","type","error","log"],"sources":["../src/zip-writer.ts"],"sourcesContent":["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils';\nimport JSZip, {JSZipGeneratorOptions} from 'jszip';\n\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nconst VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';\n\nexport type ZipWriterOptions = WriterOptions & {\n zip?: {\n onUpdate?: (metadata: {percent: number}) => void;\n };\n /** Passthrough options to jszip */\n jszip?: JSZipGeneratorOptions;\n};\n\n/**\n * Zip exporter\n */\nexport const ZipWriter: WriterWithEncoder<Record<string, ArrayBuffer>, never, ZipWriterOptions> = {\n name: 'Zip Archive',\n id: 'zip',\n module: 'zip',\n version: VERSION,\n extensions: ['zip'],\n category: 'archive',\n mimeTypes: ['application/zip'],\n options: {\n zip: {\n onUpdate: () => {}\n },\n jszip: {}\n },\n encode: encodeZipAsync\n};\n\nasync function encodeZipAsync(\n fileMap: Record<string, ArrayBuffer>,\n options: ZipWriterOptions = {}\n): Promise<ArrayBuffer> {\n const jsZip = new JSZip();\n // add files to the zip\n for (const subFileName in fileMap) {\n const subFileData = fileMap[subFileName];\n\n // jszip supports both arraybuffer and string data (the main loaders.gl types)\n // https://stuk.github.io/jszip/documentation/api_zipobject/async.html\n jsZip.file(subFileName, subFileData, options?.jszip || {});\n }\n\n const zipOptions = {...ZipWriter.options.zip, ...options?.zip};\n const jszipOptions: JSZipGeneratorOptions = {...ZipWriter.options?.jszip, ...options.jszip};\n\n try {\n return await jsZip.generateAsync(\n {...jszipOptions, type: 'arraybuffer'}, // generate an arraybuffer\n zipOptions.onUpdate\n );\n } catch (error) {\n options.log.error(`Unable to encode zip archive: ${error}`);\n throw error;\n }\n}\n"],"mappings":"AAKA,OAAOA,KAAK,MAA+B,OAAO;AAGlD,MAAMC,OAAO,GAAG,sBAAkB,KAAK,WAAW,qBAAiB,QAAQ;AAa3E,OAAO,MAAMC,SAAkF,GAAG;EAChGC,IAAI,EAAE,aAAa;EACnBC,EAAE,EAAE,KAAK;EACTC,MAAM,EAAE,KAAK;EACbC,OAAO,EAAEL,OAAO;EAChBM,UAAU,EAAE,CAAC,KAAK,CAAC;EACnBC,QAAQ,EAAE,SAAS;EACnBC,SAAS,EAAE,CAAC,iBAAiB,CAAC;EAC9BC,OAAO,EAAE;IACPC,GAAG,EAAE;MACHC,QAAQ,EAAEA,CAAA,KAAM,CAAC;IACnB,CAAC;IACDC,KAAK,EAAE,CAAC;EACV,CAAC;EACDC,MAAM,EAAEC;AACV,CAAC;AAED,eAAeA,cAAcA,CAC3BC,OAAoC,EAEd;EAAA,IAAAC,kBAAA;EAAA,IADtBP,OAAyB,GAAAQ,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC,CAAC;EAE9B,MAAMG,KAAK,GAAG,IAAIrB,KAAK,CAAC,CAAC;EAEzB,KAAK,MAAMsB,WAAW,IAAIN,OAAO,EAAE;IACjC,MAAMO,WAAW,GAAGP,OAAO,CAACM,WAAW,CAAC;IAIxCD,KAAK,CAACG,IAAI,CAACF,WAAW,EAAEC,WAAW,EAAE,CAAAb,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAEG,KAAK,KAAI,CAAC,CAAC,CAAC;EAC5D;EAEA,MAAMY,UAAU,GAAG;IAAC,GAAGvB,SAAS,CAACQ,OAAO,CAACC,GAAG;IAAE,IAAGD,OAAO,aAAPA,OAAO,uBAAPA,OAAO,CAAEC,GAAG;EAAA,CAAC;EAC9D,MAAMe,YAAmC,GAAG;IAAC,KAAAT,kBAAA,GAAGf,SAAS,CAACQ,OAAO,cAAAO,kBAAA,uBAAjBA,kBAAA,CAAmBJ,KAAK;IAAE,GAAGH,OAAO,CAACG;EAAK,CAAC;EAE3F,IAAI;IACF,OAAO,MAAMQ,KAAK,CAACM,aAAa,CAC9B;MAAC,GAAGD,YAAY;MAAEE,IAAI,EAAE;IAAa,CAAC,EACtCH,UAAU,CAACb,QACb,CAAC;EACH,CAAC,CAAC,OAAOiB,KAAK,EAAE;IACdnB,OAAO,CAACoB,GAAG,CAACD,KAAK,CAAE,iCAAgCA,KAAM,EAAC,CAAC;IAC3D,MAAMA,KAAK;EACb;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loaders.gl/zip",
3
- "version": "4.1.0-alpha.2",
3
+ "version": "4.1.0-alpha.4",
4
4
  "description": "Zip Archive Loader",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,11 +38,11 @@
38
38
  "build-bundle": "ocular-bundle ./src/index.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@loaders.gl/compression": "4.1.0-alpha.2",
42
- "@loaders.gl/crypto": "4.1.0-alpha.2",
43
- "@loaders.gl/loader-utils": "4.1.0-alpha.2",
41
+ "@loaders.gl/compression": "4.1.0-alpha.4",
42
+ "@loaders.gl/crypto": "4.1.0-alpha.4",
43
+ "@loaders.gl/loader-utils": "4.1.0-alpha.4",
44
44
  "jszip": "^3.1.5",
45
45
  "md5": "^2.3.0"
46
46
  },
47
- "gitHead": "a248382edd20e846c1ccb23c15d089fb9b368dbc"
47
+ "gitHead": "b18ba1d63be704fd021e4470e8ab84175621e62d"
48
48
  }
@@ -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 {FileSystem, isBrowser} from '@loaders.gl/loader-utils';
@@ -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 {MD5Hash} from '@loaders.gl/crypto';
package/src/index.ts CHANGED
@@ -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
  export {ZipLoader} from './zip-loader';
@@ -13,7 +14,8 @@ export {
13
14
  } from './parse-zip/cd-file-header';
14
15
  export {
15
16
  parseZipLocalFileHeader,
16
- signature as localHeaderSignature
17
+ signature as localHeaderSignature,
18
+ generateLocalHeader
17
19
  } from './parse-zip/local-file-header';
18
20
  export {parseEoCDRecord} from './parse-zip/end-of-central-directory';
19
21
  export {searchFromTheEnd} from './parse-zip/search-from-the-end';
@@ -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
  // 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,10 +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
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';
7
- import {createZip64Info, NUMBER_SETTERS} from './zip64-info-generation';
8
+ import {createZip64Info, setFieldToNumber} from './zip64-info-generation';
8
9
 
9
10
  /**
10
11
  * zip central directory file header info
@@ -199,7 +200,7 @@ type GenerateCDOptions = {
199
200
  /** File size */
200
201
  length: number;
201
202
  /** Relative offset of local file header */
202
- offset: number;
203
+ offset: bigint;
203
204
  };
204
205
 
205
206
  /**
@@ -219,7 +220,7 @@ export function generateCDHeader(options: GenerateCDOptions): ArrayBuffer {
219
220
  const optionsToZip64: any = {};
220
221
  if (optionsToUse.offset >= 0xffffffff) {
221
222
  optionsToZip64.offset = optionsToUse.offset;
222
- optionsToUse.offset = 0xffffffff;
223
+ optionsToUse.offset = BigInt(0xffffffff);
223
224
  }
224
225
  if (optionsToUse.length >= 0xffffffff) {
225
226
  optionsToZip64.size = optionsToUse.length;
@@ -230,11 +231,12 @@ export function generateCDHeader(options: GenerateCDOptions): ArrayBuffer {
230
231
  zip64header = createZip64Info(optionsToZip64);
231
232
  optionsToUse.extraLength = zip64header.byteLength;
232
233
  }
233
- const header = new DataView(new ArrayBuffer(46));
234
+ const header = new DataView(new ArrayBuffer(Number(CD_FILE_NAME_OFFSET)));
234
235
 
235
236
  for (const field of ZIP_HEADER_FIELDS) {
236
- NUMBER_SETTERS[field.size](
237
+ setFieldToNumber(
237
238
  header,
239
+ field.size,
238
240
  field.offset,
239
241
  optionsToUse[field.name ?? ''] ?? field.default ?? 0
240
242
  );
@@ -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,82 @@ 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.zipEoCDOffset - (oldEoCDOffsets.zip64EoCDOffset ?? 0n);
107
+
108
+ // updating classic EoCD record with new CD records number in general and on disc
109
+ if (Number(newCDRecordsNumber) <= 0xffff) {
110
+ setFieldToNumber(eocd, 2, classicEoCDOffset + CD_RECORDS_NUMBER_OFFSET, newCDRecordsNumber);
111
+ setFieldToNumber(
112
+ eocd,
113
+ 2,
114
+ classicEoCDOffset + CD_RECORDS_NUMBER_ON_DISC_OFFSET,
115
+ newCDRecordsNumber
116
+ );
117
+ }
118
+
119
+ // updating zip64 EoCD record with new size of CD
120
+ if (eocdStartOffset - newCDStartOffset <= 0xffffffff) {
121
+ setFieldToNumber(
122
+ eocd,
123
+ 4,
124
+ classicEoCDOffset + CD_CD_BYTE_SIZE_OFFSET,
125
+ eocdStartOffset - newCDStartOffset
126
+ );
127
+ }
128
+
129
+ // updating classic EoCD record with new CD start offset
130
+ if (newCDStartOffset < 0xffffffff) {
131
+ setFieldToNumber(eocd, 4, classicEoCDOffset + CD_START_OFFSET_OFFSET, newCDStartOffset);
132
+ }
133
+
134
+ // updating zip64 EoCD locator and record with new EoCD record start offset and cd records number
135
+ if (oldEoCDOffsets.zip64EoCDLocatorOffset && oldEoCDOffsets.zip64EoCDOffset) {
136
+ // updating zip64 EoCD locator with new EoCD record start offset
137
+ const locatorOffset = oldEoCDOffsets.zip64EoCDLocatorOffset - oldEoCDOffsets.zip64EoCDOffset;
138
+ setFieldToNumber(eocd, 8, locatorOffset + ZIP64_EOCD_START_OFFSET_OFFSET, eocdStartOffset);
139
+
140
+ // updating zip64 EoCD record with new cd start offset
141
+ setFieldToNumber(eocd, 8, ZIP64_CD_START_OFFSET_OFFSET, newCDStartOffset);
142
+
143
+ // updating zip64 EoCD record with new cd records number
144
+ setFieldToNumber(eocd, 8, ZIP64_CD_RECORDS_NUMBER_OFFSET, newCDRecordsNumber);
145
+ setFieldToNumber(eocd, 8, ZIP64_CD_RECORDS_NUMBER_ON_DISC_OFFSET, newCDRecordsNumber);
146
+
147
+ // updating zip64 EoCD record with new size of CD
148
+ setFieldToNumber(eocd, 8, ZIP64_CD_CD_BYTE_SIZE_OFFSET, eocdStartOffset - newCDStartOffset);
149
+ }
150
+
151
+ return new Uint8Array(eocd.buffer);
152
+ }
@@ -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
+ }
@@ -41,15 +41,31 @@ export function createZip64Info(options: Zip64Options): ArrayBuffer {
41
41
  * @param offset offset of the writing start
42
42
  * @param value value to be written
43
43
  */
44
- type NumberSetter = (header: DataView, offset: number, value: number) => void;
44
+ type NumberSetter = (header: DataView, offset: number, value: number | bigint) => void;
45
+
46
+ /**
47
+ * Writes values into buffer according to the bytes amount
48
+ * @param header header where to write the data
49
+ * @param fieldSize size of the field in bytes
50
+ * @param fieldOffset offset of the field
51
+ * @param value value to be written
52
+ */
53
+ export function setFieldToNumber(
54
+ header: DataView,
55
+ fieldSize: number,
56
+ fieldOffset: number | bigint,
57
+ value: number | bigint
58
+ ): void {
59
+ NUMBER_SETTERS[fieldSize](header, Number(fieldOffset), value);
60
+ }
45
61
 
46
62
  /** functions to write values into buffer according to the bytes amount */
47
- export const NUMBER_SETTERS: {[key: number]: NumberSetter} = {
63
+ const NUMBER_SETTERS: {[key: number]: NumberSetter} = {
48
64
  2: (header, offset, value) => {
49
- header.setUint16(offset, value, true);
65
+ header.setUint16(offset, Number(value), true);
50
66
  },
51
67
  4: (header, offset, value) => {
52
- header.setUint32(offset, value, true);
68
+ header.setUint32(offset, Number(value), true);
53
69
  },
54
70
  8: (header, offset, value) => {
55
71
  header.setBigUint64(offset, BigInt(value), true);
@@ -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 Tar from './lib/tar/tar';
package/src/zip-loader.ts CHANGED
@@ -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 type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';