// I wrote this inflate years ago, seems to work for zip class BitReader { pos = 0; bits = 0; acc = 0; len: number; data: Uint8Array; constructor(data: Uint8Array) { this.data = data; this.len = data.length; } read(bits: number) { while (this.bits < bits) { if (this.pos >= this.len) throw "EOF"; this.acc |= this.data[this.pos++] << this.bits; this.bits += 8; } let rval = this.acc & ((1 << bits) - 1); this.acc >>= bits; this.bits -= bits; return rval; } read8() { if (this.pos > this.len) throw "EOF"; // flush if (this.bits > 0) { this.bits = 0; this.acc = 0; } return this.data[this.pos++]; } read16() { let rval = this.read8() * 256 + this.read8(); return rval; } } class HuffDic { limit: number[]; codes: number[]; base: number[]; constructor(lengths: number[]) { let counts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let min = 0; let max = 0; for (let i = 0; i < lengths.length; i++) { let len = lengths[i]; if (len != 0) { if (len < min || min == 0) min = len; if (len > max) max = len; counts[len]++; } } this.base = []; this.limit = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ]; let code = 0; let seq = 0; let next_code = []; for (let i = min; i <= max; i++) { let n = counts[i]; next_code[i] = code; this.base[i] = code - seq; code += n; seq += n; this.limit[i] = code - 1; code <<= 1; } this.codes = []; for (let i = 0; i < lengths.length; i++) { let n = lengths[i]; if (n != 0) { code = next_code[n]; next_code[n]++; if (!this.base[n]) this.base[n] = 0; seq = code - this.base[n]; this.codes[seq] = i; } } } readSymbol(r: BitReader) { let v = 0; let l = 0; let offset = 0; for (let i = 1; i < this.limit.length; i++) { v <<= 1; v |= r.read(1); let limit = this.limit[i]; if (v <= limit) { return this.codes[v - this.base[i]]; } } throw "eHUFF"; } } let staticHuff: HuffDic; let distHuff: HuffDic; { let tmp = []; for (let i = 0; i < 144; i++) tmp[i] = 8; for (let i = 144; i < 256; i++) tmp[i] = 9; for (let i = 256; i < 280; i++) tmp[i] = 7; for (let i = 280; i < 288; i++) tmp[i] = 8; staticHuff = new HuffDic(tmp); tmp = []; for (let i = 0; i < 30; i++) { tmp[i] = 5; } distHuff = new HuffDic(tmp); } function inflate(input: Uint8Array) { let r = new BitReader(input); let out = new Uint8Array(65536); let pos = 0; const push = (b: number) => { if (pos + 10 > out.length) { const tmp = new Uint8Array(out.length * 1.5); tmp.set(out); out = tmp; } out[pos++] = b; }; let fin = 0; while (!fin) { fin = r.read(1); let btype = r.read(2); let huff2; let huff3; if (btype == 0) { let len = r.read16(); let nlen = r.read16(); for (let i = 0; i < len; i++) push(r.read8()); } else if (btype == 1) { // fixed huffman huff2 = staticHuff; huff3 = distHuff; } else if (btype == 2) { // dynamic huffman let hlit = r.read(5) + 257; let hdist = r.read(5) + 1; let hclen = r.read(4) + 4; let lengths: number[] = []; for (let i = 0; i < 19; i++) lengths[i] = 0; let xx = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, ]; for (let i = 0; i < hclen; i++) { let t = r.read(3); lengths[xx[i]] = t; } let huff = new HuffDic(lengths); lengths = []; while (true) { let k = huff.readSymbol(r); if (k < 16) { lengths.push(k); } else if (k == 16) { let count = r.read(2) + 3; if (lengths.length == 0) throw new Error("no lengths?"); for (; count > 0; count--) lengths.push(lengths[lengths.length - 1]); } else if (k == 17) { let count = r.read(3) + 3; for (; count > 0; count--) lengths.push(0); } else if (k == 18) { let count = r.read(7) + 11; for (; count > 0; count--) lengths.push(0); } if (lengths.length >= hlit + hdist) break; } huff2 = new HuffDic(lengths.slice(0, hlit)); huff3 = new HuffDic(lengths.slice(hlit)); } else { throw new Error("btype " + btype); } if (huff2) { while (true) { let k = huff2.readSymbol(r); let len = 0; let n = 0; // extra bits if (k < 256) { push(k); continue; } else if (k == 256) { // End of block break; } else if (k < 265) { len = k - 257 + 3; n = 0; } else if (k < 269) { len = (k - 265) * 2 + 11; n = 1; } else if (k < 273) { len = (k - 269) * 4 + 19; n = 2; } else if (k < 277) { len = (k - 273) * 8 + 35; n = 3; } else if (k < 281) { len = (k - 277) * 16 + 67; n = 4; } else if (k < 285) { len = (k - 281) * 32 + 131; n = 5; } else { len = 258; n = 0; } if (n > 0) len += r.read(n); // distance if (r.pos > r.len) throw new Error("EOF"); let dist; if (huff3) dist = huff3.readSymbol(r); else dist = r.read(5); if (dist < 4) { dist++; } else if (dist < 30) { let db = (dist - 2) >> 1; let extra = (dist & 1) << db; extra |= r.read(db); dist = (1 << (db + 1)) + 1 + extra; } else { throw new Error(`dist ${dist}`); } if (dist > pos) throw new Error(`dist ${dist} > pos ${pos}`); let s: number = pos - dist; for (let i = 0; i < len; i++) push(out[s + i]); } } } return out.slice(0, pos); } interface Entry { size: number; start: number; end: number; } export class ZipFile { data: Uint8Array; entries: Record; constructor(data: Uint8Array) { this.data = data; this.entries = {}; let td = new TextDecoder(); let error = (msg: string) => { throw new Error(`${msg} at ${pos}`); }; let view = new DataView(data.buffer); let pos = 0; while (pos < view.byteLength) { let sig = view.getUint32(pos, true); if (sig == 0x02014b50) break; if (sig != 0x04034b50) error(`bad sig ${sig.toString(16)}`); let method = view.getUint16(pos + 8, true); let csize = view.getUint32(pos + 18, true); let size = view.getUint32(pos + 22, true); let fnlen = view.getUint16(pos + 26, true); let eflen = view.getUint16(pos + 28, true); let fn = td.decode(data.slice(pos + 30, pos + 30 + fnlen)); if (size) { let start = pos + 30 + fnlen + eflen; let end = start + csize; this.entries[fn] = { size, start, end }; } pos = pos + 30 + fnlen + eflen + csize; } } getData(name: string) { if (!(name in this.entries)) return let { start, end, size } = this.entries[name]; return inflate(new Uint8Array(this.data.slice(start, end))); } }