bun-memory 1.1.41 → 1.1.43

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