bun-memory 1.1.12 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # bun-memory
2
2
 
3
- High-performance Windows process memory utilities for [Bun](https://bun.sh) using `bun:ffi` and Win32 APIs.
3
+ High-performance Windows process memory utilities for [Bun](https://bun.sh) using `bun:ffi` and
4
+ Win32 APIs.
4
5
 
5
6
  ## Features
6
7
 
@@ -24,7 +25,7 @@ bun add bun-memory
24
25
 
25
26
  ## Usage
26
27
 
27
- See [example/trigger-bot.ts](example/trigger-bot.ts) for a real-world example.
28
+ See [example/trigger-bot.ts](example/trigger-bot.ts) for a real-world example for Counter-Strike 2.
28
29
 
29
30
  ### Basic Example
30
31
 
@@ -32,42 +33,95 @@ See [example/trigger-bot.ts](example/trigger-bot.ts) for a real-world example.
32
33
  import Memory from 'bun-memory';
33
34
 
34
35
  // Attach to process by name…
35
- const memory = new Memory('notepad.exe');
36
- // …or PID
37
- const memory = new Memory(1234);
36
+ const memory = new Memory('cs2.exe');
37
+ // …or PID
38
+ const memory = new Memory(1_234);
38
39
 
39
- // Access loaded modules
40
+ // Access loaded modules
40
41
  const modules = memory.modules;
41
- const mainModule = modules['notepad.exe'];
42
- console.log(`Base address: 0x${mainModule.modBaseAddr.toString(16)}`);
43
- console.log(`Size: ${mainModule.modBaseSize} bytes`);
44
42
 
45
- // Read a 32-bit integer
46
- const value = memory.i32(0x12345678n);
43
+ const client = modules['client.dll'];
47
44
 
48
- // Write a float
49
- memory.f32(0x12345678n, 3.14159);
45
+ console.log(`Base address: 0x${client.base.toString(16)}`);
46
+ console.log(`Size: ${client.size} bytes`);
50
47
 
51
- // Clean up
48
+ // Read a 32-bit integer…
49
+ const value = memory.i32(client.base + 0x12345678n);
50
+
51
+ // Write a float…
52
+ memory.f32(client.base + 0x12345678n, 3.14159);
53
+
54
+ // Clean up…
52
55
  memory.close();
53
56
  ```
54
57
 
55
- ### Pattern Scanning
58
+ ### API — Typed Reads / Writes
59
+
60
+ A `Memory` instance exposes typed helpers for reading and writing process memory. Pairs indicate
61
+ scalar and array variants; entries without a pair are scalar-only or array-only.
62
+
63
+ - bool
64
+ - cString
65
+ - f32 / f32Array
66
+ - f64 / f64Array
67
+ - i16 / i16Array
68
+ - i32 / i32Array
69
+ - i64 / i64Array
70
+ - i8 / i8Array
71
+ - matrix3x3
72
+ - matrix3x4
73
+ - matrix4x4
74
+ - networkUtlVector
75
+ - qAngle / qAngleArray
76
+ - quaternion / quaternionArray
77
+ - u16 / u16Array
78
+ - u32 / u32Array
79
+ - u64 / u64Array
80
+ - u8 / u8Array
81
+ - vector2 / vector2Array
82
+ - vector3 / vector3Array
83
+ - vector4 / vector4Array
84
+
85
+ ### Efficient Reads / Writes Using Scratches
86
+
87
+ There are many ways to use `scratch`es. Scratches are great for avoiding allocation costs by reusing
88
+ a preexisting array, buffer, string, etc.
56
89
 
57
90
  ```ts
58
- const offset = memory.findPattern('aa??bbccdd??ff', mainModule.modBaseAddr, mainModule.modBaseSize);
59
- const value = memory.bool(offset + 0x1234n);
60
- memory.close();
61
- ```
91
+ const handles = new Uint32Array(0x100);
62
92
 
63
- ### Efficient Buffer Reads
93
+ while (true) {
94
+ try {
95
+ memory.read(myAddress, handles); // Updated handles, no allocations…
96
+
97
+ // Do something with your handles…
98
+ for (const handle of handles) {
99
+ // …
100
+ }
101
+ } finally {
102
+ continue;
103
+ }
104
+ }
105
+ ```
64
106
 
65
107
  ```ts
66
- const scratch = Buffer.allocUnsafe(0xf000);
67
- const view = new BigUint64Array(scratch.buffer, scratch.byteOffset, 0xf000 / 8);
108
+ const buffer = Buffer.allocUnsafe(0xf000); // Use buffer as a scratch…
109
+ const pointers = new BigUint64Array(scratchBuffer.buffer, scratchBuffer.byteOffset, 0xf000 / 8);
68
110
 
69
111
  while (true) {
70
- memory.read(myAddress, scratch); // Updates scratch and view, no allocations
112
+ try {
113
+ memory.read(myAddress, buffer); // Updates buffer and pointers, no allocations…
114
+
115
+ // Do something with your pointers…
116
+ for (const pointer of pointers) {
117
+ // Read a 32 length string at pointer…
118
+ const myString = memory.cString(pointer, 32).toString();
119
+
120
+ // …
121
+ }
122
+ } finally {
123
+ continue;
124
+ }
71
125
  }
72
126
  ```
73
127
 
@@ -76,6 +130,21 @@ const scratch = Buffer.allocUnsafe(0x100);
76
130
  memory.read(myAddress, scratch);
77
131
  ```
78
132
 
133
+ ```ts
134
+ const scratch = new Uint32Array(0x10);
135
+ memory.read(myAddress, scratch);
136
+ ```
137
+
138
+ ### Pattern Scanning
139
+
140
+ Pattern scanning is temporarily disabled but will return shortly.
141
+
142
+ ```ts
143
+ const offset = memory.findPattern('aa??bbccdd??ff', mainModule.modBaseAddr, mainModule.modBaseSize);
144
+ const value = memory.bool(offset + 0x1234n);
145
+ memory.close();
146
+ ```
147
+
79
148
  ## Notes
80
149
 
81
150
  - Only works with Bun and Windows.
@@ -11,15 +11,6 @@ import OffsetsJSON from './offsets/offsets.json';
11
11
 
12
12
  const { random } = Math;
13
13
 
14
- // !
15
- const buffer = Buffer.from('Hello world!');
16
- const bufferPtr = ptr(buffer);
17
-
18
- const cString = new CString(bufferPtr);
19
-
20
- console.log(cString.byteLength, cString.byteOffset); // undefined undefined
21
- // !
22
-
23
14
  const Delay = 2.5;
24
15
 
25
16
  // Load user32.dll…
@@ -54,14 +45,24 @@ if (ClientPtr === undefined) {
54
45
  }
55
46
  // !
56
47
 
57
- console.time('Read test');
48
+ const dec = new TextDecoder('utf-8');
58
49
 
59
- for (let i = 0; i < 10_000_000; i++) {
60
- // new Uint8Array((random() * 256 + 1) >> 0);
61
- Buffer.allocUnsafe((random() * 256 + 1) >> 0);
50
+ const buffer = Buffer.allocUnsafe(32);
51
+ const pointers = [4749947025128n, 4749937696488n, 4749757372648n, 4745786211048n, 4745800086248n, 4748263960808n, 4748274490088n, 4748287459048n, 4747096900328n, 4747107757288n];
52
+
53
+ console.time('test');
54
+
55
+ while (true) {
56
+ for (const pointer of pointers) {
57
+ const cString = cs2.cString(pointer, 32).toString();
58
+
59
+ if (cString.length > 32) {
60
+ console.log(cString);
61
+ }
62
+ }
62
63
  }
63
64
 
64
- console.timeEnd('Read test');
65
+ console.timeEnd('test');
65
66
 
66
67
  // !
67
68
 
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "url": "git://github.com/obscuritysrl/bun-memory.git"
23
23
  },
24
24
  "type": "module",
25
- "version": "1.1.12",
25
+ "version": "1.1.14",
26
26
  "main": "./index.ts",
27
27
  "keywords": [
28
28
  "bun",
package/structs/Memory.ts CHANGED
@@ -1,10 +1,6 @@
1
- // TODO: Reintroduce findPattern(…)…
2
- // TODO: Reintroduce indexOf(…)…
3
- // TODO: String methods…
4
-
5
1
  import { CString, FFIType, dlopen, read } from 'bun:ffi';
6
2
 
7
- import type { Module, NetworkUtlVector, Quaternion, Region, Scratch, Vector2, Vector3 } from '../types/Memory';
3
+ import type { Module, NetworkUtlVector, QAngle, Quaternion, Region, Scratch, Vector2, Vector3, Vector4 } from '../types/Memory';
8
4
  import Win32Error from './Win32Error';
9
5
 
10
6
  const { f32, f64, i16, i32, i64, i8, u16, u32, u64, u8 } = read;
@@ -33,6 +29,9 @@ const { symbols: Kernel32 } = dlopen('kernel32.dll', {
33
29
  * This class allows reading from and writing to memory addresses in external processes,
34
30
  * supporting various data types including primitives, arrays, and custom structures like vectors and quaternions.
35
31
  *
32
+ * @todo Reimplement `findPattern(…)`.
33
+ * @todo Reimplement `indexOf(…)`.
34
+ *
36
35
  * @example
37
36
  * ```typescript
38
37
  * // Connect to a process by name
@@ -168,6 +167,18 @@ class Memory {
168
167
  private readonly ScratchMemoryBasicInformation = Buffer.allocUnsafe(0x30 /* sizeof(MEMORY_BASIC_INFORMATION) */);
169
168
  private readonly ScratchModuleEntry32W = Buffer.allocUnsafe(0x438 /* sizeof(MODULEENTRY32W) */);
170
169
 
170
+ /**
171
+ * Reusable UTF-16 decoder for interpreting raw process memory as UTF-16 text.
172
+ * Kept as a singleton to avoid repeated allocations in hot paths.
173
+ */
174
+ private static readonly TextDecoderUTF16 = new TextDecoder('utf-16');
175
+
176
+ /**
177
+ * Reusable UTF-8 decoder for interpreting raw process memory as UTF-8 text.
178
+ * Kept as a singleton to avoid repeated allocations in hot paths.
179
+ */
180
+ private static readonly TextDecoderUTF8 = new TextDecoder('utf-8');
181
+
171
182
  /**
172
183
  * Handle to the target process.
173
184
  */
@@ -185,7 +196,6 @@ class Memory {
185
196
  *
186
197
  * @example
187
198
  * ```typescript
188
- * const memory = new Memory('notepad.exe');
189
199
  * const modules = memory.modules;
190
200
  *
191
201
  * // Access a specific module
@@ -211,7 +221,6 @@ class Memory {
211
221
  *
212
222
  * @example
213
223
  * ```typescript
214
- * const memory = new Memory('notepad.exe');
215
224
  * const regions = memory.regions(0x10000000n, 0x1000n);
216
225
  *
217
226
  * regions.forEach(region => {
@@ -265,7 +274,6 @@ class Memory {
265
274
  *
266
275
  * @example
267
276
  * ```typescript
268
- * const memory = new Memory('notepad.exe');
269
277
  * const buffer = new Uint8Array(4);
270
278
  * memory.read(0x12345678n, buffer);
271
279
  * ```
@@ -295,7 +303,6 @@ class Memory {
295
303
  *
296
304
  * @example
297
305
  * ```typescript
298
- * const memory = new Memory('notepad.exe');
299
306
  * const buffer = new Uint8Array([0x41, 0x42, 0x43, 0x44]);
300
307
  * memory.write(0x12345678n, buffer);
301
308
  * ```
@@ -342,8 +349,6 @@ class Memory {
342
349
  *
343
350
  * @example
344
351
  * ```typescript
345
- * const memory = new Memory('notepad.exe');
346
- *
347
352
  * // Initial modules
348
353
  * console.log('Initial modules:', Object.keys(memory.modules));
349
354
  *
@@ -401,8 +406,6 @@ class Memory {
401
406
  *
402
407
  * @example
403
408
  * ```typescript
404
- * const memory = new Memory('game.exe');
405
- *
406
409
  * // Read a boolean value
407
410
  * const isAlive = memory.bool(0x12345678n);
408
411
  * console.log('Player is alive:', isAlive);
@@ -444,14 +447,14 @@ class Memory {
444
447
  *
445
448
  * @example
446
449
  * ```typescript
447
- * const memory = new Memory('game.exe');
448
- *
449
450
  * // Read up to 64 bytes and interpret as a C string
450
451
  * const playerName = memory.cString(0x12345678n, 64);
451
452
  * console.log('Player name:', playerName.toString());
452
453
  *
453
454
  * // Write a C string (NUL-terminated)
454
- * const value = new CString('PlayerOne');
455
+ * const valueBuffer = Buffer.from('PlayerOne\0');
456
+ * const valuePtr = ptr(valueBuffer);
457
+ * const value = new CString(valuePtr);
455
458
  * memory.cString(0x12345678n, value);
456
459
  * ```
457
460
  */
@@ -482,8 +485,6 @@ class Memory {
482
485
  *
483
486
  * @example
484
487
  * ```typescript
485
- * const memory = new Memory('game.exe');
486
- *
487
488
  * // Read a float value
488
489
  * const playerHealth = memory.f32(0x12345678n);
489
490
  * console.log('Player health:', playerHealth);
@@ -517,8 +518,6 @@ class Memory {
517
518
  *
518
519
  * @example
519
520
  * ```typescript
520
- * const memory = new Memory('game.exe');
521
- *
522
521
  * // Read an array of 10 float values
523
522
  * const coordinates = memory.f32Array(0x12345678n, 10);
524
523
  * console.log('Coordinates:', coordinates);
@@ -556,8 +555,6 @@ class Memory {
556
555
  *
557
556
  * @example
558
557
  * ```typescript
559
- * const memory = new Memory('scientific_app.exe');
560
- *
561
558
  * // Read a double precision value
562
559
  * const preciseValue = memory.f64(0x12345678n);
563
560
  * console.log('Precise value:', preciseValue);
@@ -591,8 +588,6 @@ class Memory {
591
588
  *
592
589
  * @example
593
590
  * ```typescript
594
- * const memory = new Memory('scientific_app.exe');
595
- *
596
591
  * // Read an array of 5 double precision values
597
592
  * const preciseData = memory.f64Array(0x12345678n, 5);
598
593
  * console.log('Precise data:', preciseData);
@@ -630,8 +625,6 @@ class Memory {
630
625
  *
631
626
  * @example
632
627
  * ```typescript
633
- * const memory = new Memory('game.exe');
634
- *
635
628
  * // Read a signed 16-bit integer
636
629
  * const temperature = memory.i16(0x12345678n);
637
630
  * console.log('Temperature:', temperature);
@@ -665,8 +658,6 @@ class Memory {
665
658
  *
666
659
  * @example
667
660
  * ```typescript
668
- * const memory = new Memory('audio_app.exe');
669
- *
670
661
  * // Read an array of audio samples
671
662
  * const samples = memory.i16Array(0x12345678n, 1024);
672
663
  * console.log('Audio samples:', samples);
@@ -704,8 +695,6 @@ class Memory {
704
695
  *
705
696
  * @example
706
697
  * ```typescript
707
- * const memory = new Memory('game.exe');
708
- *
709
698
  * // Read a player's score
710
699
  * const score = memory.i32(0x12345678n);
711
700
  * console.log('Player score:', score);
@@ -739,8 +728,6 @@ class Memory {
739
728
  *
740
729
  * @example
741
730
  * ```typescript
742
- * const memory = new Memory('game.exe');
743
- *
744
731
  * // Read inventory item counts
745
732
  * const inventory = memory.i32Array(0x12345678n, 20);
746
733
  * console.log('Inventory:', inventory);
@@ -778,8 +765,6 @@ class Memory {
778
765
  *
779
766
  * @example
780
767
  * ```typescript
781
- * const memory = new Memory('database.exe');
782
- *
783
768
  * // Read a large number (timestamp, file size, etc.)
784
769
  * const timestamp = memory.i64(0x12345678n);
785
770
  * console.log('Timestamp:', timestamp);
@@ -813,8 +798,6 @@ class Memory {
813
798
  *
814
799
  * @example
815
800
  * ```typescript
816
- * const memory = new Memory('database.exe');
817
- *
818
801
  * // Read an array of large numbers
819
802
  * const largeNumbers = memory.i64Array(0x12345678n, 10);
820
803
  * console.log('Large numbers:', largeNumbers);
@@ -852,8 +835,6 @@ class Memory {
852
835
  *
853
836
  * @example
854
837
  * ```typescript
855
- * const memory = new Memory('game.exe');
856
- *
857
838
  * // Read a small signed value (e.g., direction, state)
858
839
  * const direction = memory.i8(0x12345678n);
859
840
  * console.log('Direction:', direction);
@@ -887,8 +868,6 @@ class Memory {
887
868
  *
888
869
  * @example
889
870
  * ```typescript
890
- * const memory = new Memory('game.exe');
891
- *
892
871
  * // Read an array of small signed values
893
872
  * const directions = memory.i8Array(0x12345678n, 8);
894
873
  * console.log('Movement directions:', directions);
@@ -917,6 +896,117 @@ class Memory {
917
896
  return this;
918
897
  }
919
898
 
899
+ /**
900
+ * Reads a 3×3 matrix from memory or writes a 3×3 matrix to memory.
901
+ *
902
+ * The matrix is represented as 9 contiguous 32-bit floating-point values (`Float32Array`).
903
+ * No transposition or stride is applied—values are copied exactly as stored in memory.
904
+ *
905
+ * @param address - Memory address to read from or write to
906
+ * @param values - Optional `Float32Array` of length 9 to write. If omitted, performs a read operation
907
+ * @returns `Float32Array` of length 9 when reading, or this `Memory` instance when writing
908
+ * @throws {RangeError} When `values.length` is not exactly 9
909
+ *
910
+ * @example
911
+ * ```typescript
912
+ * // Read a 3×3 matrix
913
+ * const matrix = memory.matrix3x3(0x12345678n); // Float32Array(9)
914
+ *
915
+ * // Write a 3×3 matrix (length must be 9)
916
+ * const next = new Float32Array([
917
+ * 1, 0, 0,
918
+ * 0, 1, 0,
919
+ * 0, 0, 1
920
+ * ]);
921
+ * memory.matrix3x3(0x12345678n, next);
922
+ * ```
923
+ */
924
+ public matrix3x3(address: bigint): Float32Array;
925
+ public matrix3x3(address: bigint, values: Float32Array): this;
926
+ public matrix3x3(address: bigint, values?: Float32Array): Float32Array | this {
927
+ if (values === undefined) {
928
+ const scratch = new Float32Array(0x09);
929
+
930
+ this.read(address, scratch);
931
+
932
+ return scratch;
933
+ }
934
+
935
+ if (values.length !== 0x09) {
936
+ throw new RangeError('values.length must be 9.');
937
+ }
938
+
939
+ this.write(address, values);
940
+
941
+ return this;
942
+ }
943
+
944
+ public matrix3x4(address: bigint): Float32Array;
945
+ public matrix3x4(address: bigint, values: Float32Array): this;
946
+ public matrix3x4(address: bigint, values?: Float32Array): Float32Array | this {
947
+ if (values === undefined) {
948
+ const scratch = new Float32Array(0x0c);
949
+
950
+ this.read(address, scratch);
951
+
952
+ return scratch;
953
+ }
954
+
955
+ if (values.length !== 0x0c) {
956
+ throw new RangeError('values.length must be 12.');
957
+ }
958
+
959
+ this.write(address, values);
960
+
961
+ return this;
962
+ }
963
+
964
+ /**
965
+ * Reads a 4×4 matrix from memory or writes a 4×4 matrix to memory.
966
+ *
967
+ * The matrix is represented as 16 contiguous 32-bit floating-point values (`Float32Array`).
968
+ * No transposition or stride is applied—values are copied exactly as stored in memory.
969
+ *
970
+ * @param address - Memory address to read from or write to
971
+ * @param values - Optional `Float32Array` of length 16 to write. If omitted, performs a read operation
972
+ * @returns `Float32Array` of length 16 when reading, or this `Memory` instance when writing
973
+ * @throws {RangeError} When `values.length` is not exactly 16
974
+ *
975
+ * @example
976
+ * ```typescript
977
+ * // Read a 4×4 matrix
978
+ * const matrix = memory.matrix4x4(0x12345678n); // Float32Array(16)
979
+ *
980
+ * // Write a 4×4 matrix (length must be 16)
981
+ * const next = new Float32Array([
982
+ * 1, 0, 0, 0,
983
+ * 0, 1, 0, 0,
984
+ * 0, 0, 1, 0,
985
+ * 0, 0, 0, 1
986
+ * ]);
987
+ * memory.matrix4x4(0x12345678n, next);
988
+ * ```
989
+ */
990
+ public matrix4x4(address: bigint): Float32Array;
991
+ public matrix4x4(address: bigint, values: Float32Array): this;
992
+ public matrix4x4(address: bigint, values?: Float32Array): Float32Array | this {
993
+ if (values === undefined) {
994
+ const scratch = new Float32Array(0x10);
995
+
996
+ this.read(address, scratch);
997
+
998
+ return scratch;
999
+ }
1000
+
1001
+ if (values.length !== 0x10) {
1002
+ throw new RangeError('values.length must be 16.');
1003
+ }
1004
+
1005
+ this.write(address, values);
1006
+
1007
+ return this;
1008
+ }
1009
+
920
1010
  /**
921
1011
  * Reads a `NetworkUtlVector` (`Uint32Array`) from memory or writes a `NetworkUtlVector` to memory.
922
1012
  *
@@ -936,8 +1026,6 @@ class Memory {
936
1026
  *
937
1027
  * @example
938
1028
  * ```typescript
939
- * const memory = new Memory('network_app.exe');
940
- *
941
1029
  * // Read the current vector
942
1030
  * const ids = memory.networkUtlVector(0x12345678n);
943
1031
  * console.log('IDs:', Array.from(ids));
@@ -969,6 +1057,103 @@ class Memory {
969
1057
  return this;
970
1058
  }
971
1059
 
1060
+ /**
1061
+ * Reads a set of Euler angles from memory or writes a set of Euler angles to memory.
1062
+ *
1063
+ * Angles are stored as three 32-bit floats in the order **pitch, yaw, roll**.
1064
+ *
1065
+ * @param address - Memory address to read from or write to
1066
+ * @param value - Optional `QAngle` to write. If omitted, performs a read operation
1067
+ * @returns The `QAngle` value when reading, or this `Memory` instance when writing
1068
+ *
1069
+ * @example
1070
+ * ```typescript
1071
+ * // Read current view angles
1072
+ * const view = memory.qAngle(0x12345678n);
1073
+ *
1074
+ * // Write new view angles (e.g., level the roll)
1075
+ * memory.qAngle(0x12345678n, { ...view, roll: 0 });
1076
+ * ```
1077
+ */
1078
+
1079
+ public qAngle(address: bigint): QAngle;
1080
+ public qAngle(address: bigint, value: QAngle): this;
1081
+ public qAngle(address: bigint, value?: QAngle): QAngle | this {
1082
+ if (value === undefined) {
1083
+ this.read(address, this.Scratch12);
1084
+
1085
+ const pitch = f32(this.Scratch12.ptr);
1086
+ const roll = f32(this.Scratch12.ptr, 0x08);
1087
+ const yaw = f32(this.Scratch12.ptr, 0x04);
1088
+
1089
+ return { pitch, roll, yaw };
1090
+ }
1091
+
1092
+ this.Scratch12Buffer.writeFloatLE(value.pitch);
1093
+ this.Scratch12Buffer.writeFloatLE(value.roll, 0x08);
1094
+ this.Scratch12Buffer.writeFloatLE(value.yaw, 0x04);
1095
+
1096
+ this.write(address, this.Scratch12);
1097
+
1098
+ return this;
1099
+ }
1100
+
1101
+ /**
1102
+ * Reads an array of Euler angles from memory or writes an array of Euler angles to memory.
1103
+ *
1104
+ * Each element is three 32-bit floats in the order **pitch, yaw, roll**.
1105
+ *
1106
+ * @param address - Memory address to read from or write to
1107
+ * @param lengthOrValues - Length of array to read, or array of `QAngle` values to write
1108
+ * @returns `QAngle[]` when reading, or this `Memory` instance when writing
1109
+ *
1110
+ * @example
1111
+ * ```typescript
1112
+ * // Read bone aim offsets
1113
+ * const bones = memory.qAngleArray(0x12345678n, 64);
1114
+ *
1115
+ * // Write new bone angles (e.g., reset all roll to zero)
1116
+ * bones.forEach(b => (b.roll = 0));
1117
+ * memory.qAngleArray(0x12345678n, bones);
1118
+ * ```
1119
+ */
1120
+ public qAngleArray(address: bigint, length: number): QAngle[];
1121
+ public qAngleArray(address: bigint, values: QAngle[]): this;
1122
+ public qAngleArray(address: bigint, lengthOrValues: QAngle[] | number): QAngle[] | this {
1123
+ if (typeof lengthOrValues === 'number') {
1124
+ const length = lengthOrValues;
1125
+ const scratch = new Float32Array(length * 0x03);
1126
+
1127
+ this.read(address, scratch);
1128
+
1129
+ const result = new Array<QAngle>(length);
1130
+
1131
+ for (let i = 0, j = 0; i < length; i++, j += 0x03) {
1132
+ const pitch = scratch[j];
1133
+ const yaw = scratch[j + 0x01];
1134
+ const roll = scratch[j + 0x02];
1135
+ result[i] = { pitch, yaw, roll };
1136
+ }
1137
+
1138
+ return result;
1139
+ }
1140
+
1141
+ const values = lengthOrValues;
1142
+ const scratch = new Float32Array(values.length * 0x03);
1143
+
1144
+ for (let i = 0, j = 0; i < values.length; i++, j += 0x03) {
1145
+ const qAngle = values[i];
1146
+
1147
+ scratch[j] = qAngle.pitch;
1148
+ scratch[j + 0x02] = qAngle.roll;
1149
+ scratch[j + 0x01] = qAngle.yaw;
1150
+ }
1151
+
1152
+ this.write(address, scratch);
1153
+
1154
+ return this;
1155
+ }
1156
+
972
1157
  /**
973
1158
  * Reads a quaternion (4D rotation) from memory or writes a quaternion to memory.
974
1159
  * Quaternions are stored as four 32-bit floats: x, y, z, w.
@@ -979,8 +1164,6 @@ class Memory {
979
1164
  *
980
1165
  * @example
981
1166
  * ```typescript
982
- * const memory = new Memory('game.exe');
983
- *
984
1167
  * // Read player rotation
985
1168
  * const rotation = memory.quaternion(0x12345678n);
986
1169
  * console.log('Player rotation:', rotation);
@@ -1022,8 +1205,6 @@ class Memory {
1022
1205
  *
1023
1206
  * @example
1024
1207
  * ```typescript
1025
- * const memory = new Memory('game.exe');
1026
- *
1027
1208
  * // Read bone rotations for skeletal animation
1028
1209
  * const boneRotations = memory.quaternionArray(0x12345678n, 50);
1029
1210
  * console.log('Bone rotations:', boneRotations);
@@ -1082,8 +1263,6 @@ class Memory {
1082
1263
  *
1083
1264
  * @example
1084
1265
  * ```typescript
1085
- * const memory = new Memory('game.exe');
1086
- *
1087
1266
  * // Read a port number or small positive value
1088
1267
  * const port = memory.u16(0x12345678n);
1089
1268
  * console.log('Network port:', port);
@@ -1117,8 +1296,6 @@ class Memory {
1117
1296
  *
1118
1297
  * @example
1119
1298
  * ```typescript
1120
- * const memory = new Memory('network_app.exe');
1121
- *
1122
1299
  * // Read an array of port numbers
1123
1300
  * const ports = memory.u16Array(0x12345678n, 10);
1124
1301
  * console.log('Active ports:', ports);
@@ -1156,8 +1333,6 @@ class Memory {
1156
1333
  *
1157
1334
  * @example
1158
1335
  * ```typescript
1159
- * const memory = new Memory('game.exe');
1160
- *
1161
1336
  * // Read player's money (always positive)
1162
1337
  * const money = memory.u32(0x12345678n);
1163
1338
  * console.log('Player money:', money);
@@ -1191,8 +1366,6 @@ class Memory {
1191
1366
  *
1192
1367
  * @example
1193
1368
  * ```typescript
1194
- * const memory = new Memory('game.exe');
1195
- *
1196
1369
  * // Read resource amounts
1197
1370
  * const resources = memory.u32Array(0x12345678n, 6);
1198
1371
  * console.log('Resources:', resources);
@@ -1230,8 +1403,6 @@ class Memory {
1230
1403
  *
1231
1404
  * @example
1232
1405
  * ```typescript
1233
- * const memory = new Memory('database.exe');
1234
- *
1235
1406
  * // Read a very large positive number
1236
1407
  * const recordId = memory.u64(0x12345678n);
1237
1408
  * console.log('Record ID:', recordId);
@@ -1265,8 +1436,6 @@ class Memory {
1265
1436
  *
1266
1437
  * @example
1267
1438
  * ```typescript
1268
- * const memory = new Memory('database.exe');
1269
- *
1270
1439
  * // Read an array of record IDs
1271
1440
  * const recordIds = memory.u64Array(0x12345678n, 100);
1272
1441
  * console.log('Record IDs:', recordIds);
@@ -1304,8 +1473,6 @@ class Memory {
1304
1473
  *
1305
1474
  * @example
1306
1475
  * ```typescript
1307
- * const memory = new Memory('game.exe');
1308
- *
1309
1476
  * // Read a byte value (0-255)
1310
1477
  * const opacity = memory.u8(0x12345678n);
1311
1478
  * console.log('UI opacity:', opacity);
@@ -1339,8 +1506,6 @@ class Memory {
1339
1506
  *
1340
1507
  * @example
1341
1508
  * ```typescript
1342
- * const memory = new Memory('image_editor.exe');
1343
- *
1344
1509
  * // Read pixel data
1345
1510
  * const pixels = memory.u8Array(0x12345678n, 1024);
1346
1511
  * console.log('Pixel data:', pixels);
@@ -1379,8 +1544,6 @@ class Memory {
1379
1544
  *
1380
1545
  * @example
1381
1546
  * ```typescript
1382
- * const memory = new Memory('game.exe');
1383
- *
1384
1547
  * // Read player position
1385
1548
  * const position = memory.vector2(0x12345678n);
1386
1549
  * console.log('Player position:', position);
@@ -1418,8 +1581,6 @@ class Memory {
1418
1581
  *
1419
1582
  * @example
1420
1583
  * ```typescript
1421
- * const memory = new Memory('game.exe');
1422
- *
1423
1584
  * // Read waypoints for AI pathfinding
1424
1585
  * const waypoints = memory.vector2Array(0x12345678n, 20);
1425
1586
  * console.log('AI waypoints:', waypoints);
@@ -1479,8 +1640,6 @@ class Memory {
1479
1640
  *
1480
1641
  * @example
1481
1642
  * ```typescript
1482
- * const memory = new Memory('game.exe');
1483
- *
1484
1643
  * // Read player 3D position
1485
1644
  * const position = memory.vector3(0x12345678n);
1486
1645
  * console.log('Player 3D position:', position);
@@ -1520,8 +1679,6 @@ class Memory {
1520
1679
  *
1521
1680
  * @example
1522
1681
  * ```typescript
1523
- * const memory = new Memory('game.exe');
1524
- *
1525
1682
  * // Read vertex positions for 3D model
1526
1683
  * const vertices = memory.vector3Array(0x12345678n, 500);
1527
1684
  * console.log('3D vertices:', vertices);
@@ -1572,6 +1729,67 @@ class Memory {
1572
1729
 
1573
1730
  return this;
1574
1731
  }
1732
+
1733
+ /**
1734
+ * Reads a 4D vector from memory or writes a 4D vector to memory.
1735
+ *
1736
+ * Uses the same 16-byte layout as {@link Memory.quaternion}: four 32-bit floats
1737
+ * stored in the order **x, y, z, w**. Returned objects are shaped as `{ w, x, y, z }`.
1738
+ *
1739
+ * @param address - Memory address to read from or write to
1740
+ * @param value - Optional `Vector4` to write. If omitted, performs a read operation
1741
+ * @returns The `Vector4` value when reading, or this `Memory` instance when writing
1742
+ *
1743
+ * @example
1744
+ * ```typescript
1745
+ * // Read directional data in projective space
1746
+ * const value = memory.vector4(0x12345678n);
1747
+ *
1748
+ * // Write a vector4 value (e.g., identity quaternion)
1749
+ * memory.vector4(0x12345678n, { x: 0, y: 0, z: 0, w: 1 });
1750
+ * ```
1751
+ */
1752
+ public vector4(address: bigint): Vector4;
1753
+ public vector4(address: bigint, value: Vector4): this;
1754
+ public vector4(address: bigint, value?: Vector4): Vector4 | this {
1755
+ // TypeScript is funny sometimes, isn't it?… 🫠…
1756
+ if (value === undefined) {
1757
+ return this.quaternion(address);
1758
+ }
1759
+
1760
+ return this.quaternion(address, value);
1761
+ }
1762
+
1763
+ /**
1764
+ * Reads an array of 4D vectors from memory or writes an array of 4D vectors to memory.
1765
+ *
1766
+ * Each element uses the same 16-byte layout as {@link Memory.quaternionArray}:
1767
+ * four 32-bit floats stored in the order **x, y, z, w**.
1768
+ *
1769
+ * @param address - Memory address to read from or write to
1770
+ * @param lengthOrValues - Length of array to read, or array of `Vector4` values to write
1771
+ * @returns `Vector4[]` when reading, or this `Memory` instance when writing
1772
+ *
1773
+ * @example
1774
+ * ```typescript
1775
+ * // Read per-vertex tangent vectors (xyzw)
1776
+ * const tangents = memory.vector4Array(0x12345678n, 1024);
1777
+ *
1778
+ * // Write tangent vectors (eg. normalize w to 1.0)
1779
+ * tangents.forEach(v => (v.w = 1.0));
1780
+ * memory.vector4Array(0x12345678n, tangents);
1781
+ * ```
1782
+ */
1783
+ public vector4Array(address: bigint, length: number): Vector4[];
1784
+ public vector4Array(address: bigint, values: Vector4[]): this;
1785
+ public vector4Array(address: bigint, lengthOrValues: Vector4[] | number): Vector4[] | this {
1786
+ // TypeScript is funny sometimes, isn't it?… 🫠…
1787
+ if (typeof lengthOrValues === 'number') {
1788
+ return this.quaternionArray(address, lengthOrValues);
1789
+ }
1790
+
1791
+ return this.quaternionArray(address, lengthOrValues);
1792
+ }
1575
1793
  }
1576
1794
 
1577
1795
  export default Memory;
package/types/Memory.ts CHANGED
@@ -319,6 +319,68 @@ export type Quaternion = {
319
319
  z: number;
320
320
  };
321
321
 
322
+ /**
323
+ * Represents an orientation using Euler angles.
324
+ *
325
+ * `QAngle` stores three 32-bit floating-point angles that describe rotation
326
+ * in degrees around the principal axes: `pitch` (X), `yaw` (Y), and `roll` (Z).
327
+ * This format is common in Source-engine-style telemetry and many gameplay
328
+ * camera systems. For quaternions, see {@link Quaternion}.
329
+ *
330
+ * Typical uses include:
331
+ * - Camera/view rotations
332
+ * - Bone/attachment orientations
333
+ * - Aim and recoil calculations
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * // Read and tweak view angles
338
+ * const view: QAngle = memory.qAngle(0x12345678n);
339
+ * const leveled: QAngle = { pitch: 0, yaw: view.yaw, roll: 0 };
340
+ * memory.qAngle(0x12345678n, leveled);
341
+ * ```
342
+ */
343
+ export interface QAngle {
344
+ /**
345
+ * Rotation around the X axis, in degrees.
346
+ *
347
+ * Positive values pitch the nose downward in many right-handed game
348
+ * coordinate systems; verify the target engine’s convention before writing.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * const aimDown: QAngle = { pitch: 10, yaw: 0, roll: 0 };
353
+ * ```
354
+ */
355
+ pitch: number;
356
+
357
+ /**
358
+ * Rotation around the Z axis, in degrees.
359
+ *
360
+ * Often used for banking/tilt effects. Some engines clamp or ignore roll for
361
+ * first-person cameras.
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * const slightBank: QAngle = { pitch: 0, yaw: 0, roll: 5 };
366
+ * ```
367
+ */
368
+ roll: number;
369
+
370
+ /**
371
+ * Rotation around the Y axis, in degrees.
372
+ *
373
+ * Positive values typically turn to the right (clockwise when viewed from
374
+ * above) in right-handed systems.
375
+ *
376
+ * @example
377
+ * ```typescript
378
+ * const turnRight: QAngle = { pitch: 0, yaw: 90, roll: 0 };
379
+ * ```
380
+ */
381
+ yaw: number;
382
+ }
383
+
322
384
  /**
323
385
  * Represents a memory region with its properties and protection flags.
324
386
  *
@@ -791,3 +853,32 @@ export type Vector3 = {
791
853
  */
792
854
  z: number;
793
855
  };
856
+
857
+ /**
858
+ * Represents a 4D vector with w, x, y, z components.
859
+ *
860
+ * Common use cases:
861
+ * - Homogeneous coordinates (w ≠ 0)
862
+ * - Colors with alpha (x = R, y = G, z = B, w = A)
863
+ * - Packed attributes and shader parameters
864
+ * - As a quaternion carrier when interoperating with APIs expecting {x,y,z,w}
865
+ *
866
+ * Memory layout used by {@link Memory.vector4} is four 32-bit floats stored in
867
+ * the order **x, y, z, w**, while this type exposes `{ w, x, y, z }` for
868
+ * consistency with {@link Quaternion}.
869
+ *
870
+ * @example
871
+ * ```typescript
872
+ * // RGBA color
873
+ * const red: Vector4 = { x: 255, y: 0, z: 0, w: 255 };
874
+ *
875
+ * // Homogeneous point (x, y, z, w)
876
+ * const p: Vector4 = { x: 10, y: 20, z: 30, w: 1 };
877
+ * ```
878
+ */
879
+ export type Vector4 = {
880
+ w: number;
881
+ x: number;
882
+ y: number;
883
+ z: number;
884
+ };