/** * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose * * ported from https://github.com/photopea/UZIP.js/blob/master/UZIP.js * MIT License, Copyright (c) 2018 Photopea * * - added `ungzip` */ import { writeUint, writeUshort, sizeUTF8, writeUTF8, readUshort, readUint, readUTF8, toInt32 } from './bin'; import { crc, adler } from './checksum'; import { _inflate } from './inflate'; import { _deflateRaw } from './deflate'; import { RuntimeContext } from '../../mol-task'; export async function unzip(runtime: RuntimeContext, buf: ArrayBuffer, onlyNames = false) { const out: { [k: string]: Uint8Array | { size: number, csize: number } } = Object.create(null); const data = new Uint8Array(buf); let eocd = data.length-4; while(readUint(data, eocd) !== 0x06054b50) eocd--; let o = eocd; o += 4; // sign = 0x06054b50 o += 4; // disks = 0; const cnu = readUshort(data, o); o += 2; // const cnt = readUshort(data, o); o += 2; // const csize = readUint(data, o); o+=4; const coffs = readUint(data, o); o+=4; o = coffs; for(let i = 0; i>>4); // console.log(CM, CINFO,CMF,FLG); return inflateRaw(runtime, new Uint8Array(file.buffer, file.byteOffset+2, file.length-6), buf); } // https://tools.ietf.org/html/rfc1952 export async function ungzip(runtime: RuntimeContext, file: Uint8Array, buf?: Uint8Array) { // const id1 = file[0] // const id2 = file[1] // const cm = file[2] const flg = file[3] // const mtime = readUint(file, 4) // const xfl = file[8] // const os = file[9] let o = 10 if (flg & 4) { // FEXTRA const xlen = readUshort(file, o) // console.log('FEXTRA', xlen) o += xlen } if (flg & 8) { // FNAME let zero = o while(file[zero] !== 0) ++zero // const name = readUTF8(file, o, zero - o) // console.log('FNAME', name, zero - o) o = zero + 1 } if (flg & 16) { // FCOMMENT let zero = o while(file[zero] !== 0) ++zero // const comment = readUTF8(file, o, zero - o) // console.log('FCOMMENT', comment) o = zero + 1 } if (flg & 1) { // FHCRC // const hcrc = readUshort(file, o) // console.log('FHCRC', hcrc) o += 2 } const crc32 = toInt32(readUint(file, file.length - 8)) const isize = readUint(file, file.length - 4) if (buf === undefined) buf = new Uint8Array(isize) const blocks = new Uint8Array(file.buffer, file.byteOffset + o, file.length - o - 8) const inflated = await inflateRaw(runtime, blocks, buf); const crcValue = crc(inflated, 0, inflated.length) if (crc32 !== crcValue) { console.error("ungzip: checksums don't match") } return inflated } export function deflate(data: Uint8Array, opts?: { level: number }/* , buf, off*/) { if(opts === undefined) opts={ level: 6 }; let off=0 const buf = new Uint8Array(50 + Math.floor(data.length * 1.1)); buf[off]=120; buf[off+1]=156; off+=2; off = _deflateRaw(data, buf, off, opts.level); const crcValue = adler(data, 0, data.length); buf[off+0] = ((crcValue>>>24)&255); buf[off+1] = ((crcValue>>>16)&255); buf[off+2] = ((crcValue>>> 8)&255); buf[off+3] = ((crcValue>>> 0)&255); return new Uint8Array(buf.buffer, 0, off+4); } function deflateRaw(data: Uint8Array, opts?: { level: number }) { if(opts === undefined) opts = { level: 6 }; const buf = new Uint8Array(50 + Math.floor(data.length * 1.1)); const off = _deflateRaw(data, buf, 0, opts.level); return new Uint8Array(buf.buffer, 0, off); } export function zip(obj: { [k: string]: Uint8Array }, noCmpr = false) { let tot = 0; const zpd: { [k: string]: { cpr: boolean, usize: number, crc: number, file: Uint8Array } } = {}; for(const p in obj) { const cpr = !_noNeed(p) && !noCmpr, buf = obj[p] const crcValue = crc(buf, 0, buf.length); zpd[p] = { cpr, usize: buf.length, crc: crcValue, file: (cpr ? deflateRaw(buf) : buf) }; } for(const p in zpd) tot += zpd[p].file.length + 30 + 46 + 2 * sizeUTF8(p); tot += 22; const data = new Uint8Array(tot) let o = 0; const fof = [] for(const p in zpd) { const file = zpd[p]; fof.push(o); o = _writeHeader(data, o, p, file, 0); } let i=0, ioff = o; for(const p in zpd) { const file = zpd[p]; fof.push(o); o = _writeHeader(data, o, p, file, 1, fof[i++]); } const csize = o-ioff; writeUint(data, o, 0x06054b50); o+=4; o += 4; // disks writeUshort(data, o, i); o += 2; writeUshort(data, o, i); o += 2; // number of c d records writeUint(data, o, csize); o += 4; writeUint(data, o, ioff ); o += 4; o += 2; return data.buffer; } // no need to compress .PNG, .ZIP, .JPEG .... function _noNeed(fn: string) { const ext = fn.split('.').pop()!.toLowerCase(); return 'png,jpg,jpeg,zip'.indexOf(ext) !== -1; } function _writeHeader(data: Uint8Array, o: number, p: string, obj: { cpr: boolean, usize: number, crc: number, file: Uint8Array }, t: number, roff = 0) { const file = obj.file; writeUint(data, o, t === 0 ? 0x04034b50 : 0x02014b50); o+=4; // sign if(t === 1) o+=2; // ver made by writeUshort(data, o, 20); o+=2; // ver writeUshort(data, o, 0); o+=2; // gflip writeUshort(data, o, obj.cpr?8:0); o+=2; // cmpr writeUint(data, o, 0); o+=4; // time writeUint(data, o, obj.crc); o+=4; // crc32 writeUint(data, o, file.length); o+=4; // csize writeUint(data, o, obj.usize); o+=4; // usize writeUshort(data, o, sizeUTF8(p)); o+=2; // nlen writeUshort(data, o, 0); o+=2; // elen if(t === 1) { o += 2; // comment length o += 2; // disk number o += 6; // attributes writeUint(data, o, roff); o+=4; // usize } const nlen = writeUTF8(data, o, p); o+= nlen; if(t === 0) { data.set(file, o); o += file.length; } return o; }