bun-memory 1.1.44 → 1.1.45

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/structs/Memory.ts +2493 -2456
package/structs/Memory.ts CHANGED
@@ -1,2456 +1,2493 @@
1
- import '../runtime/extensions';
2
-
3
- import { CString, type Pointer, ptr } from 'bun:ffi';
4
-
5
- import Kernel32, { INVALID_HANDLE_VALUE } from 'bun-kernel32';
6
-
7
- import type { Module, Point, QAngle, Quaternion, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
8
- import Win32Error from './Win32Error';
9
-
10
- /**
11
- * Provides cross-process memory manipulation for native applications.
12
- *
13
- * Use this class to read and write memory, access modules, and work with common data structures in external processes.
14
- *
15
- * Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
16
- *
17
- * @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
18
- * @todo When adding 32-bit support, several u64 will need changed to u64_fast.
19
- *
20
- * @example
21
- * ```ts
22
- * import Memory from 'bun-memory';
23
- * const cs2 = new Memory('cs2.exe');
24
- * const myFloat = cs2.f32(0x12345678n);
25
- * cs2.close();
26
- * ```
27
- */
28
- class Memory {
29
- /**
30
- * Opens a process by PID or executable name.
31
- * @param identifier Process ID or executable name.
32
- * @throws If the process cannot be found or opened.
33
- * @example
34
- * ```ts
35
- * const cs2 = new Memory('cs2.exe');
36
- * ```
37
- */
38
- constructor(identifier: number | string) {
39
- const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
40
-
41
- const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
42
- const th32ProcessID = 0;
43
-
44
- const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
45
-
46
- if (hSnapshot === -1n) {
47
- throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
48
- }
49
-
50
- const lppeBuffer = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
51
- /* */ lppeBuffer.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
52
-
53
- const lppe = lppeBuffer.ptr;
54
-
55
- const bProcess32FirstW = Kernel32.Process32FirstW(hSnapshot, lppe);
56
-
57
- if (!bProcess32FirstW) {
58
- Kernel32.CloseHandle(hSnapshot);
59
-
60
- throw new Win32Error('Process32FirstW', Kernel32.GetLastError());
61
- }
62
-
63
- do {
64
- const szExeFile = lppeBuffer.toString('utf16le', 0x2c, 0x234).replace(ReplaceTrailingNull, '');
65
- const th32ProcessID = lppeBuffer.readUInt32LE(0x08);
66
-
67
- if (
68
- (typeof identifier === 'number' && identifier !== th32ProcessID) || //
69
- (typeof identifier === 'string' && identifier !== szExeFile)
70
- ) {
71
- continue;
72
- }
73
-
74
- const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
75
- const inheritHandle = 0;
76
-
77
- const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
78
-
79
- if (hProcess === 0n) {
80
- Kernel32.CloseHandle(hSnapshot);
81
-
82
- throw new Win32Error('OpenProcess', Kernel32.GetLastError());
83
- }
84
-
85
- this.__modules = {};
86
-
87
- this.hProcess = hProcess;
88
- this.th32ProcessID = th32ProcessID;
89
-
90
- this.refresh();
91
-
92
- Kernel32.CloseHandle(hSnapshot);
93
-
94
- return;
95
- } while (Kernel32.Process32NextW(hSnapshot, lppe));
96
-
97
- Kernel32.CloseHandle(hSnapshot);
98
-
99
- throw new Error(`Process not found: ${identifier}.`);
100
- }
101
-
102
- /**
103
- * Map of loaded modules in the process, keyed by module name.
104
- * @example
105
- * ```ts
106
- * const cs2 = new Memory('cs2.exe');
107
- * const mainModule = cs2.modules['cs2.exe'];
108
- * ```
109
- */
110
- private __modules: { [key: string]: Readonly<Module> };
111
-
112
- /**
113
- * Regex patterns for matching hex strings and wildcards in memory scans.
114
- * Used by the pattern method.
115
- */
116
- private static readonly Patterns = {
117
- PatternMatchAll: /(?:[0-9A-Fa-f]{2})+/g,
118
- PatternTest: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
119
- ReplaceTrailingNull: /\0+$/,
120
- };
121
-
122
- /**
123
- * Scratch buffers and typed views for temporary FFI reads/writes.
124
- * Used internally for efficient memory access and conversions.
125
- */
126
- private readonly Scratch1 = new Uint8Array(0x01);
127
- private readonly Scratch1Int8Array = new Int8Array(this.Scratch1.buffer, this.Scratch1.byteOffset, 0x01);
128
-
129
- private readonly Scratch2 = new Uint8Array(0x02);
130
- private readonly Scratch2Int16Array = new Int16Array(this.Scratch2.buffer, this.Scratch2.byteOffset, 0x01);
131
- private readonly Scratch2Uint16Array = new Uint16Array(this.Scratch2.buffer, this.Scratch2.byteOffset, 0x01);
132
-
133
- private readonly Scratch3 = new Uint8Array(0x03);
134
-
135
- private readonly Scratch4 = new Uint8Array(0x04);
136
- private readonly Scratch4Float32Array = new Float32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
137
- private readonly Scratch4Int32Array = new Int32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
138
- private readonly Scratch4Uint32Array = new Uint32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
139
-
140
- private readonly Scratch8 = new Uint8Array(0x08);
141
- private readonly Scratch8BigInt64Array = new BigInt64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
142
- private readonly Scratch8BigUint64Array = new BigUint64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
143
- private readonly Scratch8Float32Array = new Float32Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x02);
144
- private readonly Scratch8Float64Array = new Float64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
145
-
146
- private readonly Scratch12 = new Uint8Array(0x0c);
147
- private readonly Scratch12Float32Array = new Float32Array(this.Scratch12.buffer, this.Scratch12.byteOffset, 0x03);
148
-
149
- private readonly Scratch16 = new Uint8Array(0x10);
150
- private readonly Scratch16Float32Array = new Float32Array(this.Scratch16.buffer, this.Scratch16.byteOffset, 0x04);
151
-
152
- private readonly Scratch48 = new Uint8Array([
153
- 0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xec, 0x28, 0xff, 0xd0, 0x48, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc4,
154
- 0x28, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155
- ]);
156
-
157
- private readonly Scratch112 = new Uint8Array(0x70);
158
-
159
- private readonly Scratch1080 = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
160
-
161
- private static TextDecoderUTF8 = new TextDecoder('utf-8');
162
-
163
- private static TextEncoderUTF8 = new TextEncoder('utf-8');
164
-
165
- private readonly hProcess: bigint;
166
- private readonly th32ProcessID: number;
167
-
168
- /**
169
- * Gets all loaded modules in the process.
170
- * @returns Map of module name to module info with base-relative helpers.
171
- * @example
172
- * ```ts
173
- * const cs2 = new Memory('cs2.exe');
174
- * const client = cs2.modules['client.dll'];
175
- * ```
176
- */
177
- public get modules(): Memory['__modules'] {
178
- return this.__modules;
179
- }
180
-
181
- /**
182
- * Disposes resources held by this Memory instance.
183
- * Called automatically when using `using` blocks.
184
- * @example
185
- * ```ts
186
- * using const mem = new Memory('cs2.exe');
187
- * // mem is disposed at the end of the block
188
- * ```
189
- */
190
- public [Symbol.dispose](): void {
191
- this.close();
192
-
193
- return;
194
- }
195
-
196
- /**
197
- * Asynchronously disposes resources held by this Memory instance.
198
- * Use in `await using` blocks for async cleanup.
199
- * @example
200
- * ```ts
201
- * await using const mem = new Memory('cs2.exe');
202
- * // mem is disposed asynchronously at the end of the block
203
- * ```
204
- */
205
- public [Symbol.asyncDispose](): Promise<void> {
206
- this.close();
207
-
208
- return Promise.resolve();
209
- }
210
-
211
- /**
212
- * Allocates memory in the remote process.
213
- * @param length Bytes to allocate.
214
- * @param protect Page protection (defaults to PAGE_READWRITE).
215
- * @returns Base address of the allocation.
216
- * @example
217
- * ```ts
218
- * const ptr = cs2.alloc(0x100);
219
- * ```
220
- */
221
- public alloc(length: number, protect: number = 0x04): bigint {
222
- const { hProcess } = this;
223
-
224
- if (length <= 0) {
225
- throw new RangeError('length must be greater than 0.');
226
- }
227
-
228
- const dwSize = BigInt(length);
229
- const flAllocationType = 0x3000; /* MEM_COMMIT | MEM_RESERVE */
230
- const flProtect = protect;
231
- const lpAddress = 0n;
232
-
233
- const lpBaseAddress = Kernel32.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
234
-
235
- if (lpBaseAddress === 0n) {
236
- throw new Win32Error('VirtualAllocEx', Kernel32.GetLastError());
237
- }
238
-
239
- return lpBaseAddress;
240
- }
241
-
242
- /**
243
- * Closes the process handle.
244
- * @example
245
- * ```ts
246
- * const cs2 = new Memory('cs2.exe');
247
- * cs2.close();
248
- * ```
249
- */
250
- public close(): void {
251
- Kernel32.CloseHandle(this.hProcess);
252
-
253
- return;
254
- }
255
-
256
- /**
257
- * Frees memory allocated in the remote process.
258
- * @param address Allocation base address.
259
- * @example
260
- * ```ts
261
- * const ptr = cs2.alloc(0x100);
262
- * cs2.free(ptr);
263
- * ```
264
- */
265
- public free(address: bigint): void {
266
- const { hProcess } = this;
267
-
268
- const dwFreeType = 0x8000; /* MEM_RELEASE */
269
- const dwSize = 0x00n;
270
- const lpAddress = address;
271
-
272
- const bVirtualFreeEx = !!Kernel32.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType);
273
-
274
- if (!bVirtualFreeEx) {
275
- throw new Win32Error('VirtualFreeEx', Kernel32.GetLastError());
276
- }
277
-
278
- return;
279
- }
280
-
281
- /**
282
- * Changes the page protection of a region.
283
- * @param address Base address to protect.
284
- * @param length Bytes to protect.
285
- * @param protect New protection flags.
286
- * @returns Previous protection flags.
287
- * @example
288
- * ```ts
289
- * const cs2 = new Memory('cs2.exe');
290
- * const previous = cs2.protection(0x12345678n, 0x1000, 0x40);
291
- * ```
292
- */
293
- public protection(address: bigint, length: number, protect: number): number {
294
- const { Scratch4Uint32Array, hProcess } = this;
295
-
296
- if (length <= 0) {
297
- throw new RangeError('length must be greater than 0.');
298
- }
299
-
300
- const dwSize = BigInt(length);
301
- const flNewProtect = protect;
302
- const lpAddress = address;
303
- const lpflOldProtect = Scratch4Uint32Array.ptr;
304
-
305
- const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
306
-
307
- if (!bVirtualProtectEx) {
308
- throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
309
- }
310
-
311
- return Scratch4Uint32Array[0x00];
312
- }
313
-
314
- /**
315
- * Reads memory into a buffer.
316
- * @param address Address to read from.
317
- * @param scratch Buffer to fill.
318
- * @returns The filled buffer.
319
- * @todo Consider inlining the call in the if to cut a binding… I hate the idea… 🫠…
320
- * @example
321
- * ```ts
322
- * const cs2 = new Memory('cs2.exe');
323
- * const myBuffer = cs2.read(0x12345678n, new Uint8Array(4));
324
- * ```
325
- */
326
- public read<T extends Scratch>(address: bigint, scratch: T): T {
327
- const { hProcess } = this;
328
-
329
- const lpBaseAddress = address;
330
- const lpBuffer = ptr(scratch);
331
- const nSize = BigInt(scratch.byteLength);
332
- const numberOfBytesRead = 0x00n;
333
-
334
- const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
335
-
336
- if (!bReadProcessMemory) {
337
- throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
338
- }
339
-
340
- return scratch;
341
- }
342
-
343
- /**
344
- * Refreshes the module list for the process.
345
- * @example
346
- * ```ts
347
- * const cs2 = new Memory('cs2.exe');
348
- * cs2.refresh();
349
- * ```
350
- */
351
- public refresh(): void {
352
- const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
353
- const { th32ProcessID } = this;
354
-
355
- const dwFlags = 0x00000018; /* TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 */
356
-
357
- const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID)!;
358
-
359
- if (hSnapshot === INVALID_HANDLE_VALUE) {
360
- throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
361
- }
362
-
363
- const { Scratch1080: lpme } = this;
364
- /* */ lpme.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
365
-
366
- const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, ptr(lpme));
367
-
368
- if (!bModule32FirstW) {
369
- Kernel32.CloseHandle(hSnapshot);
370
-
371
- throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
372
- }
373
-
374
- const modules: Memory['__modules'] = {};
375
-
376
- do {
377
- const modBaseAddr = lpme.readBigUInt64LE(0x18);
378
- const modBaseSize = lpme.readUInt32LE(0x20);
379
- const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(ReplaceTrailingNull, '');
380
-
381
- modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
382
- } while (Kernel32.Module32NextW(hSnapshot, ptr(lpme)));
383
-
384
- Kernel32.CloseHandle(hSnapshot);
385
-
386
- this.__modules = Object.freeze(modules);
387
-
388
- return;
389
- }
390
-
391
- /**
392
- * Writes a buffer to memory.
393
- * @param address Address to write to.
394
- * @param scratch Buffer to write.
395
- * @param force When writing, if true temporarily changes page protection to allow the write.
396
- * @returns This instance.
397
- * @example
398
- * ```ts
399
- * const cs2 = new Memory('cs2.exe');
400
- * cs2.write(0x12345678n, new Uint8Array([1,2,3,4]));
401
- * // Force a write by temporarily changing memory protection
402
- * cs2.write(0x12345678n, new Uint8Array([1,2,3,4]), true);
403
- * ```
404
- */
405
- public write(address: bigint, scratch: Scratch, force: boolean = false): this {
406
- const { hProcess } = this;
407
-
408
- const lpBaseAddress = address;
409
- const lpBuffer = scratch.ptr;
410
- const nSize = BigInt(scratch.byteLength);
411
- const numberOfBytesWritten = 0x00n;
412
-
413
- if (!force) {
414
- const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
415
-
416
- if (!bWriteProcessMemory) {
417
- throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
418
- }
419
-
420
- return this;
421
- }
422
-
423
- const dwSize = nSize;
424
- const flNewProtect = 0x40; /* PAGE_EXECUTE_READWRITE */
425
- const lpflOldProtect = Buffer.allocUnsafe(0x04);
426
-
427
- const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect, lpflOldProtect.ptr);
428
-
429
- if (!bVirtualProtectEx) {
430
- throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
431
- }
432
-
433
- try {
434
- const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
435
-
436
- if (!bWriteProcessMemory) {
437
- throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
438
- }
439
- } finally {
440
- const flNewProtect2 = lpflOldProtect.readUInt32LE(0x00);
441
- const lpflOldProtect2 = Buffer.allocUnsafe(0x04);
442
-
443
- const bVirtualProtectEx2 = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect2, lpflOldProtect2.ptr);
444
-
445
- if (!bVirtualProtectEx2) {
446
- throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
447
- }
448
- }
449
-
450
- return this;
451
- }
452
-
453
- /**
454
- * Reads or writes a boolean value.
455
- * @param address Address to access.
456
- * @param value Optional value to write.
457
- * @param force When writing, if true temporarily changes page protection to allow the write.
458
- * @returns The boolean at address, or this instance if writing.
459
- * @example
460
- * ```ts
461
- * const cs2 = new Memory('cs2.exe');
462
- * const myBool = cs2.bool(0x12345678n);
463
- * cs2.bool(0x12345678n, true);
464
- * ```
465
- */
466
- public bool(address: bigint): boolean;
467
- public bool(address: bigint, value: boolean, force?: boolean): this;
468
- public bool(address: bigint, value?: boolean, force?: boolean): boolean | this {
469
- const { Scratch1 } = this;
470
-
471
- if (value === undefined) {
472
- return this.read(address, Scratch1)[0x00] !== 0;
473
- }
474
-
475
- Scratch1[0x00] = value ? 0x01 : 0x00;
476
-
477
- void this.write(address, Scratch1, force);
478
-
479
- return this;
480
- }
481
-
482
- /**
483
- * Reads or writes a Buffer.
484
- * @param address Address to access.
485
- * @param lengthOrValue Length to read or Buffer to write.
486
- * @param force When writing, if true temporarily changes page protection to allow the write.
487
- * @returns Buffer read or this instance if writing.
488
- * @example
489
- * ```ts
490
- * const cs2 = new Memory('cs2.exe');
491
- * const myBuffer = cs2.buffer(0x12345678n, 8);
492
- * cs2.buffer(0x12345678n, Buffer.from([1,2,3,4]));
493
- * ```
494
- */
495
- public buffer(address: bigint, length: number): Buffer;
496
- public buffer(address: bigint, value: Buffer, force?: boolean): this;
497
- public buffer(address: bigint, lengthOrValue: number | Buffer, force?: boolean): Buffer | this {
498
- if (typeof lengthOrValue === 'number') {
499
- const length = lengthOrValue;
500
- const scratch = Buffer.allocUnsafe(length);
501
-
502
- return this.read(address, scratch);
503
- }
504
-
505
- const value = lengthOrValue;
506
-
507
- void this.write(address, value, force);
508
-
509
- return this;
510
- }
511
-
512
- /**
513
- * Reads or writes a C-style string.
514
- * @param address Address to access.
515
- * @param lengthOrValue Length to read or CString to write.
516
- * @param force When writing, if true temporarily changes page protection to allow the write.
517
- * @returns CString read or this instance if writing.
518
- * @example
519
- * ```ts
520
- * const cs2 = new Memory('cs2.exe');
521
- * const myCString = cs2.cString(0x12345678n, 16);
522
- * cs2.cString(0x12345678n, new CString('hello'));
523
- * ```
524
- */
525
- public cString(address: bigint, length: number): CString;
526
- public cString(address: bigint, value: CString, force?: boolean): this;
527
- public cString(address: bigint, lengthOrValue: number | CString, force?: boolean): CString | this {
528
- if (typeof lengthOrValue === 'number') {
529
- const scratch = new Uint8Array(lengthOrValue);
530
-
531
- void this.read(address, scratch);
532
-
533
- const indexOf = scratch.indexOf(0x00);
534
-
535
- if (indexOf === -1) {
536
- scratch[lengthOrValue - 1] = 0x00;
537
- }
538
-
539
- return new CString(scratch.ptr);
540
- }
541
-
542
- const scratch = Buffer.from(lengthOrValue);
543
-
544
- void this.write(address, scratch, force);
545
-
546
- return this;
547
- }
548
-
549
- /**
550
- * Reads or writes a 32-bit float.
551
- * @param address Address to access.
552
- * @param value Optional value to write.
553
- * @param force When writing, if true temporarily changes page protection to allow the write.
554
- * @returns The float at address, or this instance if writing.
555
- * @example
556
- * ```ts
557
- * const cs2 = new Memory('cs2.exe');
558
- * const myFloat = cs2.f32(0x12345678n);
559
- * cs2.f32(0x12345678n, 1.23);
560
- * ```
561
- */
562
- public f32(address: bigint): number;
563
- public f32(address: bigint, value: number, force?: boolean): this;
564
- public f32(address: bigint, value?: number, force?: boolean): number | this {
565
- const { Scratch4Float32Array } = this; // prettier-ignore
566
-
567
- if (value === undefined) {
568
- return this.read(address, Scratch4Float32Array)[0x00];
569
- }
570
-
571
- Scratch4Float32Array[0x00] = value;
572
-
573
- void this.write(address, Scratch4Float32Array, force);
574
-
575
- return this;
576
- }
577
-
578
- /**
579
- * Reads or writes a Float32Array.
580
- * @param address Address to access.
581
- * @param lengthOrValues Length to read or Float32Array to write.
582
- * @param force When writing, if true temporarily changes page protection to allow the write.
583
- * @returns Float32Array read or this instance if writing.
584
- * @example
585
- * ```ts
586
- * const cs2 = new Memory('cs2.exe');
587
- * const myArray = cs2.f32Array(0x12345678n, 3);
588
- * cs2.f32Array(0x12345678n, new Float32Array([1,2,3]));
589
- * ```
590
- */
591
- public f32Array(address: bigint, length: number): Float32Array;
592
- public f32Array(address: bigint, values: Float32Array, force?: boolean): this;
593
- public f32Array(address: bigint, lengthOrValues: Float32Array | number, force?: boolean): Float32Array | this {
594
- if (typeof lengthOrValues === 'number') {
595
- const length = lengthOrValues;
596
- const scratch = new Float32Array(length);
597
-
598
- void this.read(address, scratch);
599
-
600
- return scratch;
601
- }
602
-
603
- const values = lengthOrValues;
604
-
605
- void this.write(address, values, force);
606
-
607
- return this;
608
- }
609
-
610
- /**
611
- * Reads or writes a 64-bit float.
612
- * @param address Address to access.
613
- * @param value Optional value to write.
614
- * @param force When writing, if true temporarily changes page protection to allow the write.
615
- * @returns The float at address, or this instance if writing.
616
- * @example
617
- * ```ts
618
- * const cs2 = new Memory('cs2.exe');
619
- * const myFloat = cs2.f64(0x12345678n);
620
- * cs2.f64(0x12345678n, 1.23);
621
- * ```
622
- */
623
- public f64(address: bigint): number;
624
- public f64(address: bigint, value: number, force?: boolean): this;
625
- public f64(address: bigint, value?: number, force?: boolean): number | this {
626
- const { Scratch8Float64Array } = this; // prettier-ignore
627
-
628
- if (value === undefined) {
629
- return this.read(address, Scratch8Float64Array)[0x00];
630
- }
631
-
632
- Scratch8Float64Array[0x00] = value;
633
-
634
- void this.write(address, Scratch8Float64Array, force);
635
-
636
- return this;
637
- }
638
-
639
- /**
640
- * Reads or writes a Float64Array.
641
- * @param address Address to access.
642
- * @param lengthOrValues Length to read or Float64Array to write.
643
- * @param force When writing, if true temporarily changes page protection to allow the write.
644
- * @returns Float64Array read or this instance if writing.
645
- * @example
646
- * ```ts
647
- * const cs2 = new Memory('cs2.exe');
648
- * const myArray = cs2.f64Array(0x12345678n, 2);
649
- * cs2.f64Array(0x12345678n, new Float64Array([1,2]));
650
- * ```
651
- */
652
- public f64Array(address: bigint, length: number): Float64Array;
653
- public f64Array(address: bigint, values: Float64Array, force?: boolean): this;
654
- public f64Array(address: bigint, lengthOrValues: Float64Array | number, force?: boolean): Float64Array | this {
655
- if (typeof lengthOrValues === 'number') {
656
- const length = lengthOrValues;
657
- const scratch = new Float64Array(length);
658
-
659
- void this.read(address, scratch);
660
-
661
- return scratch;
662
- }
663
-
664
- const values = lengthOrValues;
665
-
666
- void this.write(address, values, force);
667
-
668
- return this;
669
- }
670
-
671
- /**
672
- * Reads or writes a 16-bit integer.
673
- * @param address Address to access.
674
- * @param value Optional value to write.
675
- * @param force When writing, if true temporarily changes page protection to allow the write.
676
- * @returns The int at address, or this instance if writing.
677
- * @example
678
- * ```ts
679
- * const cs2 = new Memory('cs2.exe');
680
- * const myInt = cs2.i16(0x12345678n);
681
- * cs2.i16(0x12345678n, 42);
682
- * ```
683
- */
684
- public i16(address: bigint): number;
685
- public i16(address: bigint, value: number, force?: boolean): this;
686
- public i16(address: bigint, value?: number, force?: boolean): number | this {
687
- const { Scratch2Int16Array } = this; // prettier-ignore
688
-
689
- if (value === undefined) {
690
- return this.read(address, Scratch2Int16Array)[0x00];
691
- }
692
-
693
- Scratch2Int16Array[0x00] = value;
694
-
695
- void this.write(address, Scratch2Int16Array, force);
696
-
697
- return this;
698
- }
699
-
700
- /**
701
- * Reads or writes an Int16Array.
702
- * @param address Address to access.
703
- * @param lengthOrValues Length to read or Int16Array to write.
704
- * @param force When writing, if true temporarily changes page protection to allow the write.
705
- * @returns Int16Array read or this instance if writing.
706
- * @example
707
- * ```ts
708
- * const cs2 = new Memory('cs2.exe');
709
- * const myArray = cs2.i16Array(0x12345678n, 2);
710
- * cs2.i16Array(0x12345678n, new Int16Array([1,2]));
711
- * ```
712
- */
713
- public i16Array(address: bigint, length: number): Int16Array;
714
- public i16Array(address: bigint, values: Int16Array, force?: boolean): this;
715
- public i16Array(address: bigint, lengthOrValues: Int16Array | number, force?: boolean): Int16Array | this {
716
- if (typeof lengthOrValues === 'number') {
717
- const length = lengthOrValues;
718
- const scratch = new Int16Array(length);
719
-
720
- void this.read(address, scratch);
721
-
722
- return scratch;
723
- }
724
-
725
- const values = lengthOrValues;
726
-
727
- void this.write(address, values, force);
728
-
729
- return this;
730
- }
731
-
732
- /**
733
- * Reads or writes a 32-bit integer.
734
- * @param address Address to access.
735
- * @param value Optional value to write.
736
- * @param force When writing, if true temporarily changes page protection to allow the write.
737
- * @returns The int at address, or this instance if writing.
738
- * @example
739
- * ```ts
740
- * const cs2 = new Memory('cs2.exe');
741
- * const myInt = cs2.i32(0x12345678n);
742
- * cs2.i32(0x12345678n, 42);
743
- * ```
744
- */
745
- public i32(address: bigint): number;
746
- public i32(address: bigint, value: number, force?: boolean): this;
747
- public i32(address: bigint, value?: number, force?: boolean): number | this {
748
- const { Scratch4Int32Array } = this;
749
-
750
- if (value === undefined) {
751
- return this.read(address, Scratch4Int32Array)[0x00];
752
- }
753
-
754
- Scratch4Int32Array[0x00] = value;
755
-
756
- void this.write(address, Scratch4Int32Array, force);
757
-
758
- return this;
759
- }
760
-
761
- /**
762
- * Reads or writes an Int32Array.
763
- * @param address Address to access.
764
- * @param lengthOrValues Length to read or Int32Array to write.
765
- * @param force When writing, if true temporarily changes page protection to allow the write.
766
- * @returns Int32Array read or this instance if writing.
767
- * @example
768
- * ```ts
769
- * const cs2 = new Memory('cs2.exe');
770
- * const myArray = cs2.i32Array(0x12345678n, 2);
771
- * cs2.i32Array(0x12345678n, new Int32Array([1,2]));
772
- * ```
773
- */
774
- public i32Array(address: bigint, length: number): Int32Array;
775
- public i32Array(address: bigint, values: Int32Array, force?: boolean): this;
776
- public i32Array(address: bigint, lengthOrValues: Int32Array | number, force?: boolean): Int32Array | this {
777
- if (typeof lengthOrValues === 'number') {
778
- const length = lengthOrValues;
779
- const scratch = new Int32Array(length);
780
-
781
- void this.read(address, scratch);
782
-
783
- return scratch;
784
- }
785
-
786
- const values = lengthOrValues;
787
-
788
- void this.write(address, values, force);
789
-
790
- return this;
791
- }
792
-
793
- /**
794
- * Reads or writes a 64-bit integer.
795
- * @param address Address to access.
796
- * @param value Optional value to write.
797
- * @param force When writing, if true temporarily changes page protection to allow the write.
798
- * @returns The bigint at address, or this instance if writing.
799
- * @example
800
- * ```ts
801
- * const cs2 = new Memory('cs2.exe');
802
- * const myBigInt = cs2.i64(0x12345678n);
803
- * cs2.i64(0x12345678n, 123n);
804
- * ```
805
- */
806
- public i64(address: bigint): bigint;
807
- public i64(address: bigint, value: bigint, force?: boolean): this;
808
- public i64(address: bigint, value?: bigint, force?: boolean): bigint | this {
809
- const { Scratch8BigInt64Array } = this;
810
-
811
- if (value === undefined) {
812
- return this.read(address, Scratch8BigInt64Array)[0x00];
813
- }
814
-
815
- Scratch8BigInt64Array[0x00] = value;
816
-
817
- void this.write(address, Scratch8BigInt64Array, force);
818
-
819
- return this;
820
- }
821
-
822
- /**
823
- * Reads or writes a BigInt64Array.
824
- * @param address Address to access.
825
- * @param lengthOrValues Length to read or BigInt64Array to write.
826
- * @param force When writing, if true temporarily changes page protection to allow the write.
827
- * @returns BigInt64Array read or this instance if writing.
828
- * @example
829
- * ```ts
830
- * const cs2 = new Memory('cs2.exe');
831
- * const myArray = cs2.i64Array(0x12345678n, 2);
832
- * cs2.i64Array(0x12345678n, new BigInt64Array([1n,2n]));
833
- * ```
834
- */
835
- public i64Array(address: bigint, length: number): BigInt64Array;
836
- public i64Array(address: bigint, values: BigInt64Array, force?: boolean): this;
837
- public i64Array(address: bigint, lengthOrValues: BigInt64Array | number, force?: boolean): BigInt64Array | this {
838
- if (typeof lengthOrValues === 'number') {
839
- const length = lengthOrValues;
840
- const scratch = new BigInt64Array(length);
841
-
842
- void this.read(address, scratch);
843
-
844
- return scratch;
845
- }
846
-
847
- const values = lengthOrValues;
848
-
849
- void this.write(address, values, force);
850
-
851
- return this;
852
- }
853
-
854
- /**
855
- * Reads or writes an 8-bit integer.
856
- * @param address Address to access.
857
- * @param value Optional value to write.
858
- * @param force When writing, if true temporarily changes page protection to allow the write.
859
- * @returns The int at address, or this instance if writing.
860
- * @example
861
- * ```ts
862
- * const cs2 = new Memory('cs2.exe');
863
- * const myInt = cs2.i8(0x12345678n);
864
- * cs2.i8(0x12345678n, 7);
865
- * ```
866
- */
867
- public i8(address: bigint): number;
868
- public i8(address: bigint, value: number, force?: boolean): this;
869
- public i8(address: bigint, value?: number, force?: boolean): number | this {
870
- const { Scratch1Int8Array } = this;
871
-
872
- if (value === undefined) {
873
- return this.read(address, Scratch1Int8Array)[0x00];
874
- }
875
-
876
- Scratch1Int8Array[0x00] = value;
877
-
878
- void this.write(address, Scratch1Int8Array, force);
879
-
880
- return this;
881
- }
882
-
883
- /**
884
- * Reads or writes an Int8Array.
885
- * @param address Address to access.
886
- * @param lengthOrValues Length to read or Int8Array to write.
887
- * @param force When writing, if true temporarily changes page protection to allow the write.
888
- * @returns Int8Array read or this instance if writing.
889
- * @example
890
- * ```ts
891
- * const cs2 = new Memory('cs2.exe');
892
- * const myArray = cs2.i8Array(0x12345678n, 2);
893
- * cs2.i8Array(0x12345678n, new Int8Array([1,2]));
894
- * ```
895
- */
896
- public i8Array(address: bigint, length: number): Int8Array;
897
- public i8Array(address: bigint, values: Int8Array, force?: boolean): this;
898
- public i8Array(address: bigint, lengthOrValues: Int8Array | number, force?: boolean): Int8Array | this {
899
- if (typeof lengthOrValues === 'number') {
900
- const length = lengthOrValues;
901
- const scratch = new Int8Array(length);
902
-
903
- void this.read(address, scratch);
904
-
905
- return scratch;
906
- }
907
-
908
- const values = lengthOrValues;
909
-
910
- void this.write(address, values, force);
911
-
912
- return this;
913
- }
914
-
915
- /**
916
- * Reads or writes a 3x3 matrix (Float32Array of length 9).
917
- * @param address Address to access.
918
- * @param values Optional Float32Array to write.
919
- * @param force When writing, if true temporarily changes page protection to allow the write.
920
- * @returns The matrix at address, or this instance if writing.
921
- * @example
922
- * ```ts
923
- * const cs2 = new Memory('cs2.exe');
924
- * const myMatrix = cs2.matrix3x3(0x12345678n);
925
- * cs2.matrix3x3(0x12345678n, new Float32Array(9));
926
- * ```
927
- */
928
- public matrix3x3(address: bigint): Float32Array;
929
- public matrix3x3(address: bigint, values: Float32Array, force?: boolean): this;
930
- public matrix3x3(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
931
- if (values === undefined) {
932
- const scratch = new Float32Array(0x09);
933
-
934
- void this.read(address, scratch);
935
-
936
- return scratch;
937
- }
938
-
939
- if (values.length !== 0x09) {
940
- throw new RangeError('values.length must be 9.');
941
- }
942
-
943
- void this.write(address, values, force);
944
-
945
- return this;
946
- }
947
-
948
- /**
949
- * Reads or writes a 3x4 matrix (Float32Array of length 12).
950
- * @param address Address to access.
951
- * @param values Optional Float32Array to write.
952
- * @param force When writing, if true temporarily changes page protection to allow the write.
953
- * @returns The matrix at address, or this instance if writing.
954
- * @example
955
- * ```ts
956
- * const cs2 = new Memory('cs2.exe');
957
- * const myMatrix = cs2.matrix3x4(0x12345678n);
958
- * cs2.matrix3x4(0x12345678n, new Float32Array(12));
959
- * ```
960
- */
961
- public matrix3x4(address: bigint): Float32Array;
962
- public matrix3x4(address: bigint, values: Float32Array, force?: boolean): this;
963
- public matrix3x4(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
964
- if (values === undefined) {
965
- const scratch = new Float32Array(0x0c);
966
-
967
- void this.read(address, scratch);
968
-
969
- return scratch;
970
- }
971
-
972
- if (values.length !== 0x0c) {
973
- throw new RangeError('values.length must be 12.');
974
- }
975
-
976
- void this.write(address, values, force);
977
-
978
- return this;
979
- }
980
-
981
- /**
982
- * Reads or writes a 4x4 matrix (Float32Array of length 16).
983
- * @param address Address to access.
984
- * @param values Optional Float32Array to write.
985
- * @param force When writing, if true temporarily changes page protection to allow the write.
986
- * @returns The matrix at address, or this instance if writing.
987
- * @example
988
- * ```ts
989
- * const cs2 = new Memory('cs2.exe');
990
- * const myMatrix = cs2.matrix4x4(0x12345678n);
991
- * cs2.matrix4x4(0x12345678n, new Float32Array(16));
992
- * ```
993
- */
994
- public matrix4x4(address: bigint): Float32Array;
995
- public matrix4x4(address: bigint, values: Float32Array, force?: boolean): this;
996
- public matrix4x4(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
997
- if (values === undefined) {
998
- const scratch = new Float32Array(0x10);
999
-
1000
- void this.read(address, scratch);
1001
-
1002
- return scratch;
1003
- }
1004
-
1005
- if (values.length !== 0x10) {
1006
- throw new RangeError('values.length must be 16.');
1007
- }
1008
-
1009
- void this.write(address, values, force);
1010
-
1011
- return this;
1012
- }
1013
-
1014
- /**
1015
- * Reads or writes a Point (object with x, y).
1016
- * @param address Address to access.
1017
- * @param value Optional Point to write.
1018
- * @param force When writing, if true temporarily changes page protection to allow the write.
1019
- * @returns The point at address, or this instance if writing.
1020
- * @example
1021
- * ```ts
1022
- * const cs2 = new Memory('cs2.exe');
1023
- * const myPoint = cs2.point(0x12345678n);
1024
- * cs2.point(0x12345678n, { x: 1, y: 2 });
1025
- * ```
1026
- */
1027
- public point(address: bigint): Point;
1028
- public point(address: bigint, value: Point, force?: boolean): this;
1029
- public point(address: bigint, value?: Point, force?: boolean): Point | this {
1030
- const { Scratch8Float32Array } = this;
1031
-
1032
- if (value === undefined) {
1033
- void this.read(address, Scratch8Float32Array);
1034
-
1035
- const x = Scratch8Float32Array[0x00],
1036
- y = Scratch8Float32Array[0x01]; // prettier-ignore
1037
-
1038
- return { x, y };
1039
- }
1040
-
1041
- Scratch8Float32Array[0x00] = value.x;
1042
- Scratch8Float32Array[0x01] = value.y;
1043
-
1044
- void this.write(address, Scratch8Float32Array, force);
1045
-
1046
- return this;
1047
- }
1048
-
1049
- /**
1050
- * Reads or writes an array of Points.
1051
- * @param address Address to access.
1052
- * @param lengthOrValues Length to read or array to write.
1053
- * @param force When writing, if true temporarily changes page protection to allow the write.
1054
- * @returns Array of points read or this instance if writing.
1055
- * @example
1056
- * ```ts
1057
- * const cs2 = new Memory('cs2.exe');
1058
- * const myPoints = cs2.pointArray(0x12345678n, 2);
1059
- * cs2.pointArray(0x12345678n, [{ x: 1, y: 2 }, { x: 3, y: 4 }]);
1060
- * ```
1061
- */
1062
- public pointArray(address: bigint, length: number): Point[];
1063
- public pointArray(address: bigint, value: Point[], force?: boolean): this;
1064
- public pointArray(address: bigint, lengthOrValues: number | Point[], force?: boolean): Point[] | this {
1065
- if (typeof lengthOrValues === 'number') {
1066
- const length = lengthOrValues;
1067
- const scratch = new Float32Array(length * 2);
1068
-
1069
- void this.read(address, scratch);
1070
-
1071
- const result = new Array<Vector2>(length);
1072
-
1073
- for (let i = 0, j = 0; i < length; i++, j += 0x02) {
1074
- const x = scratch[j],
1075
- y = scratch[j + 0x01]; // prettier-ignore
1076
-
1077
- result[i] = { x, y };
1078
- }
1079
-
1080
- return result;
1081
- }
1082
-
1083
- const values = lengthOrValues;
1084
- const scratch = new Float32Array(values.length * 0x02);
1085
-
1086
- for (let i = 0, j = 0; i < values.length; i++, j += 0x02) {
1087
- const vector2 = values[i];
1088
-
1089
- scratch[j] = vector2.x;
1090
- scratch[j + 0x01] = vector2.y;
1091
- }
1092
-
1093
- void this.write(address, scratch, force);
1094
-
1095
- return this;
1096
- }
1097
-
1098
- /**
1099
- * Reads or writes a raw Point (two Float32 values) as a Float32Array.
1100
- * @param address Address to access.
1101
- * @param values Optional Float32Array of length 2 to write.
1102
- * @param force When writing, if true temporarily changes page protection to allow the write.
1103
- * @returns Float32Array read or this instance if writing.
1104
- * @example
1105
- * ```ts
1106
- * const cs2 = new Memory('cs2.exe');
1107
- * const myPoint = cs2.pointRaw(0x12345678n);
1108
- * cs2.pointRaw(0x12345678n, new Float32Array([1, 2]));
1109
- * ```
1110
- */
1111
- public pointRaw(address: bigint): Float32Array;
1112
- public pointRaw(address: bigint, values: Float32Array, force?: boolean): this;
1113
- public pointRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1114
- if (values === undefined) {
1115
- return this.f32Array(address, 0x02);
1116
- }
1117
-
1118
- if (values.length !== 0x02) {
1119
- throw new RangeError('values.length must be 2.');
1120
- }
1121
-
1122
- void this.write(address, values, force);
1123
-
1124
- return this;
1125
- }
1126
-
1127
- /**
1128
- * Reads or writes a QAngle (object with pitch, yaw, roll).
1129
- * @param address Address to access.
1130
- * @param value Optional QAngle to write.
1131
- * @returns The QAngle at address, or this instance if writing.
1132
- * @example
1133
- * ```ts
1134
- * const cs2 = new Memory('cs2.exe');
1135
- * const myQAngle = cs2.qAngle(0x12345678n);
1136
- * cs2.qAngle(0x12345678n, { pitch: 1, yaw: 2, roll: 3 });
1137
- * ```
1138
- */
1139
- public qAngle(address: bigint): QAngle;
1140
- public qAngle(address: bigint, value: QAngle, force?: boolean): this;
1141
- public qAngle(address: bigint, value?: QAngle, force?: boolean): QAngle | this {
1142
- const { Scratch12Float32Array } = this;
1143
-
1144
- if (value === undefined) {
1145
- void this.read(address, Scratch12Float32Array);
1146
-
1147
- const pitch = Scratch12Float32Array[0x00],
1148
- roll = Scratch12Float32Array[0x02],
1149
- yaw = Scratch12Float32Array[0x01]; // prettier-ignore
1150
-
1151
- return { pitch, roll, yaw };
1152
- }
1153
-
1154
- Scratch12Float32Array[0x00] = value.pitch;
1155
- Scratch12Float32Array[0x02] = value.roll;
1156
- Scratch12Float32Array[0x01] = value.yaw;
1157
-
1158
- void this.write(address, Scratch12Float32Array, force);
1159
-
1160
- return this;
1161
- }
1162
-
1163
- /**
1164
- * Reads or writes an array of QAngles.
1165
- * @param address Address to access.
1166
- * @param lengthOrValues Length to read or array to write.
1167
- * @returns Array of QAngles read or this instance if writing.
1168
- * @example
1169
- * ```ts
1170
- * const cs2 = new Memory('cs2.exe');
1171
- * const myQAngles = cs2.qAngleArray(0x12345678n, 2);
1172
- * cs2.qAngleArray(0x12345678n, [{ pitch: 1, yaw: 2, roll: 3 }]);
1173
- * ```
1174
- */
1175
- public qAngleArray(address: bigint, length: number): QAngle[];
1176
- public qAngleArray(address: bigint, values: QAngle[], force?: boolean): this;
1177
- public qAngleArray(address: bigint, lengthOrValues: QAngle[] | number, force?: boolean): QAngle[] | this {
1178
- if (typeof lengthOrValues === 'number') {
1179
- const length = lengthOrValues;
1180
- const scratch = new Float32Array(length * 0x03);
1181
-
1182
- void this.read(address, scratch);
1183
-
1184
- const result = new Array<QAngle>(length);
1185
-
1186
- for (let i = 0, j = 0; i < length; i++, j += 0x03) {
1187
- const pitch = scratch[j],
1188
- yaw = scratch[j + 0x01],
1189
- roll = scratch[j + 0x02]; // prettier-ignore
1190
-
1191
- result[i] = { pitch, yaw, roll };
1192
- }
1193
-
1194
- return result;
1195
- }
1196
-
1197
- const values = lengthOrValues;
1198
- const scratch = new Float32Array(values.length * 0x03);
1199
-
1200
- for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
1201
- const qAngle = values[i];
1202
-
1203
- scratch[j] = qAngle.pitch;
1204
- scratch[j + 0x02] = qAngle.roll;
1205
- scratch[j + 0x01] = qAngle.yaw;
1206
- }
1207
-
1208
- void this.write(address, scratch, force);
1209
-
1210
- return this;
1211
- }
1212
-
1213
- /**
1214
- * Reads or writes a raw QAngle as a Float32Array.
1215
- * @param address Address to access.
1216
- * @param values Optional Float32Array to write.
1217
- * @param force When writing, if true temporarily changes page protection to allow the write.
1218
- * @returns Float32Array read or this instance if writing.
1219
- * @example
1220
- * ```ts
1221
- * const cs2 = new Memory('cs2.exe');
1222
- * const raw = cs2.qAngleRaw(0x12345678n);
1223
- * cs2.qAngleRaw(0x12345678n, new Float32Array([1,2,3]));
1224
- * ```
1225
- */
1226
- public qAngleRaw(address: bigint): Float32Array;
1227
- public qAngleRaw(address: bigint, values: Float32Array, force?: boolean): this;
1228
- public qAngleRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1229
- if (values === undefined) {
1230
- return this.f32Array(address, 0x03);
1231
- }
1232
-
1233
- if (values.length !== 0x03) {
1234
- throw new RangeError('values.length must be 3.');
1235
- }
1236
-
1237
- void this.write(address, values, force);
1238
-
1239
- return this;
1240
- }
1241
-
1242
- /**
1243
- * Reads or writes a Quaternion (object with w, x, y, z).
1244
- * @param address Address to access.
1245
- * @param value Optional Quaternion to write.
1246
- * @param force When writing, if true temporarily changes page protection to allow the write.
1247
- * @returns The Quaternion at address, or this instance if writing.
1248
- * @example
1249
- * ```ts
1250
- * const cs2 = new Memory('cs2.exe');
1251
- * const myQuaternion = cs2.quaternion(0x12345678n);
1252
- * cs2.quaternion(0x12345678n, { w: 1, x: 0, y: 0, z: 0 });
1253
- * ```
1254
- */
1255
- public quaternion(address: bigint): Quaternion;
1256
- public quaternion(address: bigint, value: Quaternion, force?: boolean): this;
1257
- public quaternion(address: bigint, value?: Quaternion, force?: boolean): Quaternion | this {
1258
- const { Scratch16Float32Array } = this;
1259
-
1260
- if (value === undefined) {
1261
- void this.read(address, Scratch16Float32Array);
1262
-
1263
- const w = Scratch16Float32Array[0x03],
1264
- x = Scratch16Float32Array[0x00],
1265
- y = Scratch16Float32Array[0x01],
1266
- z = Scratch16Float32Array[0x02]; // prettier-ignore
1267
-
1268
- return { w, x, y, z };
1269
- }
1270
-
1271
- Scratch16Float32Array[0x03] = value.w;
1272
- Scratch16Float32Array[0x00] = value.x;
1273
- Scratch16Float32Array[0x01] = value.y;
1274
- Scratch16Float32Array[0x02] = value.z;
1275
-
1276
- void this.write(address, Scratch16Float32Array, force);
1277
-
1278
- return this;
1279
- }
1280
-
1281
- /**
1282
- * Reads or writes an array of Quaternions.
1283
- * @param address Address to access.
1284
- * @param lengthOrValues Length to read or array to write.
1285
- * @param force When writing, if true temporarily changes page protection to allow the write.
1286
- * @returns Array of Quaternions read or this instance if writing.
1287
- * @example
1288
- * ```ts
1289
- * const cs2 = new Memory('cs2.exe');
1290
- * const myQuaternions = cs2.quaternionArray(0x12345678n, 2);
1291
- * cs2.quaternionArray(0x12345678n, [{ w: 1, x: 0, y: 0, z: 0 }]);
1292
- * ```
1293
- */
1294
- public quaternionArray(address: bigint, length: number): Quaternion[];
1295
- public quaternionArray(address: bigint, values: Quaternion[], force?: boolean): this;
1296
- public quaternionArray(address: bigint, lengthOrValues: Quaternion[] | number, force?: boolean): Quaternion[] | this {
1297
- if (typeof lengthOrValues === 'number') {
1298
- const length = lengthOrValues;
1299
- const scratch = new Float32Array(length * 0x04); // 4 * f32 per Quaternion
1300
-
1301
- void this.read(address, scratch);
1302
-
1303
- const result = new Array<Quaternion>(length);
1304
-
1305
- for (let i = 0, j = 0; i < length; i++, j += 0x04) {
1306
- const w = scratch[j + 0x03];
1307
- const x = scratch[j];
1308
- const y = scratch[j + 0x01];
1309
- const z = scratch[j + 0x02];
1310
-
1311
- result[i] = { w, x, y, z };
1312
- }
1313
-
1314
- return result;
1315
- }
1316
-
1317
- const values = lengthOrValues;
1318
- const scratch = new Float32Array(values.length * 0x04);
1319
-
1320
- for (let i = 0, j = 0; i < values.length; i++, j += 0x04) {
1321
- const quaternion = values[i];
1322
-
1323
- scratch[j + 0x03] = quaternion.w;
1324
- scratch[j] = quaternion.x;
1325
- scratch[j + 0x01] = quaternion.y;
1326
- scratch[j + 0x02] = quaternion.z;
1327
- }
1328
-
1329
- void this.write(address, scratch, force);
1330
-
1331
- return this;
1332
- }
1333
-
1334
- /**
1335
- * Reads or writes a raw Quaternion as a Float32Array.
1336
- * @param address Address to access.
1337
- * @param values Optional Float32Array to write.
1338
- * @param force When writing, if true temporarily changes page protection to allow the write.
1339
- * @returns Float32Array read or this instance if writing.
1340
- * @example
1341
- * ```ts
1342
- * const cs2 = new Memory('cs2.exe');
1343
- * const raw = cs2.quaternionRaw(0x12345678n);
1344
- * cs2.quaternionRaw(0x12345678n, new Float32Array([1,0,0,0]));
1345
- * ```
1346
- */
1347
- public quaternionRaw(address: bigint): Float32Array;
1348
- public quaternionRaw(address: bigint, values: Float32Array, force?: boolean): this;
1349
- public quaternionRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1350
- if (values === undefined) {
1351
- return this.f32Array(address, 0x04);
1352
- }
1353
-
1354
- if (values.length !== 0x04) {
1355
- throw new RangeError('values.length must be 4.');
1356
- }
1357
-
1358
- void this.write(address, values, force);
1359
-
1360
- return this;
1361
- }
1362
-
1363
- /**
1364
- * Reads or writes an RGB color (object with r, g, b).
1365
- * @param address Address to access.
1366
- * @param value Optional RGB to write.
1367
- * @param force When writing, if true temporarily changes page protection to allow the write.
1368
- * @returns The RGB at address, or this instance if writing.
1369
- * @example
1370
- * ```ts
1371
- * const cs2 = new Memory('cs2.exe');
1372
- * const myRGB = cs2.rgb(0x12345678n);
1373
- * cs2.rgb(0x12345678n, { r: 255, g: 0, b: 0 });
1374
- * ```
1375
- */
1376
- public rgb(address: bigint): RGB;
1377
- public rgb(address: bigint, value: RGB, force?: boolean): this;
1378
- public rgb(address: bigint, value?: RGB, force?: boolean): RGB | this {
1379
- const { Scratch3 } = this;
1380
-
1381
- if (value === undefined) {
1382
- void this.read(address, Scratch3);
1383
-
1384
- const r = Scratch3[0x00],
1385
- g = Scratch3[0x01],
1386
- b = Scratch3[0x02]; // prettier-ignore
1387
-
1388
- return { r, g, b };
1389
- }
1390
-
1391
- Scratch3[0x00] = value.r;
1392
- Scratch3[0x01] = value.g;
1393
- Scratch3[0x02] = value.b;
1394
-
1395
- void this.write(address, Scratch3, force);
1396
-
1397
- return this;
1398
- }
1399
-
1400
- /**
1401
- * Reads or writes a raw RGB value as a Uint8Array.
1402
- * @param address Address to access.
1403
- * @param values Optional buffer to write.
1404
- * @param force When writing, if true temporarily changes page protection to allow the write.
1405
- * @returns Uint8Array read or this instance if writing.
1406
- * @example
1407
- * ```ts
1408
- * const cs2 = new Memory('cs2.exe');
1409
- * const raw = cs2.rgbRaw(0x12345678n);
1410
- * cs2.rgbRaw(0x12345678n, new Uint8Array([255,0,0]));
1411
- * ```
1412
- */
1413
- public rgbRaw(address: bigint): Uint8Array;
1414
- public rgbRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1415
- public rgbRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1416
- if (values === undefined) {
1417
- return this.u8Array(address, 0x03);
1418
- }
1419
-
1420
- if (values.length !== 0x03) {
1421
- throw new RangeError('values.length must be 3.');
1422
- }
1423
-
1424
- void this.write(address, values, force);
1425
-
1426
- return this;
1427
- }
1428
-
1429
- /**
1430
- * Reads or writes an RGBA color (object with r, g, b, a).
1431
- * @param address Address to access.
1432
- * @param value Optional RGBA to write.
1433
- * @param force When writing, if true temporarily changes page protection to allow the write.
1434
- * @returns The RGBA at address, or this instance if writing.
1435
- * @example
1436
- * ```ts
1437
- * const cs2 = new Memory('cs2.exe');
1438
- * const myRGBA = cs2.rgba(0x12345678n);
1439
- * cs2.rgba(0x12345678n, { r: 255, g: 0, b: 0, a: 255 });
1440
- * ```
1441
- */
1442
- public rgba(address: bigint): RGBA;
1443
- public rgba(address: bigint, value: RGBA, force?: boolean): this;
1444
- public rgba(address: bigint, value?: RGBA, force?: boolean): RGBA | this {
1445
- const { Scratch4 } = this;
1446
-
1447
- if (value === undefined) {
1448
- void this.read(address, Scratch4);
1449
-
1450
- const r = Scratch4[0x00],
1451
- g = Scratch4[0x01],
1452
- b = Scratch4[0x02],
1453
- a = Scratch4[0x03]; // prettier-ignore
1454
-
1455
- return { r, g, b, a };
1456
- }
1457
-
1458
- Scratch4[0x00] = value.r;
1459
- Scratch4[0x01] = value.g;
1460
- Scratch4[0x02] = value.b;
1461
- Scratch4[0x03] = value.a;
1462
-
1463
- void this.write(address, Scratch4, force);
1464
-
1465
- return this;
1466
- }
1467
-
1468
- /**
1469
- * Reads or writes a raw RGBA value as a Uint8Array.
1470
- * @param address Address to access.
1471
- * @param values Optional buffer to write.
1472
- * @param force When writing, if true temporarily changes page protection to allow the write.
1473
- * @returns Uint8Array read or this instance if writing.
1474
- * @example
1475
- * ```ts
1476
- * const cs2 = new Memory('cs2.exe');
1477
- * const raw = cs2.rgbaRaw(0x12345678n);
1478
- * cs2.rgbaRaw(0x12345678n, new Uint8Array([255,0,0,255]));
1479
- * ```
1480
- */
1481
- public rgbaRaw(address: bigint): Uint8Array;
1482
- public rgbaRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1483
- public rgbaRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1484
- if (values === undefined) {
1485
- return this.u8Array(address, 0x04);
1486
- }
1487
-
1488
- if (values.length !== 0x04) {
1489
- throw new RangeError('values.length must be 4.');
1490
- }
1491
-
1492
- void this.write(address, values, force);
1493
-
1494
- return this;
1495
- }
1496
-
1497
- /**
1498
- * Reads or writes a UTF-8 string.
1499
- * @param address Address to access.
1500
- * @param lengthOrValue Length to read or string to write.
1501
- * @param force When writing, if true temporarily changes page protection to allow the write.
1502
- * @returns The string at address, or this instance if writing.
1503
- * @notice When writing, remember to null-terminate your string (e.g., 'hello\0').
1504
- * @todo Compare performance when using CString vs TextDecoder when reading…
1505
- * @example
1506
- * ```ts
1507
- * const cs2 = new Memory('cs2.exe');
1508
- * const myString = cs2.string(0x12345678n, 16);
1509
- * cs2.string(0x12345678n, 'hello\0');
1510
- * ```
1511
- */
1512
- public string(address: bigint, length: number): string;
1513
- public string(address: bigint, value: string, force?: boolean): this;
1514
- public string(address: bigint, lengthOrValue: number | string, force?: boolean): string | this {
1515
- if (typeof lengthOrValue === 'number') {
1516
- const scratch = new Uint8Array(lengthOrValue);
1517
-
1518
- void this.read(address, scratch);
1519
-
1520
- const indexOf = scratch.indexOf(0x00);
1521
-
1522
- return Memory.TextDecoderUTF8.decode(
1523
- scratch.subarray(0, indexOf !== -1 ? indexOf : lengthOrValue) //
1524
- );
1525
-
1526
- // return new CString(scratch.ptr).valueOf();
1527
- }
1528
-
1529
- const scratch = Memory.TextEncoderUTF8.encode(lengthOrValue);
1530
-
1531
- void this.write(address, scratch, force);
1532
-
1533
- return this;
1534
- }
1535
-
1536
- /**
1537
- * Reads or writes a 16-bit unsigned integer.
1538
- * @param address Address to access.
1539
- * @param value Optional value to write.
1540
- * @param force When writing, if true temporarily changes page protection to allow the write.
1541
- * @returns The value at address, or this instance if writing.
1542
- * @example
1543
- * ```ts
1544
- * const cs2 = new Memory('cs2.exe');
1545
- * const myInt = cs2.u16(0x12345678n);
1546
- * cs2.u16(0x12345678n, 42);
1547
- * ```
1548
- */
1549
- public u16(address: bigint): number;
1550
- public u16(address: bigint, value: number, force?: boolean): this;
1551
- public u16(address: bigint, value?: number, force?: boolean): number | this {
1552
- const { Scratch2Uint16Array } = this;
1553
-
1554
- if (value === undefined) {
1555
- return this.read(address, Scratch2Uint16Array)[0x00];
1556
- }
1557
-
1558
- Scratch2Uint16Array[0x00] = value;
1559
-
1560
- void this.write(address, Scratch2Uint16Array, force);
1561
-
1562
- return this;
1563
- }
1564
-
1565
- /**
1566
- * Reads or writes a Uint16Array.
1567
- * @param address Address to access.
1568
- * @param lengthOrValues Length to read or Uint16Array to write.
1569
- * @param force When writing, if true temporarily changes page protection to allow the write.
1570
- * @returns Uint16Array read or this instance if writing.
1571
- * @example
1572
- * ```ts
1573
- * const cs2 = new Memory('cs2.exe');
1574
- * const myArray = cs2.u16Array(0x12345678n, 2);
1575
- * cs2.u16Array(0x12345678n, new Uint16Array([1,2]));
1576
- * ```
1577
- */
1578
- public u16Array(address: bigint, length: number): Uint16Array;
1579
- public u16Array(address: bigint, values: Uint16Array, force?: boolean): this;
1580
- public u16Array(address: bigint, lengthOrValues: Uint16Array | number, force?: boolean): Uint16Array | this {
1581
- if (typeof lengthOrValues === 'number') {
1582
- const length = lengthOrValues;
1583
- const scratch = new Uint16Array(length);
1584
-
1585
- void this.read(address, scratch);
1586
-
1587
- return scratch;
1588
- }
1589
-
1590
- const values = lengthOrValues;
1591
-
1592
- void this.write(address, values, force);
1593
-
1594
- return this;
1595
- }
1596
-
1597
- /**
1598
- * Reads or writes a 32-bit unsigned integer.
1599
- * @param address Address to access.
1600
- * @param value Optional value to write.
1601
- * @param force When writing, if true temporarily changes page protection to allow the write.
1602
- * @returns The value at address, or this instance if writing.
1603
- * @example
1604
- * ```ts
1605
- * const cs2 = new Memory('cs2.exe');
1606
- * const myInt = cs2.u32(0x12345678n);
1607
- * cs2.u32(0x12345678n, 42);
1608
- * ```
1609
- */
1610
- public u32(address: bigint): number;
1611
- public u32(address: bigint, value: number, force?: boolean): this;
1612
- public u32(address: bigint, value?: number, force?: boolean): number | this {
1613
- const { Scratch4Uint32Array } = this;
1614
-
1615
- if (value === undefined) {
1616
- return this.read(address, Scratch4Uint32Array)[0x00];
1617
- }
1618
-
1619
- Scratch4Uint32Array[0x00] = value;
1620
-
1621
- void this.write(address, Scratch4Uint32Array, force);
1622
-
1623
- return this;
1624
- }
1625
-
1626
- /**
1627
- * Reads or writes a Uint32Array.
1628
- * @param address Address to access.
1629
- * @param lengthOrValues Length to read or Uint32Array to write.
1630
- * @param force When writing, if true temporarily changes page protection to allow the write.
1631
- * @returns Uint32Array read or this instance if writing.
1632
- * @example
1633
- * ```ts
1634
- * const cs2 = new Memory('cs2.exe');
1635
- * const myArray = cs2.u32Array(0x12345678n, 2);
1636
- * cs2.u32Array(0x12345678n, new Uint32Array([1,2]));
1637
- * ```
1638
- */
1639
- public u32Array(address: bigint, length: number): Uint32Array;
1640
- public u32Array(address: bigint, values: Uint32Array, force?: boolean): this;
1641
- public u32Array(address: bigint, lengthOrValues: Uint32Array | number, force?: boolean): Uint32Array | this {
1642
- if (typeof lengthOrValues === 'number') {
1643
- const length = lengthOrValues;
1644
- const scratch = new Uint32Array(length);
1645
-
1646
- void this.read(address, scratch);
1647
-
1648
- return scratch;
1649
- }
1650
-
1651
- const values = lengthOrValues;
1652
-
1653
- void this.write(address, values, force);
1654
-
1655
- return this;
1656
- }
1657
-
1658
- /**
1659
- * Reads or writes a 64-bit unsigned integer.
1660
- * @param address Address to access.
1661
- * @param value Optional value to write.
1662
- * @param force When writing, if true temporarily changes page protection to allow the write.
1663
- * @returns The bigint at address, or this instance if writing.
1664
- * @example
1665
- * ```ts
1666
- * const cs2 = new Memory('cs2.exe');
1667
- * const myBigInt = cs2.u64(0x12345678n);
1668
- * cs2.u64(0x12345678n, 123n);
1669
- * ```
1670
- */
1671
- public u64(address: bigint): bigint;
1672
- public u64(address: bigint, value: bigint, force?: boolean): this;
1673
- public u64(address: bigint, value?: bigint, force?: boolean): bigint | this {
1674
- const { Scratch8BigUint64Array } = this;
1675
-
1676
- if (value === undefined) {
1677
- return this.read(address, Scratch8BigUint64Array)[0x00];
1678
- }
1679
-
1680
- Scratch8BigUint64Array[0x00] = value;
1681
-
1682
- void this.write(address, Scratch8BigUint64Array, force);
1683
-
1684
- return this;
1685
- }
1686
-
1687
- /**
1688
- * Reads or writes a BigUint64Array.
1689
- * @param address Address to access.
1690
- * @param lengthOrValues Length to read or BigUint64Array to write.
1691
- * @param force When writing, if true temporarily changes page protection to allow the write.
1692
- * @returns BigUint64Array read or this instance if writing.
1693
- * @example
1694
- * ```ts
1695
- * const cs2 = new Memory('cs2.exe');
1696
- * const myArray = cs2.u64Array(0x12345678n, 2);
1697
- * cs2.u64Array(0x12345678n, new BigUint64Array([1n,2n]));
1698
- * ```
1699
- */
1700
- public u64Array(address: bigint, length: number): BigUint64Array;
1701
- public u64Array(address: bigint, values: BigUint64Array, force?: boolean): this;
1702
- public u64Array(address: bigint, lengthOrValues: BigUint64Array | number, force?: boolean): BigUint64Array | this {
1703
- if (typeof lengthOrValues === 'number') {
1704
- const length = lengthOrValues;
1705
- const scratch = new BigUint64Array(length);
1706
-
1707
- void this.read(address, scratch);
1708
-
1709
- return scratch;
1710
- }
1711
-
1712
- const values = lengthOrValues;
1713
-
1714
- void this.write(address, values, force);
1715
-
1716
- return this;
1717
- }
1718
-
1719
- /**
1720
- * Reads or writes an 8-bit unsigned integer.
1721
- * @param address Address to access.
1722
- * @param value Optional value to write.
1723
- * @param force When writing, if true temporarily changes page protection to allow the write.
1724
- * @returns The value at address, or this instance if writing.
1725
- * @example
1726
- * ```ts
1727
- * const cs2 = new Memory('cs2.exe');
1728
- * const myInt = cs2.u8(0x12345678n);
1729
- * cs2.u8(0x12345678n, 7);
1730
- * ```
1731
- */
1732
- public u8(address: bigint): number;
1733
- public u8(address: bigint, value: number, force?: boolean): this;
1734
- public u8(address: bigint, value?: number, force?: boolean): number | this {
1735
- const { Scratch1 } = this;
1736
-
1737
- if (value === undefined) {
1738
- return this.read(address, Scratch1)[0x00];
1739
- }
1740
-
1741
- Scratch1[0x00] = value;
1742
-
1743
- void this.write(address, Scratch1, force);
1744
-
1745
- return this;
1746
- }
1747
-
1748
- /**
1749
- * Reads or writes a Uint8Array.
1750
- * @param address Address to access.
1751
- * @param lengthOrValues Length to read or Uint8Array to write.
1752
- * @param force When writing, if true temporarily changes page protection to allow the write.
1753
- * @returns Uint8Array read or this instance if writing.
1754
- * @example
1755
- * ```ts
1756
- * const cs2 = new Memory('cs2.exe');
1757
- * const myArray = cs2.u8Array(0x12345678n, 2);
1758
- * cs2.u8Array(0x12345678n, new Uint8Array([1,2]));
1759
- * ```
1760
- */
1761
- public u8Array(address: bigint, length: number): Uint8Array;
1762
- public u8Array(address: bigint, values: Uint8Array, force?: boolean): this;
1763
- public u8Array(address: bigint, lengthOrValues: Uint8Array | number, force?: boolean): Uint8Array | this {
1764
- if (typeof lengthOrValues === 'number') {
1765
- const length = lengthOrValues;
1766
- const scratch = new Uint8Array(length);
1767
-
1768
- void this.read(address, scratch);
1769
-
1770
- return scratch;
1771
- }
1772
-
1773
- const values = lengthOrValues;
1774
-
1775
- void this.write(address, values, force);
1776
-
1777
- return this;
1778
- }
1779
-
1780
- /**
1781
- * Reads or writes a pointer-sized unsigned integer.
1782
- * @param address Address to access.
1783
- * @param value Optional value to write.
1784
- * @param force When writing, if true temporarily changes page protection to allow the write.
1785
- * @returns The value at address, or this instance if writing.
1786
- * @example
1787
- * ```ts
1788
- * const cs2 = new Memory('cs2.exe');
1789
- * const myPtr = cs2.uPtr(0x12345678n);
1790
- * cs2.uPtr(0x12345678n, 123n);
1791
- * ```
1792
- */
1793
- public uPtr(address: bigint): UPtr;
1794
- public uPtr(address: bigint, value: UPtr, force?: boolean): this;
1795
- public uPtr(address: bigint, value?: UPtr, force?: boolean): UPtr | this {
1796
- // TypeScript is funny sometimes, isn't it?… 🫠…
1797
- if (value === undefined) {
1798
- return this.u64(address);
1799
- }
1800
-
1801
- return this.u64(address, value, force);
1802
- }
1803
-
1804
- /**
1805
- * Reads or writes an array of pointer-sized unsigned integers.
1806
- * @param address Address to access.
1807
- * @param lengthOrValues Length to read or array to write.
1808
- * @param force When writing, if true temporarily changes page protection to allow the write.
1809
- * @returns Array read or this instance if writing.
1810
- * @example
1811
- * ```ts
1812
- * const cs2 = new Memory('cs2.exe');
1813
- * const myPtrs = cs2.uPtrArray(0x12345678n, 2);
1814
- * cs2.uPtrArray(0x12345678n, new BigUint64Array([1n,2n]));
1815
- * ```
1816
- */
1817
- public uPtrArray(address: bigint, length: number): UPtrArray;
1818
- public uPtrArray(address: bigint, values: UPtrArray, force?: boolean): this;
1819
- public uPtrArray(address: bigint, lengthOrValues: UPtrArray | number, force?: boolean): UPtrArray | this {
1820
- // TypeScript is funny sometimes, isn't it?… 🫠…
1821
- if (typeof lengthOrValues === 'number') {
1822
- return this.u64Array(address, lengthOrValues);
1823
- }
1824
-
1825
- return this.u64Array(address, lengthOrValues, force);
1826
- }
1827
-
1828
- /**
1829
- * Reads a UtlLinkedList of 64-bit unsigned integers and returns its elements as a BigUint64Array.
1830
- *
1831
- * This helper reads the list header at `address`, validates the capacity and element pointer,
1832
- * reads the elements table, and walks the internal linked indices to produce a compact
1833
- * BigUint64Array of present elements. If the list is empty or invalid an empty array is
1834
- * returned.
1835
- *
1836
- * @param address Address of the UtlLinkedList header in the remote process.
1837
- * @returns BigUint64Array containing the list elements (empty if the list is invalid or empty).
1838
- * @todo Create a writer so that users can write linked lists…
1839
- * @example
1840
- * ```ts
1841
- * const cs2 = new Memory('cs2.exe');
1842
- * const myList = cs2.utlLinkedListU64(0x12345678n);
1843
- * ```
1844
- */
1845
- public utlLinkedListU64(address: bigint): BigUint64Array {
1846
- const header = new Uint8Array(0x18);
1847
- const headerUint16Array = new Uint16Array(header.buffer, header.byteOffset);
1848
- const headerBigUint64Array = new BigUint64Array(header.buffer, header.byteOffset + 0x08, 2);
1849
-
1850
- void this.read(address, header);
1851
-
1852
- const capacity = headerUint16Array[0x01] & 0x7fff;
1853
- const elementsPtr = headerBigUint64Array[0x00];
1854
- let index = headerUint16Array[0x08]; // prettier-ignore
1855
-
1856
- if (capacity === 0 || capacity <= index || elementsPtr === 0n || index === 0xffff) {
1857
- return new BigUint64Array(0);
1858
- }
1859
-
1860
- const scratch = new Uint8Array(capacity << 0x04);
1861
- const scratchBigUint64Array = new BigUint64Array(scratch.buffer, scratch.byteOffset);
1862
- const scratchUint16Array = new Uint16Array(scratch.buffer, scratch.byteOffset);
1863
-
1864
- void this.read(elementsPtr, scratch);
1865
-
1866
- let count = 0; // prettier-ignore
1867
- const result = new BigUint64Array(capacity);
1868
-
1869
- while (count < capacity && capacity > index && index !== 0xffff) {
1870
- result[count++] = scratchBigUint64Array[index * 0x02];
1871
-
1872
- const next = scratchUint16Array[0x05 + index * 0x08];
1873
-
1874
- if (index === next || next === 0xffff) {
1875
- break;
1876
- }
1877
-
1878
- index = next;
1879
- }
1880
-
1881
- return capacity === count ? result : result.subarray(0, count);
1882
- }
1883
-
1884
- /**
1885
- * Reads or writes a generic UtlVector as raw bytes (no typing).
1886
- * Pass elementSize (bytes per element) so we can set/read the header count.
1887
- * @example
1888
- * ```ts
1889
- * const bytes = cs2.utlVectorRaw(0x1234n, 0x14); // read size*elementSize bytes
1890
- * cs2.utlVectorRaw(0x1234n, 0x14, new Uint8Array([...])); // write
1891
- * ```
1892
- */
1893
- public utlVectorRaw(address: bigint, elementSize: number): Uint8Array;
1894
- public utlVectorRaw(address: bigint, elementSize: number, values: Uint8Array, force?: boolean): this;
1895
- public utlVectorRaw(address: bigint, elementSize: number, values?: Uint8Array, force?: boolean): Uint8Array | this {
1896
- const elementsPtr = this.u64(address + 0x08n);
1897
-
1898
- if (values === undefined) {
1899
- const count = this.u32(address);
1900
-
1901
- if (count === 0 || elementsPtr === 0n) {
1902
- return new Uint8Array(0);
1903
- }
1904
-
1905
- const byteLength = count * elementSize;
1906
- const scratch = new Uint8Array(byteLength);
1907
-
1908
- void this.read(elementsPtr, scratch);
1909
-
1910
- return scratch;
1911
- }
1912
-
1913
- if (values.byteLength % elementSize !== 0) {
1914
- throw new RangeError('values length must be a multiple of elementSize');
1915
- }
1916
-
1917
- const count = values.byteLength / elementSize;
1918
-
1919
- this.u32(address, count, force);
1920
-
1921
- void this.write(elementsPtr, values, force);
1922
-
1923
- return this;
1924
- }
1925
-
1926
- /**
1927
- * Reads or writes a UtlVectorU32 (Uint32Array).
1928
- * @param address Address to access.
1929
- * @param values Optional Uint32Array to write.
1930
- * @param force When writing, if true temporarily changes page protection to allow the write.
1931
- * @returns The vector at address, or this instance if writing.
1932
- * @example
1933
- * ```ts
1934
- * const cs2 = new Memory('cs2.exe');
1935
- * const myVector = cs2.utlVectorU32(0x12345678n);
1936
- * cs2.utlVectorU32(0x12345678n, new Uint32Array([1,2,3]));
1937
- * ```
1938
- */
1939
- public utlVectorU32(address: bigint): Uint32Array;
1940
- public utlVectorU32(address: bigint, values: Uint32Array, force?: boolean): this;
1941
- public utlVectorU32(address: bigint, values?: Uint32Array, force?: boolean): Uint32Array | this {
1942
- const elementsPtr = this.u64(address + 0x08n);
1943
-
1944
- if (values === undefined) {
1945
- const size = this.u32(address);
1946
-
1947
- const scratch = new Uint32Array(size);
1948
-
1949
- void this.read(elementsPtr, scratch);
1950
-
1951
- return scratch;
1952
- }
1953
-
1954
- this.u32(address, values.length, force);
1955
-
1956
- void this.write(elementsPtr, values, force);
1957
-
1958
- return this;
1959
- }
1960
-
1961
- /**
1962
- * Reads or writes a UtlVectorU64 (BigUint64Array).
1963
- * @param address Address to access.
1964
- * @param values Optional BigUint64Array to write.
1965
- * @param force When writing, if true temporarily changes page protection to allow the write.
1966
- * @returns The vector at address, or this instance if writing.
1967
- * @example
1968
- * ```ts
1969
- * const cs2 = new Memory('cs2.exe');
1970
- * const myVector = cs2.utlVectorU64(0x12345678n);
1971
- * cs2.utlVectorU64(0x12345678n, new BigUint64Array([1n,2n,3n]));
1972
- * ```
1973
- */
1974
- public utlVectorU64(address: bigint): BigUint64Array;
1975
- public utlVectorU64(address: bigint, values: BigUint64Array, force?: boolean): this;
1976
- public utlVectorU64(address: bigint, values?: BigUint64Array, force?: boolean): BigUint64Array | this {
1977
- const elementsPtr = this.u64(address + 0x08n);
1978
-
1979
- if (values === undefined) {
1980
- const size = this.u32(address);
1981
-
1982
- const scratch = new BigUint64Array(size);
1983
-
1984
- void this.read(elementsPtr, scratch);
1985
-
1986
- return scratch;
1987
- }
1988
-
1989
- this.u32(address, values.length, force);
1990
-
1991
- void this.write(elementsPtr, values, force);
1992
-
1993
- return this;
1994
- }
1995
-
1996
- /**
1997
- * Reads or writes a Vector2 (object with x, y).
1998
- * @param address Address to access.
1999
- * @param value Optional Vector2 to write.
2000
- * @param force When writing, if true temporarily changes page protection to allow the write.
2001
- * @returns The Vector2 at address, or this instance if writing.
2002
- * @example
2003
- * ```ts
2004
- * const cs2 = new Memory('cs2.exe');
2005
- * const myVector2 = cs2.vector2(0x12345678n);
2006
- * cs2.vector2(0x12345678n, { x: 1, y: 2 });
2007
- * ```
2008
- */
2009
- public vector2(address: bigint): Vector2;
2010
- public vector2(address: bigint, value: Vector2, force?: boolean): this;
2011
- public vector2(address: bigint, value?: Vector2, force?: boolean): Vector2 | this {
2012
- // TypeScript is funny sometimes, isn't it?… 🫠…
2013
- if (value === undefined) {
2014
- return this.point(address);
2015
- }
2016
-
2017
- return this.point(address, value, force);
2018
- }
2019
-
2020
- /**
2021
- * Reads or writes an array of Vector2.
2022
- * @param address Address to access.
2023
- * @param lengthOrValues Length to read or array to write.
2024
- * @param force When writing, if true temporarily changes page protection to allow the write.
2025
- * @returns Array of Vector2 read or this instance if writing.
2026
- * @example
2027
- * ```ts
2028
- * const cs2 = new Memory('cs2.exe');
2029
- * const myVector2s = cs2.vector2Array(0x12345678n, 2);
2030
- * cs2.vector2Array(0x12345678n, [{ x: 1, y: 2 }, { x: 3, y: 4 }]);
2031
- * ```
2032
- */
2033
- public vector2Array(address: bigint, length: number): Vector2[];
2034
- public vector2Array(address: bigint, values: Vector2[], force?: boolean): this;
2035
- public vector2Array(address: bigint, lengthOrValues: Vector2[] | number, force?: boolean): Vector2[] | this {
2036
- // TypeScript is funny sometimes, isn't it?… 🫠…
2037
- if (typeof lengthOrValues === 'number') {
2038
- return this.pointArray(address, lengthOrValues);
2039
- }
2040
-
2041
- return this.pointArray(address, lengthOrValues, force);
2042
- }
2043
-
2044
- /**
2045
- * Reads or writes a raw Vector2 as a Float32Array.
2046
- * @param address Address to access.
2047
- * @param values Optional Float32Array to write.
2048
- * @param force When writing, if true temporarily changes page protection to allow the write.
2049
- * @returns Float32Array read or this instance if writing.
2050
- * @example
2051
- * ```ts
2052
- * const cs2 = new Memory('cs2.exe');
2053
- * const myVector2 = cs2.vector2Raw(0x12345678n);
2054
- * cs2.vector2Raw(0x12345678n, new Float32Array([1, 2]));
2055
- * ```
2056
- */
2057
- public vector2Raw(address: bigint): Float32Array;
2058
- public vector2Raw(address: bigint, values: Float32Array, force?: boolean): this;
2059
- public vector2Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2060
- if (values === undefined) {
2061
- return this.f32Array(address, 0x02);
2062
- }
2063
-
2064
- if (values.length !== 0x02) {
2065
- throw new RangeError('values.length must be 2.');
2066
- }
2067
-
2068
- void this.write(address, values, force);
2069
-
2070
- return this;
2071
- }
2072
-
2073
- /**
2074
- * Reads or writes a Vector3 (object with x, y, z).
2075
- * @param address Address to access.
2076
- * @param value Optional Vector3 to write.
2077
- * @param force When writing, if true temporarily changes page protection to allow the write.
2078
- * @returns The Vector3 at address, or this instance if writing.
2079
- * @example
2080
- * ```ts
2081
- * const cs2 = new Memory('cs2.exe');
2082
- * const myVector3 = cs2.vector3(0x12345678n);
2083
- * cs2.vector3(0x12345678n, { x: 1, y: 2, z: 3 });
2084
- * ```
2085
- */
2086
- public vector3(address: bigint): Vector3;
2087
- public vector3(address: bigint, value: Vector3, force?: boolean): this;
2088
- public vector3(address: bigint, value?: Vector3, force?: boolean): Vector3 | this {
2089
- const { Scratch12Float32Array } = this;
2090
-
2091
- if (value === undefined) {
2092
- void this.read(address, Scratch12Float32Array);
2093
-
2094
- const x = Scratch12Float32Array[0x00],
2095
- y = Scratch12Float32Array[0x01],
2096
- z = Scratch12Float32Array[0x02]; // prettier-ignore
2097
-
2098
- return { x, y, z };
2099
- }
2100
-
2101
- Scratch12Float32Array[0x00] = value.x;
2102
- Scratch12Float32Array[0x01] = value.y;
2103
- Scratch12Float32Array[0x02] = value.z;
2104
-
2105
- void this.write(address, Scratch12Float32Array, force);
2106
-
2107
- return this;
2108
- }
2109
-
2110
- /**
2111
- * Reads or writes an array of Vector3.
2112
- * @param address Address to access.
2113
- * @param lengthOrValues Length to read or array to write.
2114
- * @param force When writing, if true temporarily changes page protection to allow the write.
2115
- * @returns Array of Vector3 read or this instance if writing.
2116
- * @example
2117
- * ```ts
2118
- * const cs2 = new Memory('cs2.exe');
2119
- * const myVector3s = cs2.vector3Array(0x12345678n, 2);
2120
- * cs2.vector3Array(0x12345678n, [{ x: 1, y: 2, z: 3 }]);
2121
- * ```
2122
- */
2123
- public vector3Array(address: bigint, length: number): Vector3[];
2124
- public vector3Array(address: bigint, values: Vector3[], force?: boolean): this;
2125
- public vector3Array(address: bigint, lengthOrValues: Vector3[] | number, force?: boolean): Vector3[] | this {
2126
- if (typeof lengthOrValues === 'number') {
2127
- const length = lengthOrValues;
2128
- const scratch = new Float32Array(length * 0x03);
2129
-
2130
- void this.read(address, scratch);
2131
-
2132
- const result = new Array<Vector3>(length);
2133
-
2134
- for (let i = 0, j = 0; i < length; i++, j += 0x03) {
2135
- const x = scratch[j];
2136
- const y = scratch[j + 0x01];
2137
- const z = scratch[j + 0x02];
2138
-
2139
- result[i] = { x, y, z };
2140
- }
2141
-
2142
- return result;
2143
- }
2144
-
2145
- const values = lengthOrValues;
2146
- const scratch = new Float32Array(values.length * 0x03);
2147
-
2148
- for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
2149
- const vector3 = values[i];
2150
-
2151
- scratch[j] = vector3.x;
2152
- scratch[j + 0x01] = vector3.y;
2153
- scratch[j + 0x02] = vector3.z;
2154
- }
2155
-
2156
- void this.write(address, scratch, force);
2157
-
2158
- return this;
2159
- }
2160
-
2161
- /**
2162
- * Reads or writes a raw Vector3 as a Float32Array.
2163
- * @param address Address to access.
2164
- * @param values Optional Float32Array to write.
2165
- * @param force When writing, if true temporarily changes page protection to allow the write.
2166
- * @returns Float32Array read or this instance if writing.
2167
- * @example
2168
- * ```ts
2169
- * const cs2 = new Memory('cs2.exe');
2170
- * const myVector3 = cs2.vector3Raw(0x12345678n);
2171
- * cs2.vector3Raw(0x12345678n, new Float32Array([1, 2, 3]));
2172
- * ```
2173
- */
2174
- public vector3Raw(address: bigint): Float32Array;
2175
- public vector3Raw(address: bigint, values: Float32Array, force?: boolean): this;
2176
- public vector3Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2177
- if (values === undefined) {
2178
- return this.f32Array(address, 0x03);
2179
- }
2180
-
2181
- if (values.length !== 0x03) {
2182
- throw new RangeError('values.length must be 3.');
2183
- }
2184
-
2185
- void this.write(address, values, force);
2186
-
2187
- return this;
2188
- }
2189
-
2190
- /**
2191
- * Reads or writes a Vector4 (object with w, x, y, z).
2192
- * @param address Address to access.
2193
- * @param value Optional Vector4 to write.
2194
- * @param force When writing, if true temporarily changes page protection to allow the write.
2195
- * @returns The Vector4 at address, or this instance if writing.
2196
- * @example
2197
- * ```ts
2198
- * const cs2 = new Memory('cs2.exe');
2199
- * const myVector4 = cs2.vector4(0x12345678n);
2200
- * cs2.vector4(0x12345678n, { w: 1, x: 0, y: 0, z: 0 });
2201
- * ```
2202
- */
2203
- public vector4(address: bigint): Vector4;
2204
- public vector4(address: bigint, value: Vector4, force?: boolean): this;
2205
- public vector4(address: bigint, value?: Vector4, force?: boolean): Vector4 | this {
2206
- // TypeScript is funny sometimes, isn't it?… 🫠…
2207
- if (value === undefined) {
2208
- return this.quaternion(address);
2209
- }
2210
-
2211
- return this.quaternion(address, value, force);
2212
- }
2213
-
2214
- /**
2215
- * Reads or writes an array of Vector4.
2216
- * @param address Address to access.
2217
- * @param lengthOrValues Length to read or array to write.
2218
- * @param force When writing, if true temporarily changes page protection to allow the write.
2219
- * @returns Array of Vector4 read or this instance if writing.
2220
- * @example
2221
- * ```ts
2222
- * const cs2 = new Memory('cs2.exe');
2223
- * const myVector4s = cs2.vector4Array(0x12345678n, 2);
2224
- * cs2.vector4Array(0x12345678n, [{ w: 1, x: 0, y: 0, z: 0 }]);
2225
- * ```
2226
- */
2227
- public vector4Array(address: bigint, length: number): Vector4[];
2228
- public vector4Array(address: bigint, values: Vector4[], force?: boolean): this;
2229
- public vector4Array(address: bigint, lengthOrValues: Vector4[] | number, force?: boolean): Vector4[] | this {
2230
- // TypeScript is funny sometimes, isn't it?… 🫠…
2231
- if (typeof lengthOrValues === 'number') {
2232
- return this.quaternionArray(address, lengthOrValues);
2233
- }
2234
-
2235
- return this.quaternionArray(address, lengthOrValues, force);
2236
- }
2237
-
2238
- /**
2239
- * Reads or writes a raw Vector4 as a Float32Array.
2240
- * @param address Address to access.
2241
- * @param values Optional Float32Array to write.
2242
- * @param force When writing, if true temporarily changes page protection to allow the write.
2243
- * @returns Float32Array read or this instance if writing.
2244
- * @example
2245
- * ```ts
2246
- * const cs2 = new Memory('cs2.exe');
2247
- * const myVector4 = cs2.vector4Raw(0x12345678n);
2248
- * cs2.vector4Raw(0x12345678n, new Float32Array([1, 0, 0, 0]));
2249
- * ```
2250
- */
2251
- public vector4Raw(address: bigint): Float32Array;
2252
- public vector4Raw(address: bigint, values: Float32Array, force?: boolean): this;
2253
- public vector4Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2254
- if (values === undefined) {
2255
- return this.f32Array(address, 0x04);
2256
- }
2257
-
2258
- if (values.length !== 0x04) {
2259
- throw new RangeError('values.length must be 4.');
2260
- }
2261
-
2262
- void this.write(address, values, force);
2263
-
2264
- return this;
2265
- }
2266
-
2267
- // Public utility methods…
2268
-
2269
- /**
2270
- * Follows a pointer chain with offsets.
2271
- * @param address Base address.
2272
- * @param offsets Array of pointer offsets.
2273
- * @returns Final address after following the chain, or -1n if any pointer is null.
2274
- * @example
2275
- * ```ts
2276
- * const cs2 = new Memory('cs2.exe');
2277
- * const myAddress = cs2.follow(0x10000000n, [0x10n, 0x20n]);
2278
- * ```
2279
- */
2280
- public follow(address: bigint, offsets: readonly bigint[]): bigint {
2281
- const last = offsets.length - 1;
2282
-
2283
- for (let i = 0; i < last; i++) {
2284
- address = this.u64(address + offsets[i]);
2285
-
2286
- if (address === 0n) {
2287
- return -1n;
2288
- }
2289
- }
2290
-
2291
- return address + (offsets[last] ?? 0n);
2292
- }
2293
-
2294
- /**
2295
- * Finds the address of a buffer within a memory region.
2296
- * @param needle Buffer or typed array to search for.
2297
- * @param address Start address.
2298
- * @param length Number of bytes to search.
2299
- * @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
2300
- * @returns Address of the buffer if found, or -1n. If all is true, returns an array of addresses.
2301
- * @example
2302
- * ```ts
2303
- * const cs2 = new Memory('cs2.exe');
2304
- * const needle = Buffer.from('Hello world!');
2305
- * // const needle = Buffer.from([0x01, 0x02, 0x03]);
2306
- * // const needle = new Uint8Array([0x01, 0x02, 0x03]);
2307
- * // const needle = new Float32Array([0x01, 0x02, 0x03]);
2308
- * // Find first match
2309
- * const address = cs2.indexOf(needle, 0x10000000n, 100);
2310
- * // Find all matches
2311
- * const allAddressess = cs2.indexOf(needle, 0x10000000n, 100, true);
2312
- * ```
2313
- */
2314
- public indexOf(needle: Scratch, address: bigint, length: number): bigint;
2315
- public indexOf(needle: Scratch, address: bigint, length: number, all: false): bigint;
2316
- public indexOf(needle: Scratch, address: bigint, length: number, all: true): bigint[];
2317
- public indexOf(needle: Scratch, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
2318
- const haystack = Buffer.allocUnsafe(length);
2319
-
2320
- const needleBuffer = ArrayBuffer.isView(needle) //
2321
- ? Buffer.from(needle.buffer, needle.byteOffset, needle.byteLength)
2322
- : Buffer.from(needle);
2323
-
2324
- void this.read(address, haystack);
2325
-
2326
- if (!all) {
2327
- const indexOf = haystack.indexOf(needleBuffer);
2328
-
2329
- return indexOf !== -1 ? BigInt(indexOf) + address : -1n;
2330
- }
2331
-
2332
- const results: bigint[] = [];
2333
-
2334
- let start = haystack.indexOf(needleBuffer);
2335
-
2336
- if (start === -1) {
2337
- return results;
2338
- }
2339
-
2340
- do {
2341
- results.push(address + BigInt(start));
2342
- } while ((start = haystack.indexOf(needleBuffer, start + 0x01)) !== -1);
2343
-
2344
- return results;
2345
- }
2346
-
2347
- /**
2348
- * Finds the address of a byte pattern in memory. `**` and `??` match any byte.
2349
- * @param needle Hex string pattern to search for (e.g., 'deadbeed', 'dead**ef', 'dead??ef').
2350
- * @param address Start address to search.
2351
- * @param length Number of bytes to search.
2352
- * @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
2353
- * @returns Address of the pattern if found, or -1n. If all is true, returns an array of addresses.
2354
- * @example
2355
- * ```ts
2356
- * const cs2 = new Memory('cs2.exe');
2357
- * // Find first match
2358
- * const address = cs2.pattern('dead**ef', 0x10000000n, 0x1000);
2359
- * // Find all matches
2360
- * const allAddresses = cs2.pattern('dead**ef', 0x10000000n, 0x1000, true);
2361
- * ```
2362
- */
2363
- public pattern(needle: string, address: bigint, length: number): bigint;
2364
- public pattern(needle: string, address: bigint, length: number, all: false): bigint;
2365
- public pattern(needle: string, address: bigint, length: number, all: true): bigint[];
2366
- public pattern(needle: string, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
2367
- const test = Memory.Patterns.PatternTest.test(needle);
2368
-
2369
- if (!test) {
2370
- return !all ? -1n : [];
2371
- }
2372
-
2373
- // The RegExp test ensures that we have at least one token…
2374
-
2375
- const tokens = [...needle.matchAll(Memory.Patterns.PatternMatchAll)]
2376
- .map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
2377
- .sort(({ length: a }, { length: b }) => b - a);
2378
-
2379
- const anchor = tokens.shift()!;
2380
-
2381
- const haystack = this.buffer(address, length);
2382
-
2383
- const end = length - (needle.length >>> 1);
2384
- let start = haystack.indexOf(anchor.buffer); // prettier-ignore
2385
-
2386
- if (start === -1) {
2387
- return !all ? -1n : [];
2388
- }
2389
-
2390
- if (!all) {
2391
- outer: do {
2392
- const base = start - anchor.index;
2393
-
2394
- if (base < 0) {
2395
- continue;
2396
- }
2397
-
2398
- if (base > end) {
2399
- return -1n;
2400
- }
2401
-
2402
- for (const { buffer, index, length } of tokens) {
2403
- const sourceEnd = base + index + length,
2404
- sourceStart = base + index,
2405
- target = buffer,
2406
- targetEnd = length,
2407
- targetStart = 0; // prettier-ignore
2408
-
2409
- const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
2410
-
2411
- if (compare !== 0) {
2412
- continue outer;
2413
- }
2414
- }
2415
-
2416
- return address + BigInt(base);
2417
- } while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
2418
-
2419
- return -1n;
2420
- }
2421
-
2422
- const results: bigint[] = [];
2423
-
2424
- outer: do {
2425
- const base = start - anchor.index;
2426
-
2427
- if (base < 0) {
2428
- continue;
2429
- }
2430
-
2431
- if (base > end) {
2432
- return results;
2433
- }
2434
-
2435
- for (const { buffer, index, length } of tokens) {
2436
- const sourceEnd = base + index + length,
2437
- sourceStart = base + index,
2438
- target = buffer,
2439
- targetEnd = length,
2440
- targetStart = 0; // prettier-ignore
2441
-
2442
- const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
2443
-
2444
- if (compare !== 0) {
2445
- continue outer;
2446
- }
2447
- }
2448
-
2449
- results.push(address + BigInt(base));
2450
- } while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
2451
-
2452
- return results;
2453
- }
2454
- }
2455
-
2456
- export default Memory;
1
+ import '../runtime/extensions';
2
+
3
+ import { CString, ptr } from 'bun:ffi';
4
+
5
+ import Kernel32, { INVALID_HANDLE_VALUE } from 'bun-kernel32';
6
+
7
+ import type { Module, Point, QAngle, Quaternion, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
8
+ import Win32Error from './Win32Error';
9
+
10
+ /**
11
+ * Provides cross-process memory manipulation for native applications.
12
+ *
13
+ * Use this class to read and write memory, access modules, and work with common data structures in external processes.
14
+ *
15
+ * Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
16
+ *
17
+ * @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
18
+ * @todo When adding 32-bit support, several u64 will need changed to u64_fast.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import Memory from 'bun-memory';
23
+ * const cs2 = new Memory('cs2.exe');
24
+ * const myFloat = cs2.f32(0x12345678n);
25
+ * cs2.close();
26
+ * ```
27
+ */
28
+ class Memory {
29
+ /**
30
+ * Opens a process by PID or executable name.
31
+ * @param identifier Process ID or executable name.
32
+ * @throws If the process cannot be found or opened.
33
+ * @example
34
+ * ```ts
35
+ * const cs2 = new Memory('cs2.exe');
36
+ * ```
37
+ */
38
+ constructor(identifier: number | string) {
39
+ const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
40
+
41
+ const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
42
+ const th32ProcessID = 0;
43
+
44
+ const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
45
+
46
+ if (hSnapshot === -1n) {
47
+ throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
48
+ }
49
+
50
+ const lppeBuffer = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
51
+ /* */ lppeBuffer.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
52
+
53
+ const lppe = lppeBuffer.ptr;
54
+
55
+ const bProcess32FirstW = Kernel32.Process32FirstW(hSnapshot, lppe);
56
+
57
+ if (!bProcess32FirstW) {
58
+ Kernel32.CloseHandle(hSnapshot);
59
+
60
+ throw new Win32Error('Process32FirstW', Kernel32.GetLastError());
61
+ }
62
+
63
+ do {
64
+ const szExeFile = lppeBuffer.toString('utf16le', 0x2c, 0x234).replace(ReplaceTrailingNull, '');
65
+ const th32ProcessID = lppeBuffer.readUInt32LE(0x08);
66
+
67
+ if (
68
+ (typeof identifier === 'number' && identifier !== th32ProcessID) || //
69
+ (typeof identifier === 'string' && identifier !== szExeFile)
70
+ ) {
71
+ continue;
72
+ }
73
+
74
+ const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
75
+ const inheritHandle = 0;
76
+
77
+ const hProcess = Kernel32.OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
78
+
79
+ if (hProcess === 0n) {
80
+ Kernel32.CloseHandle(hSnapshot);
81
+
82
+ throw new Win32Error('OpenProcess', Kernel32.GetLastError());
83
+ }
84
+
85
+ this.__modules = {};
86
+
87
+ this.hProcess = hProcess;
88
+ this.th32ProcessID = th32ProcessID;
89
+
90
+ this.refresh();
91
+
92
+ Kernel32.CloseHandle(hSnapshot);
93
+
94
+ return;
95
+ } while (Kernel32.Process32NextW(hSnapshot, lppe));
96
+
97
+ Kernel32.CloseHandle(hSnapshot);
98
+
99
+ throw new Error(`Process not found: ${identifier}.`);
100
+ }
101
+
102
+ /**
103
+ * Map of loaded modules in the process, keyed by module name.
104
+ * @example
105
+ * ```ts
106
+ * const cs2 = new Memory('cs2.exe');
107
+ * const mainModule = cs2.modules['cs2.exe'];
108
+ * ```
109
+ */
110
+ private __modules: { [key: string]: Readonly<Module> };
111
+
112
+ /**
113
+ * Regex patterns for matching hex strings and wildcards in memory scans.
114
+ * Used by the pattern method.
115
+ */
116
+ private static readonly Patterns = {
117
+ PatternMatchAll: /(?:[0-9A-Fa-f]{2})+/g,
118
+ PatternTest: /^(?=.*[0-9A-Fa-f]{2})(?:\*{2}|\?{2}|[0-9A-Fa-f]{2})+$/,
119
+ ReplaceTrailingNull: /\0+$/,
120
+ };
121
+
122
+ /**
123
+ * Scratch buffers and typed views for temporary FFI reads/writes.
124
+ * Used internally for efficient memory access and conversions.
125
+ */
126
+ private readonly Scratch1 = new Uint8Array(0x01);
127
+ private readonly Scratch1Int8Array = new Int8Array(this.Scratch1.buffer, this.Scratch1.byteOffset, 0x01);
128
+
129
+ private readonly Scratch2 = new Uint8Array(0x02);
130
+ private readonly Scratch2Int16Array = new Int16Array(this.Scratch2.buffer, this.Scratch2.byteOffset, 0x01);
131
+ private readonly Scratch2Uint16Array = new Uint16Array(this.Scratch2.buffer, this.Scratch2.byteOffset, 0x01);
132
+
133
+ private readonly Scratch3 = new Uint8Array(0x03);
134
+
135
+ private readonly Scratch4 = new Uint8Array(0x04);
136
+ private readonly Scratch4Float32Array = new Float32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
137
+ private readonly Scratch4Int32Array = new Int32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
138
+ private readonly Scratch4Uint32Array = new Uint32Array(this.Scratch4.buffer, this.Scratch4.byteOffset, 0x01);
139
+
140
+ private readonly Scratch8 = new Uint8Array(0x08);
141
+ private readonly Scratch8BigInt64Array = new BigInt64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
142
+ private readonly Scratch8BigUint64Array = new BigUint64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
143
+ private readonly Scratch8Float32Array = new Float32Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x02);
144
+ private readonly Scratch8Float64Array = new Float64Array(this.Scratch8.buffer, this.Scratch8.byteOffset, 0x01);
145
+
146
+ private readonly Scratch12 = new Uint8Array(0x0c);
147
+ private readonly Scratch12Float32Array = new Float32Array(this.Scratch12.buffer, this.Scratch12.byteOffset, 0x03);
148
+
149
+ private readonly Scratch16 = new Uint8Array(0x10);
150
+ private readonly Scratch16Float32Array = new Float32Array(this.Scratch16.buffer, this.Scratch16.byteOffset, 0x04);
151
+
152
+ private readonly Scratch48 = new Uint8Array([
153
+ 0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xec, 0x28, 0xff, 0xd0, 0x48, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc4,
154
+ 0x28, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155
+ ]);
156
+
157
+ private readonly Scratch112 = new Uint8Array(0x70);
158
+
159
+ private readonly Scratch1080 = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
160
+
161
+ private static TextDecoderUTF8 = new TextDecoder('utf-8');
162
+
163
+ private static TextEncoderUTF8 = new TextEncoder('utf-8');
164
+
165
+ private readonly hProcess: bigint;
166
+ private readonly th32ProcessID: number;
167
+
168
+ /**
169
+ * Gets all loaded modules in the process.
170
+ * @returns Map of module name to module info with base-relative helpers.
171
+ * @example
172
+ * ```ts
173
+ * const cs2 = new Memory('cs2.exe');
174
+ * const client = cs2.modules['client.dll'];
175
+ * ```
176
+ */
177
+ public get modules(): Memory['__modules'] {
178
+ return this.__modules;
179
+ }
180
+
181
+ /**
182
+ * Disposes resources held by this Memory instance.
183
+ * Called automatically when using `using` blocks.
184
+ * @example
185
+ * ```ts
186
+ * using const mem = new Memory('cs2.exe');
187
+ * // mem is disposed at the end of the block
188
+ * ```
189
+ */
190
+ public [Symbol.dispose](): void {
191
+ this.close();
192
+
193
+ return;
194
+ }
195
+
196
+ /**
197
+ * Asynchronously disposes resources held by this Memory instance.
198
+ * Use in `await using` blocks for async cleanup.
199
+ * @example
200
+ * ```ts
201
+ * await using const mem = new Memory('cs2.exe');
202
+ * // mem is disposed asynchronously at the end of the block
203
+ * ```
204
+ */
205
+ public [Symbol.asyncDispose](): Promise<void> {
206
+ this.close();
207
+
208
+ return Promise.resolve();
209
+ }
210
+
211
+ /**
212
+ * Allocates memory in the remote process.
213
+ * @param length Bytes to allocate.
214
+ * @param protect Page protection (defaults to PAGE_READWRITE).
215
+ * @returns Base address of the allocation.
216
+ * @example
217
+ * ```ts
218
+ * const ptr = cs2.alloc(0x100);
219
+ * ```
220
+ */
221
+ public alloc(length: number, protect: number = 0x04): bigint {
222
+ const { hProcess } = this;
223
+
224
+ if (length <= 0) {
225
+ throw new RangeError('length must be greater than 0.');
226
+ }
227
+
228
+ const dwSize = BigInt(length);
229
+ const flAllocationType = 0x3000; /* MEM_COMMIT | MEM_RESERVE */
230
+ const flProtect = protect;
231
+ const lpAddress = 0n;
232
+
233
+ const lpBaseAddress = Kernel32.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
234
+
235
+ if (lpBaseAddress === 0n) {
236
+ throw new Win32Error('VirtualAllocEx', Kernel32.GetLastError());
237
+ }
238
+
239
+ return lpBaseAddress;
240
+ }
241
+
242
+ /**
243
+ * Closes the process handle.
244
+ * @example
245
+ * ```ts
246
+ * const cs2 = new Memory('cs2.exe');
247
+ * cs2.close();
248
+ * ```
249
+ */
250
+ public close(): void {
251
+ Kernel32.CloseHandle(this.hProcess);
252
+
253
+ return;
254
+ }
255
+
256
+ /**
257
+ * Frees memory allocated in the remote process.
258
+ * @param address Allocation base address.
259
+ * @example
260
+ * ```ts
261
+ * const ptr = cs2.alloc(0x100);
262
+ * cs2.free(ptr);
263
+ * ```
264
+ */
265
+ public free(address: bigint): void {
266
+ const { hProcess } = this;
267
+
268
+ const dwFreeType = 0x8000; /* MEM_RELEASE */
269
+ const dwSize = 0x00n;
270
+ const lpAddress = address;
271
+
272
+ const bVirtualFreeEx = !!Kernel32.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType);
273
+
274
+ if (!bVirtualFreeEx) {
275
+ throw new Win32Error('VirtualFreeEx', Kernel32.GetLastError());
276
+ }
277
+
278
+ return;
279
+ }
280
+
281
+ /**
282
+ * Changes the page protection of a region.
283
+ * @param address Base address to protect.
284
+ * @param length Bytes to protect.
285
+ * @param protect New protection flags.
286
+ * @returns Previous protection flags.
287
+ * @example
288
+ * ```ts
289
+ * const cs2 = new Memory('cs2.exe');
290
+ * const previous = cs2.protection(0x12345678n, 0x1000, 0x40);
291
+ * ```
292
+ */
293
+ public protection(address: bigint, length: number, protect: number): number {
294
+ const { Scratch4Uint32Array, hProcess } = this;
295
+
296
+ if (length <= 0) {
297
+ throw new RangeError('length must be greater than 0.');
298
+ }
299
+
300
+ const dwSize = BigInt(length);
301
+ const flNewProtect = protect;
302
+ const lpAddress = address;
303
+ const lpflOldProtect = Scratch4Uint32Array.ptr;
304
+
305
+ const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
306
+
307
+ if (!bVirtualProtectEx) {
308
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
309
+ }
310
+
311
+ return Scratch4Uint32Array[0x00];
312
+ }
313
+
314
+ /**
315
+ * Reads memory into a buffer.
316
+ * @param address Address to read from.
317
+ * @param scratch Buffer to fill.
318
+ * @returns The filled buffer.
319
+ * @todo Consider inlining the call in the if to cut a binding… I hate the idea… 🫠…
320
+ * @example
321
+ * ```ts
322
+ * const cs2 = new Memory('cs2.exe');
323
+ * const myBuffer = cs2.read(0x12345678n, new Uint8Array(4));
324
+ * ```
325
+ */
326
+ public read<T extends Scratch>(address: bigint, scratch: T): T {
327
+ const { hProcess } = this;
328
+
329
+ const lpBaseAddress = address;
330
+ const lpBuffer = ptr(scratch);
331
+ const nSize = BigInt(scratch.byteLength);
332
+ const numberOfBytesRead = 0x00n;
333
+
334
+ const bReadProcessMemory = Kernel32.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
335
+
336
+ if (!bReadProcessMemory) {
337
+ throw new Win32Error('ReadProcessMemory', Kernel32.GetLastError());
338
+ }
339
+
340
+ return scratch;
341
+ }
342
+
343
+ /**
344
+ * Refreshes the module list for the process.
345
+ * @example
346
+ * ```ts
347
+ * const cs2 = new Memory('cs2.exe');
348
+ * cs2.refresh();
349
+ * ```
350
+ */
351
+ public refresh(): void {
352
+ const { Patterns: { ReplaceTrailingNull } } = Memory; // prettier-ignore
353
+ const { th32ProcessID } = this;
354
+
355
+ const dwFlags = 0x00000018; /* TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 */
356
+
357
+ const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, th32ProcessID)!;
358
+
359
+ if (hSnapshot === INVALID_HANDLE_VALUE) {
360
+ throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
361
+ }
362
+
363
+ const { Scratch1080: lpme } = this;
364
+ /* */ lpme.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
365
+
366
+ const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, ptr(lpme));
367
+
368
+ if (!bModule32FirstW) {
369
+ Kernel32.CloseHandle(hSnapshot);
370
+
371
+ throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
372
+ }
373
+
374
+ const modules: Memory['__modules'] = {};
375
+
376
+ do {
377
+ const modBaseAddr = lpme.readBigUInt64LE(0x18);
378
+ const modBaseSize = lpme.readUInt32LE(0x20);
379
+ const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(ReplaceTrailingNull, '');
380
+
381
+ modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
382
+ } while (Kernel32.Module32NextW(hSnapshot, ptr(lpme)));
383
+
384
+ Kernel32.CloseHandle(hSnapshot);
385
+
386
+ this.__modules = Object.freeze(modules);
387
+
388
+ return;
389
+ }
390
+
391
+ /**
392
+ * Writes a buffer to memory.
393
+ * @param address Address to write to.
394
+ * @param scratch Buffer to write.
395
+ * @param force When writing, if true temporarily changes page protection to allow the write.
396
+ * @returns This instance.
397
+ * @example
398
+ * ```ts
399
+ * const cs2 = new Memory('cs2.exe');
400
+ * cs2.write(0x12345678n, new Uint8Array([1,2,3,4]));
401
+ * // Force a write by temporarily changing memory protection
402
+ * cs2.write(0x12345678n, new Uint8Array([1,2,3,4]), true);
403
+ * ```
404
+ */
405
+ public write(address: bigint, scratch: Scratch, force: boolean = false): this {
406
+ const { hProcess } = this;
407
+
408
+ const lpBaseAddress = address;
409
+ const lpBuffer = scratch.ptr;
410
+ const nSize = BigInt(scratch.byteLength);
411
+ const numberOfBytesWritten = 0x00n;
412
+
413
+ if (!force) {
414
+ const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
415
+
416
+ if (!bWriteProcessMemory) {
417
+ throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
418
+ }
419
+
420
+ return this;
421
+ }
422
+
423
+ const dwSize = nSize;
424
+ const flNewProtect = 0x40; /* PAGE_EXECUTE_READWRITE */
425
+ const lpflOldProtect = Buffer.allocUnsafe(0x04);
426
+
427
+ const bVirtualProtectEx = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect, lpflOldProtect.ptr);
428
+
429
+ if (!bVirtualProtectEx) {
430
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
431
+ }
432
+
433
+ try {
434
+ const bWriteProcessMemory = Kernel32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
435
+
436
+ if (!bWriteProcessMemory) {
437
+ throw new Win32Error('WriteProcessMemory', Kernel32.GetLastError());
438
+ }
439
+ } finally {
440
+ const flNewProtect2 = lpflOldProtect.readUInt32LE(0x00);
441
+ const lpflOldProtect2 = Buffer.allocUnsafe(0x04);
442
+
443
+ const bVirtualProtectEx2 = Kernel32.VirtualProtectEx(hProcess, lpBaseAddress, dwSize, flNewProtect2, lpflOldProtect2.ptr);
444
+
445
+ if (!bVirtualProtectEx2) {
446
+ throw new Win32Error('VirtualProtectEx', Kernel32.GetLastError());
447
+ }
448
+ }
449
+
450
+ return this;
451
+ }
452
+
453
+ /**
454
+ * Reads or writes a boolean value.
455
+ * @param address Address to access.
456
+ * @param value Optional value to write.
457
+ * @param force When writing, if true temporarily changes page protection to allow the write.
458
+ * @returns The boolean at address, or this instance if writing.
459
+ * @example
460
+ * ```ts
461
+ * const cs2 = new Memory('cs2.exe');
462
+ * const myBool = cs2.bool(0x12345678n);
463
+ * cs2.bool(0x12345678n, true);
464
+ * ```
465
+ */
466
+ public bool(address: bigint): boolean;
467
+ public bool(address: bigint, value: boolean, force?: boolean): this;
468
+ public bool(address: bigint, value?: boolean, force?: boolean): boolean | this {
469
+ const { Scratch1 } = this;
470
+
471
+ if (value === undefined) {
472
+ return this.read(address, Scratch1)[0x00] !== 0;
473
+ }
474
+
475
+ Scratch1[0x00] = value ? 0x01 : 0x00;
476
+
477
+ void this.write(address, Scratch1, force);
478
+
479
+ return this;
480
+ }
481
+
482
+ /**
483
+ * Reads or writes a Buffer.
484
+ * @param address Address to access.
485
+ * @param lengthOrValue Length to read or Buffer to write.
486
+ * @param force When writing, if true temporarily changes page protection to allow the write.
487
+ * @returns Buffer read or this instance if writing.
488
+ * @example
489
+ * ```ts
490
+ * const cs2 = new Memory('cs2.exe');
491
+ * const myBuffer = cs2.buffer(0x12345678n, 8);
492
+ * cs2.buffer(0x12345678n, Buffer.from([1,2,3,4]));
493
+ * ```
494
+ */
495
+ public buffer(address: bigint, length: number): Buffer;
496
+ public buffer(address: bigint, value: Buffer, force?: boolean): this;
497
+ public buffer(address: bigint, lengthOrValue: number | Buffer, force?: boolean): Buffer | this {
498
+ if (typeof lengthOrValue === 'number') {
499
+ const length = lengthOrValue;
500
+ const scratch = Buffer.allocUnsafe(length);
501
+
502
+ return this.read(address, scratch);
503
+ }
504
+
505
+ const value = lengthOrValue;
506
+
507
+ void this.write(address, value, force);
508
+
509
+ return this;
510
+ }
511
+
512
+ /**
513
+ * Reads or writes a C-style string.
514
+ * @param address Address to access.
515
+ * @param lengthOrValue Length to read or CString to write.
516
+ * @param force When writing, if true temporarily changes page protection to allow the write.
517
+ * @returns CString read or this instance if writing.
518
+ * @example
519
+ * ```ts
520
+ * const cs2 = new Memory('cs2.exe');
521
+ * const myCString = cs2.cString(0x12345678n, 16);
522
+ * cs2.cString(0x12345678n, new CString('hello'));
523
+ * ```
524
+ */
525
+ public cString(address: bigint, length: number): CString;
526
+ public cString(address: bigint, value: CString, force?: boolean): this;
527
+ public cString(address: bigint, lengthOrValue: number | CString, force?: boolean): CString | this {
528
+ if (typeof lengthOrValue === 'number') {
529
+ const scratch = new Uint8Array(lengthOrValue);
530
+
531
+ void this.read(address, scratch);
532
+
533
+ const indexOf = scratch.indexOf(0x00);
534
+
535
+ if (indexOf === -1) {
536
+ scratch[lengthOrValue - 1] = 0x00;
537
+ }
538
+
539
+ return new CString(scratch.ptr);
540
+ }
541
+
542
+ const scratch = Buffer.from(lengthOrValue);
543
+
544
+ void this.write(address, scratch, force);
545
+
546
+ return this;
547
+ }
548
+
549
+ /**
550
+ * Reads or writes a 32-bit float.
551
+ * @param address Address to access.
552
+ * @param value Optional value to write.
553
+ * @param force When writing, if true temporarily changes page protection to allow the write.
554
+ * @returns The float at address, or this instance if writing.
555
+ * @example
556
+ * ```ts
557
+ * const cs2 = new Memory('cs2.exe');
558
+ * const myFloat = cs2.f32(0x12345678n);
559
+ * cs2.f32(0x12345678n, 1.23);
560
+ * ```
561
+ */
562
+ public f32(address: bigint): number;
563
+ public f32(address: bigint, value: number, force?: boolean): this;
564
+ public f32(address: bigint, value?: number, force?: boolean): number | this {
565
+ const { Scratch4Float32Array } = this; // prettier-ignore
566
+
567
+ if (value === undefined) {
568
+ return this.read(address, Scratch4Float32Array)[0x00];
569
+ }
570
+
571
+ Scratch4Float32Array[0x00] = value;
572
+
573
+ void this.write(address, Scratch4Float32Array, force);
574
+
575
+ return this;
576
+ }
577
+
578
+ /**
579
+ * Reads or writes a Float32Array.
580
+ * @param address Address to access.
581
+ * @param lengthOrValues Length to read or Float32Array to write.
582
+ * @param force When writing, if true temporarily changes page protection to allow the write.
583
+ * @returns Float32Array read or this instance if writing.
584
+ * @example
585
+ * ```ts
586
+ * const cs2 = new Memory('cs2.exe');
587
+ * const myArray = cs2.f32Array(0x12345678n, 3);
588
+ * cs2.f32Array(0x12345678n, new Float32Array([1,2,3]));
589
+ * ```
590
+ */
591
+ public f32Array(address: bigint, length: number): Float32Array;
592
+ public f32Array(address: bigint, values: Float32Array, force?: boolean): this;
593
+ public f32Array(address: bigint, lengthOrValues: Float32Array | number, force?: boolean): Float32Array | this {
594
+ if (typeof lengthOrValues === 'number') {
595
+ const length = lengthOrValues;
596
+ const scratch = new Float32Array(length);
597
+
598
+ void this.read(address, scratch);
599
+
600
+ return scratch;
601
+ }
602
+
603
+ const values = lengthOrValues;
604
+
605
+ void this.write(address, values, force);
606
+
607
+ return this;
608
+ }
609
+
610
+ /**
611
+ * Reads or writes a 64-bit float.
612
+ * @param address Address to access.
613
+ * @param value Optional value to write.
614
+ * @param force When writing, if true temporarily changes page protection to allow the write.
615
+ * @returns The float at address, or this instance if writing.
616
+ * @example
617
+ * ```ts
618
+ * const cs2 = new Memory('cs2.exe');
619
+ * const myFloat = cs2.f64(0x12345678n);
620
+ * cs2.f64(0x12345678n, 1.23);
621
+ * ```
622
+ */
623
+ public f64(address: bigint): number;
624
+ public f64(address: bigint, value: number, force?: boolean): this;
625
+ public f64(address: bigint, value?: number, force?: boolean): number | this {
626
+ const { Scratch8Float64Array } = this; // prettier-ignore
627
+
628
+ if (value === undefined) {
629
+ return this.read(address, Scratch8Float64Array)[0x00];
630
+ }
631
+
632
+ Scratch8Float64Array[0x00] = value;
633
+
634
+ void this.write(address, Scratch8Float64Array, force);
635
+
636
+ return this;
637
+ }
638
+
639
+ /**
640
+ * Reads or writes a Float64Array.
641
+ * @param address Address to access.
642
+ * @param lengthOrValues Length to read or Float64Array to write.
643
+ * @param force When writing, if true temporarily changes page protection to allow the write.
644
+ * @returns Float64Array read or this instance if writing.
645
+ * @example
646
+ * ```ts
647
+ * const cs2 = new Memory('cs2.exe');
648
+ * const myArray = cs2.f64Array(0x12345678n, 2);
649
+ * cs2.f64Array(0x12345678n, new Float64Array([1,2]));
650
+ * ```
651
+ */
652
+ public f64Array(address: bigint, length: number): Float64Array;
653
+ public f64Array(address: bigint, values: Float64Array, force?: boolean): this;
654
+ public f64Array(address: bigint, lengthOrValues: Float64Array | number, force?: boolean): Float64Array | this {
655
+ if (typeof lengthOrValues === 'number') {
656
+ const length = lengthOrValues;
657
+ const scratch = new Float64Array(length);
658
+
659
+ void this.read(address, scratch);
660
+
661
+ return scratch;
662
+ }
663
+
664
+ const values = lengthOrValues;
665
+
666
+ void this.write(address, values, force);
667
+
668
+ return this;
669
+ }
670
+
671
+ /**
672
+ * Reads or writes a 16-bit integer.
673
+ * @param address Address to access.
674
+ * @param value Optional value to write.
675
+ * @param force When writing, if true temporarily changes page protection to allow the write.
676
+ * @returns The int at address, or this instance if writing.
677
+ * @example
678
+ * ```ts
679
+ * const cs2 = new Memory('cs2.exe');
680
+ * const myInt = cs2.i16(0x12345678n);
681
+ * cs2.i16(0x12345678n, 42);
682
+ * ```
683
+ */
684
+ public i16(address: bigint): number;
685
+ public i16(address: bigint, value: number, force?: boolean): this;
686
+ public i16(address: bigint, value?: number, force?: boolean): number | this {
687
+ const { Scratch2Int16Array } = this; // prettier-ignore
688
+
689
+ if (value === undefined) {
690
+ return this.read(address, Scratch2Int16Array)[0x00];
691
+ }
692
+
693
+ Scratch2Int16Array[0x00] = value;
694
+
695
+ void this.write(address, Scratch2Int16Array, force);
696
+
697
+ return this;
698
+ }
699
+
700
+ /**
701
+ * Reads or writes an Int16Array.
702
+ * @param address Address to access.
703
+ * @param lengthOrValues Length to read or Int16Array to write.
704
+ * @param force When writing, if true temporarily changes page protection to allow the write.
705
+ * @returns Int16Array read or this instance if writing.
706
+ * @example
707
+ * ```ts
708
+ * const cs2 = new Memory('cs2.exe');
709
+ * const myArray = cs2.i16Array(0x12345678n, 2);
710
+ * cs2.i16Array(0x12345678n, new Int16Array([1,2]));
711
+ * ```
712
+ */
713
+ public i16Array(address: bigint, length: number): Int16Array;
714
+ public i16Array(address: bigint, values: Int16Array, force?: boolean): this;
715
+ public i16Array(address: bigint, lengthOrValues: Int16Array | number, force?: boolean): Int16Array | this {
716
+ if (typeof lengthOrValues === 'number') {
717
+ const length = lengthOrValues;
718
+ const scratch = new Int16Array(length);
719
+
720
+ void this.read(address, scratch);
721
+
722
+ return scratch;
723
+ }
724
+
725
+ const values = lengthOrValues;
726
+
727
+ void this.write(address, values, force);
728
+
729
+ return this;
730
+ }
731
+
732
+ /**
733
+ * Reads or writes a 32-bit integer.
734
+ * @param address Address to access.
735
+ * @param value Optional value to write.
736
+ * @param force When writing, if true temporarily changes page protection to allow the write.
737
+ * @returns The int at address, or this instance if writing.
738
+ * @example
739
+ * ```ts
740
+ * const cs2 = new Memory('cs2.exe');
741
+ * const myInt = cs2.i32(0x12345678n);
742
+ * cs2.i32(0x12345678n, 42);
743
+ * ```
744
+ */
745
+ public i32(address: bigint): number;
746
+ public i32(address: bigint, value: number, force?: boolean): this;
747
+ public i32(address: bigint, value?: number, force?: boolean): number | this {
748
+ const { Scratch4Int32Array } = this;
749
+
750
+ if (value === undefined) {
751
+ return this.read(address, Scratch4Int32Array)[0x00];
752
+ }
753
+
754
+ Scratch4Int32Array[0x00] = value;
755
+
756
+ void this.write(address, Scratch4Int32Array, force);
757
+
758
+ return this;
759
+ }
760
+
761
+ /**
762
+ * Reads or writes an Int32Array.
763
+ * @param address Address to access.
764
+ * @param lengthOrValues Length to read or Int32Array to write.
765
+ * @param force When writing, if true temporarily changes page protection to allow the write.
766
+ * @returns Int32Array read or this instance if writing.
767
+ * @example
768
+ * ```ts
769
+ * const cs2 = new Memory('cs2.exe');
770
+ * const myArray = cs2.i32Array(0x12345678n, 2);
771
+ * cs2.i32Array(0x12345678n, new Int32Array([1,2]));
772
+ * ```
773
+ */
774
+ public i32Array(address: bigint, length: number): Int32Array;
775
+ public i32Array(address: bigint, values: Int32Array, force?: boolean): this;
776
+ public i32Array(address: bigint, lengthOrValues: Int32Array | number, force?: boolean): Int32Array | this {
777
+ if (typeof lengthOrValues === 'number') {
778
+ const length = lengthOrValues;
779
+ const scratch = new Int32Array(length);
780
+
781
+ void this.read(address, scratch);
782
+
783
+ return scratch;
784
+ }
785
+
786
+ const values = lengthOrValues;
787
+
788
+ void this.write(address, values, force);
789
+
790
+ return this;
791
+ }
792
+
793
+ /**
794
+ * Reads or writes a 64-bit integer.
795
+ * @param address Address to access.
796
+ * @param value Optional value to write.
797
+ * @param force When writing, if true temporarily changes page protection to allow the write.
798
+ * @returns The bigint at address, or this instance if writing.
799
+ * @example
800
+ * ```ts
801
+ * const cs2 = new Memory('cs2.exe');
802
+ * const myBigInt = cs2.i64(0x12345678n);
803
+ * cs2.i64(0x12345678n, 123n);
804
+ * ```
805
+ */
806
+ public i64(address: bigint): bigint;
807
+ public i64(address: bigint, value: bigint, force?: boolean): this;
808
+ public i64(address: bigint, value?: bigint, force?: boolean): bigint | this {
809
+ const { Scratch8BigInt64Array } = this;
810
+
811
+ if (value === undefined) {
812
+ return this.read(address, Scratch8BigInt64Array)[0x00];
813
+ }
814
+
815
+ Scratch8BigInt64Array[0x00] = value;
816
+
817
+ void this.write(address, Scratch8BigInt64Array, force);
818
+
819
+ return this;
820
+ }
821
+
822
+ /**
823
+ * Reads or writes a BigInt64Array.
824
+ * @param address Address to access.
825
+ * @param lengthOrValues Length to read or BigInt64Array to write.
826
+ * @param force When writing, if true temporarily changes page protection to allow the write.
827
+ * @returns BigInt64Array read or this instance if writing.
828
+ * @example
829
+ * ```ts
830
+ * const cs2 = new Memory('cs2.exe');
831
+ * const myArray = cs2.i64Array(0x12345678n, 2);
832
+ * cs2.i64Array(0x12345678n, new BigInt64Array([1n,2n]));
833
+ * ```
834
+ */
835
+ public i64Array(address: bigint, length: number): BigInt64Array;
836
+ public i64Array(address: bigint, values: BigInt64Array, force?: boolean): this;
837
+ public i64Array(address: bigint, lengthOrValues: BigInt64Array | number, force?: boolean): BigInt64Array | this {
838
+ if (typeof lengthOrValues === 'number') {
839
+ const length = lengthOrValues;
840
+ const scratch = new BigInt64Array(length);
841
+
842
+ void this.read(address, scratch);
843
+
844
+ return scratch;
845
+ }
846
+
847
+ const values = lengthOrValues;
848
+
849
+ void this.write(address, values, force);
850
+
851
+ return this;
852
+ }
853
+
854
+ /**
855
+ * Reads or writes an 8-bit integer.
856
+ * @param address Address to access.
857
+ * @param value Optional value to write.
858
+ * @param force When writing, if true temporarily changes page protection to allow the write.
859
+ * @returns The int at address, or this instance if writing.
860
+ * @example
861
+ * ```ts
862
+ * const cs2 = new Memory('cs2.exe');
863
+ * const myInt = cs2.i8(0x12345678n);
864
+ * cs2.i8(0x12345678n, 7);
865
+ * ```
866
+ */
867
+ public i8(address: bigint): number;
868
+ public i8(address: bigint, value: number, force?: boolean): this;
869
+ public i8(address: bigint, value?: number, force?: boolean): number | this {
870
+ const { Scratch1Int8Array } = this;
871
+
872
+ if (value === undefined) {
873
+ return this.read(address, Scratch1Int8Array)[0x00];
874
+ }
875
+
876
+ Scratch1Int8Array[0x00] = value;
877
+
878
+ void this.write(address, Scratch1Int8Array, force);
879
+
880
+ return this;
881
+ }
882
+
883
+ /**
884
+ * Reads or writes an Int8Array.
885
+ * @param address Address to access.
886
+ * @param lengthOrValues Length to read or Int8Array to write.
887
+ * @param force When writing, if true temporarily changes page protection to allow the write.
888
+ * @returns Int8Array read or this instance if writing.
889
+ * @example
890
+ * ```ts
891
+ * const cs2 = new Memory('cs2.exe');
892
+ * const myArray = cs2.i8Array(0x12345678n, 2);
893
+ * cs2.i8Array(0x12345678n, new Int8Array([1,2]));
894
+ * ```
895
+ */
896
+ public i8Array(address: bigint, length: number): Int8Array;
897
+ public i8Array(address: bigint, values: Int8Array, force?: boolean): this;
898
+ public i8Array(address: bigint, lengthOrValues: Int8Array | number, force?: boolean): Int8Array | this {
899
+ if (typeof lengthOrValues === 'number') {
900
+ const length = lengthOrValues;
901
+ const scratch = new Int8Array(length);
902
+
903
+ void this.read(address, scratch);
904
+
905
+ return scratch;
906
+ }
907
+
908
+ const values = lengthOrValues;
909
+
910
+ void this.write(address, values, force);
911
+
912
+ return this;
913
+ }
914
+
915
+ /**
916
+ * Reads or writes a 3x3 matrix (Float32Array of length 9).
917
+ * @param address Address to access.
918
+ * @param values Optional Float32Array to write.
919
+ * @param force When writing, if true temporarily changes page protection to allow the write.
920
+ * @returns The matrix at address, or this instance if writing.
921
+ * @example
922
+ * ```ts
923
+ * const cs2 = new Memory('cs2.exe');
924
+ * const myMatrix = cs2.matrix3x3(0x12345678n);
925
+ * cs2.matrix3x3(0x12345678n, new Float32Array(9));
926
+ * ```
927
+ */
928
+ public matrix3x3(address: bigint): Float32Array;
929
+ public matrix3x3(address: bigint, values: Float32Array, force?: boolean): this;
930
+ public matrix3x3(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
931
+ if (values === undefined) {
932
+ const scratch = new Float32Array(0x09);
933
+
934
+ void this.read(address, scratch);
935
+
936
+ return scratch;
937
+ }
938
+
939
+ if (values.length !== 0x09) {
940
+ throw new RangeError('values.length must be 9.');
941
+ }
942
+
943
+ void this.write(address, values, force);
944
+
945
+ return this;
946
+ }
947
+
948
+ /**
949
+ * Reads or writes a 3x4 matrix (Float32Array of length 12).
950
+ * @param address Address to access.
951
+ * @param values Optional Float32Array to write.
952
+ * @param force When writing, if true temporarily changes page protection to allow the write.
953
+ * @returns The matrix at address, or this instance if writing.
954
+ * @example
955
+ * ```ts
956
+ * const cs2 = new Memory('cs2.exe');
957
+ * const myMatrix = cs2.matrix3x4(0x12345678n);
958
+ * cs2.matrix3x4(0x12345678n, new Float32Array(12));
959
+ * ```
960
+ */
961
+ public matrix3x4(address: bigint): Float32Array;
962
+ public matrix3x4(address: bigint, values: Float32Array, force?: boolean): this;
963
+ public matrix3x4(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
964
+ if (values === undefined) {
965
+ const scratch = new Float32Array(0x0c);
966
+
967
+ void this.read(address, scratch);
968
+
969
+ return scratch;
970
+ }
971
+
972
+ if (values.length !== 0x0c) {
973
+ throw new RangeError('values.length must be 12.');
974
+ }
975
+
976
+ void this.write(address, values, force);
977
+
978
+ return this;
979
+ }
980
+
981
+ /**
982
+ * Reads or writes a 4x4 matrix (Float32Array of length 16).
983
+ * @param address Address to access.
984
+ * @param values Optional Float32Array to write.
985
+ * @param force When writing, if true temporarily changes page protection to allow the write.
986
+ * @returns The matrix at address, or this instance if writing.
987
+ * @example
988
+ * ```ts
989
+ * const cs2 = new Memory('cs2.exe');
990
+ * const myMatrix = cs2.matrix4x4(0x12345678n);
991
+ * cs2.matrix4x4(0x12345678n, new Float32Array(16));
992
+ * ```
993
+ */
994
+ public matrix4x4(address: bigint): Float32Array;
995
+ public matrix4x4(address: bigint, values: Float32Array, force?: boolean): this;
996
+ public matrix4x4(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
997
+ if (values === undefined) {
998
+ const scratch = new Float32Array(0x10);
999
+
1000
+ void this.read(address, scratch);
1001
+
1002
+ return scratch;
1003
+ }
1004
+
1005
+ if (values.length !== 0x10) {
1006
+ throw new RangeError('values.length must be 16.');
1007
+ }
1008
+
1009
+ void this.write(address, values, force);
1010
+
1011
+ return this;
1012
+ }
1013
+
1014
+ /**
1015
+ * Reads or writes a Point (object with x, y).
1016
+ * @param address Address to access.
1017
+ * @param value Optional Point to write.
1018
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1019
+ * @returns The point at address, or this instance if writing.
1020
+ * @example
1021
+ * ```ts
1022
+ * const cs2 = new Memory('cs2.exe');
1023
+ * const myPoint = cs2.point(0x12345678n);
1024
+ * cs2.point(0x12345678n, { x: 1, y: 2 });
1025
+ * ```
1026
+ */
1027
+ public point(address: bigint): Point;
1028
+ public point(address: bigint, value: Point, force?: boolean): this;
1029
+ public point(address: bigint, value?: Point, force?: boolean): Point | this {
1030
+ const { Scratch8Float32Array } = this;
1031
+
1032
+ if (value === undefined) {
1033
+ void this.read(address, Scratch8Float32Array);
1034
+
1035
+ const x = Scratch8Float32Array[0x00],
1036
+ y = Scratch8Float32Array[0x01]; // prettier-ignore
1037
+
1038
+ return { x, y };
1039
+ }
1040
+
1041
+ Scratch8Float32Array[0x00] = value.x;
1042
+ Scratch8Float32Array[0x01] = value.y;
1043
+
1044
+ void this.write(address, Scratch8Float32Array, force);
1045
+
1046
+ return this;
1047
+ }
1048
+
1049
+ /**
1050
+ * Reads or writes an array of Points.
1051
+ * @param address Address to access.
1052
+ * @param lengthOrValues Length to read or array to write.
1053
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1054
+ * @returns Array of points read or this instance if writing.
1055
+ * @example
1056
+ * ```ts
1057
+ * const cs2 = new Memory('cs2.exe');
1058
+ * const myPoints = cs2.pointArray(0x12345678n, 2);
1059
+ * cs2.pointArray(0x12345678n, [{ x: 1, y: 2 }, { x: 3, y: 4 }]);
1060
+ * ```
1061
+ */
1062
+ public pointArray(address: bigint, length: number): Point[];
1063
+ public pointArray(address: bigint, value: Point[], force?: boolean): this;
1064
+ public pointArray(address: bigint, lengthOrValues: number | Point[], force?: boolean): Point[] | this {
1065
+ if (typeof lengthOrValues === 'number') {
1066
+ const length = lengthOrValues;
1067
+ const scratch = new Float32Array(length * 2);
1068
+
1069
+ void this.read(address, scratch);
1070
+
1071
+ const result = new Array<Vector2>(length);
1072
+
1073
+ for (let i = 0, j = 0; i < length; i++, j += 0x02) {
1074
+ const x = scratch[j],
1075
+ y = scratch[j + 0x01]; // prettier-ignore
1076
+
1077
+ result[i] = { x, y };
1078
+ }
1079
+
1080
+ return result;
1081
+ }
1082
+
1083
+ const values = lengthOrValues;
1084
+ const scratch = new Float32Array(values.length * 0x02);
1085
+
1086
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x02) {
1087
+ const vector2 = values[i];
1088
+
1089
+ scratch[j] = vector2.x;
1090
+ scratch[j + 0x01] = vector2.y;
1091
+ }
1092
+
1093
+ void this.write(address, scratch, force);
1094
+
1095
+ return this;
1096
+ }
1097
+
1098
+ /**
1099
+ * Reads or writes a raw Point (two Float32 values) as a Float32Array.
1100
+ * @param address Address to access.
1101
+ * @param values Optional Float32Array of length 2 to write.
1102
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1103
+ * @returns Float32Array read or this instance if writing.
1104
+ * @example
1105
+ * ```ts
1106
+ * const cs2 = new Memory('cs2.exe');
1107
+ * const myPoint = cs2.pointRaw(0x12345678n);
1108
+ * cs2.pointRaw(0x12345678n, new Float32Array([1, 2]));
1109
+ * ```
1110
+ */
1111
+ public pointRaw(address: bigint): Float32Array;
1112
+ public pointRaw(address: bigint, values: Float32Array, force?: boolean): this;
1113
+ public pointRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1114
+ if (values === undefined) {
1115
+ return this.f32Array(address, 0x02);
1116
+ }
1117
+
1118
+ if (values.length !== 0x02) {
1119
+ throw new RangeError('values.length must be 2.');
1120
+ }
1121
+
1122
+ void this.write(address, values, force);
1123
+
1124
+ return this;
1125
+ }
1126
+
1127
+ /**
1128
+ * Reads or writes a QAngle (object with pitch, yaw, roll).
1129
+ * @param address Address to access.
1130
+ * @param value Optional QAngle to write.
1131
+ * @returns The QAngle at address, or this instance if writing.
1132
+ * @example
1133
+ * ```ts
1134
+ * const cs2 = new Memory('cs2.exe');
1135
+ * const myQAngle = cs2.qAngle(0x12345678n);
1136
+ * cs2.qAngle(0x12345678n, { pitch: 1, yaw: 2, roll: 3 });
1137
+ * ```
1138
+ */
1139
+ public qAngle(address: bigint): QAngle;
1140
+ public qAngle(address: bigint, value: QAngle, force?: boolean): this;
1141
+ public qAngle(address: bigint, value?: QAngle, force?: boolean): QAngle | this {
1142
+ const { Scratch12Float32Array } = this;
1143
+
1144
+ if (value === undefined) {
1145
+ void this.read(address, Scratch12Float32Array);
1146
+
1147
+ const pitch = Scratch12Float32Array[0x00],
1148
+ roll = Scratch12Float32Array[0x02],
1149
+ yaw = Scratch12Float32Array[0x01]; // prettier-ignore
1150
+
1151
+ return { pitch, roll, yaw };
1152
+ }
1153
+
1154
+ Scratch12Float32Array[0x00] = value.pitch;
1155
+ Scratch12Float32Array[0x02] = value.roll;
1156
+ Scratch12Float32Array[0x01] = value.yaw;
1157
+
1158
+ void this.write(address, Scratch12Float32Array, force);
1159
+
1160
+ return this;
1161
+ }
1162
+
1163
+ /**
1164
+ * Reads or writes an array of QAngles.
1165
+ * @param address Address to access.
1166
+ * @param lengthOrValues Length to read or array to write.
1167
+ * @returns Array of QAngles read or this instance if writing.
1168
+ * @example
1169
+ * ```ts
1170
+ * const cs2 = new Memory('cs2.exe');
1171
+ * const myQAngles = cs2.qAngleArray(0x12345678n, 2);
1172
+ * cs2.qAngleArray(0x12345678n, [{ pitch: 1, yaw: 2, roll: 3 }]);
1173
+ * ```
1174
+ */
1175
+ public qAngleArray(address: bigint, length: number): QAngle[];
1176
+ public qAngleArray(address: bigint, values: QAngle[], force?: boolean): this;
1177
+ public qAngleArray(address: bigint, lengthOrValues: QAngle[] | number, force?: boolean): QAngle[] | this {
1178
+ if (typeof lengthOrValues === 'number') {
1179
+ const length = lengthOrValues;
1180
+ const scratch = new Float32Array(length * 0x03);
1181
+
1182
+ void this.read(address, scratch);
1183
+
1184
+ const result = new Array<QAngle>(length);
1185
+
1186
+ for (let i = 0, j = 0; i < length; i++, j += 0x03) {
1187
+ const pitch = scratch[j],
1188
+ yaw = scratch[j + 0x01],
1189
+ roll = scratch[j + 0x02]; // prettier-ignore
1190
+
1191
+ result[i] = { pitch, yaw, roll };
1192
+ }
1193
+
1194
+ return result;
1195
+ }
1196
+
1197
+ const values = lengthOrValues;
1198
+ const scratch = new Float32Array(values.length * 0x03);
1199
+
1200
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
1201
+ const qAngle = values[i];
1202
+
1203
+ scratch[j] = qAngle.pitch;
1204
+ scratch[j + 0x02] = qAngle.roll;
1205
+ scratch[j + 0x01] = qAngle.yaw;
1206
+ }
1207
+
1208
+ void this.write(address, scratch, force);
1209
+
1210
+ return this;
1211
+ }
1212
+
1213
+ /**
1214
+ * Reads or writes a raw QAngle as a Float32Array.
1215
+ * @param address Address to access.
1216
+ * @param values Optional Float32Array to write.
1217
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1218
+ * @returns Float32Array read or this instance if writing.
1219
+ * @example
1220
+ * ```ts
1221
+ * const cs2 = new Memory('cs2.exe');
1222
+ * const raw = cs2.qAngleRaw(0x12345678n);
1223
+ * cs2.qAngleRaw(0x12345678n, new Float32Array([1,2,3]));
1224
+ * ```
1225
+ */
1226
+ public qAngleRaw(address: bigint): Float32Array;
1227
+ public qAngleRaw(address: bigint, values: Float32Array, force?: boolean): this;
1228
+ public qAngleRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1229
+ if (values === undefined) {
1230
+ return this.f32Array(address, 0x03);
1231
+ }
1232
+
1233
+ if (values.length !== 0x03) {
1234
+ throw new RangeError('values.length must be 3.');
1235
+ }
1236
+
1237
+ void this.write(address, values, force);
1238
+
1239
+ return this;
1240
+ }
1241
+
1242
+ /**
1243
+ * Reads or writes a Quaternion (object with w, x, y, z).
1244
+ * @param address Address to access.
1245
+ * @param value Optional Quaternion to write.
1246
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1247
+ * @returns The Quaternion at address, or this instance if writing.
1248
+ * @example
1249
+ * ```ts
1250
+ * const cs2 = new Memory('cs2.exe');
1251
+ * const myQuaternion = cs2.quaternion(0x12345678n);
1252
+ * cs2.quaternion(0x12345678n, { w: 1, x: 0, y: 0, z: 0 });
1253
+ * ```
1254
+ */
1255
+ public quaternion(address: bigint): Quaternion;
1256
+ public quaternion(address: bigint, value: Quaternion, force?: boolean): this;
1257
+ public quaternion(address: bigint, value?: Quaternion, force?: boolean): Quaternion | this {
1258
+ const { Scratch16Float32Array } = this;
1259
+
1260
+ if (value === undefined) {
1261
+ void this.read(address, Scratch16Float32Array);
1262
+
1263
+ const w = Scratch16Float32Array[0x03],
1264
+ x = Scratch16Float32Array[0x00],
1265
+ y = Scratch16Float32Array[0x01],
1266
+ z = Scratch16Float32Array[0x02]; // prettier-ignore
1267
+
1268
+ return { w, x, y, z };
1269
+ }
1270
+
1271
+ Scratch16Float32Array[0x03] = value.w;
1272
+ Scratch16Float32Array[0x00] = value.x;
1273
+ Scratch16Float32Array[0x01] = value.y;
1274
+ Scratch16Float32Array[0x02] = value.z;
1275
+
1276
+ void this.write(address, Scratch16Float32Array, force);
1277
+
1278
+ return this;
1279
+ }
1280
+
1281
+ /**
1282
+ * Reads or writes an array of Quaternions.
1283
+ * @param address Address to access.
1284
+ * @param lengthOrValues Length to read or array to write.
1285
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1286
+ * @returns Array of Quaternions read or this instance if writing.
1287
+ * @example
1288
+ * ```ts
1289
+ * const cs2 = new Memory('cs2.exe');
1290
+ * const myQuaternions = cs2.quaternionArray(0x12345678n, 2);
1291
+ * cs2.quaternionArray(0x12345678n, [{ w: 1, x: 0, y: 0, z: 0 }]);
1292
+ * ```
1293
+ */
1294
+ public quaternionArray(address: bigint, length: number): Quaternion[];
1295
+ public quaternionArray(address: bigint, values: Quaternion[], force?: boolean): this;
1296
+ public quaternionArray(address: bigint, lengthOrValues: Quaternion[] | number, force?: boolean): Quaternion[] | this {
1297
+ if (typeof lengthOrValues === 'number') {
1298
+ const length = lengthOrValues;
1299
+ const scratch = new Float32Array(length * 0x04); // 4 * f32 per Quaternion
1300
+
1301
+ void this.read(address, scratch);
1302
+
1303
+ const result = new Array<Quaternion>(length);
1304
+
1305
+ for (let i = 0, j = 0; i < length; i++, j += 0x04) {
1306
+ const w = scratch[j + 0x03];
1307
+ const x = scratch[j];
1308
+ const y = scratch[j + 0x01];
1309
+ const z = scratch[j + 0x02];
1310
+
1311
+ result[i] = { w, x, y, z };
1312
+ }
1313
+
1314
+ return result;
1315
+ }
1316
+
1317
+ const values = lengthOrValues;
1318
+ const scratch = new Float32Array(values.length * 0x04);
1319
+
1320
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x04) {
1321
+ const quaternion = values[i];
1322
+
1323
+ scratch[j + 0x03] = quaternion.w;
1324
+ scratch[j] = quaternion.x;
1325
+ scratch[j + 0x01] = quaternion.y;
1326
+ scratch[j + 0x02] = quaternion.z;
1327
+ }
1328
+
1329
+ void this.write(address, scratch, force);
1330
+
1331
+ return this;
1332
+ }
1333
+
1334
+ /**
1335
+ * Reads or writes a raw Quaternion as a Float32Array.
1336
+ * @param address Address to access.
1337
+ * @param values Optional Float32Array to write.
1338
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1339
+ * @returns Float32Array read or this instance if writing.
1340
+ * @example
1341
+ * ```ts
1342
+ * const cs2 = new Memory('cs2.exe');
1343
+ * const raw = cs2.quaternionRaw(0x12345678n);
1344
+ * cs2.quaternionRaw(0x12345678n, new Float32Array([1,0,0,0]));
1345
+ * ```
1346
+ */
1347
+ public quaternionRaw(address: bigint): Float32Array;
1348
+ public quaternionRaw(address: bigint, values: Float32Array, force?: boolean): this;
1349
+ public quaternionRaw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
1350
+ if (values === undefined) {
1351
+ return this.f32Array(address, 0x04);
1352
+ }
1353
+
1354
+ if (values.length !== 0x04) {
1355
+ throw new RangeError('values.length must be 4.');
1356
+ }
1357
+
1358
+ void this.write(address, values, force);
1359
+
1360
+ return this;
1361
+ }
1362
+
1363
+ /**
1364
+ * Reads or writes an RGB color (object with r, g, b).
1365
+ * @param address Address to access.
1366
+ * @param value Optional RGB to write.
1367
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1368
+ * @returns The RGB at address, or this instance if writing.
1369
+ * @example
1370
+ * ```ts
1371
+ * const cs2 = new Memory('cs2.exe');
1372
+ * const myRGB = cs2.rgb(0x12345678n);
1373
+ * cs2.rgb(0x12345678n, { r: 255, g: 0, b: 0 });
1374
+ * ```
1375
+ */
1376
+ public rgb(address: bigint): RGB;
1377
+ public rgb(address: bigint, value: RGB, force?: boolean): this;
1378
+ public rgb(address: bigint, value?: RGB, force?: boolean): RGB | this {
1379
+ const { Scratch3 } = this;
1380
+
1381
+ if (value === undefined) {
1382
+ void this.read(address, Scratch3);
1383
+
1384
+ const r = Scratch3[0x00],
1385
+ g = Scratch3[0x01],
1386
+ b = Scratch3[0x02]; // prettier-ignore
1387
+
1388
+ return { r, g, b };
1389
+ }
1390
+
1391
+ Scratch3[0x00] = value.r;
1392
+ Scratch3[0x01] = value.g;
1393
+ Scratch3[0x02] = value.b;
1394
+
1395
+ void this.write(address, Scratch3, force);
1396
+
1397
+ return this;
1398
+ }
1399
+
1400
+ /**
1401
+ * Reads or writes a raw RGB value as a Uint8Array.
1402
+ * @param address Address to access.
1403
+ * @param values Optional buffer to write.
1404
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1405
+ * @returns Uint8Array read or this instance if writing.
1406
+ * @example
1407
+ * ```ts
1408
+ * const cs2 = new Memory('cs2.exe');
1409
+ * const raw = cs2.rgbRaw(0x12345678n);
1410
+ * cs2.rgbRaw(0x12345678n, new Uint8Array([255,0,0]));
1411
+ * ```
1412
+ */
1413
+ public rgbRaw(address: bigint): Uint8Array;
1414
+ public rgbRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1415
+ public rgbRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1416
+ if (values === undefined) {
1417
+ return this.u8Array(address, 0x03);
1418
+ }
1419
+
1420
+ if (values.length !== 0x03) {
1421
+ throw new RangeError('values.length must be 3.');
1422
+ }
1423
+
1424
+ void this.write(address, values, force);
1425
+
1426
+ return this;
1427
+ }
1428
+
1429
+ /**
1430
+ * Reads or writes an RGBA color (object with r, g, b, a).
1431
+ * @param address Address to access.
1432
+ * @param value Optional RGBA to write.
1433
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1434
+ * @returns The RGBA at address, or this instance if writing.
1435
+ * @example
1436
+ * ```ts
1437
+ * const cs2 = new Memory('cs2.exe');
1438
+ * const myRGBA = cs2.rgba(0x12345678n);
1439
+ * cs2.rgba(0x12345678n, { r: 255, g: 0, b: 0, a: 255 });
1440
+ * ```
1441
+ */
1442
+ public rgba(address: bigint): RGBA;
1443
+ public rgba(address: bigint, value: RGBA, force?: boolean): this;
1444
+ public rgba(address: bigint, value?: RGBA, force?: boolean): RGBA | this {
1445
+ const { Scratch4 } = this;
1446
+
1447
+ if (value === undefined) {
1448
+ void this.read(address, Scratch4);
1449
+
1450
+ const r = Scratch4[0x00],
1451
+ g = Scratch4[0x01],
1452
+ b = Scratch4[0x02],
1453
+ a = Scratch4[0x03]; // prettier-ignore
1454
+
1455
+ return { r, g, b, a };
1456
+ }
1457
+
1458
+ Scratch4[0x00] = value.r;
1459
+ Scratch4[0x01] = value.g;
1460
+ Scratch4[0x02] = value.b;
1461
+ Scratch4[0x03] = value.a;
1462
+
1463
+ void this.write(address, Scratch4, force);
1464
+
1465
+ return this;
1466
+ }
1467
+
1468
+ /**
1469
+ * Reads or writes a raw RGBA value as a Uint8Array.
1470
+ * @param address Address to access.
1471
+ * @param values Optional buffer to write.
1472
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1473
+ * @returns Uint8Array read or this instance if writing.
1474
+ * @example
1475
+ * ```ts
1476
+ * const cs2 = new Memory('cs2.exe');
1477
+ * const raw = cs2.rgbaRaw(0x12345678n);
1478
+ * cs2.rgbaRaw(0x12345678n, new Uint8Array([255,0,0,255]));
1479
+ * ```
1480
+ */
1481
+ public rgbaRaw(address: bigint): Uint8Array;
1482
+ public rgbaRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): this;
1483
+ public rgbaRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray, force?: boolean): Uint8Array | this {
1484
+ if (values === undefined) {
1485
+ return this.u8Array(address, 0x04);
1486
+ }
1487
+
1488
+ if (values.length !== 0x04) {
1489
+ throw new RangeError('values.length must be 4.');
1490
+ }
1491
+
1492
+ void this.write(address, values, force);
1493
+
1494
+ return this;
1495
+ }
1496
+
1497
+ /**
1498
+ * Reads or writes a UTF-8 string.
1499
+ * @param address Address to access.
1500
+ * @param lengthOrValue Length to read or string to write.
1501
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1502
+ * @returns The string at address, or this instance if writing.
1503
+ * @notice When writing, remember to null-terminate your string (e.g., 'hello\0').
1504
+ * @todo Compare performance when using CString vs TextDecoder when reading…
1505
+ * @example
1506
+ * ```ts
1507
+ * const cs2 = new Memory('cs2.exe');
1508
+ * const myString = cs2.string(0x12345678n, 16);
1509
+ * cs2.string(0x12345678n, 'hello\0');
1510
+ * ```
1511
+ */
1512
+ public string(address: bigint, length: number): string;
1513
+ public string(address: bigint, value: string, force?: boolean): this;
1514
+ public string(address: bigint, lengthOrValue: number | string, force?: boolean): string | this {
1515
+ if (typeof lengthOrValue === 'number') {
1516
+ const scratch = new Uint8Array(lengthOrValue);
1517
+
1518
+ void this.read(address, scratch);
1519
+
1520
+ const indexOf = scratch.indexOf(0x00);
1521
+
1522
+ return Memory.TextDecoderUTF8.decode(
1523
+ scratch.subarray(0, indexOf !== -1 ? indexOf : lengthOrValue) //
1524
+ );
1525
+
1526
+ // return new CString(scratch.ptr).valueOf();
1527
+ }
1528
+
1529
+ const scratch = Memory.TextEncoderUTF8.encode(lengthOrValue);
1530
+
1531
+ void this.write(address, scratch, force);
1532
+
1533
+ return this;
1534
+ }
1535
+
1536
+ /**
1537
+ * Reads or writes a 16-bit unsigned integer.
1538
+ * @param address Address to access.
1539
+ * @param value Optional value to write.
1540
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1541
+ * @returns The value at address, or this instance if writing.
1542
+ * @example
1543
+ * ```ts
1544
+ * const cs2 = new Memory('cs2.exe');
1545
+ * const myInt = cs2.u16(0x12345678n);
1546
+ * cs2.u16(0x12345678n, 42);
1547
+ * ```
1548
+ */
1549
+ public u16(address: bigint): number;
1550
+ public u16(address: bigint, value: number, force?: boolean): this;
1551
+ public u16(address: bigint, value?: number, force?: boolean): number | this {
1552
+ const { Scratch2Uint16Array } = this;
1553
+
1554
+ if (value === undefined) {
1555
+ return this.read(address, Scratch2Uint16Array)[0x00];
1556
+ }
1557
+
1558
+ Scratch2Uint16Array[0x00] = value;
1559
+
1560
+ void this.write(address, Scratch2Uint16Array, force);
1561
+
1562
+ return this;
1563
+ }
1564
+
1565
+ /**
1566
+ * Reads or writes a Uint16Array.
1567
+ * @param address Address to access.
1568
+ * @param lengthOrValues Length to read or Uint16Array to write.
1569
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1570
+ * @returns Uint16Array read or this instance if writing.
1571
+ * @example
1572
+ * ```ts
1573
+ * const cs2 = new Memory('cs2.exe');
1574
+ * const myArray = cs2.u16Array(0x12345678n, 2);
1575
+ * cs2.u16Array(0x12345678n, new Uint16Array([1,2]));
1576
+ * ```
1577
+ */
1578
+ public u16Array(address: bigint, length: number): Uint16Array;
1579
+ public u16Array(address: bigint, values: Uint16Array, force?: boolean): this;
1580
+ public u16Array(address: bigint, lengthOrValues: Uint16Array | number, force?: boolean): Uint16Array | this {
1581
+ if (typeof lengthOrValues === 'number') {
1582
+ const length = lengthOrValues;
1583
+ const scratch = new Uint16Array(length);
1584
+
1585
+ void this.read(address, scratch);
1586
+
1587
+ return scratch;
1588
+ }
1589
+
1590
+ const values = lengthOrValues;
1591
+
1592
+ void this.write(address, values, force);
1593
+
1594
+ return this;
1595
+ }
1596
+
1597
+ /**
1598
+ * Reads or writes a 32-bit unsigned integer.
1599
+ * @param address Address to access.
1600
+ * @param value Optional value to write.
1601
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1602
+ * @returns The value at address, or this instance if writing.
1603
+ * @example
1604
+ * ```ts
1605
+ * const cs2 = new Memory('cs2.exe');
1606
+ * const myInt = cs2.u32(0x12345678n);
1607
+ * cs2.u32(0x12345678n, 42);
1608
+ * ```
1609
+ */
1610
+ public u32(address: bigint): number;
1611
+ public u32(address: bigint, value: number, force?: boolean): this;
1612
+ public u32(address: bigint, value?: number, force?: boolean): number | this {
1613
+ const { Scratch4Uint32Array } = this;
1614
+
1615
+ if (value === undefined) {
1616
+ return this.read(address, Scratch4Uint32Array)[0x00];
1617
+ }
1618
+
1619
+ Scratch4Uint32Array[0x00] = value;
1620
+
1621
+ void this.write(address, Scratch4Uint32Array, force);
1622
+
1623
+ return this;
1624
+ }
1625
+
1626
+ /**
1627
+ * Reads or writes a Uint32Array.
1628
+ * @param address Address to access.
1629
+ * @param lengthOrValues Length to read or Uint32Array to write.
1630
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1631
+ * @returns Uint32Array read or this instance if writing.
1632
+ * @example
1633
+ * ```ts
1634
+ * const cs2 = new Memory('cs2.exe');
1635
+ * const myArray = cs2.u32Array(0x12345678n, 2);
1636
+ * cs2.u32Array(0x12345678n, new Uint32Array([1,2]));
1637
+ * ```
1638
+ */
1639
+ public u32Array(address: bigint, length: number): Uint32Array;
1640
+ public u32Array(address: bigint, values: Uint32Array, force?: boolean): this;
1641
+ public u32Array(address: bigint, lengthOrValues: Uint32Array | number, force?: boolean): Uint32Array | this {
1642
+ if (typeof lengthOrValues === 'number') {
1643
+ const length = lengthOrValues;
1644
+ const scratch = new Uint32Array(length);
1645
+
1646
+ void this.read(address, scratch);
1647
+
1648
+ return scratch;
1649
+ }
1650
+
1651
+ const values = lengthOrValues;
1652
+
1653
+ void this.write(address, values, force);
1654
+
1655
+ return this;
1656
+ }
1657
+
1658
+ /**
1659
+ * Reads or writes a 64-bit unsigned integer.
1660
+ * @param address Address to access.
1661
+ * @param value Optional value to write.
1662
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1663
+ * @returns The bigint at address, or this instance if writing.
1664
+ * @example
1665
+ * ```ts
1666
+ * const cs2 = new Memory('cs2.exe');
1667
+ * const myBigInt = cs2.u64(0x12345678n);
1668
+ * cs2.u64(0x12345678n, 123n);
1669
+ * ```
1670
+ */
1671
+ public u64(address: bigint): bigint;
1672
+ public u64(address: bigint, value: bigint, force?: boolean): this;
1673
+ public u64(address: bigint, value?: bigint, force?: boolean): bigint | this {
1674
+ const { Scratch8BigUint64Array } = this;
1675
+
1676
+ if (value === undefined) {
1677
+ return this.read(address, Scratch8BigUint64Array)[0x00];
1678
+ }
1679
+
1680
+ Scratch8BigUint64Array[0x00] = value;
1681
+
1682
+ void this.write(address, Scratch8BigUint64Array, force);
1683
+
1684
+ return this;
1685
+ }
1686
+
1687
+ /**
1688
+ * Reads or writes a BigUint64Array.
1689
+ * @param address Address to access.
1690
+ * @param lengthOrValues Length to read or BigUint64Array to write.
1691
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1692
+ * @returns BigUint64Array read or this instance if writing.
1693
+ * @example
1694
+ * ```ts
1695
+ * const cs2 = new Memory('cs2.exe');
1696
+ * const myArray = cs2.u64Array(0x12345678n, 2);
1697
+ * cs2.u64Array(0x12345678n, new BigUint64Array([1n,2n]));
1698
+ * ```
1699
+ */
1700
+ public u64Array(address: bigint, length: number): BigUint64Array;
1701
+ public u64Array(address: bigint, values: BigUint64Array, force?: boolean): this;
1702
+ public u64Array(address: bigint, lengthOrValues: BigUint64Array | number, force?: boolean): BigUint64Array | this {
1703
+ if (typeof lengthOrValues === 'number') {
1704
+ const length = lengthOrValues;
1705
+ const scratch = new BigUint64Array(length);
1706
+
1707
+ void this.read(address, scratch);
1708
+
1709
+ return scratch;
1710
+ }
1711
+
1712
+ const values = lengthOrValues;
1713
+
1714
+ void this.write(address, values, force);
1715
+
1716
+ return this;
1717
+ }
1718
+
1719
+ /**
1720
+ * Reads or writes an 8-bit unsigned integer.
1721
+ * @param address Address to access.
1722
+ * @param value Optional value to write.
1723
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1724
+ * @returns The value at address, or this instance if writing.
1725
+ * @example
1726
+ * ```ts
1727
+ * const cs2 = new Memory('cs2.exe');
1728
+ * const myInt = cs2.u8(0x12345678n);
1729
+ * cs2.u8(0x12345678n, 7);
1730
+ * ```
1731
+ */
1732
+ public u8(address: bigint): number;
1733
+ public u8(address: bigint, value: number, force?: boolean): this;
1734
+ public u8(address: bigint, value?: number, force?: boolean): number | this {
1735
+ const { Scratch1 } = this;
1736
+
1737
+ if (value === undefined) {
1738
+ return this.read(address, Scratch1)[0x00];
1739
+ }
1740
+
1741
+ Scratch1[0x00] = value;
1742
+
1743
+ void this.write(address, Scratch1, force);
1744
+
1745
+ return this;
1746
+ }
1747
+
1748
+ /**
1749
+ * Reads or writes a Uint8Array.
1750
+ * @param address Address to access.
1751
+ * @param lengthOrValues Length to read or Uint8Array to write.
1752
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1753
+ * @returns Uint8Array read or this instance if writing.
1754
+ * @example
1755
+ * ```ts
1756
+ * const cs2 = new Memory('cs2.exe');
1757
+ * const myArray = cs2.u8Array(0x12345678n, 2);
1758
+ * cs2.u8Array(0x12345678n, new Uint8Array([1,2]));
1759
+ * ```
1760
+ */
1761
+ public u8Array(address: bigint, length: number): Uint8Array;
1762
+ public u8Array(address: bigint, values: Uint8Array, force?: boolean): this;
1763
+ public u8Array(address: bigint, lengthOrValues: Uint8Array | number, force?: boolean): Uint8Array | this {
1764
+ if (typeof lengthOrValues === 'number') {
1765
+ const length = lengthOrValues;
1766
+ const scratch = new Uint8Array(length);
1767
+
1768
+ void this.read(address, scratch);
1769
+
1770
+ return scratch;
1771
+ }
1772
+
1773
+ const values = lengthOrValues;
1774
+
1775
+ void this.write(address, values, force);
1776
+
1777
+ return this;
1778
+ }
1779
+
1780
+ /**
1781
+ * Reads or writes a pointer-sized unsigned integer.
1782
+ * @param address Address to access.
1783
+ * @param value Optional value to write.
1784
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1785
+ * @returns The value at address, or this instance if writing.
1786
+ * @example
1787
+ * ```ts
1788
+ * const cs2 = new Memory('cs2.exe');
1789
+ * const myPtr = cs2.uPtr(0x12345678n);
1790
+ * cs2.uPtr(0x12345678n, 123n);
1791
+ * ```
1792
+ */
1793
+ public uPtr(address: bigint): UPtr;
1794
+ public uPtr(address: bigint, value: UPtr, force?: boolean): this;
1795
+ public uPtr(address: bigint, value?: UPtr, force?: boolean): UPtr | this {
1796
+ // TypeScript is funny sometimes, isn't it?… 🫠…
1797
+ if (value === undefined) {
1798
+ return this.u64(address);
1799
+ }
1800
+
1801
+ return this.u64(address, value, force);
1802
+ }
1803
+
1804
+ /**
1805
+ * Reads or writes an array of pointer-sized unsigned integers.
1806
+ * @param address Address to access.
1807
+ * @param lengthOrValues Length to read or array to write.
1808
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1809
+ * @returns Array read or this instance if writing.
1810
+ * @example
1811
+ * ```ts
1812
+ * const cs2 = new Memory('cs2.exe');
1813
+ * const myPtrs = cs2.uPtrArray(0x12345678n, 2);
1814
+ * cs2.uPtrArray(0x12345678n, new BigUint64Array([1n,2n]));
1815
+ * ```
1816
+ */
1817
+ public uPtrArray(address: bigint, length: number): UPtrArray;
1818
+ public uPtrArray(address: bigint, values: UPtrArray, force?: boolean): this;
1819
+ public uPtrArray(address: bigint, lengthOrValues: UPtrArray | number, force?: boolean): UPtrArray | this {
1820
+ // TypeScript is funny sometimes, isn't it?… 🫠…
1821
+ if (typeof lengthOrValues === 'number') {
1822
+ return this.u64Array(address, lengthOrValues);
1823
+ }
1824
+
1825
+ return this.u64Array(address, lengthOrValues, force);
1826
+ }
1827
+
1828
+ /**
1829
+ * Reads a UtlLinkedList of 64-bit unsigned integers and returns its elements as a BigUint64Array.
1830
+ *
1831
+ * This helper reads the list header at `address`, validates the capacity and element pointer,
1832
+ * reads the elements table, and walks the internal linked indices to produce a compact
1833
+ * BigUint64Array of present elements. If the list is empty or invalid an empty array is
1834
+ * returned.
1835
+ *
1836
+ * @param address Address of the UtlLinkedList header in the remote process.
1837
+ * @returns BigUint64Array containing the list elements (empty if the list is invalid or empty).
1838
+ * @todo Create a writer so that users can write linked lists…
1839
+ * @example
1840
+ * ```ts
1841
+ * const cs2 = new Memory('cs2.exe');
1842
+ * const myList = cs2.utlLinkedListU64(0x12345678n);
1843
+ * ```
1844
+ */
1845
+ public utlLinkedListU64(address: bigint): BigUint64Array {
1846
+ const header = new Uint8Array(0x18);
1847
+ const headerUint16Array = new Uint16Array(header.buffer, header.byteOffset);
1848
+ const headerBigUint64Array = new BigUint64Array(header.buffer, header.byteOffset + 0x08, 2);
1849
+
1850
+ void this.read(address, header);
1851
+
1852
+ const capacity = headerUint16Array[0x01] & 0x7fff;
1853
+ const elementsPtr = headerBigUint64Array[0x00];
1854
+ let index = headerUint16Array[0x08]; // prettier-ignore
1855
+
1856
+ if (capacity === 0 || capacity <= index || elementsPtr === 0n || index === 0xffff) {
1857
+ return new BigUint64Array(0);
1858
+ }
1859
+
1860
+ const scratch = new Uint8Array(capacity << 0x04);
1861
+ const scratchBigUint64Array = new BigUint64Array(scratch.buffer, scratch.byteOffset);
1862
+ const scratchUint16Array = new Uint16Array(scratch.buffer, scratch.byteOffset);
1863
+
1864
+ void this.read(elementsPtr, scratch);
1865
+
1866
+ let count = 0; // prettier-ignore
1867
+ const result = new BigUint64Array(capacity);
1868
+
1869
+ while (count < capacity && capacity > index && index !== 0xffff) {
1870
+ result[count++] = scratchBigUint64Array[index * 0x02];
1871
+
1872
+ const next = scratchUint16Array[0x05 + index * 0x08];
1873
+
1874
+ if (index === next || next === 0xffff) {
1875
+ break;
1876
+ }
1877
+
1878
+ index = next;
1879
+ }
1880
+
1881
+ return capacity === count ? result : result.subarray(0, count);
1882
+ }
1883
+
1884
+ /**
1885
+ * Reads or writes a generic UtlVector as raw bytes (no typing).
1886
+ * Pass elementSize (bytes per element) so we can set/read the header count.
1887
+ * @example
1888
+ * ```ts
1889
+ * const bytes = cs2.utlVectorRaw(0x1234n, 0x14); // read size*elementSize bytes
1890
+ * cs2.utlVectorRaw(0x1234n, 0x14, new Uint8Array([...])); // write
1891
+ * ```
1892
+ */
1893
+ public utlVectorRaw(address: bigint, elementSize: number): Uint8Array;
1894
+ public utlVectorRaw(address: bigint, elementSize: number, values: Uint8Array, force?: boolean): this;
1895
+ public utlVectorRaw(address: bigint, elementSize: number, values?: Uint8Array, force?: boolean): Uint8Array | this {
1896
+ const elementsPtr = this.u64(address + 0x08n);
1897
+
1898
+ if (values === undefined) {
1899
+ const count = this.u32(address);
1900
+
1901
+ if (count === 0 || elementsPtr === 0n) {
1902
+ return new Uint8Array(0);
1903
+ }
1904
+
1905
+ const byteLength = count * elementSize;
1906
+ const scratch = new Uint8Array(byteLength);
1907
+
1908
+ void this.read(elementsPtr, scratch);
1909
+
1910
+ return scratch;
1911
+ }
1912
+
1913
+ if (values.byteLength % elementSize !== 0) {
1914
+ throw new RangeError('values length must be a multiple of elementSize');
1915
+ }
1916
+
1917
+ const count = values.byteLength / elementSize;
1918
+
1919
+ this.u32(address, count, force);
1920
+
1921
+ void this.write(elementsPtr, values, force);
1922
+
1923
+ return this;
1924
+ }
1925
+
1926
+ /**
1927
+ * Reads or writes a UtlVectorU32 (Uint32Array).
1928
+ * @param address Address to access.
1929
+ * @param values Optional Uint32Array to write.
1930
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1931
+ * @returns The vector at address, or this instance if writing.
1932
+ * @example
1933
+ * ```ts
1934
+ * const cs2 = new Memory('cs2.exe');
1935
+ * const myVector = cs2.utlVectorU32(0x12345678n);
1936
+ * cs2.utlVectorU32(0x12345678n, new Uint32Array([1,2,3]));
1937
+ * ```
1938
+ */
1939
+ public utlVectorU32(address: bigint): Uint32Array;
1940
+ public utlVectorU32(address: bigint, values: Uint32Array, force?: boolean): this;
1941
+ public utlVectorU32(address: bigint, values?: Uint32Array, force?: boolean): Uint32Array | this {
1942
+ const elementsPtr = this.u64(address + 0x08n);
1943
+
1944
+ if (values === undefined) {
1945
+ const size = this.u32(address);
1946
+
1947
+ const scratch = new Uint32Array(size);
1948
+
1949
+ void this.read(elementsPtr, scratch);
1950
+
1951
+ return scratch;
1952
+ }
1953
+
1954
+ this.u32(address, values.length, force);
1955
+
1956
+ void this.write(elementsPtr, values, force);
1957
+
1958
+ return this;
1959
+ }
1960
+
1961
+ /**
1962
+ * Reads or writes a UtlVectorU64 (BigUint64Array).
1963
+ * @param address Address to access.
1964
+ * @param values Optional BigUint64Array to write.
1965
+ * @param force When writing, if true temporarily changes page protection to allow the write.
1966
+ * @returns The vector at address, or this instance if writing.
1967
+ * @example
1968
+ * ```ts
1969
+ * const cs2 = new Memory('cs2.exe');
1970
+ * const myVector = cs2.utlVectorU64(0x12345678n);
1971
+ * cs2.utlVectorU64(0x12345678n, new BigUint64Array([1n,2n,3n]));
1972
+ * ```
1973
+ */
1974
+ public utlVectorU64(address: bigint): BigUint64Array;
1975
+ public utlVectorU64(address: bigint, values: BigUint64Array, force?: boolean): this;
1976
+ public utlVectorU64(address: bigint, values?: BigUint64Array, force?: boolean): BigUint64Array | this {
1977
+ const elementsPtr = this.u64(address + 0x08n);
1978
+
1979
+ if (values === undefined) {
1980
+ const size = this.u32(address);
1981
+
1982
+ const scratch = new BigUint64Array(size);
1983
+
1984
+ void this.read(elementsPtr, scratch);
1985
+
1986
+ return scratch;
1987
+ }
1988
+
1989
+ this.u32(address, values.length, force);
1990
+
1991
+ void this.write(elementsPtr, values, force);
1992
+
1993
+ return this;
1994
+ }
1995
+
1996
+ /**
1997
+ * Reads or writes a Vector2 (object with x, y).
1998
+ * @param address Address to access.
1999
+ * @param value Optional Vector2 to write.
2000
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2001
+ * @returns The Vector2 at address, or this instance if writing.
2002
+ * @example
2003
+ * ```ts
2004
+ * const cs2 = new Memory('cs2.exe');
2005
+ * const myVector2 = cs2.vector2(0x12345678n);
2006
+ * cs2.vector2(0x12345678n, { x: 1, y: 2 });
2007
+ * ```
2008
+ */
2009
+ public vector2(address: bigint): Vector2;
2010
+ public vector2(address: bigint, value: Vector2, force?: boolean): this;
2011
+ public vector2(address: bigint, value?: Vector2, force?: boolean): Vector2 | this {
2012
+ // TypeScript is funny sometimes, isn't it?… 🫠…
2013
+ if (value === undefined) {
2014
+ return this.point(address);
2015
+ }
2016
+
2017
+ return this.point(address, value, force);
2018
+ }
2019
+
2020
+ /**
2021
+ * Reads or writes an array of Vector2.
2022
+ * @param address Address to access.
2023
+ * @param lengthOrValues Length to read or array to write.
2024
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2025
+ * @returns Array of Vector2 read or this instance if writing.
2026
+ * @example
2027
+ * ```ts
2028
+ * const cs2 = new Memory('cs2.exe');
2029
+ * const myVector2s = cs2.vector2Array(0x12345678n, 2);
2030
+ * cs2.vector2Array(0x12345678n, [{ x: 1, y: 2 }, { x: 3, y: 4 }]);
2031
+ * ```
2032
+ */
2033
+ public vector2Array(address: bigint, length: number): Vector2[];
2034
+ public vector2Array(address: bigint, values: Vector2[], force?: boolean): this;
2035
+ public vector2Array(address: bigint, lengthOrValues: Vector2[] | number, force?: boolean): Vector2[] | this {
2036
+ // TypeScript is funny sometimes, isn't it?… 🫠…
2037
+ if (typeof lengthOrValues === 'number') {
2038
+ return this.pointArray(address, lengthOrValues);
2039
+ }
2040
+
2041
+ return this.pointArray(address, lengthOrValues, force);
2042
+ }
2043
+
2044
+ /**
2045
+ * Reads or writes a raw Vector2 as a Float32Array.
2046
+ * @param address Address to access.
2047
+ * @param values Optional Float32Array to write.
2048
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2049
+ * @returns Float32Array read or this instance if writing.
2050
+ * @example
2051
+ * ```ts
2052
+ * const cs2 = new Memory('cs2.exe');
2053
+ * const myVector2 = cs2.vector2Raw(0x12345678n);
2054
+ * cs2.vector2Raw(0x12345678n, new Float32Array([1, 2]));
2055
+ * ```
2056
+ */
2057
+ public vector2Raw(address: bigint): Float32Array;
2058
+ public vector2Raw(address: bigint, values: Float32Array, force?: boolean): this;
2059
+ public vector2Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2060
+ if (values === undefined) {
2061
+ return this.f32Array(address, 0x02);
2062
+ }
2063
+
2064
+ if (values.length !== 0x02) {
2065
+ throw new RangeError('values.length must be 2.');
2066
+ }
2067
+
2068
+ void this.write(address, values, force);
2069
+
2070
+ return this;
2071
+ }
2072
+
2073
+ /**
2074
+ * Reads or writes a Vector3 (object with x, y, z).
2075
+ * @param address Address to access.
2076
+ * @param value Optional Vector3 to write.
2077
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2078
+ * @returns The Vector3 at address, or this instance if writing.
2079
+ * @example
2080
+ * ```ts
2081
+ * const cs2 = new Memory('cs2.exe');
2082
+ * const myVector3 = cs2.vector3(0x12345678n);
2083
+ * cs2.vector3(0x12345678n, { x: 1, y: 2, z: 3 });
2084
+ * ```
2085
+ */
2086
+ public vector3(address: bigint): Vector3;
2087
+ public vector3(address: bigint, value: Vector3, force?: boolean): this;
2088
+ public vector3(address: bigint, value?: Vector3, force?: boolean): Vector3 | this {
2089
+ const { Scratch12Float32Array } = this;
2090
+
2091
+ if (value === undefined) {
2092
+ void this.read(address, Scratch12Float32Array);
2093
+
2094
+ const x = Scratch12Float32Array[0x00],
2095
+ y = Scratch12Float32Array[0x01],
2096
+ z = Scratch12Float32Array[0x02]; // prettier-ignore
2097
+
2098
+ return { x, y, z };
2099
+ }
2100
+
2101
+ Scratch12Float32Array[0x00] = value.x;
2102
+ Scratch12Float32Array[0x01] = value.y;
2103
+ Scratch12Float32Array[0x02] = value.z;
2104
+
2105
+ void this.write(address, Scratch12Float32Array, force);
2106
+
2107
+ return this;
2108
+ }
2109
+
2110
+ /**
2111
+ * Reads or writes an array of Vector3.
2112
+ * @param address Address to access.
2113
+ * @param lengthOrValues Length to read or array to write.
2114
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2115
+ * @returns Array of Vector3 read or this instance if writing.
2116
+ * @example
2117
+ * ```ts
2118
+ * const cs2 = new Memory('cs2.exe');
2119
+ * const myVector3s = cs2.vector3Array(0x12345678n, 2);
2120
+ * cs2.vector3Array(0x12345678n, [{ x: 1, y: 2, z: 3 }]);
2121
+ * ```
2122
+ */
2123
+ public vector3Array(address: bigint, length: number): Vector3[];
2124
+ public vector3Array(address: bigint, values: Vector3[], force?: boolean): this;
2125
+ public vector3Array(address: bigint, lengthOrValues: Vector3[] | number, force?: boolean): Vector3[] | this {
2126
+ if (typeof lengthOrValues === 'number') {
2127
+ const length = lengthOrValues;
2128
+ const scratch = new Float32Array(length * 0x03);
2129
+
2130
+ void this.read(address, scratch);
2131
+
2132
+ const result = new Array<Vector3>(length);
2133
+
2134
+ for (let i = 0, j = 0; i < length; i++, j += 0x03) {
2135
+ const x = scratch[j];
2136
+ const y = scratch[j + 0x01];
2137
+ const z = scratch[j + 0x02];
2138
+
2139
+ result[i] = { x, y, z };
2140
+ }
2141
+
2142
+ return result;
2143
+ }
2144
+
2145
+ const values = lengthOrValues;
2146
+ const scratch = new Float32Array(values.length * 0x03);
2147
+
2148
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
2149
+ const vector3 = values[i];
2150
+
2151
+ scratch[j] = vector3.x;
2152
+ scratch[j + 0x01] = vector3.y;
2153
+ scratch[j + 0x02] = vector3.z;
2154
+ }
2155
+
2156
+ void this.write(address, scratch, force);
2157
+
2158
+ return this;
2159
+ }
2160
+
2161
+ /**
2162
+ * Reads or writes a raw Vector3 as a Float32Array.
2163
+ * @param address Address to access.
2164
+ * @param values Optional Float32Array to write.
2165
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2166
+ * @returns Float32Array read or this instance if writing.
2167
+ * @example
2168
+ * ```ts
2169
+ * const cs2 = new Memory('cs2.exe');
2170
+ * const myVector3 = cs2.vector3Raw(0x12345678n);
2171
+ * cs2.vector3Raw(0x12345678n, new Float32Array([1, 2, 3]));
2172
+ * ```
2173
+ */
2174
+ public vector3Raw(address: bigint): Float32Array;
2175
+ public vector3Raw(address: bigint, values: Float32Array, force?: boolean): this;
2176
+ public vector3Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2177
+ if (values === undefined) {
2178
+ return this.f32Array(address, 0x03);
2179
+ }
2180
+
2181
+ if (values.length !== 0x03) {
2182
+ throw new RangeError('values.length must be 3.');
2183
+ }
2184
+
2185
+ void this.write(address, values, force);
2186
+
2187
+ return this;
2188
+ }
2189
+
2190
+ /**
2191
+ * Reads or writes a Vector4 (object with w, x, y, z).
2192
+ * @param address Address to access.
2193
+ * @param value Optional Vector4 to write.
2194
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2195
+ * @returns The Vector4 at address, or this instance if writing.
2196
+ * @example
2197
+ * ```ts
2198
+ * const cs2 = new Memory('cs2.exe');
2199
+ * const myVector4 = cs2.vector4(0x12345678n);
2200
+ * cs2.vector4(0x12345678n, { w: 1, x: 0, y: 0, z: 0 });
2201
+ * ```
2202
+ */
2203
+ public vector4(address: bigint): Vector4;
2204
+ public vector4(address: bigint, value: Vector4, force?: boolean): this;
2205
+ public vector4(address: bigint, value?: Vector4, force?: boolean): Vector4 | this {
2206
+ // TypeScript is funny sometimes, isn't it?… 🫠…
2207
+ if (value === undefined) {
2208
+ return this.quaternion(address);
2209
+ }
2210
+
2211
+ return this.quaternion(address, value, force);
2212
+ }
2213
+
2214
+ /**
2215
+ * Reads or writes an array of Vector4.
2216
+ * @param address Address to access.
2217
+ * @param lengthOrValues Length to read or array to write.
2218
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2219
+ * @returns Array of Vector4 read or this instance if writing.
2220
+ * @example
2221
+ * ```ts
2222
+ * const cs2 = new Memory('cs2.exe');
2223
+ * const myVector4s = cs2.vector4Array(0x12345678n, 2);
2224
+ * cs2.vector4Array(0x12345678n, [{ w: 1, x: 0, y: 0, z: 0 }]);
2225
+ * ```
2226
+ */
2227
+ public vector4Array(address: bigint, length: number): Vector4[];
2228
+ public vector4Array(address: bigint, values: Vector4[], force?: boolean): this;
2229
+ public vector4Array(address: bigint, lengthOrValues: Vector4[] | number, force?: boolean): Vector4[] | this {
2230
+ // TypeScript is funny sometimes, isn't it?… 🫠…
2231
+ if (typeof lengthOrValues === 'number') {
2232
+ return this.quaternionArray(address, lengthOrValues);
2233
+ }
2234
+
2235
+ return this.quaternionArray(address, lengthOrValues, force);
2236
+ }
2237
+
2238
+ /**
2239
+ * Reads or writes a raw Vector4 as a Float32Array.
2240
+ * @param address Address to access.
2241
+ * @param values Optional Float32Array to write.
2242
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2243
+ * @returns Float32Array read or this instance if writing.
2244
+ * @example
2245
+ * ```ts
2246
+ * const cs2 = new Memory('cs2.exe');
2247
+ * const myVector4 = cs2.vector4Raw(0x12345678n);
2248
+ * cs2.vector4Raw(0x12345678n, new Float32Array([1, 0, 0, 0]));
2249
+ * ```
2250
+ */
2251
+ public vector4Raw(address: bigint): Float32Array;
2252
+ public vector4Raw(address: bigint, values: Float32Array, force?: boolean): this;
2253
+ public vector4Raw(address: bigint, values?: Float32Array, force?: boolean): Float32Array | this {
2254
+ if (values === undefined) {
2255
+ return this.f32Array(address, 0x04);
2256
+ }
2257
+
2258
+ if (values.length !== 0x04) {
2259
+ throw new RangeError('values.length must be 4.');
2260
+ }
2261
+
2262
+ void this.write(address, values, force);
2263
+
2264
+ return this;
2265
+ }
2266
+
2267
+ /**
2268
+ * Reads or writes a wide (UTF-16LE) string.
2269
+ * @param address Address to access.
2270
+ * @param lengthOrValue Length in characters to read or string to write.
2271
+ * @param force When writing, if true temporarily changes page protection to allow the write.
2272
+ * @returns The string at address, or this instance if writing.
2273
+ * @notice When writing, remember to null-terminate your string (e.g., 'hello\0').
2274
+ * @example
2275
+ * ```ts
2276
+ * const cs2 = new Memory('cs2.exe');
2277
+ * const myWideString = cs2.wideString(0x12345678n, 16);
2278
+ * cs2.wideString(0x12345678n, 'hello\0');
2279
+ * ```
2280
+ */
2281
+ public wideString(address: bigint, length: number): string;
2282
+ public wideString(address: bigint, value: string, force?: boolean): this;
2283
+ public wideString(address: bigint, lengthOrValue: number | string, force?: boolean): string | this {
2284
+ if (typeof lengthOrValue === 'number') {
2285
+ const scratch = Buffer.allocUnsafe(lengthOrValue * 2);
2286
+
2287
+ void this.read(address, scratch);
2288
+
2289
+ const u16View = new Uint16Array(scratch.buffer, scratch.byteOffset, lengthOrValue);
2290
+ const indexOf = u16View.indexOf(0x0000);
2291
+
2292
+ return scratch.toString('utf16le', 0, (indexOf !== -1 ? indexOf : lengthOrValue) * 2);
2293
+ }
2294
+
2295
+ const scratch = Buffer.allocUnsafe(lengthOrValue.length * 2);
2296
+
2297
+ scratch.write(lengthOrValue, 0, 'utf16le');
2298
+
2299
+ void this.write(address, scratch, force);
2300
+
2301
+ return this;
2302
+ }
2303
+
2304
+ // Public utility methods…
2305
+
2306
+ /**
2307
+ * Follows a pointer chain with offsets.
2308
+ * @param address Base address.
2309
+ * @param offsets Array of pointer offsets.
2310
+ * @returns Final address after following the chain, or -1n if any pointer is null.
2311
+ * @example
2312
+ * ```ts
2313
+ * const cs2 = new Memory('cs2.exe');
2314
+ * const myAddress = cs2.follow(0x10000000n, [0x10n, 0x20n]);
2315
+ * ```
2316
+ */
2317
+ public follow(address: bigint, offsets: readonly bigint[]): bigint {
2318
+ const last = offsets.length - 1;
2319
+
2320
+ for (let i = 0; i < last; i++) {
2321
+ address = this.u64(address + offsets[i]);
2322
+
2323
+ if (address === 0n) {
2324
+ return -1n;
2325
+ }
2326
+ }
2327
+
2328
+ return address + (offsets[last] ?? 0n);
2329
+ }
2330
+
2331
+ /**
2332
+ * Finds the address of a buffer within a memory region.
2333
+ * @param needle Buffer or typed array to search for.
2334
+ * @param address Start address.
2335
+ * @param length Number of bytes to search.
2336
+ * @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
2337
+ * @returns Address of the buffer if found, or -1n. If all is true, returns an array of addresses.
2338
+ * @example
2339
+ * ```ts
2340
+ * const cs2 = new Memory('cs2.exe');
2341
+ * const needle = Buffer.from('Hello world!');
2342
+ * // const needle = Buffer.from([0x01, 0x02, 0x03]);
2343
+ * // const needle = new Uint8Array([0x01, 0x02, 0x03]);
2344
+ * // const needle = new Float32Array([0x01, 0x02, 0x03]);
2345
+ * // Find first match
2346
+ * const address = cs2.indexOf(needle, 0x10000000n, 100);
2347
+ * // Find all matches
2348
+ * const allAddressess = cs2.indexOf(needle, 0x10000000n, 100, true);
2349
+ * ```
2350
+ */
2351
+ public indexOf(needle: Scratch, address: bigint, length: number): bigint;
2352
+ public indexOf(needle: Scratch, address: bigint, length: number, all: false): bigint;
2353
+ public indexOf(needle: Scratch, address: bigint, length: number, all: true): bigint[];
2354
+ public indexOf(needle: Scratch, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
2355
+ const haystack = Buffer.allocUnsafe(length);
2356
+
2357
+ const needleBuffer = ArrayBuffer.isView(needle) //
2358
+ ? Buffer.from(needle.buffer, needle.byteOffset, needle.byteLength)
2359
+ : Buffer.from(needle);
2360
+
2361
+ void this.read(address, haystack);
2362
+
2363
+ if (!all) {
2364
+ const indexOf = haystack.indexOf(needleBuffer);
2365
+
2366
+ return indexOf !== -1 ? BigInt(indexOf) + address : -1n;
2367
+ }
2368
+
2369
+ const results: bigint[] = [];
2370
+
2371
+ let start = haystack.indexOf(needleBuffer);
2372
+
2373
+ if (start === -1) {
2374
+ return results;
2375
+ }
2376
+
2377
+ do {
2378
+ results.push(address + BigInt(start));
2379
+ } while ((start = haystack.indexOf(needleBuffer, start + 0x01)) !== -1);
2380
+
2381
+ return results;
2382
+ }
2383
+
2384
+ /**
2385
+ * Finds the address of a byte pattern in memory. `**` and `??` match any byte.
2386
+ * @param needle Hex string pattern to search for (e.g., 'deadbeed', 'dead**ef', 'dead??ef').
2387
+ * @param address Start address to search.
2388
+ * @param length Number of bytes to search.
2389
+ * @param all If true, returns all matches as an array. If false or omitted, returns the first match or -1n.
2390
+ * @returns Address of the pattern if found, or -1n. If all is true, returns an array of addresses.
2391
+ * @example
2392
+ * ```ts
2393
+ * const cs2 = new Memory('cs2.exe');
2394
+ * // Find first match
2395
+ * const address = cs2.pattern('dead**ef', 0x10000000n, 0x1000);
2396
+ * // Find all matches
2397
+ * const allAddresses = cs2.pattern('dead**ef', 0x10000000n, 0x1000, true);
2398
+ * ```
2399
+ */
2400
+ public pattern(needle: string, address: bigint, length: number): bigint;
2401
+ public pattern(needle: string, address: bigint, length: number, all: false): bigint;
2402
+ public pattern(needle: string, address: bigint, length: number, all: true): bigint[];
2403
+ public pattern(needle: string, address: bigint, length: number, all: boolean = false): bigint | bigint[] {
2404
+ const test = Memory.Patterns.PatternTest.test(needle);
2405
+
2406
+ if (!test) {
2407
+ return !all ? -1n : [];
2408
+ }
2409
+
2410
+ // The RegExp test ensures that we have at least one token…
2411
+
2412
+ const tokens = [...needle.matchAll(Memory.Patterns.PatternMatchAll)]
2413
+ .map((match) => ({ buffer: Buffer.from(match[0], 'hex'), index: match.index >>> 1, length: match[0].length >>> 1 })) //
2414
+ .sort(({ length: a }, { length: b }) => b - a);
2415
+
2416
+ const anchor = tokens.shift()!;
2417
+
2418
+ const haystack = this.buffer(address, length);
2419
+
2420
+ const end = length - (needle.length >>> 1);
2421
+ let start = haystack.indexOf(anchor.buffer); // prettier-ignore
2422
+
2423
+ if (start === -1) {
2424
+ return !all ? -1n : [];
2425
+ }
2426
+
2427
+ if (!all) {
2428
+ outer: do {
2429
+ const base = start - anchor.index;
2430
+
2431
+ if (base < 0) {
2432
+ continue;
2433
+ }
2434
+
2435
+ if (base > end) {
2436
+ return -1n;
2437
+ }
2438
+
2439
+ for (const { buffer, index, length } of tokens) {
2440
+ const sourceEnd = base + index + length,
2441
+ sourceStart = base + index,
2442
+ target = buffer,
2443
+ targetEnd = length,
2444
+ targetStart = 0; // prettier-ignore
2445
+
2446
+ const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
2447
+
2448
+ if (compare !== 0) {
2449
+ continue outer;
2450
+ }
2451
+ }
2452
+
2453
+ return address + BigInt(base);
2454
+ } while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
2455
+
2456
+ return -1n;
2457
+ }
2458
+
2459
+ const results: bigint[] = [];
2460
+
2461
+ outer: do {
2462
+ const base = start - anchor.index;
2463
+
2464
+ if (base < 0) {
2465
+ continue;
2466
+ }
2467
+
2468
+ if (base > end) {
2469
+ return results;
2470
+ }
2471
+
2472
+ for (const { buffer, index, length } of tokens) {
2473
+ const sourceEnd = base + index + length,
2474
+ sourceStart = base + index,
2475
+ target = buffer,
2476
+ targetEnd = length,
2477
+ targetStart = 0; // prettier-ignore
2478
+
2479
+ const compare = haystack.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
2480
+
2481
+ if (compare !== 0) {
2482
+ continue outer;
2483
+ }
2484
+ }
2485
+
2486
+ results.push(address + BigInt(base));
2487
+ } while ((start = haystack.indexOf(anchor.buffer, start + 0x01)) !== -1);
2488
+
2489
+ return results;
2490
+ }
2491
+ }
2492
+
2493
+ export default Memory;