function warn_or_throw(wrn, msg) { if(wrn) { if(typeof console !== 'undefined') console.error(msg); } else throw new Error(msg); } function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/; prep_blob(blob, 0); var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array*/ = []; var o = { FileIndex: FileIndex, FullPaths: FullPaths }; init_cfb(o, { root: options.root }); /* find end of central directory, start just after signature */ var i = blob.length - 4; while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; blob.l = i + 4; /* parse end of central directory */ blob.l += 4; var fcnt = blob.read_shift(2); blob.l += 6; var start_cd = blob.read_shift(4); /* parse central directory */ blob.l = start_cd; for(i = 0; i < fcnt; ++i) { /* trust local file header instead of CD entry */ blob.l += 20; var csz = blob.read_shift(4); var usz = blob.read_shift(4); var namelen = blob.read_shift(2); var efsz = blob.read_shift(2); var fcsz = blob.read_shift(2); blob.l += 8; var offset = blob.read_shift(4); var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/); blob.l += namelen + efsz + fcsz; var L = blob.l; blob.l = offset + 4; parse_local_file(blob, csz, usz, o, EF); blob.l = L; } return o; } /* head starts just after local file header signature */ function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) { /* [local file header] */ blob.l += 2; var flags = blob.read_shift(2); var meth = blob.read_shift(2); var date = parse_dos_date(blob); if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); var crc32 = blob.read_shift(4); var _csz = blob.read_shift(4); var _usz = blob.read_shift(4); var namelen = blob.read_shift(2); var efsz = blob.read_shift(2); // TODO: flags & (1<<11) // UTF8 var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); if(efsz) { var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/); if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; } blob.l += efsz; /* [encryption header] */ /* [file data] */ var data = blob.slice(blob.l, blob.l + _csz); switch(meth) { case 8: data = _inflateRawSync(blob, _usz); break; case 0: break; default: throw new Error("Unsupported ZIP Compression method " + meth); } /* [data descriptor] */ var wrn = false; if(flags & 8) { crc32 = blob.read_shift(4); if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } _csz = blob.read_shift(4); _usz = blob.read_shift(4); } if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); var _crc32 = CRC32.buf(data, 0); if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); cfb_add(o, name, data, {unsafe: true, mt: date}); }