fix: accept per-cabinet reserve metadata
This commit is contained in:
@@ -48,34 +48,39 @@ function mutateU16(
|
||||
return { ...part, bytes };
|
||||
}
|
||||
|
||||
function withEmptyReserveLayout(part: CabSetPart): CabSetPart {
|
||||
function withDistinctFolderReserve(part: CabSetPart): CabSetPart {
|
||||
const original = part.bytes;
|
||||
const insertAt = 36;
|
||||
const addedBytes = 4;
|
||||
const originalView = new DataView(
|
||||
original.buffer,
|
||||
original.byteOffset,
|
||||
original.byteLength
|
||||
);
|
||||
const reserveHeaderOffset = 36;
|
||||
const originalFileOffset = originalView.getUint32(16, true);
|
||||
// The pinned fixture part has one zero-reserve CFFOLDER immediately before
|
||||
// its CFFILE table. Add the optional reserve-size header plus one private
|
||||
// folder-reserve byte without changing any compressed data.
|
||||
const originalFolderOffset = originalFileOffset - 8;
|
||||
const addedBytes = 5;
|
||||
const bytes = new Uint8Array(original.length + addedBytes);
|
||||
bytes.set(original.subarray(0, insertAt));
|
||||
bytes.set(original.subarray(insertAt), insertAt + addedBytes);
|
||||
bytes.set(original.subarray(0, reserveHeaderOffset));
|
||||
bytes.set(
|
||||
original.subarray(reserveHeaderOffset, originalFileOffset),
|
||||
reserveHeaderOffset + 4
|
||||
);
|
||||
bytes[originalFileOffset + 4] = 0xa5;
|
||||
bytes.set(original.subarray(originalFileOffset), originalFileOffset + 5);
|
||||
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint32(8, original.length + addedBytes, true);
|
||||
view.setUint32(16, originalFileOffset + addedBytes, true);
|
||||
view.setUint16(30, originalView.getUint16(30, true) | 0x0004, true);
|
||||
view.setUint16(reserveHeaderOffset, 0, true);
|
||||
view.setUint8(reserveHeaderOffset + 2, 1);
|
||||
view.setUint8(reserveHeaderOffset + 3, 0);
|
||||
view.setUint32(
|
||||
16,
|
||||
new DataView(original.buffer, original.byteOffset).getUint32(16, true) +
|
||||
addedBytes,
|
||||
true
|
||||
);
|
||||
view.setUint16(
|
||||
30,
|
||||
new DataView(original.buffer, original.byteOffset).getUint16(30, true) |
|
||||
0x0004,
|
||||
true
|
||||
);
|
||||
// cbCFHeader, cbCFFolder, and cbCFData are all zero.
|
||||
bytes.fill(0, insertAt, insertAt + addedBytes);
|
||||
// This fixture's only CFFOLDER begins at 0x8e before insertion.
|
||||
view.setUint32(
|
||||
0x8e + addedBytes,
|
||||
new DataView(original.buffer, original.byteOffset).getUint32(0x8e, true) +
|
||||
addedBytes,
|
||||
originalFolderOffset + 4,
|
||||
originalView.getUint32(originalFolderOffset, true) + addedBytes,
|
||||
true
|
||||
);
|
||||
return { ...part, bytes };
|
||||
@@ -167,12 +172,6 @@ describe('multi-cabinet sets', () => {
|
||||
diagnostic: { code: 'cab-set-file-continuation-mismatch' },
|
||||
});
|
||||
|
||||
const reserve = fixtureSet();
|
||||
reserve[2] = withEmptyReserveLayout(reserve[2]!);
|
||||
await expect(extractCabinetSet(reserve)).rejects.toMatchObject({
|
||||
diagnostic: { code: 'cab-set-reserve-layout-mismatch' },
|
||||
});
|
||||
|
||||
const incompleteBlock = fixtureSet();
|
||||
// Part 5's last CFDATA cbUncomp is at 0xb5.
|
||||
incompleteBlock[4] = mutateU16(incompleteBlock[4]!, 0xb5, 0);
|
||||
@@ -181,6 +180,23 @@ describe('multi-cabinet sets', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts independent reserve layouts and folder metadata per part', async () => {
|
||||
const parts = fixtureSet();
|
||||
parts[2] = withDistinctFolderReserve(parts[2]!);
|
||||
|
||||
const result = await extractCabinetSet(parts);
|
||||
|
||||
expect(
|
||||
result.extractedEntries.map((entry) =>
|
||||
new TextDecoder().decode(entry.bytes)
|
||||
)
|
||||
).toEqual([
|
||||
expectedPart(1) + expectedPart(2),
|
||||
expectedPart(3),
|
||||
expectedPart(4) + expectedPart(5),
|
||||
]);
|
||||
});
|
||||
|
||||
it('opens the complete set through the package API', async () => {
|
||||
const parts = fixtureSet();
|
||||
const result = await openOneNotePackageSet(parts, {
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
FOLDER_CONTINUED_FROM_PREVIOUS,
|
||||
FOLDER_CONTINUED_PREVIOUS_AND_NEXT,
|
||||
FOLDER_CONTINUED_TO_NEXT,
|
||||
FLAG_RESERVE_PRESENT,
|
||||
parseCabinetInternal,
|
||||
verifyDataBlockChecksumInternal,
|
||||
type CabDataBlockInternal,
|
||||
@@ -87,13 +86,6 @@ function checkedAdd(
|
||||
return sum;
|
||||
}
|
||||
|
||||
function bytesEqual(left: Uint8Array, right: Uint8Array): boolean {
|
||||
return (
|
||||
left.length === right.length &&
|
||||
left.every((value, index) => value === right[index])
|
||||
);
|
||||
}
|
||||
|
||||
function cabinetNameKey(value: string, structure: string): string {
|
||||
const normalized = value.normalize('NFC');
|
||||
if (
|
||||
@@ -186,13 +178,6 @@ function validateBoundary(left: NamedCabinet, right: NamedCabinet): boolean {
|
||||
{ structure: 'CFFOLDER.typeCompress' }
|
||||
);
|
||||
}
|
||||
if (!bytesEqual(leftFolder.reserve, rightFolder.reserve)) {
|
||||
fail(
|
||||
'cab-set-folder-reserve-mismatch',
|
||||
'Continued folder segments have different reserved metadata.',
|
||||
{ structure: 'CFFOLDER.abReserve' }
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -258,7 +243,6 @@ function validateNamedCabinets(
|
||||
left.parsed.result.cabinetSetIndex - right.parsed.result.cabinetSetIndex
|
||||
);
|
||||
const setId = cabinets[0]!.parsed.result.cabinetSetId;
|
||||
const reserveTemplate = cabinets[0]!.parsed;
|
||||
for (let index = 0; index < cabinets.length; index += 1) {
|
||||
const cabinet = cabinets[index]!;
|
||||
const parsed = cabinet.parsed;
|
||||
@@ -276,19 +260,6 @@ function validateNamedCabinets(
|
||||
{ structure: 'CFHEADER.iCabinet' }
|
||||
);
|
||||
}
|
||||
if (
|
||||
(parsed.flags & FLAG_RESERVE_PRESENT) !==
|
||||
(reserveTemplate.flags & FLAG_RESERVE_PRESENT) ||
|
||||
parsed.headerReserveSize !== reserveTemplate.headerReserveSize ||
|
||||
parsed.folderReserveSize !== reserveTemplate.folderReserveSize ||
|
||||
parsed.dataReserveSize !== reserveTemplate.dataReserveSize
|
||||
) {
|
||||
fail(
|
||||
'cab-set-reserve-layout-mismatch',
|
||||
'Cabinet parts use inconsistent reserve-area layouts.',
|
||||
{ structure: 'CFHEADER reserve sizes' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0; index < cabinets.length; index += 1) {
|
||||
|
||||
Reference in New Issue
Block a user