-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
125 lines (118 loc) · 3.93 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import * as fs from "fs";
import { BinaryReader } from "binutils64";
const magic27 = 0x07564427;
const magic28 = 0x07564428;
const magic29 = 0x07564429;
// Pretty print number in hexadecimal
const hexify = (n: number, d: number = 8) => {
return "0x" + ("0".repeat(d) + n.toString(16).toUpperCase()).slice(-d);
};
// Read entire stream into a buffer
const readIntoBuf = (stream: fs.ReadStream): Promise<Buffer> => {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
stream.on("data", (chunk) => {
chunks.push(Buffer.from(chunk));
});
stream.on("error", (err) => {
reject(err);
});
stream.on("end", () => {
resolve(Buffer.concat(chunks));
});
});
};
// Read a null terminated string in utf8
const readNullTerminatedString = (binaryReader: typeof BinaryReader) => {
const bufferArray: Buffer[] = []
while(true) {
let b: Buffer = binaryReader.ReadBytes(1);
if (b.readUInt8()==0) {
break;
}
bufferArray.push(b);
}
return Buffer.concat(bufferArray).toString('utf8');
}
// Deserialize a single data entry
const readEntry = (binaryReader: typeof BinaryReader, stringPool: string[], type: number, magic: number) => {
let key;
if([magic27,magic28].includes(magic)) {
key = readNullTerminatedString(binaryReader);
} else {
const index = binaryReader.ReadUInt32();
key = stringPool[index];
}
switch(type) {
case 0x00: // nested entry
return [key, deserialize(binaryReader, stringPool, magic)]
case 0x01: // string
return [key, readNullTerminatedString(binaryReader)]
case 0x02: // number
return [key, binaryReader.ReadUInt32()]
default:
throw new Error(`Unknown entry type: ${hexify(type)}`)
}
}
// Deserialize all data entries
const deserialize = (binaryReader: typeof BinaryReader, stringPool: string[], magic: number) =>{
const entries: any = {};
while(true) {
const type = binaryReader.ReadUInt8();
if(type == 0x08) { // entries list terminator
break;
}
let [key, val] = readEntry(binaryReader, stringPool, type, magic);
entries[key] = val;
}
return entries
}
// Read BVDF format into a json object
export const readBinaryVDF = async (stream: fs.ReadStream) => {
const originalBuffer = await readIntoBuf(stream);
const binaryReader = new BinaryReader(originalBuffer, "little");
const magic = binaryReader.ReadUInt32();
if (![magic27, magic28, magic29].includes(magic)) {
throw new Error(`Unknown magic header: ${hexify(magic)}`);
}
const universe = binaryReader.ReadUInt32();
const stringPool: string[] = [];
if (magic == magic29) {
const stringTableOffset = binaryReader.ReadInt64();
const oldPos = binaryReader.Position;
const oldBuffer = Buffer.from(binaryReader.ByteBuffer);
binaryReader.Position = stringTableOffset;
binaryReader.ByteBuffer = originalBuffer.subarray(stringTableOffset)
const stringCount = binaryReader.ReadUInt32();
for(let i=0; i < stringCount; i++) {
stringPool.push(readNullTerminatedString(binaryReader))
}
binaryReader.Position = oldPos;
binaryReader.ByteBuffer = oldBuffer;
}
const appinfos: any[] = [];
while(true) {
const appid = binaryReader.ReadUInt32();
if(appid == 0) {
break;
}
const size = binaryReader.ReadUInt32();
const end = binaryReader.Position + size;
let app: {[k: string]: any} = {
appid: appid,
infoState: binaryReader.ReadUInt32(),
lastUpdated: new Date(binaryReader.ReadUInt32()),
token: binaryReader.ReadUInt64(),
hash: binaryReader.ReadBytes(20),
changeNumber: binaryReader.ReadUInt32()
}
if([magic28, magic29].includes(magic)) {
app.binaryDataHash = binaryReader.ReadBytes(20);
}
appinfos.push(deserialize(binaryReader, stringPool, magic))
if(binaryReader.Position !== end) {
throw new Error(`Expected ${hexify(end)} byte offset, but got ${hexify(binaryReader.Position)}`)
}
}
return appinfos
};