bun-memory 1.0.2 → 1.1.0

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/structs/Memory.ts CHANGED
@@ -1,25 +1,18 @@
1
- /**
2
- * Windows process memory utilities for Bun using `bun:ffi` and Win32 APIs.
3
- *
4
- * This module exposes a {@link Memory} class that can attach to a running process by name
5
- * and perform high-performance reads and writes directly against the target process'
6
- * virtual address space. It wraps selected Kernel32 functions via FFI and provides
7
- * strongly-typed helpers for common primitives and patterns.
8
- *
9
- * @remarks
10
- * - Requires Windows (uses `kernel32.dll`).
11
- * - Runs under Bun (uses `bun:ffi`).
12
- * - Use with appropriate privileges; many operations require administrator rights.
13
- */
1
+ // TODO: Reintroduce findPattern(…)…
2
+ // TODO: Reintroduce indexOf(…)…
3
+ // TODO: String methods…
14
4
 
15
- import { dlopen, FFIType } from 'bun:ffi';
5
+ import { FFIType, dlopen, read } from 'bun:ffi';
16
6
 
7
+ import type { Module, Quaternion, Region, Scratch, Vector2, Vector3 } from '../types/Memory';
17
8
  import Win32Error from './Win32Error';
18
9
 
10
+ const { f32, f64, i16, i32, i64, i8, u16, u32, u64, u8 } = read;
11
+
19
12
  /**
20
- * Minimal Kernel32 FFI surface used by this module.
13
+ * Kernel32 Windows API functions imported via Foreign Function Interface (FFI).
14
+ * These functions provide low-level access to process and memory management operations.
21
15
  */
22
-
23
16
  const { symbols: Kernel32 } = dlopen('kernel32.dll', {
24
17
  CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
25
18
  CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
@@ -29,83 +22,53 @@ const { symbols: Kernel32 } = dlopen('kernel32.dll', {
29
22
  OpenProcess: { args: [FFIType.u32, FFIType.bool, FFIType.u32], returns: FFIType.u64 },
30
23
  Process32FirstW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
31
24
  Process32NextW: { args: [FFIType.u64, FFIType.ptr], returns: FFIType.bool },
32
- ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
25
+ ReadProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
33
26
  VirtualProtectEx: { args: [FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u32, FFIType.ptr], returns: FFIType.bool },
34
27
  VirtualQueryEx: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
35
- WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.bool },
28
+ WriteProcessMemory: { args: [FFIType.u64, FFIType.u64, FFIType.ptr, FFIType.u64_fast, FFIType.ptr], returns: FFIType.bool },
36
29
  });
37
30
 
38
31
  /**
39
- * Loaded module metadata captured via Toolhelp APIs.
40
- */
41
-
42
- type Module = {
43
- modBaseAddr: bigint;
44
- modBaseSize: number;
45
- szModule: string;
46
- };
47
-
48
- /**
49
- * Three-dimensional vector laid out as three 32‑bit floats (x, y, z).
50
- */
51
-
52
- type Vector3 = {
53
- x: number;
54
- y: number;
55
- z: number;
56
- };
57
-
58
- /**
59
- * Memory region information derived from `MEMORY_BASIC_INFORMATION`.
60
- */
61
-
62
- type Region = {
63
- base: bigint;
64
- protect: number;
65
- size: bigint;
66
- state: number;
67
- type: number;
68
- };
69
-
70
- /**
71
- * Attaches to a Windows process and provides safe, typed memory accessors.
32
+ * Memory class provides cross-process memory manipulation capabilities on Windows systems.
33
+ * This class allows reading from and writing to memory addresses in external processes,
34
+ * supporting various data types including primitives, arrays, and custom structures like vectors and quaternions.
72
35
  *
73
36
  * @example
74
- * ```ts
75
- * const memory = new Memory("strounter-cike.exe");
76
- *
77
- * const clientDLL = memory.modules['client.dll'];
37
+ * ```typescript
38
+ * // Connect to a process by name
39
+ * const memory = new Memory('notepad.exe');
78
40
  *
79
- * if(clientDLL === undefined) {
80
- * // ...
81
- * }
41
+ * // Connect to a process by ID
42
+ * const memory = new Memory(1234);
82
43
  *
83
- * // Write to your anmo...
84
- * const ammoOffset = 0xabcdefn;
85
- * memory.writeUInt32(clientDLL.modBaseAddr + ammoOffset, 9_999);
44
+ * // Read a 32-bit integer from memory
45
+ * const value = memory.i32(0x12345678n);
86
46
  *
87
- * // Read your health...
88
- * const healthOffset = 0x123456n;
89
- * const health = memory.readUInt32LE(clientDLL.modBaseAddr + healthOffset);
90
- * console.log('You have %d health...', health);
91
- *
92
- * // Find an offset by pattern...
93
- * const otherOffset = memory.findPattern('aa??bbccdd??ff', clientDLL.modBaseAddr, clientDLL.modBaseSize);
94
- * const otherValue = memory.readBoolean(otherOffset + 0x1234n);
47
+ * // Write a float to memory
48
+ * memory.f32(0x12345678n, 3.14159);
95
49
  *
50
+ * // Clean up when done
96
51
  * memory.close();
97
52
  * ```
98
53
  */
99
-
100
54
  class Memory {
101
55
  /**
102
- * Create a new {@link Memory} instance by process image name.
56
+ * Creates a new Memory instance and attaches to the specified process.
57
+ *
58
+ * @param identifier - Either a process ID (number) or process name (string)
59
+ * @throws {Win32Error} When process operations fail
60
+ * @throws {Error} When the specified process is not found
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Attach to process by name
65
+ * const memory = new Memory('calculator.exe');
103
66
  *
104
- * @param name Fully-qualified process image name (e.g. `"notepad.exe"`).
105
- * @throws {Win32Error} If enumerating processes or opening the process fails.
106
- * @throws {Error} If the process cannot be found.
67
+ * // Attach to process by ID
68
+ * const memory = new Memory(5432);
69
+ * ```
107
70
  */
108
- constructor(name: string) {
71
+ constructor(identifier: number | string) {
109
72
  const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
110
73
  const th32ProcessID = 0;
111
74
 
@@ -128,112 +91,135 @@ class Memory {
128
91
 
129
92
  do {
130
93
  const szExeFile = lppe.toString('utf16le', 0x2c, 0x234).replace(/\0+$/, '');
94
+ const th32ProcessID = lppe.readUInt32LE(0x08);
131
95
 
132
- if (name === szExeFile) {
133
- const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
134
- const inheritHandle = false;
135
- const th32ProcessID = lppe.readUInt32LE(0x08);
96
+ if (
97
+ (typeof identifier === 'number' && identifier !== th32ProcessID) || //
98
+ (typeof identifier === 'string' && identifier !== szExeFile)
99
+ ) {
100
+ continue;
101
+ }
136
102
 
137
- const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
103
+ const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
104
+ const inheritHandle = false;
138
105
 
139
- if (hProcess === 0n) {
140
- Kernel32.CloseHandle(hSnapshot);
106
+ const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
141
107
 
142
- throw new Win32Error('OpenProcess', Kernel32.GetLastError());
143
- }
108
+ if (hProcess === 0n) {
109
+ Kernel32.CloseHandle(hSnapshot);
144
110
 
145
- this.szModule = szExeFile;
146
- this.hProcess = hProcess;
147
- this.th32ProcessID = th32ProcessID;
111
+ throw new Win32Error('OpenProcess', Kernel32.GetLastError());
112
+ }
148
113
 
149
- this.refresh();
114
+ this._modules = {};
115
+ this.hProcess = hProcess;
116
+ this.th32ProcessID = th32ProcessID;
150
117
 
151
- Kernel32.CloseHandle(hSnapshot);
118
+ this.refresh();
152
119
 
153
- return;
154
- }
120
+ Kernel32.CloseHandle(hSnapshot);
121
+
122
+ return;
155
123
  } while (Kernel32.Process32NextW(hSnapshot, lppe));
156
124
 
157
125
  Kernel32.CloseHandle(hSnapshot);
158
126
 
159
- throw new Error(`Process not found: ${name}…`);
127
+ throw new Error(`Process not found: ${identifier}…`);
160
128
  }
161
129
 
162
- // Properties…
163
-
130
+ /**
131
+ * Memory protection constants used for determining safe memory regions.
132
+ * Safe regions can be read from or written to, while unsafe regions should be avoided.
133
+ */
164
134
  private static readonly MemoryProtections = {
165
135
  Safe: 0x10 /* PAGE_EXECUTE */ | 0x20 /* PAGE_EXECUTE_READ */ | 0x40 /* PAGE_EXECUTE_READWRITE */ | 0x80 /* PAGE_EXECUTE_WRITECOPY */ | 0x02 /* PAGE_READONLY */ | 0x04 /* PAGE_READWRITE */ | 0x08 /* PAGE_WRITECOPY */,
166
136
  Unsafe: 0x100 /* PAGE_GUARD */ | 0x01 /* PAGE_NOACCESS */,
167
137
  };
168
138
 
169
- private static readonly PatternMatchAll = /([0-9A-Fa-f]{2})+/g;
170
- private static readonly PatternTest = /^(?:[0-9A-Fa-f]{2}|\?{2})+$/;
139
+ /**
140
+ * Internal storage for loaded modules information.
141
+ */
142
+ private _modules: { [key: string]: Module };
171
143
 
172
- private static readonly Scratch1 = Buffer.allocUnsafe(0x01);
173
- private static readonly Scratch2 = Buffer.allocUnsafe(0x02);
174
- private static readonly Scratch4 = Buffer.allocUnsafe(0x04);
175
- private static readonly Scratch4_2 = Buffer.allocUnsafe(0x04);
176
- private static readonly Scratch8 = Buffer.allocUnsafe(0x08);
177
- private static readonly Scratch12 = Buffer.allocUnsafe(0x0c);
144
+ /**
145
+ * Pre-allocated scratch buffers for memory operations to avoid repeated allocations.
146
+ * These buffers are reused across multiple read/write operations for performance.
147
+ */
148
+ private readonly Scratch1 = new Uint8Array(0x01);
149
+ private readonly Scratch2 = new Uint8Array(0x02);
150
+ private readonly Scratch4 = new Uint8Array(0x04);
151
+ private readonly Scratch8 = new Uint8Array(0x08);
152
+ private readonly Scratch12 = new Uint8Array(0x0c);
153
+ private readonly Scratch16 = new Uint8Array(0x10);
178
154
 
155
+ /**
156
+ * Buffer views of the scratch arrays for easier data manipulation.
157
+ */
158
+ private readonly Scratch1Buffer = Buffer.from(this.Scratch1.buffer, this.Scratch1.byteOffset, this.Scratch1.byteLength);
159
+ private readonly Scratch2Buffer = Buffer.from(this.Scratch2.buffer, this.Scratch2.byteOffset, this.Scratch2.byteLength);
160
+ private readonly Scratch4Buffer = Buffer.from(this.Scratch4.buffer, this.Scratch4.byteOffset, this.Scratch4.byteLength);
161
+ private readonly Scratch8Buffer = Buffer.from(this.Scratch8.buffer, this.Scratch8.byteOffset, this.Scratch8.byteLength);
162
+ private readonly Scratch12Buffer = Buffer.from(this.Scratch12.buffer, this.Scratch12.byteOffset, this.Scratch12.byteLength);
163
+ private readonly Scratch16Buffer = Buffer.from(this.Scratch16.buffer, this.Scratch16.byteOffset, this.Scratch16.byteLength);
164
+
165
+ /**
166
+ * Scratch buffers for Windows API structures.
167
+ */
179
168
  private readonly ScratchMemoryBasicInformation = Buffer.allocUnsafe(0x30 /* sizeof(MEMORY_BASIC_INFORMATION) */);
180
169
  private readonly ScratchModuleEntry32W = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
181
170
 
182
- private _modules!: { [key: string]: Module };
183
-
184
171
  /**
185
- * Native process handle returned by `OpenProcess`.
172
+ * Handle to the target process.
186
173
  */
187
-
188
- public readonly hProcess: bigint;
189
- public readonly szModule: string;
174
+ private readonly hProcess: bigint;
190
175
 
191
176
  /**
192
- * Target process identifier (PID).
177
+ * Process ID of the target process.
193
178
  */
194
-
195
- public readonly th32ProcessID: number;
196
-
197
- public get modBaseAddr(): Memory['_modules'][string]['modBaseAddr'] {
198
- return this.modules[this.szModule].modBaseAddr;
199
- }
200
-
201
- public get modBaseSize(): Memory['_modules'][string]['modBaseSize'] {
202
- return this.modules[this.szModule].modBaseSize;
203
- }
179
+ private readonly th32ProcessID: number;
204
180
 
205
181
  /**
206
- * Snapshot of modules loaded in the target process.
182
+ * Gets the loaded modules for the target process.
183
+ *
184
+ * @returns A frozen object containing module information indexed by module name
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * const memory = new Memory('notepad.exe');
189
+ * const modules = memory.modules;
207
190
  *
208
- * @remarks Call {@link refresh} to update this list.
191
+ * // Access a specific module
192
+ * const mainModule = modules['notepad.exe'];
193
+ * console.log(`Base address: 0x${mainModule.base.toString(16)}`);
194
+ * console.log(`Size: ${mainModule.size} bytes`);
195
+ * ```
209
196
  */
210
-
211
197
  public get modules(): Memory['_modules'] {
212
198
  return this._modules;
213
199
  }
214
200
 
215
- // Methods…
201
+ // Internal methods
216
202
 
217
203
  /**
218
- * Close the underlying process handle.
219
- */
220
-
221
- public close(): void {
222
- Kernel32.CloseHandle(this.hProcess);
223
-
224
- return;
225
- }
226
-
227
- /**
228
- * Enumerate committed, readable/executable memory regions within the given range.
204
+ * Retrieves memory region information for a specified address range.
205
+ * This method queries the virtual memory layout to identify safe regions for memory operations.
206
+ *
207
+ * @param address - Starting memory address
208
+ * @param length - Length of the memory range to query
209
+ * @returns Array of Region objects describing the memory layout
210
+ * @throws {Win32Error} When memory query operations fail
229
211
  *
230
- * @param address Start address for the query.
231
- * @param length Number of bytes to cover from `address`.
232
- * @returns Array of safe regions intersecting the requested range.
233
- * @private
212
+ * @example
213
+ * ```typescript
214
+ * const memory = new Memory('notepad.exe');
215
+ * const regions = memory.regions(0x10000000n, 0x1000n);
216
+ *
217
+ * regions.forEach(region => {
218
+ * console.log(`Region: 0x${region.base.toString(16)} - Size: ${region.size}`);
219
+ * });
220
+ * ```
234
221
  */
235
-
236
- private regions(address: bigint | number, length: bigint | number): Region[] {
222
+ private regions(address: bigint, length: bigint | number): Region[] {
237
223
  const dwLength = 0x30; /* sizeof(MEMORY_BASIC_INFORMATION) */
238
224
  let lpAddress = BigInt(address); // prettier-ignore
239
225
  const lpBuffer = this.ScratchMemoryBasicInformation;
@@ -264,1035 +250,1225 @@ class Memory {
264
250
  return result;
265
251
  }
266
252
 
253
+ // Core memory operations
254
+
267
255
  /**
268
- * Refresh the list of loaded modules for the target process.
256
+ * Reads data from the target process memory into a scratch buffer.
257
+ * This is a low-level method used internally by the typed read methods.
269
258
  *
270
- * @throws {Win32Error} If Toolhelp snapshots cannot be created or iterated.
271
- */
272
-
273
- public refresh(): void {
274
- const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
259
+ * @param address - Memory address to read from
260
+ * @param scratch - Buffer to store the read data
261
+ * @returns This Memory instance for method chaining
262
+ * @throws {Win32Error} When the read operation fails
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const memory = new Memory('notepad.exe');
267
+ * const buffer = new Uint8Array(4);
268
+ * memory.read(0x12345678n, buffer);
269
+ * ```
270
+ */
271
+ public read(address: bigint, scratch: Scratch): this {
272
+ const lpBaseAddress = address;
273
+ const lpBuffer = scratch.ptr;
274
+ const nSize = scratch.byteLength;
275
+ const numberOfBytesRead = 0x00n;
275
276
 
276
- const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
277
+ const bReadProcessMemory = Kernel32.ReadProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
277
278
 
278
- if (hSnapshot === -1n) {
279
- throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
279
+ if (!bReadProcessMemory) {
280
+ throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
280
281
  }
281
282
 
282
- const lpme = this.ScratchModuleEntry32W;
283
- /* */ lpme.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
283
+ return this;
284
+ }
284
285
 
285
- const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, lpme);
286
+ /**
287
+ * Writes data from a scratch buffer to the target process memory.
288
+ * This is a low-level method used internally by the typed write methods.
289
+ *
290
+ * @param address - Memory address to write to
291
+ * @param scratch - Buffer containing the data to write
292
+ * @throws {Win32Error} When the write operation fails
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * const memory = new Memory('notepad.exe');
297
+ * const buffer = new Uint8Array([0x41, 0x42, 0x43, 0x44]);
298
+ * memory.write(0x12345678n, buffer);
299
+ * ```
300
+ */
301
+ private write(address: bigint, scratch: Scratch): void {
302
+ const lpBaseAddress = address;
303
+ const lpBuffer = scratch.ptr;
304
+ const nSize = scratch.byteLength;
305
+ const numberOfBytesWritten = 0x00n;
286
306
 
287
- if (!bModule32FirstW) {
288
- Kernel32.CloseHandle(hSnapshot);
307
+ const WriteProcessMemory = Kernel32.WriteProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
289
308
 
290
- throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
309
+ if (!WriteProcessMemory) {
310
+ throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
291
311
  }
292
312
 
293
- const modules: Memory['_modules'] = {};
294
-
295
- do {
296
- const modBaseAddr = lpme.readBigUInt64LE(0x18);
297
- const modBaseSize = lpme.readUInt32LE(0x20);
298
- const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
299
-
300
- modules[szModule] = { modBaseAddr, modBaseSize, szModule };
301
- } while (Kernel32.Module32NextW(hSnapshot, lpme));
302
-
303
- Kernel32.CloseHandle(hSnapshot);
304
-
305
- this._modules = Object.freeze(modules);
306
-
307
313
  return;
308
314
  }
309
315
 
310
- // Private Methods…
311
-
312
- // QoL methods…
316
+ // Public utility methods
313
317
 
314
318
  /**
315
- * Scan memory for a hex signature with wildcard support.
319
+ * Closes the handle to the target process and releases resources.
320
+ * This method should be called when the Memory instance is no longer needed.
316
321
  *
317
- * @param needle Hex pattern using pairs of hex digits; use `??` as a byte wildcard.
318
- * Whitespace is not permitted (e.g. `"48895C24??48896C24??"`).
319
- * @param address Start address to begin scanning.
320
- * @param length Number of bytes to scan from `address`.
321
- * @returns Address of the first match, or `-1n` if not found.
322
+ * @example
323
+ * ```typescript
324
+ * const memory = new Memory('notepad.exe');
325
+ * // ... perform memory operations
326
+ * memory.close(); // Clean up resources
327
+ * ```
322
328
  */
329
+ public close(): void {
330
+ Kernel32.CloseHandle(this.hProcess);
323
331
 
324
- public findPattern(needle: string, address: bigint | number, length: bigint | number): bigint {
325
- const { PatternMatchAll, PatternTest } = Memory;
326
-
327
- address = BigInt(address);
328
- length = BigInt(length);
329
-
330
- const test = PatternTest.test(needle);
331
-
332
- if (!test) {
333
- return -1n;
334
- }
335
-
336
- const actualEnd = address + length;
337
- const actualStart = address;
338
-
339
- const needleLength = needle.length >>> 1;
340
-
341
- const [anchor, ...tokens] = [...needle.matchAll(PatternMatchAll)] //
342
- .map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 }))
343
- .sort(({ buffer: { length: a } }, { buffer: { length: b } }) => b - a);
344
-
345
- const regions = this.regions(address, length);
346
-
347
- for (const region of regions) {
348
- const regionEnd = region.base + region.size;
349
- const regionStart = region.base;
350
-
351
- const scanEnd = regionEnd < actualEnd ? regionEnd : actualEnd;
352
- const scanStart = regionStart > actualStart ? regionStart : actualStart;
353
-
354
- const scanLength = scanEnd - scanStart;
355
-
356
- if (needleLength > scanLength) {
357
- continue;
358
- }
359
-
360
- const haystack = this.readBuffer(scanStart, scanLength);
361
-
362
- let indexOf = haystack.indexOf(anchor.buffer, anchor.index);
363
-
364
- if (indexOf === -1) {
365
- continue;
366
- }
367
-
368
- const lastStart = scanLength - BigInt(needleLength);
369
-
370
- outer: do {
371
- const matchStart = indexOf - anchor.index;
372
-
373
- if (lastStart < matchStart) {
374
- break;
375
- }
376
-
377
- for (const token of tokens) {
378
- const sourceEnd = matchStart + token.index + token.length;
379
- const sourceStart = matchStart + token.index;
380
-
381
- const targetEnd = token.length;
382
- const targetStart = 0;
383
-
384
- const compare = haystack.compare(token.buffer, targetStart, targetEnd, sourceStart, sourceEnd);
385
-
386
- if (compare !== 0) {
387
- indexOf = haystack.indexOf(anchor.buffer, indexOf + 1);
388
- continue outer;
389
- }
390
- }
391
-
392
- return scanStart + BigInt(matchStart);
393
- } while (indexOf !== -1);
394
- }
395
-
396
- return -1n;
332
+ return;
397
333
  }
398
334
 
399
335
  /**
400
- * Search memory for a sequence of bytes or a string.
336
+ * Refreshes the list of modules loaded in the target process.
337
+ * This method should be called if modules are loaded or unloaded during runtime.
401
338
  *
402
- * @param needle A `Uint8Array`, number, or string to locate.
403
- * @param address Start address.
404
- * @param length Number of bytes to search.
405
- * @param encoding Optional encoding when `needle` is a string.
406
- * @returns Address of the first match, or `-1n` if not found.
339
+ * @throws {Win32Error} When module enumeration fails
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * const memory = new Memory('notepad.exe');
344
+ *
345
+ * // Initial modules
346
+ * console.log('Initial modules:', Object.keys(memory.modules));
347
+ *
348
+ * // After some time, refresh to get updated module list
349
+ * memory.refresh();
350
+ * console.log('Updated modules:', Object.keys(memory.modules));
351
+ * ```
407
352
  */
353
+ public refresh(): void {
354
+ const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
408
355
 
409
- public indexOf(needle: Uint8Array | number | string, address: bigint | number, length: bigint | number, encoding?: BufferEncoding): bigint {
410
- address = BigInt(address);
411
- length = BigInt(length);
412
-
413
- const regions = this.regions(address, length);
356
+ const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
414
357
 
415
- for (const { base, size } of regions) {
416
- const address_ = address > base ? address : base;
358
+ if (hSnapshot === -1n) {
359
+ throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
360
+ }
417
361
 
418
- const haystack = this.readBuffer(address_, base + size - address_);
419
- const indexOf = haystack.indexOf(needle, 0, encoding);
362
+ this.ScratchModuleEntry32W.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
420
363
 
421
- if (indexOf === -1) {
422
- continue;
423
- }
364
+ const lpme = this.ScratchModuleEntry32W.ptr;
424
365
 
425
- return address_ + BigInt(indexOf);
426
- }
366
+ const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, lpme);
427
367
 
428
- return -1n;
429
- }
368
+ if (!bModule32FirstW) {
369
+ Kernel32.CloseHandle(hSnapshot);
430
370
 
431
- // Read/write methods…
371
+ throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
372
+ }
432
373
 
433
- /**
434
- * Read a contiguous block as a `BigInt64Array`.
435
- *
436
- * @param address Source address.
437
- * @param length Element count (not bytes).
438
- * @param scratch Optional destination buffer to avoid allocations.
439
- * @returns View over the backing buffer as `BigInt64Array`.
440
- */
374
+ const modules: Memory['_modules'] = {};
441
375
 
442
- public readBigInt64Array(address: bigint | number, length: number, scratch?: Buffer): BigInt64Array {
443
- const buffer = this.readBuffer(address, length * 8, scratch);
376
+ do {
377
+ const modBaseAddr = this.ScratchModuleEntry32W.readBigUInt64LE(0x18);
378
+ const modBaseSize = this.ScratchModuleEntry32W.readUInt32LE(0x20);
379
+ const szModule = this.ScratchModuleEntry32W.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
444
380
 
445
- const bigUInt64Array = new BigInt64Array(buffer.buffer, buffer.byteOffset, length);
381
+ modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
382
+ } while (Kernel32.Module32NextW(hSnapshot, lpme));
446
383
 
447
- return bigUInt64Array;
448
- }
384
+ Kernel32.CloseHandle(hSnapshot);
449
385
 
450
- /**
451
- * Read a signed 64‑bit big-endian integer.
452
- * @param address Source address.
453
- */
386
+ this._modules = Object.freeze(modules);
454
387
 
455
- public readBigInt64BE(address: bigint | number): bigint {
456
- return this.readBuffer(address, 8).readBigInt64BE();
388
+ return;
457
389
  }
458
390
 
459
- /**
460
- * Read a signed 64‑bit little-endian integer.
461
- * @param address Source address.
462
- */
463
-
464
- public readBigInt64LE(address: bigint | number): bigint {
465
- return this.readBuffer(address, 8).readBigInt64LE();
466
- }
391
+ // Typed read/write methods
467
392
 
468
393
  /**
469
- * Read a contiguous block as a `BigUint64Array`.
394
+ * Reads a boolean value from memory or writes a boolean value to memory.
470
395
  *
471
- * @param address Source address.
472
- * @param length Element count (not bytes).
473
- * @param scratch Optional destination buffer.
474
- * @returns View over the backing buffer as `BigUint64Array`.
475
- */
476
-
477
- public readBigUint64Array(address: bigint | number, length: number, scratch?: Buffer): BigUint64Array {
478
- const buffer = this.readBuffer(address, length * 8, scratch);
479
-
480
- const bigUInt64Array = new BigUint64Array(buffer.buffer, buffer.byteOffset, length);
481
-
482
- return bigUInt64Array;
483
- }
484
-
485
- /**
486
- * Read an unsigned 64‑bit big-endian integer.
487
- * @param address Source address.
396
+ * @param address - Memory address to read from or write to
397
+ * @param value - Optional value to write. If omitted, performs a read operation
398
+ * @returns The boolean value when reading, or this Memory instance when writing
399
+ *
400
+ * @example
401
+ * ```typescript
402
+ * const memory = new Memory('game.exe');
403
+ *
404
+ * // Read a boolean value
405
+ * const isAlive = memory.bool(0x12345678n);
406
+ * console.log('Player is alive:', isAlive);
407
+ *
408
+ * // Write a boolean value
409
+ * memory.bool(0x12345678n, true);
410
+ * ```
488
411
  */
412
+ public bool(address: bigint): boolean;
413
+ public bool(address: bigint, value: boolean): this;
414
+ public bool(address: bigint, value?: boolean): boolean | this {
415
+ if (value === undefined) {
416
+ this.read(address, this.Scratch1);
489
417
 
490
- public readBigUInt64BE(address: bigint | number): bigint {
491
- return this.readBuffer(address, 0x08, Memory.Scratch8).readBigUInt64BE();
492
- }
418
+ return u8(this.Scratch1.ptr) !== 0;
419
+ }
493
420
 
494
- /**
495
- * Read an unsigned 64‑bit little-endian integer.
496
- * @param address Source address.
497
- */
421
+ this.Scratch1Buffer.writeUInt8(+value);
498
422
 
499
- public readBigUInt64LE(address: bigint | number): bigint {
500
- return this.readBuffer(address, 0x08, Memory.Scratch8).readBigUInt64LE();
501
- }
423
+ this.write(address, this.Scratch1);
502
424
 
503
- /**
504
- * Read a boolean value (non-zero -> `true`).
505
- * @param address Source address.
506
- */
507
-
508
- public readBoolean(address: bigint | number): boolean {
509
- return Boolean(this.readUInt8(address));
425
+ return this;
510
426
  }
511
427
 
512
428
  /**
513
- * Read raw bytes from the target process.
429
+ * Reads a 32-bit floating-point value from memory or writes a 32-bit floating-point value to memory.
430
+ *
431
+ * @param address - Memory address to read from or write to
432
+ * @param value - Optional value to write. If omitted, performs a read operation
433
+ * @returns The float value when reading, or this Memory instance when writing
514
434
  *
515
- * @param address Source address.
516
- * @param length Number of bytes to read.
517
- * @param scratch Optional Buffer to reuse for improved performance.
518
- * @returns Buffer containing the bytes read.
519
- * @throws {Win32Error} If `ReadProcessMemory` fails.
435
+ * @example
436
+ * ```typescript
437
+ * const memory = new Memory('game.exe');
438
+ *
439
+ * // Read a float value
440
+ * const playerHealth = memory.f32(0x12345678n);
441
+ * console.log('Player health:', playerHealth);
442
+ *
443
+ * // Write a float value
444
+ * memory.f32(0x12345678n, 100.0);
445
+ * ```
520
446
  */
447
+ public f32(address: bigint): number;
448
+ public f32(address: bigint, value: number): this;
449
+ public f32(address: bigint, value?: number): number | this {
450
+ if (value === undefined) {
451
+ this.read(address, this.Scratch4);
521
452
 
522
- public readBuffer(address: bigint | number, length: bigint | number, scratch?: Buffer): Buffer {
523
- const { hProcess } = this;
524
-
525
- address = BigInt(address);
526
- length = BigInt(length);
527
-
528
- const lpBaseAddress = address;
529
- const lpBuffer = scratch ?? Buffer.allocUnsafe(Number(length));
530
- const nSize = length;
531
- const numberOfBytesRead = 0x00n;
532
-
533
- const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
534
-
535
- if (!bReadProcessMemory) {
536
- throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
453
+ return f32(this.Scratch4.ptr);
537
454
  }
538
455
 
539
- return lpBuffer;
540
- }
541
-
542
- /**
543
- * Read a 64‑bit big-endian IEEE-754 float.
544
- * @param address Source address.
545
- */
456
+ this.Scratch4Buffer.writeFloatLE(value);
546
457
 
547
- public readDoubleBE(address: bigint | number): number {
548
- return this.readBuffer(address, 0x08, Memory.Scratch8).readDoubleBE();
549
- }
550
-
551
- /**
552
- * Read a 64‑bit little-endian IEEE-754 float.
553
- * @param address Source address.
554
- */
458
+ this.write(address, this.Scratch4);
555
459
 
556
- public readDoubleLE(address: bigint | number): number {
557
- return this.readBuffer(address, 0x08, Memory.Scratch8).readDoubleLE();
460
+ return this;
558
461
  }
559
462
 
560
- // + TODO: Implement scratch…
561
-
562
463
  /**
563
- * Read a contiguous block as a `Float32Array`.
464
+ * Reads an array of 32-bit floating-point values from memory or writes an array of 32-bit floating-point values to memory.
465
+ *
466
+ * @param address - Memory address to read from or write to
467
+ * @param lengthOrValues - Length of array to read, or array of values to write
468
+ * @returns Float32Array when reading, or this Memory instance when writing
564
469
  *
565
- * @param address Source address.
566
- * @param length Element count (not bytes).
567
- * @param scratch Optional Buffer to reuse for improved performance.
568
- * @returns View over the backing buffer as `Float32Array`.
470
+ * @example
471
+ * ```typescript
472
+ * const memory = new Memory('game.exe');
473
+ *
474
+ * // Read an array of 10 float values
475
+ * const coordinates = memory.f32Array(0x12345678n, 10);
476
+ * console.log('Coordinates:', coordinates);
477
+ *
478
+ * // Write an array of float values
479
+ * const newCoordinates = new Float32Array([1.0, 2.5, 3.14, 4.2]);
480
+ * memory.f32Array(0x12345678n, newCoordinates);
481
+ * ```
569
482
  */
483
+ public f32Array(address: bigint, length: number): Float32Array;
484
+ public f32Array(address: bigint, values: Float32Array): this;
485
+ public f32Array(address: bigint, lengthOrValues: Float32Array | number): Float32Array | this {
486
+ if (typeof lengthOrValues === 'number') {
487
+ const length = lengthOrValues;
488
+ const scratch = new Float32Array(length);
570
489
 
571
- public readFloat32Array(address: bigint | number, length: number, scratch?: Buffer): Float32Array {
572
- const buffer = this.readBuffer(address, length * 0x04, scratch);
490
+ this.read(address, scratch);
573
491
 
574
- const float32Array = new Float32Array(buffer.buffer, buffer.byteOffset, length);
575
-
576
- return float32Array;
577
- }
492
+ return scratch;
493
+ }
578
494
 
579
- /**
580
- * Read a 32‑bit big-endian IEEE-754 float.
581
- * @param address Source address.
582
- */
495
+ const values = lengthOrValues;
583
496
 
584
- public readFloatBE(address: bigint | number): number {
585
- return this.readBuffer(address, 0x04, Memory.Scratch4).readFloatBE();
586
- }
587
-
588
- /**
589
- * Read a 32‑bit little-endian IEEE-754 float.
590
- * @param address Source address.
591
- */
497
+ this.write(address, values);
592
498
 
593
- public readFloatLE(address: bigint | number): number {
594
- return this.readBuffer(address, 0x04, Memory.Scratch4).readFloatLE();
499
+ return this;
595
500
  }
596
501
 
597
502
  /**
598
- * Read a 16‑bit big-endian signed integer.
599
- * @param address Source address.
503
+ * Reads a 64-bit floating-point value from memory or writes a 64-bit floating-point value to memory.
504
+ *
505
+ * @param address - Memory address to read from or write to
506
+ * @param value - Optional value to write. If omitted, performs a read operation
507
+ * @returns The double value when reading, or this Memory instance when writing
508
+ *
509
+ * @example
510
+ * ```typescript
511
+ * const memory = new Memory('scientific_app.exe');
512
+ *
513
+ * // Read a double precision value
514
+ * const preciseValue = memory.f64(0x12345678n);
515
+ * console.log('Precise value:', preciseValue);
516
+ *
517
+ * // Write a double precision value
518
+ * memory.f64(0x12345678n, 3.141592653589793);
519
+ * ```
600
520
  */
521
+ public f64(address: bigint): number;
522
+ public f64(address: bigint, value: number): this;
523
+ public f64(address: bigint, value?: number): number | this {
524
+ if (value === undefined) {
525
+ this.read(address, this.Scratch8);
601
526
 
602
- public readInt16BE(address: bigint | number): number {
603
- return this.readBuffer(address, 0x02, Memory.Scratch2).readInt16BE();
604
- }
605
-
606
- /**
607
- * Read a 16‑bit little-endian signed integer.
608
- * @param address Source address.
609
- */
527
+ return f64(this.Scratch8.ptr);
528
+ }
610
529
 
611
- public readInt16LE(address: bigint | number): number {
612
- return this.readBuffer(address, 0x02, Memory.Scratch2).readInt16LE();
613
- }
530
+ this.Scratch8Buffer.writeDoubleLE(value);
614
531
 
615
- /**
616
- * Read a 32‑bit big-endian signed integer.
617
- * @param address Source address.
618
- */
532
+ this.write(address, this.Scratch8);
619
533
 
620
- public readInt32BE(address: bigint | number): number {
621
- return this.readBuffer(address, 0x04, Memory.Scratch4).readInt32BE();
534
+ return this;
622
535
  }
623
536
 
624
537
  /**
625
- * Read a 32‑bit little-endian signed integer.
626
- * @param address Source address.
538
+ * Reads an array of 64-bit floating-point values from memory or writes an array of 64-bit floating-point values to memory.
539
+ *
540
+ * @param address - Memory address to read from or write to
541
+ * @param lengthOrValues - Length of array to read, or array of values to write
542
+ * @returns Float64Array when reading, or this Memory instance when writing
543
+ *
544
+ * @example
545
+ * ```typescript
546
+ * const memory = new Memory('scientific_app.exe');
547
+ *
548
+ * // Read an array of 5 double precision values
549
+ * const preciseData = memory.f64Array(0x12345678n, 5);
550
+ * console.log('Precise data:', preciseData);
551
+ *
552
+ * // Write an array of double precision values
553
+ * const newData = new Float64Array([1.1, 2.2, 3.3, 4.4, 5.5]);
554
+ * memory.f64Array(0x12345678n, newData);
555
+ * ```
627
556
  */
557
+ public f64Array(address: bigint, length: number): Float64Array;
558
+ public f64Array(address: bigint, values: Float64Array): this;
559
+ public f64Array(address: bigint, lengthOrValues: Float64Array | number): Float64Array | this {
560
+ if (typeof lengthOrValues === 'number') {
561
+ const length = lengthOrValues;
562
+ const scratch = new Float64Array(length);
628
563
 
629
- public readInt32LE(address: bigint | number): number {
630
- return this.readBuffer(address, 0x04, Memory.Scratch4).readInt32LE();
631
- }
632
-
633
- /**
634
- * Read an 8‑bit signed integer.
635
- * @param address Source address.
636
- */
564
+ this.read(address, scratch);
637
565
 
638
- public readInt8(address: bigint | number): number {
639
- return this.readBuffer(address, 0x01, Memory.Scratch1).readInt8();
640
- }
566
+ return scratch;
567
+ }
641
568
 
642
- // ? I don't have the brain-power for this right now… 🫠…
569
+ const values = lengthOrValues;
643
570
 
644
- /**
645
- * Read a big-endian signed integer of arbitrary byte length.
646
- * @param address Source address.
647
- * @param byteLength Number of bytes (1–6).
648
- * @param scratch Optional Buffer to reuse for improved performance.
649
- */
571
+ this.write(address, values);
650
572
 
651
- public readIntBE(address: bigint | number, byteLength: number, scratch: Buffer): number {
652
- return this.readBuffer(address, byteLength, scratch).readIntBE(0, byteLength);
573
+ return this;
653
574
  }
654
575
 
655
576
  /**
656
- * Read a little-endian signed integer of arbitrary byte length.
657
- * @param address Source address.
658
- * @param byteLength Number of bytes (1–6).
659
- * @param scratch Optional Buffer to reuse for improved performance.
577
+ * Reads a signed 16-bit integer value from memory or writes a signed 16-bit integer value to memory.
578
+ *
579
+ * @param address - Memory address to read from or write to
580
+ * @param value - Optional value to write. If omitted, performs a read operation
581
+ * @returns The int16 value when reading, or this Memory instance when writing
582
+ *
583
+ * @example
584
+ * ```typescript
585
+ * const memory = new Memory('game.exe');
586
+ *
587
+ * // Read a signed 16-bit integer
588
+ * const temperature = memory.i16(0x12345678n);
589
+ * console.log('Temperature:', temperature);
590
+ *
591
+ * // Write a signed 16-bit integer
592
+ * memory.i16(0x12345678n, -273);
593
+ * ```
660
594
  */
595
+ public i16(address: bigint): number;
596
+ public i16(address: bigint, value: number): this;
597
+ public i16(address: bigint, value?: number): number | this {
598
+ if (value === undefined) {
599
+ this.read(address, this.Scratch2);
661
600
 
662
- public readIntLE(address: bigint | number, byteLength: number, scratch?: Buffer): number {
663
- return this.readBuffer(address, byteLength, scratch).readIntLE(0, byteLength);
664
- }
665
-
666
- // ? …
601
+ return i16(this.Scratch2.ptr);
602
+ }
667
603
 
668
- /**
669
- * Read bytes directly into the provided scratch buffer.
670
- * @param address Source address.
671
- * @param scratch Destination buffer; its length determines the read size.
672
- * @returns The same scratch buffer for chaining.
673
- */
604
+ this.Scratch2Buffer.writeInt16LE(value);
674
605
 
675
- public readInto(address: bigint | number, scratch: Buffer): Buffer {
676
- this.readBuffer(address, scratch.byteLength, scratch);
606
+ this.write(address, this.Scratch2);
677
607
 
678
- return scratch;
608
+ return this;
679
609
  }
680
610
 
681
- // + TODO: Implement scratch…
682
-
683
611
  /**
684
- * Read a `CUtlVector`-like structure of 32‑bit elements used in some networked engines.
612
+ * Reads an array of signed 16-bit integer values from memory or writes an array of signed 16-bit integer values to memory.
613
+ *
614
+ * @param address - Memory address to read from or write to
615
+ * @param lengthOrValues - Length of array to read, or array of values to write
616
+ * @returns Int16Array when reading, or this Memory instance when writing
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * const memory = new Memory('audio_app.exe');
685
621
  *
686
- * @param address Address of the vector base structure:
687
- * - `+0x00` = size (uint32)
688
- * - `+0x08` = pointer to elements
689
- * @param scratch Optional Buffer to reuse for improved performance.
690
- * @returns A `Uint32Array` of elements, or empty if size is 0 or pointer is null.
622
+ * // Read an array of audio samples
623
+ * const samples = memory.i16Array(0x12345678n, 1024);
624
+ * console.log('Audio samples:', samples);
625
+ *
626
+ * // Write audio samples
627
+ * const newSamples = new Int16Array([-100, 200, -300, 400]);
628
+ * memory.i16Array(0x12345678n, newSamples);
629
+ * ```
691
630
  */
631
+ public i16Array(address: bigint, length: number): Int16Array;
632
+ public i16Array(address: bigint, values: Int16Array): this;
633
+ public i16Array(address: bigint, lengthOrValues: Int16Array | number): Int16Array | this {
634
+ if (typeof lengthOrValues === 'number') {
635
+ const length = lengthOrValues;
636
+ const scratch = new Int16Array(length);
692
637
 
693
- public readNetworkUtlVectorBase(address: bigint | number, scratch?: Buffer): Uint32Array {
694
- address = BigInt(address);
695
-
696
- const size = this.readUInt32LE(address);
638
+ this.read(address, scratch);
697
639
 
698
- if (size === 0) {
699
- return new Uint32Array(0);
640
+ return scratch;
700
641
  }
701
642
 
702
- const elementsPtr = this.readBigUInt64LE(address + 0x08n);
703
-
704
- if (elementsPtr === 0n) {
705
- return new Uint32Array(0);
706
- }
643
+ const values = lengthOrValues;
707
644
 
708
- const elementsBuffer = this.readBuffer(elementsPtr, size * 0x04, scratch);
645
+ this.write(address, values);
709
646
 
710
- return new Uint32Array(elementsBuffer.buffer, elementsBuffer.byteOffset, size);
647
+ return this;
711
648
  }
712
649
 
713
650
  /**
714
- * Read a UTF‑8 string up to `length` bytes or until the first NUL terminator.
715
- * @param address Source address.
716
- * @param length Maximum number of bytes to read.
717
- * @param scratch Optional Buffer to reuse for improved performance.
651
+ * Reads a signed 32-bit integer value from memory or writes a signed 32-bit integer value to memory.
652
+ *
653
+ * @param address - Memory address to read from or write to
654
+ * @param value - Optional value to write. If omitted, performs a read operation
655
+ * @returns The int32 value when reading, or this Memory instance when writing
656
+ *
657
+ * @example
658
+ * ```typescript
659
+ * const memory = new Memory('game.exe');
660
+ *
661
+ * // Read a player's score
662
+ * const score = memory.i32(0x12345678n);
663
+ * console.log('Player score:', score);
664
+ *
665
+ * // Set a new score
666
+ * memory.i32(0x12345678n, 999999);
667
+ * ```
718
668
  */
669
+ public i32(address: bigint): number;
670
+ public i32(address: bigint, value: number): this;
671
+ public i32(address: bigint, value?: number): number | this {
672
+ if (value === undefined) {
673
+ this.read(address, this.Scratch4);
719
674
 
720
- public readString(address: bigint | number, length: number, scratch?: Buffer): string {
721
- const buffer = this.readBuffer(address, length, scratch);
722
-
723
- const indexOf = buffer.indexOf(0);
724
-
725
- const end = indexOf !== -1 ? indexOf : buffer.length;
726
- const start = 0;
675
+ return i32(this.Scratch4.ptr);
676
+ }
727
677
 
728
- return buffer.toString('utf8', start, end);
729
- }
678
+ this.Scratch4Buffer.writeInt32LE(value);
730
679
 
731
- /**
732
- * Read a 16‑bit big-endian unsigned integer.
733
- * @param address Source address.
734
- */
680
+ this.write(address, this.Scratch4);
735
681
 
736
- public readUInt16BE(address: bigint | number): number {
737
- return this.readBuffer(address, 0x02, Memory.Scratch2).readUInt16BE();
682
+ return this;
738
683
  }
739
684
 
740
685
  /**
741
- * Read a 16‑bit little-endian unsigned integer.
742
- * @param address Source address.
686
+ * Reads an array of signed 32-bit integer values from memory or writes an array of signed 32-bit integer values to memory.
687
+ *
688
+ * @param address - Memory address to read from or write to
689
+ * @param lengthOrValues - Length of array to read, or array of values to write
690
+ * @returns Int32Array when reading, or this Memory instance when writing
691
+ *
692
+ * @example
693
+ * ```typescript
694
+ * const memory = new Memory('game.exe');
695
+ *
696
+ * // Read inventory item counts
697
+ * const inventory = memory.i32Array(0x12345678n, 20);
698
+ * console.log('Inventory:', inventory);
699
+ *
700
+ * // Set inventory values
701
+ * const newInventory = new Int32Array([99, 50, 25, 10, 5]);
702
+ * memory.i32Array(0x12345678n, newInventory);
703
+ * ```
743
704
  */
705
+ public i32Array(address: bigint, length: number): Int32Array;
706
+ public i32Array(address: bigint, values: Int32Array): this;
707
+ public i32Array(address: bigint, lengthOrValues: Int32Array | number): Int32Array | this {
708
+ if (typeof lengthOrValues === 'number') {
709
+ const length = lengthOrValues;
710
+ const scratch = new Int32Array(length);
744
711
 
745
- public readUInt16LE(address: bigint | number): number {
746
- return this.readBuffer(address, 0x02, Memory.Scratch2).readUInt16LE();
747
- }
712
+ this.read(address, scratch);
748
713
 
749
- /**
750
- * Read a 32‑bit big-endian unsigned integer.
751
- * @param address Source address.
752
- */
714
+ return scratch;
715
+ }
753
716
 
754
- public readUInt32BE(address: bigint | number): number {
755
- return this.readBuffer(address, 0x04, Memory.Scratch4).readUInt32BE();
756
- }
717
+ const values = lengthOrValues;
757
718
 
758
- /**
759
- * Read a 32‑bit little-endian unsigned integer.
760
- * @param address Source address.
761
- */
719
+ this.write(address, values);
762
720
 
763
- public readUInt32LE(address: bigint | number): number {
764
- return this.readBuffer(address, 0x04, Memory.Scratch4).readUInt32LE();
721
+ return this;
765
722
  }
766
723
 
767
724
  /**
768
- * Read an 8‑bit unsigned integer.
769
- * @param address Source address.
725
+ * Reads a signed 64-bit integer value from memory or writes a signed 64-bit integer value to memory.
726
+ *
727
+ * @param address - Memory address to read from or write to
728
+ * @param value - Optional value to write. If omitted, performs a read operation
729
+ * @returns The int64 value when reading, or this Memory instance when writing
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * const memory = new Memory('database.exe');
734
+ *
735
+ * // Read a large number (timestamp, file size, etc.)
736
+ * const timestamp = memory.i64(0x12345678n);
737
+ * console.log('Timestamp:', timestamp);
738
+ *
739
+ * // Write a large number
740
+ * memory.i64(0x12345678n, 9223372036854775807n);
741
+ * ```
770
742
  */
743
+ public i64(address: bigint): bigint;
744
+ public i64(address: bigint, value: bigint): this;
745
+ public i64(address: bigint, value?: bigint): bigint | this {
746
+ if (value === undefined) {
747
+ this.read(address, this.Scratch8);
771
748
 
772
- public readUInt8(address: bigint | number): number {
773
- return this.readBuffer(address, 0x01, Memory.Scratch1).readUInt8();
774
- }
775
-
776
- // ? I don't have the brain-power for this right now… 🫠…
749
+ return i64(this.Scratch8.ptr);
750
+ }
777
751
 
778
- /**
779
- * Read a big-endian unsigned integer of arbitrary byte length.
780
- * @param address Source address.
781
- * @param byteLength Number of bytes (1–6).
782
- * @param scratch Optional Buffer to reuse for improved performance.
783
- */
752
+ this.Scratch8Buffer.writeBigInt64LE(value);
784
753
 
785
- public readUIntBE(address: bigint | number, byteLength: number, scratch?: Buffer): number {
786
- return this.readBuffer(address, byteLength, scratch).readUIntBE(0, byteLength);
787
- }
754
+ this.write(address, this.Scratch8);
788
755
 
789
- /**
790
- * Read a little-endian unsigned integer of arbitrary byte length.
791
- * @param address Source address.
792
- * @param byteLength Number of bytes (1–6).
793
- * @param scratch Optional Buffer to reuse for improved performance.
794
- */
795
-
796
- public readUIntLE(address: bigint | number, byteLength: number, scratch?: Buffer): number {
797
- return this.readBuffer(address, byteLength, scratch).readUIntLE(0, byteLength);
756
+ return this;
798
757
  }
799
758
 
800
- // ? …
801
-
802
759
  /**
803
- * Read a {@link Vector3} (three consecutive 32‑bit floats).
804
- * @param address Source address.
805
- * @returns A `{ x, y, z }` object.
760
+ * Reads an array of signed 64-bit integer values from memory or writes an array of signed 64-bit integer values to memory.
761
+ *
762
+ * @param address - Memory address to read from or write to
763
+ * @param lengthOrValues - Length of array to read, or array of values to write
764
+ * @returns BigInt64Array when reading, or this Memory instance when writing
765
+ *
766
+ * @example
767
+ * ```typescript
768
+ * const memory = new Memory('database.exe');
769
+ *
770
+ * // Read an array of large numbers
771
+ * const largeNumbers = memory.i64Array(0x12345678n, 10);
772
+ * console.log('Large numbers:', largeNumbers);
773
+ *
774
+ * // Write an array of large numbers
775
+ * const newNumbers = new BigInt64Array([1n, 2n, 3n, 4n, 5n]);
776
+ * memory.i64Array(0x12345678n, newNumbers);
777
+ * ```
806
778
  */
779
+ public i64Array(address: bigint, length: number): BigInt64Array;
780
+ public i64Array(address: bigint, values: BigInt64Array): this;
781
+ public i64Array(address: bigint, lengthOrValues: BigInt64Array | number): BigInt64Array | this {
782
+ if (typeof lengthOrValues === 'number') {
783
+ const length = lengthOrValues;
784
+ const scratch = new BigInt64Array(length);
807
785
 
808
- public readVector3(address: bigint | number): Vector3 {
809
- const buffer = this.readBuffer(address, 0x0c, Memory.Scratch12);
786
+ this.read(address, scratch);
810
787
 
811
- const x = buffer.readFloatLE();
812
- const y = buffer.readFloatLE(0x04);
813
- const z = buffer.readFloatLE(0x08);
814
-
815
- return { x, y, z };
816
- }
817
-
818
- // ? …
819
-
820
- /**
821
- * Write a signed 64‑bit big-endian integer.
822
- * @param address Destination address.
823
- * @param value Value to write.
824
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
825
- * @returns `this` for chaining.
826
- * @throws {Win32Error} If the underlying write or protection change fails.
827
- */
788
+ return scratch;
789
+ }
828
790
 
829
- public writeBigInt64BE(address: bigint | number, value: bigint, force = false): this {
830
- Memory.Scratch8.writeBigInt64BE(value);
791
+ const values = lengthOrValues;
831
792
 
832
- this.writeBuffer(address, Memory.Scratch8, force);
793
+ this.write(address, values);
833
794
 
834
795
  return this;
835
796
  }
836
797
 
837
798
  /**
838
- * Write a signed 64‑bit little-endian integer.
839
- * @param address Destination address.
840
- * @param value Value to write.
841
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
842
- * @returns `this` for chaining.
843
- * @throws {Win32Error} If the underlying write or protection change fails.
799
+ * Reads a signed 8-bit integer value from memory or writes a signed 8-bit integer value to memory.
800
+ *
801
+ * @param address - Memory address to read from or write to
802
+ * @param value - Optional value to write. If omitted, performs a read operation
803
+ * @returns The int8 value when reading, or this Memory instance when writing
804
+ *
805
+ * @example
806
+ * ```typescript
807
+ * const memory = new Memory('game.exe');
808
+ *
809
+ * // Read a small signed value (e.g., direction, state)
810
+ * const direction = memory.i8(0x12345678n);
811
+ * console.log('Direction:', direction);
812
+ *
813
+ * // Write a small signed value
814
+ * memory.i8(0x12345678n, -127);
815
+ * ```
844
816
  */
817
+ public i8(address: bigint): number;
818
+ public i8(address: bigint, value: number): this;
819
+ public i8(address: bigint, value?: number): number | this {
820
+ if (value === undefined) {
821
+ this.read(address, this.Scratch1);
845
822
 
846
- public writeBigInt64LE(address: bigint | number, value: bigint, force = false): this {
847
- Memory.Scratch8.writeBigInt64LE(value);
823
+ return i8(this.Scratch1.ptr);
824
+ }
825
+
826
+ this.Scratch1Buffer.writeInt8(value);
848
827
 
849
- this.writeBuffer(address, Memory.Scratch8, force);
828
+ this.write(address, this.Scratch1);
850
829
 
851
830
  return this;
852
831
  }
853
832
 
854
833
  /**
855
- * Write an unsigned 64‑bit big-endian integer.
856
- * @param address Destination address.
857
- * @param value Value to write.
858
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
859
- * @returns `this` for chaining.
860
- * @throws {Win32Error} If the underlying write or protection change fails.
834
+ * Reads an array of signed 8-bit integer values from memory or writes an array of signed 8-bit integer values to memory.
835
+ *
836
+ * @param address - Memory address to read from or write to
837
+ * @param lengthOrValues - Length of array to read, or array of values to write
838
+ * @returns Int8Array when reading, or this Memory instance when writing
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * const memory = new Memory('game.exe');
843
+ *
844
+ * // Read an array of small signed values
845
+ * const directions = memory.i8Array(0x12345678n, 8);
846
+ * console.log('Movement directions:', directions);
847
+ *
848
+ * // Write movement directions
849
+ * const newDirections = new Int8Array([-1, 0, 1, -1, 0, 1, -1, 0]);
850
+ * memory.i8Array(0x12345678n, newDirections);
851
+ * ```
861
852
  */
853
+ public i8Array(address: bigint, length: number): Int8Array;
854
+ public i8Array(address: bigint, values: Int8Array): this;
855
+ public i8Array(address: bigint, lengthOrValues: Int8Array | number): Int8Array | this {
856
+ if (typeof lengthOrValues === 'number') {
857
+ const length = lengthOrValues;
858
+ const scratch = new Int8Array(length);
862
859
 
863
- public writeBigUInt64BE(address: bigint | number, value: bigint, force = false): this {
864
- Memory.Scratch8.writeBigUInt64BE(value);
865
-
866
- this.writeBuffer(address, Memory.Scratch8, force);
860
+ this.read(address, scratch);
867
861
 
868
- return this;
869
- }
870
-
871
- /**
872
- * Write an unsigned 64‑bit little-endian integer.
873
- * @param address Destination address.
874
- * @param value Value to write.
875
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
876
- * @returns `this` for chaining.
877
- * @throws {Win32Error} If the underlying write or protection change fails.
878
- */
862
+ return scratch;
863
+ }
879
864
 
880
- public writeBigUInt64LE(address: bigint | number, value: bigint, force = false): this {
881
- Memory.Scratch8.writeBigUInt64LE(value);
865
+ const values = lengthOrValues;
882
866
 
883
- this.writeBuffer(address, Memory.Scratch8, force);
867
+ this.write(address, values);
884
868
 
885
869
  return this;
886
870
  }
887
871
 
888
872
  /**
889
- * Write a boolean as an 8‑bit value (0 or 1).
890
- * @param address Destination address.
891
- * @param value Value to write.
892
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
893
- * @returns `this` for chaining.
894
- * @throws {Win32Error} If the underlying write or protection change fails.
895
- */
873
+ * Reads a quaternion (4D rotation) from memory or writes a quaternion to memory.
874
+ * Quaternions are stored as four 32-bit floats: x, y, z, w.
875
+ *
876
+ * @param address - Memory address to read from or write to
877
+ * @param value - Optional quaternion to write. If omitted, performs a read operation
878
+ * @returns The Quaternion object when reading, or this Memory instance when writing
879
+ *
880
+ * @example
881
+ * ```typescript
882
+ * const memory = new Memory('game.exe');
883
+ *
884
+ * // Read player rotation
885
+ * const rotation = memory.quaternion(0x12345678n);
886
+ * console.log('Player rotation:', rotation);
887
+ *
888
+ * // Set player rotation to identity
889
+ * memory.quaternion(0x12345678n, { x: 0, y: 0, z: 0, w: 1 });
890
+ * ```
891
+ */
892
+ public quaternion(address: bigint): Quaternion;
893
+ public quaternion(address: bigint, value: Quaternion): this;
894
+ public quaternion(address: bigint, value?: Quaternion): Quaternion | this {
895
+ if (value === undefined) {
896
+ this.read(address, this.Scratch16);
897
+
898
+ const w = f32(this.Scratch16.ptr, 0x0c);
899
+ const x = f32(this.Scratch16.ptr);
900
+ const y = f32(this.Scratch16.ptr, 0x04);
901
+ const z = f32(this.Scratch16.ptr, 0x08);
902
+
903
+ return { w, x, y, z };
904
+ }
896
905
 
897
- public writeBoolean(address: bigint | number, value: boolean, force = false): this {
898
- Memory.Scratch1.writeUInt8(+value);
906
+ this.Scratch16Buffer.writeFloatLE(value.w, 0x0c);
907
+ this.Scratch16Buffer.writeFloatLE(value.x);
908
+ this.Scratch16Buffer.writeFloatLE(value.y, 0x04);
909
+ this.Scratch16Buffer.writeFloatLE(value.z, 0x08);
899
910
 
900
- this.writeBuffer(address, Memory.Scratch1, force);
911
+ this.write(address, this.Scratch16);
901
912
 
902
913
  return this;
903
914
  }
904
915
 
905
916
  /**
906
- * Write raw bytes to the target process.
907
- *
908
- * @param address Destination address.
909
- * @param buffer Source buffer to write.
910
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` and restores
911
- * the original protection after the write.
912
- * @returns `this` for chaining.
913
- * @throws {Win32Error} If the underlying write or protection change fails.
917
+ * Reads an array of quaternions from memory or writes an array of quaternions to memory.
918
+ *
919
+ * @param address - Memory address to read from or write to
920
+ * @param lengthOrValues - Length of array to read, or array of quaternions to write
921
+ * @returns Array of Quaternion objects when reading, or this Memory instance when writing
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * const memory = new Memory('game.exe');
926
+ *
927
+ * // Read bone rotations for skeletal animation
928
+ * const boneRotations = memory.quaternionArray(0x12345678n, 50);
929
+ * console.log('Bone rotations:', boneRotations);
930
+ *
931
+ * // Set all bones to identity rotation
932
+ * const identityRotations = Array(50).fill({ x: 0, y: 0, z: 0, w: 1 });
933
+ * memory.quaternionArray(0x12345678n, identityRotations);
934
+ * ```
914
935
  */
936
+ public quaternionArray(address: bigint, length: number): Quaternion[];
937
+ public quaternionArray(address: bigint, values: Quaternion[]): this;
938
+ public quaternionArray(address: bigint, lengthOrValues: Quaternion[] | number): Quaternion[] | this {
939
+ if (typeof lengthOrValues === 'number') {
940
+ const length = lengthOrValues;
941
+ const scratch = new Float32Array(length * 0x04); // 4 * f32 per Quaternion
915
942
 
916
- public writeBuffer(address: bigint | number, buffer: Buffer, force = false): this {
917
- const { hProcess } = this;
918
-
919
- address = BigInt(address);
943
+ this.read(address, scratch);
920
944
 
921
- const lpBaseAddress = address;
922
- const lpBuffer = buffer;
923
- // const lpNumberOfBytesWritten = 0n;
924
- const nSize = BigInt(buffer.byteLength);
945
+ const result = new Array<Quaternion>(length);
925
946
 
926
- if (!force) {
927
- const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, 0n);
947
+ for (let i = 0, j = 0; i < length; i++, j += 0x04) {
948
+ const w = scratch[j + 0x03];
949
+ const x = scratch[j];
950
+ const y = scratch[j + 0x01];
951
+ const z = scratch[j + 0x02];
928
952
 
929
- if (!bWriteProcessMemory) {
930
- throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
953
+ result[i] = { w, x, y, z };
931
954
  }
932
955
 
933
- return this;
956
+ return result;
934
957
  }
935
958
 
936
- const dwSize = BigInt(buffer.byteLength);
937
- const flNewProtect = 0x40; /* PAGE_EXECUTE_READWRITE */
938
- const lpAddress = address;
939
- const lpflOldProtect = Memory.Scratch4;
959
+ const values = lengthOrValues;
960
+ const scratch = new Float32Array(values.length * 0x04);
940
961
 
941
- const bVirtualProtectEx = !!Kernel32.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
962
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x04) {
963
+ const quaternion = values[i];
942
964
 
943
- if (!bVirtualProtectEx) {
944
- throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
965
+ scratch[j + 0x03] = quaternion.w;
966
+ scratch[j] = quaternion.x;
967
+ scratch[j + 0x01] = quaternion.y;
968
+ scratch[j + 0x02] = quaternion.z;
945
969
  }
946
970
 
947
- const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, 0n);
948
-
949
- if (!bWriteProcessMemory) {
950
- Kernel32.VirtualProtectEx(hProcess, lpAddress, nSize, lpflOldProtect.readUInt32LE(), Memory.Scratch4_2);
951
-
952
- throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
953
- }
954
-
955
- Kernel32.VirtualProtectEx(hProcess, lpAddress, nSize, lpflOldProtect.readUInt32LE(), Memory.Scratch4_2);
971
+ this.write(address, scratch);
956
972
 
957
973
  return this;
958
974
  }
959
975
 
960
976
  /**
961
- * Write a 64‑bit big-endian IEEE-754 float.
962
- * @param address Destination address.
963
- * @param value Value to write.
964
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
965
- * @returns `this` for chaining.
966
- * @throws {Win32Error} If the underlying write or protection change fails.
977
+ * Reads an unsigned 16-bit integer value from memory or writes an unsigned 16-bit integer value to memory.
978
+ *
979
+ * @param address - Memory address to read from or write to
980
+ * @param value - Optional value to write. If omitted, performs a read operation
981
+ * @returns The uint16 value when reading, or this Memory instance when writing
982
+ *
983
+ * @example
984
+ * ```typescript
985
+ * const memory = new Memory('game.exe');
986
+ *
987
+ * // Read a port number or small positive value
988
+ * const port = memory.u16(0x12345678n);
989
+ * console.log('Network port:', port);
990
+ *
991
+ * // Write a port number
992
+ * memory.u16(0x12345678n, 8080);
993
+ * ```
967
994
  */
995
+ public u16(address: bigint): number;
996
+ public u16(address: bigint, value: number): this;
997
+ public u16(address: bigint, value?: number): number | this {
998
+ if (value === undefined) {
999
+ this.read(address, this.Scratch2);
968
1000
 
969
- public writeDoubleBE(address: bigint | number, value: number, force = false): this {
970
- Memory.Scratch8.writeDoubleBE(value);
971
-
972
- this.writeBuffer(address, Memory.Scratch8, force);
973
-
974
- return this;
975
- }
976
-
977
- /**
978
- * Write a 64‑bit little-endian IEEE-754 float.
979
- * @param address Destination address.
980
- * @param value Value to write.
981
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
982
- * @returns `this` for chaining.
983
- * @throws {Win32Error} If the underlying write or protection change fails.
984
- */
1001
+ return u16(this.Scratch2.ptr);
1002
+ }
985
1003
 
986
- public writeDoubleLE(address: bigint | number, value: number, force = false): this {
987
- Memory.Scratch8.writeDoubleLE(value);
1004
+ this.Scratch2Buffer.writeUInt16LE(value);
988
1005
 
989
- this.writeBuffer(address, Memory.Scratch8, force);
1006
+ this.write(address, this.Scratch2);
990
1007
 
991
1008
  return this;
992
1009
  }
993
1010
 
994
1011
  /**
995
- * Write a 32‑bit big-endian IEEE-754 float.
996
- * @param address Destination address.
997
- * @param value Value to write.
998
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
999
- * @returns `this` for chaining.
1000
- * @throws {Win32Error} If the underlying write or protection change fails.
1012
+ * Reads an array of unsigned 16-bit integer values from memory or writes an array of unsigned 16-bit integer values to memory.
1013
+ *
1014
+ * @param address - Memory address to read from or write to
1015
+ * @param lengthOrValues - Length of array to read, or array of values to write
1016
+ * @returns Uint16Array when reading, or this Memory instance when writing
1017
+ *
1018
+ * @example
1019
+ * ```typescript
1020
+ * const memory = new Memory('network_app.exe');
1021
+ *
1022
+ * // Read an array of port numbers
1023
+ * const ports = memory.u16Array(0x12345678n, 10);
1024
+ * console.log('Active ports:', ports);
1025
+ *
1026
+ * // Write port configuration
1027
+ * const newPorts = new Uint16Array([80, 443, 8080, 3000, 5432]);
1028
+ * memory.u16Array(0x12345678n, newPorts);
1029
+ * ```
1001
1030
  */
1031
+ public u16Array(address: bigint, length: number): Uint16Array;
1032
+ public u16Array(address: bigint, values: Uint16Array): this;
1033
+ public u16Array(address: bigint, lengthOrValues: Uint16Array | number): Uint16Array | this {
1034
+ if (typeof lengthOrValues === 'number') {
1035
+ const length = lengthOrValues;
1036
+ const scratch = new Uint16Array(length);
1002
1037
 
1003
- public writeFloatBE(address: bigint | number, value: number, force = false): this {
1004
- Memory.Scratch4.writeFloatBE(value);
1005
-
1006
- this.writeBuffer(address, Memory.Scratch4, force);
1038
+ this.read(address, scratch);
1007
1039
 
1008
- return this;
1009
- }
1010
-
1011
- /**
1012
- * Write a 32‑bit little-endian IEEE-754 float.
1013
- * @param address Destination address.
1014
- * @param value Value to write.
1015
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1016
- * @returns `this` for chaining.
1017
- * @throws {Win32Error} If the underlying write or protection change fails.
1018
- */
1040
+ return scratch;
1041
+ }
1019
1042
 
1020
- public writeFloatLE(address: bigint | number, value: number, force = false): this {
1021
- Memory.Scratch4.writeFloatLE(value);
1043
+ const values = lengthOrValues;
1022
1044
 
1023
- this.writeBuffer(address, Memory.Scratch4, force);
1045
+ this.write(address, values);
1024
1046
 
1025
1047
  return this;
1026
1048
  }
1027
1049
 
1028
1050
  /**
1029
- * Write a 16‑bit big-endian signed integer.
1030
- * @param address Destination address.
1031
- * @param value Value to write.
1032
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1033
- * @returns `this` for chaining.
1034
- * @throws {Win32Error} If the underlying write or protection change fails.
1051
+ * Reads an unsigned 32-bit integer value from memory or writes an unsigned 32-bit integer value to memory.
1052
+ *
1053
+ * @param address - Memory address to read from or write to
1054
+ * @param value - Optional value to write. If omitted, performs a read operation
1055
+ * @returns The uint32 value when reading, or this Memory instance when writing
1056
+ *
1057
+ * @example
1058
+ * ```typescript
1059
+ * const memory = new Memory('game.exe');
1060
+ *
1061
+ * // Read player's money (always positive)
1062
+ * const money = memory.u32(0x12345678n);
1063
+ * console.log('Player money:', money);
1064
+ *
1065
+ * // Give player maximum money
1066
+ * memory.u32(0x12345678n, 4294967295);
1067
+ * ```
1035
1068
  */
1069
+ public u32(address: bigint): number;
1070
+ public u32(address: bigint, value: number): this;
1071
+ public u32(address: bigint, value?: number): number | this {
1072
+ if (value === undefined) {
1073
+ this.read(address, this.Scratch4);
1036
1074
 
1037
- public writeInt16BE(address: bigint | number, value: number, force = false): this {
1038
- Memory.Scratch2.writeInt16BE(value);
1075
+ return u32(this.Scratch4.ptr);
1076
+ }
1039
1077
 
1040
- this.writeBuffer(address, Memory.Scratch2, force);
1078
+ this.Scratch4Buffer.writeUInt32LE(value);
1079
+
1080
+ this.write(address, this.Scratch4);
1041
1081
 
1042
1082
  return this;
1043
1083
  }
1044
1084
 
1045
1085
  /**
1046
- * Write a 16‑bit little-endian signed integer.
1047
- * @param address Destination address.
1048
- * @param value Value to write.
1049
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1050
- * @returns `this` for chaining.
1051
- * @throws {Win32Error} If the underlying write or protection change fails.
1086
+ * Reads an array of unsigned 32-bit integer values from memory or writes an array of unsigned 32-bit integer values to memory.
1087
+ *
1088
+ * @param address - Memory address to read from or write to
1089
+ * @param lengthOrValues - Length of array to read, or array of values to write
1090
+ * @returns Uint32Array when reading, or this Memory instance when writing
1091
+ *
1092
+ * @example
1093
+ * ```typescript
1094
+ * const memory = new Memory('game.exe');
1095
+ *
1096
+ * // Read resource amounts
1097
+ * const resources = memory.u32Array(0x12345678n, 6);
1098
+ * console.log('Resources:', resources);
1099
+ *
1100
+ * // Set resource amounts
1101
+ * const newResources = new Uint32Array([1000, 2000, 3000, 4000, 5000, 6000]);
1102
+ * memory.u32Array(0x12345678n, newResources);
1103
+ * ```
1052
1104
  */
1105
+ public u32Array(address: bigint, length: number): Uint32Array;
1106
+ public u32Array(address: bigint, values: Uint32Array): this;
1107
+ public u32Array(address: bigint, lengthOrValues: Uint32Array | number): Uint32Array | this {
1108
+ if (typeof lengthOrValues === 'number') {
1109
+ const length = lengthOrValues;
1110
+ const scratch = new Uint32Array(length);
1053
1111
 
1054
- public writeInt16LE(address: bigint | number, value: number, force = false): this {
1055
- Memory.Scratch2.writeInt16LE(value);
1056
-
1057
- this.writeBuffer(address, Memory.Scratch2, force);
1058
-
1059
- return this;
1060
- }
1112
+ this.read(address, scratch);
1061
1113
 
1062
- /**
1063
- * Write a 32‑bit big-endian signed integer.
1064
- * @param address Destination address.
1065
- * @param value Value to write.
1066
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1067
- * @returns `this` for chaining.
1068
- * @throws {Win32Error} If the underlying write or protection change fails.
1069
- */
1114
+ return scratch;
1115
+ }
1070
1116
 
1071
- public writeInt32BE(address: bigint | number, value: number, force = false): this {
1072
- Memory.Scratch4.writeInt32BE(value);
1117
+ const values = lengthOrValues;
1073
1118
 
1074
- this.writeBuffer(address, Memory.Scratch4, force);
1119
+ this.write(address, values);
1075
1120
 
1076
1121
  return this;
1077
1122
  }
1078
1123
 
1079
1124
  /**
1080
- * Write a 32‑bit little-endian signed integer.
1081
- * @param address Destination address.
1082
- * @param value Value to write.
1083
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1084
- * @returns `this` for chaining.
1085
- * @throws {Win32Error} If the underlying write or protection change fails.
1125
+ * Reads an unsigned 64-bit integer value from memory or writes an unsigned 64-bit integer value to memory.
1126
+ *
1127
+ * @param address - Memory address to read from or write to
1128
+ * @param value - Optional value to write. If omitted, performs a read operation
1129
+ * @returns The uint64 value when reading, or this Memory instance when writing
1130
+ *
1131
+ * @example
1132
+ * ```typescript
1133
+ * const memory = new Memory('database.exe');
1134
+ *
1135
+ * // Read a very large positive number
1136
+ * const recordId = memory.u64(0x12345678n);
1137
+ * console.log('Record ID:', recordId);
1138
+ *
1139
+ * // Write a very large positive number
1140
+ * memory.u64(0x12345678n, 18446744073709551615n);
1141
+ * ```
1086
1142
  */
1143
+ public u64(address: bigint): bigint;
1144
+ public u64(address: bigint, value: bigint): this;
1145
+ public u64(address: bigint, value?: bigint): bigint | this {
1146
+ if (value === undefined) {
1147
+ this.read(address, this.Scratch8);
1148
+
1149
+ return u64(this.Scratch8.ptr);
1150
+ }
1087
1151
 
1088
- public writeInt32LE(address: bigint | number, value: number, force = false): this {
1089
- Memory.Scratch4.writeInt32LE(value);
1152
+ this.Scratch8Buffer.writeBigUInt64LE(value);
1090
1153
 
1091
- this.writeBuffer(address, Memory.Scratch4, force);
1154
+ this.write(address, this.Scratch8);
1092
1155
 
1093
1156
  return this;
1094
1157
  }
1095
1158
 
1096
1159
  /**
1097
- * Write an 8‑bit signed integer.
1098
- * @param address Destination address.
1099
- * @param value Value to write.
1100
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1101
- * @returns `this` for chaining.
1102
- * @throws {Win32Error} If the underlying write or protection change fails.
1160
+ * Reads an array of unsigned 64-bit integer values from memory or writes an array of unsigned 64-bit integer values to memory.
1161
+ *
1162
+ * @param address - Memory address to read from or write to
1163
+ * @param lengthOrValues - Length of array to read, or array of values to write
1164
+ * @returns BigUint64Array when reading, or this Memory instance when writing
1165
+ *
1166
+ * @example
1167
+ * ```typescript
1168
+ * const memory = new Memory('database.exe');
1169
+ *
1170
+ * // Read an array of record IDs
1171
+ * const recordIds = memory.u64Array(0x12345678n, 100);
1172
+ * console.log('Record IDs:', recordIds);
1173
+ *
1174
+ * // Write record IDs
1175
+ * const newIds = new BigUint64Array([1000n, 2000n, 3000n, 4000n]);
1176
+ * memory.u64Array(0x12345678n, newIds);
1177
+ * ```
1103
1178
  */
1179
+ public u64Array(address: bigint, length: number): BigUint64Array;
1180
+ public u64Array(address: bigint, values: BigUint64Array): this;
1181
+ public u64Array(address: bigint, lengthOrValues: BigUint64Array | number): BigUint64Array | this {
1182
+ if (typeof lengthOrValues === 'number') {
1183
+ const length = lengthOrValues;
1184
+ const scratch = new BigUint64Array(length);
1104
1185
 
1105
- public writeInt8(address: bigint | number, value: number, force = false): this {
1106
- Memory.Scratch1.writeInt8(value);
1107
-
1108
- this.writeBuffer(address, Memory.Scratch1, force);
1109
-
1110
- return this;
1111
- }
1112
-
1113
- // + TODO: Implement scratch…
1186
+ this.read(address, scratch);
1114
1187
 
1115
- /**
1116
- * Write a big-endian signed integer of arbitrary byte length.
1117
- * @param address Destination address.
1118
- * @param value Value to write.
1119
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1120
- * @returns `this` for chaining.
1121
- * @throws {Win32Error} If the underlying write or protection change fails.
1122
- */
1188
+ return scratch;
1189
+ }
1123
1190
 
1124
- public writeIntBE(address: bigint | number, value: number, byteLength: number, force = false): this {
1125
- const buffer = Buffer.allocUnsafe(byteLength);
1126
- /* */ buffer.writeIntBE(value, 0, byteLength);
1191
+ const values = lengthOrValues;
1127
1192
 
1128
- this.writeBuffer(address, buffer, force);
1193
+ this.write(address, values);
1129
1194
 
1130
1195
  return this;
1131
1196
  }
1132
1197
 
1133
- // + TODO: Implement scratch…
1134
-
1135
1198
  /**
1136
- * Write a little-endian signed integer of arbitrary byte length.
1137
- * @param address Destination address.
1138
- * @param value Value to write.
1139
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1140
- * @returns `this` for chaining.
1141
- * @throws {Win32Error} If the underlying write or protection change fails.
1199
+ * Reads an unsigned 8-bit integer value from memory or writes an unsigned 8-bit integer value to memory.
1200
+ *
1201
+ * @param address - Memory address to read from or write to
1202
+ * @param value - Optional value to write. If omitted, performs a read operation
1203
+ * @returns The uint8 value when reading, or this Memory instance when writing
1204
+ *
1205
+ * @example
1206
+ * ```typescript
1207
+ * const memory = new Memory('game.exe');
1208
+ *
1209
+ * // Read a byte value (0-255)
1210
+ * const opacity = memory.u8(0x12345678n);
1211
+ * console.log('UI opacity:', opacity);
1212
+ *
1213
+ * // Set opacity to maximum
1214
+ * memory.u8(0x12345678n, 255);
1215
+ * ```
1142
1216
  */
1217
+ public u8(address: bigint): number;
1218
+ public u8(address: bigint, value: number): this;
1219
+ public u8(address: bigint, value?: number): number | this {
1220
+ if (value === undefined) {
1221
+ this.read(address, this.Scratch1);
1222
+
1223
+ return u8(this.Scratch1.ptr);
1224
+ }
1143
1225
 
1144
- public writeIntLE(address: bigint | number, value: number, byteLength: number, force = false): this {
1145
- const buffer = Buffer.allocUnsafe(byteLength);
1146
- /* */ buffer.writeIntLE(value, 0, byteLength);
1226
+ this.Scratch1Buffer.writeUInt8(value);
1147
1227
 
1148
- this.writeBuffer(address, buffer, force);
1228
+ this.write(address, this.Scratch1);
1149
1229
 
1150
1230
  return this;
1151
1231
  }
1152
1232
 
1153
- // + TODO: Implement scratch…
1154
-
1155
1233
  /**
1156
- * Write a UTF‑8 string (no terminator is appended).
1157
- * @param address Destination address.
1158
- * @param value Value to write.
1159
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1160
- * @returns `this` for chaining.
1161
- * @throws {Win32Error} If the underlying write or protection change fails.
1234
+ * Reads an array of unsigned 8-bit integer values from memory or writes an array of unsigned 8-bit integer values to memory.
1235
+ *
1236
+ * @param address - Memory address to read from or write to
1237
+ * @param lengthOrValues - Length of array to read, or array of values to write
1238
+ * @returns Uint8Array when reading, or this Memory instance when writing
1239
+ *
1240
+ * @example
1241
+ * ```typescript
1242
+ * const memory = new Memory('image_editor.exe');
1243
+ *
1244
+ * // Read pixel data
1245
+ * const pixels = memory.u8Array(0x12345678n, 1024);
1246
+ * console.log('Pixel data:', pixels);
1247
+ *
1248
+ * // Write pixel data
1249
+ * const newPixels = new Uint8Array([255, 128, 64, 32, 16, 8, 4, 2]);
1250
+ * memory.u8Array(0x12345678n, newPixels);
1251
+ * ```
1162
1252
  */
1253
+ public u8Array(address: bigint, length: number): Uint8Array;
1254
+ public u8Array(address: bigint, values: Uint8Array): this;
1255
+ public u8Array(address: bigint, lengthOrValues: Uint8Array | number): Uint8Array | this {
1256
+ if (typeof lengthOrValues === 'number') {
1257
+ const length = lengthOrValues;
1258
+ const scratch = new Uint8Array(length);
1163
1259
 
1164
- public writeString(address: bigint | number, value: string, force = false): this {
1165
- const byteLength = Buffer.byteLength(value);
1260
+ this.read(address, scratch);
1261
+
1262
+ return scratch;
1263
+ }
1166
1264
 
1167
- const buffer = Buffer.allocUnsafe(byteLength);
1168
- /* */ buffer.write(value, 0, byteLength, 'utf8');
1265
+ const values = lengthOrValues;
1169
1266
 
1170
- this.writeBuffer(address, buffer, force);
1267
+ this.write(address, values);
1171
1268
 
1172
1269
  return this;
1173
1270
  }
1174
1271
 
1175
1272
  /**
1176
- * Write a 16‑bit big-endian unsigned integer.
1177
- * @param address Destination address.
1178
- * @param value Value to write.
1179
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1180
- * @returns `this` for chaining.
1181
- * @throws {Win32Error} If the underlying write or protection change fails.
1273
+ * Reads a 2D vector from memory or writes a 2D vector to memory.
1274
+ * Vectors are stored as two 32-bit floats: x, y.
1275
+ *
1276
+ * @param address - Memory address to read from or write to
1277
+ * @param value - Optional vector to write. If omitted, performs a read operation
1278
+ * @returns The Vector2 object when reading, or this Memory instance when writing
1279
+ *
1280
+ * @example
1281
+ * ```typescript
1282
+ * const memory = new Memory('game.exe');
1283
+ *
1284
+ * // Read player position
1285
+ * const position = memory.vector2(0x12345678n);
1286
+ * console.log('Player position:', position);
1287
+ *
1288
+ * // Teleport player to origin
1289
+ * memory.vector2(0x12345678n, { x: 0, y: 0 });
1290
+ * ```
1182
1291
  */
1292
+ public vector2(address: bigint): Vector2;
1293
+ public vector2(address: bigint, value: Vector2): this;
1294
+ public vector2(address: bigint, value?: Vector2): Vector2 | this {
1295
+ if (value === undefined) {
1296
+ this.read(address, this.Scratch8);
1183
1297
 
1184
- public writeUInt16BE(address: bigint | number, value: number, force = false): this {
1185
- Memory.Scratch2.writeUInt16BE(value);
1186
-
1187
- this.writeBuffer(address, Memory.Scratch2, force);
1298
+ const x = f32(this.Scratch8.ptr);
1299
+ const y = f32(this.Scratch8.ptr, 0x04);
1188
1300
 
1189
- return this;
1190
- }
1191
-
1192
- /**
1193
- * Write a 16‑bit little-endian unsigned integer.
1194
- * @param address Destination address.
1195
- * @param value Value to write.
1196
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1197
- * @returns `this` for chaining.
1198
- * @throws {Win32Error} If the underlying write or protection change fails.
1199
- */
1301
+ return { x, y };
1302
+ }
1200
1303
 
1201
- public writeUInt16LE(address: bigint | number, value: number, force = false): this {
1202
- Memory.Scratch2.writeUInt16LE(value);
1304
+ this.Scratch8Buffer.writeFloatLE(value.x);
1305
+ this.Scratch8Buffer.writeFloatLE(value.y, 0x04);
1203
1306
 
1204
- this.writeBuffer(address, Memory.Scratch2, force);
1307
+ this.write(address, this.Scratch8);
1205
1308
 
1206
1309
  return this;
1207
1310
  }
1208
1311
 
1209
1312
  /**
1210
- * Write a 32‑bit big-endian unsigned integer.
1211
- * @param address Destination address.
1212
- * @param value Value to write.
1213
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1214
- * @returns `this` for chaining.
1215
- * @throws {Win32Error} If the underlying write or protection change fails.
1216
- */
1217
-
1218
- public writeUInt32BE(address: bigint | number, value: number, force = false): this {
1219
- Memory.Scratch4.writeUInt32BE(value);
1313
+ * Reads an array of 2D vectors from memory or writes an array of 2D vectors to memory.
1314
+ *
1315
+ * @param address - Memory address to read from or write to
1316
+ * @param lengthOrValues - Length of array to read, or array of vectors to write
1317
+ * @returns Array of Vector2 objects when reading, or this Memory instance when writing
1318
+ *
1319
+ * @example
1320
+ * ```typescript
1321
+ * const memory = new Memory('game.exe');
1322
+ *
1323
+ * // Read waypoints for AI pathfinding
1324
+ * const waypoints = memory.vector2Array(0x12345678n, 20);
1325
+ * console.log('AI waypoints:', waypoints);
1326
+ *
1327
+ * // Set new waypoints
1328
+ * const newWaypoints = [
1329
+ * { x: 10, y: 20 },
1330
+ * { x: 30, y: 40 },
1331
+ * { x: 50, y: 60 }
1332
+ * ];
1333
+ * memory.vector2Array(0x12345678n, newWaypoints);
1334
+ * ```
1335
+ */
1336
+ public vector2Array(address: bigint, length: number): Vector2[];
1337
+ public vector2Array(address: bigint, values: Vector2[]): this;
1338
+ public vector2Array(address: bigint, lengthOrValues: Vector2[] | number): Vector2[] | this {
1339
+ if (typeof lengthOrValues === 'number') {
1340
+ const length = lengthOrValues;
1341
+ const scratch = new Float32Array(length * 2);
1342
+
1343
+ this.read(address, scratch);
1344
+
1345
+ const result = new Array<Vector2>(length);
1346
+
1347
+ for (let i = 0, j = 0; i < length; i++, j += 0x02) {
1348
+ const x = scratch[j];
1349
+ const y = scratch[j + 0x01];
1350
+
1351
+ result[i] = { x, y };
1352
+ }
1220
1353
 
1221
- this.writeBuffer(address, Memory.Scratch4, force);
1354
+ return result;
1355
+ }
1222
1356
 
1223
- return this;
1224
- }
1357
+ const values = lengthOrValues;
1358
+ const scratch = new Float32Array(values.length * 0x02);
1225
1359
 
1226
- /**
1227
- * Write a 32‑bit little-endian unsigned integer.
1228
- * @param address Destination address.
1229
- * @param value Value to write.
1230
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1231
- * @returns `this` for chaining.
1232
- * @throws {Win32Error} If the underlying write or protection change fails.
1233
- */
1360
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x02) {
1361
+ const vector2 = values[i];
1234
1362
 
1235
- public writeUInt32LE(address: bigint | number, value: number, force = false): this {
1236
- Memory.Scratch4.writeUInt32LE(value);
1363
+ scratch[j] = vector2.x;
1364
+ scratch[j + 0x01] = vector2.y;
1365
+ }
1237
1366
 
1238
- this.writeBuffer(address, Memory.Scratch4, force);
1367
+ this.write(address, scratch);
1239
1368
 
1240
1369
  return this;
1241
1370
  }
1242
1371
 
1243
1372
  /**
1244
- * Write an 8‑bit unsigned integer.
1245
- * @param address Destination address.
1246
- * @param value Value to write.
1247
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1248
- * @returns `this` for chaining.
1249
- * @throws {Win32Error} If the underlying write or protection change fails.
1373
+ * Reads a 3D vector from memory or writes a 3D vector to memory.
1374
+ * Vectors are stored as three 32-bit floats: x, y, z.
1375
+ *
1376
+ * @param address - Memory address to read from or write to
1377
+ * @param value - Optional vector to write. If omitted, performs a read operation
1378
+ * @returns The Vector3 object when reading, or this Memory instance when writing
1379
+ *
1380
+ * @example
1381
+ * ```typescript
1382
+ * const memory = new Memory('game.exe');
1383
+ *
1384
+ * // Read player 3D position
1385
+ * const position = memory.vector3(0x12345678n);
1386
+ * console.log('Player 3D position:', position);
1387
+ *
1388
+ * // Teleport player to specific location
1389
+ * memory.vector3(0x12345678n, { x: 100, y: 50, z: 200 });
1390
+ * ```
1250
1391
  */
1392
+ public vector3(address: bigint): Vector3;
1393
+ public vector3(address: bigint, value: Vector3): this;
1394
+ public vector3(address: bigint, value?: Vector3): Vector3 | this {
1395
+ if (value === undefined) {
1396
+ this.read(address, this.Scratch12);
1251
1397
 
1252
- public writeUInt8(address: bigint | number, value: number, force = false): this {
1253
- Memory.Scratch1.writeUInt8(value);
1398
+ const x = f32(this.Scratch12.ptr);
1399
+ const y = f32(this.Scratch12.ptr, 0x04);
1400
+ const z = f32(this.Scratch12.ptr, 0x08);
1254
1401
 
1255
- this.writeBuffer(address, Memory.Scratch1, force);
1402
+ return { x, y, z };
1403
+ }
1404
+
1405
+ this.Scratch12Buffer.writeFloatLE(value.x);
1406
+ this.Scratch12Buffer.writeFloatLE(value.y, 0x04);
1407
+ this.Scratch12Buffer.writeFloatLE(value.z, 0x08);
1408
+
1409
+ this.write(address, this.Scratch12);
1256
1410
 
1257
1411
  return this;
1258
1412
  }
1259
1413
 
1260
- // + TODO: Implement scratch…
1261
-
1262
1414
  /**
1263
- * Write a big-endian unsigned integer of arbitrary byte length.
1264
- * @param address Destination address.
1265
- * @param value Value to write.
1266
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1267
- * @returns `this` for chaining.
1268
- * @throws {Win32Error} If the underlying write or protection change fails.
1269
- */
1270
-
1271
- public writeUIntBE(address: bigint | number, value: number, byteLength: number, force = false): this {
1272
- const buffer = Buffer.allocUnsafe(byteLength);
1273
- /* */ buffer.writeUIntBE(value, 0, byteLength);
1274
-
1275
- this.writeBuffer(address, buffer, force);
1415
+ * Reads an array of 3D vectors from memory or writes an array of 3D vectors to memory.
1416
+ *
1417
+ * @param address - Memory address to read from or write to
1418
+ * @param lengthOrValues - Length of array to read, or array of vectors to write
1419
+ * @returns Array of Vector3 objects when reading, or this Memory instance when writing
1420
+ *
1421
+ * @example
1422
+ * ```typescript
1423
+ * const memory = new Memory('game.exe');
1424
+ *
1425
+ * // Read vertex positions for 3D model
1426
+ * const vertices = memory.vector3Array(0x12345678n, 500);
1427
+ * console.log('3D vertices:', vertices);
1428
+ *
1429
+ * // Update vertex positions
1430
+ * const newVertices = [
1431
+ * { x: 1.0, y: 0.0, z: 0.0 },
1432
+ * { x: 0.0, y: 1.0, z: 0.0 },
1433
+ * { x: 0.0, y: 0.0, z: 1.0 }
1434
+ * ];
1435
+ * memory.vector3Array(0x12345678n, newVertices);
1436
+ * ```
1437
+ */
1438
+ public vector3Array(address: bigint, length: number): Vector3[];
1439
+ public vector3Array(address: bigint, values: Vector3[]): this;
1440
+ public vector3Array(address: bigint, lengthOrValues: Vector3[] | number): Vector3[] | this {
1441
+ if (typeof lengthOrValues === 'number') {
1442
+ const length = lengthOrValues;
1443
+ const scratch = new Float32Array(length * 0x03);
1444
+
1445
+ this.read(address, scratch);
1446
+
1447
+ const result = new Array<Vector3>(length);
1448
+
1449
+ for (let i = 0, j = 0; i < length; i++, j += 0x03) {
1450
+ const x = scratch[j];
1451
+ const y = scratch[j + 0x01];
1452
+ const z = scratch[j + 0x02];
1453
+
1454
+ result[i] = { x, y, z };
1455
+ }
1276
1456
 
1277
- return this;
1278
- }
1457
+ return result;
1458
+ }
1279
1459
 
1280
- // + TODO: Implement scratch…
1460
+ const values = lengthOrValues;
1461
+ const scratch = new Float32Array(values.length * 0x03);
1281
1462
 
1282
- /**
1283
- * Write a little-endian unsigned integer of arbitrary byte length.
1284
- * @param address Destination address.
1285
- * @param value Value to write.
1286
- * @param force When true, temporarily enables `PAGE_EXECUTE_READWRITE` to bypass protection.
1287
- * @returns `this` for chaining.
1288
- * @throws {Win32Error} If the underlying write or protection change fails.
1289
- */
1463
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
1464
+ const vector3 = values[i];
1290
1465
 
1291
- public writeUIntLE(address: bigint | number, value: number, byteLength: number, force = false): this {
1292
- const buffer = Buffer.allocUnsafe(byteLength);
1293
- /* */ buffer.writeUIntLE(value, 0, byteLength);
1466
+ scratch[j] = vector3.x;
1467
+ scratch[j + 0x01] = vector3.y;
1468
+ scratch[j + 0x02] = vector3.z;
1469
+ }
1294
1470
 
1295
- this.writeBuffer(address, buffer, force);
1471
+ this.write(address, scratch);
1296
1472
 
1297
1473
  return this;
1298
1474
  }