bun-memory 1.1.13 → 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
 
@@ -32,40 +33,59 @@ See [example/trigger-bot.ts](example/trigger-bot.ts) for a real-world example fo
32
33
  import Memory from 'bun-memory';
33
34
 
34
35
  // Attach to process by name…
35
- const memory = new Memory('notepad.exe');
36
+ const memory = new Memory('cs2.exe');
36
37
  // …or PID…
37
38
  const memory = new Memory(1_234);
38
39
 
39
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`);
42
+
43
+ const client = modules['client.dll'];
44
+
45
+ console.log(`Base address: 0x${client.base.toString(16)}`);
46
+ console.log(`Size: ${client.size} bytes`);
44
47
 
45
48
  // Read a 32-bit integer…
46
- const value = memory.i32(0x12345678n);
49
+ const value = memory.i32(client.base + 0x12345678n);
47
50
 
48
51
  // Write a float…
49
- memory.f32(0x12345678n, 3.14159);
52
+ memory.f32(client.base + 0x12345678n, 3.14159);
50
53
 
51
54
  // Clean up…
52
55
  memory.close();
53
56
  ```
54
57
 
55
- ### Pattern Scanning
56
-
57
- Pattern scanning is temporarily disabled but will return shortly.
58
-
59
- ```ts
60
- const offset = memory.findPattern('aa??bbccdd??ff', mainModule.modBaseAddr, mainModule.modBaseSize);
61
- const value = memory.bool(offset + 0x1234n);
62
- memory.close();
63
- ```
64
-
65
- ### Efficient Buffer Reads
66
-
67
- There are many ways to use `scratch`es. Scratches are great for avoiding allocation costs by reusing a
68
- preexisting array, buffer, string, etc.
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.
69
89
 
70
90
  ```ts
71
91
  const handles = new Uint32Array(0x100);
@@ -110,6 +130,21 @@ const scratch = Buffer.allocUnsafe(0x100);
110
130
  memory.read(myAddress, scratch);
111
131
  ```
112
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
+
113
148
  ## Notes
114
149
 
115
150
  - Only works with Bun and Windows.
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.13",
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,7 +167,16 @@ 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
+ */
171
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
+ */
172
180
  private static readonly TextDecoderUTF8 = new TextDecoder('utf-8');
173
181
 
174
182
  /**
@@ -933,6 +941,26 @@ class Memory {
933
941
  return this;
934
942
  }
935
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
+
936
964
  /**
937
965
  * Reads a 4×4 matrix from memory or writes a 4×4 matrix to memory.
938
966
  *
@@ -1029,6 +1057,103 @@ class Memory {
1029
1057
  return this;
1030
1058
  }
1031
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
+
1032
1157
  /**
1033
1158
  * Reads a quaternion (4D rotation) from memory or writes a quaternion to memory.
1034
1159
  * Quaternions are stored as four 32-bit floats: x, y, z, w.
@@ -1604,6 +1729,67 @@ class Memory {
1604
1729
 
1605
1730
  return this;
1606
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
+ }
1607
1793
  }
1608
1794
 
1609
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
+ };