@intactfile/engines 0.2.0 → 0.3.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/dist/detect/index.d.ts +1 -1
- package/dist/detect/index.d.ts.map +1 -1
- package/dist/detect/index.js +77 -0
- package/dist/detect/index.js.map +1 -1
- package/dist/heic/format.d.ts +87 -0
- package/dist/heic/format.d.ts.map +1 -0
- package/dist/heic/format.js +241 -0
- package/dist/heic/format.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/office-legacy/diagnose.d.ts +24 -0
- package/dist/office-legacy/diagnose.d.ts.map +1 -0
- package/dist/office-legacy/diagnose.js +111 -0
- package/dist/office-legacy/diagnose.js.map +1 -0
- package/dist/office-legacy/format.d.ts +89 -0
- package/dist/office-legacy/format.d.ts.map +1 -0
- package/dist/office-legacy/format.js +281 -0
- package/dist/office-legacy/format.js.map +1 -0
- package/dist/office-legacy/index.d.ts +9 -0
- package/dist/office-legacy/index.d.ts.map +1 -0
- package/dist/office-legacy/index.js +18 -0
- package/dist/office-legacy/index.js.map +1 -0
- package/dist/office-legacy/rebuild.d.ts +8 -0
- package/dist/office-legacy/rebuild.d.ts.map +1 -0
- package/dist/office-legacy/rebuild.js +234 -0
- package/dist/office-legacy/rebuild.js.map +1 -0
- package/dist/office-legacy/repair.d.ts +40 -0
- package/dist/office-legacy/repair.d.ts.map +1 -0
- package/dist/office-legacy/repair.js +161 -0
- package/dist/office-legacy/repair.js.map +1 -0
- package/dist/psd/diagnose.d.ts +33 -0
- package/dist/psd/diagnose.d.ts.map +1 -0
- package/dist/psd/diagnose.js +176 -0
- package/dist/psd/diagnose.js.map +1 -0
- package/dist/psd/format.d.ts +106 -0
- package/dist/psd/format.d.ts.map +1 -0
- package/dist/psd/format.js +238 -0
- package/dist/psd/format.js.map +1 -0
- package/dist/psd/index.d.ts +8 -0
- package/dist/psd/index.d.ts.map +1 -0
- package/dist/psd/index.js +13 -0
- package/dist/psd/index.js.map +1 -0
- package/dist/psd/repair.d.ts +36 -0
- package/dist/psd/repair.d.ts.map +1 -0
- package/dist/psd/repair.js +451 -0
- package/dist/psd/repair.js.map +1 -0
- package/dist/raw/diagnose.d.ts +29 -0
- package/dist/raw/diagnose.d.ts.map +1 -0
- package/dist/raw/diagnose.js +95 -0
- package/dist/raw/diagnose.js.map +1 -0
- package/dist/raw/format.d.ts +47 -0
- package/dist/raw/format.d.ts.map +1 -0
- package/dist/raw/format.js +213 -0
- package/dist/raw/format.js.map +1 -0
- package/dist/raw/index.d.ts +7 -0
- package/dist/raw/index.d.ts.map +1 -0
- package/dist/raw/index.js +13 -0
- package/dist/raw/index.js.map +1 -0
- package/dist/raw/repair.d.ts +46 -0
- package/dist/raw/repair.d.ts.map +1 -0
- package/dist/raw/repair.js +94 -0
- package/dist/raw/repair.js.map +1 -0
- package/package.json +14 -2
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readAll } from "../bytes-util.js";
|
|
2
|
+
import { MAXREGSECT, buildFat, classifyContainer, findMagic, hasMagicAt, linearScanEntries, magicScore, parseHeader, readDifat, readDirectory, sectOffset, u16, } from "./format.js";
|
|
3
|
+
const SCAN_WINDOW = 64 * 1024;
|
|
4
|
+
const isEncrypted = (names) => names.some((n) => n === "EncryptedPackage" || n === "EncryptionInfo" || /encrypt/i.test(n));
|
|
5
|
+
export function diagnoseOfficeBytes(input) {
|
|
6
|
+
const notes = [];
|
|
7
|
+
const base = {
|
|
8
|
+
notes,
|
|
9
|
+
majorVersion: null,
|
|
10
|
+
sectorSize: null,
|
|
11
|
+
container: null,
|
|
12
|
+
streamsFound: 0,
|
|
13
|
+
prefixBytes: 0,
|
|
14
|
+
};
|
|
15
|
+
const done = (damageClass, confidence) => ({
|
|
16
|
+
damageClass,
|
|
17
|
+
confidence,
|
|
18
|
+
...base,
|
|
19
|
+
});
|
|
20
|
+
if (input.length < 512) {
|
|
21
|
+
notes.push(`file is only ${input.length} bytes: too small to be a compound file`);
|
|
22
|
+
return done("not-cfbf", "high");
|
|
23
|
+
}
|
|
24
|
+
// Locate the header.
|
|
25
|
+
let work = input;
|
|
26
|
+
let headerDamaged = false;
|
|
27
|
+
if (!hasMagicAt(input, 0)) {
|
|
28
|
+
const score = magicScore(input, 0);
|
|
29
|
+
const at = findMagic(input, SCAN_WINDOW);
|
|
30
|
+
if (at > 0) {
|
|
31
|
+
base.prefixBytes = at;
|
|
32
|
+
work = input.subarray(at);
|
|
33
|
+
headerDamaged = true;
|
|
34
|
+
notes.push(`CFBF magic found at offset ${at} (garbage prefix)`);
|
|
35
|
+
}
|
|
36
|
+
else if (score >= 5) {
|
|
37
|
+
headerDamaged = true;
|
|
38
|
+
notes.push("CFBF signature is partially damaged but recognizable");
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
notes.push("no CFBF signature (D0 CF 11 E0 …) found");
|
|
42
|
+
return done("not-cfbf", input.length > SCAN_WINDOW ? "medium" : "high");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const header = parseHeader(work);
|
|
46
|
+
if (!header) {
|
|
47
|
+
notes.push("compound-file header is unreadable");
|
|
48
|
+
return done("header-damage", "medium");
|
|
49
|
+
}
|
|
50
|
+
base.majorVersion = header.major;
|
|
51
|
+
base.sectorSize = header.sectorSize;
|
|
52
|
+
// Raw-header field sanity (before normalization): a garbled sector shift /
|
|
53
|
+
// byte order / mini shift that we can deterministically fix.
|
|
54
|
+
const rawShift = u16(work, 30);
|
|
55
|
+
const rawByteOrder = u16(work, 28);
|
|
56
|
+
const rawMiniShift = u16(work, 32);
|
|
57
|
+
if (rawByteOrder !== 0xfffe || (rawShift !== 9 && rawShift !== 12) || rawMiniShift !== 6) {
|
|
58
|
+
headerDamaged = true;
|
|
59
|
+
}
|
|
60
|
+
const fatSectors = readDifat(work, header);
|
|
61
|
+
const fat = fatSectors.length ? buildFat(work, header, fatSectors) : null;
|
|
62
|
+
// Directory via the chain; fall back to a linear scan.
|
|
63
|
+
let entries = fat ? readDirectory(work, header, fat) : [];
|
|
64
|
+
let usedScan = false;
|
|
65
|
+
const streamEntries = (e) => e.filter((x) => (x.type === 1 || x.type === 2) && x.name);
|
|
66
|
+
if (!entries.some((e) => e.type === 5) || streamEntries(entries).length === 0) {
|
|
67
|
+
const scanned = linearScanEntries(work, header);
|
|
68
|
+
if (streamEntries(scanned).length > streamEntries(entries).length) {
|
|
69
|
+
entries = scanned;
|
|
70
|
+
usedScan = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const names = streamEntries(entries).map((e) => e.name);
|
|
74
|
+
base.streamsFound = names.length;
|
|
75
|
+
base.container = classifyContainer(names);
|
|
76
|
+
if (isEncrypted(names)) {
|
|
77
|
+
notes.push("legacy Office encryption detected (EncryptedPackage/EncryptionInfo): recovery needs the password");
|
|
78
|
+
return done("encrypted", "high");
|
|
79
|
+
}
|
|
80
|
+
// Truncation: a real (non-sentinel) directory or FAT sector points past EOF.
|
|
81
|
+
const dirOff = sectOffset(header.firstDirSector, header.sectorSize);
|
|
82
|
+
const dirPastEof = header.firstDirSector <= MAXREGSECT && dirOff + header.sectorSize > work.length;
|
|
83
|
+
const fatPastEof = fatSectors.some((s) => sectOffset(s, header.sectorSize) + header.sectorSize > work.length);
|
|
84
|
+
if (dirPastEof || fatPastEof) {
|
|
85
|
+
notes.push("declared FAT/directory sectors extend past end-of-file: the file was cut short");
|
|
86
|
+
notes.push("data beyond the cut is physically gone; recovery is partial");
|
|
87
|
+
return done("truncated", "high");
|
|
88
|
+
}
|
|
89
|
+
if (!fat || fatSectors.length === 0) {
|
|
90
|
+
notes.push("FAT/DIFAT is unreadable: sector chains cannot be followed");
|
|
91
|
+
return done("fat-damage", "high");
|
|
92
|
+
}
|
|
93
|
+
if (usedScan) {
|
|
94
|
+
notes.push("the directory chain/tree is broken; streams are only reachable by a linear scan");
|
|
95
|
+
return done("directory-damage", "high");
|
|
96
|
+
}
|
|
97
|
+
if (headerDamaged) {
|
|
98
|
+
notes.push("header fields are damaged but the body is intact: deterministically fixable");
|
|
99
|
+
return done("header-damage", "high");
|
|
100
|
+
}
|
|
101
|
+
if (base.streamsFound === 0) {
|
|
102
|
+
notes.push("no streams could be located");
|
|
103
|
+
return done("directory-damage", "medium");
|
|
104
|
+
}
|
|
105
|
+
notes.push(`compound file OK: ${base.streamsFound} stream(s), container ${base.container}`);
|
|
106
|
+
return done("none", "high");
|
|
107
|
+
}
|
|
108
|
+
export async function diagnoseOffice(src) {
|
|
109
|
+
return diagnoseOfficeBytes(await readAll(src));
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=diagnose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../src/office-legacy/diagnose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure CFBF / OLE2 (Compound File Binary Format) primitives: header parse,
|
|
3
|
+
* FAT/DIFAT/MiniFAT walk, 128-byte directory entries, sector→offset math, and
|
|
4
|
+
* a pointer-free linear directory scan for salvage. No wasm, no compression —
|
|
5
|
+
* CFBF stores stream bytes raw, so this is all integer/offset arithmetic.
|
|
6
|
+
*
|
|
7
|
+
* Backs legacy binary Office: .doc/.xls/.ppt (97–2003), plus .msg/.msi. Repair
|
|
8
|
+
* scope is the CONTAINER + stream salvage — never the document content inside a
|
|
9
|
+
* stream. Reference: [MS-CFB]. All fields little-endian.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CFBF_MAGIC: Uint8Array<ArrayBuffer>;
|
|
12
|
+
export declare const MAXREGSECT = 4294967290;
|
|
13
|
+
export declare const DIFSECT = 4294967292;
|
|
14
|
+
export declare const FATSECT = 4294967293;
|
|
15
|
+
export declare const ENDOFCHAIN = 4294967294;
|
|
16
|
+
export declare const FREESECT = 4294967295;
|
|
17
|
+
export declare const NOSTREAM = 4294967295;
|
|
18
|
+
export declare const MINI_CUTOFF = 4096;
|
|
19
|
+
export declare const MINI_SECTOR = 64;
|
|
20
|
+
export declare const DIR_ENTRY = 128;
|
|
21
|
+
export declare const u16: (b: Uint8Array, o: number) => number;
|
|
22
|
+
export declare const u32: (b: Uint8Array, o: number) => number;
|
|
23
|
+
/** 64-bit little-endian as a JS number (v3 uses only the low 32 bits). */
|
|
24
|
+
export declare const u64: (b: Uint8Array, o: number) => number;
|
|
25
|
+
export interface CfbfHeader {
|
|
26
|
+
major: number;
|
|
27
|
+
sectorSize: number;
|
|
28
|
+
miniCutoff: number;
|
|
29
|
+
numFatSectors: number;
|
|
30
|
+
firstDirSector: number;
|
|
31
|
+
firstMiniFatSector: number;
|
|
32
|
+
numMiniFatSectors: number;
|
|
33
|
+
firstDifatSector: number;
|
|
34
|
+
numDifatSectors: number;
|
|
35
|
+
}
|
|
36
|
+
export declare function hasMagicAt(b: Uint8Array, off: number): boolean;
|
|
37
|
+
/** How many of the 8 magic bytes match at `off` (partial-magic heuristic). */
|
|
38
|
+
export declare function magicScore(b: Uint8Array, off: number): number;
|
|
39
|
+
/** First offset of an exact CFBF magic within `window`, or -1. */
|
|
40
|
+
export declare function findMagic(b: Uint8Array, window: number): number;
|
|
41
|
+
/** Parse the 512-byte header. Returns null if the sector shift is implausible. */
|
|
42
|
+
export declare function parseHeader(b: Uint8Array): CfbfHeader | null;
|
|
43
|
+
/** SECT number → absolute file offset. Sector 0 begins one sector past the header. */
|
|
44
|
+
export declare const sectOffset: (sect: number, sectorSize: number) => number;
|
|
45
|
+
/** Collect the FAT-sector numbers from the header DIFAT + any chained DIFAT sectors. */
|
|
46
|
+
export declare function readDifat(b: Uint8Array, header: CfbfHeader): number[];
|
|
47
|
+
/** Build the FAT array from its sectors. Out-of-range sectors read as FREESECT. */
|
|
48
|
+
export declare function buildFat(b: Uint8Array, header: CfbfHeader, fatSectors: number[]): Uint32Array;
|
|
49
|
+
/** Follow a FAT/MiniFAT chain from `start`, with cycle + bound guards. */
|
|
50
|
+
export declare function readChain(fat: Uint32Array, start: number, maxLen: number): number[];
|
|
51
|
+
export interface StreamRead {
|
|
52
|
+
bytes: Uint8Array;
|
|
53
|
+
complete: boolean;
|
|
54
|
+
brokenChain: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Read a normal (FAT-chained) stream of `size` bytes from `start`. */
|
|
57
|
+
export declare function readBigStream(b: Uint8Array, header: CfbfHeader, fat: Uint32Array, start: number, size: number): StreamRead;
|
|
58
|
+
/** Build the MiniFAT array by reading its sector chain from the FAT. */
|
|
59
|
+
export declare function buildMiniFat(b: Uint8Array, header: CfbfHeader, fat: Uint32Array): Uint32Array;
|
|
60
|
+
/** Read a small (MiniFAT-chained) stream from the mini stream. */
|
|
61
|
+
export declare function readMiniStream(miniStream: Uint8Array, miniFat: Uint32Array, start: number, size: number): StreamRead;
|
|
62
|
+
export interface DirEntry {
|
|
63
|
+
id: number;
|
|
64
|
+
name: string;
|
|
65
|
+
type: number;
|
|
66
|
+
color: number;
|
|
67
|
+
left: number;
|
|
68
|
+
right: number;
|
|
69
|
+
child: number;
|
|
70
|
+
startSector: number;
|
|
71
|
+
size: number;
|
|
72
|
+
/** byte offset of the raw 128-byte record (for the linear scanner). */
|
|
73
|
+
recordOffset: number;
|
|
74
|
+
}
|
|
75
|
+
/** Parse one 128-byte directory entry at absolute offset `off`. */
|
|
76
|
+
export declare function parseDirEntry(b: Uint8Array, off: number, id: number): DirEntry | null;
|
|
77
|
+
/** Read the directory entries by following the directory-sector chain. */
|
|
78
|
+
export declare function readDirectory(b: Uint8Array, header: CfbfHeader, fat: Uint32Array): DirEntry[];
|
|
79
|
+
/** True when a 128-byte record looks like a real directory entry. */
|
|
80
|
+
export declare function looksLikeEntry(b: Uint8Array, off: number): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Pointer-free salvage: scan the whole file at 128-byte alignment for records
|
|
83
|
+
* that look like directory entries. Recovers streams when the tree/chain is
|
|
84
|
+
* destroyed (the CFBF analog of the ZIP local-header scan).
|
|
85
|
+
*/
|
|
86
|
+
export declare function linearScanEntries(b: Uint8Array, header: CfbfHeader): DirEntry[];
|
|
87
|
+
/** Detect the Office document type from the recovered stream names. */
|
|
88
|
+
export declare function classifyContainer(names: string[]): "doc" | "xls" | "ppt" | "msg" | "ole";
|
|
89
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/office-legacy/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure CFBF / OLE2 (Compound File Binary Format) primitives: header parse,
|
|
3
|
+
* FAT/DIFAT/MiniFAT walk, 128-byte directory entries, sector→offset math, and
|
|
4
|
+
* a pointer-free linear directory scan for salvage. No wasm, no compression —
|
|
5
|
+
* CFBF stores stream bytes raw, so this is all integer/offset arithmetic.
|
|
6
|
+
*
|
|
7
|
+
* Backs legacy binary Office: .doc/.xls/.ppt (97–2003), plus .msg/.msi. Repair
|
|
8
|
+
* scope is the CONTAINER + stream salvage — never the document content inside a
|
|
9
|
+
* stream. Reference: [MS-CFB]. All fields little-endian.
|
|
10
|
+
*/
|
|
11
|
+
export const CFBF_MAGIC = Uint8Array.of(0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1);
|
|
12
|
+
export const MAXREGSECT = 0xfffffffa;
|
|
13
|
+
export const DIFSECT = 0xfffffffc;
|
|
14
|
+
export const FATSECT = 0xfffffffd;
|
|
15
|
+
export const ENDOFCHAIN = 0xfffffffe;
|
|
16
|
+
export const FREESECT = 0xffffffff;
|
|
17
|
+
export const NOSTREAM = 0xffffffff;
|
|
18
|
+
export const MINI_CUTOFF = 4096;
|
|
19
|
+
export const MINI_SECTOR = 64;
|
|
20
|
+
export const DIR_ENTRY = 128;
|
|
21
|
+
export const u16 = (b, o) => (b[o] | (b[o + 1] << 8)) >>> 0;
|
|
22
|
+
export const u32 = (b, o) => ((b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)) >>> 0);
|
|
23
|
+
/** 64-bit little-endian as a JS number (v3 uses only the low 32 bits). */
|
|
24
|
+
export const u64 = (b, o) => u32(b, o) + u32(b, o + 4) * 0x1_0000_0000;
|
|
25
|
+
export function hasMagicAt(b, off) {
|
|
26
|
+
for (let i = 0; i < 8; i++)
|
|
27
|
+
if (b[off + i] !== CFBF_MAGIC[i])
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
/** How many of the 8 magic bytes match at `off` (partial-magic heuristic). */
|
|
32
|
+
export function magicScore(b, off) {
|
|
33
|
+
let n = 0;
|
|
34
|
+
for (let i = 0; i < 8; i++)
|
|
35
|
+
if (b[off + i] === CFBF_MAGIC[i])
|
|
36
|
+
n++;
|
|
37
|
+
return n;
|
|
38
|
+
}
|
|
39
|
+
/** First offset of an exact CFBF magic within `window`, or -1. */
|
|
40
|
+
export function findMagic(b, window) {
|
|
41
|
+
const limit = Math.min(b.length - 8, window);
|
|
42
|
+
for (let i = 0; i <= limit; i++)
|
|
43
|
+
if (b[i] === 0xd0 && hasMagicAt(b, i))
|
|
44
|
+
return i;
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
47
|
+
/** Parse the 512-byte header. Returns null if the sector shift is implausible. */
|
|
48
|
+
export function parseHeader(b) {
|
|
49
|
+
if (b.length < 512)
|
|
50
|
+
return null;
|
|
51
|
+
const major = u16(b, 26);
|
|
52
|
+
const sectorShift = u16(b, 30);
|
|
53
|
+
// Normalize: sector shift is derivable from the major version if garbled.
|
|
54
|
+
const shift = sectorShift === 9 || sectorShift === 12 ? sectorShift : major === 4 ? 12 : 9;
|
|
55
|
+
const sectorSize = 1 << shift;
|
|
56
|
+
return {
|
|
57
|
+
major: major === 3 || major === 4 ? major : sectorSize === 4096 ? 4 : 3,
|
|
58
|
+
sectorSize,
|
|
59
|
+
miniCutoff: u32(b, 56) || MINI_CUTOFF,
|
|
60
|
+
numFatSectors: u32(b, 44),
|
|
61
|
+
firstDirSector: u32(b, 48),
|
|
62
|
+
firstMiniFatSector: u32(b, 60),
|
|
63
|
+
numMiniFatSectors: u32(b, 64),
|
|
64
|
+
firstDifatSector: u32(b, 68),
|
|
65
|
+
numDifatSectors: u32(b, 72),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** SECT number → absolute file offset. Sector 0 begins one sector past the header. */
|
|
69
|
+
export const sectOffset = (sect, sectorSize) => (sect + 1) * sectorSize;
|
|
70
|
+
/** Collect the FAT-sector numbers from the header DIFAT + any chained DIFAT sectors. */
|
|
71
|
+
export function readDifat(b, header) {
|
|
72
|
+
const { sectorSize } = header;
|
|
73
|
+
const fatSectors = [];
|
|
74
|
+
for (let i = 0; i < 109; i++) {
|
|
75
|
+
const s = u32(b, 76 + i * 4);
|
|
76
|
+
if (s <= MAXREGSECT)
|
|
77
|
+
fatSectors.push(s);
|
|
78
|
+
}
|
|
79
|
+
// chained DIFAT sectors: 127 FAT-sector numbers + a "next DIFAT" link each
|
|
80
|
+
let difat = header.firstDifatSector;
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
const perSector = sectorSize / 4;
|
|
83
|
+
while (difat <= MAXREGSECT && !seen.has(difat)) {
|
|
84
|
+
seen.add(difat);
|
|
85
|
+
const base = sectOffset(difat, sectorSize);
|
|
86
|
+
if (base + sectorSize > b.length)
|
|
87
|
+
break;
|
|
88
|
+
for (let i = 0; i < perSector - 1; i++) {
|
|
89
|
+
const s = u32(b, base + i * 4);
|
|
90
|
+
if (s <= MAXREGSECT)
|
|
91
|
+
fatSectors.push(s);
|
|
92
|
+
}
|
|
93
|
+
difat = u32(b, base + (perSector - 1) * 4);
|
|
94
|
+
}
|
|
95
|
+
return fatSectors;
|
|
96
|
+
}
|
|
97
|
+
/** Build the FAT array from its sectors. Out-of-range sectors read as FREESECT. */
|
|
98
|
+
export function buildFat(b, header, fatSectors) {
|
|
99
|
+
const { sectorSize } = header;
|
|
100
|
+
const perSector = sectorSize / 4;
|
|
101
|
+
const fat = new Uint32Array(fatSectors.length * perSector).fill(FREESECT);
|
|
102
|
+
let w = 0;
|
|
103
|
+
for (const s of fatSectors) {
|
|
104
|
+
const base = sectOffset(s, sectorSize);
|
|
105
|
+
for (let i = 0; i < perSector; i++) {
|
|
106
|
+
fat[w++] = base + i * 4 + 4 <= b.length ? u32(b, base + i * 4) : FREESECT;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return fat;
|
|
110
|
+
}
|
|
111
|
+
/** Follow a FAT/MiniFAT chain from `start`, with cycle + bound guards. */
|
|
112
|
+
export function readChain(fat, start, maxLen) {
|
|
113
|
+
const out = [];
|
|
114
|
+
const seen = new Set();
|
|
115
|
+
let s = start;
|
|
116
|
+
while (s <= MAXREGSECT && s < fat.length && !seen.has(s) && out.length <= maxLen) {
|
|
117
|
+
out.push(s);
|
|
118
|
+
seen.add(s);
|
|
119
|
+
s = fat[s];
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/** Read a normal (FAT-chained) stream of `size` bytes from `start`. */
|
|
124
|
+
export function readBigStream(b, header, fat, start, size) {
|
|
125
|
+
const { sectorSize } = header;
|
|
126
|
+
const need = Math.ceil(size / sectorSize);
|
|
127
|
+
const chain = readChain(fat, start, need + 4);
|
|
128
|
+
const out = new Uint8Array(size);
|
|
129
|
+
let written = 0;
|
|
130
|
+
let brokenChain = chain.length < need;
|
|
131
|
+
for (const s of chain) {
|
|
132
|
+
if (written >= size)
|
|
133
|
+
break;
|
|
134
|
+
const base = sectOffset(s, sectorSize);
|
|
135
|
+
const take = Math.min(sectorSize, size - written);
|
|
136
|
+
if (base + take > b.length) {
|
|
137
|
+
brokenChain = true;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
out.set(b.subarray(base, base + take), written);
|
|
141
|
+
written += take;
|
|
142
|
+
}
|
|
143
|
+
return { bytes: out.subarray(0, written), complete: written >= size, brokenChain };
|
|
144
|
+
}
|
|
145
|
+
/** Build the MiniFAT array by reading its sector chain from the FAT. */
|
|
146
|
+
export function buildMiniFat(b, header, fat) {
|
|
147
|
+
const chain = readChain(fat, header.firstMiniFatSector, 1 << 20);
|
|
148
|
+
const perSector = header.sectorSize / 4;
|
|
149
|
+
const mf = new Uint32Array(chain.length * perSector).fill(FREESECT);
|
|
150
|
+
let w = 0;
|
|
151
|
+
for (const s of chain) {
|
|
152
|
+
const base = sectOffset(s, header.sectorSize);
|
|
153
|
+
for (let i = 0; i < perSector; i++) {
|
|
154
|
+
mf[w++] = base + i * 4 + 4 <= b.length ? u32(b, base + i * 4) : FREESECT;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return mf;
|
|
158
|
+
}
|
|
159
|
+
/** Read a small (MiniFAT-chained) stream from the mini stream. */
|
|
160
|
+
export function readMiniStream(miniStream, miniFat, start, size) {
|
|
161
|
+
const need = Math.ceil(size / MINI_SECTOR);
|
|
162
|
+
const chain = readChain(miniFat, start, need + 4);
|
|
163
|
+
const out = new Uint8Array(size);
|
|
164
|
+
let written = 0;
|
|
165
|
+
let brokenChain = chain.length < need;
|
|
166
|
+
for (const s of chain) {
|
|
167
|
+
if (written >= size)
|
|
168
|
+
break;
|
|
169
|
+
const base = s * MINI_SECTOR;
|
|
170
|
+
const take = Math.min(MINI_SECTOR, size - written);
|
|
171
|
+
if (base + take > miniStream.length) {
|
|
172
|
+
brokenChain = true;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
out.set(miniStream.subarray(base, base + take), written);
|
|
176
|
+
written += take;
|
|
177
|
+
}
|
|
178
|
+
return { bytes: out.subarray(0, written), complete: written >= size, brokenChain };
|
|
179
|
+
}
|
|
180
|
+
function decodeName(b, off, nameLenBytes) {
|
|
181
|
+
if (nameLenBytes < 2 || nameLenBytes > 64)
|
|
182
|
+
return "";
|
|
183
|
+
let s = "";
|
|
184
|
+
for (let i = 0; i + 2 <= nameLenBytes; i += 2) {
|
|
185
|
+
const c = u16(b, off + i);
|
|
186
|
+
if (c === 0)
|
|
187
|
+
break;
|
|
188
|
+
s += String.fromCharCode(c);
|
|
189
|
+
}
|
|
190
|
+
return s;
|
|
191
|
+
}
|
|
192
|
+
/** Parse one 128-byte directory entry at absolute offset `off`. */
|
|
193
|
+
export function parseDirEntry(b, off, id) {
|
|
194
|
+
if (off + DIR_ENTRY > b.length)
|
|
195
|
+
return null;
|
|
196
|
+
const type = b[off + 66];
|
|
197
|
+
const nameLen = u16(b, off + 64);
|
|
198
|
+
return {
|
|
199
|
+
id,
|
|
200
|
+
name: decodeName(b, off, nameLen),
|
|
201
|
+
type,
|
|
202
|
+
color: b[off + 67],
|
|
203
|
+
left: u32(b, off + 68),
|
|
204
|
+
right: u32(b, off + 72),
|
|
205
|
+
child: u32(b, off + 76),
|
|
206
|
+
startSector: u32(b, off + 116),
|
|
207
|
+
size: u64(b, off + 120),
|
|
208
|
+
recordOffset: off,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** Read the directory entries by following the directory-sector chain. */
|
|
212
|
+
export function readDirectory(b, header, fat) {
|
|
213
|
+
const { sectorSize } = header;
|
|
214
|
+
const perSector = sectorSize / DIR_ENTRY;
|
|
215
|
+
const chain = readChain(fat, header.firstDirSector, 1 << 20);
|
|
216
|
+
const entries = [];
|
|
217
|
+
let id = 0;
|
|
218
|
+
for (const s of chain) {
|
|
219
|
+
const base = sectOffset(s, sectorSize);
|
|
220
|
+
for (let i = 0; i < perSector; i++) {
|
|
221
|
+
const e = parseDirEntry(b, base + i * DIR_ENTRY, id++);
|
|
222
|
+
if (e)
|
|
223
|
+
entries.push(e);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return entries;
|
|
227
|
+
}
|
|
228
|
+
/** True when a 128-byte record looks like a real directory entry. */
|
|
229
|
+
export function looksLikeEntry(b, off) {
|
|
230
|
+
const type = b[off + 66];
|
|
231
|
+
if (type !== 1 && type !== 2 && type !== 5)
|
|
232
|
+
return false;
|
|
233
|
+
const nameLen = u16(b, off + 64);
|
|
234
|
+
if (nameLen < 2 || nameLen > 64 || nameLen % 2 !== 0)
|
|
235
|
+
return false;
|
|
236
|
+
// name must be NUL-terminated UTF-16 within nameLen and start with a printable-ish unit
|
|
237
|
+
const first = u16(b, off);
|
|
238
|
+
if (first === 0 || first > 0xffff)
|
|
239
|
+
return false;
|
|
240
|
+
if (u16(b, off + nameLen - 2) !== 0)
|
|
241
|
+
return false;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Pointer-free salvage: scan the whole file at 128-byte alignment for records
|
|
246
|
+
* that look like directory entries. Recovers streams when the tree/chain is
|
|
247
|
+
* destroyed (the CFBF analog of the ZIP local-header scan).
|
|
248
|
+
*/
|
|
249
|
+
export function linearScanEntries(b, header) {
|
|
250
|
+
const out = [];
|
|
251
|
+
const start = header.sectorSize; // skip the header sector
|
|
252
|
+
let id = 0;
|
|
253
|
+
const seen = new Set();
|
|
254
|
+
for (let off = start; off + DIR_ENTRY <= b.length; off += DIR_ENTRY) {
|
|
255
|
+
if (!looksLikeEntry(b, off))
|
|
256
|
+
continue;
|
|
257
|
+
const e = parseDirEntry(b, off, id);
|
|
258
|
+
if (!e || !e.name)
|
|
259
|
+
continue;
|
|
260
|
+
const key = `${e.name}:${e.startSector}:${e.size}`;
|
|
261
|
+
if (seen.has(key))
|
|
262
|
+
continue;
|
|
263
|
+
seen.add(key);
|
|
264
|
+
e.id = id++;
|
|
265
|
+
out.push(e);
|
|
266
|
+
}
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
/** Detect the Office document type from the recovered stream names. */
|
|
270
|
+
export function classifyContainer(names) {
|
|
271
|
+
if (names.includes("WordDocument"))
|
|
272
|
+
return "doc";
|
|
273
|
+
if (names.includes("Workbook") || names.includes("Book"))
|
|
274
|
+
return "xls";
|
|
275
|
+
if (names.includes("PowerPoint Document"))
|
|
276
|
+
return "ppt";
|
|
277
|
+
if (names.some((n) => n === "__properties_version1.0" || /__recip_version/.test(n) || n === "__nameid_version1.0"))
|
|
278
|
+
return "msg";
|
|
279
|
+
return "ole";
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/office-legacy/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const repairOffice: (src: import("../byte-source.js").ByteSource) => Promise<import("./repair.js").OfficeRepairResult>;
|
|
2
|
+
export { repairOfficeBytes } from "./repair.js";
|
|
3
|
+
export type { OfficeRepairOutcome, OfficeRepairStrategy, OfficeRepairReport, OfficeRepairResult, RecoveredStream, } from "./repair.js";
|
|
4
|
+
export { diagnoseOffice, diagnoseOfficeBytes } from "./diagnose.js";
|
|
5
|
+
export type { OfficeDiagnosis, OfficeDamageClass } from "./diagnose.js";
|
|
6
|
+
export { writeCfbf } from "./rebuild.js";
|
|
7
|
+
export { CFBF_MAGIC, parseHeader, readDifat, buildFat, readDirectory, linearScanEntries, classifyContainer, } from "./format.js";
|
|
8
|
+
export type { CfbfHeader, DirEntry } from "./format.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/office-legacy/index.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,YAAY,oGAAsB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy Office (CFBF / OLE2) engine subpath export
|
|
3
|
+
* (`@intactfile/engines/office-legacy`). Pure TypeScript, no wasm — CFBF stores
|
|
4
|
+
* streams raw, so parsing and the canonical rebuild are plain byte arithmetic.
|
|
5
|
+
*
|
|
6
|
+
* Scope: container + stream salvage for .doc/.xls/.ppt (97–2003) and .msg. It
|
|
7
|
+
* recovers the streams and rebuilds a valid compound file; it does NOT repair
|
|
8
|
+
* the document content (BIFF/FIB) inside a stream. diagnoseOffice is pure and
|
|
9
|
+
* promoted to the root barrel; repairOffice stays here and is gated.
|
|
10
|
+
*/
|
|
11
|
+
import { gate } from "../license.js";
|
|
12
|
+
import { repairOffice as _repairOffice } from "./repair.js";
|
|
13
|
+
export const repairOffice = gate(_repairOffice);
|
|
14
|
+
export { repairOfficeBytes } from "./repair.js";
|
|
15
|
+
export { diagnoseOffice, diagnoseOfficeBytes } from "./diagnose.js";
|
|
16
|
+
export { writeCfbf } from "./rebuild.js";
|
|
17
|
+
export { CFBF_MAGIC, parseHeader, readDifat, buildFat, readDirectory, linearScanEntries, classifyContainer, } from "./format.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/office-legacy/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,YAAY,IAAI,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAShD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface OutStream {
|
|
2
|
+
name: string;
|
|
3
|
+
bytes: Uint8Array;
|
|
4
|
+
}
|
|
5
|
+
/** True if a rebuild would need more than the 109 inline DIFAT slots. */
|
|
6
|
+
export declare function rebuildExceedsLimit(streams: OutStream[]): boolean;
|
|
7
|
+
export declare function writeCfbf(streams: OutStream[]): Uint8Array;
|
|
8
|
+
//# sourceMappingURL=rebuild.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rebuild.d.ts","sourceRoot":"","sources":["../../src/office-legacy/rebuild.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;CACnB;AAeD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAejE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAmK1D"}
|