UNPKG

compound-binary-file-js

Version:

This is an implementation of [Compound Binary File v.3](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b) \ Allows reading existing files, creation of the/write operation

158 lines (141 loc) 9.88 kB
import {Header} from "../src/Header"; import {ENDOFCHAIN_MARK, initializedWidth} from "../src/utils"; import { expect } from "chai"; import {SimpleDataview} from "../src/dataview/SimpleDataview"; import Long from "long"; import "../src/Long"; const DUMMY_HEADER = initializedWidth(Header.HEADER_LENGTH, 0); DUMMY_HEADER.splice(Header.FLAG_POSITION.SIGNATURE, 8, ...Header.HEADER_SIGNATURE); DUMMY_HEADER.splice(Header.FLAG_POSITION.MINOR_VERSION, 2, ...Header.MINOR_VERSION_3); DUMMY_HEADER.splice(Header.FLAG_POSITION.MAJOR_VERSION, 2, ...Header.MAJOR_VERSION_3); DUMMY_HEADER.splice(Header.FLAG_POSITION.BYTE_ORDER, 2, ...Header.BYTE_ORDER_LITTLE_ENDIAN); DUMMY_HEADER.splice(Header.FLAG_POSITION.SECTOR_SHIFT, 2, ...Header.SECTOR_SHIFT_VERSION_3); DUMMY_HEADER.splice(Header.FLAG_POSITION.MINI_SECTOR_SHIFT, 2, ...Header.MINI_SECTOR_SHIFT_VERSION_3); DUMMY_HEADER.splice(Header.FLAG_POSITION.MINI_STREAM_CUTOFF_SIZE_POSITION, 4, ...Header.MINI_STREAM_CUTOFF_SIZE); DUMMY_HEADER.splice(Header.FLAG_POSITION.FIRST_DIFAT_SECTOR, 4, ...ENDOFCHAIN_MARK); DUMMY_HEADER.splice(Header.FLAG_POSITION.DIFAT_ENTRIES_FIRST_POSITION, 436, ...initializedWidth(436, 0xff)); export function dummyHeader(): number[] { const result = []; result.push(...DUMMY_HEADER); return result; } describe('header test', () => { let data: number[]; beforeEach(() => { data = dummyHeader() }); it('header block size should be exactly 512 bytes long', () => { expect(() => new Header(new SimpleDataview(new Array(513)))).to.throw(); }); it('only CFB version 3 is supported', () => { data.splice(Header.FLAG_POSITION.MAJOR_VERSION, 2, ...[0x04, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(Header.FLAG_POSITION.MAJOR_VERSION, 2, ...Header.MAJOR_VERSION_3); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('only Little Endian byte order is supported', () => { data.splice(Header.FLAG_POSITION.BYTE_ORDER,2, ...[0xfd, 0xff]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(Header.FLAG_POSITION.BYTE_ORDER,2, ...Header.BYTE_ORDER_LITTLE_ENDIAN); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('only 512-byte sector shift is supported', () => { data.splice(Header.FLAG_POSITION.SECTOR_SHIFT,2, ...[0x90, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(Header.FLAG_POSITION.SECTOR_SHIFT,2, ...Header.SECTOR_SHIFT_VERSION_3); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('only 64-byte mini-sector shift is supported', () => { data.splice(Header.FLAG_POSITION.MINI_SECTOR_SHIFT,2, ...[0x07, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(Header.FLAG_POSITION.MINI_SECTOR_SHIFT,2, ...Header.MINI_SECTOR_SHIFT_VERSION_3); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('reserved bytes in Header should be initialized to 0\'s', () => { data.splice(34,6, ...[0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(34,6, ...[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('bytes in Header describing Number of Directory Sectors in CFB file should be initialized to 0\'s', () => { data.splice(40,4, ...[0xdd, 0xdd, 0xdd, 0xdd]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(40,4, ...[0x00, 0x00, 0x00, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('only 4096-bytes mini-stream cutoff size is supported', () => { data.splice(Header.FLAG_POSITION.MINI_STREAM_CUTOFF_SIZE_POSITION, 4, ...[0x00, 0x20, 0x00, 0x00]); expect(() => new Header(new SimpleDataview(data))).to.throw(); data.splice(Header.FLAG_POSITION.MINI_STREAM_CUTOFF_SIZE_POSITION, 4, ...Header.MINI_STREAM_CUTOFF_SIZE); expect(() => new Header(new SimpleDataview(data))).to.not.throw(); }); it('check proper handling of read/write operations for certain service information', () => { data.splice(Header.FLAG_POSITION.NUMBER_OF_FAT_SECTORS, 4, ...[0x01, 0x00, 0x00, 0x00]); data.splice(Header.FLAG_POSITION.FIRST_DIRECTORY_SECTOR, 4, ...[0x02, 0x00, 0x00, 0x00]); data.splice(Header.FLAG_POSITION.FIRST_MINIFAT_SECTOR, 4, ...[0x03, 0x00, 0x00, 0x00]); data.splice(Header.FLAG_POSITION.NUMBER_OF_MINIFAT_SECTORS, 4, ...[0x04, 0x00, 0x00, 0x00]); data.splice(Header.FLAG_POSITION.FIRST_DIFAT_SECTOR, 4, ...[0x05, 0x00, 0x00, 0x00]); data.splice(Header.FLAG_POSITION.NUMBER_OF_DIFAT_SECTORS, 4, ...[0x06, 0x00, 0x00, 0x00]); const header = new Header(new SimpleDataview(data)); expect(header.getNumberOfFatSectors()).eq(1); expect(header.getFirstDirectorySectorLocation()).eq(2); expect(header.getFirstMinifatSectorLocation()).eq(3); expect(header.getNumberOfMiniFatSectors()).eq(4); expect(header.getFirstDifatSectorLocation()).eq(5); expect(header.getNumberOfDifatSectors()).eq(6); header.setNumberOfFatSectors(6); header.setFirstDirectorySectorLocation(5); header.setFirstMinifatSectorLocation(4); header.setNumberOfMiniFatSectors(3); header.setFirstDifatSectorLocation(2); header.setNumberOfDifatSectors(1); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.NUMBER_OF_FAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_FAT_SECTORS + 4)).toNumber()).eq(6); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.FIRST_DIRECTORY_SECTOR, Header.FLAG_POSITION.FIRST_DIRECTORY_SECTOR + 4)).toNumber()).eq(5); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.FIRST_MINIFAT_SECTOR, Header.FLAG_POSITION.FIRST_MINIFAT_SECTOR + 4)).toNumber()).eq(4); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.NUMBER_OF_MINIFAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_MINIFAT_SECTORS + 4)).toNumber()).eq(3); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.FIRST_DIFAT_SECTOR, Header.FLAG_POSITION.FIRST_DIFAT_SECTOR + 4)).toNumber()).eq(2); expect(Long.fromBytesLE(data.slice(Header.FLAG_POSITION.NUMBER_OF_DIFAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_DIFAT_SECTORS + 4)).toNumber()).eq(1); // sector shifts expect(header.getSectorShift()).eq(Header.SECTOR_SHIFT_VERSION_3_INT); expect(header.getMiniSectorShift()).eq(Header.MINI_SECTOR_SHIFT_VERSION_3_INT); expect(header.getMiniStreamCutoffSize()).eq(Header.MINI_STREAM_CUTOFF_SIZE_INT); }); it('get information about DIFAT entries in Header', () => { data.splice(76, 4, ...Long.fromValue(0).to4BytesLE()); data.splice(80, 4, ...Long.fromValue(1).to4BytesLE()); data.splice(84, 4, ...Long.fromValue(2).to4BytesLE()); const difatEntries = new Header(new SimpleDataview(data)).getDifatEntries(); expect(difatEntries).to.deep.eq([0,1,2]); }); it('register fat sector', () => { const header = new Header(new SimpleDataview(data)); header.registerFatSector(100); expect(header.getDifatEntries().length).eq(1); expect(header.getDifatEntries()[0]).eq(100); expect( Long.fromBytesLE(data.slice(Header.FLAG_POSITION.DIFAT_ENTRIES_FIRST_POSITION, Header.FLAG_POSITION.DIFAT_ENTRIES_FIRST_POSITION + 4)).toNumber() ).eq(100); }); it('register fat sector out of acceptable range', () => { for (let i = Header.FLAG_POSITION.DIFAT_ENTRIES_FIRST_POSITION; i < Header.HEADER_LENGTH; i++) { data.splice(i, 4, ...Long.fromValue(i).toBytesLE()); } expect(() => new Header(new SimpleDataview(data)).registerFatSector(1)).to.throw(); }); it('creation of a new Header for empty CFB file', () => { const dataView = new SimpleDataview(initializedWidth(512, 0)); // should not throw and error // A side-effect function Header.empty(dataView); // dataView should contain proper information to be read by Header at this point const header = new Header(dataView); expect(dataView.subView(Header.FLAG_POSITION.SIGNATURE, Header.FLAG_POSITION.SIGNATURE + 8).getData()).to.deep.eq(Header.HEADER_SIGNATURE); expect(dataView.subView(Header.FLAG_POSITION.CLSID, Header.FLAG_POSITION.CLSID + 16).getData()).to.deep.eq(initializedWidth(16, 0)); expect(dataView.subView(Header.FLAG_POSITION.FIRST_DIFAT_SECTOR, Header.FLAG_POSITION.FIRST_DIFAT_SECTOR + 4).getData()).to.deep.eq(ENDOFCHAIN_MARK); expect(dataView.subView(Header.FLAG_POSITION.NUMBER_OF_FAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_FAT_SECTORS + 4).getData()).to.deep.eq([0,0,0,0]); expect(dataView.subView(Header.FLAG_POSITION.FIRST_MINIFAT_SECTOR, Header.FLAG_POSITION.FIRST_MINIFAT_SECTOR + 4).getData()).to.deep.eq(ENDOFCHAIN_MARK); expect(dataView.subView(Header.FLAG_POSITION.NUMBER_OF_MINIFAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_MINIFAT_SECTORS + 4).getData()).to.deep.eq([0,0,0,0]); expect(dataView.subView(Header.FLAG_POSITION.NUMBER_OF_DIFAT_SECTORS, Header.FLAG_POSITION.NUMBER_OF_DIFAT_SECTORS + 4).getData()).to.deep.eq([0,0,0,0]); expect(dataView.subView(Header.FLAG_POSITION.FIRST_DIRECTORY_SECTOR, Header.FLAG_POSITION.FIRST_DIRECTORY_SECTOR + 4).getData()).to.deep.eq(ENDOFCHAIN_MARK); }); });