@loaders.gl/zip 4.3.0-alpha.1 → 4.3.0-alpha.3
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/dist.dev.js +137 -56
- package/dist/dist.min.js +2 -2
- package/dist/filesystems/IndexedArchive.d.ts +3 -3
- package/dist/filesystems/IndexedArchive.d.ts.map +1 -1
- package/dist/filesystems/zip-filesystem.d.ts +3 -3
- package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
- package/dist/hash-file-utility.d.ts +2 -2
- package/dist/hash-file-utility.d.ts.map +1 -1
- package/dist/index.cjs +71 -51
- package/dist/index.cjs.map +2 -2
- package/dist/parse-zip/cd-file-header.d.ts +3 -3
- package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
- package/dist/parse-zip/cd-file-header.js +25 -19
- package/dist/parse-zip/end-of-central-directory.d.ts +6 -4
- package/dist/parse-zip/end-of-central-directory.d.ts.map +1 -1
- package/dist/parse-zip/end-of-central-directory.js +3 -0
- package/dist/parse-zip/local-file-header.d.ts +2 -2
- package/dist/parse-zip/local-file-header.d.ts.map +1 -1
- package/dist/parse-zip/local-file-header.js +23 -23
- package/dist/parse-zip/search-from-the-end.d.ts +2 -2
- package/dist/parse-zip/search-from-the-end.d.ts.map +1 -1
- package/dist/parse-zip/search-from-the-end.js +19 -11
- package/dist/zip-loader.js +1 -1
- package/dist/zip-writer.js +1 -1
- package/package.json +5 -5
- package/src/filesystems/IndexedArchive.ts +7 -3
- package/src/filesystems/zip-filesystem.ts +3 -3
- package/src/hash-file-utility.ts +2 -2
- package/src/parse-zip/cd-file-header.ts +40 -22
- package/src/parse-zip/end-of-central-directory.ts +13 -4
- package/src/parse-zip/local-file-header.ts +38 -30
- package/src/parse-zip/search-from-the-end.ts +22 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileProviderInterface } from '@loaders.gl/loader-utils';
|
|
2
2
|
import { ZipSignature } from "./search-from-the-end.js";
|
|
3
3
|
/**
|
|
4
4
|
* zip local file header info
|
|
@@ -25,7 +25,7 @@ export declare const signature: ZipSignature;
|
|
|
25
25
|
* @param buffer - buffer containing whole array
|
|
26
26
|
* @returns Info from the header
|
|
27
27
|
*/
|
|
28
|
-
export declare const parseZipLocalFileHeader: (headerOffset: bigint,
|
|
28
|
+
export declare const parseZipLocalFileHeader: (headerOffset: bigint, file: FileProviderInterface) => Promise<ZipLocalFileHeader | null>;
|
|
29
29
|
/** info that can be placed into cd header */
|
|
30
30
|
type GenerateLocalOptions = {
|
|
31
31
|
/** CRC-32 of uncompressed data */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-file-header.d.ts","sourceRoot":"","sources":["../../src/parse-zip/local-file-header.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"local-file-header.d.ts","sourceRoot":"","sources":["../../src/parse-zip/local-file-header.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,qBAAqB,EAGtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAC,YAAY,EAAC,iCAA8B;AAGnD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uBAAuB;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,yBAAyB;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAUF,eAAO,MAAM,SAAS,EAAE,YAAuD,CAAC;AAEhF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,iBACpB,MAAM,QACd,qBAAqB,KAC1B,QAAQ,kBAAkB,GAAG,IAAI,CAuDnC,CAAC;AAEF,6CAA6C;AAC7C,KAAK,oBAAoB,GAAG;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CAqC9E"}
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
import { compareArrayBuffers, concatenateArrayBuffers } from '@loaders.gl/loader-utils';
|
|
5
5
|
import { createZip64Info, setFieldToNumber } from "./zip64-info-generation.js";
|
|
6
6
|
// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
7
|
-
const COMPRESSION_METHOD_OFFSET =
|
|
8
|
-
const COMPRESSED_SIZE_OFFSET =
|
|
9
|
-
const UNCOMPRESSED_SIZE_OFFSET =
|
|
10
|
-
const FILE_NAME_LENGTH_OFFSET =
|
|
11
|
-
const EXTRA_FIELD_LENGTH_OFFSET =
|
|
7
|
+
const COMPRESSION_METHOD_OFFSET = 8;
|
|
8
|
+
const COMPRESSED_SIZE_OFFSET = 18;
|
|
9
|
+
const UNCOMPRESSED_SIZE_OFFSET = 22;
|
|
10
|
+
const FILE_NAME_LENGTH_OFFSET = 26;
|
|
11
|
+
const EXTRA_FIELD_LENGTH_OFFSET = 28;
|
|
12
12
|
const FILE_NAME_OFFSET = 30n;
|
|
13
13
|
export const signature = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);
|
|
14
14
|
/**
|
|
@@ -17,34 +17,34 @@ export const signature = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);
|
|
|
17
17
|
* @param buffer - buffer containing whole array
|
|
18
18
|
* @returns Info from the header
|
|
19
19
|
*/
|
|
20
|
-
export const parseZipLocalFileHeader = async (headerOffset,
|
|
21
|
-
const
|
|
20
|
+
export const parseZipLocalFileHeader = async (headerOffset, file) => {
|
|
21
|
+
const mainHeader = new DataView(await file.slice(headerOffset, headerOffset + FILE_NAME_OFFSET));
|
|
22
|
+
const magicBytes = mainHeader.buffer.slice(0, 4);
|
|
22
23
|
if (!compareArrayBuffers(magicBytes, signature)) {
|
|
23
24
|
return null;
|
|
24
25
|
}
|
|
25
|
-
const fileNameLength =
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
26
|
+
const fileNameLength = mainHeader.getUint16(FILE_NAME_LENGTH_OFFSET, true);
|
|
27
|
+
const extraFieldLength = mainHeader.getUint16(EXTRA_FIELD_LENGTH_OFFSET, true);
|
|
28
|
+
const additionalHeader = await file.slice(headerOffset + FILE_NAME_OFFSET, headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength));
|
|
29
|
+
const fileNameBuffer = additionalHeader.slice(0, fileNameLength);
|
|
30
|
+
const extraDataBuffer = new DataView(additionalHeader.slice(fileNameLength, additionalHeader.byteLength));
|
|
31
|
+
const fileName = new TextDecoder().decode(fileNameBuffer).split('\\').join('/');
|
|
31
32
|
let fileDataOffset = headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength);
|
|
32
|
-
const compressionMethod =
|
|
33
|
-
let compressedSize = BigInt(
|
|
34
|
-
let uncompressedSize = BigInt(
|
|
35
|
-
|
|
36
|
-
let offsetInZip64Data = 4n;
|
|
33
|
+
const compressionMethod = mainHeader.getUint16(COMPRESSION_METHOD_OFFSET, true);
|
|
34
|
+
let compressedSize = BigInt(mainHeader.getUint32(COMPRESSED_SIZE_OFFSET, true)); // add zip 64 logic
|
|
35
|
+
let uncompressedSize = BigInt(mainHeader.getUint32(UNCOMPRESSED_SIZE_OFFSET, true)); // add zip 64 logic
|
|
36
|
+
let offsetInZip64Data = 4;
|
|
37
37
|
// looking for info that might be also be in zip64 extra field
|
|
38
38
|
if (uncompressedSize === BigInt(0xffffffff)) {
|
|
39
|
-
uncompressedSize =
|
|
40
|
-
offsetInZip64Data +=
|
|
39
|
+
uncompressedSize = extraDataBuffer.getBigUint64(offsetInZip64Data, true);
|
|
40
|
+
offsetInZip64Data += 8;
|
|
41
41
|
}
|
|
42
42
|
if (compressedSize === BigInt(0xffffffff)) {
|
|
43
|
-
compressedSize =
|
|
44
|
-
offsetInZip64Data +=
|
|
43
|
+
compressedSize = extraDataBuffer.getBigUint64(offsetInZip64Data, true);
|
|
44
|
+
offsetInZip64Data += 8;
|
|
45
45
|
}
|
|
46
46
|
if (fileDataOffset === BigInt(0xffffffff)) {
|
|
47
|
-
fileDataOffset =
|
|
47
|
+
fileDataOffset = extraDataBuffer.getBigUint64(offsetInZip64Data, true); // setting it to the one from zip64
|
|
48
48
|
}
|
|
49
49
|
return {
|
|
50
50
|
fileNameLength,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileProviderInterface } from '@loaders.gl/loader-utils';
|
|
2
2
|
/** Description of zip signature type */
|
|
3
3
|
export type ZipSignature = Uint8Array;
|
|
4
4
|
/**
|
|
@@ -7,5 +7,5 @@ export type ZipSignature = Uint8Array;
|
|
|
7
7
|
* @param target
|
|
8
8
|
* @returns
|
|
9
9
|
*/
|
|
10
|
-
export declare const searchFromTheEnd: (file:
|
|
10
|
+
export declare const searchFromTheEnd: (file: FileProviderInterface, target: ZipSignature) => Promise<bigint>;
|
|
11
11
|
//# sourceMappingURL=search-from-the-end.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-from-the-end.d.ts","sourceRoot":"","sources":["../../src/parse-zip/search-from-the-end.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"search-from-the-end.d.ts","sourceRoot":"","sources":["../../src/parse-zip/search-from-the-end.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,qBAAqB,EAAC,MAAM,0BAA0B,CAAC;AAE/D,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAItC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,SACrB,qBAAqB,UACnB,YAAY,KACnB,QAAQ,MAAM,CA8BhB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// loaders.gl
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
|
+
const buffLength = 1024;
|
|
4
5
|
/**
|
|
5
6
|
* looking for the last occurrence of the provided
|
|
6
7
|
* @param file
|
|
@@ -14,17 +15,24 @@ export const searchFromTheEnd = async (file, target) => {
|
|
|
14
15
|
await file.getUint8(file.length - 3n),
|
|
15
16
|
undefined
|
|
16
17
|
];
|
|
17
|
-
let targetOffset =
|
|
18
|
+
let targetOffset = -1;
|
|
18
19
|
// looking for the last record in the central directory
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
let point = file.length - 4n;
|
|
21
|
+
do {
|
|
22
|
+
const prevPoint = point;
|
|
23
|
+
point -= BigInt(buffLength);
|
|
24
|
+
point = point >= 0n ? point : 0n;
|
|
25
|
+
const buff = new Uint8Array(await file.slice(point, prevPoint));
|
|
26
|
+
for (let i = buff.length - 1; i > -1; i--) {
|
|
27
|
+
searchWindow[3] = searchWindow[2];
|
|
28
|
+
searchWindow[2] = searchWindow[1];
|
|
29
|
+
searchWindow[1] = searchWindow[0];
|
|
30
|
+
searchWindow[0] = buff[i];
|
|
31
|
+
if (searchWindow.every((val, index) => val === target[index])) {
|
|
32
|
+
targetOffset = i;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
27
35
|
}
|
|
28
|
-
}
|
|
29
|
-
return targetOffset;
|
|
36
|
+
} while (targetOffset === -1 && point > 0n);
|
|
37
|
+
return point + BigInt(targetOffset);
|
|
30
38
|
};
|
package/dist/zip-loader.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import JSZip from 'jszip';
|
|
5
5
|
// __VERSION__ is injected by babel-plugin-version-inline
|
|
6
6
|
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
|
|
7
|
-
const VERSION = typeof "4.
|
|
7
|
+
const VERSION = typeof "4.3.0-alpha.2" !== 'undefined' ? "4.3.0-alpha.2" : 'latest';
|
|
8
8
|
export const ZipLoader = {
|
|
9
9
|
dataType: null,
|
|
10
10
|
batchType: null,
|
package/dist/zip-writer.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
import JSZip from 'jszip';
|
|
5
5
|
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
|
|
6
|
-
const VERSION = typeof "4.
|
|
6
|
+
const VERSION = typeof "4.3.0-alpha.2" !== 'undefined' ? "4.3.0-alpha.2" : 'latest';
|
|
7
7
|
/**
|
|
8
8
|
* Zip exporter
|
|
9
9
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loaders.gl/zip",
|
|
3
|
-
"version": "4.3.0-alpha.
|
|
3
|
+
"version": "4.3.0-alpha.3",
|
|
4
4
|
"description": "Zip Archive Loader",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
"build-bundle-dev": "ocular-bundle ./bundle.ts --env=dev --output=dist/dist.dev.js"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@loaders.gl/compression": "4.3.0-alpha.
|
|
43
|
-
"@loaders.gl/crypto": "4.3.0-alpha.
|
|
44
|
-
"@loaders.gl/loader-utils": "4.3.0-alpha.
|
|
42
|
+
"@loaders.gl/compression": "4.3.0-alpha.3",
|
|
43
|
+
"@loaders.gl/crypto": "4.3.0-alpha.3",
|
|
44
|
+
"@loaders.gl/loader-utils": "4.3.0-alpha.3",
|
|
45
45
|
"jszip": "^3.1.5",
|
|
46
46
|
"md5": "^2.3.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@loaders.gl/core": "^4.0.0"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "3213679d79e6ff2814d48fd3337acfa446c74099"
|
|
52
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {FileProviderInterface} from '@loaders.gl/loader-utils';
|
|
2
2
|
import {ZipFileSystem} from './zip-filesystem';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,7 +6,7 @@ import {ZipFileSystem} from './zip-filesystem';
|
|
|
6
6
|
* a hash file inside that allows to increase reading speed
|
|
7
7
|
*/
|
|
8
8
|
export abstract class IndexedArchive {
|
|
9
|
-
public fileProvider:
|
|
9
|
+
public fileProvider: FileProviderInterface;
|
|
10
10
|
public fileName?: string;
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -15,7 +15,11 @@ export abstract class IndexedArchive {
|
|
|
15
15
|
* @param hashTable - pre-loaded hashTable. If presented, getFile will skip reading the hash file
|
|
16
16
|
* @param fileName - name of the archive. It is used to add to an URL of a loader context
|
|
17
17
|
*/
|
|
18
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
fileProvider: FileProviderInterface,
|
|
20
|
+
hashTable?: Record<string, bigint>,
|
|
21
|
+
fileName?: string
|
|
22
|
+
) {
|
|
19
23
|
this.fileProvider = fileProvider;
|
|
20
24
|
this.fileName = fileName;
|
|
21
25
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {FileSystem, isBrowser} from '@loaders.gl/loader-utils';
|
|
6
|
-
import {
|
|
6
|
+
import {FileProviderInterface, isFileProvider} from '@loaders.gl/loader-utils';
|
|
7
7
|
import {FileHandleFile} from '@loaders.gl/loader-utils';
|
|
8
8
|
import {ZipCDFileHeader, makeZipCDHeaderIterator} from '../parse-zip/cd-file-header';
|
|
9
9
|
import {parseZipLocalFileHeader} from '../parse-zip/local-file-header';
|
|
@@ -29,7 +29,7 @@ export const ZIP_COMPRESSION_HANDLERS: {[key: number]: CompressionHandler} = {
|
|
|
29
29
|
*/
|
|
30
30
|
export class ZipFileSystem implements FileSystem {
|
|
31
31
|
/** FileProvider instance promise */
|
|
32
|
-
public fileProvider:
|
|
32
|
+
public fileProvider: FileProviderInterface | null = null;
|
|
33
33
|
public fileName?: string;
|
|
34
34
|
public archive: IndexedArchive | null = null;
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ export class ZipFileSystem implements FileSystem {
|
|
|
37
37
|
* Constructor
|
|
38
38
|
* @param file - instance of FileProvider or file path string
|
|
39
39
|
*/
|
|
40
|
-
constructor(file:
|
|
40
|
+
constructor(file: FileProviderInterface | IndexedArchive | string) {
|
|
41
41
|
// Try to open file in NodeJS
|
|
42
42
|
if (typeof file === 'string') {
|
|
43
43
|
this.fileName = file;
|
package/src/hash-file-utility.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import {MD5Hash} from '@loaders.gl/crypto';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
FileProviderInterface,
|
|
8
8
|
concatenateArrayBuffers,
|
|
9
9
|
concatenateArrayBuffersFromArray
|
|
10
10
|
} from '@loaders.gl/loader-utils';
|
|
@@ -42,7 +42,7 @@ function bufferToHex(buffer: ArrayBuffer, start: number, length: number): string
|
|
|
42
42
|
* @returns ready to use hash info
|
|
43
43
|
*/
|
|
44
44
|
export async function makeHashTableFromZipHeaders(
|
|
45
|
-
fileProvider:
|
|
45
|
+
fileProvider: FileProviderInterface
|
|
46
46
|
): Promise<Record<string, bigint>> {
|
|
47
47
|
const zipCDIterator = makeZipCDHeaderIterator(fileProvider);
|
|
48
48
|
return getHashTable(zipCDIterator);
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DataViewFile,
|
|
7
|
+
FileProviderInterface,
|
|
8
|
+
compareArrayBuffers,
|
|
9
|
+
concatenateArrayBuffers
|
|
10
|
+
} from '@loaders.gl/loader-utils';
|
|
6
11
|
import {parseEoCDRecord} from './end-of-central-directory';
|
|
7
12
|
import {ZipSignature} from './search-from-the-end';
|
|
8
13
|
import {createZip64Info, setFieldToNumber} from './zip64-info-generation';
|
|
@@ -43,12 +48,12 @@ type Zip64Data = {
|
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
46
|
-
const CD_COMPRESSED_SIZE_OFFSET =
|
|
47
|
-
const CD_UNCOMPRESSED_SIZE_OFFSET =
|
|
48
|
-
const CD_FILE_NAME_LENGTH_OFFSET =
|
|
49
|
-
const CD_EXTRA_FIELD_LENGTH_OFFSET =
|
|
50
|
-
const CD_START_DISK_OFFSET =
|
|
51
|
-
const CD_LOCAL_HEADER_OFFSET_OFFSET =
|
|
51
|
+
const CD_COMPRESSED_SIZE_OFFSET = 20;
|
|
52
|
+
const CD_UNCOMPRESSED_SIZE_OFFSET = 24;
|
|
53
|
+
const CD_FILE_NAME_LENGTH_OFFSET = 28;
|
|
54
|
+
const CD_EXTRA_FIELD_LENGTH_OFFSET = 30;
|
|
55
|
+
const CD_START_DISK_OFFSET = 32;
|
|
56
|
+
const CD_LOCAL_HEADER_OFFSET_OFFSET = 42;
|
|
52
57
|
const CD_FILE_NAME_OFFSET = 46n;
|
|
53
58
|
|
|
54
59
|
export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x01, 0x02]);
|
|
@@ -61,30 +66,40 @@ export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x01, 0x02]);
|
|
|
61
66
|
*/
|
|
62
67
|
export const parseZipCDFileHeader = async (
|
|
63
68
|
headerOffset: bigint,
|
|
64
|
-
file:
|
|
69
|
+
file: FileProviderInterface
|
|
65
70
|
): Promise<ZipCDFileHeader | null> => {
|
|
66
|
-
|
|
71
|
+
if (headerOffset >= file.length) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const mainHeader = new DataView(
|
|
75
|
+
await file.slice(headerOffset, headerOffset + CD_FILE_NAME_OFFSET)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const magicBytes = mainHeader.buffer.slice(0, 4);
|
|
67
79
|
if (!compareArrayBuffers(magicBytes, signature.buffer)) {
|
|
68
80
|
return null;
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
const compressedSize = BigInt(
|
|
72
|
-
const uncompressedSize = BigInt(
|
|
73
|
-
const extraFieldLength =
|
|
74
|
-
const startDisk = BigInt(
|
|
75
|
-
const fileNameLength =
|
|
76
|
-
|
|
83
|
+
const compressedSize = BigInt(mainHeader.getUint32(CD_COMPRESSED_SIZE_OFFSET, true));
|
|
84
|
+
const uncompressedSize = BigInt(mainHeader.getUint32(CD_UNCOMPRESSED_SIZE_OFFSET, true));
|
|
85
|
+
const extraFieldLength = mainHeader.getUint16(CD_EXTRA_FIELD_LENGTH_OFFSET, true);
|
|
86
|
+
const startDisk = BigInt(mainHeader.getUint16(CD_START_DISK_OFFSET, true));
|
|
87
|
+
const fileNameLength = mainHeader.getUint16(CD_FILE_NAME_LENGTH_OFFSET, true);
|
|
88
|
+
|
|
89
|
+
const additionalHeader = await file.slice(
|
|
77
90
|
headerOffset + CD_FILE_NAME_OFFSET,
|
|
78
|
-
headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength)
|
|
91
|
+
headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength)
|
|
79
92
|
);
|
|
93
|
+
|
|
94
|
+
const filenameBytes = additionalHeader.slice(0, fileNameLength);
|
|
80
95
|
const fileName = new TextDecoder().decode(filenameBytes);
|
|
81
96
|
|
|
82
97
|
const extraOffset = headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength);
|
|
83
|
-
const oldFormatOffset =
|
|
98
|
+
const oldFormatOffset = mainHeader.getUint32(CD_LOCAL_HEADER_OFFSET_OFFSET, true);
|
|
84
99
|
|
|
85
100
|
const localHeaderOffset = BigInt(oldFormatOffset);
|
|
86
101
|
const extraField = new DataView(
|
|
87
|
-
|
|
102
|
+
additionalHeader.slice(fileNameLength, additionalHeader.byteLength)
|
|
88
103
|
);
|
|
89
104
|
// looking for info that might be also be in zip64 extra field
|
|
90
105
|
|
|
@@ -112,15 +127,18 @@ export const parseZipCDFileHeader = async (
|
|
|
112
127
|
* @param fileProvider - file provider that provider random access to the file
|
|
113
128
|
*/
|
|
114
129
|
export async function* makeZipCDHeaderIterator(
|
|
115
|
-
fileProvider:
|
|
130
|
+
fileProvider: FileProviderInterface
|
|
116
131
|
): AsyncIterable<ZipCDFileHeader> {
|
|
117
|
-
const {cdStartOffset} = await parseEoCDRecord(fileProvider);
|
|
118
|
-
|
|
132
|
+
const {cdStartOffset, cdByteSize} = await parseEoCDRecord(fileProvider);
|
|
133
|
+
const centralDirectory = new DataViewFile(
|
|
134
|
+
new DataView(await fileProvider.slice(cdStartOffset, cdStartOffset + cdByteSize))
|
|
135
|
+
);
|
|
136
|
+
let cdHeader = await parseZipCDFileHeader(0n, centralDirectory);
|
|
119
137
|
while (cdHeader) {
|
|
120
138
|
yield cdHeader;
|
|
121
139
|
cdHeader = await parseZipCDFileHeader(
|
|
122
140
|
cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength),
|
|
123
|
-
|
|
141
|
+
centralDirectory
|
|
124
142
|
);
|
|
125
143
|
}
|
|
126
144
|
}
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
FileProviderInterface,
|
|
7
|
+
compareArrayBuffers,
|
|
8
|
+
concatenateArrayBuffers
|
|
9
|
+
} from '@loaders.gl/loader-utils';
|
|
6
10
|
import {ZipSignature, searchFromTheEnd} from './search-from-the-end';
|
|
7
11
|
import {setFieldToNumber} from './zip64-info-generation';
|
|
8
12
|
|
|
@@ -11,10 +15,12 @@ import {setFieldToNumber} from './zip64-info-generation';
|
|
|
11
15
|
* according to https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
12
16
|
*/
|
|
13
17
|
export type ZipEoCDRecord = {
|
|
14
|
-
/** Relative offset of
|
|
18
|
+
/** Relative offset of cd start */
|
|
15
19
|
cdStartOffset: bigint;
|
|
16
|
-
/**
|
|
20
|
+
/** Total number of central directory records */
|
|
17
21
|
cdRecordsNumber: bigint;
|
|
22
|
+
/** Size of central directory */
|
|
23
|
+
cdByteSize: bigint;
|
|
18
24
|
offsets: ZipEoCDRecordOffsets;
|
|
19
25
|
};
|
|
20
26
|
|
|
@@ -62,10 +68,11 @@ const ZIP64_COMMENT_OFFSET = 56n;
|
|
|
62
68
|
* @param file - FileProvider instance
|
|
63
69
|
* @returns Info from the header
|
|
64
70
|
*/
|
|
65
|
-
export const parseEoCDRecord = async (file:
|
|
71
|
+
export const parseEoCDRecord = async (file: FileProviderInterface): Promise<ZipEoCDRecord> => {
|
|
66
72
|
const zipEoCDOffset = await searchFromTheEnd(file, eoCDSignature);
|
|
67
73
|
|
|
68
74
|
let cdRecordsNumber = BigInt(await file.getUint16(zipEoCDOffset + CD_RECORDS_NUMBER_OFFSET));
|
|
75
|
+
let cdByteSize = BigInt(await file.getUint32(zipEoCDOffset + CD_CD_BYTE_SIZE_OFFSET));
|
|
69
76
|
let cdStartOffset = BigInt(await file.getUint32(zipEoCDOffset + CD_START_OFFSET_OFFSET));
|
|
70
77
|
|
|
71
78
|
let zip64EoCDLocatorOffset = zipEoCDOffset - 20n;
|
|
@@ -83,6 +90,7 @@ export const parseEoCDRecord = async (file: FileProvider): Promise<ZipEoCDRecord
|
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
cdRecordsNumber = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_RECORDS_NUMBER_OFFSET);
|
|
93
|
+
cdByteSize = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_CD_BYTE_SIZE_OFFSET);
|
|
86
94
|
cdStartOffset = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_START_OFFSET_OFFSET);
|
|
87
95
|
} else {
|
|
88
96
|
zip64EoCDLocatorOffset = 0n;
|
|
@@ -91,6 +99,7 @@ export const parseEoCDRecord = async (file: FileProvider): Promise<ZipEoCDRecord
|
|
|
91
99
|
return {
|
|
92
100
|
cdRecordsNumber,
|
|
93
101
|
cdStartOffset,
|
|
102
|
+
cdByteSize,
|
|
94
103
|
offsets: {
|
|
95
104
|
zip64EoCDOffset,
|
|
96
105
|
zip64EoCDLocatorOffset,
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
FileProviderInterface,
|
|
7
|
+
compareArrayBuffers,
|
|
8
|
+
concatenateArrayBuffers
|
|
9
|
+
} from '@loaders.gl/loader-utils';
|
|
6
10
|
import {ZipSignature} from './search-from-the-end';
|
|
7
11
|
import {createZip64Info, setFieldToNumber} from './zip64-info-generation';
|
|
8
12
|
|
|
@@ -26,11 +30,11 @@ export type ZipLocalFileHeader = {
|
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
29
|
-
const COMPRESSION_METHOD_OFFSET =
|
|
30
|
-
const COMPRESSED_SIZE_OFFSET =
|
|
31
|
-
const UNCOMPRESSED_SIZE_OFFSET =
|
|
32
|
-
const FILE_NAME_LENGTH_OFFSET =
|
|
33
|
-
const EXTRA_FIELD_LENGTH_OFFSET =
|
|
33
|
+
const COMPRESSION_METHOD_OFFSET = 8;
|
|
34
|
+
const COMPRESSED_SIZE_OFFSET = 18;
|
|
35
|
+
const UNCOMPRESSED_SIZE_OFFSET = 22;
|
|
36
|
+
const FILE_NAME_LENGTH_OFFSET = 26;
|
|
37
|
+
const EXTRA_FIELD_LENGTH_OFFSET = 28;
|
|
34
38
|
const FILE_NAME_OFFSET = 30n;
|
|
35
39
|
|
|
36
40
|
export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);
|
|
@@ -43,48 +47,52 @@ export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);
|
|
|
43
47
|
*/
|
|
44
48
|
export const parseZipLocalFileHeader = async (
|
|
45
49
|
headerOffset: bigint,
|
|
46
|
-
|
|
50
|
+
file: FileProviderInterface
|
|
47
51
|
): Promise<ZipLocalFileHeader | null> => {
|
|
48
|
-
const
|
|
52
|
+
const mainHeader = new DataView(await file.slice(headerOffset, headerOffset + FILE_NAME_OFFSET));
|
|
53
|
+
|
|
54
|
+
const magicBytes = mainHeader.buffer.slice(0, 4);
|
|
49
55
|
if (!compareArrayBuffers(magicBytes, signature)) {
|
|
50
56
|
return null;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
const fileNameLength =
|
|
59
|
+
const fileNameLength = mainHeader.getUint16(FILE_NAME_LENGTH_OFFSET, true);
|
|
54
60
|
|
|
55
|
-
const
|
|
56
|
-
.decode(
|
|
57
|
-
await buffer.slice(
|
|
58
|
-
headerOffset + FILE_NAME_OFFSET,
|
|
59
|
-
headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength)
|
|
60
|
-
)
|
|
61
|
-
)
|
|
62
|
-
.split('\\')
|
|
63
|
-
.join('/');
|
|
64
|
-
const extraFieldLength = await buffer.getUint16(headerOffset + EXTRA_FIELD_LENGTH_OFFSET);
|
|
61
|
+
const extraFieldLength = mainHeader.getUint16(EXTRA_FIELD_LENGTH_OFFSET, true);
|
|
65
62
|
|
|
66
|
-
|
|
63
|
+
const additionalHeader = await file.slice(
|
|
64
|
+
headerOffset + FILE_NAME_OFFSET,
|
|
65
|
+
headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const fileNameBuffer = additionalHeader.slice(0, fileNameLength);
|
|
67
69
|
|
|
68
|
-
const
|
|
70
|
+
const extraDataBuffer = new DataView(
|
|
71
|
+
additionalHeader.slice(fileNameLength, additionalHeader.byteLength)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const fileName = new TextDecoder().decode(fileNameBuffer).split('\\').join('/');
|
|
75
|
+
|
|
76
|
+
let fileDataOffset = headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength);
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
const compressionMethod = mainHeader.getUint16(COMPRESSION_METHOD_OFFSET, true);
|
|
71
79
|
|
|
72
|
-
let
|
|
80
|
+
let compressedSize = BigInt(mainHeader.getUint32(COMPRESSED_SIZE_OFFSET, true)); // add zip 64 logic
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
let uncompressedSize = BigInt(mainHeader.getUint32(UNCOMPRESSED_SIZE_OFFSET, true)); // add zip 64 logic
|
|
75
83
|
|
|
76
|
-
let offsetInZip64Data =
|
|
84
|
+
let offsetInZip64Data = 4;
|
|
77
85
|
// looking for info that might be also be in zip64 extra field
|
|
78
86
|
if (uncompressedSize === BigInt(0xffffffff)) {
|
|
79
|
-
uncompressedSize =
|
|
80
|
-
offsetInZip64Data +=
|
|
87
|
+
uncompressedSize = extraDataBuffer.getBigUint64(offsetInZip64Data, true);
|
|
88
|
+
offsetInZip64Data += 8;
|
|
81
89
|
}
|
|
82
90
|
if (compressedSize === BigInt(0xffffffff)) {
|
|
83
|
-
compressedSize =
|
|
84
|
-
offsetInZip64Data +=
|
|
91
|
+
compressedSize = extraDataBuffer.getBigUint64(offsetInZip64Data, true);
|
|
92
|
+
offsetInZip64Data += 8;
|
|
85
93
|
}
|
|
86
94
|
if (fileDataOffset === BigInt(0xffffffff)) {
|
|
87
|
-
fileDataOffset =
|
|
95
|
+
fileDataOffset = extraDataBuffer.getBigUint64(offsetInZip64Data, true); // setting it to the one from zip64
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
return {
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {FileProviderInterface} from '@loaders.gl/loader-utils';
|
|
6
6
|
|
|
7
7
|
/** Description of zip signature type */
|
|
8
8
|
export type ZipSignature = Uint8Array;
|
|
9
9
|
|
|
10
|
+
const buffLength = 1024;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* looking for the last occurrence of the provided
|
|
12
14
|
* @param file
|
|
@@ -14,7 +16,7 @@ export type ZipSignature = Uint8Array;
|
|
|
14
16
|
* @returns
|
|
15
17
|
*/
|
|
16
18
|
export const searchFromTheEnd = async (
|
|
17
|
-
file:
|
|
19
|
+
file: FileProviderInterface,
|
|
18
20
|
target: ZipSignature
|
|
19
21
|
): Promise<bigint> => {
|
|
20
22
|
const searchWindow = [
|
|
@@ -24,19 +26,26 @@ export const searchFromTheEnd = async (
|
|
|
24
26
|
undefined
|
|
25
27
|
];
|
|
26
28
|
|
|
27
|
-
let targetOffset =
|
|
29
|
+
let targetOffset = -1;
|
|
28
30
|
|
|
29
31
|
// looking for the last record in the central directory
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
let point = file.length - 4n;
|
|
33
|
+
do {
|
|
34
|
+
const prevPoint = point;
|
|
35
|
+
point -= BigInt(buffLength);
|
|
36
|
+
point = point >= 0n ? point : 0n;
|
|
37
|
+
const buff = new Uint8Array(await file.slice(point, prevPoint));
|
|
38
|
+
for (let i = buff.length - 1; i > -1; i--) {
|
|
39
|
+
searchWindow[3] = searchWindow[2];
|
|
40
|
+
searchWindow[2] = searchWindow[1];
|
|
41
|
+
searchWindow[1] = searchWindow[0];
|
|
42
|
+
searchWindow[0] = buff[i];
|
|
43
|
+
if (searchWindow.every((val, index) => val === target[index])) {
|
|
44
|
+
targetOffset = i;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
38
47
|
}
|
|
39
|
-
}
|
|
48
|
+
} while (targetOffset === -1 && point > 0n);
|
|
40
49
|
|
|
41
|
-
return targetOffset;
|
|
50
|
+
return point + BigInt(targetOffset);
|
|
42
51
|
};
|