@shbernal/ts-xlsx 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -23
- package/dist/bytes.d.ts +3 -2
- package/dist/core/address.d.ts +36 -0
- package/dist/core/address.js +47 -9
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/color-resolution.js +2 -2
- package/dist/core/column.js +3 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/conditional-formatting-overlay.d.ts +9 -0
- package/dist/core/conditional-formatting-overlay.js +28 -1
- package/dist/core/conditional-formatting.d.ts +98 -10
- package/dist/core/conditional-formatting.js +42 -1
- package/dist/core/data-validation-overlay.d.ts +5 -0
- package/dist/core/data-validation-overlay.js +15 -0
- package/dist/core/data-validation.d.ts +16 -0
- package/dist/core/data-validation.js +15 -0
- package/dist/core/date-format.js +14 -4
- package/dist/core/date.js +1 -1
- package/dist/core/formula-references.d.ts +26 -0
- package/dist/core/formula-references.js +173 -0
- package/dist/core/formula-scan.d.ts +2 -0
- package/dist/core/formula-scan.js +58 -0
- package/dist/core/formula.d.ts +51 -18
- package/dist/core/formula.js +77 -86
- package/dist/core/function-values.d.ts +4 -0
- package/dist/core/function-values.js +58 -0
- package/dist/core/future-functions.d.ts +3 -0
- package/dist/core/{modern-functions.js → future-functions.js} +119 -88
- package/dist/core/grid-edits.d.ts +28 -0
- package/dist/core/grid-edits.js +193 -25
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +36 -5
- package/dist/core/image.js +9 -0
- package/dist/core/internal.d.ts +3 -1
- package/dist/core/limits.d.ts +5 -3
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +15 -1
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +55 -21
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +4 -0
- package/dist/core/row.js +3 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/table.d.ts +38 -8
- package/dist/core/table.js +37 -7
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +4 -1
- package/dist/core/value.d.ts +51 -28
- package/dist/core/value.js +15 -12
- package/dist/core/workbook-styles.d.ts +17 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook.d.ts +20 -3
- package/dist/core/workbook.js +26 -5
- package/dist/core/worksheet-merges.d.ts +9 -4
- package/dist/core/worksheet-merges.js +17 -11
- package/dist/core/worksheet-model.d.ts +1 -1
- package/dist/core/worksheet-model.js +4 -0
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +110 -21
- package/dist/core/worksheet.js +174 -49
- package/dist/entries/core.d.ts +7 -5
- package/dist/entries/core.js +4 -3
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/errors.js +1 -1
- package/dist/io/cell-metadata/metadata.d.ts +42 -0
- package/dist/io/cell-metadata/metadata.js +35 -0
- package/dist/io/cell-metadata/rich-values.d.ts +11 -0
- package/dist/io/cell-metadata/rich-values.js +80 -0
- package/dist/io/csv/delimiter.d.ts +14 -2
- package/dist/io/csv/delimiter.js +12 -0
- package/dist/io/csv/errors.d.ts +14 -0
- package/dist/io/csv/errors.js +5 -0
- package/dist/io/csv/read.d.ts +6 -1
- package/dist/io/csv/read.js +46 -24
- package/dist/io/csv/write.d.ts +10 -3
- package/dist/io/csv/write.js +16 -7
- package/dist/io/opc/part-paths.js +4 -4
- package/dist/io/opc/read-opc.d.ts +4 -0
- package/dist/io/opc/read-opc.js +10 -4
- package/dist/io/opc/strict-relationships.d.ts +2 -0
- package/dist/io/opc/strict-relationships.js +12 -0
- package/dist/io/read-policy/column-budget.d.ts +26 -0
- package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
- package/dist/io/read-policy/read-repair.d.ts +74 -0
- package/dist/io/read-policy/read-repair.js +119 -0
- package/dist/io/style/cell-style-resolution.d.ts +25 -0
- package/dist/io/style/cell-style-resolution.js +30 -0
- package/dist/io/xlsb/formula.js +4 -2
- package/dist/io/xlsb/ptg-functions.js +110 -0
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-worksheet.d.ts +6 -0
- package/dist/io/xlsb/read-worksheet.js +95 -37
- package/dist/io/xlsb/read.js +26 -8
- package/dist/io/xlsb/record-types.d.ts +13 -0
- package/dist/io/xlsb/record-types.js +13 -0
- package/dist/io/xlsb/sheet-protection.d.ts +20 -0
- package/dist/io/xlsb/sheet-protection.js +60 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
- package/dist/io/xlsx/cell-accumulator.js +49 -19
- package/dist/io/xlsx/cell-metadata.d.ts +39 -0
- package/dist/io/xlsx/cell-metadata.js +162 -0
- package/dist/io/xlsx/cell-value.d.ts +16 -4
- package/dist/io/xlsx/cell-value.js +38 -21
- package/dist/io/xlsx/color-xml.js +1 -1
- package/dist/io/xlsx/column-span.d.ts +11 -0
- package/dist/io/xlsx/column-span.js +9 -0
- package/dist/io/xlsx/comments.d.ts +3 -28
- package/dist/io/xlsx/comments.js +3 -65
- package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
- package/dist/io/xlsx/conditional-formatting.js +424 -129
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +41 -29
- package/dist/io/xlsx/edit-vba.js +43 -36
- package/dist/io/xlsx/hyperlinks.d.ts +16 -50
- package/dist/io/xlsx/hyperlinks.js +8 -37
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +145 -44
- package/dist/io/xlsx/namespaces.d.ts +8 -0
- package/dist/io/xlsx/namespaces.js +4 -0
- package/dist/io/xlsx/package-plan.d.ts +48 -15
- package/dist/io/xlsx/package-plan.js +46 -9
- package/dist/io/xlsx/part-names.d.ts +6 -8
- package/dist/io/xlsx/part-names.js +3 -7
- package/dist/io/xlsx/pivot.d.ts +5 -5
- package/dist/io/xlsx/pivot.js +23 -22
- package/dist/io/xlsx/preserved-splices.d.ts +6 -0
- package/dist/io/xlsx/preserved-splices.js +74 -0
- package/dist/io/xlsx/read-comments.d.ts +29 -0
- package/dist/io/xlsx/read-comments.js +75 -0
- package/dist/io/xlsx/read-parts.d.ts +2 -2
- package/dist/io/xlsx/read-parts.js +44 -37
- package/dist/io/xlsx/read-pivot.js +9 -3
- package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
- package/dist/io/xlsx/read-rich-runs.js +18 -1
- package/dist/io/xlsx/read-rows.d.ts +18 -7
- package/dist/io/xlsx/read-rows.js +47 -37
- package/dist/io/xlsx/read-styles.d.ts +0 -25
- package/dist/io/xlsx/read-styles.js +39 -34
- package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
- package/dist/io/xlsx/read-workbook-xml.js +9 -4
- package/dist/io/xlsx/read-worksheet.d.ts +8 -3
- package/dist/io/xlsx/read-worksheet.js +45 -23
- package/dist/io/xlsx/read.d.ts +0 -4
- package/dist/io/xlsx/read.js +17 -21
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/row-position.d.ts +3 -1
- package/dist/io/xlsx/row-xml.d.ts +18 -7
- package/dist/io/xlsx/row-xml.js +54 -35
- package/dist/io/xlsx/sheet-properties.js +68 -42
- package/dist/io/xlsx/strict-parts.d.ts +7 -0
- package/dist/io/xlsx/strict-parts.js +171 -0
- package/dist/io/xlsx/style-elements.d.ts +0 -2
- package/dist/io/xlsx/style-elements.js +9 -29
- package/dist/io/xlsx/styles.d.ts +5 -0
- package/dist/io/xlsx/styles.js +11 -5
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +49 -42
- package/dist/io/xlsx/theme-xml.d.ts +6 -0
- package/dist/io/xlsx/theme-xml.js +16 -19
- package/dist/io/xlsx/threaded-comments.d.ts +6 -1
- package/dist/io/xlsx/threaded-comments.js +14 -7
- package/dist/io/xlsx/workbook-xml.d.ts +13 -24
- package/dist/io/xlsx/workbook-xml.js +15 -33
- package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
- package/dist/io/xlsx/worksheet-xml.js +37 -69
- package/dist/io/xlsx/write-stream.d.ts +28 -6
- package/dist/io/xlsx/write-stream.js +42 -17
- package/dist/io/xlsx/write.d.ts +11 -4
- package/dist/io/xlsx/write.js +153 -90
- package/dist/rel-type.d.ts +9 -0
- package/dist/rel-type.js +3 -0
- package/dist/vba/bytes.d.ts +2 -0
- package/dist/vba/bytes.js +11 -0
- package/dist/vba/cfb-format.d.ts +47 -0
- package/dist/vba/cfb-format.js +34 -0
- package/dist/vba/cfb-writer.d.ts +1 -2
- package/dist/vba/cfb-writer.js +33 -36
- package/dist/vba/cfb.d.ts +9 -4
- package/dist/vba/cfb.js +55 -43
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +4 -4
- package/dist/vba/index.d.ts +0 -2
- package/dist/vba/index.js +0 -2
- package/dist/vba/ms-ovba.d.ts +6 -2
- package/dist/vba/ms-ovba.js +15 -6
- package/dist/vba/project-editor.js +102 -87
- package/dist/vba/project.d.ts +4 -0
- package/dist/vba/project.js +12 -6
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +2 -15
- package/dist/xml/xml-read.d.ts +29 -1
- package/dist/xml/xml-read.js +56 -0
- package/dist/xml/xml-scan.d.ts +13 -0
- package/dist/xml/xml-scan.js +58 -7
- package/dist/xml/xml.d.ts +33 -1
- package/dist/xml/xml.js +22 -0
- package/package.json +5 -4
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
- package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
- package/dist/io/xlsx/cell-style-resolution.js +0 -29
- package/dist/io/xlsx/column-budget.d.ts +0 -26
- package/dist/io/xlsx/read-repair.d.ts +0 -26
- package/dist/io/xlsx/read-repair.js +0 -39
package/dist/vba/cfb-format.d.ts
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
/** The header's first eight bytes, as the two little-endian `u32`s both directions handle them as. */
|
|
2
|
+
export declare const CFB_SIGNATURE: {
|
|
3
|
+
readonly low: 3759263696;
|
|
4
|
+
readonly high: 3776623009;
|
|
5
|
+
};
|
|
6
|
+
/** Byte offsets of the header fields either direction reads or writes ([MS-CFB] 2.2). */
|
|
7
|
+
export declare const HEADER_FIELD: {
|
|
8
|
+
readonly signatureLow: 0;
|
|
9
|
+
readonly signatureHigh: 4;
|
|
10
|
+
readonly minorVersion: 24;
|
|
11
|
+
readonly majorVersion: 26;
|
|
12
|
+
readonly byteOrder: 28;
|
|
13
|
+
readonly sectorShift: 30;
|
|
14
|
+
readonly miniSectorShift: 32;
|
|
15
|
+
readonly fatSectors: 44;
|
|
16
|
+
readonly firstDirectorySector: 48;
|
|
17
|
+
readonly miniStreamCutoff: 56;
|
|
18
|
+
readonly firstMiniFatSector: 60;
|
|
19
|
+
readonly miniFatSectors: 64;
|
|
20
|
+
readonly firstDifatSector: 68;
|
|
21
|
+
readonly difatSectors: 72;
|
|
22
|
+
readonly difat: 76;
|
|
23
|
+
};
|
|
24
|
+
/** Byte offsets within a directory entry, from the entry's start ([MS-CFB] 2.6.1). The name's UTF-16
|
|
25
|
+
* code units begin at offset 0. */
|
|
26
|
+
export declare const DIR_ENTRY_FIELD: {
|
|
27
|
+
readonly nameLength: 64;
|
|
28
|
+
readonly objectType: 66;
|
|
29
|
+
readonly color: 67;
|
|
30
|
+
readonly leftSibling: 68;
|
|
31
|
+
readonly rightSibling: 72;
|
|
32
|
+
readonly child: 76;
|
|
33
|
+
readonly startSector: 116;
|
|
34
|
+
readonly sizeLow: 120;
|
|
35
|
+
readonly sizeHigh: 124;
|
|
36
|
+
};
|
|
37
|
+
/** FAT-sector pointers the header holds before a DIFAT sector is needed ([MS-CFB] 2.2). */
|
|
38
|
+
export declare const HEADER_DIFAT_SLOTS = 109;
|
|
39
|
+
/** 64-byte mini sectors: [MS-CFB] 2.2 fixes the shift at 6 for both versions. */
|
|
40
|
+
export declare const MINI_SECTOR_SHIFT = 6;
|
|
1
41
|
/** Sector chain markers ([MS-CFB] 2.2). */
|
|
2
42
|
export declare const FREESECT = 4294967295;
|
|
3
43
|
export declare const ENDOFCHAIN = 4294967294;
|
|
@@ -30,3 +70,10 @@ export declare const DIR_ENTRY_SIZE = 128;
|
|
|
30
70
|
/** 32 UTF-16 code units including the NUL terminator. This is also why a VBA module name is capped
|
|
31
71
|
* at 31: the module is a stream, and the stream's name is the module's. */
|
|
32
72
|
export declare const MAX_NAME_CHARS = 31;
|
|
73
|
+
/**
|
|
74
|
+
* Whether two directory-entry names are the same entry. [MS-CFB] 2.6.4 orders siblings by length and
|
|
75
|
+
* then by their uppercase forms, and forbids two siblings that compare equal, so a host looking a name
|
|
76
|
+
* up treats `dir` and `DIR` as one entry. A lookup here does the same, or a file whose producer cased a
|
|
77
|
+
* name differently would read as missing a stream it has.
|
|
78
|
+
*/
|
|
79
|
+
export declare function sameEntryName(a: string, b: string): boolean;
|
package/dist/vba/cfb-format.js
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
export const CFB_SIGNATURE = { low: 0xe011cfd0, high: 0xe11ab1a1 };
|
|
2
|
+
export const HEADER_FIELD = {
|
|
3
|
+
signatureLow: 0,
|
|
4
|
+
signatureHigh: 4,
|
|
5
|
+
minorVersion: 24,
|
|
6
|
+
majorVersion: 26,
|
|
7
|
+
byteOrder: 28,
|
|
8
|
+
sectorShift: 30,
|
|
9
|
+
miniSectorShift: 32,
|
|
10
|
+
fatSectors: 44,
|
|
11
|
+
firstDirectorySector: 48,
|
|
12
|
+
miniStreamCutoff: 56,
|
|
13
|
+
firstMiniFatSector: 60,
|
|
14
|
+
miniFatSectors: 64,
|
|
15
|
+
firstDifatSector: 68,
|
|
16
|
+
difatSectors: 72,
|
|
17
|
+
difat: 76,
|
|
18
|
+
};
|
|
19
|
+
export const DIR_ENTRY_FIELD = {
|
|
20
|
+
nameLength: 64,
|
|
21
|
+
objectType: 66,
|
|
22
|
+
color: 67,
|
|
23
|
+
leftSibling: 68,
|
|
24
|
+
rightSibling: 72,
|
|
25
|
+
child: 76,
|
|
26
|
+
startSector: 116,
|
|
27
|
+
sizeLow: 120,
|
|
28
|
+
sizeHigh: 124,
|
|
29
|
+
};
|
|
30
|
+
export const HEADER_DIFAT_SLOTS = 109;
|
|
31
|
+
export const MINI_SECTOR_SHIFT = 6;
|
|
1
32
|
export const FREESECT = 0xffffffff;
|
|
2
33
|
export const ENDOFCHAIN = 0xfffffffe;
|
|
3
34
|
export const FATSECT = 0xfffffffd;
|
|
@@ -11,3 +42,6 @@ export const TYPE_ROOT = 5;
|
|
|
11
42
|
export const MINI_STREAM_CUTOFF = 4096;
|
|
12
43
|
export const DIR_ENTRY_SIZE = 128;
|
|
13
44
|
export const MAX_NAME_CHARS = 31;
|
|
45
|
+
export function sameEntryName(a, b) {
|
|
46
|
+
return a.length === b.length && a.toUpperCase() === b.toUpperCase();
|
|
47
|
+
}
|
package/dist/vba/cfb-writer.d.ts
CHANGED
|
@@ -13,10 +13,9 @@ export type CfbNode = CfbStream | CfbStorage;
|
|
|
13
13
|
*
|
|
14
14
|
* Exported beside the union rather than kept private, because the discriminant is `'data' in node`
|
|
15
15
|
* and that is the sort of test callers re-spell inline: `project-editor.ts` had it three times, once
|
|
16
|
-
* negated. The name says what the test means, and
|
|
16
|
+
* negated. The name says what the test means, and a node it answers `false` for is a storage.
|
|
17
17
|
*/
|
|
18
18
|
export declare function isStream(node: CfbNode): node is CfbStream;
|
|
19
|
-
export declare function isStorage(node: CfbNode): node is CfbStorage;
|
|
20
19
|
/**
|
|
21
20
|
* Encode a hierarchy of storages and streams into a v3 compound file. The Root Entry is synthesized
|
|
22
21
|
* automatically; `root` is its top-level children. Every stream becomes a directory entry reachable both
|
package/dist/vba/cfb-writer.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { quoted } from '../errors.js';
|
|
2
|
-
import { DIFSECT, DIR_ENTRY_SIZE, ENDOFCHAIN, FATSECT, FREESECT, MAX_NAME_CHARS, MINI_STREAM_CUTOFF, NOSTREAM, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
2
|
+
import { CFB_SIGNATURE, DIFSECT, DIR_ENTRY_FIELD, DIR_ENTRY_SIZE, ENDOFCHAIN, FATSECT, FREESECT, HEADER_DIFAT_SLOTS, HEADER_FIELD, MAX_NAME_CHARS, MINI_SECTOR_SHIFT, MINI_STREAM_CUTOFF, NOSTREAM, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
3
3
|
import { VbaAuthorError } from './errors.js';
|
|
4
4
|
export function isStream(node) {
|
|
5
5
|
return 'data' in node;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const SECTOR = 512;
|
|
11
|
-
const MINI_SECTOR = 64;
|
|
7
|
+
const SECTOR_SHIFT = 9;
|
|
8
|
+
const SECTOR = 1 << SECTOR_SHIFT;
|
|
9
|
+
const MINI_SECTOR = 1 << MINI_SECTOR_SHIFT;
|
|
12
10
|
const ENTRIES_PER_DIR_SECTOR = SECTOR / DIR_ENTRY_SIZE;
|
|
13
11
|
const FAT_ENTRIES_PER_SECTOR = SECTOR / 4;
|
|
14
|
-
const DIFAT_HEADER_SLOTS = 109;
|
|
15
12
|
const COLOR_BLACK = 1;
|
|
16
13
|
export function writeCompoundFile(root) {
|
|
17
14
|
const entries = [];
|
|
@@ -82,16 +79,16 @@ export function writeCompoundFile(root) {
|
|
|
82
79
|
for (;;) {
|
|
83
80
|
const total = baseSectors + fatSectors + difatSectors;
|
|
84
81
|
const needFat = Math.ceil(total / FAT_ENTRIES_PER_SECTOR);
|
|
85
|
-
const needDifat = needFat >
|
|
86
|
-
? Math.ceil((needFat -
|
|
82
|
+
const needDifat = needFat > HEADER_DIFAT_SLOTS
|
|
83
|
+
? Math.ceil((needFat - HEADER_DIFAT_SLOTS) / (FAT_ENTRIES_PER_SECTOR - 1))
|
|
87
84
|
: 0;
|
|
88
85
|
if (needFat === fatSectors && needDifat === difatSectors)
|
|
89
86
|
break;
|
|
90
87
|
fatSectors = needFat;
|
|
91
88
|
difatSectors = needDifat;
|
|
92
89
|
}
|
|
93
|
-
if (fatSectors >
|
|
94
|
-
throw new VbaAuthorError(`project needs ${fatSectors} FAT sectors, exceeding the ${
|
|
90
|
+
if (fatSectors > HEADER_DIFAT_SLOTS) {
|
|
91
|
+
throw new VbaAuthorError(`project needs ${fatSectors} FAT sectors, exceeding the ${HEADER_DIFAT_SLOTS}-sector single-header bound`);
|
|
95
92
|
}
|
|
96
93
|
let cursor = 0;
|
|
97
94
|
const dirStart = cursor;
|
|
@@ -158,7 +155,7 @@ export function writeCompoundFile(root) {
|
|
|
158
155
|
for (let d = 0; d < difatSectors; d++) {
|
|
159
156
|
const base = at(difatStart + d);
|
|
160
157
|
for (let i = 0; i < FAT_ENTRIES_PER_SECTOR - 1; i++) {
|
|
161
|
-
const fatIdx =
|
|
158
|
+
const fatIdx = HEADER_DIFAT_SLOTS + d * (FAT_ENTRIES_PER_SECTOR - 1) + i;
|
|
162
159
|
dv.setUint32(base + i * 4, fatIdx < fatSectors ? fatStart + fatIdx : FREESECT, true);
|
|
163
160
|
}
|
|
164
161
|
dv.setUint32(base + (FAT_ENTRIES_PER_SECTOR - 1) * 4, d < difatSectors - 1 ? difatStart + d + 1 : ENDOFCHAIN, true);
|
|
@@ -201,33 +198,33 @@ function compareNames(a, b) {
|
|
|
201
198
|
return ua < ub ? -1 : ua > ub ? 1 : 0;
|
|
202
199
|
}
|
|
203
200
|
function writeHeader(dv, p) {
|
|
204
|
-
dv.setUint32(
|
|
205
|
-
dv.setUint32(
|
|
206
|
-
dv.setUint16(
|
|
207
|
-
dv.setUint16(
|
|
208
|
-
dv.setUint16(
|
|
209
|
-
dv.setUint16(
|
|
210
|
-
dv.setUint16(
|
|
211
|
-
dv.setUint32(
|
|
212
|
-
dv.setUint32(
|
|
213
|
-
dv.setUint32(
|
|
214
|
-
dv.setUint32(
|
|
215
|
-
dv.setUint32(
|
|
216
|
-
dv.setUint32(
|
|
217
|
-
dv.setUint32(
|
|
218
|
-
for (let i = 0; i <
|
|
219
|
-
dv.setUint32(
|
|
201
|
+
dv.setUint32(HEADER_FIELD.signatureLow, CFB_SIGNATURE.low, true);
|
|
202
|
+
dv.setUint32(HEADER_FIELD.signatureHigh, CFB_SIGNATURE.high, true);
|
|
203
|
+
dv.setUint16(HEADER_FIELD.minorVersion, 0x003e, true);
|
|
204
|
+
dv.setUint16(HEADER_FIELD.majorVersion, 0x0003, true);
|
|
205
|
+
dv.setUint16(HEADER_FIELD.byteOrder, 0xfffe, true);
|
|
206
|
+
dv.setUint16(HEADER_FIELD.sectorShift, SECTOR_SHIFT, true);
|
|
207
|
+
dv.setUint16(HEADER_FIELD.miniSectorShift, MINI_SECTOR_SHIFT, true);
|
|
208
|
+
dv.setUint32(HEADER_FIELD.fatSectors, p.fatSectors, true);
|
|
209
|
+
dv.setUint32(HEADER_FIELD.firstDirectorySector, p.dirStart, true);
|
|
210
|
+
dv.setUint32(HEADER_FIELD.miniStreamCutoff, MINI_STREAM_CUTOFF, true);
|
|
211
|
+
dv.setUint32(HEADER_FIELD.firstMiniFatSector, p.miniFatStart, true);
|
|
212
|
+
dv.setUint32(HEADER_FIELD.miniFatSectors, p.miniFatSectors, true);
|
|
213
|
+
dv.setUint32(HEADER_FIELD.firstDifatSector, p.difatSectors > 0 ? p.difatStart : ENDOFCHAIN, true);
|
|
214
|
+
dv.setUint32(HEADER_FIELD.difatSectors, p.difatSectors, true);
|
|
215
|
+
for (let i = 0; i < HEADER_DIFAT_SLOTS; i++) {
|
|
216
|
+
dv.setUint32(HEADER_FIELD.difat + i * 4, i < p.fatSectors ? p.fatStart + i : FREESECT, true);
|
|
220
217
|
}
|
|
221
218
|
}
|
|
222
219
|
function writeDirEntry(dv, off, e) {
|
|
223
220
|
for (let i = 0; i < e.name.length; i++)
|
|
224
221
|
dv.setUint16(off + i * 2, e.name.charCodeAt(i), true);
|
|
225
|
-
dv.setUint16(off +
|
|
226
|
-
dv.setUint8(off +
|
|
227
|
-
dv.setUint8(off +
|
|
228
|
-
dv.setUint32(off +
|
|
229
|
-
dv.setUint32(off +
|
|
230
|
-
dv.setUint32(off +
|
|
231
|
-
dv.setUint32(off +
|
|
232
|
-
dv.setUint32(off +
|
|
222
|
+
dv.setUint16(off + DIR_ENTRY_FIELD.nameLength, (e.name.length + 1) * 2, true);
|
|
223
|
+
dv.setUint8(off + DIR_ENTRY_FIELD.objectType, e.type);
|
|
224
|
+
dv.setUint8(off + DIR_ENTRY_FIELD.color, COLOR_BLACK);
|
|
225
|
+
dv.setUint32(off + DIR_ENTRY_FIELD.leftSibling, e.left, true);
|
|
226
|
+
dv.setUint32(off + DIR_ENTRY_FIELD.rightSibling, e.right, true);
|
|
227
|
+
dv.setUint32(off + DIR_ENTRY_FIELD.child, e.child, true);
|
|
228
|
+
dv.setUint32(off + DIR_ENTRY_FIELD.startSector, e.startSector, true);
|
|
229
|
+
dv.setUint32(off + DIR_ENTRY_FIELD.sizeLow, e.size, true);
|
|
233
230
|
}
|
package/dist/vba/cfb.d.ts
CHANGED
|
@@ -2,10 +2,15 @@ import type { CfbNode } from './cfb-writer.ts';
|
|
|
2
2
|
export declare class CompoundFile {
|
|
3
3
|
#private;
|
|
4
4
|
constructor(buf: Uint8Array);
|
|
5
|
-
/** List every stream/storage name in the directory (order as stored). */
|
|
6
|
-
names(): string[];
|
|
7
5
|
/**
|
|
8
|
-
* Read a stream's raw bytes by
|
|
6
|
+
* Read a stream's raw bytes by its storage path from the root (`['VBA', 'dir']`), or `undefined` when
|
|
7
|
+
* no stream sits there. Each segment is matched among one storage's children, as [MS-CFB] compares
|
|
8
|
+
* names, by walking the sibling tree that storage links.
|
|
9
|
+
*
|
|
10
|
+
* A path because a name is not an address. [MS-OVBA] puts `PROJECT` at the root, `dir` and every
|
|
11
|
+
* module stream in `VBA`, and each UserForm's `f` and `o` in a storage of its own, so a bare name
|
|
12
|
+
* repeats. Looked up by name, the first entry in directory order won: a module named `f` read a
|
|
13
|
+
* UserForm's form data, and an edit wrote a module named `PROJECT` over the root `PROJECT`.
|
|
9
14
|
*
|
|
10
15
|
* @returns bytes that may be a **view onto the caller's buffer** rather than a copy. A stream small
|
|
11
16
|
* enough to fit in one sector is assembled by `concat` from a single chunk, and `concat` hands a
|
|
@@ -15,7 +20,7 @@ export declare class CompoundFile {
|
|
|
15
20
|
* thing that is true until someone writes the first mutation, and then true only for large
|
|
16
21
|
* streams. Copy before mutating.
|
|
17
22
|
*/
|
|
18
|
-
readStream(
|
|
23
|
+
readStream(path: readonly string[]): Uint8Array | undefined;
|
|
19
24
|
/**
|
|
20
25
|
* Reconstruct the container's top-level children as the writer's node shape, recursing into every
|
|
21
26
|
* storage, so a caller can swap one stream and re-emit the whole hierarchy with {@link writeCompoundFile}.
|
package/dist/vba/cfb.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { quoted } from '../errors.js';
|
|
2
2
|
import { concat, decodeUtf16le, readU16, readU32 } from './bytes.js';
|
|
3
|
-
import { DIR_ENTRY_SIZE, MAX_REGULAR_SECTOR, MINI_STREAM_CUTOFF, NOSTREAM, TYPE_EMPTY, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
3
|
+
import { CFB_SIGNATURE, DIR_ENTRY_FIELD, DIR_ENTRY_SIZE, HEADER_DIFAT_SLOTS, HEADER_FIELD, MAX_REGULAR_SECTOR, MINI_SECTOR_SHIFT, MINI_STREAM_CUTOFF, NOSTREAM, sameEntryName, TYPE_EMPTY, TYPE_ROOT, TYPE_STORAGE, TYPE_STREAM, } from './cfb-format.js';
|
|
4
4
|
import { VbaParseError } from './errors.js';
|
|
5
|
-
const CFB_SIGNATURE_LO = 0xe011cfd0;
|
|
6
|
-
const CFB_SIGNATURE_HI = 0xe11ab1a1;
|
|
7
5
|
const MAX_TREE_DEPTH = 256;
|
|
8
6
|
export class CompoundFile {
|
|
9
7
|
#buf;
|
|
@@ -19,28 +17,29 @@ export class CompoundFile {
|
|
|
19
17
|
this.#buf = buf;
|
|
20
18
|
if (buf.length < 512)
|
|
21
19
|
throw new VbaParseError('compound file shorter than its 512-byte header');
|
|
22
|
-
if (readU32(this.#buf,
|
|
20
|
+
if (readU32(this.#buf, HEADER_FIELD.signatureLow) !== CFB_SIGNATURE.low ||
|
|
21
|
+
readU32(this.#buf, HEADER_FIELD.signatureHigh) !== CFB_SIGNATURE.high) {
|
|
23
22
|
throw new VbaParseError('not a compound file (bad OLE2 signature)');
|
|
24
23
|
}
|
|
25
|
-
const sectorShift = readU16(this.#buf,
|
|
26
|
-
const miniSectorShift = readU16(this.#buf,
|
|
24
|
+
const sectorShift = readU16(this.#buf, HEADER_FIELD.sectorShift);
|
|
25
|
+
const miniSectorShift = readU16(this.#buf, HEADER_FIELD.miniSectorShift);
|
|
27
26
|
if (sectorShift !== 9 && sectorShift !== 12) {
|
|
28
27
|
throw new VbaParseError(`unsupported sector shift ${sectorShift}`);
|
|
29
28
|
}
|
|
30
|
-
if (miniSectorShift !==
|
|
29
|
+
if (miniSectorShift !== MINI_SECTOR_SHIFT)
|
|
31
30
|
throw new VbaParseError(`unsupported mini-sector shift ${miniSectorShift}`);
|
|
32
31
|
this.#sectorSize = 1 << sectorShift;
|
|
33
32
|
this.#miniSectorSize = 1 << miniSectorShift;
|
|
34
33
|
this.#maxSector = Math.floor(buf.length / this.#sectorSize);
|
|
35
|
-
const numFatSectors = readU32(this.#buf,
|
|
36
|
-
const firstDirSector = readU32(this.#buf,
|
|
37
|
-
this.#miniCutoff = readU32(this.#buf,
|
|
34
|
+
const numFatSectors = readU32(this.#buf, HEADER_FIELD.fatSectors);
|
|
35
|
+
const firstDirSector = readU32(this.#buf, HEADER_FIELD.firstDirectorySector);
|
|
36
|
+
this.#miniCutoff = readU32(this.#buf, HEADER_FIELD.miniStreamCutoff);
|
|
38
37
|
if (this.#miniCutoff !== MINI_STREAM_CUTOFF) {
|
|
39
38
|
throw new VbaParseError(`unsupported mini-stream cutoff ${this.#miniCutoff}`);
|
|
40
39
|
}
|
|
41
|
-
const firstMiniFatSector = readU32(this.#buf,
|
|
42
|
-
const firstDifatSector = readU32(this.#buf,
|
|
43
|
-
const numDifatSectors = readU32(this.#buf,
|
|
40
|
+
const firstMiniFatSector = readU32(this.#buf, HEADER_FIELD.firstMiniFatSector);
|
|
41
|
+
const firstDifatSector = readU32(this.#buf, HEADER_FIELD.firstDifatSector);
|
|
42
|
+
const numDifatSectors = readU32(this.#buf, HEADER_FIELD.difatSectors);
|
|
44
43
|
const fatSectorIds = this.#readDifat(numFatSectors, firstDifatSector, numDifatSectors);
|
|
45
44
|
this.#fat = this.#readFat(fatSectorIds);
|
|
46
45
|
this.#miniFat = this.#readChainValues(firstMiniFatSector);
|
|
@@ -50,28 +49,46 @@ export class CompoundFile {
|
|
|
50
49
|
throw new VbaParseError('compound file has no root storage entry');
|
|
51
50
|
this.#miniStream = this.#readViaFat(root.startSector, root.size);
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
readStream(path) {
|
|
53
|
+
const rootIdx = this.#rootIndex();
|
|
54
|
+
const seen = new Set([rootIdx]);
|
|
55
|
+
let firstChild = this.#dir[rootIdx].child;
|
|
56
|
+
for (const [depth, segment] of path.entries()) {
|
|
57
|
+
const stream = depth === path.length - 1;
|
|
58
|
+
const entry = this.#childIndices(firstChild, seen, depth)
|
|
59
|
+
.map((idx) => this.#dir[idx])
|
|
60
|
+
.find((e) => (stream ? e.type === TYPE_STREAM : e.type === TYPE_STORAGE) &&
|
|
61
|
+
sameEntryName(e.name, segment));
|
|
62
|
+
if (entry === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
if (stream)
|
|
65
|
+
return this.#readEntryData(entry);
|
|
66
|
+
firstChild = entry.child;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
63
69
|
}
|
|
64
70
|
tree() {
|
|
71
|
+
const rootIdx = this.#rootIndex();
|
|
72
|
+
return this.#buildSiblings(this.#dir[rootIdx].child, new Set([rootIdx]));
|
|
73
|
+
}
|
|
74
|
+
#rootIndex() {
|
|
65
75
|
const rootIdx = this.#dir.findIndex((e) => e.type === TYPE_ROOT);
|
|
66
76
|
if (rootIdx < 0)
|
|
67
77
|
throw new VbaParseError('compound file has no root storage entry');
|
|
68
|
-
|
|
69
|
-
return this.#buildSiblings(root.child, new Set([rootIdx]));
|
|
78
|
+
return rootIdx;
|
|
70
79
|
}
|
|
71
80
|
#buildSiblings(firstChild, seen, depth = 0) {
|
|
72
81
|
if (depth > MAX_TREE_DEPTH)
|
|
73
82
|
throw new VbaParseError('directory tree nests too deep');
|
|
74
|
-
|
|
83
|
+
return this.#childIndices(firstChild, seen, depth).map((idx) => {
|
|
84
|
+
const e = this.#dir[idx];
|
|
85
|
+
return e.type === TYPE_STORAGE
|
|
86
|
+
? { name: e.name, children: this.#buildSiblings(e.child, seen, depth + 1) }
|
|
87
|
+
: { name: e.name, data: this.#readEntryData(e) };
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
#childIndices(firstChild, seen, depth) {
|
|
91
|
+
const indices = [];
|
|
75
92
|
const walk = (idx, siblingDepth) => {
|
|
76
93
|
if (idx >= MAX_REGULAR_SECTOR)
|
|
77
94
|
return;
|
|
@@ -86,16 +103,11 @@ export class CompoundFile {
|
|
|
86
103
|
if (e.type === TYPE_EMPTY)
|
|
87
104
|
throw new VbaParseError('directory tree links an empty entry');
|
|
88
105
|
walk(e.left, siblingDepth + 1);
|
|
89
|
-
|
|
90
|
-
nodes.push({ name: e.name, children: this.#buildSiblings(e.child, seen, depth + 1) });
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
nodes.push({ name: e.name, data: this.#readEntryData(e) });
|
|
94
|
-
}
|
|
106
|
+
indices.push(idx);
|
|
95
107
|
walk(e.right, siblingDepth + 1);
|
|
96
108
|
};
|
|
97
109
|
walk(firstChild, depth);
|
|
98
|
-
return
|
|
110
|
+
return indices;
|
|
99
111
|
}
|
|
100
112
|
#readEntryData(entry) {
|
|
101
113
|
if (entry.size >= this.#miniCutoff)
|
|
@@ -105,8 +117,8 @@ export class CompoundFile {
|
|
|
105
117
|
#readDifat(numFatSectors, firstDifat, numDifat) {
|
|
106
118
|
const wanted = Math.min(numFatSectors, this.#maxSector);
|
|
107
119
|
const ids = [];
|
|
108
|
-
for (let i = 0; i <
|
|
109
|
-
const v = readU32(this.#buf,
|
|
120
|
+
for (let i = 0; i < HEADER_DIFAT_SLOTS && ids.length < wanted; i++) {
|
|
121
|
+
const v = readU32(this.#buf, HEADER_FIELD.difat + i * 4);
|
|
110
122
|
if (v >= MAX_REGULAR_SECTOR)
|
|
111
123
|
break;
|
|
112
124
|
ids.push(v);
|
|
@@ -163,16 +175,16 @@ export class CompoundFile {
|
|
|
163
175
|
const raw = this.#readChainFull(firstDirSector);
|
|
164
176
|
const entries = [];
|
|
165
177
|
for (let off = 0; off + DIR_ENTRY_SIZE <= raw.length; off += DIR_ENTRY_SIZE) {
|
|
166
|
-
const type = raw[off +
|
|
178
|
+
const type = raw[off + DIR_ENTRY_FIELD.objectType];
|
|
167
179
|
if (type !== TYPE_EMPTY &&
|
|
168
180
|
type !== TYPE_STORAGE &&
|
|
169
181
|
type !== TYPE_STREAM &&
|
|
170
182
|
type !== TYPE_ROOT) {
|
|
171
183
|
throw new VbaParseError(`directory entry has invalid object type ${type}`);
|
|
172
184
|
}
|
|
173
|
-
const left = readU32(raw, off +
|
|
174
|
-
const right = readU32(raw, off +
|
|
175
|
-
const child = readU32(raw, off +
|
|
185
|
+
const left = readU32(raw, off + DIR_ENTRY_FIELD.leftSibling);
|
|
186
|
+
const right = readU32(raw, off + DIR_ENTRY_FIELD.rightSibling);
|
|
187
|
+
const child = readU32(raw, off + DIR_ENTRY_FIELD.child);
|
|
176
188
|
if (type === TYPE_EMPTY) {
|
|
177
189
|
entries.push({
|
|
178
190
|
name: '',
|
|
@@ -185,14 +197,14 @@ export class CompoundFile {
|
|
|
185
197
|
});
|
|
186
198
|
continue;
|
|
187
199
|
}
|
|
188
|
-
const nameLen = readU16(raw, off +
|
|
200
|
+
const nameLen = readU16(raw, off + DIR_ENTRY_FIELD.nameLength);
|
|
189
201
|
if (nameLen > 64 || nameLen % 2 !== 0) {
|
|
190
202
|
throw new VbaParseError(`directory entry has invalid name length ${nameLen}`);
|
|
191
203
|
}
|
|
192
204
|
const name = decodeUtf16le(raw.subarray(off, off + Math.max(0, nameLen - 2)));
|
|
193
|
-
const startSector = readU32(raw, off +
|
|
194
|
-
const size = readU32(raw, off +
|
|
195
|
-
if (readU32(raw, off +
|
|
205
|
+
const startSector = readU32(raw, off + DIR_ENTRY_FIELD.startSector);
|
|
206
|
+
const size = readU32(raw, off + DIR_ENTRY_FIELD.sizeLow);
|
|
207
|
+
if (readU32(raw, off + DIR_ENTRY_FIELD.sizeHigh) !== 0) {
|
|
196
208
|
throw new VbaParseError(`directory entry ${quoted(name)} declares a stream larger than 4 GiB`);
|
|
197
209
|
}
|
|
198
210
|
entries.push({ name, type, startSector, size, left, right, child });
|
|
@@ -51,3 +51,9 @@ export interface DirRecord {
|
|
|
51
51
|
* @throws {VbaParseError} when a record's payload runs past the end of the stream.
|
|
52
52
|
*/
|
|
53
53
|
export declare function dirRecords(dir: Uint8Array, context: string): Generator<DirRecord>;
|
|
54
|
+
/**
|
|
55
|
+
* One record's bytes, `Id(u16) Size(u32) data[Size]`: the grammar {@link dirRecords} walks, written.
|
|
56
|
+
* Built as bytes rather than spread into a `number[]`, which overflowed the call stack on a payload of a
|
|
57
|
+
* few hundred thousand bytes. Not for PROJECTVERSION, whose uncounted VersionMinor no edit here writes.
|
|
58
|
+
*/
|
|
59
|
+
export declare function dirRecord(id: number, data: Uint8Array): Uint8Array;
|
package/dist/vba/dir-records.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hex } from '../hex.js';
|
|
2
|
+
import { readU16, readU32, writeU16, writeU32 } from './bytes.js';
|
|
2
3
|
import { VbaParseError } from './errors.js';
|
|
3
4
|
export const REC_PROJECT_CODEPAGE = 0x0003;
|
|
4
5
|
export const REC_PROJECT_VERSION = 0x0009;
|
|
@@ -19,10 +20,17 @@ export function* dirRecords(dir, context) {
|
|
|
19
20
|
const size = readU32(dir, pos + 2);
|
|
20
21
|
const dataStart = pos + 6;
|
|
21
22
|
if (dataStart + size > dir.length) {
|
|
22
|
-
throw new VbaParseError(`dir record 0x${id
|
|
23
|
+
throw new VbaParseError(`dir record 0x${hex(id, 4)} ${context}`);
|
|
23
24
|
}
|
|
24
25
|
const end = dataStart + size + (id === REC_PROJECT_VERSION ? 2 : 0);
|
|
25
26
|
yield { id, recordStart: pos, dataStart, size, end };
|
|
26
27
|
pos = end;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
30
|
+
export function dirRecord(id, data) {
|
|
31
|
+
const out = new Uint8Array(6 + data.length);
|
|
32
|
+
writeU16(out, 0, id);
|
|
33
|
+
writeU32(out, 2, data.length);
|
|
34
|
+
out.set(data, 6);
|
|
35
|
+
return out;
|
|
36
|
+
}
|
package/dist/vba/errors.d.ts
CHANGED
|
@@ -12,10 +12,10 @@ export declare class VbaParseError extends XlsxError {
|
|
|
12
12
|
readonly code = "malformed-input";
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Thrown when
|
|
16
|
-
* produce something that cannot be encoded to a well-formed container: a stream name longer
|
|
17
|
-
* [MS-CFB] 31-character limit, a duplicate stream name, or a project so large it would exceed
|
|
18
|
-
* writer's single-header DIFAT bound. This is a caller-side contract violation, distinct from
|
|
15
|
+
* Thrown when a structural edit of a VBA project (removing a module, adding a library reference) is
|
|
16
|
+
* asked to produce something that cannot be encoded to a well-formed container: a stream name longer
|
|
17
|
+
* than the [MS-CFB] 31-character limit, a duplicate stream name, or a project so large it would exceed
|
|
18
|
+
* the writer's single-header DIFAT bound. This is a caller-side contract violation, distinct from
|
|
19
19
|
* {@link VbaParseError} (which reports a malformed blob *read* from an untrusted file).
|
|
20
20
|
*
|
|
21
21
|
* It shares `code: 'authoring'` with `AuthoringError` rather than being one, and it is the only
|
package/dist/vba/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { type CfbNode, type CfbStorage, type CfbStream, writeCompoundFile } from './cfb-writer.ts';
|
|
2
1
|
export { VbaAuthorError, VbaParseError } from './errors.ts';
|
|
3
|
-
export { compressContainer, decompressContainer } from './ms-ovba.ts';
|
|
4
2
|
export { parseVbaProject, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, type VbaModule, type VbaModuleKind, type VbaProject, type VbaProjectSignature, type VbaProjectSignatureKind, vbaProjectSignatureKind, } from './project.ts';
|
|
5
3
|
export { addVbaReference, removeVbaModule, type VbaLibraryReference } from './project-editor.ts';
|
package/dist/vba/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { writeCompoundFile } from './cfb-writer.js';
|
|
2
1
|
export { VbaAuthorError, VbaParseError } from './errors.js';
|
|
3
|
-
export { compressContainer, decompressContainer } from './ms-ovba.js';
|
|
4
2
|
export { parseVbaProject, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, vbaProjectSignatureKind, } from './project.js';
|
|
5
3
|
export { addVbaReference, removeVbaModule } from './project-editor.js';
|
package/dist/vba/ms-ovba.d.ts
CHANGED
|
@@ -6,7 +6,11 @@ export declare function decompressContainer(buf: Uint8Array, start?: number, max
|
|
|
6
6
|
/**
|
|
7
7
|
* Compress `data` into an MS-OVBA CompressedContainer: the inverse of {@link decompressContainer}.
|
|
8
8
|
* Every 4096-decompressed-byte window is emitted as a compressed chunk of literal and copy tokens, or
|
|
9
|
-
* stored
|
|
10
|
-
*
|
|
9
|
+
* stored raw when those tokens would not fit the 4096 bytes a chunk's data may hold.
|
|
10
|
+
*
|
|
11
|
+
* The result re-expands to `data` byte-for-byte, with one exception the format itself prescribes: a
|
|
12
|
+
* final window that is shorter than 4096 bytes and does not compress (only possible past about 3640
|
|
13
|
+
* bytes, where a literal's flag-bit overhead first overflows the chunk) is stored raw and padded with
|
|
14
|
+
* `0x00` to 4096, so it re-expands with those zeros after it ([MS-OVBA] 2.4.1.3.10).
|
|
11
15
|
*/
|
|
12
16
|
export declare function compressContainer(data: Uint8Array): Uint8Array;
|
package/dist/vba/ms-ovba.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { hex } from '../hex.js';
|
|
1
2
|
import { readU16 } from './bytes.js';
|
|
2
3
|
import { VbaParseError } from './errors.js';
|
|
3
4
|
const MAX_CHUNK_DECOMPRESSED = 4096;
|
|
4
5
|
const CHUNK_SIGNATURE = 0b011 << 12;
|
|
6
|
+
const CHUNK_SIGNATURE_MASK = 0b111 << 12;
|
|
5
7
|
const CHUNK_COMPRESSED_FLAG = 0x8000;
|
|
6
8
|
const MAX_MATCH_CANDIDATES = 64;
|
|
7
9
|
const HASH_BITS = 12;
|
|
@@ -12,7 +14,7 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
12
14
|
throw new VbaParseError(`compressed container starts past end of stream (${start} >= ${buf.length})`);
|
|
13
15
|
}
|
|
14
16
|
if (buf[start] !== 0x01) {
|
|
15
|
-
throw new VbaParseError(`compressed container must begin with a 0x01 signature byte, found 0x${(buf[start] ?? 0)
|
|
17
|
+
throw new VbaParseError(`compressed container must begin with a 0x01 signature byte, found 0x${hex(buf[start] ?? 0, 2)}`);
|
|
16
18
|
}
|
|
17
19
|
const out = new ByteSink(maxOutput);
|
|
18
20
|
let pos = start + 1;
|
|
@@ -20,9 +22,9 @@ export function decompressContainer(buf, start = 0, maxOutput = DEFAULT_MAX_OUTP
|
|
|
20
22
|
const header = readU16(buf, pos);
|
|
21
23
|
pos += 2;
|
|
22
24
|
const chunkDataSize = (header & 0x0fff) + 1;
|
|
23
|
-
const compressed = (header &
|
|
24
|
-
if ((
|
|
25
|
-
throw new VbaParseError(`chunk header has a bad 0b011 signature (0x${header
|
|
25
|
+
const compressed = (header & CHUNK_COMPRESSED_FLAG) !== 0;
|
|
26
|
+
if ((header & CHUNK_SIGNATURE_MASK) !== CHUNK_SIGNATURE) {
|
|
27
|
+
throw new VbaParseError(`chunk header has a bad 0b011 signature (0x${hex(header, 4)})`);
|
|
26
28
|
}
|
|
27
29
|
const chunkEnd = pos + chunkDataSize;
|
|
28
30
|
if (chunkEnd > buf.length) {
|
|
@@ -108,8 +110,8 @@ export function compressContainer(data) {
|
|
|
108
110
|
for (let start = 0; start < data.length; start += MAX_CHUNK_DECOMPRESSED) {
|
|
109
111
|
const chunk = data.subarray(start, Math.min(start + MAX_CHUNK_DECOMPRESSED, data.length));
|
|
110
112
|
const tokens = compressChunk(chunk);
|
|
111
|
-
const compressed = tokens.length
|
|
112
|
-
const body = compressed ? tokens : chunk;
|
|
113
|
+
const compressed = tokens.length <= MAX_CHUNK_DECOMPRESSED;
|
|
114
|
+
const body = compressed ? tokens : padToChunk(chunk);
|
|
113
115
|
const header = (compressed ? CHUNK_COMPRESSED_FLAG : 0) | CHUNK_SIGNATURE | ((body.length - 1) & 0x0fff);
|
|
114
116
|
out.push(header & 0xff);
|
|
115
117
|
out.push((header >> 8) & 0xff);
|
|
@@ -117,6 +119,13 @@ export function compressContainer(data) {
|
|
|
117
119
|
}
|
|
118
120
|
return out.bytes();
|
|
119
121
|
}
|
|
122
|
+
function padToChunk(chunk) {
|
|
123
|
+
if (chunk.length === MAX_CHUNK_DECOMPRESSED)
|
|
124
|
+
return chunk;
|
|
125
|
+
const padded = new Uint8Array(MAX_CHUNK_DECOMPRESSED);
|
|
126
|
+
padded.set(chunk);
|
|
127
|
+
return padded;
|
|
128
|
+
}
|
|
120
129
|
function compressChunk(chunk) {
|
|
121
130
|
const tokens = new ByteSink();
|
|
122
131
|
const heads = new Int32Array(1 << HASH_BITS).fill(NO_CANDIDATE);
|