arithmancy
This commit is contained in:
parent
bb997654ef
commit
cb8046c447
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,6 +27,7 @@ tmp
|
||||
*.[bB][iI][fF][fF][23458]
|
||||
*.[rR][tT][fF]
|
||||
*.[eE][tT][hH]
|
||||
*.[nN][uU][mM][bB][eE][rR][sS]
|
||||
*.123
|
||||
*.htm
|
||||
*.html
|
||||
|
@ -1,4 +1,5 @@
|
||||
test_files/
|
||||
modules/
|
||||
packages/
|
||||
.github/
|
||||
tests/files/
|
||||
|
@ -51,6 +51,7 @@ VueJS
|
||||
WebSQL
|
||||
WK_
|
||||
iOS
|
||||
iWork
|
||||
nodejs
|
||||
node.js
|
||||
npm
|
||||
|
@ -7,6 +7,7 @@ changes may not be included if they are not expected to break existing code.
|
||||
## v0.18.0
|
||||
|
||||
* Browser scripts only expose `XLSX` variable
|
||||
* Module no longer ships with `dist/jszip.js` browser script
|
||||
|
||||
## v0.17.4
|
||||
|
||||
|
1
Makefile
1
Makefile
@ -94,7 +94,6 @@ dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
|
||||
dist-deps: ## Copy dependencies for distribution
|
||||
mkdir -p dist
|
||||
cp node_modules/codepage/dist/cpexcel.full.js dist/cpexcel.js
|
||||
cp jszip.js dist/jszip.js
|
||||
|
||||
.PHONY: aux
|
||||
aux: $(AUXTARGETS)
|
||||
|
@ -54,6 +54,11 @@ var NUMBERS = (function() {
|
||||
});
|
||||
return out;
|
||||
};
|
||||
var popcnt = function(x) {
|
||||
x -= x >> 1 & 1431655765;
|
||||
x = (x & 858993459) + (x >> 2 & 858993459);
|
||||
return (x + (x >> 4) & 252645135) * 16843009 >>> 24;
|
||||
};
|
||||
|
||||
// src/proto.ts
|
||||
function parse_varint49(buf, ptr) {
|
||||
@ -126,12 +131,16 @@ var NUMBERS = (function() {
|
||||
break;
|
||||
case 5:
|
||||
len = 4;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 1:
|
||||
if (!len)
|
||||
len = 8;
|
||||
len = 8;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 2:
|
||||
if (!len)
|
||||
len = parse_varint49(buf, ptr);
|
||||
len = parse_varint49(buf, ptr);
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
@ -140,7 +149,7 @@ var NUMBERS = (function() {
|
||||
default:
|
||||
throw new Error("PB Type ".concat(type, " for Field ").concat(num, " at offset ").concat(off));
|
||||
}
|
||||
var v = { offset: off, data: res };
|
||||
var v = { offset: off, data: res, type: type };
|
||||
if (out[num] == null)
|
||||
out[num] = [v];
|
||||
else
|
||||
@ -172,14 +181,14 @@ var NUMBERS = (function() {
|
||||
var t = buf[l++];
|
||||
var len = buf[l] | buf[l + 1] << 8 | buf[l + 2] << 16;
|
||||
l += 3;
|
||||
out.push(process_chunk(t, buf.slice(l, l + len)));
|
||||
out.push(parse_snappy_chunk(t, buf.slice(l, l + len)));
|
||||
l += len;
|
||||
}
|
||||
if (l !== buf.length)
|
||||
throw new Error("data is not a valid framed stream!");
|
||||
return u8concat(out);
|
||||
}
|
||||
function process_chunk(type, buf) {
|
||||
function parse_snappy_chunk(type, buf) {
|
||||
if (type != 0)
|
||||
throw new Error("Unexpected Snappy chunk type ".concat(type));
|
||||
var ptr = [0];
|
||||
@ -270,6 +279,62 @@ var NUMBERS = (function() {
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/prebnccell.ts
|
||||
function parseit(buf, version) {
|
||||
var dv = u8_to_dataview(buf);
|
||||
var ctype = buf[version == 4 ? 1 : 2];
|
||||
var flags = dv.getUint32(4, true);
|
||||
var data_offset = 12 + popcnt(flags & 16270) * 4;
|
||||
var sidx = -1, ieee = NaN, dt = NaN;
|
||||
if (flags & 16) {
|
||||
sidx = dv.getUint32(data_offset, true);
|
||||
data_offset += 4;
|
||||
}
|
||||
if (flags & 32) {
|
||||
ieee = dv.getFloat64(data_offset, true);
|
||||
data_offset += 8;
|
||||
}
|
||||
if (flags & 64) {
|
||||
dt = dv.getFloat64(data_offset, true);
|
||||
data_offset += 8;
|
||||
}
|
||||
var ret;
|
||||
switch (ctype) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
ret = { t: "n", v: ieee };
|
||||
break;
|
||||
case 3:
|
||||
ret = { t: "s", v: sidx };
|
||||
break;
|
||||
case 5:
|
||||
var dd = new Date(2001, 0, 1);
|
||||
dd.setTime(dd.getTime() + dt * 1e3);
|
||||
ret = { t: "d", v: dd };
|
||||
break;
|
||||
case 6:
|
||||
ret = { t: "b", v: ieee > 0 };
|
||||
break;
|
||||
case 7:
|
||||
ret = { t: "n", v: ieee };
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(buf.slice(0, 4)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function parse(buf) {
|
||||
var version = buf[0];
|
||||
switch (version) {
|
||||
case 3:
|
||||
case 4:
|
||||
return parseit(buf, version);
|
||||
default:
|
||||
throw new Error("Unsupported pre-BNC version ".concat(version));
|
||||
}
|
||||
}
|
||||
|
||||
// src/numbers.ts
|
||||
var encode_col = function(C) {
|
||||
var s = "";
|
||||
@ -343,7 +408,7 @@ var NUMBERS = (function() {
|
||||
var pb = parse_shallow(root.data);
|
||||
var entries = pb[3];
|
||||
var data = [];
|
||||
entries == null ? void 0 : entries.forEach(function(entry) {
|
||||
(entries || []).forEach(function(entry) {
|
||||
var le = parse_shallow(entry.data);
|
||||
var key = varint_to_i32(le[1][0].data) >>> 0;
|
||||
data[key] = u8str(le[3][0].data);
|
||||
@ -405,50 +470,12 @@ var NUMBERS = (function() {
|
||||
tiles.forEach(function(tile2) {
|
||||
tile2.ref.forEach(function(row, R) {
|
||||
row.forEach(function(buf, C) {
|
||||
var dv = u8_to_dataview(buf);
|
||||
var ctype = buf[2];
|
||||
var addr = encode_cell({ r: R, c: C });
|
||||
switch (ctype) {
|
||||
case 0:
|
||||
{
|
||||
switch (buf[1]) {
|
||||
case 3:
|
||||
ws[addr] = { t: "s", v: sst[dv.getUint32(buf.length - 4, true)] };
|
||||
break;
|
||||
case 2:
|
||||
ws[addr] = { t: "n", v: dv.getFloat64(16, true) };
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
break;
|
||||
case 7:
|
||||
break;
|
||||
case 6:
|
||||
ws[addr] = { t: "b", v: dv.getFloat64(buf.length - 8, true) > 0 };
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(buf.slice(0, 4)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
ws[addr] = { t: "s", v: sst[dv.getUint32(16, true)] };
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
ws[addr] = { t: "n", v: dv.getFloat64(buf.length - 12, true) };
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
ws[addr] = { t: "b", v: dv.getFloat64(16, true) > 0 };
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(ctype));
|
||||
var res = parse(buf);
|
||||
if (res) {
|
||||
ws[addr] = res;
|
||||
if (res.t == "s" && typeof res.v == "number")
|
||||
res.v = sst[res.v];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
|
||||
/* Numbers */
|
||||
if(safegetzipfile(zip, 'Index/Document.iwa')) {
|
||||
if(typeof Uint8Array == "undefined") throw new Error('NUMBERS file parsing requires Uint8Array support')
|
||||
if(typeof Uint8Array == "undefined") throw new Error('NUMBERS file parsing requires Uint8Array support');
|
||||
if(typeof NUMBERS != "undefined") {
|
||||
if(zip.FileIndex) return NUMBERS.parse_numbers(zip);
|
||||
var _zip = CFB.utils.cfb_new();
|
||||
|
9000
dist/jszip.js
generated
vendored
9000
dist/jszip.js
generated
vendored
File diff suppressed because it is too large
Load Diff
@ -88,7 +88,7 @@ file but Excel will know how to handle it. This library applies similar logic:
|
||||
| `0xD0` | CFB Container | BIFF 5/8 or protected XLSX/XLSB or WQ3/QPW or XLR |
|
||||
| `0x09` | BIFF Stream | BIFF 2/3/4/5 |
|
||||
| `0x3C` | XML/HTML | SpreadsheetML / Flat ODS / UOS1 / HTML / plain text |
|
||||
| `0x50` | ZIP Archive | XLSB or XLSX/M or ODS or UOS2 or plain text |
|
||||
| `0x50` | ZIP Archive | XLSB or XLSX/M or ODS or UOS2 or NUMBERS or text |
|
||||
| `0x49` | Plain Text | SYLK or plain text |
|
||||
| `0x54` | Plain Text | DIF or plain text |
|
||||
| `0xEF` | UTF8 Encoded | SpreadsheetML / Flat ODS / UOS1 / HTML / plain text |
|
||||
|
@ -20,6 +20,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
|
||||
| Lotus Formatted Text (PRN) | ✔ | ✔ |
|
||||
| UTF-16 Unicode Text (TXT) | ✔ | ✔ |
|
||||
| **Other Workbook/Worksheet Formats** |:-----:|:-----:|
|
||||
| Numbers 3.0+ / iWork 2013+ Spreadsheet (NUMBERS) | ✔ | |
|
||||
| OpenDocument Spreadsheet (ODS) | ✔ | ✔ |
|
||||
| Flat XML ODF Spreadsheet (FODS) | ✔ | ✔ |
|
||||
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | ✔ | |
|
||||
@ -205,6 +206,21 @@ XLR also includes a `WksSSWorkBook` stream similar to Lotus FM3/FMT files.
|
||||
|
||||
</details>
|
||||
|
||||
#### Numbers 3.0+ / iWork 2013+ Spreadsheet (NUMBERS)
|
||||
|
||||
<details>
|
||||
<summary>(click to show)</summary>
|
||||
|
||||
iWork 2013 (Numbers 3.0 / Pages 5.0 / Keynote 6.0) switched from a proprietary
|
||||
XML-based format to the current file format based on the iWork Archive (IWA).
|
||||
This format has been used up through the current release (Numbers 11.2).
|
||||
|
||||
The parser focuses on extracting raw data from tables. Numbers technically
|
||||
supports multiple tables in a logical worksheet, including custom titles. This
|
||||
parser will only extract the raw data from the first table from each worksheet.
|
||||
|
||||
</details>
|
||||
|
||||
#### OpenDocument Spreadsheet (ODS/FODS)
|
||||
|
||||
<details>
|
||||
|
BIN
formats.png
BIN
formats.png
Binary file not shown.
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 203 KiB |
@ -16,6 +16,7 @@ digraph G {
|
||||
|
||||
subgraph OLD {
|
||||
node [style=filled,color=cyan];
|
||||
nums [label="NUMBERS"];
|
||||
ods [label="ODS"];
|
||||
fods [label="FODS"];
|
||||
uos [label="UOS"];
|
||||
@ -63,6 +64,7 @@ digraph G {
|
||||
wk4 -> csf
|
||||
123 -> csf
|
||||
qpw -> csf
|
||||
nums -> csf
|
||||
}
|
||||
subgraph WORKSHEET {
|
||||
edge [color=aquamarine4];
|
||||
|
@ -54,6 +54,11 @@ var NUMBERS = (function() {
|
||||
});
|
||||
return out;
|
||||
};
|
||||
var popcnt = function(x) {
|
||||
x -= x >> 1 & 1431655765;
|
||||
x = (x & 858993459) + (x >> 2 & 858993459);
|
||||
return (x + (x >> 4) & 252645135) * 16843009 >>> 24;
|
||||
};
|
||||
|
||||
// src/proto.ts
|
||||
function parse_varint49(buf, ptr) {
|
||||
@ -126,12 +131,16 @@ var NUMBERS = (function() {
|
||||
break;
|
||||
case 5:
|
||||
len = 4;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 1:
|
||||
if (!len)
|
||||
len = 8;
|
||||
len = 8;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 2:
|
||||
if (!len)
|
||||
len = parse_varint49(buf, ptr);
|
||||
len = parse_varint49(buf, ptr);
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
@ -140,7 +149,7 @@ var NUMBERS = (function() {
|
||||
default:
|
||||
throw new Error("PB Type ".concat(type, " for Field ").concat(num, " at offset ").concat(off));
|
||||
}
|
||||
var v = { offset: off, data: res };
|
||||
var v = { offset: off, data: res, type: type };
|
||||
if (out[num] == null)
|
||||
out[num] = [v];
|
||||
else
|
||||
@ -172,14 +181,14 @@ var NUMBERS = (function() {
|
||||
var t = buf[l++];
|
||||
var len = buf[l] | buf[l + 1] << 8 | buf[l + 2] << 16;
|
||||
l += 3;
|
||||
out.push(process_chunk(t, buf.slice(l, l + len)));
|
||||
out.push(parse_snappy_chunk(t, buf.slice(l, l + len)));
|
||||
l += len;
|
||||
}
|
||||
if (l !== buf.length)
|
||||
throw new Error("data is not a valid framed stream!");
|
||||
return u8concat(out);
|
||||
}
|
||||
function process_chunk(type, buf) {
|
||||
function parse_snappy_chunk(type, buf) {
|
||||
if (type != 0)
|
||||
throw new Error("Unexpected Snappy chunk type ".concat(type));
|
||||
var ptr = [0];
|
||||
@ -270,6 +279,62 @@ var NUMBERS = (function() {
|
||||
return out;
|
||||
}
|
||||
|
||||
// src/prebnccell.ts
|
||||
function parseit(buf, version) {
|
||||
var dv = u8_to_dataview(buf);
|
||||
var ctype = buf[version == 4 ? 1 : 2];
|
||||
var flags = dv.getUint32(4, true);
|
||||
var data_offset = 12 + popcnt(flags & 16270) * 4;
|
||||
var sidx = -1, ieee = NaN, dt = NaN;
|
||||
if (flags & 16) {
|
||||
sidx = dv.getUint32(data_offset, true);
|
||||
data_offset += 4;
|
||||
}
|
||||
if (flags & 32) {
|
||||
ieee = dv.getFloat64(data_offset, true);
|
||||
data_offset += 8;
|
||||
}
|
||||
if (flags & 64) {
|
||||
dt = dv.getFloat64(data_offset, true);
|
||||
data_offset += 8;
|
||||
}
|
||||
var ret;
|
||||
switch (ctype) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
ret = { t: "n", v: ieee };
|
||||
break;
|
||||
case 3:
|
||||
ret = { t: "s", v: sidx };
|
||||
break;
|
||||
case 5:
|
||||
var dd = new Date(2001, 0, 1);
|
||||
dd.setTime(dd.getTime() + dt * 1e3);
|
||||
ret = { t: "d", v: dd };
|
||||
break;
|
||||
case 6:
|
||||
ret = { t: "b", v: ieee > 0 };
|
||||
break;
|
||||
case 7:
|
||||
ret = { t: "n", v: ieee };
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(buf.slice(0, 4)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function parse(buf) {
|
||||
var version = buf[0];
|
||||
switch (version) {
|
||||
case 3:
|
||||
case 4:
|
||||
return parseit(buf, version);
|
||||
default:
|
||||
throw new Error("Unsupported pre-BNC version ".concat(version));
|
||||
}
|
||||
}
|
||||
|
||||
// src/numbers.ts
|
||||
var encode_col = function(C) {
|
||||
var s = "";
|
||||
@ -343,7 +408,7 @@ var NUMBERS = (function() {
|
||||
var pb = parse_shallow(root.data);
|
||||
var entries = pb[3];
|
||||
var data = [];
|
||||
entries == null ? void 0 : entries.forEach(function(entry) {
|
||||
(entries || []).forEach(function(entry) {
|
||||
var le = parse_shallow(entry.data);
|
||||
var key = varint_to_i32(le[1][0].data) >>> 0;
|
||||
data[key] = u8str(le[3][0].data);
|
||||
@ -405,50 +470,12 @@ var NUMBERS = (function() {
|
||||
tiles.forEach(function(tile2) {
|
||||
tile2.ref.forEach(function(row, R) {
|
||||
row.forEach(function(buf, C) {
|
||||
var dv = u8_to_dataview(buf);
|
||||
var ctype = buf[2];
|
||||
var addr = encode_cell({ r: R, c: C });
|
||||
switch (ctype) {
|
||||
case 0:
|
||||
{
|
||||
switch (buf[1]) {
|
||||
case 3:
|
||||
ws[addr] = { t: "s", v: sst[dv.getUint32(buf.length - 4, true)] };
|
||||
break;
|
||||
case 2:
|
||||
ws[addr] = { t: "n", v: dv.getFloat64(16, true) };
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
break;
|
||||
case 7:
|
||||
break;
|
||||
case 6:
|
||||
ws[addr] = { t: "b", v: dv.getFloat64(buf.length - 8, true) > 0 };
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(buf.slice(0, 4)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
ws[addr] = { t: "s", v: sst[dv.getUint32(16, true)] };
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
ws[addr] = { t: "n", v: dv.getFloat64(buf.length - 12, true) };
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
ws[addr] = { t: "b", v: dv.getFloat64(16, true) > 0 };
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported cell type ".concat(ctype));
|
||||
var res = parse(buf);
|
||||
if (res) {
|
||||
ws[addr] = res;
|
||||
if (res.t == "s" && typeof res.v == "number")
|
||||
res.v = sst[res.v];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -8,6 +8,10 @@ var f = process.argv[2];
|
||||
var cfb = read(f, {type: "file"});
|
||||
var wb = parse_numbers(cfb);
|
||||
var sn = process.argv[3];
|
||||
if(sn && !isNaN(+sn)) sn = wb.SheetNames[+sn];
|
||||
if(wb.SheetNames.indexOf(sn) == -1) sn = wb.SheetNames[0];
|
||||
console.log(utils.sheet_to_csv(wb.Sheets[sn]));
|
||||
if(typeof sn == "undefined") {
|
||||
wb.SheetNames.forEach(sn => console.log(utils.sheet_to_csv(wb.Sheets[sn])));
|
||||
} else {
|
||||
if(sn && !isNaN(+sn)) sn = wb.SheetNames[+sn];
|
||||
if(wb.SheetNames.indexOf(sn) == -1) sn = wb.SheetNames[0];
|
||||
console.log(utils.sheet_to_csv(wb.Sheets[sn]));
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
import { Ptr, parse_varint49 } from './proto';
|
||||
import { Ptr, parse_varint49, write_varint49 } from './proto';
|
||||
import { u8concat } from './util';
|
||||
|
||||
function is_framed(buf: Uint8Array): boolean {
|
||||
@ -19,7 +19,7 @@ function deframe(buf: Uint8Array): Uint8Array {
|
||||
while(l < buf.length) {
|
||||
var t = buf[l++];
|
||||
var len = buf[l] | (buf[l+1]<<8) | (buf[l+2] << 16); l += 3;
|
||||
out.push(process_chunk(t, buf.slice(l, l + len)));
|
||||
out.push(parse_snappy_chunk(t, buf.slice(l, l + len)));
|
||||
l += len;
|
||||
}
|
||||
if(l !== buf.length) throw new Error("data is not a valid framed stream!");
|
||||
@ -27,7 +27,34 @@ function deframe(buf: Uint8Array): Uint8Array {
|
||||
}
|
||||
export { deframe };
|
||||
|
||||
function process_chunk(type: number, buf: Uint8Array): Uint8Array {
|
||||
function reframe(buf: Uint8Array): Uint8Array {
|
||||
var out: Uint8Array[] = [];
|
||||
var l = 0;
|
||||
while(l < buf.length) {
|
||||
var c = Math.min(buf.length - l, 0xFFFFFFF);
|
||||
var frame = new Uint8Array(4);
|
||||
out.push(frame);
|
||||
var usz = write_varint49(c);
|
||||
var L = usz.length;
|
||||
out.push(usz);
|
||||
|
||||
if(c <= 60) { L++; out.push(new Uint8Array([(c - 1)<<2])); }
|
||||
else if(c <= 0x100) { L += 2; out.push(new Uint8Array([0xF0, (c-1) & 0xFF])); }
|
||||
else if(c <= 0x10000) { L += 3; out.push(new Uint8Array([0xF4, (c-1) & 0xFF, ((c-1) >> 8) & 0xFF])); }
|
||||
else if(c <= 0x1000000) { L += 4; out.push(new Uint8Array([0xF8, (c-1) & 0xFF, ((c-1) >> 8) & 0xFF, ((c-1) >> 16) & 0xFF])); }
|
||||
else if(c <= 0x100000000) { L += 5; out.push(new Uint8Array([0xFC, (c-1) & 0xFF, ((c-1) >> 8) & 0xFF, ((c-1) >> 16) & 0xFF, ((c-1) >>> 24) & 0xFF])); }
|
||||
|
||||
out.push(buf.slice(l, l + c)); L += c;
|
||||
|
||||
frame[0] = 0;
|
||||
frame[1] = L & 0xFF; frame[2] = (L >> 8) & 0xFF; frame[3] = (L >> 16) & 0xFF;
|
||||
l += c;
|
||||
}
|
||||
return u8concat(out);
|
||||
}
|
||||
export { reframe };
|
||||
|
||||
function parse_snappy_chunk(type: number, buf: Uint8Array): Uint8Array {
|
||||
if(type != 0) throw new Error(`Unexpected Snappy chunk type ${type}`);
|
||||
var ptr: Ptr = [0];
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { u8str, u8_to_dataview } from './util';
|
||||
import { parse_shallow, varint_to_i32, parse_varint49, mappa } from './proto';
|
||||
import { deframe } from './frame';
|
||||
import { IWAArchiveInfo, IWAMessage, parse_iwa } from './iwa';
|
||||
import { parse as parse_bnc } from "./prebnccell";
|
||||
|
||||
/* written here to avoid a full import of the 'xlsx' library */
|
||||
var encode_col = (C: number): string => {
|
||||
@ -59,7 +60,7 @@ function parse_TST_TableDataList(M: IWAMessage[][], root: IWAMessage): string[]
|
||||
var pb = parse_shallow(root.data);
|
||||
var entries = pb[3];
|
||||
var data = [];
|
||||
entries?.forEach(entry => {
|
||||
(entries||[]).forEach(entry => {
|
||||
var le = parse_shallow(entry.data);
|
||||
var key = varint_to_i32(le[1][0].data)>>>0;
|
||||
data[key] = u8str(le[3][0].data);
|
||||
@ -129,33 +130,11 @@ function parse_TST_TableModelArchive(M: IWAMessage[][], root: IWAMessage, ws: Wo
|
||||
tiles.forEach((tile) => {
|
||||
tile.ref.forEach((row, R) => {
|
||||
row.forEach((buf, C) => {
|
||||
var dv = u8_to_dataview(buf);
|
||||
//var version = buf[0]; // numbers 3.x use "3", 6.x - 11.x use "4"
|
||||
/* TODO: find the correct field position of the data type and value. */
|
||||
var ctype = buf[2];
|
||||
var addr = encode_cell({r:R,c:C});
|
||||
switch(ctype) {
|
||||
case 0: { // TODO: generic ??
|
||||
switch(buf[1]) {
|
||||
case 3: ws[addr] = { t: "s", v: sst[dv.getUint32(buf.length - 4,true)] } as CellObject; break;
|
||||
case 2: ws[addr] = { t: "n", v: dv.getFloat64(16, true) } as CellObject; break;
|
||||
case 0: break; // ws[addr] = { t: "z" } as CellObject; // blank?
|
||||
case 5: break; // date-time
|
||||
case 7: break; // duration
|
||||
case 6: ws[addr] = { t: "b", v: dv.getFloat64(buf.length - 8, true) > 0 } as CellObject; break;
|
||||
default: throw new Error(`Unsupported cell type ${buf.slice(0,4)}`);
|
||||
}
|
||||
} break;
|
||||
case 3: { // string
|
||||
ws[addr] = { t: "s", v: sst[dv.getUint32(16,true)] } as CellObject;
|
||||
} break;
|
||||
case 2: { // number
|
||||
ws[addr] = { t: "n", v: dv.getFloat64(buf.length - 12, true) } as CellObject;
|
||||
} break;
|
||||
case 6: { // boolean
|
||||
ws[addr] = { t: "b", v: dv.getFloat64(16, true) > 0 } as CellObject; // 1 or 0
|
||||
} break;
|
||||
default: throw new Error(`Unsupported cell type ${ctype}`);
|
||||
var res = parse_bnc(buf);
|
||||
if(res) {
|
||||
ws[addr] = res as CellObject;
|
||||
if(res.t == "s" && typeof res.v == "number") res.v = sst[res.v];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
41
modules/src/prebnccell.ts
Normal file
41
modules/src/prebnccell.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
import { CellObject } from '../../';
|
||||
import { u8_to_dataview, popcnt } from './util';
|
||||
|
||||
function parseit(buf: Uint8Array, version: number): CellObject {
|
||||
var dv = u8_to_dataview(buf);
|
||||
var ctype = buf[version == 4 ? 1 : 2];
|
||||
|
||||
/* TODO: find the correct field position of number formats, formulae, etc */
|
||||
var flags = dv.getUint32(4, true);
|
||||
var data_offset = 12 + popcnt(flags & 0x3F8E) * 4;
|
||||
|
||||
var sidx = -1, ieee = NaN, dt = NaN;
|
||||
if(flags & 0x10) { sidx = dv.getUint32(data_offset, true); data_offset += 4; }
|
||||
if(flags & 0x20) { ieee = dv.getFloat64(data_offset, true); data_offset += 8; }
|
||||
if(flags & 0x40) { dt = dv.getFloat64(data_offset, true); data_offset += 8; }
|
||||
|
||||
var ret;
|
||||
switch(ctype) {
|
||||
case 0: break; // return { t: "z" }; // blank?
|
||||
case 2: ret = { t: "n", v: ieee }; break;
|
||||
case 3: ret = { t: "s", v: sidx }; break;
|
||||
case 5: var dd = new Date(2001, 0, 1); dd.setTime(dd.getTime() + dt * 1000); ret = { t: "d", v: dd }; break; // date-time TODO: relative or absolute?
|
||||
case 6: ret = { t: "b", v: ieee > 0 }; break;
|
||||
case 7: ret = { t: "n", v: ieee }; break; // duration in seconds TODO: emit [hh]:[mm] style format with adjusted value
|
||||
default: throw new Error(`Unsupported cell type ${buf.slice(0,4)}`);
|
||||
}
|
||||
/* TODO: Some fields appear after the cell data */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function parse(buf: Uint8Array): CellObject {
|
||||
var version = buf[0]; // numbers 3.5 uses "3", 6.x - 11.x use "4"
|
||||
switch(version) {
|
||||
case 3: case 4: return parseit(buf, version);
|
||||
default: throw new Error(`Unsupported pre-BNC version ${version}`);
|
||||
}
|
||||
}
|
||||
|
||||
export { parse };
|
@ -1,4 +1,6 @@
|
||||
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
import { u8concat } from "./util";
|
||||
|
||||
type Ptr = [number];
|
||||
export { Ptr };
|
||||
|
||||
@ -18,6 +20,26 @@ function parse_varint49(buf: Uint8Array, ptr?: Ptr): number {
|
||||
return usz;
|
||||
}
|
||||
export { parse_varint49 };
|
||||
function write_varint49(v: number): Uint8Array {
|
||||
var usz = new Uint8Array(7);
|
||||
usz[0] = (v & 0x7F);
|
||||
var L = 1;
|
||||
sz: if(v > 0x7F) {
|
||||
usz[L-1] |= 0x80; usz[L] = (v >> 7) & 0x7F; ++L;
|
||||
if(v <= 0x3FFF) break sz;
|
||||
usz[L-1] |= 0x80; usz[L] = (v >> 14) & 0x7F; ++L;
|
||||
if(v <= 0x1FFFFF) break sz;
|
||||
usz[L-1] |= 0x80; usz[L] = (v >> 21) & 0x7F; ++L;
|
||||
if(v <= 0xFFFFFFF) break sz;
|
||||
usz[L-1] |= 0x80; usz[L] = ((v/0x100) >>> 21) & 0x7F; ++L;
|
||||
if(v <= 0x7FFFFFFFF) break sz;
|
||||
usz[L-1] |= 0x80; usz[L] = ((v/0x10000) >>> 21) & 0x7F; ++L;
|
||||
if(v <= 0x3FFFFFFFFFF) break sz;
|
||||
usz[L-1] |= 0x80; usz[L] = ((v/0x1000000) >>> 21) & 0x7F; ++L;
|
||||
}
|
||||
return usz.slice(0, L);
|
||||
}
|
||||
export { write_varint49 };
|
||||
|
||||
/** Parse a 32-bit signed integer from the raw varint */
|
||||
function varint_to_i32(buf: Uint8Array): number {
|
||||
@ -33,12 +55,13 @@ function varint_to_i32(buf: Uint8Array): number {
|
||||
export { varint_to_i32 };
|
||||
|
||||
interface ProtoItem {
|
||||
offset: number;
|
||||
offset?: number;
|
||||
data: Uint8Array;
|
||||
type: number;
|
||||
}
|
||||
type ProtoField = Array<ProtoItem>
|
||||
type ProtoMessage = Array<ProtoField>;
|
||||
export { ProtoItem, ProtoField, ProtoMessage }
|
||||
export { ProtoItem, ProtoField, ProtoMessage };
|
||||
/** Shallow parse of a message */
|
||||
function parse_shallow(buf: Uint8Array): ProtoMessage {
|
||||
var out: ProtoMessage = [], ptr: Ptr = [0];
|
||||
@ -55,23 +78,32 @@ function parse_shallow(buf: Uint8Array): ProtoMessage {
|
||||
while(buf[ptr[0]++] >= 0x80);
|
||||
res = buf.slice(l, ptr[0]);
|
||||
} break;
|
||||
case 5: len = 4;
|
||||
/* falls through */
|
||||
case 1: if(!len) len = 8;
|
||||
/* falls through */
|
||||
case 2: if(!len) len = parse_varint49(buf, ptr);
|
||||
res = buf.slice(ptr[0], ptr[0] + len); ptr[0] += len; break;
|
||||
case 5: len = 4; res = buf.slice(ptr[0], ptr[0] + len); ptr[0] += len; break;
|
||||
case 1: len = 8; res = buf.slice(ptr[0], ptr[0] + len); ptr[0] += len; break;
|
||||
case 2: len = parse_varint49(buf, ptr); res = buf.slice(ptr[0], ptr[0] + len); ptr[0] += len; break;
|
||||
case 3: // Start group
|
||||
case 4: // End group
|
||||
default: throw new Error(`PB Type ${type} for Field ${num} at offset ${off}`);
|
||||
}
|
||||
var v: ProtoItem = { offset: off, data: res };
|
||||
var v: ProtoItem = { offset: off, data: res, type };
|
||||
if(out[num] == null) out[num] = [v];
|
||||
else out[num].push(v);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
export { parse_shallow };
|
||||
/** Serialize a shallow parse */
|
||||
function write_shallow(proto: ProtoMessage): Uint8Array {
|
||||
var out: Uint8Array[] = [];
|
||||
proto.forEach((field, idx) => {
|
||||
field.forEach(item => {
|
||||
out.push(write_varint49(idx * 8 + item.type));
|
||||
out.push(item.data);
|
||||
});
|
||||
});
|
||||
return u8concat(out);
|
||||
}
|
||||
export { write_shallow };
|
||||
|
||||
function mappa<U>(data: ProtoField, cb:(Uint8Array) => U): U[] {
|
||||
if(!data) return [];
|
||||
|
@ -37,3 +37,11 @@ function u8indexOf(u8: Uint8Array, data: string | number | Uint8Array, byteOffse
|
||||
return -1;
|
||||
}
|
||||
export { u8indexOf };
|
||||
|
||||
/* Hopefully one day this will be added to the language */
|
||||
var popcnt = (x: number): number => {
|
||||
x -= ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
|
||||
};
|
||||
export { popcnt };
|
||||
|
@ -171,12 +171,16 @@ function parse_shallow(buf) {
|
||||
break;
|
||||
case 5:
|
||||
len = 4;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 1:
|
||||
if (!len)
|
||||
len = 8;
|
||||
len = 8;
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
case 2:
|
||||
if (!len)
|
||||
len = parse_varint49(buf, ptr);
|
||||
len = parse_varint49(buf, ptr);
|
||||
res = buf.slice(ptr[0], ptr[0] + len);
|
||||
ptr[0] += len;
|
||||
break;
|
||||
@ -468,6 +472,9 @@ function otorp(buf, builtins = false) {
|
||||
var otorp_default = otorp;
|
||||
var is_referenced = (buf, pos) => {
|
||||
var dv = u8_to_dataview(buf);
|
||||
for (var leaddr = 0; leaddr > -1 && leaddr < pos; leaddr = u8indexOf(buf, 141, leaddr + 1))
|
||||
if (dv.getUint32(leaddr + 2, true) == pos - leaddr - 6)
|
||||
return true;
|
||||
try {
|
||||
var headers = parse_macho(buf);
|
||||
for (var i = 0; i < headers.length; ++i) {
|
||||
@ -507,6 +514,7 @@ var proto_offsets = (buf) => {
|
||||
if (buf[--pos] != 10)
|
||||
continue;
|
||||
if (!is_referenced(buf, pos)) {
|
||||
console.error(`Reference to ${name} not found`);
|
||||
continue;
|
||||
}
|
||||
var bin = meta.find((m) => m.offset <= pos && m.offset + m.size >= pos);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "otorp",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"author": "sheetjs",
|
||||
"description": "Recover protobuf definitions from Mach-O binaries",
|
||||
"bin": {
|
||||
|
@ -55,6 +55,11 @@ export default otorp;
|
||||
var is_referenced = (buf: Uint8Array, pos: number): boolean => {
|
||||
var dv = u8_to_dataview(buf);
|
||||
|
||||
/* Search for LEA reference (x86) */
|
||||
for(var leaddr = 0; leaddr > -1 && leaddr < pos; leaddr = u8indexOf(buf, 0x8D, leaddr + 1))
|
||||
if(dv.getUint32(leaddr + 2, true) == pos - leaddr - 6) return true;
|
||||
|
||||
/* Search for absolute reference to address */
|
||||
try {
|
||||
var headers = parse_macho(buf);
|
||||
for(var i = 0; i < headers.length; ++i) {
|
||||
@ -69,7 +74,6 @@ var is_referenced = (buf: Uint8Array, pos: number): boolean => {
|
||||
if(u8indexOf(b, ref, 0) > 0) return true;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -90,7 +94,7 @@ var proto_offsets = (buf: Uint8Array): OffsetList => {
|
||||
if(off - pos > 250) continue;
|
||||
var name = u8str(buf.slice(pos + 1, off));
|
||||
if(buf[--pos] != 0x0A) continue;
|
||||
if(!is_referenced(buf, pos)) { /* console.error(`Reference to ${name} not found`); */ continue; }
|
||||
if(!is_referenced(buf, pos)) { console.error(`Reference to ${name} not found`); continue; }
|
||||
var bin = meta.find(m => m.offset <= pos && m.offset + m.size >= pos);
|
||||
out.push([pos, name, bin?.type || -1, bin?.subtype || -1]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user