1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = readVmdkGrainTable;
|
7 | exports.readCapacityAndGrainTable = readCapacityAndGrainTable;
|
8 | var _util = require("./util");
|
9 | const SECTOR_SIZE = 512;
|
10 | const HEADER_SIZE = 512;
|
11 | const FOOTER_POSITION = -1024;
|
12 | const DISK_CAPACITY_OFFSET = 12;
|
13 | const GRAIN_SIZE_OFFSET = 20;
|
14 | const NUM_GTE_PER_GT_OFFSET = 44;
|
15 | const GRAIN_ADDRESS_OFFSET = 56;
|
16 | const MANTISSA_BITS_IN_DOUBLE = 53;
|
17 | const getLongLong = (buffer, offset, name) => {
|
18 | if (buffer.byteLength < offset + 8) {
|
19 | throw new Error(`buffer ${name} is too short, expecting ${offset + 8} minimum, got ${buffer.byteLength}`);
|
20 | }
|
21 | const dataView = new DataView(buffer);
|
22 | const highBits = dataView.getUint32(offset + 4, true);
|
23 | if (highBits >= Math.pow(2, MANTISSA_BITS_IN_DOUBLE - 32)) {
|
24 | throw new Error('Unsupported file, high order bits are too high in field ' + name);
|
25 | }
|
26 | const res = dataView.getUint32(offset, true);
|
27 | return res + highBits * Math.pow(2, 32);
|
28 | };
|
29 | async function readVmdkGrainTable(fileAccessor) {
|
30 | return (await readCapacityAndGrainTable(fileAccessor)).tablePromise;
|
31 | }
|
32 | async function grabTables(grainDirectoryEntries, grainDir, grainTablePhysicalSize, fileAccessor) {
|
33 | const cachedGrainTables = [];
|
34 | for (let i = 0; i < grainDirectoryEntries; i++) {
|
35 | const grainTableAddr = grainDir[i] * SECTOR_SIZE;
|
36 | if (grainTableAddr !== 0) {
|
37 | cachedGrainTables[i] = new Uint32Array(await fileAccessor(grainTableAddr, grainTableAddr + grainTablePhysicalSize));
|
38 | }
|
39 | }
|
40 | return cachedGrainTables;
|
41 | }
|
42 | async function readCapacityAndGrainTable(fileAccessor) {
|
43 | let headerBuffer = await fileAccessor(0, HEADER_SIZE);
|
44 | let grainAddrBuffer = headerBuffer.slice(GRAIN_ADDRESS_OFFSET, GRAIN_ADDRESS_OFFSET + 8);
|
45 | if (new Int8Array(grainAddrBuffer).every(val => val === -1)) {
|
46 | headerBuffer = await fileAccessor(FOOTER_POSITION, FOOTER_POSITION + HEADER_SIZE);
|
47 | grainAddrBuffer = headerBuffer.slice(GRAIN_ADDRESS_OFFSET, GRAIN_ADDRESS_OFFSET + 8);
|
48 | }
|
49 | const grainDirPosBytes = getLongLong(grainAddrBuffer, 0, 'grain directory address') * SECTOR_SIZE;
|
50 | const capacity = getLongLong(headerBuffer, DISK_CAPACITY_OFFSET, 'capacity') * SECTOR_SIZE;
|
51 | async function readTable() {
|
52 | const grainSizeByte = getLongLong(headerBuffer, GRAIN_SIZE_OFFSET, 'grain size') * SECTOR_SIZE;
|
53 | const grainCount = Math.ceil(capacity / grainSizeByte);
|
54 | const numGTEsPerGT = new DataView(headerBuffer).getUint32(NUM_GTE_PER_GT_OFFSET, true);
|
55 | const grainTablePhysicalSize = numGTEsPerGT * 4;
|
56 | const grainDirectoryEntries = Math.ceil(grainCount / numGTEsPerGT);
|
57 | const grainDirectoryPhysicalSize = grainDirectoryEntries * 4;
|
58 | const grainDir = new Uint32Array(await fileAccessor(grainDirPosBytes, grainDirPosBytes + grainDirectoryPhysicalSize));
|
59 | const cachedGrainTables = await grabTables(grainDirectoryEntries, grainDir, grainTablePhysicalSize, fileAccessor);
|
60 | const extractedGrainTable = [];
|
61 | for (let i = 0; i < grainCount; i++) {
|
62 | const directoryEntry = Math.floor(i / numGTEsPerGT);
|
63 | const grainTable = cachedGrainTables[directoryEntry];
|
64 | if (grainTable !== undefined) {
|
65 | const grainAddr = grainTable[i % numGTEsPerGT];
|
66 | if (grainAddr !== 0) {
|
67 | extractedGrainTable.push([i, grainAddr]);
|
68 | }
|
69 | }
|
70 | }
|
71 | extractedGrainTable.sort(([_i1, grainAddress1], [_i2, grainAddress2]) => grainAddress1 - grainAddress2);
|
72 | const byteLength = 4 * extractedGrainTable.length;
|
73 | const grainLogicalAddressList = new DataView(new ArrayBuffer(byteLength));
|
74 | const grainFileOffsetList = new DataView(new ArrayBuffer(byteLength));
|
75 | extractedGrainTable.forEach(([index, grainAddress], i) => {
|
76 | grainLogicalAddressList.setUint32(i * 4, index, true);
|
77 | grainFileOffsetList.setUint32(i * 4, grainAddress, true);
|
78 | });
|
79 | return {
|
80 | grainLogicalAddressList: grainLogicalAddressList.buffer,
|
81 | grainFileOffsetList: grainFileOffsetList.buffer
|
82 | };
|
83 | }
|
84 | return {
|
85 | tablePromise: (0, _util.suppressUnhandledRejection)(readTable()),
|
86 | capacityBytes: capacity
|
87 | };
|
88 | }
|
89 |
|
\ | No newline at end of file |