molstar
Version:
A comprehensive macromolecular library.
293 lines (292 loc) • 9.34 kB
JavaScript
/**
* Copyright (c) 2020-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* ported from https://github.com/photopea/UZIP.js/blob/master/UZIP.js
* MIT License, Copyright (c) 2018 Photopea
*
* - added `ungzip`
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Unzip = Unzip;
exports.unzip = unzip;
exports.inflateRaw = inflateRaw;
exports.inflate = inflate;
exports.ungzip = ungzip;
exports.deflate = deflate;
exports.Zip = Zip;
exports.zip = zip;
const bin_1 = require("./bin");
const checksum_1 = require("./checksum");
const inflate_1 = require("./inflate");
const deflate_1 = require("./deflate");
const mol_task_1 = require("../../mol-task");
function Unzip(buf, onlyNames = false) {
return mol_task_1.Task.create('Unzip', ctx => unzip(ctx, buf, onlyNames));
}
async function unzip(runtime, buf, onlyNames = false) {
const out = Object.create(null);
const data = new Uint8Array(buf);
if ((0, bin_1.readUshort)(data, 0) !== 0x4b50) {
throw new Error('Invalid ZIP file. A valid ZIP file must start with two magic bytes \\x50\\x4b ("PK" in ASCII).');
}
let eocd = data.length - 4;
while ((0, bin_1.readUint)(data, eocd) !== 0x06054b50)
eocd--;
let o = eocd;
o += 4; // sign = 0x06054b50
o += 4; // disks = 0;
const cnu = (0, bin_1.readUshort)(data, o);
o += 2;
// const cnt = readUshort(data, o);
o += 2;
// const csize = readUint(data, o);
o += 4;
const coffs = (0, bin_1.readUint)(data, o);
o += 4;
o = coffs;
for (let i = 0; i < cnu; i++) {
// const sign = readUint(data, o);
o += 4;
o += 4; // versions;
o += 4; // flag + compr
o += 4; // time
// const crc32 = readUint(data, o);
o += 4;
// const csize = readUint(data, o);
o += 4;
// const usize = readUint(data, o);
o += 4;
const nl = (0, bin_1.readUshort)(data, o);
const el = (0, bin_1.readUshort)(data, o + 2);
const cl = (0, bin_1.readUshort)(data, o + 4);
o += 6; // name, extra, comment
o += 8; // disk, attribs
const roff = (0, bin_1.readUint)(data, o);
o += 4;
o += nl + el + cl;
await _readLocal(runtime, data, roff, out, onlyNames);
}
// console.log(out);
return out;
}
async function _readLocal(runtime, data, o, out, onlyNames) {
// const sign = readUint(data, o);
o += 4;
// const ver = readUshort(data, o);
o += 2;
// const gpflg = readUshort(data, o);
o += 2;
// if((gpflg&8)!=0) throw "unknown sizes";
const cmpr = (0, bin_1.readUshort)(data, o);
o += 2;
// const time = readUint(data, o);
o += 4;
// const crc32 = readUint(data, o);
o += 4;
const csize = (0, bin_1.readUint)(data, o);
o += 4;
const usize = (0, bin_1.readUint)(data, o);
o += 4;
const nlen = (0, bin_1.readUshort)(data, o);
o += 2;
const elen = (0, bin_1.readUshort)(data, o);
o += 2;
const name = (0, bin_1.readUTF8)(data, o, nlen);
// console.log({ name, nlen, elen });
o += nlen;
o += elen;
if (onlyNames) {
out[name] = { size: usize, csize };
return;
}
const file = new Uint8Array(data.buffer, o, csize);
if (cmpr === 0) {
out[name] = file;
}
else if (cmpr === 8) {
const buf = new Uint8Array(usize);
await inflateRaw(runtime, file, buf);
out[name] = buf;
}
else {
throw new Error(`unknown compression method: ${cmpr}`);
}
}
async function inflateRaw(runtime, file, buf) {
return (0, inflate_1._inflate)(runtime, file, buf);
}
function inflate(runtime, file, buf) {
// const CMF = file[0]
// const FLG = file[1]
// const CM = (CMF&15)
// const CINFO = (CMF>>>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
async function ungzip(runtime, file, buf) {
// 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 = (0, bin_1.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 = (0, bin_1.toInt32)((0, bin_1.readUint)(file, file.length - 8));
const isize = (0, bin_1.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 = (0, checksum_1.crc)(inflated, 0, inflated.length);
if (crc32 !== crcValue) {
console.error("ungzip: checksums don't match");
}
return inflated;
}
async function deflate(runtime, data, opts /* , 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 = await (0, deflate_1._deflateRaw)(runtime, data, buf, off, opts.level);
const crcValue = (0, checksum_1.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);
}
async function deflateRaw(runtime, data, opts) {
if (opts === undefined)
opts = { level: 6 };
const buf = new Uint8Array(50 + Math.floor(data.length * 1.1));
const off = await (0, deflate_1._deflateRaw)(runtime, data, buf, 0, opts.level);
return new Uint8Array(buf.buffer, 0, off);
}
function Zip(obj, noCmpr = false) {
return mol_task_1.Task.create('Zip', ctx => zip(ctx, obj, noCmpr));
}
async function zip(runtime, obj, noCmpr = false) {
let tot = 0;
const zpd = {};
for (const p in obj) {
const cpr = !_noNeed(p) && !noCmpr, buf = obj[p];
const crcValue = (0, checksum_1.crc)(buf, 0, buf.length);
zpd[p] = {
cpr,
usize: buf.length,
crc: crcValue,
file: (cpr ? await deflateRaw(runtime, buf) : buf)
};
}
for (const p in zpd)
tot += zpd[p].file.length + 30 + 46 + 2 * (0, bin_1.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;
const 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;
(0, bin_1.writeUint)(data, o, 0x06054b50);
o += 4;
o += 4; // disks
(0, bin_1.writeUshort)(data, o, i);
o += 2;
(0, bin_1.writeUshort)(data, o, i);
o += 2; // number of c d records
(0, bin_1.writeUint)(data, o, csize);
o += 4;
(0, bin_1.writeUint)(data, o, ioff);
o += 4;
o += 2;
return data.buffer;
}
// no need to compress .PNG, .ZIP, .JPEG ....
function _noNeed(fn) {
const ext = fn.split('.').pop().toLowerCase();
return 'png,jpg,jpeg,zip'.indexOf(ext) !== -1;
}
function _writeHeader(data, o, p, obj, t, roff = 0) {
const file = obj.file;
(0, bin_1.writeUint)(data, o, t === 0 ? 0x04034b50 : 0x02014b50);
o += 4; // sign
if (t === 1)
o += 2; // ver made by
(0, bin_1.writeUshort)(data, o, 20);
o += 2; // ver
(0, bin_1.writeUshort)(data, o, 0);
o += 2; // gflip
(0, bin_1.writeUshort)(data, o, obj.cpr ? 8 : 0);
o += 2; // cmpr
(0, bin_1.writeUint)(data, o, 0);
o += 4; // time
(0, bin_1.writeUint)(data, o, obj.crc);
o += 4; // crc32
(0, bin_1.writeUint)(data, o, file.length);
o += 4; // csize
(0, bin_1.writeUint)(data, o, obj.usize);
o += 4; // usize
(0, bin_1.writeUshort)(data, o, (0, bin_1.sizeUTF8)(p));
o += 2; // nlen
(0, bin_1.writeUshort)(data, o, 0);
o += 2; // elen
if (t === 1) {
o += 2; // comment length
o += 2; // disk number
o += 6; // attributes
(0, bin_1.writeUint)(data, o, roff);
o += 4; // usize
}
const nlen = (0, bin_1.writeUTF8)(data, o, p);
o += nlen;
if (t === 0) {
data.set(file, o);
o += file.length;
}
return o;
}
;