bun-memory 1.1.42 → 1.1.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-memory",
3
- "version": "1.1.42",
3
+ "version": "1.1.44",
4
4
  "author": "Stev Peifer <stev@bell.net>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  "@types/bun": "latest"
13
13
  },
14
14
  "peerDependencies": {
15
- "typescript": "^5"
15
+ "typescript": "^5.9.2"
16
16
  },
17
17
  "exports": {
18
18
  ".": "./index.ts"
@@ -50,5 +50,8 @@
50
50
  "run:benchmark": "bun ./example/benchmark.ts",
51
51
  "run:trigger-bot": "bun ./example/trigger-bot.ts"
52
52
  },
53
- "type": "module"
53
+ "type": "module",
54
+ "dependencies": {
55
+ "bun-kernel32": "^1.0.9"
56
+ }
54
57
  }
package/structs/Memory.ts CHANGED
@@ -1,25 +1,11 @@
1
- import { CString, FFIType, dlopen, ptr } from 'bun:ffi';
1
+ import '../runtime/extensions';
2
2
 
3
- import type { Module, Point, QAngle, Quaternion, Region, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
4
- import Win32Error from './Win32Error';
3
+ import { CString, type Pointer, ptr } from 'bun:ffi';
4
+
5
+ import Kernel32, { INVALID_HANDLE_VALUE } from 'bun-kernel32';
5
6
 
6
- const {
7
- symbols: { CloseHandle, CreateToolhelp32Snapshot, GetLastError, Module32FirstW, Module32NextW, OpenProcess, Process32FirstW, Process32NextW, ReadProcessMemory, VirtualProtectEx, WriteProcessMemory },
8
- } = dlopen('kernel32.dll', {
9
- CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
10
- CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
11
- GetCurrentProcess: { args: [], returns: FFIType.ptr },
12
- GetLastError: { returns: FFIType.u32 },
13
- Module32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
14
- Module32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
15
- OpenProcess: { args: [FFIType.u32, FFIType.bool, FFIType.u32], returns: FFIType.u64 },
16
- Process32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
17
- Process32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
18
- ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
19
- VirtualProtectEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
20
- VirtualQueryEx: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
21
- WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
22
- });
7
+ import type { Module, Point, QAngle, Quaternion, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
8
+ import Win32Error from './Win32Error';
23
9
 
24
10
  /**
25
11
  * Provides cross-process memory manipulation for native applications.
@@ -28,13 +14,12 @@ const {
28
14
  *
29
15
  * Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
30
16
  *
31
- * @todo Add call method for calling functions in remote process.
32
17
  * @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
33
18
  * @todo When adding 32-bit support, several u64 will need changed to u64_fast.
34
19
  *
35
20
  * @example
36
21
  * ```ts
37
- * import Memory from './structs/Memory';
22
+ * import Memory from 'bun-memory';
38
23
  * const cs2 = new Memory('cs2.exe');
39
24
  * const myFloat = cs2.f32(0x12345678n);
40
25
  * cs2.close();
@@ -51,29 +36,33 @@ class Memory {
51
36
  * ```
52
37
  */
53
38
  constructor(identifier: number | string) {
39
+ const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
40
+
54
41
  const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
55
42
  const th32ProcessID = 0;
56
43
 
57
- const hSnapshot = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
44
+ const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
58
45
 
59
46
  if (hSnapshot === -1n) {
60
- throw new Win32Error('CreateToolhelp32Snapshot', GetLastError());
47
+ throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
61
48
  }
62
49
 
63
- const lppe = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
64
- /* */ lppe.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
50
+ const lppeBuffer = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
51
+ /* */ lppeBuffer.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
65
52
 
66
- const bProcess32FirstW = Process32FirstW(hSnapshot, lppe);
53
+ const lppe = lppeBuffer.ptr;
54
+
55
+ const bProcess32FirstW = Kernel32.Process32FirstW(hSnapshot, lppe);
67
56
 
68
57
  if (!bProcess32FirstW) {
69
- CloseHandle(hSnapshot);
58
+ Kernel32.CloseHandle(hSnapshot);
70
59
 
71
- throw new Win32Error('Process32FirstW', GetLastError());
60
+ throw new Win32Error('Process32FirstW', Kernel32.GetLastError());
72
61
  }
73
62
 
74
63
  do {
75
- const szExeFile = lppe.toString('utf16le', 0x2c, 0x234).replace(/\0+$/, '');
76
- const th32ProcessID = lppe.readUInt32LE(0x08);
64
+ const szExeFile = lppeBuffer.toString('utf16le', 0x2c, 0x234).replace(ReplaceTrailingNull, '');
65
+ const th32ProcessID = lppeBuffer.readUInt32LE(0x08);
77
66
 
78
67
  if (
79
68
  (typeof identifier === 'number' && identifier !== th32ProcessID) || //
@@ -83,43 +72,33 @@ class Memory {
83
72
  }
84
73
 
85
74
  const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
86
- const inheritHandle = false;
75
+ const inheritHandle = 0;
87
76
 
88
- const hProcess = OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
77
+ const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
89
78
 
90
79
  if (hProcess === 0n) {
91
- CloseHandle(hSnapshot);
80
+ Kernel32.CloseHandle(hSnapshot);
92
81
 
93
- throw new Win32Error('OpenProcess', GetLastError());
82
+ throw new Win32Error('OpenProcess', Kernel32.GetLastError());
94
83
  }
95
84
 
96
- this._modules = {};
85
+ this.__modules = {};
97
86
 
98
87
  this.hProcess = hProcess;
99
88
  this.th32ProcessID = th32ProcessID;
100
89
 
101
90
  this.refresh();
102
91
 
103
- CloseHandle(hSnapshot);
92
+ Kernel32.CloseHandle(hSnapshot);
104
93
 
105
94
  return;
106
- } while (Process32NextW(hSnapshot, lppe));
95
+ } while (Kernel32.Process32NextW(hSnapshot, lppe));
107
96
 
108
- CloseHandle(hSnapshot);
97
+ Kernel32.CloseHandle(hSnapshot);
109
98
 
110
- throw new Error(`Process not found: ${identifier}…`);
99
+ throw new Error(`Process not found: ${identifier}.`);
111
100
  }
112
101
 
113
- /**
114
- * Regex patterns for matching hex strings and wildcards in memory scans.
115
- * Used by the pattern method.
116
- */
117
- private static readonly Patterns = {
118
- MatchAll: /(?:[0-9A-Fa-f]{2})+/g,
119
- Test: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
120
- // Test: /^(?:\*{2}|[0-9A-Fa-f]{2}|\?{2})+$/,
121
- };
122
-
123
102
  /**
124
103
  * Map of loaded modules in the process, keyed by module name.
125
104
  * @example
@@ -128,7 +107,17 @@ class Memory {
128
107
  * const mainModule = cs2.modules['cs2.exe'];
129
108
  * ```
130
109
  */
131
- private _modules: { [key: string]: Module };
110
+ private __modules: { [key: string]: Readonly<Module> };
111
+
112
+ /**
113
+ * Regex patterns for matching hex strings and wildcards in memory scans.
114
+ * Used by the pattern method.
115
+ */
116
+ private static readonly Patterns = {
117
+ PatternMatchAll: /(?:[0-9A-Fa-f]{2})+/g,
118
+ PatternTest: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
119
+ ReplaceTrailingNull: /\0+$/,
120
+ };
132
121
 
133
122
  /**
134
123
  * Scratch buffers and typed views for temporary FFI reads/writes.
@@ -160,7 +149,14 @@ class Memory {
160
149
  private readonly Scratch16 = new Uint8Array(0x10);
161
150
  private readonly Scratch16Float32Array = new Float32Array(this.Scratch16.buffer, this.Scratch16.byteOffset, 0x04);
162
151
 
163
- private readonly ScratchModuleEntry32W = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
152
+ private readonly Scratch48 = new Uint8Array([
153
+ 0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xec, 0x28, 0xff, 0xd0, 0x48, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc4,
154
+ 0x28, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155
+ ]);
156
+
157
+ private readonly Scratch112 = new Uint8Array(0x70);
158
+
159
+ private readonly Scratch1080 = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
164
160
 
165
161
  private static TextDecoderUTF8 = new TextDecoder('utf-8');
166
162
 
@@ -171,29 +167,15 @@ class Memory {
171
167
 
172
168
  /**
173
169
  * Gets all loaded modules in the process.
174
- * @returns Map of module name to module info.
170
+ * @returns Map of module name to module info with base-relative helpers.
175
171
  * @example
176
172
  * ```ts
177
173
  * const cs2 = new Memory('cs2.exe');
178
174
  * const client = cs2.modules['client.dll'];
179
175
  * ```
180
176
  */
181
- public get modules(): Memory['_modules'] {
182
- return this._modules;
183
- }
184
-
185
- /**
186
- * Closes the process handle.
187
- * @example
188
- * ```ts
189
- * const cs2 = new Memory('cs2.exe');
190
- * cs2.close();
191
- * ```
192
- */
193
- public close(): void {
194
- CloseHandle(this.hProcess);
195
-
196
- return;
177
+ public get modules(): Memory['__modules'] {
178
+ return this.__modules;
197
179
  }
198
180
 
199
181
  /**
@@ -227,49 +209,106 @@ class Memory {
227
209
  }
228
210
 
229
211
  /**
230
- * Refreshes the module list for the process.
212
+ * Allocates memory in the remote process.
213
+ * @param length Bytes to allocate.
214
+ * @param protect Page protection (defaults to PAGE_READWRITE).
215
+ * @returns Base address of the allocation.
231
216
  * @example
232
217
  * ```ts
233
- * const cs2 = new Memory('cs2.exe');
234
- * cs2.refresh();
218
+ * const ptr = cs2.alloc(0x100);
235
219
  * ```
236
220
  */
237
- public refresh(): void {
238
- const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
221
+ public alloc(length: number, protect: number = 0x04): bigint {
222
+ const { hProcess } = this;
239
223
 
240
- const hSnapshot = CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
224
+ if (length <= 0) {
225
+ throw new RangeError('length must be greater than 0.');
226
+ }
241
227
 
242
- if (hSnapshot === -1n) {
243
- throw new Win32Error('CreateToolhelp32Snapshot', GetLastError());
228
+ const dwSize = BigInt(length);
229
+ const flAllocationType = 0x3000; /* MEM_COMMIT | MEM_RESERVE */
230
+ const flProtect = protect;
231
+ const lpAddress = 0n;
232
+
233
+ const lpBaseAddress = Kernel32.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
234
+
235
+ if (lpBaseAddress === 0n) {
236
+ throw new Win32Error('VirtualAllocEx', Kernel32.GetLastError());
244
237
  }
245
238
 
246
- this.ScratchModuleEntry32W.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
239
+ return lpBaseAddress;
240
+ }
247
241
 
248
- const lpme = this.ScratchModuleEntry32W;
242
+ /**
243
+ * Closes the process handle.
244
+ * @example
245
+ * ```ts
246
+ * const cs2 = new Memory('cs2.exe');
247
+ * cs2.close();
248
+ * ```
249
+ */
250
+ public close(): void {
251
+ Kernel32.CloseHandle(this.hProcess);
249
252
 
250
- const bModule32FirstW = Module32FirstW(hSnapshot, lpme);
253
+ return;
254
+ }
251
255
 
252
- if (!bModule32FirstW) {
253
- CloseHandle(hSnapshot);
256
+ /**
257
+ * Frees memory allocated in the remote process.
258
+ * @param address Allocation base address.
259
+ * @example
260
+ * ```ts
261
+ * const ptr = cs2.alloc(0x100);
262
+ * cs2.free(ptr);
263
+ * ```
264
+ */
265
+ public free(address: bigint): void {
266
+ const { hProcess } = this;
254
267
 
255
- throw new Win32Error('Module32FirstW', GetLastError());
268
+ const dwFreeType = 0x8000; /* MEM_RELEASE */
269
+ const dwSize = 0x00n;
270
+ const lpAddress = address;
271
+
272
+ const bVirtualFreeEx = !!Kernel32.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType);
273
+
274
+ if (!bVirtualFreeEx) {
275
+ throw new Win32Error('VirtualFreeEx', Kernel32.GetLastError());
256
276
  }
257
277
 
258
- const modules: Memory['_modules'] = {};
278
+ return;
279
+ }
259
280
 
260
- do {
261
- const modBaseAddr = lpme.readBigUInt64LE(0x18);
262
- const modBaseSize = lpme.readUInt32LE(0x20);
263
- const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
281
+ /**
282
+ * Changes the page protection of a region.
283
+ * @param address Base address to protect.
284
+ * @param length Bytes to protect.
285
+ * @param protect New protection flags.
286
+ * @returns Previous protection flags.
287
+ * @example
288
+ * ```ts
289
+ * const cs2 = new Memory('cs2.exe');
290
+ * const previous = cs2.protection(0x12345678n, 0x1000, 0x40);
291
+ * ```
292
+ */
293
+ public protection(address: bigint, length: number, protect: number): number {
294
+ const { Scratch4Uint32Array, hProcess } = this;
264
295
 
265
- modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
266
- } while (Module32NextW(hSnapshot, lpme));
296
+ if (length <= 0) {
297
+ throw new RangeError('length must be greater than 0.');
298
+ }
267
299
 
268
- CloseHandle(hSnapshot);
300
+ const dwSize = BigInt(length);
301
+ const flNewProtect = protect;
302
+ const lpAddress = address;
303
+ const lpflOldProtect = Scratch4Uint32Array.ptr;
269
304
 
270
- this._modules = Object.freeze(modules);
305
+ const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
271
306
 
272
- return;
307
+ if (!bVirtualProtectEx) {
308
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
309
+ }
310
+
311
+ return Scratch4Uint32Array[0x00];
273
312
  }
274
313
 
275
314
  /**
@@ -285,20 +324,70 @@ class Memory {
285
324
  * ```
286
325
  */
287
326
  public read<T extends Scratch>(address: bigint, scratch: T): T {
327
+ const { hProcess } = this;
328
+
288
329
  const lpBaseAddress = address;
289
330
  const lpBuffer = ptr(scratch);
290
331
  const nSize = BigInt(scratch.byteLength);
291
332
  const numberOfBytesRead = 0x00n;
292
333
 
293
- const bReadProcessMemory = ReadProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
334
+ const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
294
335
 
295
336
  if (!bReadProcessMemory) {
296
- throw new Win32Error('ReadProcessMemory', GetLastError());
337
+ throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
297
338
  }
298
339
 
299
340
  return scratch;
300
341
  }
301
342
 
343
+ /**
344
+ * Refreshes the module list for the process.
345
+ * @example
346
+ * ```ts
347
+ * const cs2 = new Memory('cs2.exe');
348
+ * cs2.refresh();
349
+ * ```
350
+ */
351
+ public refresh(): void {
352
+ const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
353
+ const { th32ProcessID } = this;
354
+
355
+ const dwFlags = 0x00000018; /* TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 */
356
+
357
+ const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID)!;
358
+
359
+ if (hSnapshot === INVALID_HANDLE_VALUE) {
360
+ throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
361
+ }
362
+
363
+ const { Scratch1080: lpme } = this;
364
+ /* */ lpme.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
365
+
366
+ const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, ptr(lpme));
367
+
368
+ if (!bModule32FirstW) {
369
+ Kernel32.CloseHandle(hSnapshot);
370
+
371
+ throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
372
+ }
373
+
374
+ const modules: Memory['__modules'] = {};
375
+
376
+ do {
377
+ const modBaseAddr = lpme.readBigUInt64LE(0x18);
378
+ const modBaseSize = lpme.readUInt32LE(0x20);
379
+ const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(ReplaceTrailingNull, '');
380
+
381
+ modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
382
+ } while (Kernel32.Module32NextW(hSnapshot, ptr(lpme)));
383
+
384
+ Kernel32.CloseHandle(hSnapshot);
385
+
386
+ this.__modules = Object.freeze(modules);
387
+
388
+ return;
389
+ }
390
+
302
391
  /**
303
392
  * Writes a buffer to memory.
304
393
  * @param address Address to write to.
@@ -314,16 +403,18 @@ class Memory {
314
403
  * ```
315
404
  */
316
405
  public write(address: bigint, scratch: Scratch, force: boolean = false): this {
406
+ const { hProcess } = this;
407
+
317
408
  const lpBaseAddress = address;
318
409
  const lpBuffer = scratch.ptr;
319
410
  const nSize = BigInt(scratch.byteLength);
320
411
  const numberOfBytesWritten = 0x00n;
321
412
 
322
413
  if (!force) {
323
- const bWriteProcessMemory = WriteProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
414
+ const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
324
415
 
325
416
  if (!bWriteProcessMemory) {
326
- throw new Win32Error('WriteProcessMemory', GetLastError());
417
+ throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
327
418
  }
328
419
 
329
420
  return this;
@@ -333,26 +424,26 @@ class Memory {
333
424
  const flNewProtect = 0x40; /* PAGE_EXECUTE_READWRITE */
334
425
  const lpflOldProtect = Buffer.allocUnsafe(0x04);
335
426
 
336
- const bVirtualProtectEx = VirtualProtectEx(this.hProcess, lpBaseAddress, dwSize, flNewProtect, lpflOldProtect.ptr);
427
+ const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect, lpflOldProtect.ptr);
337
428
 
338
429
  if (!bVirtualProtectEx) {
339
- throw new Win32Error('VirtualProtectEx', GetLastError());
430
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
340
431
  }
341
432
 
342
433
  try {
343
- const bWriteProcessMemory = WriteProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
434
+ const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
344
435
 
345
436
  if (!bWriteProcessMemory) {
346
- throw new Win32Error('WriteProcessMemory', GetLastError());
437
+ throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
347
438
  }
348
439
  } finally {
349
440
  const flNewProtect2 = lpflOldProtect.readUInt32LE(0x00);
350
441
  const lpflOldProtect2 = Buffer.allocUnsafe(0x04);
351
442
 
352
- const bVirtualProtectEx2 = VirtualProtectEx(this.hProcess, lpBaseAddress, dwSize, flNewProtect2, lpflOldProtect2.ptr);
443
+ const bVirtualProtectEx2 = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect2, lpflOldProtect2.ptr);
353
444
 
354
445
  if (!bVirtualProtectEx2) {
355
- throw new Win32Error('VirtualProtectEx', GetLastError());
446
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
356
447
  }
357
448
  }
358
449
 
@@ -375,13 +466,13 @@ class Memory {
375
466
  public bool(address: bigint): boolean;
376
467
  public bool(address: bigint, value: boolean, force?: boolean): this;
377
468
  public bool(address: bigint, value?: boolean, force?: boolean): boolean | this {
378
- const Scratch1 = this.Scratch1;
469
+ const { Scratch1 } = this;
379
470
 
380
471
  if (value === undefined) {
381
472
  return this.read(address, Scratch1)[0x00] !== 0;
382
473
  }
383
474
 
384
- Scratch1[0x00] = value ? 1 : 0;
475
+ Scratch1[0x00] = value ? 0x01 : 0x00;
385
476
 
386
477
  void this.write(address, Scratch1, force);
387
478
 
@@ -471,7 +562,7 @@ class Memory {
471
562
  public f32(address: bigint): number;
472
563
  public f32(address: bigint, value: number, force?: boolean): this;
473
564
  public f32(address: bigint, value?: number, force?: boolean): number | this {
474
- const Scratch4Float32Array = this.Scratch4Float32Array; // prettier-ignore
565
+ const { Scratch4Float32Array } = this; // prettier-ignore
475
566
 
476
567
  if (value === undefined) {
477
568
  return this.read(address, Scratch4Float32Array)[0x00];
@@ -532,7 +623,7 @@ class Memory {
532
623
  public f64(address: bigint): number;
533
624
  public f64(address: bigint, value: number, force?: boolean): this;
534
625
  public f64(address: bigint, value?: number, force?: boolean): number | this {
535
- const Scratch8Float64Array = this.Scratch8Float64Array; // prettier-ignore
626
+ const { Scratch8Float64Array } = this; // prettier-ignore
536
627
 
537
628
  if (value === undefined) {
538
629
  return this.read(address, Scratch8Float64Array)[0x00];
@@ -593,7 +684,7 @@ class Memory {
593
684
  public i16(address: bigint): number;
594
685
  public i16(address: bigint, value: number, force?: boolean): this;
595
686
  public i16(address: bigint, value?: number, force?: boolean): number | this {
596
- const Scratch2Int16Array = this.Scratch2Int16Array; // prettier-ignore
687
+ const { Scratch2Int16Array } = this; // prettier-ignore
597
688
 
598
689
  if (value === undefined) {
599
690
  return this.read(address, Scratch2Int16Array)[0x00];
@@ -654,7 +745,7 @@ class Memory {
654
745
  public i32(address: bigint): number;
655
746
  public i32(address: bigint, value: number, force?: boolean): this;
656
747
  public i32(address: bigint, value?: number, force?: boolean): number | this {
657
- const Scratch4Int32Array = this.Scratch4Int32Array;
748
+ const { Scratch4Int32Array } = this;
658
749
 
659
750
  if (value === undefined) {
660
751
  return this.read(address, Scratch4Int32Array)[0x00];
@@ -715,7 +806,7 @@ class Memory {
715
806
  public i64(address: bigint): bigint;
716
807
  public i64(address: bigint, value: bigint, force?: boolean): this;
717
808
  public i64(address: bigint, value?: bigint, force?: boolean): bigint | this {
718
- const Scratch8BigInt64Array = this.Scratch8BigInt64Array;
809
+ const { Scratch8BigInt64Array } = this;
719
810
 
720
811
  if (value === undefined) {
721
812
  return this.read(address, Scratch8BigInt64Array)[0x00];
@@ -776,7 +867,7 @@ class Memory {
776
867
  public i8(address: bigint): number;
777
868
  public i8(address: bigint, value: number, force?: boolean): this;
778
869
  public i8(address: bigint, value?: number, force?: boolean): number | this {
779
- const Scratch1Int8Array = this.Scratch1Int8Array;
870
+ const { Scratch1Int8Array } = this;
780
871
 
781
872
  if (value === undefined) {
782
873
  return this.read(address, Scratch1Int8Array)[0x00];
@@ -936,7 +1027,7 @@ class Memory {
936
1027
  public point(address: bigint): Point;
937
1028
  public point(address: bigint, value: Point, force?: boolean): this;
938
1029
  public point(address: bigint, value?: Point, force?: boolean): Point | this {
939
- const Scratch8Float32Array = this.Scratch8Float32Array;
1030
+ const { Scratch8Float32Array } = this;
940
1031
 
941
1032
  if (value === undefined) {
942
1033
  void this.read(address, Scratch8Float32Array);
@@ -980,8 +1071,8 @@ class Memory {
980
1071
  const result = new Array<Vector2>(length);
981
1072
 
982
1073
  for (let i = 0, j = 0; i < length; i++, j += 0x02) {
983
- const x = scratch[j];
984
- const y = scratch[j + 0x01];
1074
+ const x = scratch[j],
1075
+ y = scratch[j + 0x01]; // prettier-ignore
985
1076
 
986
1077
  result[i] = { x, y };
987
1078
  }
@@ -1048,7 +1139,7 @@ class Memory {
1048
1139
  public qAngle(address: bigint): QAngle;
1049
1140
  public qAngle(address: bigint, value: QAngle, force?: boolean): this;
1050
1141
  public qAngle(address: bigint, value?: QAngle, force?: boolean): QAngle | this {
1051
- const Scratch12Float32Array = this.Scratch12Float32Array;
1142
+ const { Scratch12Float32Array } = this;
1052
1143
 
1053
1144
  if (value === undefined) {
1054
1145
  void this.read(address, Scratch12Float32Array);
@@ -1093,9 +1184,9 @@ class Memory {
1093
1184
  const result = new Array<QAngle>(length);
1094
1185
 
1095
1186
  for (let i = 0, j = 0; i < length; i++, j += 0x03) {
1096
- const pitch = scratch[j];
1097
- const yaw = scratch[j + 0x01];
1098
- const roll = scratch[j + 0x02];
1187
+ const pitch = scratch[j],
1188
+ yaw = scratch[j + 0x01],
1189
+ roll = scratch[j + 0x02]; // prettier-ignore
1099
1190
 
1100
1191
  result[i] = { pitch, yaw, roll };
1101
1192
  }
@@ -1164,7 +1255,7 @@ class Memory {
1164
1255
  public quaternion(address: bigint): Quaternion;
1165
1256
  public quaternion(address: bigint, value: Quaternion, force?: boolean): this;
1166
1257
  public quaternion(address: bigint, value?: Quaternion, force?: boolean): Quaternion | this {
1167
- const Scratch16Float32Array = this.Scratch16Float32Array;
1258
+ const { Scratch16Float32Array } = this;
1168
1259
 
1169
1260
  if (value === undefined) {
1170
1261
  void this.read(address, Scratch16Float32Array);
@@ -1285,7 +1376,7 @@ class Memory {
1285
1376
  public rgb(address: bigint): RGB;
1286
1377
  public rgb(address: bigint, value: RGB, force?: boolean): this;
1287
1378
  public rgb(address: bigint, value?: RGB, force?: boolean): RGB | this {
1288
- const Scratch3 = this.Scratch3;
1379
+ const { Scratch3 } = this;
1289
1380
 
1290
1381
  if (value === undefined) {
1291
1382
  void this.read(address, Scratch3);
@@ -1320,7 +1411,6 @@ class Memory {
1320
1411
  * ```
1321
1412
  */
1322
1413
  public rgbRaw(address: bigint): Uint8Array;
1323
- public rgbRaw(address: bigint): Uint8Array;
1324
1414
  public rgbRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1325
1415
  public rgbRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1326
1416
  if (values === undefined) {
@@ -1352,7 +1442,7 @@ class Memory {
1352
1442
  public rgba(address: bigint): RGBA;
1353
1443
  public rgba(address: bigint, value: RGBA, force?: boolean): this;
1354
1444
  public rgba(address: bigint, value?: RGBA, force?: boolean): RGBA | this {
1355
- const Scratch4 = this.Scratch4;
1445
+ const { Scratch4 } = this;
1356
1446
 
1357
1447
  if (value === undefined) {
1358
1448
  void this.read(address, Scratch4);
@@ -1389,7 +1479,6 @@ class Memory {
1389
1479
  * ```
1390
1480
  */
1391
1481
  public rgbaRaw(address: bigint): Uint8Array;
1392
- public rgbaRaw(address: bigint): Uint8Array;
1393
1482
  public rgbaRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1394
1483
  public rgbaRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1395
1484
  if (values === undefined) {
@@ -1421,7 +1510,6 @@ class Memory {
1421
1510
  * ```
1422
1511
  */
1423
1512
  public string(address: bigint, length: number): string;
1424
- public string(address: bigint, length: number): string;
1425
1513
  public string(address: bigint, value: string, force?: boolean): this;
1426
1514
  public string(address: bigint, lengthOrValue: number | string, force?: boolean): string | this {
1427
1515
  if (typeof lengthOrValue === 'number') {
@@ -1461,7 +1549,7 @@ class Memory {
1461
1549
  public u16(address: bigint): number;
1462
1550
  public u16(address: bigint, value: number, force?: boolean): this;
1463
1551
  public u16(address: bigint, value?: number, force?: boolean): number | this {
1464
- const Scratch2Uint16Array = this.Scratch2Uint16Array;
1552
+ const { Scratch2Uint16Array } = this;
1465
1553
 
1466
1554
  if (value === undefined) {
1467
1555
  return this.read(address, Scratch2Uint16Array)[0x00];
@@ -1522,7 +1610,7 @@ class Memory {
1522
1610
  public u32(address: bigint): number;
1523
1611
  public u32(address: bigint, value: number, force?: boolean): this;
1524
1612
  public u32(address: bigint, value?: number, force?: boolean): number | this {
1525
- const Scratch4Uint32Array = this.Scratch4Uint32Array;
1613
+ const { Scratch4Uint32Array } = this;
1526
1614
 
1527
1615
  if (value === undefined) {
1528
1616
  return this.read(address, Scratch4Uint32Array)[0x00];
@@ -1583,7 +1671,7 @@ class Memory {
1583
1671
  public u64(address: bigint): bigint;
1584
1672
  public u64(address: bigint, value: bigint, force?: boolean): this;
1585
1673
  public u64(address: bigint, value?: bigint, force?: boolean): bigint | this {
1586
- const Scratch8BigUint64Array = this.Scratch8BigUint64Array;
1674
+ const { Scratch8BigUint64Array } = this;
1587
1675
 
1588
1676
  if (value === undefined) {
1589
1677
  return this.read(address, Scratch8BigUint64Array)[0x00];
@@ -1644,7 +1732,7 @@ class Memory {
1644
1732
  public u8(address: bigint): number;
1645
1733
  public u8(address: bigint, value: number, force?: boolean): this;
1646
1734
  public u8(address: bigint, value?: number, force?: boolean): number | this {
1647
- const Scratch1 = this.Scratch1;
1735
+ const { Scratch1 } = this;
1648
1736
 
1649
1737
  if (value === undefined) {
1650
1738
  return this.read(address, Scratch1)[0x00];
@@ -1998,7 +2086,7 @@ class Memory {
1998
2086
  public vector3(address: bigint): Vector3;
1999
2087
  public vector3(address: bigint, value: Vector3, force?: boolean): this;
2000
2088
  public vector3(address: bigint, value?: Vector3, force?: boolean): Vector3 | this {
2001
- const Scratch12Float32Array = this.Scratch12Float32Array;
2089
+ const { Scratch12Float32Array } = this;
2002
2090
 
2003
2091
  if (value === undefined) {
2004
2092
  void this.read(address, Scratch12Float32Array);
@@ -2147,7 +2235,6 @@ class Memory {
2147
2235
  return this.quaternionArray(address, lengthOrValues, force);
2148
2236
  }
2149
2237
 
2150
- public vector4Raw(address: bigint): Float32Array;
2151
2238
  /**
2152
2239
  * Reads or writes a raw Vector4 as a Float32Array.
2153
2240
  * @param address Address to access.
@@ -2277,7 +2364,7 @@ class Memory {
2277
2364
  public pattern(needle: string, address: bigint, length: number, all: false): bigint;
2278
2365
  public pattern(needle: string, address: bigint, length: number, all: true): bigint[];
2279
2366
  public pattern(needle: string, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
2280
- const test = Memory.Patterns.Test.test(needle);
2367
+ const test = Memory.Patterns.PatternTest.test(needle);
2281
2368
 
2282
2369
  if (!test) {
2283
2370
  return !all ? -1n : [];
@@ -2285,7 +2372,7 @@ class Memory {
2285
2372
 
2286
2373
  // The RegExp test ensures that we have at least one token…
2287
2374
 
2288
- const tokens = [...needle.matchAll(Memory.Patterns.MatchAll)]
2375
+ const tokens = [...needle.matchAll(Memory.Patterns.PatternMatchAll)]
2289
2376
  .map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
2290
2377
  .sort(({ length: a }, { length: b }) => b - a);
2291
2378
 
package/types/Memory.ts CHANGED
@@ -1,3 +1,34 @@
1
+ import type { FFIType, Pointer } from 'bun:ffi';
2
+
3
+ export type CallResult<R extends FFIType> = R extends typeof FFIType.bool
4
+ ? boolean
5
+ : R extends typeof FFIType.f32 | typeof FFIType.f64 | typeof FFIType.i8 | typeof FFIType.i16 | typeof FFIType.i32 | typeof FFIType.u8 | typeof FFIType.u16 | typeof FFIType.u32
6
+ ? number
7
+ : R extends typeof FFIType.i64 | typeof FFIType.u64
8
+ ? bigint
9
+ : R extends typeof FFIType.cstring | typeof FFIType.ptr
10
+ ? bigint | Pointer
11
+ : void;
12
+
13
+ export type CallSignature = {
14
+ args: (
15
+ | { type: typeof FFIType.bool; value: boolean }
16
+ | { type: typeof FFIType.cstring; value: ArrayBuffer | ArrayBufferView | bigint | Buffer | null | number | string }
17
+ | { type: typeof FFIType.f32; value: number }
18
+ | { type: typeof FFIType.f64; value: number }
19
+ | { type: typeof FFIType.i8; value: number }
20
+ | { type: typeof FFIType.i16; value: number }
21
+ | { type: typeof FFIType.i32; value: number }
22
+ | { type: typeof FFIType.i64; value: bigint }
23
+ | { type: typeof FFIType.ptr; value: ArrayBuffer | ArrayBufferView | bigint | Buffer | null | number | Pointer }
24
+ | { type: typeof FFIType.u8; value: number }
25
+ | { type: typeof FFIType.u16; value: number }
26
+ | { type: typeof FFIType.u32; value: number }
27
+ | { type: typeof FFIType.u64; value: bigint }
28
+ )[];
29
+ returns: FFIType;
30
+ };
31
+
1
32
  /**
2
33
  * Represents a loaded module in a process.
3
34
  * @property base Base address of the module.