@loaders.gl/zip
Version:
Zip Archive Loader
107 lines (92 loc) • 2.9 kB
text/typescript
import {concatenateArrayBuffers} from '@loaders.gl/loader-utils';
export const signature = new Uint8Array([0x01, 0x00]);
/** info that can be placed into zip64 field, doc: https://en.wikipedia.org/wiki/ZIP_(file_format)#ZIP64 */
type Zip64Options = {
/** Original uncompressed file size and Size of compressed data */
size?: number;
/** Offset of local header record */
offset?: number;
};
/**
* creates zip64 extra field
* @param options info that can be placed into zip64 field
* @returns buffer with field
*/
export function createZip64Info(options: Zip64Options): ArrayBuffer {
const optionsToUse = {
...options,
zip64Length: (options.offset ? 1 : 0) * 8 + (options.size ? 1 : 0) * 16
};
const arraysToConcat: ArrayBuffer[] = [];
for (const field of ZIP64_FIELDS) {
if (!optionsToUse[field.name ?? ''] && !field.default) {
continue; // eslint-disable-line no-continue
}
const newValue = new DataView(new ArrayBuffer(field.size));
NUMBER_SETTERS[field.size](newValue, 0, optionsToUse[field.name ?? ''] ?? field.default);
arraysToConcat.push(newValue.buffer);
}
return concatenateArrayBuffers(...arraysToConcat);
}
/**
* Function to write values into buffer
* @param header buffer where to write a value
* @param offset offset of the writing start
* @param value value to be written
*/
type NumberSetter = (header: DataView, offset: number, value: number | bigint) => void;
/**
* Writes values into buffer according to the bytes amount
* @param header header where to write the data
* @param fieldSize size of the field in bytes
* @param fieldOffset offset of the field
* @param value value to be written
*/
export function setFieldToNumber(
header: DataView,
fieldSize: number,
fieldOffset: number | bigint,
value: number | bigint
): void {
NUMBER_SETTERS[fieldSize](header, Number(fieldOffset), value);
}
/** functions to write values into buffer according to the bytes amount */
const NUMBER_SETTERS: {[key: number]: NumberSetter} = {
2: (header, offset, value) => {
header.setUint16(offset, Number(value > 0xffff ? 0xffff : value), true);
},
4: (header, offset, value) => {
header.setUint32(offset, Number(value > 0xffffffff ? 0xffffffff : value), true);
},
8: (header, offset, value) => {
header.setBigUint64(offset, BigInt(value), true);
}
};
/** zip64 info fields description, we need it as a pattern to build a zip64 info */
const ZIP64_FIELDS = [
// Header ID 0x0001
{
size: 2,
default: new DataView(signature.buffer).getUint16(0, true)
},
// Size of the extra field chunk (8, 16, 24 or 28)
{
size: 2,
name: 'zip64Length'
},
// Original uncompressed file size
{
size: 8,
name: 'size'
},
// Size of compressed data
{
size: 8,
name: 'size'
},
// Offset of local header record
{
size: 8,
name: 'offset'
}
];