diff --git a/.gitignore b/.gitignore
index 5f8f8ce..200a0dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,18 +10,26 @@ test_files_pres
*.[cC][sS][vV]
*.[dD][iIbB][fF]
*.[pP][rR][nN]
+*.[pP][mM][dD]*
+*.[pP][dD][fF]
*.[sS][lL][kK]
*.socialcalc
-*.[xX][lL][sSwWcC]
-*.[xX][lL][sS][xXmMbB]
+*.[xX][lL][sSwWcCaAtTmM]
+*.[xX][lL][sSaAtT][xXmMbB]
*.[oO][dD][sS]
*.[fF][oO][dD][sS]
*.[xX][mM][lL]
*.[uU][oO][sS]
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
+*.[bB][iI][fF][fF][23458]
+*.[rR][tT][fF]
+*.[eE][tT][hH]
+*.[zZ][iI][pP]
+*.[mM][sS][iI]
*.123
*.htm
*.html
*.sheetjs
*.exe
+*.img
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 157d868..c65a3a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
+## 1.1.0 (2018-09-04)
+
+* Support for ZIP file format
+
## 1.0.6 (2018-04-09)
* `lastIndexOf` in FAT builder enables larger file parsing at minor cost
diff --git a/README.md b/README.md
index 899d702..948f176 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
-# Compound File Binary Format
+# Container File Blobs
-Pure JS implementation of MS-CFB: Compound File Binary File Format, a container
-format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB)
+Pure JS implementation of various container file formats, including ZIP and CFB.
[](https://travis-ci.org/SheetJS/js-cfb)
[](https://coveralls.io/r/SheetJS/js-cfb?branch=master)
[](https://david-dm.org/sheetjs/js-cfb)
[](https://npmjs.org/package/cfb)
-[](https://ghit.me/repo/sheetjs/js-xlsx)
[](https://github.com/SheetJS/js-cfb)
## Installation
@@ -79,29 +77,48 @@ The CFB object exposes the following methods and properties:
`CFB.parse(blob)` takes a nodejs Buffer or an array of bytes and returns an
parsed representation of the data.
-`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior:
+`CFB.read(blob, opts)` wraps `parse`.
+
+`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
+name, if there are no slashes) and returns an entry object or null if not found.
+
+`CFB.write(cfb, opts)` generates a file based on the container.
+
+`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
+
+### Parse Options
+
+`CFB.read` takes an options argument. `opts.type` controls the behavior:
| `type` | expected input |
-|------------|-----------------------------------------------------------------|
+|------------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"file"` | string: path of file that will be read (nodejs only) |
| (default) | buffer or array of 8-bit unsigned int (byte `n` is `data[n]`) |
-`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
-name, if there are no slashes) and returns an entry object or null if not found.
-`CFB.write(cfb, opts)` generates a file based on the container. `opts.type`
-controls the behavior:
+### Write Options
+
+`CFB.write` and `CFB.writeFile` take options argument.
+
+`opts.type` controls the behavior:
| `type` | output |
-|------------|-----------------------------------------------------------------|
+|------------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"file"` | string: path of file that will be created (nodejs only) |
| (default) | buffer if available, array of 8-bit unsigned int otherwise |
-`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
+`opts.fileType` controls the output file type:
+
+| `fileType` | output |
+|:-------------------|:--------------|
+| `'cfb'` (default) | CFB container |
+| `'zip'` | ZIP file |
+
+`opts.compression` enables DEFLATE compression for ZIP file type.
## Utility Functions
@@ -114,6 +131,12 @@ accept a `name` argument strictly deal with absolute file names:
Set the option `{unsafe:true}` to skip existence checks (for bulk additions)
- `.cfb_del(cfb, name)` deletes the specified file
- `.cfb_mov(cfb, old_name, new_name)` moves the old file to new path and name
+- `.use_zlib(require("zlib"))` loads a nodejs zlib instance.
+
+By default, the library uses a pure JS inflate/deflate implementation. NodeJS
+`zlib.InflateRaw` exposes the number of bytes read in versions after `8.11.0`.
+If a supplied `zlib` does not support the required features, a warning will be
+displayed in the console and the pure JS fallback will be used.
## Container Object Description
@@ -146,11 +169,7 @@ granted by the Apache 2.0 License are reserved by the Original Author.
## References
-
-
- OSP-covered Specifications (click to show)
-
- [MS-CFB]: Compound File Binary File Format
-
-
+ - ZIP `APPNOTE.TXT`: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.4.TXT
+ - RFC1951: https://www.ietf.org/rfc/rfc1951.txt
diff --git a/bin/cfb.njs b/bin/cfb.njs
index 2d69a59..862c818 100755
--- a/bin/cfb.njs
+++ b/bin/cfb.njs
@@ -19,11 +19,16 @@ program
.option('-O, --to-stdout', 'extract raw contents to stdout')
.option('-z, --dump', 'dump internal representation but do not extract')
.option('-q, --quiet', 'process but do not report')
+ .option('--here', 'skip the CFB root name when extracting')
+ .option('--osx', 'use OSX-style unzip listing')
+ .option('--zlib', 'use native zlib')
+ .option('--local', 'print times in local timezone')
.option('--dev', 'development mode')
.option('--read', 'read but do not print out contents');
-
program.parse(process.argv);
+if(program.zlib) X.utils.use_zlib(require('zlib'));
+
var exit = process.exit;
var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
@@ -53,16 +58,32 @@ if(program.dump) {
}
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
+var rlen = cfb.FullPaths[0].length;
+
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
-var format_date = function(date/*:Date*/)/*:string*/ {
- return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
+var format_date = function(date/*:Date*/, osx/*:?any*/)/*:string*/ {
+ var datefmt = osx ? "%02u-%02u-%04u %02u:%02u": "%02u-%02u-%02u %02u:%02u";
+ var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
+ var DD = program.local ? date.getDate() : date.getUTCDate();
+ var YY = (program.local ? date.getFullYear() : date.getUTCFullYear())%(osx ? 10000 : 100);
+ var hh = program.local ? date.getHours() : date.getUTCHours();
+ var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
+ return sprintf(datefmt, MM, DD, YY, hh, mm);
};
if(program.listFiles) {
- var basetime = new Date(1980,0,1);
+ var basetime = new Date(Date.UTC(1980,0,1));
var cnt = 0, rootsize = 0, filesize = 0;
- console.log(" Length Date Time Name");
- console.log(" -------- ---- ---- ----");
+ var fmtstr = "%9lu %s %s";
+ if(program.osx) {
+ console.log("Archive: " + program.args[0]);
+ console.log(" Length Date Time Name");
+ console.log("--------- ---------- ----- ----");
+ fmtstr = "%9lu %s %s";
+ } else {
+ console.log(" Length Date Time Name");
+ console.log(" -------- ---- ---- ----");
+ }
cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
switch(file.type) {
case 5:
@@ -70,13 +91,22 @@ if(program.listFiles) {
rootsize = file.size;
break;
case 2:
- console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i])));
+ var fixname = fix_string(cfb.FullPaths[i]);
+ if(program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
+ if(program.here) fixname = fixname.slice(rlen);
+ console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
filesize += file.size;
++cnt;
}
});
- console.log(" -------- -------");
- console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
+ var outfmt = "%9lu %lu file%s";
+ if(program.osx) {
+ console.log("--------- -------");
+ outfmt = "%9lu %lu file%s";
+ } else {
+ console.log(" -------- -------");
+ }
+ console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
exit(0);
}
@@ -124,8 +154,13 @@ if(program.args.length > 1) {
}
if(program.toStdout) exit(0);
-for(var i=0; i!==cfb.FullPaths.length; ++i) {
+for(var i=program.here ? 1 : 0; i!==cfb.FullPaths.length; ++i) {
if(!cfb.FileIndex[i].name) continue;
- if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
- else write(cfb.FullPaths[i], cfb.FileIndex[i]);
+ var fp = cfb.FullPaths[i];
+ if(program.here) fp = fp.slice(rlen);
+ if(fp.slice(-1) === "/") mkdirp(fp);
+ else {
+ if(fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
+ write(fp, cfb.FileIndex[i]);
+ }
}
diff --git a/bits/00_header.js b/bits/00_header.js
index 20d4aac..dd908f0 100644
--- a/bits/00_header.js
+++ b/bits/00_header.js
@@ -2,5 +2,5 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
-/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
+/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
diff --git a/bits/05_buf.js b/bits/05_buf.js
index 21c30f1..d0a0fd3 100644
--- a/bits/05_buf.js
+++ b/bits/05_buf.js
@@ -5,9 +5,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/;
if(typeof Buffer !== 'undefined') {
var nbfs = !Buffer.from;
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
- Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
+ Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/;
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
+ // $FlowIgnore
+ if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len/*:number*/) {
@@ -16,8 +18,14 @@ function new_raw_buf(len/*:number*/) {
/* jshint +W056 */
}
-var s2a = function s2a(s/*:string*/) {
- if(has_buf) return Buffer.from(s, "binary");
+function new_unsafe_buf(len/*:number*/) {
+ /* jshint -W056 */
+ return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
+ /* jshint +W056 */
+}
+
+var s2a = function s2a(s/*:string*/)/*:any*/ {
+ if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
};
diff --git a/bits/08_blob.js b/bits/08_blob.js
index c059f31..7d56d1e 100644
--- a/bits/08_blob.js
+++ b/bits/08_blob.js
@@ -22,7 +22,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
};
__hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs/*:Array>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
- // $FlowIgnore
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
bconcat = function(bufs/*:Array*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
}
diff --git a/bits/21_crc32.js b/bits/21_crc32.js
new file mode 100644
index 0000000..cc86b0f
--- /dev/null
+++ b/bits/21_crc32.js
@@ -0,0 +1,101 @@
+/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+/*exported CRC32 */
+var CRC32;
+(function (factory) {
+ /*jshint ignore:start */
+ /*eslint-disable */
+ factory(CRC32 = {});
+ /*eslint-enable */
+ /*jshint ignore:end */
+}(function(CRC32) {
+CRC32.version = '1.2.0';
+/* see perf/crc32table.js */
+/*global Int32Array */
+function signed_crc_table()/*:any*/ {
+ var c = 0, table/*:Array*/ = new Array(256);
+
+ for(var n =0; n != 256; ++n){
+ c = n;
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ table[n] = c;
+ }
+
+ return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
+}
+
+var T = signed_crc_table();
+function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = bstr.length - 1;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ }
+ if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ if(buf.length > 10000) return crc32_buf_8(buf, seed);
+ var C = seed ^ -1, L = buf.length - 3;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = buf.length - 7;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1;
+ for(var i = 0, L=str.length, c, d; i < L;) {
+ c = str.charCodeAt(i++);
+ if(c < 0x80) {
+ C = (C>>>8) ^ T[(C ^ c)&0xFF];
+ } else if(c < 0x800) {
+ C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ } else if(c >= 0xD800 && c < 0xE000) {
+ c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
+ C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
+ } else {
+ C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ }
+ }
+ return C ^ -1;
+}
+CRC32.table = T;
+CRC32.bstr = crc32_bstr;
+CRC32.buf = crc32_buf;
+CRC32.str = crc32_str;
+}));
diff --git a/bits/31_version.js b/bits/31_version.js
index 52fb8d0..f1182b7 100644
--- a/bits/31_version.js
+++ b/bits/31_version.js
@@ -1 +1 @@
-exports.version = '1.0.8';
+exports.version = '1.1.0';
diff --git a/bits/37_dosdates.js b/bits/37_dosdates.js
new file mode 100644
index 0000000..c8e9bec
--- /dev/null
+++ b/bits/37_dosdates.js
@@ -0,0 +1,38 @@
+/* -------------------------------------------------------------------------- */
+/* DOS Date format:
+ high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
+ add 1980 to stored year
+ stored second should be doubled
+*/
+
+/* write JS date to buf as a DOS date */
+function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
+ if(typeof date === "string") date = new Date(date);
+ var hms/*:number*/ = date.getHours();
+ hms = hms << 6 | date.getMinutes();
+ hms = hms << 5 | (date.getSeconds()>>>1);
+ buf.write_shift(2, hms);
+ var ymd/*:number*/ = (date.getFullYear() - 1980);
+ ymd = ymd << 4 | (date.getMonth()+1);
+ ymd = ymd << 5 | date.getDate();
+ buf.write_shift(2, ymd);
+}
+
+/* read four bytes from buf and interpret as a DOS date */
+function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
+ var hms = buf.read_shift(2) & 0xFFFF;
+ var ymd = buf.read_shift(2) & 0xFFFF;
+ var val = new Date();
+ var d = ymd & 0x1F; ymd >>>= 5;
+ var m = ymd & 0x0F; ymd >>>= 4;
+ val.setMilliseconds(0);
+ val.setFullYear(ymd + 1980);
+ val.setMonth(m-1);
+ val.setDate(d);
+ var S = hms & 0x1F; hms >>>= 5;
+ var M = hms & 0x3F; hms >>>= 6;
+ val.setHours(hms);
+ val.setMinutes(M);
+ val.setSeconds(S<<1);
+ return val;
+}
diff --git a/bits/38_extrafield.js b/bits/38_extrafield.js
new file mode 100644
index 0000000..a7b1a34
--- /dev/null
+++ b/bits/38_extrafield.js
@@ -0,0 +1,27 @@
+function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
+ prep_blob(blob, 0);
+ var o = /*::(*/{}/*:: :any)*/;
+ var flags = 0;
+ while(blob.l <= blob.length - 4) {
+ var type = blob.read_shift(2);
+ var sz = blob.read_shift(2), tgt = blob.l + sz;
+ var p = {};
+ switch(type) {
+ /* UNIX-style Timestamps */
+ case 0x5455: {
+ flags = blob.read_shift(1);
+ if(flags & 1) p.mtime = blob.read_shift(4);
+ /* for some reason, CD flag corresponds to LFH */
+ if(sz > 5) {
+ if(flags & 2) p.atime = blob.read_shift(4);
+ if(flags & 4) p.ctime = blob.read_shift(4);
+ }
+ if(p.mtime) p.mt = new Date(p.mtime*1000);
+ }
+ break;
+ }
+ blob.l = tgt;
+ o[type] = p;
+ }
+ return o;
+}
diff --git a/bits/40_parse.js b/bits/40_parse.js
index 1dc1d78..633e2b3 100644
--- a/bits/40_parse.js
+++ b/bits/40_parse.js
@@ -1,4 +1,5 @@
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
+if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@@ -19,6 +20,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
+ case 0: if(mv[1] == 0) return parse_zip(file, options);
+ /* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
diff --git a/bits/41_mver.js b/bits/41_mver.js
index 8f5f257..a970977 100644
--- a/bits/41_mver.js
+++ b/bits/41_mver.js
@@ -1,5 +1,6 @@
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
+ if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
diff --git a/bits/60_writehead.js b/bits/60_writehead.js
index 035d5df..47e5bc8 100644
--- a/bits/60_writehead.js
+++ b/bits/60_writehead.js
@@ -1,3 +1,4 @@
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
+ if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
diff --git a/bits/78_zlib.js b/bits/78_zlib.js
new file mode 100644
index 0000000..f76e5d2
--- /dev/null
+++ b/bits/78_zlib.js
@@ -0,0 +1,22 @@
+/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
+var _zlib;
+function use_zlib(zlib) { try {
+ var InflateRaw = zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
+ if(InflRaw.bytesRead) _zlib = zlib;
+ else throw new Error("zlib does not expose bytesRead");
+} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
+
+function _inflateRawSync(payload, usz) {
+ if(!_zlib) return _inflate(payload, usz);
+ var InflateRaw = _zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
+ payload.l += InflRaw.bytesRead;
+ return out;
+}
+
+function _deflateRawSync(payload) {
+ return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
+}
diff --git a/bits/79_flate.js b/bits/79_flate.js
new file mode 100644
index 0000000..d516ccc
--- /dev/null
+++ b/bits/79_flate.js
@@ -0,0 +1,70 @@
+var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
+var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
+
+/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
+var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
+
+var use_typed_arrays = typeof Uint8Array !== 'undefined';
+
+var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
+for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
+
+function bit_swap_n(n, b) {
+ var rev = bitswap8[n & 0xFF];
+ if(b <= 8) return rev >>> (8-b);
+ rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
+ if(b <= 16) return rev >>> (16-b);
+ rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
+ return rev >>> (24-b);
+}
+
+/* helpers for unaligned bit reads */
+function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
+function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
+function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
+function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
+function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
+
+/* works up to n = 3 * 8 + 1 = 25 */
+function read_bits_n(buf, bl, n) {
+ var w = (bl&7), h = (bl>>>3), f = ((1<>> w;
+ if(n < 8 - w) return v & f;
+ v |= buf[h+1]<<(8-w);
+ if(n < 16 - w) return v & f;
+ v |= buf[h+2]<<(16-w);
+ if(n < 24 - w) return v & f;
+ v |= buf[h+3]<<(24-w);
+ return v & f;
+}
+
+/* until ArrayBuffer#realloc is a thing, fake a realloc */
+function realloc(b, sz/*:number*/) {
+ var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
+ if(L >= sz) return b;
+ if(has_buf) {
+ var o = new_unsafe_buf(M);
+ // $FlowIgnore
+ if(b.copy) b.copy(o);
+ else for(; i < b.length; ++i) o[i] = b[i];
+ return o;
+ } else if(use_typed_arrays) {
+ var a = new Uint8Array(M);
+ if(a.set) a.set(b);
+ else for(; i < b.length; ++i) a[i] = b[i];
+ return a;
+ }
+ b.length = M;
+ return b;
+}
+
+/* zero-filled arrays for older browsers */
+function zero_fill_array(n) {
+ var o = new Array(n);
+ for(var i = 0; i < n; ++i) o[i] = 0;
+ return o;
+}
\ No newline at end of file
diff --git a/bits/80_deflate.js b/bits/80_deflate.js
new file mode 100644
index 0000000..dacac21
--- /dev/null
+++ b/bits/80_deflate.js
@@ -0,0 +1,23 @@
+var _deflate = (function() {
+var _deflateRaw = (function() {
+ return function deflateRaw(data, out) {
+ var boff = 0;
+ while(boff < data.length) {
+ var L = Math.min(0xFFFF, data.length - boff);
+ var h = boff + L == data.length;
+ /* TODO: this is only type 0 stored */
+ out.write_shift(1, +h);
+ out.write_shift(2, L);
+ out.write_shift(2, (~L) & 0xFFFF);
+ while(L-- > 0) out[out.l++] = data[boff++];
+ }
+ return out.l;
+ };
+})();
+
+return function(data) {
+ var buf = new_buf(50+Math.floor(data.length*1.1));
+ var off = _deflateRaw(data, buf);
+ return buf.slice(0, off);
+};
+})();
diff --git a/bits/81_inflate.js b/bits/81_inflate.js
new file mode 100644
index 0000000..0424fe0
--- /dev/null
+++ b/bits/81_inflate.js
@@ -0,0 +1,226 @@
+/* modified inflate function also moves original read head */
+
+/* build tree (used for literals and lengths) */
+function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
+ var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
+
+ var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
+ for(i = 0; i < 32; ++i) bl_count[i] = 0;
+
+ for(i = L; i < MAX; ++i) clens[i] = 0;
+ L = clens.length;
+
+ var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
+
+ /* build code tree */
+ for(i = 0; i < L; ++i) {
+ bl_count[(w = clens[i])]++;
+ if(maxlen < w) maxlen = w;
+ ctree[i] = 0;
+ }
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
+ for(i = 0; i < L; ++i) {
+ ccode = clens[i];
+ if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
+ }
+
+ /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
+ for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
+ cmap[ccode|(j<*/ = [];
+ var i = 0;
+ for(;i<32; i++) dlens.push(5);
+ build_tree(dlens, fix_dmap, 32);
+
+ var clens/*:Array*/ = [];
+ i = 0;
+ for(; i<=143; i++) clens.push(8);
+ for(; i<=255; i++) clens.push(9);
+ for(; i<=279; i++) clens.push(7);
+ for(; i<=287; i++) clens.push(8);
+ build_tree(clens, fix_lmap, 288);
+})();
+
+var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
+var dyn_len_1 = 1, dyn_len_2 = 1;
+
+/* 5.5.3 Expanding Huffman Codes */
+function dyn(data, boff/*:number*/) {
+ /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
+ var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
+ var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
+ var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
+ var w = 0;
+
+ /* grab and store code lengths */
+ var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
+ var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+ var maxlen = 1;
+ var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var L = clens.length; /* 19 */
+ for(var i = 0; i < _HCLEN; ++i) {
+ clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
+ if(maxlen < w) maxlen = w;
+ bl_count[w]++;
+ boff += 3;
+ }
+
+ /* build code tree */
+ var ccode = 0;
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
+ for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
+ /* cmap[7 bits from stream] = (off&7) + (lit<<3) */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bitswap8[ctree[i]]>>(8-cleni);
+ for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = [];
+ maxlen = 1;
+ for(; hcodes.length < _HLIT + _HDIST;) {
+ ccode = dyn_cmap[read_bits_7(data, boff)];
+ boff += ccode & 7;
+ switch((ccode >>>= 3)) {
+ case 16:
+ w = 3 + read_bits_2(data, boff); boff += 2;
+ ccode = hcodes[hcodes.length - 1];
+ while(w-- > 0) hcodes.push(ccode);
+ break;
+ case 17:
+ w = 3 + read_bits_3(data, boff); boff += 3;
+ while(w-- > 0) hcodes.push(0);
+ break;
+ case 18:
+ w = 11 + read_bits_7(data, boff); boff += 7;
+ while(w -- > 0) hcodes.push(0);
+ break;
+ default:
+ hcodes.push(ccode);
+ if(maxlen < ccode) maxlen = ccode;
+ break;
+ }
+ }
+
+ /* build literal / length trees */
+ var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
+ for(i = _HLIT; i < 286; ++i) h1[i] = 0;
+ for(i = _HDIST; i < 30; ++i) h2[i] = 0;
+ dyn_len_1 = build_tree(h1, dyn_lmap, 286);
+ dyn_len_2 = build_tree(h2, dyn_dmap, 30);
+ return boff;
+}
+
+/* return [ data, bytesRead ] */
+function inflate(data, usz/*:number*/) {
+ /* shortcircuit for empty buffer [0x03, 0x00] */
+ if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
+
+ /* bit offset */
+ var boff = 0;
+
+ /* header includes final bit and type bits */
+ var header = 0;
+
+ var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
+ var woff = 0;
+ var OL = outbuf.length>>>0;
+ var max_len_1 = 0, max_len_2 = 0;
+
+ while((header&1) == 0) {
+ header = read_bits_3(data, boff); boff += 3;
+ if((header >>> 1) == 0) {
+ /* Stored block */
+ if(boff & 7) boff += 8 - (boff&7);
+ /* 2 bytes sz, 2 bytes bit inverse */
+ var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
+ boff += 32;
+ /* push sz bytes */
+ if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
+ if(typeof data.copy === 'function') {
+ // $FlowIgnore
+ data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
+ woff += sz; boff += 8*sz;
+ } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
+ continue;
+ } else if((header >>> 1) == 1) {
+ /* Fixed Huffman */
+ max_len_1 = 9; max_len_2 = 5;
+ } else {
+ /* Dynamic Huffman */
+ boff = dyn(data, boff);
+ max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
+ }
+ if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
+ for(;;) { // while(true) is apparently out of vogue in modern JS circles
+ /* ingest code and move read head */
+ var bits = read_bits_n(data, boff, max_len_1);
+ var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
+ boff += code & 15; code >>>= 4;
+ /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
+ if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
+ else if(code == 256) break;
+ else {
+ code -= 257;
+ var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
+ var tgt = woff + LEN_LN[code];
+ /* length extra bits */
+ if(len_eb > 0) {
+ tgt += read_bits_n(data, boff, len_eb);
+ boff += len_eb;
+ }
+
+ /* dist code */
+ bits = read_bits_n(data, boff, max_len_2);
+ code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
+ boff += code & 15; code >>>= 4;
+ var dst_eb = (code < 4 ? 0 : (code-2)>>1);
+ var dst = DST_LN[code];
+ /* dist extra bits */
+ if(dst_eb > 0) {
+ dst += read_bits_n(data, boff, dst_eb);
+ boff += dst_eb;
+ }
+
+ /* in the common case, manual byte copy is faster than TA set / Buffer copy */
+ if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
+ while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
+ }
+ }
+ }
+ return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
+}
+
+function _inflate(payload, usz) {
+ var data = payload.slice(payload.l||0);
+ var out = inflate(data, usz);
+ payload.l += out[1];
+ return out[0];
+}
+
diff --git a/bits/82_zparse.js b/bits/82_zparse.js
new file mode 100644
index 0000000..29f1cf7
--- /dev/null
+++ b/bits/82_zparse.js
@@ -0,0 +1,103 @@
+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});
+}
diff --git a/bits/83_zwrite.js b/bits/83_zwrite.js
new file mode 100644
index 0000000..6797bee
--- /dev/null
+++ b/bits/83_zwrite.js
@@ -0,0 +1,102 @@
+function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
+ var _opts = options || {};
+ var out = [], cdirs = [];
+ var o/*:CFBlob*/ = new_buf(1);
+ var method = (_opts.compression ? 8 : 0), flags = 0;
+ var desc = false;
+ if(desc) flags |= 8;
+ var i = 0, j = 0;
+
+ var start_cd = 0, fcnt = 0;
+ var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
+ var crcs = [];
+ var sz_cd = 0;
+
+ for(i = 1; i < cfb.FullPaths.length; ++i) {
+ fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
+ if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
+ var start = start_cd;
+
+ /* TODO: CP437 filename */
+ var namebuf = new_buf(fp.length);
+ for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
+ namebuf = namebuf.slice(0, namebuf.l);
+ crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
+
+ var outbuf = fi.content/*::||[]*/;
+ if(method == 8) outbuf = _deflateRawSync(outbuf);
+
+ /* local file header */
+ o = new_buf(30);
+ o.write_shift(4, 0x04034b50);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ /* TODO: last mod file time/date */
+ if(fi.mt) write_dos_date(o, fi.mt);
+ else o.write_shift(4, 0);
+ o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
+ o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
+ o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+
+ start_cd += o.length;
+ out.push(o);
+ start_cd += namebuf.length;
+ out.push(namebuf);
+
+ /* TODO: encryption header ? */
+ start_cd += outbuf.length;
+ out.push(outbuf);
+
+ /* data descriptor */
+ if(flags & 8) {
+ o = new_buf(12);
+ o.write_shift(-4, crcs[fcnt]);
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ start_cd += o.l;
+ out.push(o);
+ }
+
+ /* central directory */
+ o = new_buf(46);
+ o.write_shift(4, 0x02014b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ o.write_shift(4, 0); /* TODO: last mod file time/date */
+ o.write_shift(-4, crcs[fcnt]);
+
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(4, 0);
+ o.write_shift(4, start);
+
+ sz_cd += o.l;
+ cdirs.push(o);
+ sz_cd += namebuf.length;
+ cdirs.push(namebuf);
+ ++fcnt;
+ }
+
+ /* end of central directory */
+ o = new_buf(22);
+ o.write_shift(4, 0x06054b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, fcnt);
+ o.write_shift(2, fcnt);
+ o.write_shift(4, sz_cd);
+ o.write_shift(4, start_cd);
+ o.write_shift(2, 0);
+
+ return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
+}
diff --git a/bits/85_api.js b/bits/85_api.js
index ff172c3..e34bf03 100644
--- a/bits/85_api.js
+++ b/bits/85_api.js
@@ -25,6 +25,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
+ if(opts.mt) file.mt = opts.mt;
+ if(opts.ct) file.ct = opts.ct;
}
return file;
}
diff --git a/bits/88_cfbexports.js b/bits/88_cfbexports.js
index 5ce2829..5e5a273 100644
--- a/bits/88_cfbexports.js
+++ b/bits/88_cfbexports.js
@@ -13,6 +13,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
+ use_zlib: use_zlib,
+ _deflateRaw: _deflate,
+ _inflateRaw: _inflate,
consts: consts
};
diff --git a/cfb.flow.js b/cfb.flow.js
index a27be02..06792cc 100644
--- a/cfb.flow.js
+++ b/cfb.flow.js
@@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
-/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
+/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@@ -55,9 +55,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/;
if(typeof Buffer !== 'undefined') {
var nbfs = !Buffer.from;
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
- Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
+ Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/;
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
+ // $FlowIgnore
+ if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len/*:number*/) {
@@ -66,8 +68,14 @@ function new_raw_buf(len/*:number*/) {
/* jshint +W056 */
}
-var s2a = function s2a(s/*:string*/) {
- if(has_buf) return Buffer.from(s, "binary");
+function new_unsafe_buf(len/*:number*/) {
+ /* jshint -W056 */
+ return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
+ /* jshint +W056 */
+}
+
+var s2a = function s2a(s/*:string*/)/*:any*/ {
+ if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
};
@@ -96,7 +104,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
};
__hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs/*:Array>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
- // $FlowIgnore
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
bconcat = function(bufs/*:Array*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
}
@@ -184,10 +191,111 @@ type SectorList = {
}
type CFBFiles = {[n:string]:CFBEntry};
*/
+/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+/*exported CRC32 */
+var CRC32;
+(function (factory) {
+ /*jshint ignore:start */
+ /*eslint-disable */
+ factory(CRC32 = {});
+ /*eslint-enable */
+ /*jshint ignore:end */
+}(function(CRC32) {
+CRC32.version = '1.2.0';
+/* see perf/crc32table.js */
+/*global Int32Array */
+function signed_crc_table()/*:any*/ {
+ var c = 0, table/*:Array*/ = new Array(256);
+
+ for(var n =0; n != 256; ++n){
+ c = n;
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ table[n] = c;
+ }
+
+ return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
+}
+
+var T = signed_crc_table();
+function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = bstr.length - 1;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ }
+ if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ if(buf.length > 10000) return crc32_buf_8(buf, seed);
+ var C = seed ^ -1, L = buf.length - 3;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = buf.length - 7;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1;
+ for(var i = 0, L=str.length, c, d; i < L;) {
+ c = str.charCodeAt(i++);
+ if(c < 0x80) {
+ C = (C>>>8) ^ T[(C ^ c)&0xFF];
+ } else if(c < 0x800) {
+ C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ } else if(c >= 0xD800 && c < 0xE000) {
+ c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
+ C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
+ } else {
+ C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ }
+ }
+ return C ^ -1;
+}
+CRC32.table = T;
+CRC32.bstr = crc32_bstr;
+CRC32.buf = crc32_buf;
+CRC32.str = crc32_str;
+}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
-exports.version = '1.0.8';
+exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@@ -208,9 +316,75 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
+/* -------------------------------------------------------------------------- */
+/* DOS Date format:
+ high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
+ add 1980 to stored year
+ stored second should be doubled
+*/
+
+/* write JS date to buf as a DOS date */
+function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
+ if(typeof date === "string") date = new Date(date);
+ var hms/*:number*/ = date.getHours();
+ hms = hms << 6 | date.getMinutes();
+ hms = hms << 5 | (date.getSeconds()>>>1);
+ buf.write_shift(2, hms);
+ var ymd/*:number*/ = (date.getFullYear() - 1980);
+ ymd = ymd << 4 | (date.getMonth()+1);
+ ymd = ymd << 5 | date.getDate();
+ buf.write_shift(2, ymd);
+}
+
+/* read four bytes from buf and interpret as a DOS date */
+function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
+ var hms = buf.read_shift(2) & 0xFFFF;
+ var ymd = buf.read_shift(2) & 0xFFFF;
+ var val = new Date();
+ var d = ymd & 0x1F; ymd >>>= 5;
+ var m = ymd & 0x0F; ymd >>>= 4;
+ val.setMilliseconds(0);
+ val.setFullYear(ymd + 1980);
+ val.setMonth(m-1);
+ val.setDate(d);
+ var S = hms & 0x1F; hms >>>= 5;
+ var M = hms & 0x3F; hms >>>= 6;
+ val.setHours(hms);
+ val.setMinutes(M);
+ val.setSeconds(S<<1);
+ return val;
+}
+function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
+ prep_blob(blob, 0);
+ var o = /*::(*/{}/*:: :any)*/;
+ var flags = 0;
+ while(blob.l <= blob.length - 4) {
+ var type = blob.read_shift(2);
+ var sz = blob.read_shift(2), tgt = blob.l + sz;
+ var p = {};
+ switch(type) {
+ /* UNIX-style Timestamps */
+ case 0x5455: {
+ flags = blob.read_shift(1);
+ if(flags & 1) p.mtime = blob.read_shift(4);
+ /* for some reason, CD flag corresponds to LFH */
+ if(sz > 5) {
+ if(flags & 2) p.atime = blob.read_shift(4);
+ if(flags & 4) p.ctime = blob.read_shift(4);
+ }
+ if(p.mtime) p.mt = new Date(p.mtime*1000);
+ }
+ break;
+ }
+ blob.l = tgt;
+ o[type] = p;
+ }
+ return o;
+}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
+if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@@ -231,6 +405,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
+ case 0: if(mv[1] == 0) return parse_zip(file, options);
+ /* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@@ -309,6 +485,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
+ if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@@ -626,6 +803,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
+ if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb/*:CFBContainer*/)/*:Array*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@@ -826,6 +1004,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string
}
return o;
}
+/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
+var _zlib;
+function use_zlib(zlib) { try {
+ var InflateRaw = zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
+ if(InflRaw.bytesRead) _zlib = zlib;
+ else throw new Error("zlib does not expose bytesRead");
+} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
+
+function _inflateRawSync(payload, usz) {
+ if(!_zlib) return _inflate(payload, usz);
+ var InflateRaw = _zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
+ payload.l += InflRaw.bytesRead;
+ return out;
+}
+
+function _deflateRawSync(payload) {
+ return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
+}
+var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
+var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
+
+/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
+var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
+
+var use_typed_arrays = typeof Uint8Array !== 'undefined';
+
+var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
+for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
+
+function bit_swap_n(n, b) {
+ var rev = bitswap8[n & 0xFF];
+ if(b <= 8) return rev >>> (8-b);
+ rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
+ if(b <= 16) return rev >>> (16-b);
+ rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
+ return rev >>> (24-b);
+}
+
+/* helpers for unaligned bit reads */
+function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
+function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
+function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
+function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
+function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
+
+/* works up to n = 3 * 8 + 1 = 25 */
+function read_bits_n(buf, bl, n) {
+ var w = (bl&7), h = (bl>>>3), f = ((1<>> w;
+ if(n < 8 - w) return v & f;
+ v |= buf[h+1]<<(8-w);
+ if(n < 16 - w) return v & f;
+ v |= buf[h+2]<<(16-w);
+ if(n < 24 - w) return v & f;
+ v |= buf[h+3]<<(24-w);
+ return v & f;
+}
+
+/* until ArrayBuffer#realloc is a thing, fake a realloc */
+function realloc(b, sz/*:number*/) {
+ var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
+ if(L >= sz) return b;
+ if(has_buf) {
+ var o = new_unsafe_buf(M);
+ // $FlowIgnore
+ if(b.copy) b.copy(o);
+ else for(; i < b.length; ++i) o[i] = b[i];
+ return o;
+ } else if(use_typed_arrays) {
+ var a = new Uint8Array(M);
+ if(a.set) a.set(b);
+ else for(; i < b.length; ++i) a[i] = b[i];
+ return a;
+ }
+ b.length = M;
+ return b;
+}
+
+/* zero-filled arrays for older browsers */
+function zero_fill_array(n) {
+ var o = new Array(n);
+ for(var i = 0; i < n; ++i) o[i] = 0;
+ return o;
+}var _deflate = (function() {
+var _deflateRaw = (function() {
+ return function deflateRaw(data, out) {
+ var boff = 0;
+ while(boff < data.length) {
+ var L = Math.min(0xFFFF, data.length - boff);
+ var h = boff + L == data.length;
+ /* TODO: this is only type 0 stored */
+ out.write_shift(1, +h);
+ out.write_shift(2, L);
+ out.write_shift(2, (~L) & 0xFFFF);
+ while(L-- > 0) out[out.l++] = data[boff++];
+ }
+ return out.l;
+ };
+})();
+
+return function(data) {
+ var buf = new_buf(50+Math.floor(data.length*1.1));
+ var off = _deflateRaw(data, buf);
+ return buf.slice(0, off);
+};
+})();
+/* modified inflate function also moves original read head */
+
+/* build tree (used for literals and lengths) */
+function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
+ var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
+
+ var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
+ for(i = 0; i < 32; ++i) bl_count[i] = 0;
+
+ for(i = L; i < MAX; ++i) clens[i] = 0;
+ L = clens.length;
+
+ var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
+
+ /* build code tree */
+ for(i = 0; i < L; ++i) {
+ bl_count[(w = clens[i])]++;
+ if(maxlen < w) maxlen = w;
+ ctree[i] = 0;
+ }
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
+ for(i = 0; i < L; ++i) {
+ ccode = clens[i];
+ if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
+ }
+
+ /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
+ for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
+ cmap[ccode|(j<*/ = [];
+ var i = 0;
+ for(;i<32; i++) dlens.push(5);
+ build_tree(dlens, fix_dmap, 32);
+
+ var clens/*:Array*/ = [];
+ i = 0;
+ for(; i<=143; i++) clens.push(8);
+ for(; i<=255; i++) clens.push(9);
+ for(; i<=279; i++) clens.push(7);
+ for(; i<=287; i++) clens.push(8);
+ build_tree(clens, fix_lmap, 288);
+})();
+
+var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
+var dyn_len_1 = 1, dyn_len_2 = 1;
+
+/* 5.5.3 Expanding Huffman Codes */
+function dyn(data, boff/*:number*/) {
+ /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
+ var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
+ var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
+ var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
+ var w = 0;
+
+ /* grab and store code lengths */
+ var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
+ var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+ var maxlen = 1;
+ var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var L = clens.length; /* 19 */
+ for(var i = 0; i < _HCLEN; ++i) {
+ clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
+ if(maxlen < w) maxlen = w;
+ bl_count[w]++;
+ boff += 3;
+ }
+
+ /* build code tree */
+ var ccode = 0;
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
+ for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
+ /* cmap[7 bits from stream] = (off&7) + (lit<<3) */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bitswap8[ctree[i]]>>(8-cleni);
+ for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = [];
+ maxlen = 1;
+ for(; hcodes.length < _HLIT + _HDIST;) {
+ ccode = dyn_cmap[read_bits_7(data, boff)];
+ boff += ccode & 7;
+ switch((ccode >>>= 3)) {
+ case 16:
+ w = 3 + read_bits_2(data, boff); boff += 2;
+ ccode = hcodes[hcodes.length - 1];
+ while(w-- > 0) hcodes.push(ccode);
+ break;
+ case 17:
+ w = 3 + read_bits_3(data, boff); boff += 3;
+ while(w-- > 0) hcodes.push(0);
+ break;
+ case 18:
+ w = 11 + read_bits_7(data, boff); boff += 7;
+ while(w -- > 0) hcodes.push(0);
+ break;
+ default:
+ hcodes.push(ccode);
+ if(maxlen < ccode) maxlen = ccode;
+ break;
+ }
+ }
+
+ /* build literal / length trees */
+ var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
+ for(i = _HLIT; i < 286; ++i) h1[i] = 0;
+ for(i = _HDIST; i < 30; ++i) h2[i] = 0;
+ dyn_len_1 = build_tree(h1, dyn_lmap, 286);
+ dyn_len_2 = build_tree(h2, dyn_dmap, 30);
+ return boff;
+}
+
+/* return [ data, bytesRead ] */
+function inflate(data, usz/*:number*/) {
+ /* shortcircuit for empty buffer [0x03, 0x00] */
+ if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
+
+ /* bit offset */
+ var boff = 0;
+
+ /* header includes final bit and type bits */
+ var header = 0;
+
+ var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
+ var woff = 0;
+ var OL = outbuf.length>>>0;
+ var max_len_1 = 0, max_len_2 = 0;
+
+ while((header&1) == 0) {
+ header = read_bits_3(data, boff); boff += 3;
+ if((header >>> 1) == 0) {
+ /* Stored block */
+ if(boff & 7) boff += 8 - (boff&7);
+ /* 2 bytes sz, 2 bytes bit inverse */
+ var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
+ boff += 32;
+ /* push sz bytes */
+ if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
+ if(typeof data.copy === 'function') {
+ // $FlowIgnore
+ data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
+ woff += sz; boff += 8*sz;
+ } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
+ continue;
+ } else if((header >>> 1) == 1) {
+ /* Fixed Huffman */
+ max_len_1 = 9; max_len_2 = 5;
+ } else {
+ /* Dynamic Huffman */
+ boff = dyn(data, boff);
+ max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
+ }
+ if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
+ for(;;) { // while(true) is apparently out of vogue in modern JS circles
+ /* ingest code and move read head */
+ var bits = read_bits_n(data, boff, max_len_1);
+ var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
+ boff += code & 15; code >>>= 4;
+ /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
+ if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
+ else if(code == 256) break;
+ else {
+ code -= 257;
+ var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
+ var tgt = woff + LEN_LN[code];
+ /* length extra bits */
+ if(len_eb > 0) {
+ tgt += read_bits_n(data, boff, len_eb);
+ boff += len_eb;
+ }
+
+ /* dist code */
+ bits = read_bits_n(data, boff, max_len_2);
+ code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
+ boff += code & 15; code >>>= 4;
+ var dst_eb = (code < 4 ? 0 : (code-2)>>1);
+ var dst = DST_LN[code];
+ /* dist extra bits */
+ if(dst_eb > 0) {
+ dst += read_bits_n(data, boff, dst_eb);
+ boff += dst_eb;
+ }
+
+ /* in the common case, manual byte copy is faster than TA set / Buffer copy */
+ if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
+ while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
+ }
+ }
+ }
+ return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
+}
+
+function _inflate(payload, usz) {
+ var data = payload.slice(payload.l||0);
+ var out = inflate(data, usz);
+ payload.l += out[1];
+ return out[0];
+}
+
+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});
+}
+function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
+ var _opts = options || {};
+ var out = [], cdirs = [];
+ var o/*:CFBlob*/ = new_buf(1);
+ var method = (_opts.compression ? 8 : 0), flags = 0;
+ var desc = false;
+ if(desc) flags |= 8;
+ var i = 0, j = 0;
+
+ var start_cd = 0, fcnt = 0;
+ var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
+ var crcs = [];
+ var sz_cd = 0;
+
+ for(i = 1; i < cfb.FullPaths.length; ++i) {
+ fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
+ if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
+ var start = start_cd;
+
+ /* TODO: CP437 filename */
+ var namebuf = new_buf(fp.length);
+ for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
+ namebuf = namebuf.slice(0, namebuf.l);
+ crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
+
+ var outbuf = fi.content/*::||[]*/;
+ if(method == 8) outbuf = _deflateRawSync(outbuf);
+
+ /* local file header */
+ o = new_buf(30);
+ o.write_shift(4, 0x04034b50);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ /* TODO: last mod file time/date */
+ if(fi.mt) write_dos_date(o, fi.mt);
+ else o.write_shift(4, 0);
+ o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
+ o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
+ o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+
+ start_cd += o.length;
+ out.push(o);
+ start_cd += namebuf.length;
+ out.push(namebuf);
+
+ /* TODO: encryption header ? */
+ start_cd += outbuf.length;
+ out.push(outbuf);
+
+ /* data descriptor */
+ if(flags & 8) {
+ o = new_buf(12);
+ o.write_shift(-4, crcs[fcnt]);
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ start_cd += o.l;
+ out.push(o);
+ }
+
+ /* central directory */
+ o = new_buf(46);
+ o.write_shift(4, 0x02014b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ o.write_shift(4, 0); /* TODO: last mod file time/date */
+ o.write_shift(-4, crcs[fcnt]);
+
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(4, 0);
+ o.write_shift(4, start);
+
+ sz_cd += o.l;
+ cdirs.push(o);
+ sz_cd += namebuf.length;
+ cdirs.push(namebuf);
+ ++fcnt;
+ }
+
+ /* end of central directory */
+ o = new_buf(22);
+ o.write_shift(4, 0x06054b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, fcnt);
+ o.write_shift(2, fcnt);
+ o.write_shift(4, sz_cd);
+ o.write_shift(4, start_cd);
+ o.write_shift(2, 0);
+
+ return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
+}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
@@ -853,6 +1576,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
+ if(opts.mt) file.mt = opts.mt;
+ if(opts.ct) file.ct = opts.ct;
}
return file;
}
@@ -896,6 +1621,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
+ use_zlib: use_zlib,
+ _deflateRaw: _deflate,
+ _inflateRaw: _inflate,
consts: consts
};
diff --git a/cfb.js b/cfb.js
index 9f86177..dd56c02 100644
--- a/cfb.js
+++ b/cfb.js
@@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
-/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
+/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') {
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
+ // $FlowIgnore
+ if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len) {
@@ -66,8 +68,14 @@ function new_raw_buf(len) {
/* jshint +W056 */
}
+function new_unsafe_buf(len) {
+ /* jshint -W056 */
+ return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
+ /* jshint +W056 */
+}
+
var s2a = function s2a(s) {
- if(has_buf) return Buffer.from(s, "binary");
+ if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
};
@@ -96,7 +104,6 @@ if(has_buf) {
};
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);};
- // $FlowIgnore
s2a = function(s) { return Buffer_from(s, "binary"); };
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); };
}
@@ -166,10 +173,111 @@ function new_buf(sz) {
return o;
}
+/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+/*exported CRC32 */
+var CRC32;
+(function (factory) {
+ /*jshint ignore:start */
+ /*eslint-disable */
+ factory(CRC32 = {});
+ /*eslint-enable */
+ /*jshint ignore:end */
+}(function(CRC32) {
+CRC32.version = '1.2.0';
+/* see perf/crc32table.js */
+/*global Int32Array */
+function signed_crc_table() {
+ var c = 0, table = new Array(256);
+
+ for(var n =0; n != 256; ++n){
+ c = n;
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ table[n] = c;
+ }
+
+ return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
+}
+
+var T = signed_crc_table();
+function crc32_bstr(bstr, seed) {
+ var C = seed ^ -1, L = bstr.length - 1;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ }
+ if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf(buf, seed) {
+ if(buf.length > 10000) return crc32_buf_8(buf, seed);
+ var C = seed ^ -1, L = buf.length - 3;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf_8(buf, seed) {
+ var C = seed ^ -1, L = buf.length - 7;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_str(str, seed) {
+ var C = seed ^ -1;
+ for(var i = 0, L=str.length, c, d; i < L;) {
+ c = str.charCodeAt(i++);
+ if(c < 0x80) {
+ C = (C>>>8) ^ T[(C ^ c)&0xFF];
+ } else if(c < 0x800) {
+ C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ } else if(c >= 0xD800 && c < 0xE000) {
+ c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
+ C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
+ } else {
+ C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ }
+ }
+ return C ^ -1;
+}
+CRC32.table = T;
+CRC32.bstr = crc32_bstr;
+CRC32.buf = crc32_buf;
+CRC32.str = crc32_str;
+}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports = {};
-exports.version = '1.0.8';
+exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@@ -190,9 +298,75 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
+/* -------------------------------------------------------------------------- */
+/* DOS Date format:
+ high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
+ add 1980 to stored year
+ stored second should be doubled
+*/
+
+/* write JS date to buf as a DOS date */
+function write_dos_date(buf, date) {
+ if(typeof date === "string") date = new Date(date);
+ var hms = date.getHours();
+ hms = hms << 6 | date.getMinutes();
+ hms = hms << 5 | (date.getSeconds()>>>1);
+ buf.write_shift(2, hms);
+ var ymd = (date.getFullYear() - 1980);
+ ymd = ymd << 4 | (date.getMonth()+1);
+ ymd = ymd << 5 | date.getDate();
+ buf.write_shift(2, ymd);
+}
+
+/* read four bytes from buf and interpret as a DOS date */
+function parse_dos_date(buf) {
+ var hms = buf.read_shift(2) & 0xFFFF;
+ var ymd = buf.read_shift(2) & 0xFFFF;
+ var val = new Date();
+ var d = ymd & 0x1F; ymd >>>= 5;
+ var m = ymd & 0x0F; ymd >>>= 4;
+ val.setMilliseconds(0);
+ val.setFullYear(ymd + 1980);
+ val.setMonth(m-1);
+ val.setDate(d);
+ var S = hms & 0x1F; hms >>>= 5;
+ var M = hms & 0x3F; hms >>>= 6;
+ val.setHours(hms);
+ val.setMinutes(M);
+ val.setSeconds(S<<1);
+ return val;
+}
+function parse_extra_field(blob) {
+ prep_blob(blob, 0);
+ var o = {};
+ var flags = 0;
+ while(blob.l <= blob.length - 4) {
+ var type = blob.read_shift(2);
+ var sz = blob.read_shift(2), tgt = blob.l + sz;
+ var p = {};
+ switch(type) {
+ /* UNIX-style Timestamps */
+ case 0x5455: {
+ flags = blob.read_shift(1);
+ if(flags & 1) p.mtime = blob.read_shift(4);
+ /* for some reason, CD flag corresponds to LFH */
+ if(sz > 5) {
+ if(flags & 2) p.atime = blob.read_shift(4);
+ if(flags & 4) p.ctime = blob.read_shift(4);
+ }
+ if(p.mtime) p.mt = new Date(p.mtime*1000);
+ }
+ break;
+ }
+ blob.l = tgt;
+ o[type] = p;
+ }
+ return o;
+}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
+if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@@ -213,6 +387,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
+ case 0: if(mv[1] == 0) return parse_zip(file, options);
+ /* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@@ -291,6 +467,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob) {
+ if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) {
function _write(cfb, options) {
var _opts = options || {};
rebuild_cfb(cfb);
+ if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb){
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@@ -802,6 +980,551 @@ function write(cfb, options) {
}
return o;
}
+/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
+var _zlib;
+function use_zlib(zlib) { try {
+ var InflateRaw = zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
+ if(InflRaw.bytesRead) _zlib = zlib;
+ else throw new Error("zlib does not expose bytesRead");
+} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
+
+function _inflateRawSync(payload, usz) {
+ if(!_zlib) return _inflate(payload, usz);
+ var InflateRaw = _zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
+ payload.l += InflRaw.bytesRead;
+ return out;
+}
+
+function _deflateRawSync(payload) {
+ return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
+}
+var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
+var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
+
+/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
+var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
+
+var use_typed_arrays = typeof Uint8Array !== 'undefined';
+
+var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
+for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
+
+function bit_swap_n(n, b) {
+ var rev = bitswap8[n & 0xFF];
+ if(b <= 8) return rev >>> (8-b);
+ rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
+ if(b <= 16) return rev >>> (16-b);
+ rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
+ return rev >>> (24-b);
+}
+
+/* helpers for unaligned bit reads */
+function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
+function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
+function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
+function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
+function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
+
+/* works up to n = 3 * 8 + 1 = 25 */
+function read_bits_n(buf, bl, n) {
+ var w = (bl&7), h = (bl>>>3), f = ((1<>> w;
+ if(n < 8 - w) return v & f;
+ v |= buf[h+1]<<(8-w);
+ if(n < 16 - w) return v & f;
+ v |= buf[h+2]<<(16-w);
+ if(n < 24 - w) return v & f;
+ v |= buf[h+3]<<(24-w);
+ return v & f;
+}
+
+/* until ArrayBuffer#realloc is a thing, fake a realloc */
+function realloc(b, sz) {
+ var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
+ if(L >= sz) return b;
+ if(has_buf) {
+ var o = new_unsafe_buf(M);
+ // $FlowIgnore
+ if(b.copy) b.copy(o);
+ else for(; i < b.length; ++i) o[i] = b[i];
+ return o;
+ } else if(use_typed_arrays) {
+ var a = new Uint8Array(M);
+ if(a.set) a.set(b);
+ else for(; i < b.length; ++i) a[i] = b[i];
+ return a;
+ }
+ b.length = M;
+ return b;
+}
+
+/* zero-filled arrays for older browsers */
+function zero_fill_array(n) {
+ var o = new Array(n);
+ for(var i = 0; i < n; ++i) o[i] = 0;
+ return o;
+}var _deflate = (function() {
+var _deflateRaw = (function() {
+ return function deflateRaw(data, out) {
+ var boff = 0;
+ while(boff < data.length) {
+ var L = Math.min(0xFFFF, data.length - boff);
+ var h = boff + L == data.length;
+ /* TODO: this is only type 0 stored */
+ out.write_shift(1, +h);
+ out.write_shift(2, L);
+ out.write_shift(2, (~L) & 0xFFFF);
+ while(L-- > 0) out[out.l++] = data[boff++];
+ }
+ return out.l;
+ };
+})();
+
+return function(data) {
+ var buf = new_buf(50+Math.floor(data.length*1.1));
+ var off = _deflateRaw(data, buf);
+ return buf.slice(0, off);
+};
+})();
+/* modified inflate function also moves original read head */
+
+/* build tree (used for literals and lengths) */
+function build_tree(clens, cmap, MAX) {
+ var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
+
+ var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
+ for(i = 0; i < 32; ++i) bl_count[i] = 0;
+
+ for(i = L; i < MAX; ++i) clens[i] = 0;
+ L = clens.length;
+
+ var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
+
+ /* build code tree */
+ for(i = 0; i < L; ++i) {
+ bl_count[(w = clens[i])]++;
+ if(maxlen < w) maxlen = w;
+ ctree[i] = 0;
+ }
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
+ for(i = 0; i < L; ++i) {
+ ccode = clens[i];
+ if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
+ }
+
+ /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
+ for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
+ cmap[ccode|(j<>(8-cleni);
+ for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<>>= 3)) {
+ case 16:
+ w = 3 + read_bits_2(data, boff); boff += 2;
+ ccode = hcodes[hcodes.length - 1];
+ while(w-- > 0) hcodes.push(ccode);
+ break;
+ case 17:
+ w = 3 + read_bits_3(data, boff); boff += 3;
+ while(w-- > 0) hcodes.push(0);
+ break;
+ case 18:
+ w = 11 + read_bits_7(data, boff); boff += 7;
+ while(w -- > 0) hcodes.push(0);
+ break;
+ default:
+ hcodes.push(ccode);
+ if(maxlen < ccode) maxlen = ccode;
+ break;
+ }
+ }
+
+ /* build literal / length trees */
+ var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
+ for(i = _HLIT; i < 286; ++i) h1[i] = 0;
+ for(i = _HDIST; i < 30; ++i) h2[i] = 0;
+ dyn_len_1 = build_tree(h1, dyn_lmap, 286);
+ dyn_len_2 = build_tree(h2, dyn_dmap, 30);
+ return boff;
+}
+
+/* return [ data, bytesRead ] */
+function inflate(data, usz) {
+ /* shortcircuit for empty buffer [0x03, 0x00] */
+ if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
+
+ /* bit offset */
+ var boff = 0;
+
+ /* header includes final bit and type bits */
+ var header = 0;
+
+ var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
+ var woff = 0;
+ var OL = outbuf.length>>>0;
+ var max_len_1 = 0, max_len_2 = 0;
+
+ while((header&1) == 0) {
+ header = read_bits_3(data, boff); boff += 3;
+ if((header >>> 1) == 0) {
+ /* Stored block */
+ if(boff & 7) boff += 8 - (boff&7);
+ /* 2 bytes sz, 2 bytes bit inverse */
+ var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
+ boff += 32;
+ /* push sz bytes */
+ if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
+ if(typeof data.copy === 'function') {
+ // $FlowIgnore
+ data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
+ woff += sz; boff += 8*sz;
+ } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
+ continue;
+ } else if((header >>> 1) == 1) {
+ /* Fixed Huffman */
+ max_len_1 = 9; max_len_2 = 5;
+ } else {
+ /* Dynamic Huffman */
+ boff = dyn(data, boff);
+ max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
+ }
+ if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
+ for(;;) { // while(true) is apparently out of vogue in modern JS circles
+ /* ingest code and move read head */
+ var bits = read_bits_n(data, boff, max_len_1);
+ var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
+ boff += code & 15; code >>>= 4;
+ /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
+ if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
+ else if(code == 256) break;
+ else {
+ code -= 257;
+ var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
+ var tgt = woff + LEN_LN[code];
+ /* length extra bits */
+ if(len_eb > 0) {
+ tgt += read_bits_n(data, boff, len_eb);
+ boff += len_eb;
+ }
+
+ /* dist code */
+ bits = read_bits_n(data, boff, max_len_2);
+ code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
+ boff += code & 15; code >>>= 4;
+ var dst_eb = (code < 4 ? 0 : (code-2)>>1);
+ var dst = DST_LN[code];
+ /* dist extra bits */
+ if(dst_eb > 0) {
+ dst += read_bits_n(data, boff, dst_eb);
+ boff += dst_eb;
+ }
+
+ /* in the common case, manual byte copy is faster than TA set / Buffer copy */
+ if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
+ while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
+ }
+ }
+ }
+ return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
+}
+
+function _inflate(payload, usz) {
+ var data = payload.slice(payload.l||0);
+ var out = inflate(data, usz);
+ payload.l += out[1];
+ return out[0];
+}
+
+function warn_or_throw(wrn, msg) {
+ if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
+ else throw new Error(msg);
+}
+
+function parse_zip(file, options) {
+ var blob = file;
+ prep_blob(blob, 0);
+
+ var FileIndex = [], FullPaths = [];
+ 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));
+ 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, csz, usz, o, 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));
+ 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});
+}
+function write_zip(cfb, options) {
+ var _opts = options || {};
+ var out = [], cdirs = [];
+ var o = new_buf(1);
+ var method = (_opts.compression ? 8 : 0), flags = 0;
+ var desc = false;
+ if(desc) flags |= 8;
+ var i = 0, j = 0;
+
+ var start_cd = 0, fcnt = 0;
+ var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
+ var crcs = [];
+ var sz_cd = 0;
+
+ for(i = 1; i < cfb.FullPaths.length; ++i) {
+ fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
+ if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
+ var start = start_cd;
+
+ /* TODO: CP437 filename */
+ var namebuf = new_buf(fp.length);
+ for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
+ namebuf = namebuf.slice(0, namebuf.l);
+ crcs[fcnt] = CRC32.buf(fi.content, 0);
+
+ var outbuf = fi.content;
+ if(method == 8) outbuf = _deflateRawSync(outbuf);
+
+ /* local file header */
+ o = new_buf(30);
+ o.write_shift(4, 0x04034b50);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ /* TODO: last mod file time/date */
+ if(fi.mt) write_dos_date(o, fi.mt);
+ else o.write_shift(4, 0);
+ o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
+ o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
+ o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+
+ start_cd += o.length;
+ out.push(o);
+ start_cd += namebuf.length;
+ out.push(namebuf);
+
+ /* TODO: encryption header ? */
+ start_cd += outbuf.length;
+ out.push(outbuf);
+
+ /* data descriptor */
+ if(flags & 8) {
+ o = new_buf(12);
+ o.write_shift(-4, crcs[fcnt]);
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, fi.content.length);
+ start_cd += o.l;
+ out.push(o);
+ }
+
+ /* central directory */
+ o = new_buf(46);
+ o.write_shift(4, 0x02014b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ o.write_shift(4, 0); /* TODO: last mod file time/date */
+ o.write_shift(-4, crcs[fcnt]);
+
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, fi.content.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(4, 0);
+ o.write_shift(4, start);
+
+ sz_cd += o.l;
+ cdirs.push(o);
+ sz_cd += namebuf.length;
+ cdirs.push(namebuf);
+ ++fcnt;
+ }
+
+ /* end of central directory */
+ o = new_buf(22);
+ o.write_shift(4, 0x06054b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, fcnt);
+ o.write_shift(2, fcnt);
+ o.write_shift(4, sz_cd);
+ o.write_shift(4, start_cd);
+ o.write_shift(2, 0);
+
+ return bconcat(([bconcat((out)), bconcat(cdirs), o]));
+}
function cfb_new(opts) {
var o = ({});
init_cfb(o, opts);
@@ -828,6 +1551,8 @@ file.content = (content);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
+ if(opts.mt) file.mt = opts.mt;
+ if(opts.ct) file.ct = opts.ct;
}
return file;
}
@@ -871,6 +1596,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
+ use_zlib: use_zlib,
+ _deflateRaw: _deflate,
+ _inflateRaw: _inflate,
consts: consts
};
diff --git a/dist/cfb.js b/dist/cfb.js
index 9f86177..dd56c02 100644
--- a/dist/cfb.js
+++ b/dist/cfb.js
@@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
-/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
+/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') {
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
+ // $FlowIgnore
+ if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len) {
@@ -66,8 +68,14 @@ function new_raw_buf(len) {
/* jshint +W056 */
}
+function new_unsafe_buf(len) {
+ /* jshint -W056 */
+ return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
+ /* jshint +W056 */
+}
+
var s2a = function s2a(s) {
- if(has_buf) return Buffer.from(s, "binary");
+ if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
};
@@ -96,7 +104,6 @@ if(has_buf) {
};
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);};
- // $FlowIgnore
s2a = function(s) { return Buffer_from(s, "binary"); };
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); };
}
@@ -166,10 +173,111 @@ function new_buf(sz) {
return o;
}
+/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+/*exported CRC32 */
+var CRC32;
+(function (factory) {
+ /*jshint ignore:start */
+ /*eslint-disable */
+ factory(CRC32 = {});
+ /*eslint-enable */
+ /*jshint ignore:end */
+}(function(CRC32) {
+CRC32.version = '1.2.0';
+/* see perf/crc32table.js */
+/*global Int32Array */
+function signed_crc_table() {
+ var c = 0, table = new Array(256);
+
+ for(var n =0; n != 256; ++n){
+ c = n;
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ table[n] = c;
+ }
+
+ return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
+}
+
+var T = signed_crc_table();
+function crc32_bstr(bstr, seed) {
+ var C = seed ^ -1, L = bstr.length - 1;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ }
+ if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf(buf, seed) {
+ if(buf.length > 10000) return crc32_buf_8(buf, seed);
+ var C = seed ^ -1, L = buf.length - 3;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf_8(buf, seed) {
+ var C = seed ^ -1, L = buf.length - 7;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_str(str, seed) {
+ var C = seed ^ -1;
+ for(var i = 0, L=str.length, c, d; i < L;) {
+ c = str.charCodeAt(i++);
+ if(c < 0x80) {
+ C = (C>>>8) ^ T[(C ^ c)&0xFF];
+ } else if(c < 0x800) {
+ C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ } else if(c >= 0xD800 && c < 0xE000) {
+ c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
+ C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
+ } else {
+ C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ }
+ }
+ return C ^ -1;
+}
+CRC32.table = T;
+CRC32.bstr = crc32_bstr;
+CRC32.buf = crc32_buf;
+CRC32.str = crc32_str;
+}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports = {};
-exports.version = '1.0.8';
+exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@@ -190,9 +298,75 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
+/* -------------------------------------------------------------------------- */
+/* DOS Date format:
+ high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
+ add 1980 to stored year
+ stored second should be doubled
+*/
+
+/* write JS date to buf as a DOS date */
+function write_dos_date(buf, date) {
+ if(typeof date === "string") date = new Date(date);
+ var hms = date.getHours();
+ hms = hms << 6 | date.getMinutes();
+ hms = hms << 5 | (date.getSeconds()>>>1);
+ buf.write_shift(2, hms);
+ var ymd = (date.getFullYear() - 1980);
+ ymd = ymd << 4 | (date.getMonth()+1);
+ ymd = ymd << 5 | date.getDate();
+ buf.write_shift(2, ymd);
+}
+
+/* read four bytes from buf and interpret as a DOS date */
+function parse_dos_date(buf) {
+ var hms = buf.read_shift(2) & 0xFFFF;
+ var ymd = buf.read_shift(2) & 0xFFFF;
+ var val = new Date();
+ var d = ymd & 0x1F; ymd >>>= 5;
+ var m = ymd & 0x0F; ymd >>>= 4;
+ val.setMilliseconds(0);
+ val.setFullYear(ymd + 1980);
+ val.setMonth(m-1);
+ val.setDate(d);
+ var S = hms & 0x1F; hms >>>= 5;
+ var M = hms & 0x3F; hms >>>= 6;
+ val.setHours(hms);
+ val.setMinutes(M);
+ val.setSeconds(S<<1);
+ return val;
+}
+function parse_extra_field(blob) {
+ prep_blob(blob, 0);
+ var o = {};
+ var flags = 0;
+ while(blob.l <= blob.length - 4) {
+ var type = blob.read_shift(2);
+ var sz = blob.read_shift(2), tgt = blob.l + sz;
+ var p = {};
+ switch(type) {
+ /* UNIX-style Timestamps */
+ case 0x5455: {
+ flags = blob.read_shift(1);
+ if(flags & 1) p.mtime = blob.read_shift(4);
+ /* for some reason, CD flag corresponds to LFH */
+ if(sz > 5) {
+ if(flags & 2) p.atime = blob.read_shift(4);
+ if(flags & 4) p.ctime = blob.read_shift(4);
+ }
+ if(p.mtime) p.mt = new Date(p.mtime*1000);
+ }
+ break;
+ }
+ blob.l = tgt;
+ o[type] = p;
+ }
+ return o;
+}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
+if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@@ -213,6 +387,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
+ case 0: if(mv[1] == 0) return parse_zip(file, options);
+ /* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@@ -291,6 +467,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob) {
+ if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) {
function _write(cfb, options) {
var _opts = options || {};
rebuild_cfb(cfb);
+ if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb){
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@@ -802,6 +980,551 @@ function write(cfb, options) {
}
return o;
}
+/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
+var _zlib;
+function use_zlib(zlib) { try {
+ var InflateRaw = zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
+ if(InflRaw.bytesRead) _zlib = zlib;
+ else throw new Error("zlib does not expose bytesRead");
+} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
+
+function _inflateRawSync(payload, usz) {
+ if(!_zlib) return _inflate(payload, usz);
+ var InflateRaw = _zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
+ payload.l += InflRaw.bytesRead;
+ return out;
+}
+
+function _deflateRawSync(payload) {
+ return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
+}
+var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
+var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
+
+/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
+var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
+
+var use_typed_arrays = typeof Uint8Array !== 'undefined';
+
+var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
+for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
+
+function bit_swap_n(n, b) {
+ var rev = bitswap8[n & 0xFF];
+ if(b <= 8) return rev >>> (8-b);
+ rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
+ if(b <= 16) return rev >>> (16-b);
+ rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
+ return rev >>> (24-b);
+}
+
+/* helpers for unaligned bit reads */
+function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
+function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
+function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
+function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
+function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
+
+/* works up to n = 3 * 8 + 1 = 25 */
+function read_bits_n(buf, bl, n) {
+ var w = (bl&7), h = (bl>>>3), f = ((1<>> w;
+ if(n < 8 - w) return v & f;
+ v |= buf[h+1]<<(8-w);
+ if(n < 16 - w) return v & f;
+ v |= buf[h+2]<<(16-w);
+ if(n < 24 - w) return v & f;
+ v |= buf[h+3]<<(24-w);
+ return v & f;
+}
+
+/* until ArrayBuffer#realloc is a thing, fake a realloc */
+function realloc(b, sz) {
+ var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
+ if(L >= sz) return b;
+ if(has_buf) {
+ var o = new_unsafe_buf(M);
+ // $FlowIgnore
+ if(b.copy) b.copy(o);
+ else for(; i < b.length; ++i) o[i] = b[i];
+ return o;
+ } else if(use_typed_arrays) {
+ var a = new Uint8Array(M);
+ if(a.set) a.set(b);
+ else for(; i < b.length; ++i) a[i] = b[i];
+ return a;
+ }
+ b.length = M;
+ return b;
+}
+
+/* zero-filled arrays for older browsers */
+function zero_fill_array(n) {
+ var o = new Array(n);
+ for(var i = 0; i < n; ++i) o[i] = 0;
+ return o;
+}var _deflate = (function() {
+var _deflateRaw = (function() {
+ return function deflateRaw(data, out) {
+ var boff = 0;
+ while(boff < data.length) {
+ var L = Math.min(0xFFFF, data.length - boff);
+ var h = boff + L == data.length;
+ /* TODO: this is only type 0 stored */
+ out.write_shift(1, +h);
+ out.write_shift(2, L);
+ out.write_shift(2, (~L) & 0xFFFF);
+ while(L-- > 0) out[out.l++] = data[boff++];
+ }
+ return out.l;
+ };
+})();
+
+return function(data) {
+ var buf = new_buf(50+Math.floor(data.length*1.1));
+ var off = _deflateRaw(data, buf);
+ return buf.slice(0, off);
+};
+})();
+/* modified inflate function also moves original read head */
+
+/* build tree (used for literals and lengths) */
+function build_tree(clens, cmap, MAX) {
+ var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
+
+ var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
+ for(i = 0; i < 32; ++i) bl_count[i] = 0;
+
+ for(i = L; i < MAX; ++i) clens[i] = 0;
+ L = clens.length;
+
+ var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
+
+ /* build code tree */
+ for(i = 0; i < L; ++i) {
+ bl_count[(w = clens[i])]++;
+ if(maxlen < w) maxlen = w;
+ ctree[i] = 0;
+ }
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
+ for(i = 0; i < L; ++i) {
+ ccode = clens[i];
+ if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
+ }
+
+ /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
+ for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
+ cmap[ccode|(j<>(8-cleni);
+ for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<>>= 3)) {
+ case 16:
+ w = 3 + read_bits_2(data, boff); boff += 2;
+ ccode = hcodes[hcodes.length - 1];
+ while(w-- > 0) hcodes.push(ccode);
+ break;
+ case 17:
+ w = 3 + read_bits_3(data, boff); boff += 3;
+ while(w-- > 0) hcodes.push(0);
+ break;
+ case 18:
+ w = 11 + read_bits_7(data, boff); boff += 7;
+ while(w -- > 0) hcodes.push(0);
+ break;
+ default:
+ hcodes.push(ccode);
+ if(maxlen < ccode) maxlen = ccode;
+ break;
+ }
+ }
+
+ /* build literal / length trees */
+ var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
+ for(i = _HLIT; i < 286; ++i) h1[i] = 0;
+ for(i = _HDIST; i < 30; ++i) h2[i] = 0;
+ dyn_len_1 = build_tree(h1, dyn_lmap, 286);
+ dyn_len_2 = build_tree(h2, dyn_dmap, 30);
+ return boff;
+}
+
+/* return [ data, bytesRead ] */
+function inflate(data, usz) {
+ /* shortcircuit for empty buffer [0x03, 0x00] */
+ if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
+
+ /* bit offset */
+ var boff = 0;
+
+ /* header includes final bit and type bits */
+ var header = 0;
+
+ var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
+ var woff = 0;
+ var OL = outbuf.length>>>0;
+ var max_len_1 = 0, max_len_2 = 0;
+
+ while((header&1) == 0) {
+ header = read_bits_3(data, boff); boff += 3;
+ if((header >>> 1) == 0) {
+ /* Stored block */
+ if(boff & 7) boff += 8 - (boff&7);
+ /* 2 bytes sz, 2 bytes bit inverse */
+ var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
+ boff += 32;
+ /* push sz bytes */
+ if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
+ if(typeof data.copy === 'function') {
+ // $FlowIgnore
+ data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
+ woff += sz; boff += 8*sz;
+ } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
+ continue;
+ } else if((header >>> 1) == 1) {
+ /* Fixed Huffman */
+ max_len_1 = 9; max_len_2 = 5;
+ } else {
+ /* Dynamic Huffman */
+ boff = dyn(data, boff);
+ max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
+ }
+ if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
+ for(;;) { // while(true) is apparently out of vogue in modern JS circles
+ /* ingest code and move read head */
+ var bits = read_bits_n(data, boff, max_len_1);
+ var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
+ boff += code & 15; code >>>= 4;
+ /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
+ if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
+ else if(code == 256) break;
+ else {
+ code -= 257;
+ var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
+ var tgt = woff + LEN_LN[code];
+ /* length extra bits */
+ if(len_eb > 0) {
+ tgt += read_bits_n(data, boff, len_eb);
+ boff += len_eb;
+ }
+
+ /* dist code */
+ bits = read_bits_n(data, boff, max_len_2);
+ code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
+ boff += code & 15; code >>>= 4;
+ var dst_eb = (code < 4 ? 0 : (code-2)>>1);
+ var dst = DST_LN[code];
+ /* dist extra bits */
+ if(dst_eb > 0) {
+ dst += read_bits_n(data, boff, dst_eb);
+ boff += dst_eb;
+ }
+
+ /* in the common case, manual byte copy is faster than TA set / Buffer copy */
+ if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
+ while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
+ }
+ }
+ }
+ return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
+}
+
+function _inflate(payload, usz) {
+ var data = payload.slice(payload.l||0);
+ var out = inflate(data, usz);
+ payload.l += out[1];
+ return out[0];
+}
+
+function warn_or_throw(wrn, msg) {
+ if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
+ else throw new Error(msg);
+}
+
+function parse_zip(file, options) {
+ var blob = file;
+ prep_blob(blob, 0);
+
+ var FileIndex = [], FullPaths = [];
+ 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));
+ 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, csz, usz, o, 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));
+ 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});
+}
+function write_zip(cfb, options) {
+ var _opts = options || {};
+ var out = [], cdirs = [];
+ var o = new_buf(1);
+ var method = (_opts.compression ? 8 : 0), flags = 0;
+ var desc = false;
+ if(desc) flags |= 8;
+ var i = 0, j = 0;
+
+ var start_cd = 0, fcnt = 0;
+ var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
+ var crcs = [];
+ var sz_cd = 0;
+
+ for(i = 1; i < cfb.FullPaths.length; ++i) {
+ fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
+ if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
+ var start = start_cd;
+
+ /* TODO: CP437 filename */
+ var namebuf = new_buf(fp.length);
+ for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
+ namebuf = namebuf.slice(0, namebuf.l);
+ crcs[fcnt] = CRC32.buf(fi.content, 0);
+
+ var outbuf = fi.content;
+ if(method == 8) outbuf = _deflateRawSync(outbuf);
+
+ /* local file header */
+ o = new_buf(30);
+ o.write_shift(4, 0x04034b50);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ /* TODO: last mod file time/date */
+ if(fi.mt) write_dos_date(o, fi.mt);
+ else o.write_shift(4, 0);
+ o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
+ o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
+ o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+
+ start_cd += o.length;
+ out.push(o);
+ start_cd += namebuf.length;
+ out.push(namebuf);
+
+ /* TODO: encryption header ? */
+ start_cd += outbuf.length;
+ out.push(outbuf);
+
+ /* data descriptor */
+ if(flags & 8) {
+ o = new_buf(12);
+ o.write_shift(-4, crcs[fcnt]);
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, fi.content.length);
+ start_cd += o.l;
+ out.push(o);
+ }
+
+ /* central directory */
+ o = new_buf(46);
+ o.write_shift(4, 0x02014b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ o.write_shift(4, 0); /* TODO: last mod file time/date */
+ o.write_shift(-4, crcs[fcnt]);
+
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, fi.content.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(4, 0);
+ o.write_shift(4, start);
+
+ sz_cd += o.l;
+ cdirs.push(o);
+ sz_cd += namebuf.length;
+ cdirs.push(namebuf);
+ ++fcnt;
+ }
+
+ /* end of central directory */
+ o = new_buf(22);
+ o.write_shift(4, 0x06054b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, fcnt);
+ o.write_shift(2, fcnt);
+ o.write_shift(4, sz_cd);
+ o.write_shift(4, start_cd);
+ o.write_shift(2, 0);
+
+ return bconcat(([bconcat((out)), bconcat(cdirs), o]));
+}
function cfb_new(opts) {
var o = ({});
init_cfb(o, opts);
@@ -828,6 +1551,8 @@ file.content = (content);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
+ if(opts.mt) file.mt = opts.mt;
+ if(opts.ct) file.ct = opts.ct;
}
return file;
}
@@ -871,6 +1596,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
+ use_zlib: use_zlib,
+ _deflateRaw: _deflate,
+ _inflateRaw: _inflate,
consts: consts
};
diff --git a/dist/cfb.min.js b/dist/cfb.min.js
index 2130e59..70adff3 100644
--- a/dist/cfb.min.js
+++ b/dist/cfb.min.js
@@ -1,2 +1,2 @@
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
-var Base64=function e(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){var t="";var i=0,n=0,f=0,a=0,s=0,h=0,l=0;for(var o=0;o>2;n=r.charCodeAt(o++);s=(i&3)<<4|n>>4;f=r.charCodeAt(o++);h=(n&15)<<2|f>>6;l=f&63;if(isNaN(n)){h=l=64}else if(isNaN(f)){l=64}t+=e.charAt(a)+e.charAt(s)+e.charAt(h)+e.charAt(l)}return t},decode:function r(t){var i="";var n=0,f=0,a=0,s=0,h=0,l=0,o=0;t=t.replace(/[^\w\+\/\=]/g,"");for(var u=0;u>4;i+=String.fromCharCode(n);l=e.indexOf(t.charAt(u++));f=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(f)}o=e.indexOf(t.charAt(u++));a=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(a)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;var Buffer_from=function(){};if(typeof Buffer!=="undefined"){var nbfs=!Buffer.from;if(!nbfs)try{Buffer.from("foo","utf8")}catch(e){nbfs=true}Buffer_from=nbfs?function(e,r){return r?new Buffer(e,r):new Buffer(e)}:Buffer.from.bind(Buffer);if(!Buffer.alloc)Buffer.alloc=function(e){return new Buffer(e)}}function new_raw_buf(e){return has_buf?Buffer.alloc(e):new Array(e)}var s2a=function r(e){if(has_buf)return Buffer.from(e,"binary");return e.split("").map(function(e){return e.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/g;var __toBuffer=function(e){var r=[];for(var t=0;t0&&Buffer.isBuffer(e[0][0])?Buffer.concat(e[0]):___toBuffer(e)};s2a=function(e){return Buffer_from(e,"binary")};bconcat=function(e){return Buffer.isBuffer(e[0])?Buffer.concat(e):__bconcat(e)}}var __readUInt8=function(e,r){return e[r]};var __readUInt16LE=function(e,r){return e[r+1]*(1<<8)+e[r]};var __readInt16LE=function(e,r){var t=e[r+1]*(1<<8)+e[r];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(e,r){return e[r+3]*(1<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};var __readInt32LE=function(e,r){return(e[r+3]<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};function ReadShift(e,r){var t,i,n=0;switch(e){case 1:t=__readUInt8(this,this.l);break;case 2:t=(r!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,e);}this.l+=e;if(n===0)return t;return i}var __writeUInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>>8&255;e[t+2]=r>>>16&255;e[t+3]=r>>>24&255};var __writeInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>8&255;e[t+2]=r>>16&255;e[t+3]=r>>24&255};function WriteShift(e,r,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=r&255;break;case 4:i=4;__writeUInt32LE(this,r,this.l);break;case-4:i=4;__writeInt32LE(this,r,this.l);break;}this.l+=i;return this}function CheckField(e,r){var t=__hexlify(this,this.l,e.length>>1);if(t!==e)throw new Error(r+"Expected "+e+" saw "+t);this.l+=e.length>>1}function prep_blob(e,r){e.l=r;e.read_shift=ReadShift;e.chk=CheckField;e.write_shift=WriteShift}function new_buf(e){var r=new_raw_buf(e);prep_blob(r,0);return r}var CFB=function t(){var e={};e.version="1.0.8";function r(e,r){var t=e.split("/"),i=r.split("/");for(var n=0,f=0,a=Math.min(t.length,i.length);n0&&u!==y)C[u].name="!MiniFAT";C[w[0]].name="!FAT";C.fat_addrs=w;C.ssz=i;var S={},B=[],m=[],A=[];v(a,C,E,B,n,S,m,u);o(m,A,B);B.shift();var L={FileIndex:m,FullPaths:A};if(r&&r.raw)L.raw={header:g,sectors:E};return L}function s(e){e.chk(S,"Header Signature: ");e.chk(m,"CLSID: ");var r=e.read_shift(2,"u");return[e.read_shift(2,"u"),r]}function h(e,r){var t=9;e.l+=2;switch(t=e.read_shift(2)){case 9:if(r!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(r!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}e.chk("0600","Mini Sector Shift: ");e.chk("000000000000","Reserved: ")}function l(e,r){var t=Math.ceil(e.length/r)-1;var i=[];for(var n=1;n0&&a>=0){f.push(r.slice(a*C,a*C+C));n-=C;a=__readInt32LE(t,a*4)}if(f.length===0)return new_buf(0);return bconcat(f).slice(0,e.size)}function c(e,r,t,i,n){var f=y;if(e===y){if(r!==0)throw new Error("DIFAT chain shorter than expected")}else if(e!==-1){var a=t[e],s=(i>>>2)-1;if(!a)return;for(var h=0;h=0;){n[h]=true;f[f.length]=h;a.push(e[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!e[o])break;h=__readInt32LE(e[o],l)}return{nodes:f,data:__toBuffer([a])}}function d(e,r,t,i){var n=e.length,f=[];var a=[],s=[],h=[];var l=i-1,o=0,u=0,c=0,_=0;for(o=0;o=n)c-=n;if(a[c])continue;h=[];for(u=c;u>=0;){a[u]=true;s[s.length]=u;h.push(e[u]);var d=t[Math.floor(u*4/i)];_=u*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+u+" 4 "+i);if(!e[d])break;u=__readInt32LE(e[d],_)}f[c]={nodes:s,data:__toBuffer([h])}}return f}function v(e,r,t,i,n,f,a,s){var h=0,l=i.length?2:0;var o=r[e].data;var c=0,d=0,v;for(;c0&&h!==y)r[h].name="!StreamData"}else if(F.size>=4096){F.storage="fat";if(r[F.start]===undefined)r[F.start]=_(t,F.start,r.fat_addrs,r.ssz);r[F.start].name=F.name;F.content=r[F.start].data.slice(0,F.size)}else{F.storage="minifat";if(F.size<0)F.size=0;else if(h!==y&&F.start!==y&&r[h]){F.content=u(F,r[h].data,(r[s]||{}).data)}}if(F.content)prep_blob(F.content,0);f[v]=F;a.push(F)}}function w(e,r){return new Date((__readUInt32LE(e,r+4)/1e7*Math.pow(2,32)+__readUInt32LE(e,r)/1e7-11644473600)*1e3)}function p(e,r){f();return a(n.readFileSync(e),r)}function F(e,r){switch(r&&r.type||"base64"){case"file":return p(e,r);case"base64":return a(s2a(Base64.decode(e)),r);case"binary":return a(s2a(e),r);}return a(e,r)}function g(e,r){var t=r||{},i=t.root||"Root Entry";if(!e.FullPaths)e.FullPaths=[];if(!e.FileIndex)e.FileIndex=[];if(e.FullPaths.length!==e.FileIndex.length)throw new Error("inconsistent CFB structure");if(e.FullPaths.length===0){e.FullPaths[0]=i+"/";e.FileIndex[0]={name:i,type:5}}if(t.CLSID)e.FileIndex[0].clsid=t.CLSID;I(e)}function I(e){var r="Sh33tJ5";if(CFB.find(e,"/"+r))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;e.FileIndex.push({name:r,type:2,content:t,size:4,L:69,R:69,C:69});e.FullPaths.push(e.FullPaths[0]+r);b(e)}function b(e,n){g(e);var f=false,a=false;for(var s=e.FullPaths.length-1;s>=0;--s){var h=e.FileIndex[s];switch(h.type){case 0:if(a)f=true;else{e.FileIndex.pop();e.FullPaths.pop()}break;case 1:;case 2:;case 5:a=true;if(isNaN(h.R*h.L*h.C))f=true;if(h.R>-1&&h.L>-1&&h.R==h.L)f=true;break;default:f=true;break;}}if(!f&&!n)return;var l=new Date(1987,1,19),o=0;var u=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(d.slice(-1)=="/"){for(o=s+1;o=u.length?-1:o;for(o=s+1;o=u.length?-1:o;_.type=1}else{if(t(e.FullPaths[s+1]||"")==t(d))_.R=s+1;_.type=2}}}function x(e,r){var t=r||{};b(e);var i=function(e){var r=0,t=0;for(var i=0;i0){if(f<4096)r+=f+63>>6;else t+=f+511>>9}}var a=e.FullPaths.length+3>>2;var s=r+7>>3;var h=r+127>>7;var l=s+t+a+h;var o=l+127>>7;var u=o<=109?0:Math.ceil((o-109)/127);while(l+o+u+127>>7>o)u=++o<=109?0:Math.ceil((o-109)/127);var c=[1,u,o,h,a,t,r,0];e.FileIndex[0].size=r<<6;c[7]=(e.FileIndex[0].start=c[0]+c[1]+c[2]+c[3]+c[4]+c[5])+(c[6]+7>>3);return c}(e);var n=new_buf(i[7]<<9);var f=0,a=0;{for(f=0;f<8;++f)n.write_shift(1,B[f]);for(f=0;f<8;++f)n.write_shift(2,0);n.write_shift(2,62);n.write_shift(2,3);n.write_shift(2,65534);n.write_shift(2,9);n.write_shift(2,6);for(f=0;f<3;++f)n.write_shift(2,0);n.write_shift(4,0);n.write_shift(4,i[2]);n.write_shift(4,i[0]+i[1]+i[2]+i[3]-1);n.write_shift(4,0);n.write_shift(4,1<<12);n.write_shift(4,i[3]?i[0]+i[1]+i[2]-1:y);n.write_shift(4,i[3]);n.write_shift(-4,i[1]?i[0]-1:y);n.write_shift(4,i[1]);for(f=0;f<109;++f)n.write_shift(-4,f>9)}s(i[6]+7>>3);while(n.l&511)n.write_shift(-4,A.ENDOFCHAIN);a=f=0;for(h=0;h=4096)continue;o.start=a;s(l+63>>6)}while(n.l&511)n.write_shift(-4,A.ENDOFCHAIN);for(f=0;f=4096){n.l=o.start+1<<9;for(h=0;h0&&o.size<4096){for(h=0;h>2;n=e.charCodeAt(o++);s=(i&3)<<4|n>>4;f=e.charCodeAt(o++);h=(n&15)<<2|f>>6;l=f&63;if(isNaN(n)){h=l=64}else if(isNaN(f)){l=64}t+=r.charAt(a)+r.charAt(s)+r.charAt(h)+r.charAt(l)}return t},decode:function e(t){var i="";var n=0,f=0,a=0,s=0,h=0,l=0,o=0;t=t.replace(/[^\w\+\/\=]/g,"");for(var u=0;u>4;i+=String.fromCharCode(n);l=r.indexOf(t.charAt(u++));f=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(f)}o=r.indexOf(t.charAt(u++));a=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(a)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;var Buffer_from=function(){};if(typeof Buffer!=="undefined"){var nbfs=!Buffer.from;if(!nbfs)try{Buffer.from("foo","utf8")}catch(e){nbfs=true}Buffer_from=nbfs?function(r,e){return e?new Buffer(r,e):new Buffer(r)}:Buffer.from.bind(Buffer);if(!Buffer.alloc)Buffer.alloc=function(r){return new Buffer(r)};if(!Buffer.allocUnsafe)Buffer.allocUnsafe=function(r){return new Buffer(r)}}function new_raw_buf(r){return has_buf?Buffer.alloc(r):new Array(r)}function new_unsafe_buf(r){return has_buf?Buffer.allocUnsafe(r):new Array(r)}var s2a=function e(r){if(has_buf)return Buffer_from(r,"binary");return r.split("").map(function(r){return r.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/g;var __toBuffer=function(r){var e=[];for(var t=0;t0&&Buffer.isBuffer(r[0][0])?Buffer.concat(r[0]):___toBuffer(r)};s2a=function(r){return Buffer_from(r,"binary")};bconcat=function(r){return Buffer.isBuffer(r[0])?Buffer.concat(r):__bconcat(r)}}var __readUInt8=function(r,e){return r[e]};var __readUInt16LE=function(r,e){return r[e+1]*(1<<8)+r[e]};var __readInt16LE=function(r,e){var t=r[e+1]*(1<<8)+r[e];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(r,e){return r[e+3]*(1<<24)+(r[e+2]<<16)+(r[e+1]<<8)+r[e]};var __readInt32LE=function(r,e){return(r[e+3]<<24)+(r[e+2]<<16)+(r[e+1]<<8)+r[e]};function ReadShift(r,e){var t,i,n=0;switch(r){case 1:t=__readUInt8(this,this.l);break;case 2:t=(e!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,r);}this.l+=r;if(n===0)return t;return i}var __writeUInt32LE=function(r,e,t){r[t]=e&255;r[t+1]=e>>>8&255;r[t+2]=e>>>16&255;r[t+3]=e>>>24&255};var __writeInt32LE=function(r,e,t){r[t]=e&255;r[t+1]=e>>8&255;r[t+2]=e>>16&255;r[t+3]=e>>24&255};function WriteShift(r,e,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=e&255;break;case 4:i=4;__writeUInt32LE(this,e,this.l);break;case-4:i=4;__writeInt32LE(this,e,this.l);break;}this.l+=i;return this}function CheckField(r,e){var t=__hexlify(this,this.l,r.length>>1);if(t!==r)throw new Error(e+"Expected "+r+" saw "+t);this.l+=r.length>>1}function prep_blob(r,e){r.l=e;r.read_shift=ReadShift;r.chk=CheckField;r.write_shift=WriteShift}function new_buf(r){var e=new_raw_buf(r);prep_blob(e,0);return e}var CRC32;(function(r){r(CRC32={})})(function(r){r.version="1.2.0";function e(){var r=0,e=new Array(256);for(var t=0;t!=256;++t){r=t;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;e[t]=r}return typeof Int32Array!=="undefined"?new Int32Array(e):e}var t=e();function i(r,e){var i=e^-1,n=r.length-1;for(var f=0;f>>8^t[(i^r.charCodeAt(f++))&255];i=i>>>8^t[(i^r.charCodeAt(f++))&255]}if(f===n)i=i>>>8^t[(i^r.charCodeAt(f))&255];return i^-1}function n(r,e){if(r.length>1e4)return f(r,e);var i=e^-1,n=r.length-3;for(var a=0;a>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255]}while(a>>8^t[(i^r[a++])&255];return i^-1}function f(r,e){var i=e^-1,n=r.length-7;for(var f=0;f>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255]}while(f>>8^t[(i^r[f++])&255];return i^-1}function a(r,e){var i=e^-1;for(var n=0,f=r.length,a,s;n>>8^t[(i^a)&255]}else if(a<2048){i=i>>>8^t[(i^(192|a>>6&31))&255];i=i>>>8^t[(i^(128|a&63))&255]}else if(a>=55296&&a<57344){a=(a&1023)+64;s=r.charCodeAt(n++)&1023;i=i>>>8^t[(i^(240|a>>8&7))&255];i=i>>>8^t[(i^(128|a>>2&63))&255];i=i>>>8^t[(i^(128|s>>6&15|(a&3)<<4))&255];i=i>>>8^t[(i^(128|s&63))&255]}else{i=i>>>8^t[(i^(224|a>>12&15))&255];i=i>>>8^t[(i^(128|a>>6&63))&255];i=i>>>8^t[(i^(128|a&63))&255]}}return i^-1}r.table=t;r.bstr=i;r.buf=n;r.str=a});var CFB=function t(){var r={};r.version="1.1.0";function e(r,e){var t=r.split("/"),i=e.split("/");for(var n=0,f=0,a=Math.min(t.length,i.length);n>>1;r.write_shift(2,t);var i=e.getFullYear()-1980;i=i<<4|e.getMonth()+1;i=i<<5|e.getDate();r.write_shift(2,i)}function f(r){var e=r.read_shift(2)&65535;var t=r.read_shift(2)&65535;var i=new Date;var n=t&31;t>>>=5;var f=t&15;t>>>=4;i.setMilliseconds(0);i.setFullYear(t+1980);i.setMonth(f-1);i.setDate(n);var a=e&31;e>>>=5;var s=e&63;e>>>=6;i.setHours(e);i.setMinutes(s);i.setSeconds(a<<1);return i}function a(r){prep_blob(r,0);var e={};var t=0;while(r.l<=r.length-4){var i=r.read_shift(2);var n=r.read_shift(2),f=r.l+n;var a={};switch(i){case 21589:{t=r.read_shift(1);if(t&1)a.mtime=r.read_shift(4);if(n>5){if(t&2)a.atime=r.read_shift(4);if(t&4)a.ctime=r.read_shift(4)}if(a.mtime)a.mt=new Date(a.mtime*1e3)}break;}r.l=f;e[i]=a}return e}var s;function h(){return s||(s=require("fs"))}function l(r,e){if(r[0]==80&&r[1]==75)return wr(r,e);if(r.length<512)throw new Error("CFB file size "+r.length+" < 512");var t=3;var i=512;var n=0;var f=0;var a=0;var s=0;var h=0;var l=[];var v=r.slice(0,512);prep_blob(v,0);var w=o(v);t=w[0];switch(t){case 3:i=512;break;case 4:i=4096;break;case 0:if(w[1]==0)return wr(r,e);default:throw new Error("Major Version: Expected 3 or 4 saw "+t);}if(i!==512){v=r.slice(0,i);prep_blob(v,28)}var b=r.slice(0,i);u(v,t);var F=v.read_shift(4,"i");if(t===3&&F!==0)throw new Error("# Directory Sectors: Expected 0 saw "+F);v.l+=4;a=v.read_shift(4,"i");v.l+=4;v.chk("00100000","Mini Stream Cutoff Size: ");s=v.read_shift(4,"i");n=v.read_shift(4,"i");h=v.read_shift(4,"i");f=v.read_shift(4,"i");for(var y=-1,I=0;I<109;++I){y=v.read_shift(4,"i");if(y<0)break;l[I]=y}var C=c(r,i);d(h,f,C,i,l);var x=p(C,a,l,i);x[a].name="!Directory";if(n>0&&s!==B)x[s].name="!MiniFAT";x[l[0]].name="!FAT";x.fat_addrs=l;x.ssz=i;var m={},E=[],A=[],S=[];g(a,x,C,E,n,m,A,s);_(A,S,E);E.shift();var k={FileIndex:A,FullPaths:S};if(e&&e.raw)k.raw={header:b,sectors:C};return k}function o(r){if(r[r.l]==80&&r[r.l+1]==75)return[0,0];r.chk(S,"Header Signature: ");r.chk(R,"CLSID: ");var e=r.read_shift(2,"u");return[r.read_shift(2,"u"),e]}function u(r,e){var t=9;r.l+=2;switch(t=r.read_shift(2)){case 9:if(e!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(e!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}r.chk("0600","Mini Sector Shift: ");r.chk("000000000000","Reserved: ")}function c(r,e){var t=Math.ceil(r.length/e)-1;var i=[];for(var n=1;n0&&a>=0){f.push(e.slice(a*A,a*A+A));n-=A;a=__readInt32LE(t,a*4)}if(f.length===0)return new_buf(0);return bconcat(f).slice(0,r.size)}function d(r,e,t,i,n){var f=B;if(r===B){if(e!==0)throw new Error("DIFAT chain shorter than expected")}else if(r!==-1){var a=t[r],s=(i>>>2)-1;if(!a)return;for(var h=0;h=0;){n[h]=true;f[f.length]=h;a.push(r[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!r[o])break;h=__readInt32LE(r[o],l)}return{nodes:f,data:__toBuffer([a])}}function p(r,e,t,i){var n=r.length,f=[];var a=[],s=[],h=[];var l=i-1,o=0,u=0,c=0,_=0;for(o=0;o=n)c-=n;if(a[c])continue;h=[];for(u=c;u>=0;){a[u]=true;s[s.length]=u;h.push(r[u]);var v=t[Math.floor(u*4/i)];_=u*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+u+" 4 "+i);if(!r[v])break;u=__readInt32LE(r[v],_)}f[c]={nodes:s,data:__toBuffer([h])}}return f}function g(r,e,t,i,n,f,a,s){var h=0,l=i.length?2:0;var o=e[r].data;var u=0,c=0,_;for(;u0&&h!==B)e[h].name="!StreamData"}else if(p.size>=4096){p.storage="fat";if(e[p.start]===undefined)e[p.start]=w(t,p.start,e.fat_addrs,e.ssz);e[p.start].name=p.name;p.content=e[p.start].data.slice(0,p.size)}else{p.storage="minifat";if(p.size<0)p.size=0;else if(h!==B&&p.start!==B&&e[h]){p.content=v(p,e[h].data,(e[s]||{}).data)}}if(p.content)prep_blob(p.content,0);f[_]=p;a.push(p)}}function b(r,e){return new Date((__readUInt32LE(r,e+4)/1e7*Math.pow(2,32)+__readUInt32LE(r,e)/1e7-11644473600)*1e3)}function F(r,e){h();return l(s.readFileSync(r),e)}function y(r,e){switch(e&&e.type||"base64"){case"file":return F(r,e);case"base64":return l(s2a(Base64.decode(r)),e);case"binary":return l(s2a(r),e);}return l(r,e)}function I(r,e){var t=e||{},i=t.root||"Root Entry";if(!r.FullPaths)r.FullPaths=[];if(!r.FileIndex)r.FileIndex=[];if(r.FullPaths.length!==r.FileIndex.length)throw new Error("inconsistent CFB structure");if(r.FullPaths.length===0){r.FullPaths[0]=i+"/";r.FileIndex[0]={name:i,type:5}}if(t.CLSID)r.FileIndex[0].clsid=t.CLSID;C(r)}function C(r){var e="Sh33tJ5";if(CFB.find(r,"/"+e))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;r.FileIndex.push({name:e,type:2,content:t,size:4,L:69,R:69,C:69});r.FullPaths.push(r.FullPaths[0]+e);x(r)}function x(r,n){I(r);var f=false,a=false;for(var s=r.FullPaths.length-1;s>=0;--s){var h=r.FileIndex[s];switch(h.type){case 0:if(a)f=true;else{r.FileIndex.pop();r.FullPaths.pop()}break;case 1:;case 2:;case 5:a=true;if(isNaN(h.R*h.L*h.C))f=true;if(h.R>-1&&h.L>-1&&h.R==h.L)f=true;break;default:f=true;break;}}if(!f&&!n)return;var l=new Date(1987,1,19),o=0;var u=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(v.slice(-1)=="/"){for(o=s+1;o=u.length?-1:o;for(o=s+1;o=u.length?-1:o;_.type=1}else{if(t(r.FullPaths[s+1]||"")==t(v))_.R=s+1;_.type=2}}}function m(r,e){var t=e||{};x(r);if(t.fileType=="zip")return gr(r,t);var i=function(r){var e=0,t=0;for(var i=0;i0){if(f<4096)e+=f+63>>6;else t+=f+511>>9}}var a=r.FullPaths.length+3>>2;var s=e+7>>3;var h=e+127>>7;var l=s+t+a+h;var o=l+127>>7;var u=o<=109?0:Math.ceil((o-109)/127);while(l+o+u+127>>7>o)u=++o<=109?0:Math.ceil((o-109)/127);var c=[1,u,o,h,a,t,e,0];r.FileIndex[0].size=e<<6;c[7]=(r.FileIndex[0].start=c[0]+c[1]+c[2]+c[3]+c[4]+c[5])+(c[6]+7>>3);return c}(r);var n=new_buf(i[7]<<9);var f=0,a=0;{for(f=0;f<8;++f)n.write_shift(1,k[f]);for(f=0;f<8;++f)n.write_shift(2,0);n.write_shift(2,62);n.write_shift(2,3);n.write_shift(2,65534);n.write_shift(2,9);n.write_shift(2,6);for(f=0;f<3;++f)n.write_shift(2,0);n.write_shift(4,0);n.write_shift(4,i[2]);n.write_shift(4,i[0]+i[1]+i[2]+i[3]-1);n.write_shift(4,0);n.write_shift(4,1<<12);n.write_shift(4,i[3]?i[0]+i[1]+i[2]-1:B);n.write_shift(4,i[3]);n.write_shift(-4,i[1]?i[0]-1:B);n.write_shift(4,i[1]);for(f=0;f<109;++f)n.write_shift(-4,f>9)}s(i[6]+7>>3);while(n.l&511)n.write_shift(-4,z.ENDOFCHAIN);a=f=0;for(h=0;h=4096)continue;o.start=a;s(l+63>>6)}while(n.l&511)n.write_shift(-4,z.ENDOFCHAIN);for(f=0;f=4096){n.l=o.start+1<<9;for(h=0;h0&&o.size<4096){for(h=0;h>16|e>>8|e)&255}var X=typeof Uint8Array!=="undefined";var q=X?new Uint8Array(1<<8):[];for(var J=0;J<1<<8;++J)q[J]=G(J);function V(r,e){var t=q[r&255];if(e<=8)return t>>>8-e;t=t<<8|q[r>>8&255];if(e<=16)return t>>>16-e;t=t<<8|q[r>>16&255];return t>>>24-e}function W(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=6?0:r[i+1]<<8))>>>t&3}function Y(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=5?0:r[i+1]<<8))>>>t&7}function Z(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=4?0:r[i+1]<<8))>>>t&15}function K(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=3?0:r[i+1]<<8))>>>t&31}function Q(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=1?0:r[i+1]<<8))>>>t&127}function $(r,e,t){var i=e&7,n=e>>>3,f=(1<>>i;if(t<8-i)return a&f;a|=r[n+1]<<8-i;if(t<16-i)return a&f;a|=r[n+2]<<16-i;if(t<24-i)return a&f;a|=r[n+3]<<24-i;return a&f}function rr(r,e){var t=r.length,i=2*t>e?2*t:e+5,n=0;if(t>=e)return r;if(has_buf){var f=new_unsafe_buf(i);if(r.copy)r.copy(f);else for(;n0)t[t.l++]=e[i++]}return t.l}}();return function(e){var t=new_buf(50+Math.floor(e.length*1.1));var i=r(e,t);return t.slice(0,i)}}();function ir(r,e,t){var i=1,n=0,f=0,a=0,s=0,h=r.length;var l=X?new Uint16Array(32):er(32);for(f=0;f<32;++f)l[f]=0;for(f=h;f>i-u;for(a=(1<=0;--a)e[s|a<>8-v;for(var d=(1<<7-v)-1;d>=0;--d)lr[_|d<>>=3){case 16:f=3+W(r,e);e+=2;_=w[w.length-1];while(f-- >0)w.push(_);break;case 17:f=3+Y(r,e);e+=3;while(f-- >0)w.push(0);break;case 18:f=11+Q(r,e);e+=7;while(f-- >0)w.push(0);break;default:w.push(_);if(h<_)h=_;break;}}var p=w.slice(0,t),g=w.slice(t);for(c=t;c<286;++c)p[c]=0;for(c=i;c<30;++c)g[c]=0;or=ir(p,sr,286);ur=ir(g,hr,30);return e}function _r(r,e){if(r[0]==3&&!(r[1]&3)){return[new_raw_buf(e),2]}var t=0;var i=0;var n=new_unsafe_buf(e?e:1<<18);var f=0;var a=n.length>>>0;var s=0,h=0;while((i&1)==0){i=Y(r,t);t+=3;if(i>>>1==0){if(t&7)t+=8-(t&7);var l=r[t>>>3]|r[(t>>>3)+1]<<8;t+=32;if(!e&&a>>3,(t>>>3)+l);f+=l;t+=8*l}else while(l-- >0){n[f++]=r[t>>>3];t+=8}continue}else if(i>>>1==1){s=9;h=5}else{t=cr(r,t);s=or;h=ur}if(!e&&a>>1==1?nr[o]:sr[o];t+=u&15;u>>>=4;if((u>>>8&255)===0)n[f++]=u;else if(u==256)break;else{u-=257;var c=u<8?0:u-4>>2;if(c>5)c=0;var _=f+H[u];if(c>0){_+=$(r,t,c);t+=c}o=$(r,t,h);u=i>>>1==1?fr[o]:hr[o];t+=u&15;u>>>=4;var v=u<4?0:u-2>>1;var d=j[u];if(v>0){d+=$(r,t,v);t+=v}if(!e&&a<_){n=rr(n,_);a=n.length}while(f<_){n[f]=n[f-d];++f}}}}return[e?n:n.slice(0,f),t+7>>>3]}function vr(r,e){var t=r.slice(r.l||0);var i=_r(t,e);r.l+=i[1];return i[0]}function dr(r,e){if(r){if(typeof console!=="undefined")console.error(e)}else throw new Error(e)}function wr(r,e){var t=r;prep_blob(t,0);var i=[],n=[];var f={FileIndex:i,FullPaths:n};I(f,{root:e.root});var s=t.length-4;while((t[s]!=80||t[s+1]!=75||t[s+2]!=5||t[s+3]!=6)&&s>=0)--s;t.l=s+4;t.l+=4;var h=t.read_shift(2);t.l+=6;var l=t.read_shift(4);t.l=l;for(s=0;s*/ = new Array(256);
+
+ for(var n =0; n != 256; ++n){
+ c = n;
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
+ table[n] = c;
+ }
+
+ return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
+}
+
+var T = signed_crc_table();
+function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = bstr.length - 1;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
+ }
+ if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ if(buf.length > 10000) return crc32_buf_8(buf, seed);
+ var C = seed ^ -1, L = buf.length - 3;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1, L = buf.length - 7;
+ for(var i = 0; i < L;) {
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ }
+ while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
+ return C ^ -1;
+}
+
+function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
+ var C = seed ^ -1;
+ for(var i = 0, L=str.length, c, d; i < L;) {
+ c = str.charCodeAt(i++);
+ if(c < 0x80) {
+ C = (C>>>8) ^ T[(C ^ c)&0xFF];
+ } else if(c < 0x800) {
+ C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ } else if(c >= 0xD800 && c < 0xE000) {
+ c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
+ C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
+ } else {
+ C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
+ C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
+ }
+ }
+ return C ^ -1;
+}
+CRC32.table = T;
+CRC32.bstr = crc32_bstr;
+CRC32.buf = crc32_buf;
+CRC32.str = crc32_str;
+}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
-exports.version = '1.0.8';
+exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@@ -59,9 +163,75 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
+/* -------------------------------------------------------------------------- */
+/* DOS Date format:
+ high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
+ add 1980 to stored year
+ stored second should be doubled
+*/
+
+/* write JS date to buf as a DOS date */
+function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
+ if(typeof date === "string") date = new Date(date);
+ var hms/*:number*/ = date.getHours();
+ hms = hms << 6 | date.getMinutes();
+ hms = hms << 5 | (date.getSeconds()>>>1);
+ buf.write_shift(2, hms);
+ var ymd/*:number*/ = (date.getFullYear() - 1980);
+ ymd = ymd << 4 | (date.getMonth()+1);
+ ymd = ymd << 5 | date.getDate();
+ buf.write_shift(2, ymd);
+}
+
+/* read four bytes from buf and interpret as a DOS date */
+function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
+ var hms = buf.read_shift(2) & 0xFFFF;
+ var ymd = buf.read_shift(2) & 0xFFFF;
+ var val = new Date();
+ var d = ymd & 0x1F; ymd >>>= 5;
+ var m = ymd & 0x0F; ymd >>>= 4;
+ val.setMilliseconds(0);
+ val.setFullYear(ymd + 1980);
+ val.setMonth(m-1);
+ val.setDate(d);
+ var S = hms & 0x1F; hms >>>= 5;
+ var M = hms & 0x3F; hms >>>= 6;
+ val.setHours(hms);
+ val.setMinutes(M);
+ val.setSeconds(S<<1);
+ return val;
+}
+function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
+ prep_blob(blob, 0);
+ var o = /*::(*/{}/*:: :any)*/;
+ var flags = 0;
+ while(blob.l <= blob.length - 4) {
+ var type = blob.read_shift(2);
+ var sz = blob.read_shift(2), tgt = blob.l + sz;
+ var p = {};
+ switch(type) {
+ /* UNIX-style Timestamps */
+ case 0x5455: {
+ flags = blob.read_shift(1);
+ if(flags & 1) p.mtime = blob.read_shift(4);
+ /* for some reason, CD flag corresponds to LFH */
+ if(sz > 5) {
+ if(flags & 2) p.atime = blob.read_shift(4);
+ if(flags & 4) p.ctime = blob.read_shift(4);
+ }
+ if(p.mtime) p.mt = new Date(p.mtime*1000);
+ }
+ break;
+ }
+ blob.l = tgt;
+ o[type] = p;
+ }
+ return o;
+}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
+if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@@ -82,6 +252,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
+ case 0: if(mv[1] == 0) return parse_zip(file, options);
+ /* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@@ -160,6 +332,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
+ if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@@ -477,6 +650,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
+ if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb/*:CFBContainer*/)/*:Array*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@@ -677,6 +851,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string
}
return o;
}
+/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
+var _zlib;
+function use_zlib(zlib) { try {
+ var InflateRaw = zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
+ if(InflRaw.bytesRead) _zlib = zlib;
+ else throw new Error("zlib does not expose bytesRead");
+} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
+
+function _inflateRawSync(payload, usz) {
+ if(!_zlib) return _inflate(payload, usz);
+ var InflateRaw = _zlib.InflateRaw;
+ var InflRaw = new InflateRaw();
+ var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
+ payload.l += InflRaw.bytesRead;
+ return out;
+}
+
+function _deflateRawSync(payload) {
+ return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
+}
+var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
+var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
+
+/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
+var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
+
+function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
+
+var use_typed_arrays = typeof Uint8Array !== 'undefined';
+
+var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
+for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
+
+function bit_swap_n(n, b) {
+ var rev = bitswap8[n & 0xFF];
+ if(b <= 8) return rev >>> (8-b);
+ rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
+ if(b <= 16) return rev >>> (16-b);
+ rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
+ return rev >>> (24-b);
+}
+
+/* helpers for unaligned bit reads */
+function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
+function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
+function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
+function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
+function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
+
+/* works up to n = 3 * 8 + 1 = 25 */
+function read_bits_n(buf, bl, n) {
+ var w = (bl&7), h = (bl>>>3), f = ((1<>> w;
+ if(n < 8 - w) return v & f;
+ v |= buf[h+1]<<(8-w);
+ if(n < 16 - w) return v & f;
+ v |= buf[h+2]<<(16-w);
+ if(n < 24 - w) return v & f;
+ v |= buf[h+3]<<(24-w);
+ return v & f;
+}
+
+/* until ArrayBuffer#realloc is a thing, fake a realloc */
+function realloc(b, sz/*:number*/) {
+ var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
+ if(L >= sz) return b;
+ if(has_buf) {
+ var o = new_unsafe_buf(M);
+ // $FlowIgnore
+ if(b.copy) b.copy(o);
+ else for(; i < b.length; ++i) o[i] = b[i];
+ return o;
+ } else if(use_typed_arrays) {
+ var a = new Uint8Array(M);
+ if(a.set) a.set(b);
+ else for(; i < b.length; ++i) a[i] = b[i];
+ return a;
+ }
+ b.length = M;
+ return b;
+}
+
+/* zero-filled arrays for older browsers */
+function zero_fill_array(n) {
+ var o = new Array(n);
+ for(var i = 0; i < n; ++i) o[i] = 0;
+ return o;
+}var _deflate = (function() {
+var _deflateRaw = (function() {
+ return function deflateRaw(data, out) {
+ var boff = 0;
+ while(boff < data.length) {
+ var L = Math.min(0xFFFF, data.length - boff);
+ var h = boff + L == data.length;
+ /* TODO: this is only type 0 stored */
+ out.write_shift(1, +h);
+ out.write_shift(2, L);
+ out.write_shift(2, (~L) & 0xFFFF);
+ while(L-- > 0) out[out.l++] = data[boff++];
+ }
+ return out.l;
+ };
+})();
+
+return function(data) {
+ var buf = new_buf(50+Math.floor(data.length*1.1));
+ var off = _deflateRaw(data, buf);
+ return buf.slice(0, off);
+};
+})();
+/* modified inflate function also moves original read head */
+
+/* build tree (used for literals and lengths) */
+function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
+ var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
+
+ var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
+ for(i = 0; i < 32; ++i) bl_count[i] = 0;
+
+ for(i = L; i < MAX; ++i) clens[i] = 0;
+ L = clens.length;
+
+ var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
+
+ /* build code tree */
+ for(i = 0; i < L; ++i) {
+ bl_count[(w = clens[i])]++;
+ if(maxlen < w) maxlen = w;
+ ctree[i] = 0;
+ }
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
+ for(i = 0; i < L; ++i) {
+ ccode = clens[i];
+ if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
+ }
+
+ /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
+ for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
+ cmap[ccode|(j<*/ = [];
+ var i = 0;
+ for(;i<32; i++) dlens.push(5);
+ build_tree(dlens, fix_dmap, 32);
+
+ var clens/*:Array*/ = [];
+ i = 0;
+ for(; i<=143; i++) clens.push(8);
+ for(; i<=255; i++) clens.push(9);
+ for(; i<=279; i++) clens.push(7);
+ for(; i<=287; i++) clens.push(8);
+ build_tree(clens, fix_lmap, 288);
+})();
+
+var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
+var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
+var dyn_len_1 = 1, dyn_len_2 = 1;
+
+/* 5.5.3 Expanding Huffman Codes */
+function dyn(data, boff/*:number*/) {
+ /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
+ var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
+ var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
+ var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
+ var w = 0;
+
+ /* grab and store code lengths */
+ var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
+ var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+ var maxlen = 1;
+ var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
+ var L = clens.length; /* 19 */
+ for(var i = 0; i < _HCLEN; ++i) {
+ clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
+ if(maxlen < w) maxlen = w;
+ bl_count[w]++;
+ boff += 3;
+ }
+
+ /* build code tree */
+ var ccode = 0;
+ bl_count[0] = 0;
+ for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
+ for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
+ /* cmap[7 bits from stream] = (off&7) + (lit<<3) */
+ var cleni = 0;
+ for(i = 0; i < L; ++i) {
+ cleni = clens[i];
+ if(cleni != 0) {
+ ccode = bitswap8[ctree[i]]>>(8-cleni);
+ for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = [];
+ maxlen = 1;
+ for(; hcodes.length < _HLIT + _HDIST;) {
+ ccode = dyn_cmap[read_bits_7(data, boff)];
+ boff += ccode & 7;
+ switch((ccode >>>= 3)) {
+ case 16:
+ w = 3 + read_bits_2(data, boff); boff += 2;
+ ccode = hcodes[hcodes.length - 1];
+ while(w-- > 0) hcodes.push(ccode);
+ break;
+ case 17:
+ w = 3 + read_bits_3(data, boff); boff += 3;
+ while(w-- > 0) hcodes.push(0);
+ break;
+ case 18:
+ w = 11 + read_bits_7(data, boff); boff += 7;
+ while(w -- > 0) hcodes.push(0);
+ break;
+ default:
+ hcodes.push(ccode);
+ if(maxlen < ccode) maxlen = ccode;
+ break;
+ }
+ }
+
+ /* build literal / length trees */
+ var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
+ for(i = _HLIT; i < 286; ++i) h1[i] = 0;
+ for(i = _HDIST; i < 30; ++i) h2[i] = 0;
+ dyn_len_1 = build_tree(h1, dyn_lmap, 286);
+ dyn_len_2 = build_tree(h2, dyn_dmap, 30);
+ return boff;
+}
+
+/* return [ data, bytesRead ] */
+function inflate(data, usz/*:number*/) {
+ /* shortcircuit for empty buffer [0x03, 0x00] */
+ if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
+
+ /* bit offset */
+ var boff = 0;
+
+ /* header includes final bit and type bits */
+ var header = 0;
+
+ var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
+ var woff = 0;
+ var OL = outbuf.length>>>0;
+ var max_len_1 = 0, max_len_2 = 0;
+
+ while((header&1) == 0) {
+ header = read_bits_3(data, boff); boff += 3;
+ if((header >>> 1) == 0) {
+ /* Stored block */
+ if(boff & 7) boff += 8 - (boff&7);
+ /* 2 bytes sz, 2 bytes bit inverse */
+ var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
+ boff += 32;
+ /* push sz bytes */
+ if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
+ if(typeof data.copy === 'function') {
+ // $FlowIgnore
+ data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
+ woff += sz; boff += 8*sz;
+ } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
+ continue;
+ } else if((header >>> 1) == 1) {
+ /* Fixed Huffman */
+ max_len_1 = 9; max_len_2 = 5;
+ } else {
+ /* Dynamic Huffman */
+ boff = dyn(data, boff);
+ max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
+ }
+ if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
+ for(;;) { // while(true) is apparently out of vogue in modern JS circles
+ /* ingest code and move read head */
+ var bits = read_bits_n(data, boff, max_len_1);
+ var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
+ boff += code & 15; code >>>= 4;
+ /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
+ if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
+ else if(code == 256) break;
+ else {
+ code -= 257;
+ var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
+ var tgt = woff + LEN_LN[code];
+ /* length extra bits */
+ if(len_eb > 0) {
+ tgt += read_bits_n(data, boff, len_eb);
+ boff += len_eb;
+ }
+
+ /* dist code */
+ bits = read_bits_n(data, boff, max_len_2);
+ code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
+ boff += code & 15; code >>>= 4;
+ var dst_eb = (code < 4 ? 0 : (code-2)>>1);
+ var dst = DST_LN[code];
+ /* dist extra bits */
+ if(dst_eb > 0) {
+ dst += read_bits_n(data, boff, dst_eb);
+ boff += dst_eb;
+ }
+
+ /* in the common case, manual byte copy is faster than TA set / Buffer copy */
+ if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
+ while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
+ }
+ }
+ }
+ return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
+}
+
+function _inflate(payload, usz) {
+ var data = payload.slice(payload.l||0);
+ var out = inflate(data, usz);
+ payload.l += out[1];
+ return out[0];
+}
+
+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});
+}
+function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
+ var _opts = options || {};
+ var out = [], cdirs = [];
+ var o/*:CFBlob*/ = new_buf(1);
+ var method = (_opts.compression ? 8 : 0), flags = 0;
+ var desc = false;
+ if(desc) flags |= 8;
+ var i = 0, j = 0;
+
+ var start_cd = 0, fcnt = 0;
+ var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
+ var crcs = [];
+ var sz_cd = 0;
+
+ for(i = 1; i < cfb.FullPaths.length; ++i) {
+ fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
+ if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
+ var start = start_cd;
+
+ /* TODO: CP437 filename */
+ var namebuf = new_buf(fp.length);
+ for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
+ namebuf = namebuf.slice(0, namebuf.l);
+ crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
+
+ var outbuf = fi.content/*::||[]*/;
+ if(method == 8) outbuf = _deflateRawSync(outbuf);
+
+ /* local file header */
+ o = new_buf(30);
+ o.write_shift(4, 0x04034b50);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ /* TODO: last mod file time/date */
+ if(fi.mt) write_dos_date(o, fi.mt);
+ else o.write_shift(4, 0);
+ o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
+ o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
+ o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+
+ start_cd += o.length;
+ out.push(o);
+ start_cd += namebuf.length;
+ out.push(namebuf);
+
+ /* TODO: encryption header ? */
+ start_cd += outbuf.length;
+ out.push(outbuf);
+
+ /* data descriptor */
+ if(flags & 8) {
+ o = new_buf(12);
+ o.write_shift(-4, crcs[fcnt]);
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ start_cd += o.l;
+ out.push(o);
+ }
+
+ /* central directory */
+ o = new_buf(46);
+ o.write_shift(4, 0x02014b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 20);
+ o.write_shift(2, flags);
+ o.write_shift(2, method);
+ o.write_shift(4, 0); /* TODO: last mod file time/date */
+ o.write_shift(-4, crcs[fcnt]);
+
+ o.write_shift(4, outbuf.length);
+ o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
+ o.write_shift(2, namebuf.length);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(4, 0);
+ o.write_shift(4, start);
+
+ sz_cd += o.l;
+ cdirs.push(o);
+ sz_cd += namebuf.length;
+ cdirs.push(namebuf);
+ ++fcnt;
+ }
+
+ /* end of central directory */
+ o = new_buf(22);
+ o.write_shift(4, 0x06054b50);
+ o.write_shift(2, 0);
+ o.write_shift(2, 0);
+ o.write_shift(2, fcnt);
+ o.write_shift(2, fcnt);
+ o.write_shift(4, sz_cd);
+ o.write_shift(4, start_cd);
+ o.write_shift(2, 0);
+
+ return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
+}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
@@ -704,6 +1423,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
+ if(opts.mt) file.mt = opts.mt;
+ if(opts.ct) file.ct = opts.ct;
}
return file;
}
@@ -747,6 +1468,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
+ use_zlib: use_zlib,
+ _deflateRaw: _deflate,
+ _inflateRaw: _inflate,
consts: consts
};
diff --git a/fails.lst b/fails.lst
index 38f6e24..4130113 100644
--- a/fails.lst
+++ b/fails.lst
@@ -1,6 +1,13 @@
+# not CFB or ZIP
apachepoi_testEXCEL_3.xls
apachepoi_testEXCEL_4.xls
xlrd_biff4_no_format_no_window2.xls
-roo_type_excelx.xls
-roo_type_openoffice.xls
libreoffice_calc_csv-import_malformed-quotes.xls
+# file exceeding 31 chars
+apachepoi_59746_NoRowNums.xlsx
+apachepoi_WithEmbeded.xlsx
+apachepoi_picture.xlsx
+roo_name_with_leading_slash.xlsx
+spout-xlsx_sheet_with_prefixed_xml_files.xlsx
+# not a valid file
+openpyxl_r_null_archive.xlsx
diff --git a/index.html b/index.html
index 3bce815..372a793 100644
--- a/index.html
+++ b/index.html
@@ -32,7 +32,9 @@ a { text-decoration: none }
Advanced Demo Options:
Use readAsBinaryString: (when available)
-Export data
+Export Current File
+- Export data as CFB
+- Export data as ZIP
@@ -40,7 +42,7 @@ Use readAsBinaryString: (when available)
-
+