@loaders.gl/zip 4.2.0-alpha.4 → 4.2.0-alpha.6
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 +919 -523
- package/dist/dist.min.js +25 -0
- package/dist/filesystems/IndexedArchive.js +26 -12
- package/dist/filesystems/zip-filesystem.d.ts +2 -2
- package/dist/filesystems/zip-filesystem.d.ts.map +1 -1
- package/dist/filesystems/zip-filesystem.js +125 -88
- package/dist/hash-file-utility.d.ts +1 -1
- package/dist/hash-file-utility.d.ts.map +1 -1
- package/dist/hash-file-utility.js +85 -42
- package/dist/index.cjs +64 -128
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/tar/header.d.ts +1 -1
- package/dist/lib/tar/header.d.ts.map +1 -1
- package/dist/lib/tar/header.js +69 -33
- package/dist/lib/tar/tar.d.ts +1 -1
- package/dist/lib/tar/tar.d.ts.map +1 -1
- package/dist/lib/tar/tar.js +124 -106
- package/dist/lib/tar/types.js +3 -1
- package/dist/lib/tar/utils.js +45 -18
- package/dist/parse-zip/cd-file-header.d.ts +1 -1
- package/dist/parse-zip/cd-file-header.d.ts.map +1 -1
- package/dist/parse-zip/cd-file-header.js +239 -177
- package/dist/parse-zip/end-of-central-directory.js +247 -158
- package/dist/parse-zip/local-file-header.d.ts +1 -1
- package/dist/parse-zip/local-file-header.d.ts.map +1 -1
- package/dist/parse-zip/local-file-header.js +143 -102
- package/dist/parse-zip/search-from-the-end.js +27 -13
- package/dist/parse-zip/zip-composition.js +142 -92
- package/dist/parse-zip/zip64-info-generation.js +64 -41
- package/dist/tar-builder.d.ts +1 -1
- package/dist/tar-builder.d.ts.map +1 -1
- package/dist/tar-builder.js +32 -29
- package/dist/zip-loader.js +52 -41
- package/dist/zip-writer.js +40 -40
- package/package.json +11 -7
- package/src/filesystems/zip-filesystem.ts +4 -0
- package/dist/filesystems/IndexedArchive.js.map +0 -1
- package/dist/filesystems/zip-filesystem.js.map +0 -1
- package/dist/hash-file-utility.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/tar/header.js.map +0 -1
- package/dist/lib/tar/tar.js.map +0 -1
- package/dist/lib/tar/types.js.map +0 -1
- package/dist/lib/tar/utils.js.map +0 -1
- package/dist/parse-zip/cd-file-header.js.map +0 -1
- package/dist/parse-zip/end-of-central-directory.js.map +0 -1
- package/dist/parse-zip/local-file-header.js.map +0 -1
- package/dist/parse-zip/search-from-the-end.js.map +0 -1
- package/dist/parse-zip/zip-composition.js.map +0 -1
- package/dist/parse-zip/zip64-info-generation.js.map +0 -1
- package/dist/tar-builder.js.map +0 -1
- package/dist/zip-loader.js.map +0 -1
- package/dist/zip-writer.js.map +0 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
// loaders.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
1
4
|
import { compareArrayBuffers, concatenateArrayBuffers } from '@loaders.gl/loader-utils';
|
|
2
5
|
import { parseEoCDRecord } from "./end-of-central-directory.js";
|
|
3
6
|
import { createZip64Info, setFieldToNumber } from "./zip64-info-generation.js";
|
|
7
|
+
// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format)
|
|
4
8
|
const CD_COMPRESSED_SIZE_OFFSET = 20n;
|
|
5
9
|
const CD_UNCOMPRESSED_SIZE_OFFSET = 24n;
|
|
6
10
|
const CD_FILE_NAME_LENGTH_OFFSET = 28n;
|
|
@@ -9,191 +13,249 @@ const CD_START_DISK_OFFSET = 32n;
|
|
|
9
13
|
const CD_LOCAL_HEADER_OFFSET_OFFSET = 42n;
|
|
10
14
|
const CD_FILE_NAME_OFFSET = 46n;
|
|
11
15
|
export const signature = new Uint8Array([0x50, 0x4b, 0x01, 0x02]);
|
|
16
|
+
/**
|
|
17
|
+
* Parses central directory file header of zip file
|
|
18
|
+
* @param headerOffset - offset in the archive where header starts
|
|
19
|
+
* @param buffer - buffer containing whole array
|
|
20
|
+
* @returns Info from the header
|
|
21
|
+
*/
|
|
12
22
|
export const parseZipCDFileHeader = async (headerOffset, file) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
const magicBytes = await file.slice(headerOffset, headerOffset + 4n);
|
|
24
|
+
if (!compareArrayBuffers(magicBytes, signature.buffer)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const compressedSize = BigInt(await file.getUint32(headerOffset + CD_COMPRESSED_SIZE_OFFSET));
|
|
28
|
+
const uncompressedSize = BigInt(await file.getUint32(headerOffset + CD_UNCOMPRESSED_SIZE_OFFSET));
|
|
29
|
+
const extraFieldLength = await file.getUint16(headerOffset + CD_EXTRA_FIELD_LENGTH_OFFSET);
|
|
30
|
+
const startDisk = BigInt(await file.getUint16(headerOffset + CD_START_DISK_OFFSET));
|
|
31
|
+
const fileNameLength = await file.getUint16(headerOffset + CD_FILE_NAME_LENGTH_OFFSET);
|
|
32
|
+
const filenameBytes = await file.slice(headerOffset + CD_FILE_NAME_OFFSET, headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength));
|
|
33
|
+
const fileName = new TextDecoder().decode(filenameBytes);
|
|
34
|
+
const extraOffset = headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength);
|
|
35
|
+
const oldFormatOffset = await file.getUint32(headerOffset + CD_LOCAL_HEADER_OFFSET_OFFSET);
|
|
36
|
+
const localHeaderOffset = BigInt(oldFormatOffset);
|
|
37
|
+
const extraField = new DataView(await file.slice(extraOffset, extraOffset + BigInt(extraFieldLength)));
|
|
38
|
+
// looking for info that might be also be in zip64 extra field
|
|
39
|
+
const zip64data = {
|
|
40
|
+
uncompressedSize,
|
|
41
|
+
compressedSize,
|
|
42
|
+
localHeaderOffset,
|
|
43
|
+
startDisk
|
|
44
|
+
};
|
|
45
|
+
const res = findZip64DataInExtra(zip64data, extraField);
|
|
46
|
+
return {
|
|
47
|
+
...zip64data,
|
|
48
|
+
...res,
|
|
49
|
+
extraFieldLength,
|
|
50
|
+
fileNameLength,
|
|
51
|
+
fileName,
|
|
52
|
+
extraOffset
|
|
53
|
+
};
|
|
43
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Create iterator over files of zip archive
|
|
57
|
+
* @param fileProvider - file provider that provider random access to the file
|
|
58
|
+
*/
|
|
44
59
|
export async function* makeZipCDHeaderIterator(fileProvider) {
|
|
45
|
-
|
|
46
|
-
cdStartOffset
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
cdHeader = await parseZipCDFileHeader(cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength), fileProvider);
|
|
52
|
-
}
|
|
60
|
+
const { cdStartOffset } = await parseEoCDRecord(fileProvider);
|
|
61
|
+
let cdHeader = await parseZipCDFileHeader(cdStartOffset, fileProvider);
|
|
62
|
+
while (cdHeader) {
|
|
63
|
+
yield cdHeader;
|
|
64
|
+
cdHeader = await parseZipCDFileHeader(cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength), fileProvider);
|
|
65
|
+
}
|
|
53
66
|
}
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
/**
|
|
68
|
+
* returns the number written in the provided bytes
|
|
69
|
+
* @param bytes two bytes containing the number
|
|
70
|
+
* @returns the number written in the provided bytes
|
|
71
|
+
*/
|
|
72
|
+
const getUint16 = (...bytes) => {
|
|
73
|
+
return bytes[0] + bytes[1] * 16;
|
|
56
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* reads all nesessary data from zip64 record in the extra data
|
|
77
|
+
* @param zip64data values that might be in zip64 record
|
|
78
|
+
* @param extraField full extra data
|
|
79
|
+
* @returns data read from zip64
|
|
80
|
+
*/
|
|
57
81
|
const findZip64DataInExtra = (zip64data, extraField) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
const zip64dataList = findExpectedData(zip64data);
|
|
83
|
+
const zip64DataRes = {};
|
|
84
|
+
if (zip64dataList.length > 0) {
|
|
85
|
+
// total length of data in zip64 notation in bytes
|
|
86
|
+
const zip64chunkSize = zip64dataList.reduce((sum, curr) => sum + curr.length, 0);
|
|
87
|
+
// we're looking for the zip64 nontation header (0x0001)
|
|
88
|
+
// and a size field with a correct value next to it
|
|
89
|
+
const offsetInExtraData = new Uint8Array(extraField.buffer).findIndex((_val, i, arr) => getUint16(arr[i], arr[i + 1]) === 0x0001 &&
|
|
90
|
+
getUint16(arr[i + 2], arr[i + 3]) === zip64chunkSize);
|
|
91
|
+
// then we read all the nesessary fields from the zip64 data
|
|
92
|
+
let bytesRead = 0;
|
|
93
|
+
for (const note of zip64dataList) {
|
|
94
|
+
const offset = bytesRead;
|
|
95
|
+
zip64DataRes[note.name] = extraField.getBigUint64(offsetInExtraData + 4 + offset, true);
|
|
96
|
+
bytesRead = offset + note.length;
|
|
97
|
+
}
|
|
68
98
|
}
|
|
69
|
-
|
|
70
|
-
return zip64DataRes;
|
|
99
|
+
return zip64DataRes;
|
|
71
100
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
zip64dataList.push({
|
|
94
|
-
name: 'startDisk',
|
|
95
|
-
length: 4
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return zip64dataList;
|
|
101
|
+
/**
|
|
102
|
+
* frind data that's expected to be in zip64
|
|
103
|
+
* @param zip64data values that might be in zip64 record
|
|
104
|
+
* @returns zip64 data description
|
|
105
|
+
*/
|
|
106
|
+
const findExpectedData = (zip64data) => {
|
|
107
|
+
// We define fields that should be in zip64 data
|
|
108
|
+
const zip64dataList = [];
|
|
109
|
+
if (zip64data.uncompressedSize === BigInt(0xffffffff)) {
|
|
110
|
+
zip64dataList.push({ name: 'uncompressedSize', length: 8 });
|
|
111
|
+
}
|
|
112
|
+
if (zip64data.compressedSize === BigInt(0xffffffff)) {
|
|
113
|
+
zip64dataList.push({ name: 'compressedSize', length: 8 });
|
|
114
|
+
}
|
|
115
|
+
if (zip64data.localHeaderOffset === BigInt(0xffffffff)) {
|
|
116
|
+
zip64dataList.push({ name: 'localHeaderOffset', length: 8 });
|
|
117
|
+
}
|
|
118
|
+
if (zip64data.startDisk === BigInt(0xffffffff)) {
|
|
119
|
+
zip64dataList.push({ name: 'startDisk', length: 4 });
|
|
120
|
+
}
|
|
121
|
+
return zip64dataList;
|
|
99
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* generates cd header for the file
|
|
125
|
+
* @param options info that can be placed into cd header
|
|
126
|
+
* @returns buffer with header
|
|
127
|
+
*/
|
|
100
128
|
export function generateCDHeader(options) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return resHeader;
|
|
129
|
+
const optionsToUse = {
|
|
130
|
+
...options,
|
|
131
|
+
fnlength: options.fileName.length,
|
|
132
|
+
extraLength: 0
|
|
133
|
+
};
|
|
134
|
+
let zip64header = new ArrayBuffer(0);
|
|
135
|
+
const optionsToZip64 = {};
|
|
136
|
+
if (optionsToUse.offset >= 0xffffffff) {
|
|
137
|
+
optionsToZip64.offset = optionsToUse.offset;
|
|
138
|
+
optionsToUse.offset = BigInt(0xffffffff);
|
|
139
|
+
}
|
|
140
|
+
if (optionsToUse.length >= 0xffffffff) {
|
|
141
|
+
optionsToZip64.size = optionsToUse.length;
|
|
142
|
+
optionsToUse.length = 0xffffffff;
|
|
143
|
+
}
|
|
144
|
+
if (Object.keys(optionsToZip64).length) {
|
|
145
|
+
zip64header = createZip64Info(optionsToZip64);
|
|
146
|
+
optionsToUse.extraLength = zip64header.byteLength;
|
|
147
|
+
}
|
|
148
|
+
const header = new DataView(new ArrayBuffer(Number(CD_FILE_NAME_OFFSET)));
|
|
149
|
+
for (const field of ZIP_HEADER_FIELDS) {
|
|
150
|
+
setFieldToNumber(header, field.size, field.offset, optionsToUse[field.name ?? ''] ?? field.default ?? 0);
|
|
151
|
+
}
|
|
152
|
+
const encodedName = new TextEncoder().encode(optionsToUse.fileName);
|
|
153
|
+
const resHeader = concatenateArrayBuffers(header.buffer, encodedName, zip64header);
|
|
154
|
+
return resHeader;
|
|
128
155
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
},
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
156
|
+
/** Fields map */
|
|
157
|
+
const ZIP_HEADER_FIELDS = [
|
|
158
|
+
// Central directory file header signature = 0x02014b50
|
|
159
|
+
{
|
|
160
|
+
offset: 0,
|
|
161
|
+
size: 4,
|
|
162
|
+
default: new DataView(signature.buffer).getUint32(0, true)
|
|
163
|
+
},
|
|
164
|
+
// Version made by
|
|
165
|
+
{
|
|
166
|
+
offset: 4,
|
|
167
|
+
size: 2,
|
|
168
|
+
default: 45
|
|
169
|
+
},
|
|
170
|
+
// Version needed to extract (minimum)
|
|
171
|
+
{
|
|
172
|
+
offset: 6,
|
|
173
|
+
size: 2,
|
|
174
|
+
default: 45
|
|
175
|
+
},
|
|
176
|
+
// General purpose bit flag
|
|
177
|
+
{
|
|
178
|
+
offset: 8,
|
|
179
|
+
size: 2,
|
|
180
|
+
default: 0
|
|
181
|
+
},
|
|
182
|
+
// Compression method
|
|
183
|
+
{
|
|
184
|
+
offset: 10,
|
|
185
|
+
size: 2,
|
|
186
|
+
default: 0
|
|
187
|
+
},
|
|
188
|
+
// File last modification time
|
|
189
|
+
{
|
|
190
|
+
offset: 12,
|
|
191
|
+
size: 2,
|
|
192
|
+
default: 0
|
|
193
|
+
},
|
|
194
|
+
// File last modification date
|
|
195
|
+
{
|
|
196
|
+
offset: 14,
|
|
197
|
+
size: 2,
|
|
198
|
+
default: 0
|
|
199
|
+
},
|
|
200
|
+
// CRC-32 of uncompressed data
|
|
201
|
+
{
|
|
202
|
+
offset: 16,
|
|
203
|
+
size: 4,
|
|
204
|
+
name: 'crc32'
|
|
205
|
+
},
|
|
206
|
+
// Compressed size (or 0xffffffff for ZIP64)
|
|
207
|
+
{
|
|
208
|
+
offset: 20,
|
|
209
|
+
size: 4,
|
|
210
|
+
name: 'length'
|
|
211
|
+
},
|
|
212
|
+
// Uncompressed size (or 0xffffffff for ZIP64)
|
|
213
|
+
{
|
|
214
|
+
offset: 24,
|
|
215
|
+
size: 4,
|
|
216
|
+
name: 'length'
|
|
217
|
+
},
|
|
218
|
+
// File name length (n)
|
|
219
|
+
{
|
|
220
|
+
offset: 28,
|
|
221
|
+
size: 2,
|
|
222
|
+
name: 'fnlength'
|
|
223
|
+
},
|
|
224
|
+
// Extra field length (m)
|
|
225
|
+
{
|
|
226
|
+
offset: 30,
|
|
227
|
+
size: 2,
|
|
228
|
+
default: 0,
|
|
229
|
+
name: 'extraLength'
|
|
230
|
+
},
|
|
231
|
+
// File comment length (k)
|
|
232
|
+
{
|
|
233
|
+
offset: 32,
|
|
234
|
+
size: 2,
|
|
235
|
+
default: 0
|
|
236
|
+
},
|
|
237
|
+
// Disk number where file starts (or 0xffff for ZIP64)
|
|
238
|
+
{
|
|
239
|
+
offset: 34,
|
|
240
|
+
size: 2,
|
|
241
|
+
default: 0
|
|
242
|
+
},
|
|
243
|
+
// Internal file attributes
|
|
244
|
+
{
|
|
245
|
+
offset: 36,
|
|
246
|
+
size: 2,
|
|
247
|
+
default: 0
|
|
248
|
+
},
|
|
249
|
+
// External file attributes
|
|
250
|
+
{
|
|
251
|
+
offset: 38,
|
|
252
|
+
size: 4,
|
|
253
|
+
default: 0
|
|
254
|
+
},
|
|
255
|
+
// Relative offset of local file header
|
|
256
|
+
{
|
|
257
|
+
offset: 42,
|
|
258
|
+
size: 4,
|
|
259
|
+
name: 'offset'
|
|
260
|
+
}
|
|
261
|
+
];
|