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 +56 -21
- package/package.json +1 -1
- package/structs/Memory.ts +191 -5
- package/types/Memory.ts +91 -0
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
|
|
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('
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
###
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
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
|
+
};
|