@isopodlabs/binary_libs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/tasks.json +39 -0
- package/README.md +60 -0
- package/dist/CompoundDocument.d.ts +129 -0
- package/dist/CompoundDocument.js +301 -0
- package/dist/arch.d.ts +41 -0
- package/dist/arch.js +94 -0
- package/dist/binary.d.ts +397 -0
- package/dist/binary.js +802 -0
- package/dist/binary_helpers.d.ts +69 -0
- package/dist/binary_helpers.js +328 -0
- package/dist/clr.d.ts +63 -0
- package/dist/clr.js +664 -0
- package/dist/elf.d.ts +11 -0
- package/dist/elf.js +791 -0
- package/dist/mach.d.ts +543 -0
- package/dist/mach.js +1034 -0
- package/dist/pe.d.ts +399 -0
- package/dist/pe.js +489 -0
- package/package.json +33 -0
- package/src/CompoundDocument.ts +314 -0
- package/src/arch.ts +76 -0
- package/src/clr.ts +651 -0
- package/src/elf.ts +803 -0
- package/src/mach.ts +1089 -0
- package/src/pe.ts +510 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as binary from '@isopodlabs/binary';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
|
|
4
|
+
const enum SecID {
|
|
5
|
+
FREE = -1, // Free sector, may exist in the file, but is not part of any stream
|
|
6
|
+
ENDOFCHAIN = -2, // Trailing SecID in a SecID chain
|
|
7
|
+
SAT = -3, // Sector is used by the sector allocation table
|
|
8
|
+
MSAT = -4, // Sector is used by the master sector allocation table
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class FAT {
|
|
12
|
+
fat: Int32Array;
|
|
13
|
+
freed: number[] = [];
|
|
14
|
+
dirty_fat = new Set<number>();
|
|
15
|
+
dirty_sec = new Set<number>();
|
|
16
|
+
|
|
17
|
+
constructor(size: number, public shift:number, public sectors: Uint8Array) {
|
|
18
|
+
this.fat = new Int32Array(size);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private free(id: number) {
|
|
22
|
+
this.freed.push(id);
|
|
23
|
+
this.fat[id] = SecID.FREE;
|
|
24
|
+
this.dirty_fat.add(id >> (this.shift - 2));
|
|
25
|
+
}
|
|
26
|
+
private alloc(prev: number) {
|
|
27
|
+
if (!this.freed.length) {
|
|
28
|
+
this.fat.forEach((v, i) => {
|
|
29
|
+
if (v === SecID.FREE)
|
|
30
|
+
this.freed.push(i);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const id = this.freed.length ? this.freed.pop()! : this.fat.length;
|
|
34
|
+
this.fat[prev] = id;
|
|
35
|
+
this.dirty_fat.add(id >> (this.shift - 2));
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
get_chain(id: number): number[] {
|
|
39
|
+
const chain: number[] = [];
|
|
40
|
+
while (id != SecID.ENDOFCHAIN) {
|
|
41
|
+
chain.push(id);
|
|
42
|
+
id = this.fat[id];
|
|
43
|
+
}
|
|
44
|
+
return chain;
|
|
45
|
+
}
|
|
46
|
+
resize_chain(chain: number[], data_size: number) {
|
|
47
|
+
const size = (data_size + (1 << this.shift) - 1) >> this.shift;
|
|
48
|
+
|
|
49
|
+
while (chain.length > size)
|
|
50
|
+
this.free(chain.pop()!);
|
|
51
|
+
|
|
52
|
+
if (size) {
|
|
53
|
+
let last = chain[size - 1];
|
|
54
|
+
while (chain.length < size)
|
|
55
|
+
chain.push(last = this.alloc(last));
|
|
56
|
+
|
|
57
|
+
if (this.fat[last] !== SecID.ENDOFCHAIN) {
|
|
58
|
+
this.fat[last] = SecID.ENDOFCHAIN;
|
|
59
|
+
this.dirty_fat.add(last >> (this.shift - 2));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} else {
|
|
63
|
+
chain.push(SecID.ENDOFCHAIN);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
clear_dirty() {
|
|
68
|
+
this.dirty_fat.clear();
|
|
69
|
+
this.dirty_sec.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
read_chain(chain: number[], dest: Uint8Array) {
|
|
73
|
+
chain.forEach((id, index) => {
|
|
74
|
+
const id2 = id << this.shift;
|
|
75
|
+
const index2 = index << this.shift;
|
|
76
|
+
dest.set(this.sectors.subarray(id2, id2 + Math.min(dest.length - index2)), index2);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
read_chain_alloc(chain: number[]) {
|
|
80
|
+
const dest = new Uint8Array(chain.length << this.shift);
|
|
81
|
+
this.read_chain(chain, dest);
|
|
82
|
+
return dest;
|
|
83
|
+
}
|
|
84
|
+
read(id: number, dest: Uint8Array) {
|
|
85
|
+
this.read_chain(this.get_chain(id), dest);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
write_chain(chain: number[], source: Uint8Array) {
|
|
89
|
+
chain.forEach((id, index) => {
|
|
90
|
+
this.sectors.set(source.subarray(index << this.shift, (index + 1) << this.shift), id << this.shift);
|
|
91
|
+
this.dirty_sec.add(id);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
dirty_chain_part(chain: number[], offset: number) {
|
|
95
|
+
const sector = chain[offset >> this.shift];
|
|
96
|
+
this.dirty_sec.add(sector);
|
|
97
|
+
return this.sectors.subarray((sector << this.shift) + (offset & ((1 << this.shift) - 1)));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class Header extends binary.ReadWriteStruct({
|
|
103
|
+
magic: binary.UINT64_BE,
|
|
104
|
+
id: binary.Buffer(16),
|
|
105
|
+
revision: binary.UINT16_LE,
|
|
106
|
+
version: binary.UINT16_LE,
|
|
107
|
+
byteorder: binary.UINT16_LE,
|
|
108
|
+
sector_shift: binary.UINT16_LE,
|
|
109
|
+
mini_shift: binary.UINT16_LE,
|
|
110
|
+
unused1: binary.SkipType(6),
|
|
111
|
+
num_directory: binary.UINT32_LE,
|
|
112
|
+
num_fat: binary.UINT32_LE,
|
|
113
|
+
first_directory:binary.UINT32_LE,
|
|
114
|
+
transaction: binary.SkipType(4), //must be 0
|
|
115
|
+
mini_cutoff: binary.UINT32_LE,
|
|
116
|
+
first_mini: binary.UINT32_LE,
|
|
117
|
+
num_mini: binary.UINT32_LE,
|
|
118
|
+
first_difat: binary.UINT32_LE,
|
|
119
|
+
num_difat: binary.UINT32_LE,
|
|
120
|
+
difat: binary.Buffer(436),
|
|
121
|
+
}) {
|
|
122
|
+
sector_size() { return 1 << this.sector_shift; }
|
|
123
|
+
use_mini(size: number) { return size < this.mini_cutoff; }
|
|
124
|
+
valid() { return this.magic == 0xD0CF11E0A1B11AE1n; }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const TYPE = {
|
|
128
|
+
Empty: 0,
|
|
129
|
+
UserStorage: 1,
|
|
130
|
+
UserStream: 2,
|
|
131
|
+
LockBytes: 3,
|
|
132
|
+
Property: 4,
|
|
133
|
+
RootStorage: 5,
|
|
134
|
+
} as const;
|
|
135
|
+
|
|
136
|
+
const COLOUR = {
|
|
137
|
+
RED: 0, BLACK: 1
|
|
138
|
+
} as const;
|
|
139
|
+
|
|
140
|
+
class DirEntry extends binary.ReadWriteStruct({
|
|
141
|
+
name: binary.StringType(64, 'utf16le'),
|
|
142
|
+
name_size: binary.UINT16_LE,
|
|
143
|
+
type: binary.UINT8,
|
|
144
|
+
colour: binary.UINT8,
|
|
145
|
+
left: binary.INT32_LE,
|
|
146
|
+
right: binary.INT32_LE,
|
|
147
|
+
root: binary.INT32_LE,
|
|
148
|
+
guid: binary.Buffer(16),
|
|
149
|
+
flags: binary.UINT32_LE,
|
|
150
|
+
creation: binary.UINT64_LE,
|
|
151
|
+
modification: binary.UINT64_LE,
|
|
152
|
+
sec_id: binary.INT32_LE,
|
|
153
|
+
size: binary.UINT32_LE,
|
|
154
|
+
unused: binary.UINT32_LE
|
|
155
|
+
}) {
|
|
156
|
+
constructor(public index: number, r: binary.stream) {
|
|
157
|
+
super(r);
|
|
158
|
+
this.name = this.name.substring(0, this.name_size / 2 - 1);
|
|
159
|
+
}
|
|
160
|
+
load(fat: FAT) {
|
|
161
|
+
const data = new Uint8Array(this.size);
|
|
162
|
+
fat.read(this.sec_id, data);
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export class Master {
|
|
168
|
+
difat: Int32Array;
|
|
169
|
+
fat: FAT;
|
|
170
|
+
mini_fat: FAT;
|
|
171
|
+
mini_chain: number[];
|
|
172
|
+
|
|
173
|
+
constructor(sectors: Uint8Array, public header: Header) {
|
|
174
|
+
const shift = header.sector_shift;
|
|
175
|
+
let num = header.num_difat;
|
|
176
|
+
let m_size = 109 + (num << (shift - 2));
|
|
177
|
+
this.difat = new Int32Array(m_size);
|
|
178
|
+
binary.utils.to8(this.difat).set(header.difat, 0);
|
|
179
|
+
|
|
180
|
+
let sect = header.first_difat;
|
|
181
|
+
let p = 109 * 4;
|
|
182
|
+
while (num--) {
|
|
183
|
+
const data = sectors.subarray(sect << shift, (sect + 1) << shift);
|
|
184
|
+
const end = data.length - 4;
|
|
185
|
+
sect = new DataView(data.buffer).getUint32(end);
|
|
186
|
+
binary.utils.to8(this.difat).set(data.subarray(0, end), p);
|
|
187
|
+
p += end;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
while (this.difat[m_size - 1] == SecID.FREE)
|
|
191
|
+
--m_size;
|
|
192
|
+
|
|
193
|
+
this.fat = new FAT(m_size << (shift - 2), shift, sectors);
|
|
194
|
+
|
|
195
|
+
Array.from(this.difat.subarray(0, m_size)).forEach((id, index) => {
|
|
196
|
+
const data = sectors.subarray(id << shift, (id + 1) << shift);
|
|
197
|
+
binary.utils.to8(this.fat.fat).set(data, index << shift);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const root = new DirEntry(0, new binary.stream(sectors.subarray(header.first_directory << shift)));
|
|
201
|
+
this.mini_chain = this.fat.get_chain(root.sec_id);
|
|
202
|
+
this.mini_fat = new FAT(header.num_mini << (shift - 2), header.mini_shift, root.load(this.fat));
|
|
203
|
+
this.fat.read(header.first_mini, binary.utils.to8(this.mini_fat.fat));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get_fat(mini: boolean) {
|
|
207
|
+
return mini ? this.mini_fat : this.fat;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async flush(filename: string) {
|
|
211
|
+
const dirty = new Set(this.fat.dirty_sec);
|
|
212
|
+
|
|
213
|
+
function mark_dirty_shift(entries: Iterable<number>, translate: number[], shift: number) {
|
|
214
|
+
for (const i of entries)
|
|
215
|
+
dirty.add(translate[i >> shift]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const mini_extra = this.fat.shift - this.header.mini_shift;
|
|
219
|
+
mark_dirty_shift(this.fat.dirty_fat.keys(), Array.from(this.difat), 0);
|
|
220
|
+
mark_dirty_shift(this.mini_fat.dirty_sec, this.mini_chain, mini_extra);
|
|
221
|
+
mark_dirty_shift(this.mini_fat.dirty_fat.keys(), this.fat.get_chain(this.header.first_mini), mini_extra);
|
|
222
|
+
|
|
223
|
+
if (!dirty.size)
|
|
224
|
+
return;
|
|
225
|
+
|
|
226
|
+
let fileHandle: fs.FileHandle|undefined;
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
fileHandle = await fs.open(filename, 'r+');
|
|
230
|
+
|
|
231
|
+
const ss = 1 << this.fat.shift;
|
|
232
|
+
for (const i of dirty.keys()) {
|
|
233
|
+
const position = i * ss;
|
|
234
|
+
await fileHandle.write(this.fat.sectors, position, ss, position + ss);
|
|
235
|
+
}
|
|
236
|
+
this.fat.clear_dirty();
|
|
237
|
+
this.mini_fat.clear_dirty();
|
|
238
|
+
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('An error occurred:', error);
|
|
241
|
+
} finally {
|
|
242
|
+
if (fileHandle)
|
|
243
|
+
await fileHandle.close();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export class Reader extends Master {
|
|
249
|
+
public entries: DirEntry[] = [];
|
|
250
|
+
private entry_chain: number[];
|
|
251
|
+
|
|
252
|
+
constructor(sectors: Uint8Array, header: Header) {
|
|
253
|
+
super(sectors, header);
|
|
254
|
+
|
|
255
|
+
this.entry_chain = this.fat.get_chain(header.first_directory);
|
|
256
|
+
const dir_buff = this.fat.read_chain_alloc(this.entry_chain);
|
|
257
|
+
const r2 = new binary.stream(dir_buff);
|
|
258
|
+
for (let i = 0; i < dir_buff.length / 128; i++)
|
|
259
|
+
this.entries[i] = new DirEntry(i, r2.seek(i * 128));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
find(name: string, i: number = 0): DirEntry|undefined {
|
|
263
|
+
const stack: number[] = [];
|
|
264
|
+
let sp = 0;
|
|
265
|
+
|
|
266
|
+
for (;;) {
|
|
267
|
+
const e = this.entries[i];
|
|
268
|
+
if (e.name == name)
|
|
269
|
+
return e;
|
|
270
|
+
|
|
271
|
+
if (e.type == TYPE.RootStorage)
|
|
272
|
+
stack[sp++] = e.root;
|
|
273
|
+
|
|
274
|
+
if (e.right != -1)
|
|
275
|
+
stack[sp++] = e.right;
|
|
276
|
+
|
|
277
|
+
i = e.left;
|
|
278
|
+
if (i == -1) {
|
|
279
|
+
if (sp === 0)
|
|
280
|
+
return undefined;
|
|
281
|
+
i = stack[--sp];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
read(e: DirEntry) {
|
|
287
|
+
const mini = this.header.use_mini(e.size);
|
|
288
|
+
const fat = this.get_fat(mini);
|
|
289
|
+
return e.load(fat);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
write(e: DirEntry, data: Uint8Array) {
|
|
293
|
+
const mini1 = this.header.use_mini(e.size);
|
|
294
|
+
const fat1 = this.get_fat(mini1);
|
|
295
|
+
const chain = fat1.get_chain(e.sec_id);
|
|
296
|
+
|
|
297
|
+
const mini2 = this.header.use_mini(data.length);
|
|
298
|
+
const fat2 = this.get_fat(mini2);
|
|
299
|
+
|
|
300
|
+
if (data.length != e.size) {
|
|
301
|
+
if (mini1 != mini2)
|
|
302
|
+
fat1.resize_chain(chain, 0);
|
|
303
|
+
fat2.resize_chain(chain, data.length);
|
|
304
|
+
|
|
305
|
+
e.size = data.length;
|
|
306
|
+
e.sec_id = chain[0];
|
|
307
|
+
|
|
308
|
+
const dest = this.fat.dirty_chain_part(this.entry_chain, e.index * 128);
|
|
309
|
+
e.write(new binary.stream(dest));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fat2.write_chain(chain, data);
|
|
313
|
+
}
|
|
314
|
+
}
|
package/src/arch.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as binary from '@isopodlabs/binary';
|
|
2
|
+
|
|
3
|
+
const AR_MEMBER_HEADER = {
|
|
4
|
+
name: binary.as(binary.StringType(16), x => { x = x.trim(); return x.endsWith('/') ? x.slice(0, -1) : x; }),
|
|
5
|
+
date: binary.asInt(binary.StringType(12)),
|
|
6
|
+
uid: binary.asInt(binary.StringType(6)),
|
|
7
|
+
gid: binary.asInt(binary.StringType(6)),
|
|
8
|
+
mode: binary.asInt(binary.StringType(8), 8),
|
|
9
|
+
size: binary.asInt(binary.StringType(10)),
|
|
10
|
+
fmag: binary.as(binary.StringType(2), x => x.trim() == '`' ? '' : x),
|
|
11
|
+
contents: binary.DontRead<any>()
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const AR_SYM64 = {
|
|
15
|
+
name: binary.StringType(12),
|
|
16
|
+
offset: binary.asInt(binary.StringType(4))
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class ArchFile {
|
|
20
|
+
members: binary.ReadType<typeof AR_MEMBER_HEADER>[] = [];
|
|
21
|
+
|
|
22
|
+
static check(data: Uint8Array): boolean {
|
|
23
|
+
const x = binary.utils.decodeText(data.subarray(0, 8), 'utf8');
|
|
24
|
+
return x == '!<arch>\n';
|
|
25
|
+
}
|
|
26
|
+
constructor(data: Uint8Array) {
|
|
27
|
+
const s = new binary.stream(data);
|
|
28
|
+
const header = binary.read(s, binary.StringType(8));
|
|
29
|
+
|
|
30
|
+
if (header !== '!<arch>\n')
|
|
31
|
+
throw new Error('Invalid archive file format');
|
|
32
|
+
|
|
33
|
+
let long_names;
|
|
34
|
+
while (s.remaining() > 0) {
|
|
35
|
+
const member = binary.read(s, AR_MEMBER_HEADER);
|
|
36
|
+
const data = s.read_buffer(member.size);
|
|
37
|
+
s.align(2);
|
|
38
|
+
|
|
39
|
+
if (member.name == '/') {
|
|
40
|
+
long_names = binary.utils.decodeText(data, 'utf8');
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (member.name[0] == '/' && long_names) {
|
|
44
|
+
const offset = +member.name.substring(1);
|
|
45
|
+
member.name = long_names.substring(offset, long_names.indexOf('/', offset));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (member.name == '') {
|
|
49
|
+
const s2 = new binary.stream(data);
|
|
50
|
+
const offsets = binary.ArrayType(binary.INT32_BE, binary.INT32_BE).get(s2);
|
|
51
|
+
member.name = 'Symbols';
|
|
52
|
+
member.contents = offsets.map(offset => [
|
|
53
|
+
binary.NullTerminatedStringType.get(s2),
|
|
54
|
+
offset
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
} else if (member.name == '/SYM') {
|
|
58
|
+
const s2 = new binary.stream(data);
|
|
59
|
+
const syms = binary.ArrayType(binary.INT32_BE, binary.NullTerminatedStringType).get(s2);
|
|
60
|
+
|
|
61
|
+
member.contents = syms.map(name => ({
|
|
62
|
+
name,
|
|
63
|
+
offset: binary.INT32_BE.get(s2)
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
} else if (member.name == '/SYM64') {
|
|
67
|
+
const s2 = new binary.stream(data);
|
|
68
|
+
member.contents = binary.RemainingArrayType(AR_SYM64).get(s2);
|
|
69
|
+
|
|
70
|
+
} else {
|
|
71
|
+
member.contents = data;
|
|
72
|
+
}
|
|
73
|
+
this.members.push(member);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|