rbx-api-ts 1.6.9
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/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/dependabot.yml +6 -0
- package/README.md +13 -0
- package/dist/BinaryParser.d.ts +29 -0
- package/dist/BinaryParser.js +539 -0
- package/dist/ByteReader.d.ts +38 -0
- package/dist/ByteReader.js +204 -0
- package/dist/Instance.d.ts +150 -0
- package/dist/Instance.js +231 -0
- package/dist/XmlParser.d.ts +23 -0
- package/dist/XmlParser.js +290 -0
- package/dist/attributes-parser/attributes_parser.d.ts +7 -0
- package/dist/attributes-parser/attributes_parser.js +291 -0
- package/dist/attributes-parser/attributes_parser_bg.wasm +0 -0
- package/dist/attributes-parser/attributes_parser_bg.wasm.d.ts +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +99 -0
- package/package.json +41 -0
- package/postinstall.js +1 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Describe the bug**
|
11
|
+
A clear and concise description of what the bug is.
|
12
|
+
|
13
|
+
**To Reproduce**
|
14
|
+
Steps to reproduce the behavior:
|
15
|
+
1. Go to '...'
|
16
|
+
2. Click on '....'
|
17
|
+
3. Scroll down to '....'
|
18
|
+
4. See error
|
19
|
+
|
20
|
+
**Expected behavior**
|
21
|
+
A clear and concise description of what you expected to happen.
|
22
|
+
|
23
|
+
**Screenshots**
|
24
|
+
If applicable, add screenshots to help explain your problem.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest an idea for this project
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
12
|
+
|
13
|
+
**Describe the solution you'd like**
|
14
|
+
A clear and concise description of what you want to happen.
|
15
|
+
|
16
|
+
**Describe alternatives you've considered**
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
18
|
+
|
19
|
+
**Additional context**
|
20
|
+
Add any other context or screenshots about the feature request here.
|
package/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# rbx-reader
|
2
|
+
A module that allows for reading of `.rbxm` and `.rbxl` files.
|
3
|
+
|
4
|
+
**Huge thanks to @shiinazzz for helping to get the code into useful TypeScript form.**
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
Basic reading:
|
8
|
+
```js
|
9
|
+
const fs = require("node:fs")
|
10
|
+
const reader = require("rbx-reader")
|
11
|
+
const buf = fs.readFileSync("./path/to/roblox/file.rbxm") // Can also be an .rbxl
|
12
|
+
const result = reader.parseFile(buf)
|
13
|
+
```
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import ByteReader from './ByteReader';
|
2
|
+
import { Instance, InstanceRoot } from './Instance';
|
3
|
+
export interface ParserResult {
|
4
|
+
result: InstanceRoot;
|
5
|
+
reader: ByteReader;
|
6
|
+
instances: Instance[];
|
7
|
+
groups: Group[];
|
8
|
+
sharedStrings: string[];
|
9
|
+
meta: object;
|
10
|
+
arrays: any[][];
|
11
|
+
arrayIndex: number;
|
12
|
+
}
|
13
|
+
export interface Group {
|
14
|
+
ClassName: string;
|
15
|
+
Objects: Instance[];
|
16
|
+
}
|
17
|
+
declare const BinaryParser: {
|
18
|
+
HeaderBytes: number[];
|
19
|
+
Faces: number[][];
|
20
|
+
DataTypes: (string | null)[];
|
21
|
+
parse(buffer: ArrayBuffer): ParserResult;
|
22
|
+
parseChunk(parser: ParserResult, startIndex: number): void;
|
23
|
+
parseMETA(parser: any, chunk: any): void;
|
24
|
+
parseSSTR(parser: any, chunk: any): void;
|
25
|
+
parseINST(parser: any, chunk: any): void;
|
26
|
+
parsePROP(parser: any, chunk: any): void;
|
27
|
+
parsePRNT(parser: any, chunk: any): void;
|
28
|
+
};
|
29
|
+
export default BinaryParser;
|
@@ -0,0 +1,539 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const ByteReader_1 = __importDefault(require("./ByteReader"));
|
7
|
+
const Instance_1 = require("./Instance");
|
8
|
+
const attributes_parser_1 = require("./attributes-parser/attributes_parser");
|
9
|
+
const BinaryParser = {
|
10
|
+
HeaderBytes: [0x3C, 0x72, 0x6F, 0x62, 0x6C, 0x6F, 0x78, 0x21, 0x89, 0xFF, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00],
|
11
|
+
Faces: [[1, 0, 0], [0, 1, 0], [0, 0, 1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]],
|
12
|
+
DataTypes: [
|
13
|
+
null, 'string', 'bool', 'int', 'float', 'double', 'UDim', 'UDim2', // 7
|
14
|
+
'Ray', 'Faces', 'Axes', 'BrickColor', 'Color3', 'Vector2', 'Vector3', 'Vector2int16', // 15
|
15
|
+
'CFrame', 'Quaternion', 'Enum', 'Instance', 'Vector3int16', 'NumberSequence', 'ColorSequence', // 22
|
16
|
+
'NumberRange', 'Rect2D', 'PhysicalProperties', 'Color3uint8', 'int64', 'SharedString', 'UnknownScriptFormat', // 29
|
17
|
+
'Optional', 'UniqueId'
|
18
|
+
],
|
19
|
+
parse(buffer) {
|
20
|
+
const reader = new ByteReader_1.default(buffer);
|
21
|
+
if (!reader.Match(this.HeaderBytes)) {
|
22
|
+
throw new Error('[RobloxBinaryParser] Header bytes did not match (Did binary format change?)');
|
23
|
+
}
|
24
|
+
const groupsCount = reader.UInt32LE();
|
25
|
+
const instancesCount = reader.UInt32LE();
|
26
|
+
reader.Jump(8);
|
27
|
+
const parser = {
|
28
|
+
result: new Instance_1.InstanceRoot(),
|
29
|
+
reader: reader,
|
30
|
+
instances: [],
|
31
|
+
groups: [],
|
32
|
+
sharedStrings: [],
|
33
|
+
meta: {},
|
34
|
+
arrays: [],
|
35
|
+
arrayIndex: 0,
|
36
|
+
};
|
37
|
+
for (var i = 0; i < 6; i++) {
|
38
|
+
parser.arrays.push(new Array(256));
|
39
|
+
}
|
40
|
+
reader.GetIndex();
|
41
|
+
var maxChunkSize = 0;
|
42
|
+
var chunkIndices = [];
|
43
|
+
while (reader.GetRemaining() >= 4) {
|
44
|
+
chunkIndices.push(reader.GetIndex());
|
45
|
+
reader.String(4);
|
46
|
+
const comLength = reader.UInt32LE();
|
47
|
+
const decomLength = reader.UInt32LE();
|
48
|
+
if (comLength > 0) {
|
49
|
+
reader.Jump(4 + comLength);
|
50
|
+
if (decomLength > maxChunkSize) {
|
51
|
+
maxChunkSize = decomLength;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
reader.Jump(4 + decomLength);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
reader.chunkBuffer = new Uint8Array(maxChunkSize);
|
59
|
+
for (const startIndex of chunkIndices) {
|
60
|
+
this.parseChunk(parser, startIndex);
|
61
|
+
}
|
62
|
+
return parser;
|
63
|
+
},
|
64
|
+
parseChunk(parser, startIndex) {
|
65
|
+
parser.reader.SetIndex(startIndex);
|
66
|
+
const chunkType = parser.reader.String(4);
|
67
|
+
if (chunkType === 'END\0') {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
const chunkData = parser.reader.LZ4(parser.reader.chunkBuffer);
|
71
|
+
const chunkReader = new ByteReader_1.default(chunkData);
|
72
|
+
parser.arrayIndex = 0;
|
73
|
+
switch (chunkType) {
|
74
|
+
case 'INST':
|
75
|
+
this.parseINST(parser, chunkReader);
|
76
|
+
break;
|
77
|
+
case 'PROP':
|
78
|
+
this.parsePROP(parser, chunkReader);
|
79
|
+
break;
|
80
|
+
case 'PRNT':
|
81
|
+
this.parsePRNT(parser, chunkReader);
|
82
|
+
break;
|
83
|
+
case 'SSTR':
|
84
|
+
this.parseSSTR(parser, chunkReader);
|
85
|
+
break;
|
86
|
+
case 'META':
|
87
|
+
this.parseMETA(parser, chunkReader);
|
88
|
+
break;
|
89
|
+
case 'SIGN':
|
90
|
+
break;
|
91
|
+
default:
|
92
|
+
}
|
93
|
+
},
|
94
|
+
parseMETA(parser, chunk) {
|
95
|
+
const numEntries = chunk.UInt32LE();
|
96
|
+
for (var i = 0; i < numEntries; i++) {
|
97
|
+
const key = chunk.String(chunk.UInt32LE());
|
98
|
+
const value = chunk.String(chunk.UInt32LE());
|
99
|
+
parser.meta[key] = value;
|
100
|
+
}
|
101
|
+
},
|
102
|
+
parseSSTR(parser, chunk) {
|
103
|
+
chunk.UInt32LE(); // version
|
104
|
+
const stringCount = chunk.UInt32LE();
|
105
|
+
for (var i = 0; i < stringCount; i++) {
|
106
|
+
const md5 = chunk.Array(16);
|
107
|
+
const length = chunk.UInt32LE();
|
108
|
+
const value = chunk.String(length);
|
109
|
+
parser.sharedStrings[i] = { md5, value };
|
110
|
+
}
|
111
|
+
},
|
112
|
+
parseINST(parser, chunk) {
|
113
|
+
const groupId = chunk.UInt32LE();
|
114
|
+
const className = chunk.String(chunk.UInt32LE());
|
115
|
+
chunk.Byte(); // isService
|
116
|
+
const instCount = chunk.UInt32LE();
|
117
|
+
const instIds = chunk.RBXInterleavedInt32(instCount, parser.arrays[parser.arrayIndex++]);
|
118
|
+
const group = parser.groups[groupId] = {
|
119
|
+
ClassName: className,
|
120
|
+
Objects: []
|
121
|
+
};
|
122
|
+
var instId = 0;
|
123
|
+
for (var i = 0; i < instCount; i++) {
|
124
|
+
instId += instIds[i];
|
125
|
+
group.Objects.push(parser.instances[instId] = Instance_1.Instance.new(className));
|
126
|
+
}
|
127
|
+
},
|
128
|
+
parsePROP(parser, chunk) {
|
129
|
+
const group = parser.groups[chunk.UInt32LE()];
|
130
|
+
const prop = chunk.String(chunk.UInt32LE());
|
131
|
+
if (chunk.GetRemaining() <= 0) {
|
132
|
+
return; // empty chunk?
|
133
|
+
}
|
134
|
+
const instCount = group.Objects.length;
|
135
|
+
const values = parser.arrays[parser.arrayIndex++];
|
136
|
+
var dataType = chunk.Byte();
|
137
|
+
var typeName = this.DataTypes[dataType];
|
138
|
+
var isOptional = typeName === 'Optional';
|
139
|
+
if (isOptional) {
|
140
|
+
dataType = chunk.Byte();
|
141
|
+
typeName = this.DataTypes[dataType];
|
142
|
+
}
|
143
|
+
var resultTypeName = typeName || 'Unknown';
|
144
|
+
switch (typeName) {
|
145
|
+
case 'string':
|
146
|
+
for (var i = 0; i < instCount; i++) {
|
147
|
+
const len = chunk.UInt32LE();
|
148
|
+
values[i] = chunk.String(len);
|
149
|
+
}
|
150
|
+
break;
|
151
|
+
case 'bool':
|
152
|
+
for (var i = 0; i < instCount; i++) {
|
153
|
+
values[i] = chunk.Byte() !== 0;
|
154
|
+
}
|
155
|
+
break;
|
156
|
+
case 'int':
|
157
|
+
chunk.RBXInterleavedInt32(instCount, values);
|
158
|
+
break;
|
159
|
+
case 'float':
|
160
|
+
chunk.RBXInterleavedFloat(instCount, values);
|
161
|
+
break;
|
162
|
+
case 'double':
|
163
|
+
for (var i = 0; i < instCount; i++) {
|
164
|
+
values[i] = chunk.DoubleLE();
|
165
|
+
}
|
166
|
+
break;
|
167
|
+
case 'UDim': {
|
168
|
+
const scale = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
169
|
+
const offset = chunk.RBXInterleavedInt32(instCount, parser.arrays[parser.arrayIndex++]);
|
170
|
+
for (var i = 0; i < instCount; i++) {
|
171
|
+
values[i] = [scale[i], offset[i]];
|
172
|
+
}
|
173
|
+
break;
|
174
|
+
}
|
175
|
+
case 'UDim2': {
|
176
|
+
const scaleX = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
177
|
+
const scaleY = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
178
|
+
const offsetX = chunk.RBXInterleavedInt32(instCount, parser.arrays[parser.arrayIndex++]);
|
179
|
+
const offsetY = chunk.RBXInterleavedInt32(instCount, parser.arrays[parser.arrayIndex++]);
|
180
|
+
for (var i = 0; i < instCount; i++) {
|
181
|
+
values[i] = [
|
182
|
+
[scaleX[i], offsetX[i]],
|
183
|
+
[scaleY[i], offsetY[i]]
|
184
|
+
];
|
185
|
+
}
|
186
|
+
break;
|
187
|
+
}
|
188
|
+
case 'Ray': {
|
189
|
+
for (var i = 0; i < instCount; i++) {
|
190
|
+
values[i] = [
|
191
|
+
[chunk.RBXFloatLE(), chunk.RBXFloatLE(), chunk.RBXFloatLE()],
|
192
|
+
[chunk.RBXFloatLE(), chunk.RBXFloatLE(), chunk.RBXFloatLE()]
|
193
|
+
];
|
194
|
+
}
|
195
|
+
break;
|
196
|
+
}
|
197
|
+
case 'Faces':
|
198
|
+
for (var i = 0; i < instCount; i++) {
|
199
|
+
const data = chunk.Byte();
|
200
|
+
values[i] = {
|
201
|
+
Right: !!(data & 1),
|
202
|
+
Top: !!(data & 2),
|
203
|
+
Back: !!(data & 4),
|
204
|
+
Left: !!(data & 8),
|
205
|
+
Bottom: !!(data & 16),
|
206
|
+
Front: !!(data & 32)
|
207
|
+
};
|
208
|
+
}
|
209
|
+
break;
|
210
|
+
case 'Axes':
|
211
|
+
for (var i = 0; i < instCount; i++) {
|
212
|
+
const data = chunk.Byte();
|
213
|
+
values[i] = {
|
214
|
+
X: !!(data & 1),
|
215
|
+
Y: !!(data & 2),
|
216
|
+
Z: !!(data & 4)
|
217
|
+
};
|
218
|
+
}
|
219
|
+
break;
|
220
|
+
case 'BrickColor':
|
221
|
+
chunk.RBXInterleavedUint32(instCount, values);
|
222
|
+
break;
|
223
|
+
case 'Color3': {
|
224
|
+
const red = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
225
|
+
const green = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
226
|
+
const blue = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
227
|
+
for (var i = 0; i < instCount; i++) {
|
228
|
+
values[i] = [red[i], green[i], blue[i]];
|
229
|
+
}
|
230
|
+
break;
|
231
|
+
}
|
232
|
+
case 'Vector2': {
|
233
|
+
const vecX = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
234
|
+
const vecY = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
235
|
+
for (var i = 0; i < instCount; i++) {
|
236
|
+
values[i] = [vecX[i], vecY[i]];
|
237
|
+
}
|
238
|
+
break;
|
239
|
+
}
|
240
|
+
case 'Vector3': {
|
241
|
+
const vecX = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
242
|
+
const vecY = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
243
|
+
const vecZ = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
244
|
+
for (var i = 0; i < instCount; i++) {
|
245
|
+
values[i] = [vecX[i], vecY[i], vecZ[i]];
|
246
|
+
}
|
247
|
+
break;
|
248
|
+
}
|
249
|
+
case 'Vector2int16': break; // Not used anywhere?
|
250
|
+
case 'CFrame': {
|
251
|
+
for (var vi = 0; vi < instCount; vi++) {
|
252
|
+
const value = values[vi] = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1];
|
253
|
+
const type = chunk.Byte();
|
254
|
+
if (type !== 0) {
|
255
|
+
const right = this.Faces[Math.floor((type - 1) / 6)];
|
256
|
+
const up = this.Faces[Math.floor(type - 1) % 6];
|
257
|
+
const back = [
|
258
|
+
right[1] * up[2] - up[1] * right[2],
|
259
|
+
right[2] * up[0] - up[2] * right[0],
|
260
|
+
right[0] * up[1] - up[0] * right[1]
|
261
|
+
];
|
262
|
+
for (var i = 0; i < 3; i++) {
|
263
|
+
value[3 + i * 3] = right[i];
|
264
|
+
value[4 + i * 3] = up[i];
|
265
|
+
value[5 + i * 3] = back[i];
|
266
|
+
}
|
267
|
+
}
|
268
|
+
else {
|
269
|
+
for (var i = 0; i < 9; i++) {
|
270
|
+
value[i + 3] = chunk.FloatLE();
|
271
|
+
}
|
272
|
+
}
|
273
|
+
}
|
274
|
+
const vecX = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
275
|
+
const vecY = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
276
|
+
const vecZ = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
277
|
+
for (var i = 0; i < instCount; i++) {
|
278
|
+
values[i][0] = vecX[i];
|
279
|
+
values[i][1] = vecY[i];
|
280
|
+
values[i][2] = vecZ[i];
|
281
|
+
}
|
282
|
+
break;
|
283
|
+
}
|
284
|
+
// case 'Quaternion': break // Not used anywhere?
|
285
|
+
case 'Enum':
|
286
|
+
chunk.RBXInterleavedUint32(instCount, values);
|
287
|
+
break;
|
288
|
+
case 'Instance': {
|
289
|
+
const refIds = chunk.RBXInterleavedInt32(instCount, parser.arrays[parser.arrayIndex++]);
|
290
|
+
var refId = 0;
|
291
|
+
for (var i = 0; i < instCount; i++) {
|
292
|
+
refId += refIds[i];
|
293
|
+
values[i] = parser.instances[refId];
|
294
|
+
}
|
295
|
+
break;
|
296
|
+
}
|
297
|
+
case 'Vector3int16':
|
298
|
+
break; // Not used anywhere?
|
299
|
+
case 'NumberSequence': {
|
300
|
+
for (var i = 0; i < instCount; i++) {
|
301
|
+
const seqLength = chunk.UInt32LE();
|
302
|
+
const seq = values[i] = [];
|
303
|
+
for (var j = 0; j < seqLength; j++) {
|
304
|
+
seq.push({
|
305
|
+
Time: chunk.FloatLE(),
|
306
|
+
Value: chunk.FloatLE(),
|
307
|
+
Envelope: chunk.FloatLE()
|
308
|
+
});
|
309
|
+
}
|
310
|
+
}
|
311
|
+
break;
|
312
|
+
}
|
313
|
+
case 'ColorSequence':
|
314
|
+
for (var i = 0; i < instCount; i++) {
|
315
|
+
const seqLength = chunk.UInt32LE();
|
316
|
+
const seq = values[i] = [];
|
317
|
+
for (var j = 0; j < seqLength; j++) {
|
318
|
+
seq.push({
|
319
|
+
Time: chunk.FloatLE(),
|
320
|
+
Color: [chunk.FloatLE(), chunk.FloatLE(), chunk.FloatLE()],
|
321
|
+
EnvelopeMaybe: chunk.FloatLE()
|
322
|
+
});
|
323
|
+
}
|
324
|
+
}
|
325
|
+
break;
|
326
|
+
case 'NumberRange':
|
327
|
+
for (var i = 0; i < instCount; i++) {
|
328
|
+
values[i] = {
|
329
|
+
Min: chunk.FloatLE(),
|
330
|
+
Max: chunk.FloatLE()
|
331
|
+
};
|
332
|
+
}
|
333
|
+
break;
|
334
|
+
case 'Rect2D': {
|
335
|
+
const x0 = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
336
|
+
const y0 = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
337
|
+
const x1 = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
338
|
+
const y1 = chunk.RBXInterleavedFloat(instCount, parser.arrays[parser.arrayIndex++]);
|
339
|
+
for (var i = 0; i < instCount; i++) {
|
340
|
+
values[i] = [x0[i], y0[i], x1[i], y1[i]];
|
341
|
+
}
|
342
|
+
break;
|
343
|
+
}
|
344
|
+
case 'PhysicalProperties':
|
345
|
+
for (var i = 0; i < instCount; i++) {
|
346
|
+
const enabled = chunk.Byte() !== 0;
|
347
|
+
values[i] = {
|
348
|
+
CustomPhysics: enabled,
|
349
|
+
Density: enabled ? chunk.RBXFloatLE() : undefined,
|
350
|
+
Friction: enabled ? chunk.RBXFloatLE() : undefined,
|
351
|
+
Elasticity: enabled ? chunk.RBXFloatLE() : undefined,
|
352
|
+
FrictionWeight: enabled ? chunk.RBXFloatLE() : undefined,
|
353
|
+
ElasticityWeight: enabled ? chunk.RBXFloatLE() : undefined
|
354
|
+
};
|
355
|
+
}
|
356
|
+
break;
|
357
|
+
case 'Color3uint8': {
|
358
|
+
const rgb = chunk.Array(instCount * 3);
|
359
|
+
for (var i = 0; i < instCount; i++) {
|
360
|
+
values[i] = [rgb[i] / 255, rgb[i + instCount] / 255, rgb[i + instCount * 2] / 255];
|
361
|
+
}
|
362
|
+
resultTypeName = 'Color3';
|
363
|
+
break;
|
364
|
+
}
|
365
|
+
case 'int64': { // Two's complement
|
366
|
+
const bytes = chunk.Array(instCount * 8);
|
367
|
+
for (var i = 0; i < instCount; i++) {
|
368
|
+
var byte0 = bytes[i + instCount * 0] * (256 ** 3) + bytes[i + instCount * 1] * (256 ** 2) +
|
369
|
+
bytes[i + instCount * 2] * 256 + bytes[i + instCount * 3];
|
370
|
+
var byte1 = bytes[i + instCount * 4] * (256 ** 3) + bytes[i + instCount * 5] * (256 ** 2) +
|
371
|
+
bytes[i + instCount * 6] * 256 + bytes[i + instCount * 7];
|
372
|
+
const neg = byte1 % 2;
|
373
|
+
byte1 = (byte0 % 2) * (2 ** 31) + (byte1 + neg) / 2;
|
374
|
+
byte0 = Math.floor(byte0 / 2);
|
375
|
+
if (byte0 < 2097152) {
|
376
|
+
const value = byte0 * (256 ** 4) + byte1;
|
377
|
+
values[i] = String(neg ? -value : value);
|
378
|
+
}
|
379
|
+
else { // Slow path
|
380
|
+
var result = '';
|
381
|
+
while (byte1 || byte0) {
|
382
|
+
const cur0 = byte0;
|
383
|
+
const res0 = cur0 % 10;
|
384
|
+
byte0 = (cur0 - res0) / 10;
|
385
|
+
const cur1 = byte1 + res0 * (256 ** 4);
|
386
|
+
const res1 = cur1 % 10;
|
387
|
+
byte1 = (cur1 - res1) / 10;
|
388
|
+
result = res1 + result;
|
389
|
+
}
|
390
|
+
values[i] = (neg ? '-' : '') + (result || '0');
|
391
|
+
}
|
392
|
+
}
|
393
|
+
break;
|
394
|
+
}
|
395
|
+
case 'SharedString': {
|
396
|
+
const indices = chunk.RBXInterleavedUint32(instCount, parser.arrays[parser.arrayIndex++]);
|
397
|
+
for (var i = 0; i < instCount; i++) {
|
398
|
+
values[i] = parser.sharedStrings[indices[i]].value;
|
399
|
+
}
|
400
|
+
break;
|
401
|
+
}
|
402
|
+
case 'UniqueId': {
|
403
|
+
const bytes = chunk.Array(instCount * 16);
|
404
|
+
for (var i = 0; i < instCount; i++) {
|
405
|
+
var result = '';
|
406
|
+
for (var j = 0; j < 16; j++) {
|
407
|
+
const byte = bytes[j * instCount + i];
|
408
|
+
result += ('0' + byte.toString(16)).slice(-2);
|
409
|
+
}
|
410
|
+
values[i] = result;
|
411
|
+
}
|
412
|
+
break;
|
413
|
+
}
|
414
|
+
default:
|
415
|
+
if (!typeName) {
|
416
|
+
// console.warn(`[RobloxBinaryParser] Unknown dataType 0x${dataType.toString(16).toUpperCase()} (${dataType}) for ${group.ClassName}.${prop}`)
|
417
|
+
}
|
418
|
+
else {
|
419
|
+
// console.warn(`[RobloxBinaryParser] Unimplemented dataType '${typeName}' for ${group.ClassName}.${prop}`)
|
420
|
+
}
|
421
|
+
break;
|
422
|
+
case 'UnknownScriptFormat':
|
423
|
+
for (var i = 0; i < instCount; i++) {
|
424
|
+
values[i] = `<${typeName || 'Unknown'}>`;
|
425
|
+
}
|
426
|
+
break;
|
427
|
+
}
|
428
|
+
if (isOptional) {
|
429
|
+
if (this.DataTypes[chunk.Byte()] !== 'bool' || chunk.GetRemaining() !== instCount) {
|
430
|
+
// console.warn(`[RobloxBinaryParser] Missing byte array at end of optional`)
|
431
|
+
isOptional = false;
|
432
|
+
for (var i = 0; i < instCount; i++) {
|
433
|
+
values[i] = `<Optional>`;
|
434
|
+
}
|
435
|
+
}
|
436
|
+
}
|
437
|
+
for (var index = 0; index < instCount; index++) {
|
438
|
+
if (isOptional)
|
439
|
+
if (chunk.Byte() === 0)
|
440
|
+
continue;
|
441
|
+
if (prop == 'AttributesSerialize') {
|
442
|
+
try {
|
443
|
+
const result = (0, attributes_parser_1.parse_attrs)(Buffer.from(values[index]));
|
444
|
+
const Attributes = {};
|
445
|
+
result.forEach((v, k) => {
|
446
|
+
Attributes[k] = v;
|
447
|
+
});
|
448
|
+
const finalAttrs = {};
|
449
|
+
Object.keys(Attributes).forEach(k => {
|
450
|
+
const v = Attributes[k];
|
451
|
+
if (Instance_1.AttrAssertions.isBoolAttr(v))
|
452
|
+
finalAttrs[k] = v.Bool;
|
453
|
+
else if (Instance_1.AttrAssertions.isStringAttr(v))
|
454
|
+
finalAttrs[k] = v.BinaryString;
|
455
|
+
else if (Instance_1.AttrAssertions.isNumberSequenceAttr(v))
|
456
|
+
finalAttrs[k] = v.NumberSequence;
|
457
|
+
else if (Instance_1.AttrAssertions.isRectAttr(v))
|
458
|
+
finalAttrs[k] = v.Rect;
|
459
|
+
else if (Instance_1.AttrAssertions.isCFrameAttr(v))
|
460
|
+
finalAttrs[k] = v.CFrame;
|
461
|
+
else if (Instance_1.AttrAssertions.isNumberRangeAttr(v))
|
462
|
+
finalAttrs[k] = {
|
463
|
+
lower: v.NumberRange[0],
|
464
|
+
upper: v.NumberRange[1]
|
465
|
+
};
|
466
|
+
else if (Instance_1.AttrAssertions.isColorSequenceAttr(v))
|
467
|
+
finalAttrs[k] = v.ColorSequence;
|
468
|
+
else if (Instance_1.AttrAssertions.isVector3Attr(v))
|
469
|
+
finalAttrs[k] = {
|
470
|
+
x: v.Vector3[0],
|
471
|
+
y: v.Vector3[1],
|
472
|
+
z: v.Vector3[2]
|
473
|
+
};
|
474
|
+
else if (Instance_1.AttrAssertions.isUDim2Attr(v))
|
475
|
+
finalAttrs[k] = {
|
476
|
+
x: {
|
477
|
+
scale: v.UDim2[0][0],
|
478
|
+
offset: v.UDim2[0][1]
|
479
|
+
},
|
480
|
+
y: {
|
481
|
+
scale: v.UDim2[1][0],
|
482
|
+
offset: v.UDim2[1][1]
|
483
|
+
}
|
484
|
+
};
|
485
|
+
else if (Instance_1.AttrAssertions.isVector2Attr(v))
|
486
|
+
finalAttrs[k] = {
|
487
|
+
x: v.Vector2[0],
|
488
|
+
y: v.Vector2[1]
|
489
|
+
};
|
490
|
+
else if (Instance_1.AttrAssertions.isColor3Attr(v))
|
491
|
+
finalAttrs[k] = {
|
492
|
+
r: v.Color3[0],
|
493
|
+
g: v.Color3[1],
|
494
|
+
b: v.Color3[2]
|
495
|
+
};
|
496
|
+
else if (Instance_1.AttrAssertions.isNumberAttr(v))
|
497
|
+
finalAttrs[k] = v.Float64;
|
498
|
+
else if (Instance_1.AttrAssertions.isUDimAttr(v))
|
499
|
+
finalAttrs[k] = {
|
500
|
+
scale: v.UDim[0],
|
501
|
+
offset: v.UDim[1]
|
502
|
+
};
|
503
|
+
else if (Instance_1.AttrAssertions.isBrickColorAttr(v))
|
504
|
+
finalAttrs[k] = {
|
505
|
+
...v
|
506
|
+
};
|
507
|
+
});
|
508
|
+
group.Objects[index].Attributes = finalAttrs;
|
509
|
+
}
|
510
|
+
catch {
|
511
|
+
group.Objects[index].setProperty(prop, values[index], resultTypeName);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
else {
|
515
|
+
group.Objects[index].setProperty(prop, values[index], resultTypeName);
|
516
|
+
}
|
517
|
+
}
|
518
|
+
},
|
519
|
+
parsePRNT(parser, chunk) {
|
520
|
+
chunk.Byte();
|
521
|
+
const parentCount = chunk.UInt32LE();
|
522
|
+
const childIds = chunk.RBXInterleavedInt32(parentCount, parser.arrays[parser.arrayIndex++]);
|
523
|
+
const parentIds = chunk.RBXInterleavedInt32(parentCount, parser.arrays[parser.arrayIndex++]);
|
524
|
+
var childId = 0;
|
525
|
+
var parentId = 0;
|
526
|
+
for (var i = 0; i < parentCount; i++) {
|
527
|
+
childId += childIds[i];
|
528
|
+
parentId += parentIds[i];
|
529
|
+
const child = parser.instances[childId];
|
530
|
+
if (parentId === -1) {
|
531
|
+
parser.result.push(child);
|
532
|
+
}
|
533
|
+
else {
|
534
|
+
child.setProperty('Parent', parser.instances[parentId], 'Instance');
|
535
|
+
}
|
536
|
+
}
|
537
|
+
}
|
538
|
+
};
|
539
|
+
exports.default = BinaryParser;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
declare class ByteReader extends Uint8Array {
|
2
|
+
index: number;
|
3
|
+
chunkBuffer: Uint8Array;
|
4
|
+
static ParseFloat(long: any): number;
|
5
|
+
static ParseRBXFloat(long: any): number;
|
6
|
+
static ParseDouble(long0: any, long1: any): number;
|
7
|
+
constructor(...args: any[]);
|
8
|
+
SetIndex(n: number): void;
|
9
|
+
GetIndex(): number;
|
10
|
+
GetRemaining(): number;
|
11
|
+
GetLength(): number;
|
12
|
+
Jump(n: number): void;
|
13
|
+
Array(n: number): Uint8Array<ArrayBuffer>;
|
14
|
+
Match(arr: any): boolean;
|
15
|
+
Byte(): number;
|
16
|
+
UInt8(): number;
|
17
|
+
UInt16LE(): number;
|
18
|
+
UInt16BE(): number;
|
19
|
+
UInt32LE(): number;
|
20
|
+
UInt32BE(): number;
|
21
|
+
Int8(): number;
|
22
|
+
Int16LE(): number;
|
23
|
+
Int16BE(): number;
|
24
|
+
Int32LE(): number;
|
25
|
+
Int32BE(): number;
|
26
|
+
FloatLE(): number;
|
27
|
+
FloatBE(): number;
|
28
|
+
DoubleLE(): number;
|
29
|
+
DoubleBE(): number;
|
30
|
+
String(n: number): string;
|
31
|
+
LZ4(buffer: any): any;
|
32
|
+
RBXFloatLE(): number;
|
33
|
+
RBXFloatBE(): number;
|
34
|
+
RBXInterleavedUint32(count: any, result: any): any;
|
35
|
+
RBXInterleavedInt32(count: any, result: any): any;
|
36
|
+
RBXInterleavedFloat(count: any, result: any): any;
|
37
|
+
}
|
38
|
+
export default ByteReader;
|