import {getByteLength} from './common'; let crcTable!: number[]; export default class Zip { private blob: BlobPart[] = []; private files: {header: BlobPart[], offset: number, name: string}[] = []; private offset = 0; constructor() { if(crcTable !== undefined!) return; crcTable = []; for(let c, n = 0; n < 256; n++) { c = n; for(let k = 0; k < 8; k++) c = ((c & 1) ? ((c >>> 1) ^ 0xEDB88320) : (c >>> 1)); //tslint:disable-line:strict-boolean-expressions crcTable[n] = c; } } addFile(name: string, content: string): void { let crc = -1; let length = 0; const nameLength = getByteLength(name); for(let i = 0, strlen = content.length; i < strlen; ++i) { let c = content.charCodeAt(i); if(c > 0xD800 && c < 0xD8FF) //surrogate pairs c = (c - 0xD800) * 0x400 + content.charCodeAt(++i) - 0xDC00 + 0x10000; let l = c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : c < 0x200000 ? 4 : c < 0x4000000 ? 5 : 6; length += l; let byte = l === 1 ? c : ((0xFF00 >> l) % 256) | (c >>> (l - 1) * 6); --l; while(true) { crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xFF]; if(--l >= 0) byte = ((c >>> (l * 6)) & 0x3F) | 0x80; else break; } } crc = (crc ^ (-1)) >>> 0; const file = { header: [Uint16Array.of(0, 0, 0, 0, 0), Uint32Array.of(crc, length, length), Uint16Array.of(nameLength, 0)], offset: this.offset, name }; this.blob.push(Uint32Array.of(0x04034B50)); this.blob.push(...file.header); this.blob.push(name, content); this.offset += nameLength + length + 30; this.files.push(file); } build(): Blob { const start = this.offset; for(const file of this.files) { this.blob.push(Uint16Array.of(0x4B50, 0x0201, 0)); this.blob.push(...file.header); this.blob.push(Uint16Array.of(0, 0, 0, 0, 0), Uint32Array.of(file.offset), file.name); this.offset += getByteLength(file.name) + 46; } this.blob.push(Uint16Array.of(0x4B50, 0x0605, 0, 0, this.files.length, this.files.length), Uint32Array.of(this.offset - start, start), Uint16Array.of(0)); return new Blob(this.blob); } }