1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.compressionNone = exports.compressionDeflate = exports.SECTOR_SIZE = exports.MARKER_GT = exports.MARKER_GD = exports.MARKER_FOOTER = exports.MARKER_EOS = void 0;
|
7 | exports.createStreamOptimizedHeader = createStreamOptimizedHeader;
|
8 | exports.parseFlags = parseFlags;
|
9 | exports.parseU64b = parseU64b;
|
10 | exports.unpackHeader = unpackHeader;
|
11 | const SECTOR_SIZE = exports.SECTOR_SIZE = 512;
|
12 | const compressionDeflate = exports.compressionDeflate = 'COMPRESSION_DEFLATE';
|
13 | const compressionNone = exports.compressionNone = 'COMPRESSION_NONE';
|
14 | const MARKER_EOS = exports.MARKER_EOS = 0;
|
15 | const MARKER_GT = exports.MARKER_GT = 1;
|
16 | const MARKER_GD = exports.MARKER_GD = 2;
|
17 | const MARKER_FOOTER = exports.MARKER_FOOTER = 3;
|
18 | const compressionMap = [compressionNone, compressionDeflate];
|
19 | function parseFlags(flagBuffer) {
|
20 | const number = flagBuffer.readUInt32LE(0);
|
21 | return {
|
22 | newLineTest: !!(number & 1 << 0),
|
23 | useSecondaryGrain: !!(number & 1 << 1),
|
24 | useZeroedGrainTable: !!(number & 1 << 2),
|
25 | compressedGrains: !!(number & 1 << 16),
|
26 | hasMarkers: !!(number & 1 << 17)
|
27 | };
|
28 | }
|
29 | function parseS64b(buffer, offset, valueName) {
|
30 | const extraBits = buffer.readIntLE(offset + 6, 2);
|
31 | const value = buffer.readIntLE(offset, 6);
|
32 | const hadValueInHighBytes = !(extraBits === 0 || extraBits === -1);
|
33 | const readWrongSign = Math.sign(value) * Math.sign(extraBits) < 0;
|
34 | if (hadValueInHighBytes || readWrongSign) {
|
35 | throw new Error('Unsupported VMDK, ' + valueName + ' is too big');
|
36 | }
|
37 | return value;
|
38 | }
|
39 | function parseU64b(buffer, offset, valueName) {
|
40 | const extraBits = buffer.readUIntLE(offset + 6, 2);
|
41 | const value = buffer.readUIntLE(offset, 6);
|
42 | if (extraBits > 0) {
|
43 | throw new Error('Unsupported VMDK, ' + valueName + ' is too big');
|
44 | }
|
45 | return value;
|
46 | }
|
47 | function unpackHeader(buffer) {
|
48 | const magicString = buffer.slice(0, 4).toString('ascii');
|
49 | if (magicString !== 'KDMV') {
|
50 | throw new Error('not a VMDK file');
|
51 | }
|
52 | const version = buffer.readUInt32LE(4);
|
53 | if (version !== 1 && version !== 3) {
|
54 | throw new Error('unsupported VMDK version ' + version + ', only version 1 and 3 are supported');
|
55 | }
|
56 | const flags = parseFlags(buffer.slice(8, 12));
|
57 | const capacitySectors = parseU64b(buffer, 12, 'capacitySectors');
|
58 | const grainSizeSectors = parseU64b(buffer, 20, 'grainSizeSectors');
|
59 | const descriptorOffsetSectors = parseU64b(buffer, 28, 'descriptorOffsetSectors');
|
60 | const descriptorSizeSectors = parseU64b(buffer, 36, 'descriptorSizeSectors');
|
61 | const numGTEsPerGT = buffer.readUInt32LE(44);
|
62 | const rGrainDirectoryOffsetSectors = parseS64b(buffer, 48, 'rGrainDirectoryOffsetSectors');
|
63 | const grainDirectoryOffsetSectors = parseS64b(buffer, 56, 'grainDirectoryOffsetSectors');
|
64 | const overheadSectors = parseS64b(buffer, 64, 'overheadSectors');
|
65 | const compressionMethod = compressionMap[buffer.readUInt16LE(77)];
|
66 | const l1EntrySectors = numGTEsPerGT * grainSizeSectors;
|
67 | return {
|
68 | magicString,
|
69 | version,
|
70 | flags,
|
71 | compressionMethod,
|
72 | grainSizeSectors,
|
73 | overheadSectors,
|
74 | capacitySectors,
|
75 | descriptorOffsetSectors,
|
76 | descriptorSizeSectors,
|
77 | grainDirectoryOffsetSectors,
|
78 | rGrainDirectoryOffsetSectors,
|
79 | l1EntrySectors,
|
80 | numGTEsPerGT
|
81 | };
|
82 | }
|
83 | function createStreamOptimizedHeader(capacitySectors, descriptorSizeSectors, grainDirectoryOffsetSectors = -1, rGrainDirectoryOffsetSectors = 0) {
|
84 | const headerBuffer = Buffer.alloc(SECTOR_SIZE);
|
85 | Buffer.from('KDMV', 'ascii').copy(headerBuffer, 0);
|
86 | headerBuffer.writeUInt32LE(3, 4);
|
87 | const flags = 1 | 1 << 1 | 1 << 16 | 1 << 17;
|
88 | headerBuffer.writeUInt32LE(flags, 8);
|
89 | headerBuffer.writeBigUInt64LE(BigInt(capacitySectors), 12);
|
90 | const grainSizeSectors = 128;
|
91 | headerBuffer.writeBigUInt64LE(BigInt(grainSizeSectors), 20);
|
92 | const descriptorOffsetSectors = 1;
|
93 | headerBuffer.writeBigUInt64LE(BigInt(descriptorOffsetSectors), 28);
|
94 | headerBuffer.writeBigUInt64LE(BigInt(descriptorSizeSectors), 36);
|
95 | const numGTEsPerGT = 512;
|
96 | headerBuffer.writeUInt32LE(numGTEsPerGT, 44);
|
97 | headerBuffer.writeBigInt64LE(BigInt(rGrainDirectoryOffsetSectors), 48);
|
98 | headerBuffer.writeBigInt64LE(BigInt(grainDirectoryOffsetSectors), 56);
|
99 | const grainDirectoryEntries = Math.ceil(Math.ceil(capacitySectors / grainSizeSectors) / numGTEsPerGT);
|
100 | const grainTableEntries = grainDirectoryEntries * numGTEsPerGT;
|
101 | const grainDirectorySizeSectors = Math.ceil(grainDirectoryEntries * 4 / SECTOR_SIZE);
|
102 | const grainTableSizeSectors = Math.ceil(grainTableEntries * 4 / SECTOR_SIZE);
|
103 | let overheadSectors = 1 + descriptorSizeSectors;
|
104 | if (grainDirectoryOffsetSectors > 0) {
|
105 | overheadSectors += grainDirectorySizeSectors + grainTableSizeSectors;
|
106 | }
|
107 | headerBuffer.writeBigInt64LE(BigInt(overheadSectors), 64);
|
108 | headerBuffer.write('\n \r\n', 73, 4, 'ascii');
|
109 | headerBuffer.writeUInt16LE(1, 77);
|
110 | return {
|
111 | buffer: headerBuffer,
|
112 | grainDirectorySizeSectors,
|
113 | grainTableSizeSectors,
|
114 | grainDirectoryEntries,
|
115 | grainTableEntries
|
116 | };
|
117 | }
|
118 |
|
\ | No newline at end of file |