bun-memory 1.1.24 → 1.1.26
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/example/benchmark.ts +107 -0
- package/example/trigger-bot.ts +28 -54
- package/package.json +5 -1
- package/runtime/extensions.ts +87 -865
- package/structs/Memory.ts +301 -92
package/structs/Memory.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { CString, FFIType, dlopen } from 'bun:ffi';
|
|
1
|
+
import { CString, FFIType, dlopen, ptr } from 'bun:ffi';
|
|
2
2
|
|
|
3
3
|
import type { Module, NetworkUtlVector, Point, QAngle, Quaternion, Region, RGB, RGBA, Scratch, UPtr, UPtrArray, Vector2, Vector3, Vector4 } from '../types/Memory';
|
|
4
4
|
import Win32Error from './Win32Error';
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
symbols: { CloseHandle, CreateToolhelp32Snapshot, GetLastError, Module32FirstW, Module32NextW, OpenProcess, Process32FirstW, Process32NextW, ReadProcessMemory, VirtualQueryEx, WriteProcessMemory },
|
|
8
|
+
} = dlopen('kernel32.dll', {
|
|
7
9
|
CloseHandle: { args: [FFIType.u64], returns: FFIType.bool },
|
|
8
10
|
CreateToolhelp32Snapshot: { args: [FFIType.u32, FFIType.u32], returns: FFIType.u64 },
|
|
9
11
|
GetLastError: { returns: FFIType.u32 },
|
|
@@ -27,6 +29,7 @@ const { symbols: Kernel32 } = dlopen('kernel32.dll', {
|
|
|
27
29
|
* Many scalar reads utilize `TypedArray` scratches to avoid a second FFI hop, such as calling `bun:ffi.read.*`.
|
|
28
30
|
*
|
|
29
31
|
* @todo Add support for 32 or 64-bit processes using IsWow64Process2 (Windows 10+).
|
|
32
|
+
* @todo When adding 32-bit support, several u64 will need changed to u64_fast.
|
|
30
33
|
*
|
|
31
34
|
* @example
|
|
32
35
|
* ```ts
|
|
@@ -50,21 +53,21 @@ class Memory {
|
|
|
50
53
|
const dwFlags = 0x00000002; /* TH32CS_SNAPPROCESS */
|
|
51
54
|
const th32ProcessID = 0;
|
|
52
55
|
|
|
53
|
-
const hSnapshot =
|
|
56
|
+
const hSnapshot = CreateToolhelp32Snapshot(dwFlags, th32ProcessID);
|
|
54
57
|
|
|
55
58
|
if (hSnapshot === -1n) {
|
|
56
|
-
throw new Win32Error('CreateToolhelp32Snapshot',
|
|
59
|
+
throw new Win32Error('CreateToolhelp32Snapshot', GetLastError());
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
const lppe = Buffer.allocUnsafe(0x238 /* sizeof(PROCESSENTRY32) */);
|
|
60
63
|
/* */ lppe.writeUInt32LE(0x238 /* sizeof(PROCESSENTRY32) */);
|
|
61
64
|
|
|
62
|
-
const bProcess32FirstW =
|
|
65
|
+
const bProcess32FirstW = Process32FirstW(hSnapshot, lppe);
|
|
63
66
|
|
|
64
67
|
if (!bProcess32FirstW) {
|
|
65
|
-
|
|
68
|
+
CloseHandle(hSnapshot);
|
|
66
69
|
|
|
67
|
-
throw new Win32Error('Process32FirstW',
|
|
70
|
+
throw new Win32Error('Process32FirstW', GetLastError());
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
do {
|
|
@@ -81,12 +84,12 @@ class Memory {
|
|
|
81
84
|
const desiredAccess = 0x001f0fff; /* PROCESS_ALL_ACCESS */
|
|
82
85
|
const inheritHandle = false;
|
|
83
86
|
|
|
84
|
-
const hProcess =
|
|
87
|
+
const hProcess = OpenProcess(desiredAccess, inheritHandle, th32ProcessID);
|
|
85
88
|
|
|
86
89
|
if (hProcess === 0n) {
|
|
87
|
-
|
|
90
|
+
CloseHandle(hSnapshot);
|
|
88
91
|
|
|
89
|
-
throw new Win32Error('OpenProcess',
|
|
92
|
+
throw new Win32Error('OpenProcess', GetLastError());
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
this._modules = {};
|
|
@@ -96,12 +99,12 @@ class Memory {
|
|
|
96
99
|
|
|
97
100
|
this.refresh();
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
CloseHandle(hSnapshot);
|
|
100
103
|
|
|
101
104
|
return;
|
|
102
|
-
} while (
|
|
105
|
+
} while (Process32NextW(hSnapshot, lppe));
|
|
103
106
|
|
|
104
|
-
|
|
107
|
+
CloseHandle(hSnapshot);
|
|
105
108
|
|
|
106
109
|
throw new Error(`Process not found: ${identifier}…`);
|
|
107
110
|
}
|
|
@@ -180,10 +183,10 @@ class Memory {
|
|
|
180
183
|
let lpAddress = BigInt(address); // prettier-ignore
|
|
181
184
|
const lpBuffer = this.ScratchMemoryBasicInformation;
|
|
182
185
|
|
|
183
|
-
const bVirtualQueryEx = !!
|
|
186
|
+
const bVirtualQueryEx = !!VirtualQueryEx(this.hProcess, lpAddress, lpBuffer, dwLength);
|
|
184
187
|
|
|
185
188
|
if (!bVirtualQueryEx) {
|
|
186
|
-
throw new Win32Error('VirtualQueryEx',
|
|
189
|
+
throw new Win32Error('VirtualQueryEx', GetLastError());
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
const end = lpAddress + BigInt(length);
|
|
@@ -201,11 +204,101 @@ class Memory {
|
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
lpAddress = baseAddress + regionSize;
|
|
204
|
-
} while (lpAddress < end && !!
|
|
207
|
+
} while (lpAddress < end && !!VirtualQueryEx(this.hProcess, lpAddress, lpBuffer, dwLength));
|
|
205
208
|
|
|
206
209
|
return result;
|
|
207
210
|
}
|
|
208
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Closes the process handle.
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* const cs2 = new Memory('cs2.exe');
|
|
217
|
+
* cs2.close();
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
public close(): void {
|
|
221
|
+
CloseHandle(this.hProcess);
|
|
222
|
+
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Disposes resources held by this Memory instance.
|
|
228
|
+
* Called automatically when using `using` blocks.
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* using const mem = new Memory('cs2.exe');
|
|
232
|
+
* // mem is disposed at the end of the block
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
public [Symbol.dispose](): void {
|
|
236
|
+
this.close();
|
|
237
|
+
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Asynchronously disposes resources held by this Memory instance.
|
|
243
|
+
* Use in `await using` blocks for async cleanup.
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* await using const mem = new Memory('cs2.exe');
|
|
247
|
+
* // mem is disposed asynchronously at the end of the block
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
public [Symbol.asyncDispose](): Promise<void> {
|
|
251
|
+
this.close();
|
|
252
|
+
|
|
253
|
+
return Promise.resolve();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Refreshes the module list for the process.
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* const cs2 = new Memory('cs2.exe');
|
|
261
|
+
* cs2.refresh();
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
public refresh(): void {
|
|
265
|
+
const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
|
|
266
|
+
|
|
267
|
+
const hSnapshot = CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
|
|
268
|
+
|
|
269
|
+
if (hSnapshot === -1n) {
|
|
270
|
+
throw new Win32Error('CreateToolhelp32Snapshot', GetLastError());
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.ScratchModuleEntry32W.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
274
|
+
|
|
275
|
+
const lpme = this.ScratchModuleEntry32W;
|
|
276
|
+
|
|
277
|
+
const bModule32FirstW = Module32FirstW(hSnapshot, lpme);
|
|
278
|
+
|
|
279
|
+
if (!bModule32FirstW) {
|
|
280
|
+
CloseHandle(hSnapshot);
|
|
281
|
+
|
|
282
|
+
throw new Win32Error('Module32FirstW', GetLastError());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const modules: Memory['_modules'] = {};
|
|
286
|
+
|
|
287
|
+
do {
|
|
288
|
+
const modBaseAddr = lpme.readBigUInt64LE(0x18);
|
|
289
|
+
const modBaseSize = lpme.readUInt32LE(0x20);
|
|
290
|
+
const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
|
|
291
|
+
|
|
292
|
+
modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
|
|
293
|
+
} while (Module32NextW(hSnapshot, lpme));
|
|
294
|
+
|
|
295
|
+
CloseHandle(hSnapshot);
|
|
296
|
+
|
|
297
|
+
this._modules = Object.freeze(modules);
|
|
298
|
+
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
209
302
|
/**
|
|
210
303
|
* Follows a pointer chain with offsets.
|
|
211
304
|
* @param address Base address.
|
|
@@ -236,6 +329,7 @@ class Memory {
|
|
|
236
329
|
* @param address Address to read from.
|
|
237
330
|
* @param scratch Buffer to fill.
|
|
238
331
|
* @returns The filled buffer.
|
|
332
|
+
* @todo Consider inlining the call in the if to cut a binding… I hate the idea… 🫠…
|
|
239
333
|
* @example
|
|
240
334
|
* ```ts
|
|
241
335
|
* const cs2 = new Memory('cs2.exe');
|
|
@@ -244,14 +338,14 @@ class Memory {
|
|
|
244
338
|
*/
|
|
245
339
|
public read<T extends Scratch>(address: bigint, scratch: T): T {
|
|
246
340
|
const lpBaseAddress = address;
|
|
247
|
-
const lpBuffer = scratch
|
|
341
|
+
const lpBuffer = ptr(scratch);
|
|
248
342
|
const nSize = scratch.byteLength;
|
|
249
343
|
const numberOfBytesRead = 0x00n;
|
|
250
344
|
|
|
251
|
-
const bReadProcessMemory =
|
|
345
|
+
const bReadProcessMemory = ReadProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesRead);
|
|
252
346
|
|
|
253
347
|
if (!bReadProcessMemory) {
|
|
254
|
-
throw new Win32Error('ReadProcessMemory',
|
|
348
|
+
throw new Win32Error('ReadProcessMemory', GetLastError());
|
|
255
349
|
}
|
|
256
350
|
|
|
257
351
|
return scratch;
|
|
@@ -262,6 +356,8 @@ class Memory {
|
|
|
262
356
|
* @param address Address to write to.
|
|
263
357
|
* @param scratch Buffer to write.
|
|
264
358
|
* @returns This instance.
|
|
359
|
+
* @todo Add a `force: boolean` option that uses `VirtualProtectEx` for a temporary protection change when set to `true`.
|
|
360
|
+
* @todo Consider inlining the call in the if to cut a binding… I hate the idea… 🫠…
|
|
265
361
|
* @example
|
|
266
362
|
* ```ts
|
|
267
363
|
* const cs2 = new Memory('cs2.exe');
|
|
@@ -274,75 +370,15 @@ class Memory {
|
|
|
274
370
|
const nSize = scratch.byteLength;
|
|
275
371
|
const numberOfBytesWritten = 0x00n;
|
|
276
372
|
|
|
277
|
-
const
|
|
373
|
+
const bWriteProcessMemory = WriteProcessMemory(this.hProcess, lpBaseAddress, lpBuffer, nSize, numberOfBytesWritten);
|
|
278
374
|
|
|
279
|
-
if (!
|
|
280
|
-
throw new Win32Error('WriteProcessMemory',
|
|
375
|
+
if (!bWriteProcessMemory) {
|
|
376
|
+
throw new Win32Error('WriteProcessMemory', GetLastError());
|
|
281
377
|
}
|
|
282
378
|
|
|
283
379
|
return this;
|
|
284
380
|
}
|
|
285
381
|
|
|
286
|
-
/**
|
|
287
|
-
* Closes the process handle.
|
|
288
|
-
* @example
|
|
289
|
-
* ```ts
|
|
290
|
-
* const cs2 = new Memory('cs2.exe');
|
|
291
|
-
* cs2.close();
|
|
292
|
-
* ```
|
|
293
|
-
*/
|
|
294
|
-
public close(): void {
|
|
295
|
-
Kernel32.CloseHandle(this.hProcess);
|
|
296
|
-
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Refreshes the module list for the process.
|
|
302
|
-
* @example
|
|
303
|
-
* ```ts
|
|
304
|
-
* const cs2 = new Memory('cs2.exe');
|
|
305
|
-
* cs2.refresh();
|
|
306
|
-
* ```
|
|
307
|
-
*/
|
|
308
|
-
public refresh(): void {
|
|
309
|
-
const dwFlags = 0x00000008 /* TH32CS_SNAPMODULE */ | 0x00000010; /* TH32CS_SNAPMODULE32 */
|
|
310
|
-
|
|
311
|
-
const hSnapshot = Kernel32.CreateToolhelp32Snapshot(dwFlags, this.th32ProcessID)!;
|
|
312
|
-
|
|
313
|
-
if (hSnapshot === -1n) {
|
|
314
|
-
throw new Win32Error('CreateToolhelp32Snapshot', Kernel32.GetLastError());
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
this.ScratchModuleEntry32W.writeUInt32LE(0x438 /* sizeof(MODULEENTRY32W) */);
|
|
318
|
-
|
|
319
|
-
const lpme = this.ScratchModuleEntry32W;
|
|
320
|
-
|
|
321
|
-
const bModule32FirstW = Kernel32.Module32FirstW(hSnapshot, lpme);
|
|
322
|
-
|
|
323
|
-
if (!bModule32FirstW) {
|
|
324
|
-
Kernel32.CloseHandle(hSnapshot);
|
|
325
|
-
|
|
326
|
-
throw new Win32Error('Module32FirstW', Kernel32.GetLastError());
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const modules: Memory['_modules'] = {};
|
|
330
|
-
|
|
331
|
-
do {
|
|
332
|
-
const modBaseAddr = lpme.readBigUInt64LE(0x18);
|
|
333
|
-
const modBaseSize = lpme.readUInt32LE(0x20);
|
|
334
|
-
const szModule = lpme.toString('utf16le', 0x30, 0x230).replace(/\0+$/, '');
|
|
335
|
-
|
|
336
|
-
modules[szModule] = Object.freeze({ base: modBaseAddr, name: szModule, size: modBaseSize });
|
|
337
|
-
} while (Kernel32.Module32NextW(hSnapshot, lpme));
|
|
338
|
-
|
|
339
|
-
Kernel32.CloseHandle(hSnapshot);
|
|
340
|
-
|
|
341
|
-
this._modules = Object.freeze(modules);
|
|
342
|
-
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
382
|
/**
|
|
347
383
|
* Reads or writes a boolean value.
|
|
348
384
|
* @param address Address to access.
|
|
@@ -996,6 +1032,22 @@ class Memory {
|
|
|
996
1032
|
return this;
|
|
997
1033
|
}
|
|
998
1034
|
|
|
1035
|
+
public pointRaw(address: bigint): Float32Array;
|
|
1036
|
+
public pointRaw(address: bigint, values: Float32Array): this;
|
|
1037
|
+
public pointRaw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1038
|
+
if (values === undefined) {
|
|
1039
|
+
return this.f32Array(address, 0x02);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (values.length !== 0x02) {
|
|
1043
|
+
throw new RangeError('values.length must be 2.');
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
this.write(address, values);
|
|
1047
|
+
|
|
1048
|
+
return this;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
999
1051
|
/**
|
|
1000
1052
|
* Reads or writes a QAngle (object with pitch, yaw, roll).
|
|
1001
1053
|
* @param address Address to access.
|
|
@@ -1059,6 +1111,7 @@ class Memory {
|
|
|
1059
1111
|
const pitch = scratch[j];
|
|
1060
1112
|
const yaw = scratch[j + 0x01];
|
|
1061
1113
|
const roll = scratch[j + 0x02];
|
|
1114
|
+
|
|
1062
1115
|
result[i] = { pitch, yaw, roll };
|
|
1063
1116
|
}
|
|
1064
1117
|
|
|
@@ -1081,6 +1134,34 @@ class Memory {
|
|
|
1081
1134
|
return this;
|
|
1082
1135
|
}
|
|
1083
1136
|
|
|
1137
|
+
/**
|
|
1138
|
+
* Reads or writes a raw QAngle as a Float32Array.
|
|
1139
|
+
* @param address Address to access.
|
|
1140
|
+
* @param values Optional Float32Array to write.
|
|
1141
|
+
* @returns Float32Array read or this instance if writing.
|
|
1142
|
+
* @example
|
|
1143
|
+
* ```ts
|
|
1144
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1145
|
+
* const raw = cs2.qAngleRaw(0x12345678n);
|
|
1146
|
+
* cs2.qAngleRaw(0x12345678n, new Float32Array([1,2,3]));
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
public qAngleRaw(address: bigint): Float32Array;
|
|
1150
|
+
public qAngleRaw(address: bigint, values: Float32Array): this;
|
|
1151
|
+
public qAngleRaw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1152
|
+
if (values === undefined) {
|
|
1153
|
+
return this.f32Array(address, 0x03);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if (values.length !== 0x03) {
|
|
1157
|
+
throw new RangeError('values.length must be 3.');
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
this.write(address, values);
|
|
1161
|
+
|
|
1162
|
+
return this;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1084
1165
|
/**
|
|
1085
1166
|
* Reads or writes a Quaternion (object with w, x, y, z).
|
|
1086
1167
|
* @param address Address to access.
|
|
@@ -1171,6 +1252,34 @@ class Memory {
|
|
|
1171
1252
|
return this;
|
|
1172
1253
|
}
|
|
1173
1254
|
|
|
1255
|
+
/**
|
|
1256
|
+
* Reads or writes a raw Quaternion as a Float32Array.
|
|
1257
|
+
* @param address Address to access.
|
|
1258
|
+
* @param values Optional Float32Array to write.
|
|
1259
|
+
* @returns Float32Array read or this instance if writing.
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```ts
|
|
1262
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1263
|
+
* const raw = cs2.quaternionRaw(0x12345678n);
|
|
1264
|
+
* cs2.quaternionRaw(0x12345678n, new Float32Array([1,0,0,0]));
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
public quaternionRaw(address: bigint): Float32Array;
|
|
1268
|
+
public quaternionRaw(address: bigint, values: Float32Array): this;
|
|
1269
|
+
public quaternionRaw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1270
|
+
if (values === undefined) {
|
|
1271
|
+
return this.f32Array(address, 0x04);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
if (values.length !== 0x04) {
|
|
1275
|
+
throw new RangeError('values.length must be 4.');
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
this.write(address, values);
|
|
1279
|
+
|
|
1280
|
+
return this;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1174
1283
|
/**
|
|
1175
1284
|
* Reads or writes an RGB color (object with r, g, b).
|
|
1176
1285
|
* @param address Address to access.
|
|
@@ -1205,6 +1314,34 @@ class Memory {
|
|
|
1205
1314
|
return this.write(address, Scratch3);
|
|
1206
1315
|
}
|
|
1207
1316
|
|
|
1317
|
+
/**
|
|
1318
|
+
* Reads or writes a raw RGB value as a Uint8Array.
|
|
1319
|
+
* @param address Address to access.
|
|
1320
|
+
* @param values Optional buffer to write.
|
|
1321
|
+
* @returns Uint8Array read or this instance if writing.
|
|
1322
|
+
* @example
|
|
1323
|
+
* ```ts
|
|
1324
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1325
|
+
* const raw = cs2.rgbRaw(0x12345678n);
|
|
1326
|
+
* cs2.rgbRaw(0x12345678n, new Uint8Array([255,0,0]));
|
|
1327
|
+
* ```
|
|
1328
|
+
*/
|
|
1329
|
+
public rgbRaw(address: bigint): Uint8Array;
|
|
1330
|
+
public rgbRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray): this;
|
|
1331
|
+
public rgbRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray): Uint8Array | this {
|
|
1332
|
+
if (values === undefined) {
|
|
1333
|
+
return this.u8Array(address, 0x03);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (values.length !== 0x03) {
|
|
1337
|
+
throw new RangeError('values.length must be 3.');
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
this.write(address, values);
|
|
1341
|
+
|
|
1342
|
+
return this;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1208
1345
|
/**
|
|
1209
1346
|
* Reads or writes an RGBA color (object with r, g, b, a).
|
|
1210
1347
|
* @param address Address to access.
|
|
@@ -1241,6 +1378,34 @@ class Memory {
|
|
|
1241
1378
|
return this.write(address, Scratch4);
|
|
1242
1379
|
}
|
|
1243
1380
|
|
|
1381
|
+
/**
|
|
1382
|
+
* Reads or writes a raw RGBA value as a Uint8Array.
|
|
1383
|
+
* @param address Address to access.
|
|
1384
|
+
* @param values Optional buffer to write.
|
|
1385
|
+
* @returns Uint8Array read or this instance if writing.
|
|
1386
|
+
* @example
|
|
1387
|
+
* ```ts
|
|
1388
|
+
* const cs2 = new Memory('cs2.exe');
|
|
1389
|
+
* const raw = cs2.rgbaRaw(0x12345678n);
|
|
1390
|
+
* cs2.rgbaRaw(0x12345678n, new Uint8Array([255,0,0,255]));
|
|
1391
|
+
* ```
|
|
1392
|
+
*/
|
|
1393
|
+
public rgbaRaw(address: bigint): Uint8Array;
|
|
1394
|
+
public rgbaRaw(address: bigint, values: Buffer | Uint8Array | Uint8ClampedArray): this;
|
|
1395
|
+
public rgbaRaw(address: bigint, values?: Buffer | Uint8Array | Uint8ClampedArray): Uint8Array | this {
|
|
1396
|
+
if (values === undefined) {
|
|
1397
|
+
return this.u8Array(address, 0x04);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
if (values.length !== 0x04) {
|
|
1401
|
+
throw new RangeError('values.length must be 4.');
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
this.write(address, values);
|
|
1405
|
+
|
|
1406
|
+
return this;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1244
1409
|
/**
|
|
1245
1410
|
* Reads or writes a 16-bit unsigned integer.
|
|
1246
1411
|
* @param address Address to access.
|
|
@@ -1569,6 +1734,22 @@ class Memory {
|
|
|
1569
1734
|
return this.pointArray(address, lengthOrValues);
|
|
1570
1735
|
}
|
|
1571
1736
|
|
|
1737
|
+
public vector2Raw(address: bigint): Float32Array;
|
|
1738
|
+
public vector2Raw(address: bigint, values: Float32Array): this;
|
|
1739
|
+
public vector2Raw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1740
|
+
if (values === undefined) {
|
|
1741
|
+
return this.f32Array(address, 0x02);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
if (values.length !== 0x02) {
|
|
1745
|
+
throw new RangeError('values.length must be 2.');
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
this.write(address, values);
|
|
1749
|
+
|
|
1750
|
+
return this;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1572
1753
|
/**
|
|
1573
1754
|
* Reads or writes a Vector3 (object with x, y, z).
|
|
1574
1755
|
* @param address Address to access.
|
|
@@ -1655,6 +1836,22 @@ class Memory {
|
|
|
1655
1836
|
return this;
|
|
1656
1837
|
}
|
|
1657
1838
|
|
|
1839
|
+
public vector3Raw(address: bigint): Float32Array;
|
|
1840
|
+
public vector3Raw(address: bigint, values: Float32Array): this;
|
|
1841
|
+
public vector3Raw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1842
|
+
if (values === undefined) {
|
|
1843
|
+
return this.f32Array(address, 0x03);
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
if (values.length !== 0x03) {
|
|
1847
|
+
throw new RangeError('values.length must be 3.');
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
this.write(address, values);
|
|
1851
|
+
|
|
1852
|
+
return this;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1658
1855
|
/**
|
|
1659
1856
|
* Reads or writes a Vector4 (object with w, x, y, z).
|
|
1660
1857
|
* @param address Address to access.
|
|
@@ -1701,6 +1898,22 @@ class Memory {
|
|
|
1701
1898
|
return this.quaternionArray(address, lengthOrValues);
|
|
1702
1899
|
}
|
|
1703
1900
|
|
|
1901
|
+
public vector4Raw(address: bigint): Float32Array;
|
|
1902
|
+
public vector4Raw(address: bigint, values: Float32Array): this;
|
|
1903
|
+
public vector4Raw(address: bigint, values?: Float32Array): Float32Array | this {
|
|
1904
|
+
if (values === undefined) {
|
|
1905
|
+
return this.f32Array(address, 0x04);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
if (values.length !== 0x04) {
|
|
1909
|
+
throw new RangeError('values.length must be 4.');
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
this.write(address, values);
|
|
1913
|
+
|
|
1914
|
+
return this;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1704
1917
|
// Public utility methods…
|
|
1705
1918
|
|
|
1706
1919
|
/**
|
|
@@ -1716,19 +1929,15 @@ class Memory {
|
|
|
1716
1929
|
* ```
|
|
1717
1930
|
*/
|
|
1718
1931
|
public indexOf(needle: Scratch, address: bigint, length: number): bigint {
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1721
|
-
this.read(address, haystackUint8Array);
|
|
1722
|
-
|
|
1723
|
-
const haystackBuffer = Buffer.from(haystackUint8Array.buffer, haystackUint8Array.byteOffset, haystackUint8Array.byteLength);
|
|
1932
|
+
const haystack = Buffer.allocUnsafe(length);
|
|
1724
1933
|
|
|
1725
|
-
|
|
1726
|
-
? new Uint8Array(needle.buffer, needle.byteOffset, needle.byteLength)
|
|
1727
|
-
: new Uint8Array(needle);
|
|
1934
|
+
this.read(address, haystack);
|
|
1728
1935
|
|
|
1729
|
-
const needleBuffer =
|
|
1936
|
+
const needleBuffer = ArrayBuffer.isView(needle) //
|
|
1937
|
+
? Buffer.from(needle.buffer, needle.byteOffset, needle.byteLength)
|
|
1938
|
+
: Buffer.from(needle);
|
|
1730
1939
|
|
|
1731
|
-
const indexOf =
|
|
1940
|
+
const indexOf = haystack.indexOf(needleBuffer);
|
|
1732
1941
|
|
|
1733
1942
|
return indexOf !== -1 ? BigInt(indexOf) + address : -1n;
|
|
1734
1943
|
}
|