@etothepii/satisfactory-file-parser 0.0.10 → 0.0.12
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/build/index.d.ts +15 -0
- package/build/index.js +49 -0
- package/build/parser/blueprint-reader.d.ts +18 -0
- package/build/parser/blueprint-reader.js +136 -0
- package/build/parser/blueprint-writer.d.ts +21 -0
- package/build/parser/blueprint-writer.js +69 -0
- package/build/parser/byte/alignment.enum.d.ts +4 -0
- package/build/parser/byte/alignment.enum.js +8 -0
- package/build/parser/byte/byte-reader.class.d.ts +38 -0
- package/build/parser/byte/byte-reader.class.js +132 -0
- package/build/parser/byte/byte-writer.class.d.ts +38 -0
- package/build/parser/byte/byte-writer.class.js +149 -0
- package/build/parser/error/parser.error.d.ts +12 -0
- package/build/parser/error/parser.error.js +28 -0
- package/build/parser/parser.d.ts +44 -0
- package/build/parser/parser.js +84 -0
- package/build/parser/satisfactory/blueprint/blueprint.d.ts +20 -0
- package/build/parser/satisfactory/blueprint/blueprint.js +2 -0
- package/build/parser/satisfactory/level.class.d.ts +21 -0
- package/build/parser/satisfactory/level.class.js +148 -0
- package/build/parser/satisfactory/objects/DataFields.d.ts +13 -0
- package/build/parser/satisfactory/objects/DataFields.js +249 -0
- package/build/parser/satisfactory/objects/ObjectReference.d.ts +9 -0
- package/build/parser/satisfactory/objects/ObjectReference.js +17 -0
- package/build/parser/satisfactory/objects/Property.d.ts +270 -0
- package/build/parser/satisfactory/objects/Property.js +1150 -0
- package/build/parser/satisfactory/objects/SaveComponent.d.ts +16 -0
- package/build/parser/satisfactory/objects/SaveComponent.js +31 -0
- package/build/parser/satisfactory/objects/SaveEntity.d.ts +25 -0
- package/build/parser/satisfactory/objects/SaveEntity.js +70 -0
- package/build/parser/satisfactory/objects/SaveObject.d.ts +14 -0
- package/build/parser/satisfactory/objects/SaveObject.js +29 -0
- package/build/parser/satisfactory/satisfactory-save.d.ts +57 -0
- package/build/parser/satisfactory/satisfactory-save.js +11 -0
- package/build/parser/satisfactory/static-data.d.ts +7 -0
- package/build/parser/satisfactory/static-data.js +2 -0
- package/build/parser/satisfactory/structs/util.types.d.ts +40 -0
- package/build/parser/satisfactory/structs/util.types.js +95 -0
- package/build/parser/save-reader.d.ts +43 -0
- package/build/parser/save-reader.js +251 -0
- package/build/parser/save-writer.d.ts +22 -0
- package/build/parser/save-writer.js +121 -0
- package/package.json +3 -3
- package/index.d.ts +0 -2356
- package/index.js +0 -2364
- package/rollup.config.mjs +0 -11
package/index.js
DELETED
|
@@ -1,2364 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var Pako = require('pako');
|
|
4
|
-
|
|
5
|
-
class ObjectReference {
|
|
6
|
-
constructor(levelName, pathName) {
|
|
7
|
-
this.levelName = levelName;
|
|
8
|
-
this.pathName = pathName;
|
|
9
|
-
}
|
|
10
|
-
static Parse(reader) {
|
|
11
|
-
return new ObjectReference(reader.readString(), reader.readString());
|
|
12
|
-
}
|
|
13
|
-
static Serialize(writer, ref) {
|
|
14
|
-
writer.writeString(ref.levelName);
|
|
15
|
-
writer.writeString(ref.pathName);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const SerializeCol4RGBA = (writer, value) => {
|
|
20
|
-
writer.writeFloat(value.r);
|
|
21
|
-
writer.writeFloat(value.g);
|
|
22
|
-
writer.writeFloat(value.b);
|
|
23
|
-
writer.writeFloat(value.a);
|
|
24
|
-
};
|
|
25
|
-
const ParseCol4RGBA = (reader) => {
|
|
26
|
-
return {
|
|
27
|
-
r: reader.readFloat(),
|
|
28
|
-
g: reader.readFloat(),
|
|
29
|
-
b: reader.readFloat(),
|
|
30
|
-
a: reader.readFloat(),
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
const SerializeCol4BGRA = (writer, value) => {
|
|
34
|
-
writer.writeByte(value.b);
|
|
35
|
-
writer.writeByte(value.g);
|
|
36
|
-
writer.writeByte(value.r);
|
|
37
|
-
writer.writeByte(value.a);
|
|
38
|
-
};
|
|
39
|
-
const ParseCol4BGRA = (reader) => {
|
|
40
|
-
return {
|
|
41
|
-
b: reader.readByte(),
|
|
42
|
-
g: reader.readByte(),
|
|
43
|
-
r: reader.readByte(),
|
|
44
|
-
a: reader.readByte(),
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
const ParseVec4 = (reader) => {
|
|
48
|
-
return {
|
|
49
|
-
...ParseVec3(reader),
|
|
50
|
-
w: reader.readFloat()
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const SerializeVec4 = (writer, vec) => {
|
|
54
|
-
SerializeVec3(writer, vec);
|
|
55
|
-
writer.writeFloat(vec.w);
|
|
56
|
-
};
|
|
57
|
-
const ParseVec3 = (reader) => {
|
|
58
|
-
return {
|
|
59
|
-
...ParseVec2(reader),
|
|
60
|
-
z: reader.readFloat()
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
const SerializeVec3 = (writer, vec) => {
|
|
64
|
-
SerializeVec2(writer, vec);
|
|
65
|
-
writer.writeFloat(vec.z);
|
|
66
|
-
};
|
|
67
|
-
const ParseVec2 = (reader) => {
|
|
68
|
-
return {
|
|
69
|
-
x: reader.readFloat(),
|
|
70
|
-
y: reader.readFloat(),
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
const SerializeVec2 = (writer, vec) => {
|
|
74
|
-
writer.writeFloat(vec.x);
|
|
75
|
-
writer.writeFloat(vec.y);
|
|
76
|
-
};
|
|
77
|
-
const ParseTransform = (reader) => {
|
|
78
|
-
return {
|
|
79
|
-
rotation: ParseVec4(reader),
|
|
80
|
-
translation: ParseVec3(reader),
|
|
81
|
-
scale3d: ParseVec3(reader),
|
|
82
|
-
};
|
|
83
|
-
};
|
|
84
|
-
const SerializeTransform = (writer, transform) => {
|
|
85
|
-
SerializeVec4(writer, transform.rotation);
|
|
86
|
-
SerializeVec3(writer, transform.translation);
|
|
87
|
-
SerializeVec3(writer, transform.scale3d);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
class AbstractProperty {
|
|
91
|
-
constructor(type, index) {
|
|
92
|
-
this.type = type;
|
|
93
|
-
this.index = index;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
class AbstractBaseProperty extends AbstractProperty {
|
|
97
|
-
// overhead like Guid is not calculated into property size
|
|
98
|
-
constructor(type, ueType, index) {
|
|
99
|
-
super(type, index && index !== 0 ? index : undefined);
|
|
100
|
-
this.ueType = ueType;
|
|
101
|
-
this.name = '';
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
class BasicProperty extends AbstractBaseProperty {
|
|
105
|
-
constructor(type, ueType, guidInfo, index = 0) {
|
|
106
|
-
super(type, ueType, index);
|
|
107
|
-
this.guidInfo = guidInfo;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const ParseGUID = (reader) => {
|
|
111
|
-
const hasGuid = reader.readByte() === 1;
|
|
112
|
-
let guid;
|
|
113
|
-
if (hasGuid) {
|
|
114
|
-
throw new Error('Unexpected!');
|
|
115
|
-
}
|
|
116
|
-
return guid;
|
|
117
|
-
};
|
|
118
|
-
const SerializeGUID = (writer, guid) => {
|
|
119
|
-
writer.writeByte(guid ? 1 : 0);
|
|
120
|
-
if (guid) {
|
|
121
|
-
writer.writeBytes(guid);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
class BoolProperty extends BasicProperty {
|
|
125
|
-
constructor(value, ueType = 'BoolProperty', guidInfo = undefined, index = 0) {
|
|
126
|
-
super('BoolProperty', ueType, guidInfo, index);
|
|
127
|
-
this.value = value;
|
|
128
|
-
}
|
|
129
|
-
static Parse(reader, ueType, index = 0) {
|
|
130
|
-
const value = BoolProperty.ReadValue(reader);
|
|
131
|
-
const guidInfo = ParseGUID(reader);
|
|
132
|
-
return new BoolProperty(value, ueType, guidInfo, index);
|
|
133
|
-
}
|
|
134
|
-
static ReadValue(reader) {
|
|
135
|
-
return reader.readByte() > 0;
|
|
136
|
-
}
|
|
137
|
-
static CalcOverhead(property) {
|
|
138
|
-
return 2;
|
|
139
|
-
}
|
|
140
|
-
static Serialize(writer, property) {
|
|
141
|
-
BoolProperty.SerializeValue(writer, property.value);
|
|
142
|
-
SerializeGUID(writer, property.guidInfo);
|
|
143
|
-
}
|
|
144
|
-
static SerializeValue(writer, value) {
|
|
145
|
-
writer.writeByte(value ? 1 : 0);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
class ByteProperty extends BasicProperty {
|
|
149
|
-
constructor(value, ueType = 'ByteProperty', guidInfo = undefined, index = 0) {
|
|
150
|
-
super('ByteProperty', ueType, guidInfo, index);
|
|
151
|
-
this.value = value;
|
|
152
|
-
}
|
|
153
|
-
static Parse(reader, ueType, index = 0) {
|
|
154
|
-
const type = reader.readString();
|
|
155
|
-
const guidInfo = ParseGUID(reader);
|
|
156
|
-
let value;
|
|
157
|
-
if (type === 'None') {
|
|
158
|
-
value = {
|
|
159
|
-
type,
|
|
160
|
-
value: ByteProperty.ReadValue(reader)
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
value = {
|
|
165
|
-
type,
|
|
166
|
-
value: reader.readString()
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
const property = new ByteProperty(value, ueType, guidInfo, index);
|
|
170
|
-
return property;
|
|
171
|
-
}
|
|
172
|
-
static ReadValue(reader) {
|
|
173
|
-
return reader.readByte();
|
|
174
|
-
}
|
|
175
|
-
static CalcOverhead(property) {
|
|
176
|
-
return property.value.type.length + 5 + 1;
|
|
177
|
-
}
|
|
178
|
-
static Serialize(writer, property) {
|
|
179
|
-
writer.writeString(property.value.type);
|
|
180
|
-
SerializeGUID(writer, property.guidInfo);
|
|
181
|
-
if (property.value.type === 'None') {
|
|
182
|
-
ByteProperty.SerializeValue(writer, property.value.value);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
writer.writeString(property.value.value);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
static SerializeValue(writer, value) {
|
|
189
|
-
writer.writeByte(value);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
class Int8Property extends BasicProperty {
|
|
193
|
-
constructor(value, ueType = 'Int8Property', guidInfo = undefined, index = 0) {
|
|
194
|
-
super('Int8Property', ueType, guidInfo, index);
|
|
195
|
-
this.value = value;
|
|
196
|
-
}
|
|
197
|
-
static Parse(reader, ueType, index = 0) {
|
|
198
|
-
const guidInfo = ParseGUID(reader);
|
|
199
|
-
const value = Int8Property.ReadValue(reader);
|
|
200
|
-
return new Int8Property(value, ueType, guidInfo, index);
|
|
201
|
-
}
|
|
202
|
-
static ReadValue(reader) {
|
|
203
|
-
return reader.readInt8();
|
|
204
|
-
}
|
|
205
|
-
static CalcOverhead(property) {
|
|
206
|
-
return 1;
|
|
207
|
-
}
|
|
208
|
-
static Serialize(writer, property) {
|
|
209
|
-
SerializeGUID(writer, property.guidInfo);
|
|
210
|
-
Int8Property.SerializeValue(writer, property.value);
|
|
211
|
-
}
|
|
212
|
-
static SerializeValue(writer, value) {
|
|
213
|
-
writer.writeInt8(value);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
class Uint8Property extends BasicProperty {
|
|
217
|
-
constructor(value, ueType = 'UInt8Property', guidInfo = undefined, index = 0) {
|
|
218
|
-
super('UInt8Property', ueType, guidInfo, index);
|
|
219
|
-
this.value = value;
|
|
220
|
-
}
|
|
221
|
-
static Parse(reader, ueType, index = 0) {
|
|
222
|
-
const guidInfo = ParseGUID(reader);
|
|
223
|
-
const value = Uint8Property.ReadValue(reader);
|
|
224
|
-
return new Uint8Property(value, ueType, guidInfo, index);
|
|
225
|
-
}
|
|
226
|
-
static ReadValue(reader) {
|
|
227
|
-
return reader.readUint8();
|
|
228
|
-
}
|
|
229
|
-
static CalcOverhead(property) {
|
|
230
|
-
throw new Error('Unimplemented.');
|
|
231
|
-
}
|
|
232
|
-
static Serialize(writer, property) {
|
|
233
|
-
SerializeGUID(writer, property.guidInfo);
|
|
234
|
-
Uint8Property.SerializeValue(writer, property.value);
|
|
235
|
-
}
|
|
236
|
-
static SerializeValue(writer, value) {
|
|
237
|
-
writer.writeUint8(value);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
class Int32Property extends BasicProperty {
|
|
241
|
-
constructor(value, ueType = 'IntProperty', guidInfo = undefined, index = 0) {
|
|
242
|
-
super('Int32Property', ueType, guidInfo, index);
|
|
243
|
-
this.value = value;
|
|
244
|
-
}
|
|
245
|
-
static Parse(reader, ueType, index = 0) {
|
|
246
|
-
const guidInfo = ParseGUID(reader);
|
|
247
|
-
const value = Int32Property.ReadValue(reader);
|
|
248
|
-
return new Int32Property(value, ueType, guidInfo, index);
|
|
249
|
-
}
|
|
250
|
-
static ReadValue(reader) {
|
|
251
|
-
return reader.readInt32();
|
|
252
|
-
}
|
|
253
|
-
static CalcOverhead(property) {
|
|
254
|
-
return 1;
|
|
255
|
-
}
|
|
256
|
-
static Serialize(writer, property) {
|
|
257
|
-
SerializeGUID(writer, property.guidInfo);
|
|
258
|
-
Int32Property.SerializeValue(writer, property.value);
|
|
259
|
-
}
|
|
260
|
-
static SerializeValue(writer, value) {
|
|
261
|
-
writer.writeInt32(value);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
class Uint32Property extends BasicProperty {
|
|
265
|
-
constructor(value, ueType = 'UInt32Property', guidInfo = undefined, index = 0) {
|
|
266
|
-
super('UInt32Property', ueType, guidInfo, index);
|
|
267
|
-
this.value = value;
|
|
268
|
-
}
|
|
269
|
-
static Parse(reader, ueType, index = 0) {
|
|
270
|
-
const guidInfo = ParseGUID(reader);
|
|
271
|
-
const value = Uint32Property.ReadValue(reader);
|
|
272
|
-
return new Uint32Property(value, ueType, guidInfo, index);
|
|
273
|
-
}
|
|
274
|
-
static ReadValue(reader) {
|
|
275
|
-
return reader.readUint32();
|
|
276
|
-
}
|
|
277
|
-
static CalcOverhead(property) {
|
|
278
|
-
throw new Error('unimplemented');
|
|
279
|
-
}
|
|
280
|
-
static Serialize(writer, property) {
|
|
281
|
-
SerializeGUID(writer, property.guidInfo);
|
|
282
|
-
Uint32Property.SerializeValue(writer, property.value);
|
|
283
|
-
}
|
|
284
|
-
static SerializeValue(writer, value) {
|
|
285
|
-
writer.writeUint32(value);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
class Int64Property extends BasicProperty {
|
|
289
|
-
constructor(value, ueType = 'Int64Property', guidInfo = undefined, index = 0) {
|
|
290
|
-
super('Int64Property', ueType, guidInfo, index);
|
|
291
|
-
this.value = value;
|
|
292
|
-
}
|
|
293
|
-
static Parse(reader, ueType, index = 0) {
|
|
294
|
-
const guidInfo = ParseGUID(reader);
|
|
295
|
-
const value = Int64Property.ReadValue(reader);
|
|
296
|
-
return new Int64Property(value, ueType, guidInfo, index);
|
|
297
|
-
}
|
|
298
|
-
static ReadValue(reader) {
|
|
299
|
-
return reader.readLong().toString();
|
|
300
|
-
}
|
|
301
|
-
static CalcOverhead(property) {
|
|
302
|
-
return 1;
|
|
303
|
-
}
|
|
304
|
-
static Serialize(writer, property) {
|
|
305
|
-
SerializeGUID(writer, property.guidInfo);
|
|
306
|
-
Int64Property.SerializeValue(writer, property.value);
|
|
307
|
-
}
|
|
308
|
-
static SerializeValue(writer, value) {
|
|
309
|
-
writer.writeInt64(BigInt(value));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
class FloatProperty extends BasicProperty {
|
|
313
|
-
constructor(value, ueType = 'FloatProperty', guidInfo = undefined, index = 0) {
|
|
314
|
-
super('FloatProperty', ueType, guidInfo, index);
|
|
315
|
-
this.value = value;
|
|
316
|
-
}
|
|
317
|
-
static Parse(reader, ueType, index = 0) {
|
|
318
|
-
const guidInfo = ParseGUID(reader);
|
|
319
|
-
const value = FloatProperty.ReadValue(reader);
|
|
320
|
-
return new FloatProperty(value, ueType, guidInfo, index);
|
|
321
|
-
}
|
|
322
|
-
static CalcOverhead(property) {
|
|
323
|
-
return 1;
|
|
324
|
-
}
|
|
325
|
-
static ReadValue(reader) {
|
|
326
|
-
return reader.readFloat();
|
|
327
|
-
}
|
|
328
|
-
static Serialize(writer, property) {
|
|
329
|
-
SerializeGUID(writer, property.guidInfo);
|
|
330
|
-
FloatProperty.SerializeValue(writer, property.value);
|
|
331
|
-
}
|
|
332
|
-
static SerializeValue(writer, value) {
|
|
333
|
-
writer.writeFloat(value);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
class DoubleProperty extends BasicProperty {
|
|
337
|
-
constructor(value, ueType = 'DoubleProperty', guidInfo = undefined, index = 0) {
|
|
338
|
-
super('DoubleProperty', ueType, guidInfo, index);
|
|
339
|
-
this.value = value;
|
|
340
|
-
}
|
|
341
|
-
static Parse(reader, ueType, index = 0) {
|
|
342
|
-
const guidInfo = ParseGUID(reader);
|
|
343
|
-
const value = DoubleProperty.ReadValue(reader);
|
|
344
|
-
return new DoubleProperty(value, ueType, guidInfo, index);
|
|
345
|
-
}
|
|
346
|
-
static ReadValue(reader) {
|
|
347
|
-
return reader.readDouble();
|
|
348
|
-
}
|
|
349
|
-
static CalcOverhead(property) {
|
|
350
|
-
return 1;
|
|
351
|
-
}
|
|
352
|
-
static Serialize(writer, property) {
|
|
353
|
-
SerializeGUID(writer, property.guidInfo);
|
|
354
|
-
DoubleProperty.SerializeValue(writer, property.value);
|
|
355
|
-
}
|
|
356
|
-
static SerializeValue(writer, value) {
|
|
357
|
-
writer.writeDouble(value);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
class StrProperty extends BasicProperty {
|
|
361
|
-
constructor(value, ueType = 'StrProperty', guidInfo = undefined, index = 0) {
|
|
362
|
-
super('StrProperty', ueType, guidInfo, index);
|
|
363
|
-
this.value = value;
|
|
364
|
-
}
|
|
365
|
-
static Parse(reader, ueType, index = 0) {
|
|
366
|
-
const guidInfo = ParseGUID(reader);
|
|
367
|
-
const value = StrProperty.ReadValue(reader);
|
|
368
|
-
return new StrProperty(value, ueType, guidInfo, index);
|
|
369
|
-
}
|
|
370
|
-
static ReadValue(reader) {
|
|
371
|
-
return reader.readString();
|
|
372
|
-
}
|
|
373
|
-
static CalcOverhead(property) {
|
|
374
|
-
return 1;
|
|
375
|
-
}
|
|
376
|
-
static Serialize(writer, property) {
|
|
377
|
-
SerializeGUID(writer, property.guidInfo);
|
|
378
|
-
StrProperty.SerializeValue(writer, property.value);
|
|
379
|
-
}
|
|
380
|
-
static SerializeValue(writer, value) {
|
|
381
|
-
writer.writeString(value);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
class ObjectProperty extends BasicProperty {
|
|
385
|
-
constructor(value, ueType = 'ObjectProperty', guidInfo = undefined, index = 0) {
|
|
386
|
-
super('ObjectProperty', ueType, guidInfo, index);
|
|
387
|
-
this.value = value;
|
|
388
|
-
}
|
|
389
|
-
static Parse(reader, ueType, index = 0) {
|
|
390
|
-
const guidInfo = ParseGUID(reader);
|
|
391
|
-
const value = ObjectProperty.ReadValue(reader);
|
|
392
|
-
return new ObjectProperty(value, ueType, guidInfo, index);
|
|
393
|
-
}
|
|
394
|
-
static ReadValue(reader) {
|
|
395
|
-
const x = {
|
|
396
|
-
levelName: reader.readString(),
|
|
397
|
-
pathName: reader.readString()
|
|
398
|
-
};
|
|
399
|
-
return x;
|
|
400
|
-
}
|
|
401
|
-
static CalcOverhead(property) {
|
|
402
|
-
return 1;
|
|
403
|
-
}
|
|
404
|
-
static Serialize(writer, property) {
|
|
405
|
-
SerializeGUID(writer, property.guidInfo);
|
|
406
|
-
ObjectProperty.SerializeValue(writer, property.value);
|
|
407
|
-
}
|
|
408
|
-
static SerializeValue(writer, value) {
|
|
409
|
-
writer.writeString(value.levelName);
|
|
410
|
-
writer.writeString(value.pathName);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
class EnumProperty extends BasicProperty {
|
|
414
|
-
constructor(value, ueType = 'EnumProperty', guidInfo = undefined, index = 0) {
|
|
415
|
-
super('EnumProperty', ueType, guidInfo, index);
|
|
416
|
-
this.value = value;
|
|
417
|
-
}
|
|
418
|
-
static Parse(reader, ueType, index = 0) {
|
|
419
|
-
let name = reader.readString();
|
|
420
|
-
const guidInfo = ParseGUID(reader);
|
|
421
|
-
const value = EnumProperty.ReadValue(reader);
|
|
422
|
-
const property = new EnumProperty({ name, value }, ueType, guidInfo, index);
|
|
423
|
-
return property;
|
|
424
|
-
}
|
|
425
|
-
static ReadValue(reader) {
|
|
426
|
-
return reader.readString();
|
|
427
|
-
}
|
|
428
|
-
static CalcOverhead(property) {
|
|
429
|
-
return property.value.name.length + 6;
|
|
430
|
-
}
|
|
431
|
-
static Serialize(writer, property) {
|
|
432
|
-
writer.writeString(property.value.name);
|
|
433
|
-
SerializeGUID(writer, property.guidInfo);
|
|
434
|
-
EnumProperty.SerializeValue(writer, property.value.value);
|
|
435
|
-
}
|
|
436
|
-
static SerializeValue(writer, value) {
|
|
437
|
-
writer.writeString(value);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
class TextProperty extends BasicProperty {
|
|
441
|
-
constructor(value, ueType = 'TextProperty', guidInfo = undefined, index = 0) {
|
|
442
|
-
super('TextProperty', ueType, guidInfo, index);
|
|
443
|
-
this.value = value;
|
|
444
|
-
}
|
|
445
|
-
static Parse(reader, ueType, index = 0) {
|
|
446
|
-
//let name = reader.readString();
|
|
447
|
-
const guidInfo = ParseGUID(reader);
|
|
448
|
-
const value = TextProperty.ParseValue(reader);
|
|
449
|
-
return new TextProperty(value, ueType, guidInfo, index);
|
|
450
|
-
}
|
|
451
|
-
static ParseValue(reader) {
|
|
452
|
-
const prop = {
|
|
453
|
-
flags: reader.readInt32(),
|
|
454
|
-
historyType: reader.readByte()
|
|
455
|
-
};
|
|
456
|
-
switch (prop.historyType) {
|
|
457
|
-
// HISTORYTYPE_BASE
|
|
458
|
-
case 0:
|
|
459
|
-
prop.namespace = reader.readString();
|
|
460
|
-
prop.key = reader.readString();
|
|
461
|
-
prop.value = reader.readString();
|
|
462
|
-
break;
|
|
463
|
-
// HISTORYTYPE_NAMEDFORMAT
|
|
464
|
-
case 1:
|
|
465
|
-
// HISTORYTYPE_ARGUMENTFORMAT
|
|
466
|
-
case 3:
|
|
467
|
-
prop.sourceFmt = TextProperty.ParseValue(reader);
|
|
468
|
-
const argumentsCount = reader.readInt32();
|
|
469
|
-
prop.arguments = [];
|
|
470
|
-
for (let i = 0; i < argumentsCount; i++) {
|
|
471
|
-
let currentArgumentsData = {};
|
|
472
|
-
currentArgumentsData.name = reader.readString();
|
|
473
|
-
currentArgumentsData.valueType = reader.readByte();
|
|
474
|
-
switch (currentArgumentsData.valueType) {
|
|
475
|
-
case 4:
|
|
476
|
-
currentArgumentsData.argumentValue = TextProperty.ParseValue(reader);
|
|
477
|
-
break;
|
|
478
|
-
default:
|
|
479
|
-
throw new Error('Unimplemented FormatArgumentType `' + currentArgumentsData.valueType);
|
|
480
|
-
}
|
|
481
|
-
prop.arguments.push(currentArgumentsData);
|
|
482
|
-
}
|
|
483
|
-
break;
|
|
484
|
-
// see https://github.com/EpicGames/UnrealEngine/blob/4.25/Engine/Source/Runtime/Core/Private/Internationalization/TextHistory.cpp#L2268
|
|
485
|
-
// HISTORYTYPE_TRANSFORM
|
|
486
|
-
case 10:
|
|
487
|
-
prop.sourceText = TextProperty.ParseValue(reader);
|
|
488
|
-
prop.transformType = reader.readByte();
|
|
489
|
-
break;
|
|
490
|
-
// HISTORYTYPE_NONE
|
|
491
|
-
case 255:
|
|
492
|
-
// See: https://github.com/EpicGames/UnrealEngine/blob/4.25/Engine/Source/Runtime/Core/Private/Internationalization/Text.cpp#L894
|
|
493
|
-
// TODO: do i need that? check with goz3rr
|
|
494
|
-
//if (this.header!.buildVersion >= 140822) {
|
|
495
|
-
prop.hasCultureInvariantString = reader.readInt32() === 1;
|
|
496
|
-
if (prop.hasCultureInvariantString) {
|
|
497
|
-
prop.value = reader.readString();
|
|
498
|
-
}
|
|
499
|
-
//}
|
|
500
|
-
break;
|
|
501
|
-
default:
|
|
502
|
-
throw new Error('Unimplemented historyType `' + prop.historyType);
|
|
503
|
-
}
|
|
504
|
-
return prop;
|
|
505
|
-
}
|
|
506
|
-
static CalcOverhead(property) {
|
|
507
|
-
return 1;
|
|
508
|
-
}
|
|
509
|
-
static Serialize(writer, property) {
|
|
510
|
-
SerializeGUID(writer, property.guidInfo);
|
|
511
|
-
TextProperty.SerializeValue(writer, property.value);
|
|
512
|
-
}
|
|
513
|
-
static SerializeValue(writer, value) {
|
|
514
|
-
writer.writeInt32(value.flags);
|
|
515
|
-
writer.writeByte(value.historyType);
|
|
516
|
-
switch (value.historyType) {
|
|
517
|
-
// HISTORYTYPE_BASE
|
|
518
|
-
case 0:
|
|
519
|
-
writer.writeString(value.namespace);
|
|
520
|
-
writer.writeString(value.key);
|
|
521
|
-
writer.writeString(value.value);
|
|
522
|
-
break;
|
|
523
|
-
// HISTORYTYPE_NAMEDFORMAT
|
|
524
|
-
case 1:
|
|
525
|
-
// HISTORYTYPE_ARGUMENTFORMAT
|
|
526
|
-
case 3:
|
|
527
|
-
TextProperty.SerializeValue(writer, value.sourceFmt);
|
|
528
|
-
writer.writeInt32(value.arguments.length);
|
|
529
|
-
for (const arg of value.arguments) {
|
|
530
|
-
let currentArgumentsData = {};
|
|
531
|
-
writer.writeString(arg.name);
|
|
532
|
-
writer.writeByte(arg.valueType);
|
|
533
|
-
switch (arg.valueType) {
|
|
534
|
-
case 4:
|
|
535
|
-
TextProperty.SerializeValue(writer, arg.argumentValue);
|
|
536
|
-
break;
|
|
537
|
-
default:
|
|
538
|
-
throw new Error('Unimplemented FormatArgumentType `' + currentArgumentsData.valueType);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
// see https://github.com/EpicGames/UnrealEngine/blob/4.25/Engine/Source/Runtime/Core/Private/Internationalization/TextHistory.cpp#L2268
|
|
543
|
-
// HISTORYTYPE_TRANSFORM
|
|
544
|
-
case 10:
|
|
545
|
-
TextProperty.SerializeValue(writer, value.sourceText);
|
|
546
|
-
writer.writeByte(value.transformType);
|
|
547
|
-
break;
|
|
548
|
-
// HISTORYTYPE_NONE
|
|
549
|
-
case 255:
|
|
550
|
-
// See: https://github.com/EpicGames/UnrealEngine/blob/4.25/Engine/Source/Runtime/Core/Private/Internationalization/Text.cpp#L894
|
|
551
|
-
// TODO: do i need that? check with goz3rr
|
|
552
|
-
//if (this.header!.buildVersion >= 140822) {
|
|
553
|
-
writer.writeInt32(value.hasCultureInvariantString ? 1 : 0);
|
|
554
|
-
if (value.hasCultureInvariantString) {
|
|
555
|
-
writer.writeString(value.value);
|
|
556
|
-
}
|
|
557
|
-
//}
|
|
558
|
-
break;
|
|
559
|
-
default:
|
|
560
|
-
throw new Error('Unimplemented historyType `' + value.historyType);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
class StructProperty extends AbstractBaseProperty {
|
|
565
|
-
constructor(subtype, ueType = 'StructProperty', index = 0, guid = 0) {
|
|
566
|
-
super('StructProperty', ueType, index);
|
|
567
|
-
this.subtype = subtype;
|
|
568
|
-
this.guid = guid;
|
|
569
|
-
this.value = { values: {} };
|
|
570
|
-
}
|
|
571
|
-
static Parse(reader, ueType, index, size) {
|
|
572
|
-
const struct = new StructProperty(reader.readString(), ueType, index, reader.readInt32());
|
|
573
|
-
const unk1 = reader.readInt32();
|
|
574
|
-
if (unk1 !== 0) {
|
|
575
|
-
struct.unk1 = unk1;
|
|
576
|
-
}
|
|
577
|
-
const unk2 = reader.readInt32();
|
|
578
|
-
if (unk2 !== 0) {
|
|
579
|
-
struct.unk2 = unk2;
|
|
580
|
-
}
|
|
581
|
-
const unk3 = reader.readInt32();
|
|
582
|
-
if (unk2 !== 0) {
|
|
583
|
-
struct.unk3 = unk3;
|
|
584
|
-
}
|
|
585
|
-
const unk4 = reader.readByte();
|
|
586
|
-
if (unk4 !== 0) {
|
|
587
|
-
struct.unk4 = unk4;
|
|
588
|
-
}
|
|
589
|
-
const before = reader.getBufferPosition();
|
|
590
|
-
struct.value = StructProperty.ParseValue(reader, struct.subtype, size);
|
|
591
|
-
const readBytes = reader.getBufferPosition() - before;
|
|
592
|
-
if (readBytes !== size) {
|
|
593
|
-
throw new Error(`possibly corrupt. Read ${readBytes} for StructProperty Content of ${struct.subtype}, but ${size} were indicated.`);
|
|
594
|
-
}
|
|
595
|
-
return struct;
|
|
596
|
-
}
|
|
597
|
-
static ParseValue(reader, subtype, size) {
|
|
598
|
-
let value;
|
|
599
|
-
switch (subtype) {
|
|
600
|
-
case 'Color':
|
|
601
|
-
value = ParseCol4BGRA(reader);
|
|
602
|
-
break;
|
|
603
|
-
case 'LinearColor':
|
|
604
|
-
value = ParseCol4RGBA(reader);
|
|
605
|
-
break;
|
|
606
|
-
case 'Vector':
|
|
607
|
-
case 'Rotator':
|
|
608
|
-
case 'Vector2D':
|
|
609
|
-
value = ParseVec3(reader);
|
|
610
|
-
break;
|
|
611
|
-
case 'Quat':
|
|
612
|
-
case 'Vector4':
|
|
613
|
-
case 'Vector4D':
|
|
614
|
-
value = ParseVec4(reader);
|
|
615
|
-
break;
|
|
616
|
-
case 'Box':
|
|
617
|
-
value = {
|
|
618
|
-
min: ParseVec3(reader),
|
|
619
|
-
max: ParseVec3(reader),
|
|
620
|
-
isValid: reader.readByte() >= 1
|
|
621
|
-
};
|
|
622
|
-
break;
|
|
623
|
-
case 'RailroadTrackPosition':
|
|
624
|
-
value = {
|
|
625
|
-
root: reader.readString(),
|
|
626
|
-
instanceName: reader.readString(),
|
|
627
|
-
offset: reader.readFloat(),
|
|
628
|
-
forward: reader.readFloat()
|
|
629
|
-
};
|
|
630
|
-
break;
|
|
631
|
-
case 'TimerHandle':
|
|
632
|
-
value = reader.readString();
|
|
633
|
-
break;
|
|
634
|
-
case 'Guid':
|
|
635
|
-
value = reader.readString();
|
|
636
|
-
break;
|
|
637
|
-
case 'InventoryItem':
|
|
638
|
-
value = {
|
|
639
|
-
unk1: reader.readInt32(),
|
|
640
|
-
itemName: reader.readString(),
|
|
641
|
-
unk2: reader.readString(),
|
|
642
|
-
unk3: reader.readString(),
|
|
643
|
-
properties: [],
|
|
644
|
-
};
|
|
645
|
-
//TODO - what is this? currentProperty.value.properties.push(reader.readProperty());
|
|
646
|
-
break;
|
|
647
|
-
case 'FluidBox':
|
|
648
|
-
value = {
|
|
649
|
-
value: reader.readFloat()
|
|
650
|
-
};
|
|
651
|
-
break;
|
|
652
|
-
case 'SlateBrush':
|
|
653
|
-
value = reader.readString();
|
|
654
|
-
break;
|
|
655
|
-
case 'DateTime':
|
|
656
|
-
value = reader.readLong().toString();
|
|
657
|
-
break;
|
|
658
|
-
// MODS
|
|
659
|
-
case 'FINNetworkTrace':
|
|
660
|
-
value = ReadFINNetworkTrace(reader);
|
|
661
|
-
break;
|
|
662
|
-
case 'FINLuaProcessorStateStorage':
|
|
663
|
-
value = {
|
|
664
|
-
values: ReadFINLuaProcessorStateStorage(reader, size)
|
|
665
|
-
};
|
|
666
|
-
break;
|
|
667
|
-
case 'FICFrameRange': // https://github.com/Panakotta00/FicsIt-Cam/blob/c55e254a84722c56e1badabcfaef1159cd7d2ef1/Source/FicsItCam/Public/Data/FICTypes.h#L34
|
|
668
|
-
value = {
|
|
669
|
-
begin: reader.readLong().toString(),
|
|
670
|
-
end: reader.readLong().toString(),
|
|
671
|
-
};
|
|
672
|
-
break;
|
|
673
|
-
default:
|
|
674
|
-
//TODO: use buildversion
|
|
675
|
-
value = ParseDynamicStructData(reader, 0, subtype);
|
|
676
|
-
}
|
|
677
|
-
return value;
|
|
678
|
-
}
|
|
679
|
-
static CalcOverhead(property) {
|
|
680
|
-
return property.subtype.length + 5 + 17;
|
|
681
|
-
}
|
|
682
|
-
static Serialize(writer, property) {
|
|
683
|
-
writer.writeString(property.subtype);
|
|
684
|
-
writer.writeInt32(property.guid);
|
|
685
|
-
writer.writeInt32(property.unk1 ?? 0);
|
|
686
|
-
writer.writeInt32(property.unk2 ?? 0);
|
|
687
|
-
writer.writeInt32(property.unk3 ?? 0);
|
|
688
|
-
writer.writeByte(property.unk4 ?? 0);
|
|
689
|
-
StructProperty.SerializeValue(writer, property.subtype, property.value);
|
|
690
|
-
}
|
|
691
|
-
static SerializeValue(writer, subtype, value) {
|
|
692
|
-
switch (subtype) {
|
|
693
|
-
case 'Color':
|
|
694
|
-
value = value;
|
|
695
|
-
SerializeCol4BGRA(writer, value);
|
|
696
|
-
break;
|
|
697
|
-
case 'LinearColor':
|
|
698
|
-
value = value;
|
|
699
|
-
SerializeCol4RGBA(writer, value);
|
|
700
|
-
break;
|
|
701
|
-
case 'Vector':
|
|
702
|
-
case 'Rotator':
|
|
703
|
-
case 'Vector2D':
|
|
704
|
-
value = value;
|
|
705
|
-
SerializeVec3(writer, value);
|
|
706
|
-
break;
|
|
707
|
-
case 'Quat':
|
|
708
|
-
case 'Vector4':
|
|
709
|
-
case 'Vector4D':
|
|
710
|
-
value = value;
|
|
711
|
-
SerializeVec4(writer, value);
|
|
712
|
-
break;
|
|
713
|
-
case 'Box':
|
|
714
|
-
value = value;
|
|
715
|
-
SerializeVec3(writer, value.min);
|
|
716
|
-
SerializeVec3(writer, value.max);
|
|
717
|
-
writer.writeByte(value.isValid ? 1 : 0);
|
|
718
|
-
break;
|
|
719
|
-
case 'RailroadTrackPosition':
|
|
720
|
-
value = value;
|
|
721
|
-
writer.writeString(value.root);
|
|
722
|
-
writer.writeString(value.instanceName);
|
|
723
|
-
writer.writeFloat(value.offset);
|
|
724
|
-
writer.writeFloat(value.forward);
|
|
725
|
-
break;
|
|
726
|
-
case 'TimerHandle':
|
|
727
|
-
value = value;
|
|
728
|
-
writer.writeString(value);
|
|
729
|
-
break;
|
|
730
|
-
case 'Guid':
|
|
731
|
-
value = value;
|
|
732
|
-
writer.writeString(value);
|
|
733
|
-
break;
|
|
734
|
-
case 'InventoryItem':
|
|
735
|
-
value = value;
|
|
736
|
-
writer.writeInt32(value.unk1);
|
|
737
|
-
writer.writeString(value.itemName);
|
|
738
|
-
writer.writeString(value.unk2);
|
|
739
|
-
writer.writeString(value.unk3);
|
|
740
|
-
//TODO - how to write properties?
|
|
741
|
-
break;
|
|
742
|
-
case 'FluidBox':
|
|
743
|
-
value = value;
|
|
744
|
-
writer.writeFloat(value.value);
|
|
745
|
-
break;
|
|
746
|
-
case 'SlateBrush':
|
|
747
|
-
value = value;
|
|
748
|
-
writer.writeString(value);
|
|
749
|
-
break;
|
|
750
|
-
case 'DateTime':
|
|
751
|
-
value = value;
|
|
752
|
-
writer.writeInt64(BigInt(value));
|
|
753
|
-
break;
|
|
754
|
-
// MODS
|
|
755
|
-
case 'FINNetworkTrace':
|
|
756
|
-
value = value;
|
|
757
|
-
SerializeFINNetworkTrace(writer, value);
|
|
758
|
-
break;
|
|
759
|
-
case 'FINLuaProcessorStateStorage':
|
|
760
|
-
value = value;
|
|
761
|
-
SerializeFINLuaProcessorStateStorage(writer, value.values);
|
|
762
|
-
break;
|
|
763
|
-
case 'FICFrameRange': // https://github.com/Panakotta00/FicsIt-Cam/blob/c55e254a84722c56e1badabcfaef1159cd7d2ef1/Source/FicsItCam/Public/Data/FICTypes.h#L34
|
|
764
|
-
value = value;
|
|
765
|
-
writer.writeInt64(BigInt(value.begin));
|
|
766
|
-
writer.writeInt64(BigInt(value.end));
|
|
767
|
-
break;
|
|
768
|
-
default:
|
|
769
|
-
//TODO: use buildversion
|
|
770
|
-
value = value;
|
|
771
|
-
SerializeDynamicStructData(writer, 0, value);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
class ArrayProperty extends BasicProperty {
|
|
776
|
-
constructor(subtype, values, ueType = 'ArrayProperty', index = 0, structValueFields) {
|
|
777
|
-
super('ArrayProperty', ueType, undefined, index);
|
|
778
|
-
this.subtype = subtype;
|
|
779
|
-
this.values = values;
|
|
780
|
-
this.structValueFields = structValueFields;
|
|
781
|
-
}
|
|
782
|
-
static Parse(reader, ueType, index, propertyName) {
|
|
783
|
-
const subtype = reader.readString();
|
|
784
|
-
reader.skipBytes(); // unk
|
|
785
|
-
let property;
|
|
786
|
-
const elementCount = reader.readInt32();
|
|
787
|
-
switch (subtype) {
|
|
788
|
-
case "FloatProperty":
|
|
789
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => FloatProperty.ReadValue(reader)), ueType, index);
|
|
790
|
-
break;
|
|
791
|
-
case "BoolProperty":
|
|
792
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => BoolProperty.ReadValue(reader)), ueType, index);
|
|
793
|
-
break;
|
|
794
|
-
case "IntProperty":
|
|
795
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => Int32Property.ReadValue(reader)), ueType, index);
|
|
796
|
-
break;
|
|
797
|
-
case "Int64Property":
|
|
798
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => Int64Property.ReadValue(reader)), ueType, index);
|
|
799
|
-
break;
|
|
800
|
-
case "DoubleProperty":
|
|
801
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => DoubleProperty.ReadValue(reader)), ueType, index);
|
|
802
|
-
break;
|
|
803
|
-
case "ByteProperty":
|
|
804
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => ByteProperty.ReadValue(reader)), ueType, index);
|
|
805
|
-
break;
|
|
806
|
-
case "StrProperty":
|
|
807
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => StrProperty.ReadValue(reader)), ueType, index);
|
|
808
|
-
break;
|
|
809
|
-
case "EnumProperty":
|
|
810
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => EnumProperty.ReadValue(reader)), ueType, index);
|
|
811
|
-
break;
|
|
812
|
-
case "TextProperty":
|
|
813
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => TextProperty.ParseValue(reader)), ueType, index);
|
|
814
|
-
break;
|
|
815
|
-
case "InterfaceProperty":
|
|
816
|
-
case "ObjectProperty":
|
|
817
|
-
property = new ArrayProperty(subtype, new Array(elementCount).fill(0).map(e => ObjectProperty.ReadValue(reader)), ueType, index);
|
|
818
|
-
break;
|
|
819
|
-
case "StructProperty":
|
|
820
|
-
reader.readString(); // Same as currentProperty.name
|
|
821
|
-
const type = reader.readString(); // StructProperty
|
|
822
|
-
const binarySize = reader.readInt32(); // structureSize
|
|
823
|
-
const allIndex = reader.readInt32(); // 0
|
|
824
|
-
const allStructType = reader.readString();
|
|
825
|
-
const allGuid = reader.readInt32();
|
|
826
|
-
const allUnk1 = reader.readInt32();
|
|
827
|
-
const allUnk2 = reader.readInt32();
|
|
828
|
-
const allUnk3 = reader.readInt32();
|
|
829
|
-
const allUnk4 = reader.readByte();
|
|
830
|
-
const innerStructValueFields = { allStructType, allIndex, allGuid };
|
|
831
|
-
if (allUnk1 !== 0) {
|
|
832
|
-
innerStructValueFields.allUnk1 = allUnk1;
|
|
833
|
-
}
|
|
834
|
-
if (allUnk2 !== 0) {
|
|
835
|
-
innerStructValueFields.allUnk2 = allUnk2;
|
|
836
|
-
}
|
|
837
|
-
if (allUnk3 !== 0) {
|
|
838
|
-
innerStructValueFields.allUnk3 = allUnk3;
|
|
839
|
-
}
|
|
840
|
-
if (allUnk4 !== 0) {
|
|
841
|
-
innerStructValueFields.allUnk4 = allUnk4;
|
|
842
|
-
}
|
|
843
|
-
const before = reader.getBufferPosition();
|
|
844
|
-
const maArr = new Array(elementCount).fill(0).map(e => {
|
|
845
|
-
const struct = new StructProperty(allStructType, type, allIndex, allGuid);
|
|
846
|
-
// we do NOT assign individual unk's here. Since they are only serialized always on ArrayProperty's Level once for all elements.
|
|
847
|
-
//struct.unk1 = allUnk1;
|
|
848
|
-
//struct.unk2 = allUnk2;
|
|
849
|
-
//struct.unk3 = allUnk3;
|
|
850
|
-
//struct.unk4 = allUnk4;
|
|
851
|
-
struct.value = StructProperty.ParseValue(reader, allStructType, binarySize);
|
|
852
|
-
return struct;
|
|
853
|
-
});
|
|
854
|
-
const readBytees = reader.getBufferPosition() - before;
|
|
855
|
-
if (readBytees !== binarySize) {
|
|
856
|
-
throw new Error('possibly corrupt in array of struct.');
|
|
857
|
-
}
|
|
858
|
-
// Array Properties with struct values have some more properties.
|
|
859
|
-
property = new ArrayProperty(subtype, maArr, ueType, index, innerStructValueFields);
|
|
860
|
-
break;
|
|
861
|
-
default:
|
|
862
|
-
console.log(subtype, ueType);
|
|
863
|
-
throw new Error();
|
|
864
|
-
}
|
|
865
|
-
return property;
|
|
866
|
-
}
|
|
867
|
-
static CalcOverhead(property) {
|
|
868
|
-
return property.subtype.length + 5 + 1;
|
|
869
|
-
}
|
|
870
|
-
static Serialize(writer, property, propertyName) {
|
|
871
|
-
writer.writeString(property.subtype);
|
|
872
|
-
writer.writeByte(0);
|
|
873
|
-
writer.writeInt32(property.values.length);
|
|
874
|
-
switch (property.subtype) {
|
|
875
|
-
case "FloatProperty":
|
|
876
|
-
property.values.forEach(v => FloatProperty.SerializeValue(writer, v));
|
|
877
|
-
break;
|
|
878
|
-
case "BoolProperty":
|
|
879
|
-
property.values.forEach(v => BoolProperty.SerializeValue(writer, v));
|
|
880
|
-
break;
|
|
881
|
-
case "IntProperty":
|
|
882
|
-
property.values.forEach(v => Int32Property.SerializeValue(writer, v));
|
|
883
|
-
break;
|
|
884
|
-
case "Int64Property":
|
|
885
|
-
property.values.forEach(v => Int64Property.SerializeValue(writer, v));
|
|
886
|
-
break;
|
|
887
|
-
case "DoubleProperty":
|
|
888
|
-
property.values.forEach(v => DoubleProperty.SerializeValue(writer, v));
|
|
889
|
-
break;
|
|
890
|
-
case "ByteProperty":
|
|
891
|
-
property.values.forEach(v => ByteProperty.SerializeValue(writer, v));
|
|
892
|
-
break;
|
|
893
|
-
case "StrProperty":
|
|
894
|
-
property.values.forEach(v => StrProperty.SerializeValue(writer, v));
|
|
895
|
-
break;
|
|
896
|
-
case "EnumProperty":
|
|
897
|
-
property.values.forEach(v => EnumProperty.SerializeValue(writer, v));
|
|
898
|
-
break;
|
|
899
|
-
case "TextProperty":
|
|
900
|
-
property.values.forEach(v => TextProperty.SerializeValue(writer, v));
|
|
901
|
-
break;
|
|
902
|
-
case "InterfaceProperty":
|
|
903
|
-
case "ObjectProperty":
|
|
904
|
-
property.values.forEach(v => ObjectProperty.SerializeValue(writer, v));
|
|
905
|
-
break;
|
|
906
|
-
case "StructProperty":
|
|
907
|
-
writer.writeString(propertyName);
|
|
908
|
-
writer.writeString(property.subtype);
|
|
909
|
-
const lenIndicator = writer.getBufferPosition();
|
|
910
|
-
writer.writeInt32(0);
|
|
911
|
-
writer.writeInt32(property.structValueFields.allIndex);
|
|
912
|
-
writer.writeString(property.structValueFields.allStructType);
|
|
913
|
-
writer.writeInt32(property.structValueFields.allGuid);
|
|
914
|
-
writer.writeInt32(property.structValueFields.allUnk1 ?? 0);
|
|
915
|
-
writer.writeInt32(property.structValueFields.allUnk2 ?? 0);
|
|
916
|
-
writer.writeInt32(property.structValueFields.allUnk3 ?? 0);
|
|
917
|
-
writer.writeByte(property.structValueFields.allUnk4 ?? 0);
|
|
918
|
-
const before = writer.getBufferPosition();
|
|
919
|
-
property.values.forEach(v => StructProperty.SerializeValue(writer, property.structValueFields.allStructType, v.value));
|
|
920
|
-
writer.writeBinarySizeFromPosition(lenIndicator, before);
|
|
921
|
-
break;
|
|
922
|
-
default:
|
|
923
|
-
console.log(property.type, property.ueType);
|
|
924
|
-
throw new Error();
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
class SetProperty extends BasicProperty {
|
|
929
|
-
constructor(subtype, values, ueType, index) {
|
|
930
|
-
super('SetProperty', ueType, undefined, index);
|
|
931
|
-
this.subtype = subtype;
|
|
932
|
-
this.values = values;
|
|
933
|
-
}
|
|
934
|
-
static Parse(reader, ueType, index, propertyName) {
|
|
935
|
-
const subtype = reader.readString();
|
|
936
|
-
reader.skipBytes(1); // unk
|
|
937
|
-
reader.skipBytes(4); // unk2
|
|
938
|
-
const elementCount = reader.readInt32();
|
|
939
|
-
let property;
|
|
940
|
-
switch (subtype) {
|
|
941
|
-
case "IntProperty":
|
|
942
|
-
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(e => Int32Property.ReadValue(reader)), ueType, index);
|
|
943
|
-
break;
|
|
944
|
-
case "ObjectProperty":
|
|
945
|
-
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(e => ObjectProperty.ReadValue(reader)), ueType, index);
|
|
946
|
-
break;
|
|
947
|
-
case "NameProperty":
|
|
948
|
-
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(e => StrProperty.ReadValue(reader)), ueType, index);
|
|
949
|
-
break;
|
|
950
|
-
case "StructProperty":
|
|
951
|
-
if (propertyName === 'mRemovalLocations') {
|
|
952
|
-
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(e => ParseVec3(reader)), ueType, index);
|
|
953
|
-
}
|
|
954
|
-
break;
|
|
955
|
-
default:
|
|
956
|
-
throw new Error('Not Implemented.');
|
|
957
|
-
}
|
|
958
|
-
return property;
|
|
959
|
-
}
|
|
960
|
-
static CalcOverhead(property) {
|
|
961
|
-
return property.subtype.length + 5 + 1;
|
|
962
|
-
}
|
|
963
|
-
static Serialize(writer, property) {
|
|
964
|
-
writer.writeString(property.subtype);
|
|
965
|
-
//TODO: what is that
|
|
966
|
-
writer.writeByte(0);
|
|
967
|
-
writer.writeInt32(0);
|
|
968
|
-
writer.writeInt32(property.values.length);
|
|
969
|
-
switch (property.subtype) {
|
|
970
|
-
case "IntProperty":
|
|
971
|
-
property.values.forEach(v => Int32Property.SerializeValue(writer, v));
|
|
972
|
-
break;
|
|
973
|
-
case "ObjectProperty":
|
|
974
|
-
property.values.forEach(v => ObjectProperty.SerializeValue(writer, v));
|
|
975
|
-
break;
|
|
976
|
-
case "NameProperty":
|
|
977
|
-
property.values.forEach(v => StrProperty.SerializeValue(writer, v));
|
|
978
|
-
break;
|
|
979
|
-
case "StructProperty":
|
|
980
|
-
//TODO: this would only work for mRemovalLocations. Get a way to check for propertyname at least.
|
|
981
|
-
property.values.forEach(v => SerializeVec3(writer, v));
|
|
982
|
-
break;
|
|
983
|
-
default:
|
|
984
|
-
throw new Error('Not Implemented.');
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
class MapProperty extends BasicProperty {
|
|
989
|
-
constructor(keyType, valueType, ueType, index) {
|
|
990
|
-
super('MapProperty', ueType, undefined, index);
|
|
991
|
-
this.keyType = keyType;
|
|
992
|
-
this.valueType = valueType;
|
|
993
|
-
this.modeType = 2;
|
|
994
|
-
this.modeUnk2 = '';
|
|
995
|
-
this.modeUnk3 = '';
|
|
996
|
-
this.remainingData = [];
|
|
997
|
-
this.values = [];
|
|
998
|
-
}
|
|
999
|
-
static Parse(reader, propertyName, buildVersion, size, ueType = 'MapProperty', index = 0) {
|
|
1000
|
-
reader.getBufferPosition();
|
|
1001
|
-
const property = new MapProperty(reader.readString(), reader.readString(), ueType, index);
|
|
1002
|
-
reader.skipBytes(1);
|
|
1003
|
-
property.modeType = reader.readInt32();
|
|
1004
|
-
//property.remainingData = reader.readBytes(size - (reader.getBufferPosition() - start));
|
|
1005
|
-
property.remainingData = Array.from(reader.readBytes(size - 4));
|
|
1006
|
-
// TODO
|
|
1007
|
-
/*
|
|
1008
|
-
|
|
1009
|
-
if (property.modeType === 2) {
|
|
1010
|
-
property.modeUnk2 = reader.readString();
|
|
1011
|
-
property.modeUnk3 = reader.readString();
|
|
1012
|
-
} else if (property.modeType === 3) {
|
|
1013
|
-
property.modeUnk1 = reader.readHex(9);
|
|
1014
|
-
property.modeUnk2 = reader.readString();
|
|
1015
|
-
property.modeUnk3 = reader.readString();
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
let elementCount = reader.readInt32();
|
|
1019
|
-
for (let i = 0; i < elementCount; i++) {
|
|
1020
|
-
let key: number | string | ObjectReferenceV2 | vec3 | any[] | boolean;
|
|
1021
|
-
let value: number | string | ObjectReferenceV2 | any[] | boolean;
|
|
1022
|
-
|
|
1023
|
-
switch (property.keyType) {
|
|
1024
|
-
case 'IntProperty':
|
|
1025
|
-
key = Int32Property.ReadValue(reader);
|
|
1026
|
-
break;
|
|
1027
|
-
case 'Int64Property':
|
|
1028
|
-
key = Int64Property.ReadValue(reader);
|
|
1029
|
-
break;
|
|
1030
|
-
case 'NameProperty':
|
|
1031
|
-
case 'StrProperty':
|
|
1032
|
-
key = StrProperty.ReadValue(reader);
|
|
1033
|
-
break;
|
|
1034
|
-
case 'ObjectProperty':
|
|
1035
|
-
key = ObjectProperty.ReadValue(reader);
|
|
1036
|
-
break;
|
|
1037
|
-
case 'EnumProperty':
|
|
1038
|
-
key = EnumProperty.ReadValue(reader);
|
|
1039
|
-
break;
|
|
1040
|
-
case 'StructProperty':
|
|
1041
|
-
if (propertyName === 'Destroyed_Foliage_Transform' || parentType === '/BuildGunUtilities/BGU_Subsystem.BGU_Subsystem_C') // Mod: Universal Destroyer/Factory Statistics
|
|
1042
|
-
{
|
|
1043
|
-
key = ParseVec3(reader);
|
|
1044
|
-
break;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
key = [];
|
|
1048
|
-
while (true) {
|
|
1049
|
-
let singleValueProperty = DataFieldsV2.ParseProperty(reader, buildVersion, propertyName);
|
|
1050
|
-
if (singleValueProperty === null) {
|
|
1051
|
-
break;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
key.push(singleValueProperty);
|
|
1055
|
-
}
|
|
1056
|
-
break;
|
|
1057
|
-
default:
|
|
1058
|
-
throw new Error(`not implemented type ${property.keyType}`);
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
switch (property.valueType) {
|
|
1063
|
-
case 'ByteProperty':
|
|
1064
|
-
if (property.keyType === 'StrProperty') {
|
|
1065
|
-
value = reader.readString();
|
|
1066
|
-
}
|
|
1067
|
-
else {
|
|
1068
|
-
value = reader.readByte();
|
|
1069
|
-
}
|
|
1070
|
-
break;
|
|
1071
|
-
case 'BoolProperty':
|
|
1072
|
-
value = BoolProperty.ReadValue(reader);
|
|
1073
|
-
break;
|
|
1074
|
-
case 'IntProperty':
|
|
1075
|
-
value = Int32Property.ReadValue(reader);
|
|
1076
|
-
break;
|
|
1077
|
-
case 'FloatProperty':
|
|
1078
|
-
value = FloatProperty.ReadValue(reader);
|
|
1079
|
-
break;
|
|
1080
|
-
case 'StrProperty':
|
|
1081
|
-
value = StrProperty.ReadValue(reader);
|
|
1082
|
-
break;
|
|
1083
|
-
case 'ObjectProperty':
|
|
1084
|
-
value = ObjectProperty.ReadValue(reader);
|
|
1085
|
-
break;
|
|
1086
|
-
case 'StructProperty':
|
|
1087
|
-
// TODO: what is this
|
|
1088
|
-
/*
|
|
1089
|
-
if (parentType === 'LBBalancerData') {
|
|
1090
|
-
mapPropertySubProperties.mNormalIndex = this.readInt32();
|
|
1091
|
-
mapPropertySubProperties.mOverflowIndex = this.readInt32();
|
|
1092
|
-
mapPropertySubProperties.mFilterIndex = this.readInt32();
|
|
1093
|
-
}
|
|
1094
|
-
else { }
|
|
1095
|
-
*/
|
|
1096
|
-
/*
|
|
1097
|
-
|
|
1098
|
-
value = [];
|
|
1099
|
-
while (true) {
|
|
1100
|
-
let singleValueProperty = DataFieldsV2.ParseProperty(reader, buildVersion, propertyName);
|
|
1101
|
-
if (singleValueProperty === null) {
|
|
1102
|
-
break;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
value.push(singleValueProperty);
|
|
1106
|
-
}
|
|
1107
|
-
break;
|
|
1108
|
-
default:
|
|
1109
|
-
throw new Error(`not implemented type ${property.valueType}`);
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
property.values.push({
|
|
1113
|
-
key, value
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
*/
|
|
1117
|
-
return property;
|
|
1118
|
-
}
|
|
1119
|
-
static CalcOverhead(property) {
|
|
1120
|
-
return property.keyType.length + 5 + property.valueType.length + 5 + 1;
|
|
1121
|
-
}
|
|
1122
|
-
static Serialize(writer, property) {
|
|
1123
|
-
writer.writeString(property.keyType);
|
|
1124
|
-
writer.writeString(property.valueType);
|
|
1125
|
-
writer.writeByte(0);
|
|
1126
|
-
writer.writeInt32(property.modeType);
|
|
1127
|
-
writer.writeBytesArray(property.remainingData);
|
|
1128
|
-
//TODO: do the actual serialization.
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
const ParseDynamicStructData = (reader, buildVersion, type) => {
|
|
1132
|
-
const data = {
|
|
1133
|
-
type, properties: {}
|
|
1134
|
-
};
|
|
1135
|
-
let propertyName = reader.readString();
|
|
1136
|
-
while (propertyName !== 'None') {
|
|
1137
|
-
const property = DataFields.ParseProperty(reader, buildVersion, propertyName);
|
|
1138
|
-
data.properties[propertyName] = property;
|
|
1139
|
-
propertyName = reader.readString();
|
|
1140
|
-
}
|
|
1141
|
-
return data;
|
|
1142
|
-
};
|
|
1143
|
-
const SerializeDynamicStructData = (writer, buildVersion, data) => {
|
|
1144
|
-
for (const key in data.properties) {
|
|
1145
|
-
writer.writeString(key);
|
|
1146
|
-
DataFields.SerializeProperty(writer, data.properties[key], key, buildVersion);
|
|
1147
|
-
}
|
|
1148
|
-
writer.writeString('None');
|
|
1149
|
-
};
|
|
1150
|
-
const ReadFINNetworkTrace = (reader) => {
|
|
1151
|
-
const networkTrace = {};
|
|
1152
|
-
networkTrace.ref = ObjectReference.Parse(reader);
|
|
1153
|
-
networkTrace.hasPrev = reader.readInt32();
|
|
1154
|
-
if (networkTrace.hasPrev) {
|
|
1155
|
-
networkTrace.prev = ReadFINNetworkTrace(reader);
|
|
1156
|
-
}
|
|
1157
|
-
networkTrace.hasStep = reader.readInt32();
|
|
1158
|
-
if (networkTrace.hasStep) {
|
|
1159
|
-
networkTrace.step = reader.readString();
|
|
1160
|
-
}
|
|
1161
|
-
return networkTrace;
|
|
1162
|
-
};
|
|
1163
|
-
const SerializeFINNetworkTrace = (writer, obj) => {
|
|
1164
|
-
ObjectReference.Serialize(writer, obj.ref);
|
|
1165
|
-
writer.writeInt32(obj.hasPrev);
|
|
1166
|
-
if (obj.hasPrev) {
|
|
1167
|
-
SerializeFINNetworkTrace(writer, obj.prev);
|
|
1168
|
-
}
|
|
1169
|
-
writer.writeInt32(obj.hasStep);
|
|
1170
|
-
if (obj.hasStep) {
|
|
1171
|
-
writer.writeString(obj.step);
|
|
1172
|
-
}
|
|
1173
|
-
};
|
|
1174
|
-
const ReadFINLuaProcessorStateStorage = (reader, size) => {
|
|
1175
|
-
const stateStorage = { traces: [], references: [], thread: '', globals: '', remainingStructData: {} };
|
|
1176
|
-
const start = reader.getBufferPosition();
|
|
1177
|
-
const traceCount = reader.readInt32();
|
|
1178
|
-
for (let i = 0; i < traceCount; i++) {
|
|
1179
|
-
stateStorage.traces.push(ReadFINNetworkTrace(reader));
|
|
1180
|
-
}
|
|
1181
|
-
const refCount = reader.readInt32();
|
|
1182
|
-
for (let i = 0; i < refCount; i++) {
|
|
1183
|
-
stateStorage.references.push(ObjectReference.Parse(reader));
|
|
1184
|
-
}
|
|
1185
|
-
stateStorage.thread = reader.readString();
|
|
1186
|
-
stateStorage.globals = reader.readString();
|
|
1187
|
-
const remaining = size - (reader.getBufferPosition() - start);
|
|
1188
|
-
stateStorage.remainingStructData = reader.readBytes(remaining);
|
|
1189
|
-
return stateStorage;
|
|
1190
|
-
};
|
|
1191
|
-
const SerializeFINLuaProcessorStateStorage = (writer, stateStorage) => {
|
|
1192
|
-
writer.writeInt32(stateStorage.traces.length);
|
|
1193
|
-
for (const trace of stateStorage.traces) {
|
|
1194
|
-
SerializeFINNetworkTrace(writer, trace);
|
|
1195
|
-
}
|
|
1196
|
-
writer.writeInt32(stateStorage.references.length);
|
|
1197
|
-
for (const ref of stateStorage.references) {
|
|
1198
|
-
ObjectReference.Serialize(writer, ref);
|
|
1199
|
-
}
|
|
1200
|
-
writer.writeString(stateStorage.thread);
|
|
1201
|
-
writer.writeString(stateStorage.globals);
|
|
1202
|
-
writer.writeBytes(stateStorage.remainingStructData);
|
|
1203
|
-
};
|
|
1204
|
-
|
|
1205
|
-
class DataFields {
|
|
1206
|
-
constructor() {
|
|
1207
|
-
this.properties = [];
|
|
1208
|
-
this.trailingData = [];
|
|
1209
|
-
this.shouldBeNulled = false;
|
|
1210
|
-
}
|
|
1211
|
-
static Parse(length, reader, buildVersion) {
|
|
1212
|
-
const start = reader.getBufferPosition();
|
|
1213
|
-
const fields = new DataFields();
|
|
1214
|
-
if (length === 0) {
|
|
1215
|
-
//WARNING
|
|
1216
|
-
fields.shouldBeNulled = true;
|
|
1217
|
-
return fields;
|
|
1218
|
-
}
|
|
1219
|
-
let propertyName = reader.readString();
|
|
1220
|
-
while (propertyName !== 'None') {
|
|
1221
|
-
const property = DataFields.ParseProperty(reader, buildVersion, propertyName);
|
|
1222
|
-
fields.properties.push(property);
|
|
1223
|
-
propertyName = reader.readString();
|
|
1224
|
-
}
|
|
1225
|
-
reader.readInt32();
|
|
1226
|
-
const end = reader.getBufferPosition();
|
|
1227
|
-
let remainingBytes = start + length - end;
|
|
1228
|
-
fields.trailingData = Array.from(reader.readBytes(remainingBytes));
|
|
1229
|
-
return fields;
|
|
1230
|
-
}
|
|
1231
|
-
static ParseProperty(reader, buildVersion, propertyName) {
|
|
1232
|
-
let currentProperty = {};
|
|
1233
|
-
if (currentProperty.name === 'None') {
|
|
1234
|
-
return null;
|
|
1235
|
-
}
|
|
1236
|
-
//TODO: What is this extra byte that is appearing sometime?
|
|
1237
|
-
// i dont think there is an extra byte.
|
|
1238
|
-
/*
|
|
1239
|
-
let extraByteTest = reader.readByte();
|
|
1240
|
-
if (extraByteTest !== 0) {
|
|
1241
|
-
reader.currentByte -= 1;
|
|
1242
|
-
}
|
|
1243
|
-
else {
|
|
1244
|
-
console.log('Property extra byte', currentProperty);
|
|
1245
|
-
}
|
|
1246
|
-
*/
|
|
1247
|
-
//TODO assign type and index after parsing.
|
|
1248
|
-
const propertyType = reader.readString();
|
|
1249
|
-
const binarySize = reader.readInt32();
|
|
1250
|
-
const index = reader.readInt32();
|
|
1251
|
-
const before = reader.getBufferPosition();
|
|
1252
|
-
let overhead = 0;
|
|
1253
|
-
switch (propertyType) {
|
|
1254
|
-
case 'BoolProperty':
|
|
1255
|
-
currentProperty = BoolProperty.Parse(reader, propertyType, index);
|
|
1256
|
-
overhead = BoolProperty.CalcOverhead(currentProperty);
|
|
1257
|
-
break;
|
|
1258
|
-
case 'ByteProperty':
|
|
1259
|
-
currentProperty = ByteProperty.Parse(reader, propertyType, index);
|
|
1260
|
-
overhead = ByteProperty.CalcOverhead(currentProperty);
|
|
1261
|
-
break;
|
|
1262
|
-
case 'Int8Property':
|
|
1263
|
-
currentProperty = Int8Property.Parse(reader, propertyType, index);
|
|
1264
|
-
overhead = Int8Property.CalcOverhead(currentProperty);
|
|
1265
|
-
break;
|
|
1266
|
-
case 'UInt8Property':
|
|
1267
|
-
currentProperty = Uint8Property.Parse(reader, propertyType, index);
|
|
1268
|
-
overhead = Uint8Property.CalcOverhead(currentProperty);
|
|
1269
|
-
break;
|
|
1270
|
-
case 'IntProperty':
|
|
1271
|
-
case 'Int32Property':
|
|
1272
|
-
currentProperty = Int32Property.Parse(reader, propertyType, index);
|
|
1273
|
-
overhead = Int32Property.CalcOverhead(currentProperty);
|
|
1274
|
-
break;
|
|
1275
|
-
case 'UInt32Property':
|
|
1276
|
-
currentProperty = Uint32Property.Parse(reader, propertyType, index);
|
|
1277
|
-
overhead = Uint32Property.CalcOverhead(currentProperty);
|
|
1278
|
-
break;
|
|
1279
|
-
case 'Int64Property':
|
|
1280
|
-
currentProperty = Int64Property.Parse(reader, propertyType, index);
|
|
1281
|
-
overhead = Int64Property.CalcOverhead(currentProperty);
|
|
1282
|
-
break;
|
|
1283
|
-
// TODO
|
|
1284
|
-
/*
|
|
1285
|
-
case 'UInt64Property':
|
|
1286
|
-
currentProperty = Int64Property.Parse(reader, propertyType, index);
|
|
1287
|
-
break;
|
|
1288
|
-
*/
|
|
1289
|
-
case 'SingleProperty':
|
|
1290
|
-
case 'FloatProperty':
|
|
1291
|
-
currentProperty = FloatProperty.Parse(reader, propertyType, index);
|
|
1292
|
-
overhead = FloatProperty.CalcOverhead(currentProperty);
|
|
1293
|
-
break;
|
|
1294
|
-
case 'DoubleProperty':
|
|
1295
|
-
currentProperty = DoubleProperty.Parse(reader, propertyType, index);
|
|
1296
|
-
overhead = DoubleProperty.CalcOverhead(currentProperty);
|
|
1297
|
-
break;
|
|
1298
|
-
case 'StrProperty':
|
|
1299
|
-
case 'NameProperty':
|
|
1300
|
-
currentProperty = StrProperty.Parse(reader, propertyType, index);
|
|
1301
|
-
overhead = StrProperty.CalcOverhead(currentProperty);
|
|
1302
|
-
break;
|
|
1303
|
-
case 'ObjectProperty':
|
|
1304
|
-
case 'InterfaceProperty':
|
|
1305
|
-
currentProperty = ObjectProperty.Parse(reader, propertyType, index);
|
|
1306
|
-
overhead = ObjectProperty.CalcOverhead(currentProperty);
|
|
1307
|
-
break;
|
|
1308
|
-
case 'EnumProperty':
|
|
1309
|
-
currentProperty = EnumProperty.Parse(reader, propertyType, index);
|
|
1310
|
-
overhead = EnumProperty.CalcOverhead(currentProperty);
|
|
1311
|
-
break;
|
|
1312
|
-
case 'StructProperty':
|
|
1313
|
-
currentProperty = StructProperty.Parse(reader, propertyType, index, binarySize);
|
|
1314
|
-
overhead = StructProperty.CalcOverhead(currentProperty);
|
|
1315
|
-
break;
|
|
1316
|
-
case 'ArrayProperty':
|
|
1317
|
-
currentProperty = ArrayProperty.Parse(reader, propertyType, index, propertyName);
|
|
1318
|
-
overhead = ArrayProperty.CalcOverhead(currentProperty);
|
|
1319
|
-
break;
|
|
1320
|
-
case 'MapProperty':
|
|
1321
|
-
//currentProperty = reader.readMapProperty(currentProperty, '');
|
|
1322
|
-
currentProperty = MapProperty.Parse(reader, propertyName, buildVersion, binarySize);
|
|
1323
|
-
overhead = MapProperty.CalcOverhead(currentProperty);
|
|
1324
|
-
break;
|
|
1325
|
-
case 'TextProperty':
|
|
1326
|
-
currentProperty = TextProperty.Parse(reader, propertyType, index);
|
|
1327
|
-
overhead = TextProperty.CalcOverhead(currentProperty);
|
|
1328
|
-
break;
|
|
1329
|
-
case 'SetProperty':
|
|
1330
|
-
currentProperty = SetProperty.Parse(reader, propertyType, index, propertyName);
|
|
1331
|
-
overhead = SetProperty.CalcOverhead(currentProperty);
|
|
1332
|
-
break;
|
|
1333
|
-
default:
|
|
1334
|
-
throw new Error(`Unimplemented type ${propertyType}`);
|
|
1335
|
-
}
|
|
1336
|
-
currentProperty.name = propertyName;
|
|
1337
|
-
const readBytes = reader.getBufferPosition() - before - overhead;
|
|
1338
|
-
if (readBytes !== binarySize) {
|
|
1339
|
-
throw new Error(`possibly corrupt. Read ${readBytes} for ${propertyType} ${propertyName}, but ${binarySize} were indicated.`);
|
|
1340
|
-
}
|
|
1341
|
-
return currentProperty;
|
|
1342
|
-
}
|
|
1343
|
-
static Serialize(writer, fields, buildVersion) {
|
|
1344
|
-
for (const property of fields.properties) {
|
|
1345
|
-
writer.writeString(property.name);
|
|
1346
|
-
DataFields.SerializeProperty(writer, property, property.name, buildVersion);
|
|
1347
|
-
}
|
|
1348
|
-
writer.writeString('None');
|
|
1349
|
-
writer.writeInt32(0);
|
|
1350
|
-
writer.writeBytesArray(fields.trailingData);
|
|
1351
|
-
}
|
|
1352
|
-
static SerializeProperty(writer, property, propertyName, buildVersion) {
|
|
1353
|
-
writer.writeString(property.ueType);
|
|
1354
|
-
// binary length indicator
|
|
1355
|
-
const lenIndicator = writer.getBufferPosition();
|
|
1356
|
-
writer.writeInt32(0);
|
|
1357
|
-
// write index if it is not 0. Since it normally is.
|
|
1358
|
-
writer.writeInt32(property.index ?? 0);
|
|
1359
|
-
const start = writer.getBufferPosition();
|
|
1360
|
-
let overhead = 0;
|
|
1361
|
-
switch (property.ueType) {
|
|
1362
|
-
case 'BoolProperty':
|
|
1363
|
-
overhead = BoolProperty.CalcOverhead(property);
|
|
1364
|
-
BoolProperty.Serialize(writer, property);
|
|
1365
|
-
break;
|
|
1366
|
-
case 'ByteProperty':
|
|
1367
|
-
overhead = ByteProperty.CalcOverhead(property);
|
|
1368
|
-
ByteProperty.Serialize(writer, property);
|
|
1369
|
-
break;
|
|
1370
|
-
case 'Int8Property':
|
|
1371
|
-
overhead = Int8Property.CalcOverhead(property);
|
|
1372
|
-
Int8Property.Serialize(writer, property);
|
|
1373
|
-
break;
|
|
1374
|
-
case 'UInt8Property':
|
|
1375
|
-
overhead = Uint8Property.CalcOverhead(property);
|
|
1376
|
-
Uint8Property.Serialize(writer, property);
|
|
1377
|
-
break;
|
|
1378
|
-
case 'IntProperty':
|
|
1379
|
-
case 'Int32Property':
|
|
1380
|
-
overhead = Int32Property.CalcOverhead(property);
|
|
1381
|
-
Int32Property.Serialize(writer, property);
|
|
1382
|
-
break;
|
|
1383
|
-
case 'UInt32Property':
|
|
1384
|
-
overhead = Uint32Property.CalcOverhead(property);
|
|
1385
|
-
Uint32Property.Serialize(writer, property);
|
|
1386
|
-
break;
|
|
1387
|
-
case 'Int64Property':
|
|
1388
|
-
overhead = Int64Property.CalcOverhead(property);
|
|
1389
|
-
Int64Property.Serialize(writer, property);
|
|
1390
|
-
break;
|
|
1391
|
-
// TODO: uint64Property
|
|
1392
|
-
case 'SingleProperty':
|
|
1393
|
-
case 'FloatProperty':
|
|
1394
|
-
overhead = FloatProperty.CalcOverhead(property);
|
|
1395
|
-
FloatProperty.Serialize(writer, property);
|
|
1396
|
-
break;
|
|
1397
|
-
case 'DoubleProperty':
|
|
1398
|
-
overhead = DoubleProperty.CalcOverhead(property);
|
|
1399
|
-
DoubleProperty.Serialize(writer, property);
|
|
1400
|
-
break;
|
|
1401
|
-
case 'StrProperty':
|
|
1402
|
-
case 'NameProperty':
|
|
1403
|
-
overhead = StrProperty.CalcOverhead(property);
|
|
1404
|
-
StrProperty.Serialize(writer, property);
|
|
1405
|
-
break;
|
|
1406
|
-
case 'ObjectProperty':
|
|
1407
|
-
case 'InterfaceProperty':
|
|
1408
|
-
overhead = ObjectProperty.CalcOverhead(property);
|
|
1409
|
-
ObjectProperty.Serialize(writer, property);
|
|
1410
|
-
break;
|
|
1411
|
-
case 'EnumProperty':
|
|
1412
|
-
overhead = EnumProperty.CalcOverhead(property);
|
|
1413
|
-
EnumProperty.Serialize(writer, property);
|
|
1414
|
-
break;
|
|
1415
|
-
case 'ByteProperty':
|
|
1416
|
-
overhead = ByteProperty.CalcOverhead(property);
|
|
1417
|
-
ByteProperty.Serialize(writer, property);
|
|
1418
|
-
break;
|
|
1419
|
-
case 'StructProperty':
|
|
1420
|
-
overhead = StructProperty.CalcOverhead(property);
|
|
1421
|
-
StructProperty.Serialize(writer, property);
|
|
1422
|
-
break;
|
|
1423
|
-
case 'ArrayProperty':
|
|
1424
|
-
overhead = ArrayProperty.CalcOverhead(property);
|
|
1425
|
-
ArrayProperty.Serialize(writer, property, propertyName);
|
|
1426
|
-
break;
|
|
1427
|
-
case 'MapProperty':
|
|
1428
|
-
overhead = MapProperty.CalcOverhead(property);
|
|
1429
|
-
MapProperty.Serialize(writer, property);
|
|
1430
|
-
break;
|
|
1431
|
-
case 'TextProperty':
|
|
1432
|
-
overhead = TextProperty.CalcOverhead(property);
|
|
1433
|
-
TextProperty.Serialize(writer, property);
|
|
1434
|
-
break;
|
|
1435
|
-
case 'SetProperty':
|
|
1436
|
-
overhead = SetProperty.CalcOverhead(property);
|
|
1437
|
-
SetProperty.Serialize(writer, property);
|
|
1438
|
-
break;
|
|
1439
|
-
default:
|
|
1440
|
-
throw new Error(`Unimplemented type ${property.type}`);
|
|
1441
|
-
}
|
|
1442
|
-
// replace len indicator.
|
|
1443
|
-
writer.writeBinarySizeFromPosition(lenIndicator, start + overhead);
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
class SaveObject {
|
|
1448
|
-
constructor(typePath, rootObject, instanceName) {
|
|
1449
|
-
this.typePath = typePath;
|
|
1450
|
-
this.rootObject = rootObject;
|
|
1451
|
-
this.instanceName = instanceName;
|
|
1452
|
-
this.dataFields = new DataFields();
|
|
1453
|
-
}
|
|
1454
|
-
static ParseHeader(reader, obj) {
|
|
1455
|
-
obj.typePath = reader.readString();
|
|
1456
|
-
obj.rootObject = reader.readString();
|
|
1457
|
-
obj.instanceName = reader.readString();
|
|
1458
|
-
}
|
|
1459
|
-
static SerializeHeader(writer, obj) {
|
|
1460
|
-
writer.writeString(obj.typePath);
|
|
1461
|
-
writer.writeString(obj.rootObject);
|
|
1462
|
-
writer.writeString(obj.instanceName);
|
|
1463
|
-
}
|
|
1464
|
-
static ParseData(obj, length, reader, buildVersion) {
|
|
1465
|
-
obj.dataFields = DataFields.Parse(length, reader, buildVersion);
|
|
1466
|
-
}
|
|
1467
|
-
static SerializeData(writer, obj, buildVersion) {
|
|
1468
|
-
DataFields.Serialize(writer, obj.dataFields, buildVersion);
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
const isSaveComponent = (obj) => {
|
|
1473
|
-
return obj.type === 'SaveComponent';
|
|
1474
|
-
};
|
|
1475
|
-
class SaveComponent extends SaveObject {
|
|
1476
|
-
constructor(typePath, rootObject, instanceName, parentEntityName = '') {
|
|
1477
|
-
super(typePath, rootObject, instanceName);
|
|
1478
|
-
this.typePath = typePath;
|
|
1479
|
-
this.rootObject = rootObject;
|
|
1480
|
-
this.instanceName = instanceName;
|
|
1481
|
-
this.parentEntityName = parentEntityName;
|
|
1482
|
-
this.type = 'SaveComponent';
|
|
1483
|
-
}
|
|
1484
|
-
static ParseHeader(reader, obj) {
|
|
1485
|
-
SaveObject.ParseHeader(reader, obj);
|
|
1486
|
-
obj.parentEntityName = reader.readString();
|
|
1487
|
-
}
|
|
1488
|
-
static SerializeHeader(writer, component) {
|
|
1489
|
-
SaveObject.SerializeHeader(writer, component);
|
|
1490
|
-
writer.writeString(component.parentEntityName);
|
|
1491
|
-
}
|
|
1492
|
-
static ParseData(component, length, reader, buildVersion) {
|
|
1493
|
-
SaveObject.ParseData(component, length, reader, buildVersion);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
SaveComponent.TypeID = 0;
|
|
1497
|
-
|
|
1498
|
-
const isSaveEntity = (obj) => {
|
|
1499
|
-
return obj.type === 'SaveEntity';
|
|
1500
|
-
};
|
|
1501
|
-
class SaveEntity extends SaveObject {
|
|
1502
|
-
constructor(typePath, rootObject, instanceName, parentEntityName = '', needsTransform = false) {
|
|
1503
|
-
super(typePath, rootObject, instanceName);
|
|
1504
|
-
this.typePath = typePath;
|
|
1505
|
-
this.rootObject = rootObject;
|
|
1506
|
-
this.instanceName = instanceName;
|
|
1507
|
-
this.parentEntityName = parentEntityName;
|
|
1508
|
-
this.type = 'SaveEntity';
|
|
1509
|
-
this.needTransform = needsTransform;
|
|
1510
|
-
this.wasPlacedInLevel = false;
|
|
1511
|
-
this.parentObjectRoot = '';
|
|
1512
|
-
this.parentObjectName = '';
|
|
1513
|
-
this.transform = {
|
|
1514
|
-
rotation: { x: 0, y: 0, z: 0, w: 1 },
|
|
1515
|
-
translation: { x: 0, y: 0, z: 0 },
|
|
1516
|
-
scale3d: { x: 1, y: 1, z: 1 }
|
|
1517
|
-
};
|
|
1518
|
-
this.components = [];
|
|
1519
|
-
}
|
|
1520
|
-
static ParseHeader(reader, obj) {
|
|
1521
|
-
SaveObject.ParseHeader(reader, obj);
|
|
1522
|
-
obj.needTransform = reader.readInt32() == 1;
|
|
1523
|
-
obj.transform = ParseTransform(reader);
|
|
1524
|
-
obj.wasPlacedInLevel = reader.readInt32() == 1;
|
|
1525
|
-
}
|
|
1526
|
-
static ParseData(entity, length, reader, buildVersion) {
|
|
1527
|
-
var newLen = length - 12;
|
|
1528
|
-
entity.parentObjectRoot = reader.readString();
|
|
1529
|
-
if (entity.parentObjectRoot.length > 0)
|
|
1530
|
-
newLen -= entity.parentObjectRoot.length + 1;
|
|
1531
|
-
entity.parentObjectName = reader.readString();
|
|
1532
|
-
if (entity.parentObjectName.length > 0)
|
|
1533
|
-
newLen -= entity.parentObjectName.length + 1;
|
|
1534
|
-
var componentCount = reader.readInt32();
|
|
1535
|
-
for (let i = 0; i < componentCount; i++) {
|
|
1536
|
-
var componentRef = ObjectReference.Parse(reader);
|
|
1537
|
-
entity.components.push(componentRef);
|
|
1538
|
-
newLen -= 10 + componentRef.levelName.length + componentRef.pathName.length;
|
|
1539
|
-
}
|
|
1540
|
-
SaveObject.ParseData(entity, newLen, reader, buildVersion);
|
|
1541
|
-
}
|
|
1542
|
-
static SerializeHeader(writer, entity) {
|
|
1543
|
-
SaveObject.SerializeHeader(writer, entity);
|
|
1544
|
-
writer.writeInt32(entity.needTransform ? 1 : 0);
|
|
1545
|
-
SerializeTransform(writer, entity.transform);
|
|
1546
|
-
writer.writeInt32(entity.wasPlacedInLevel ? 1 : 0);
|
|
1547
|
-
}
|
|
1548
|
-
static SerializeData(writer, entity, buildVersion) {
|
|
1549
|
-
writer.writeString(entity.parentObjectRoot);
|
|
1550
|
-
writer.writeString(entity.parentObjectName);
|
|
1551
|
-
writer.writeInt32(entity.components.length);
|
|
1552
|
-
for (const com of entity.components) {
|
|
1553
|
-
writer.writeString(com.levelName);
|
|
1554
|
-
writer.writeString(com.pathName);
|
|
1555
|
-
}
|
|
1556
|
-
SaveObject.SerializeData(writer, entity, buildVersion);
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
SaveEntity.TypeID = 1;
|
|
1560
|
-
|
|
1561
|
-
class Level {
|
|
1562
|
-
constructor(name) {
|
|
1563
|
-
this.name = name;
|
|
1564
|
-
this.objects = [];
|
|
1565
|
-
this.collectables = [];
|
|
1566
|
-
}
|
|
1567
|
-
static SerializeObjectHeaders(writer, objects) {
|
|
1568
|
-
writer.writeInt32(objects.length);
|
|
1569
|
-
for (const obj of objects) {
|
|
1570
|
-
switch (obj.type) {
|
|
1571
|
-
case 'SaveEntity':
|
|
1572
|
-
writer.writeInt32(SaveEntity.TypeID);
|
|
1573
|
-
SaveEntity.SerializeHeader(writer, obj);
|
|
1574
|
-
break;
|
|
1575
|
-
case 'SaveComponent':
|
|
1576
|
-
writer.writeInt32(SaveComponent.TypeID);
|
|
1577
|
-
SaveComponent.SerializeHeader(writer, obj);
|
|
1578
|
-
break;
|
|
1579
|
-
default:
|
|
1580
|
-
console.log('Unknown object type', obj);
|
|
1581
|
-
break;
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
static WriteLevel(writer, level, buildVersion) {
|
|
1586
|
-
const lenIndicatorHeaderAndCollectableSize = writer.getBufferPosition();
|
|
1587
|
-
writer.writeInt32(0);
|
|
1588
|
-
Level.SerializeObjectHeaders(writer, level.objects);
|
|
1589
|
-
Level.SerializeCollectablesList(writer, level.collectables);
|
|
1590
|
-
// replace binary size from earlier for - object headers + collectables
|
|
1591
|
-
writer.writeBinarySizeFromPosition(lenIndicatorHeaderAndCollectableSize, lenIndicatorHeaderAndCollectableSize + 4);
|
|
1592
|
-
// write entities
|
|
1593
|
-
Level.SerializeObjectContents(writer, level.objects, buildVersion);
|
|
1594
|
-
Level.SerializeCollectablesList(writer, level.collectables);
|
|
1595
|
-
}
|
|
1596
|
-
static SerializeObjectContents(writer, objects, buildVersion) {
|
|
1597
|
-
const lenIndicatorEntities = writer.getBufferPosition();
|
|
1598
|
-
writer.writeInt32(0);
|
|
1599
|
-
writer.writeInt32(objects.length);
|
|
1600
|
-
for (const obj of objects) {
|
|
1601
|
-
const lenReplacementPosition = writer.getBufferPosition();
|
|
1602
|
-
writer.writeInt32(0);
|
|
1603
|
-
if (isSaveEntity(obj)) {
|
|
1604
|
-
SaveEntity.SerializeData(writer, obj, buildVersion);
|
|
1605
|
-
}
|
|
1606
|
-
else if (isSaveComponent(obj)) {
|
|
1607
|
-
SaveComponent.SerializeData(writer, obj, buildVersion);
|
|
1608
|
-
}
|
|
1609
|
-
writer.writeBinarySizeFromPosition(lenReplacementPosition, lenReplacementPosition + 4);
|
|
1610
|
-
}
|
|
1611
|
-
writer.writeBinarySizeFromPosition(lenIndicatorEntities, lenIndicatorEntities + 4);
|
|
1612
|
-
}
|
|
1613
|
-
static ReadObjectHeaders(reader, objectsList, onProgressCallback) {
|
|
1614
|
-
let countObjectHeaders = reader.readInt32();
|
|
1615
|
-
for (let i = 0; i < countObjectHeaders; i++) {
|
|
1616
|
-
if (i % 1000 === 0) {
|
|
1617
|
-
onProgressCallback(reader.getBufferProgress());
|
|
1618
|
-
}
|
|
1619
|
-
let objectType = reader.readInt32();
|
|
1620
|
-
switch (objectType) {
|
|
1621
|
-
case SaveEntity.TypeID:
|
|
1622
|
-
let object = new SaveEntity('', '', '', '');
|
|
1623
|
-
SaveEntity.ParseHeader(reader, object);
|
|
1624
|
-
objectsList.push(object);
|
|
1625
|
-
break;
|
|
1626
|
-
case SaveComponent.TypeID:
|
|
1627
|
-
let component = new SaveComponent('', '', '', '');
|
|
1628
|
-
SaveComponent.ParseHeader(reader, component);
|
|
1629
|
-
objectsList.push(component);
|
|
1630
|
-
break;
|
|
1631
|
-
default:
|
|
1632
|
-
console.log('Unknown object type', objectType);
|
|
1633
|
-
break;
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
static ReadObjectContents(reader, objectsList, buildVersion, onProgressCallback) {
|
|
1638
|
-
const binarySize = reader.readInt32(); // entities binary length
|
|
1639
|
-
const posBefore = reader.getBufferPosition();
|
|
1640
|
-
const countEntities = reader.readInt32();
|
|
1641
|
-
if (countEntities !== objectsList.length) {
|
|
1642
|
-
throw new Error(`possibly corrupt. entity content count ${countEntities} does not object count of ${objectsList.length}`);
|
|
1643
|
-
}
|
|
1644
|
-
// definitely after i=25467
|
|
1645
|
-
for (let i = 0; i < countEntities; i++) {
|
|
1646
|
-
if (i % 1000 === 0) {
|
|
1647
|
-
onProgressCallback(reader.getBufferProgress());
|
|
1648
|
-
}
|
|
1649
|
-
const len = reader.readInt32();
|
|
1650
|
-
const before = reader.getBufferPosition();
|
|
1651
|
-
const obj = objectsList[i];
|
|
1652
|
-
if (isSaveEntity(obj)) {
|
|
1653
|
-
SaveEntity.ParseData(obj, len, reader, buildVersion);
|
|
1654
|
-
}
|
|
1655
|
-
else if (isSaveComponent(obj)) {
|
|
1656
|
-
SaveComponent.ParseData(obj, len, reader, buildVersion);
|
|
1657
|
-
}
|
|
1658
|
-
const after = reader.getBufferPosition();
|
|
1659
|
-
if (after - before !== len) {
|
|
1660
|
-
console.warn('entity may be corrupt', this.name, i);
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
const posAfter = reader.getBufferPosition();
|
|
1664
|
-
if (posAfter - posBefore !== binarySize) {
|
|
1665
|
-
//WARNING
|
|
1666
|
-
console.warn('save seems corrupt.', this.name);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
static ReadLevel(reader, levelName, buildVersion) {
|
|
1670
|
-
const level = new Level(levelName);
|
|
1671
|
-
reader.readInt32(); // object headers + collectables binary length
|
|
1672
|
-
// object headers
|
|
1673
|
-
Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
|
|
1674
|
-
// collectables
|
|
1675
|
-
reader.onProgressCallback(reader.getBufferProgress());
|
|
1676
|
-
level.collectables = Level.ReadCollectablesList(reader);
|
|
1677
|
-
// objects contents
|
|
1678
|
-
Level.ReadObjectContents(reader, level.objects, buildVersion, reader.onProgressCallback);
|
|
1679
|
-
// collectables 2nd time.
|
|
1680
|
-
reader.onProgressCallback(reader.getBufferProgress());
|
|
1681
|
-
Level.ReadCollectablesList(reader);
|
|
1682
|
-
return level;
|
|
1683
|
-
}
|
|
1684
|
-
// collected stuff
|
|
1685
|
-
static SerializeCollectablesList(writer, collectables) {
|
|
1686
|
-
writer.writeInt32(collectables.length);
|
|
1687
|
-
for (const collectable of collectables) {
|
|
1688
|
-
ObjectReference.Serialize(writer, collectable);
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
// collected stuff
|
|
1692
|
-
static ReadCollectablesList(reader) {
|
|
1693
|
-
let countCollected = reader.readInt32();
|
|
1694
|
-
const collected = [];
|
|
1695
|
-
for (let i = 0; i < countCollected; i++) {
|
|
1696
|
-
const collectable = ObjectReference.Parse(reader);
|
|
1697
|
-
collected.push(collectable);
|
|
1698
|
-
}
|
|
1699
|
-
return collected;
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
class SatisfactorySave {
|
|
1704
|
-
constructor(header) {
|
|
1705
|
-
this.levels = [];
|
|
1706
|
-
this.trailingCollectedObjects = [];
|
|
1707
|
-
this.header = header;
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
class ParserError extends Error {
|
|
1712
|
-
constructor(name, message) {
|
|
1713
|
-
super(message);
|
|
1714
|
-
this.name = name;
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
class UnsupportedVersionError extends ParserError {
|
|
1718
|
-
constructor(message) {
|
|
1719
|
-
super('UnsupportedVersionError', message ?? 'This save version is not supported.');
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
class CorruptSaveError extends ParserError {
|
|
1723
|
-
constructor(message) {
|
|
1724
|
-
super('CorruptSaveError', message ?? 'This save data is most likely corrupt.');
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
class CompressionLibraryError extends ParserError {
|
|
1728
|
-
constructor(message) {
|
|
1729
|
-
super('CompressionLibraryError', message ?? 'Failed to compress/decompress save data.');
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
var Alignment;
|
|
1734
|
-
(function (Alignment) {
|
|
1735
|
-
Alignment[Alignment["BIG_ENDIAN"] = 0] = "BIG_ENDIAN";
|
|
1736
|
-
Alignment[Alignment["LITTLE_ENDIAN"] = 1] = "LITTLE_ENDIAN";
|
|
1737
|
-
})(Alignment || (Alignment = {}));
|
|
1738
|
-
|
|
1739
|
-
class ByteReader {
|
|
1740
|
-
constructor(fileBuffer, alignment) {
|
|
1741
|
-
this.currentByte = 0;
|
|
1742
|
-
this.handledByte = 0;
|
|
1743
|
-
this.maxByte = 0;
|
|
1744
|
-
this.lastStrRead = 0;
|
|
1745
|
-
this.getBufferPosition = () => this.currentByte;
|
|
1746
|
-
this.getBufferSlice = (begin, end) => this.bufferView.buffer.slice(begin, end);
|
|
1747
|
-
this.getBufferProgress = () => this.currentByte / this.bufferView.buffer.byteLength;
|
|
1748
|
-
this.getBufferLength = () => this.bufferView.buffer.byteLength;
|
|
1749
|
-
this.getBuffer = () => this.bufferView.buffer;
|
|
1750
|
-
this.alignment = alignment;
|
|
1751
|
-
this.reset(fileBuffer);
|
|
1752
|
-
}
|
|
1753
|
-
/**
|
|
1754
|
-
* Resets the reader on the given arraybuffer to start from the beginning.
|
|
1755
|
-
* @param newFileBuffer the new array buffer to be read.
|
|
1756
|
-
*/
|
|
1757
|
-
reset(newFileBuffer) {
|
|
1758
|
-
this.fileBuffer = newFileBuffer;
|
|
1759
|
-
this.bufferView = new DataView(newFileBuffer, 0, Math.min(102400, this.fileBuffer.byteLength));
|
|
1760
|
-
this.maxByte = newFileBuffer.byteLength;
|
|
1761
|
-
this.currentByte = 0;
|
|
1762
|
-
this.handledByte = 0;
|
|
1763
|
-
}
|
|
1764
|
-
/*
|
|
1765
|
-
* Byte Manipulation
|
|
1766
|
-
*/
|
|
1767
|
-
skipBytes(byteLength = 1) {
|
|
1768
|
-
this.currentByte += byteLength;
|
|
1769
|
-
}
|
|
1770
|
-
readByte() {
|
|
1771
|
-
// TODO can i not just readByte() ??
|
|
1772
|
-
return parseInt(this.bufferView.getUint8(this.currentByte++).toString());
|
|
1773
|
-
}
|
|
1774
|
-
readBytes(count) {
|
|
1775
|
-
return new Uint8Array(new Array(count).fill(0).map(pl => this.bufferView.getUint8(this.currentByte++)));
|
|
1776
|
-
}
|
|
1777
|
-
readHex(hexLength) {
|
|
1778
|
-
let hexPart = [];
|
|
1779
|
-
for (let i = 0; i < hexLength; i++) {
|
|
1780
|
-
let currentHex = String.fromCharCode(this.bufferView.getUint8(this.currentByte++));
|
|
1781
|
-
hexPart.push(currentHex);
|
|
1782
|
-
}
|
|
1783
|
-
return hexPart.join('');
|
|
1784
|
-
}
|
|
1785
|
-
readInt8() {
|
|
1786
|
-
let data = this.bufferView.getInt8(this.currentByte++);
|
|
1787
|
-
return data;
|
|
1788
|
-
}
|
|
1789
|
-
readUint8() {
|
|
1790
|
-
let data = this.bufferView.getUint8(this.currentByte++);
|
|
1791
|
-
return data;
|
|
1792
|
-
}
|
|
1793
|
-
readUint16() {
|
|
1794
|
-
let data = this.bufferView.getUint16(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1795
|
-
this.currentByte += 2;
|
|
1796
|
-
return data;
|
|
1797
|
-
}
|
|
1798
|
-
readInt32() {
|
|
1799
|
-
let data = this.bufferView.getInt32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1800
|
-
this.currentByte += 4;
|
|
1801
|
-
return data;
|
|
1802
|
-
}
|
|
1803
|
-
readUint32() {
|
|
1804
|
-
let data = this.bufferView.getUint32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1805
|
-
this.currentByte += 4;
|
|
1806
|
-
return data;
|
|
1807
|
-
}
|
|
1808
|
-
readLong() {
|
|
1809
|
-
let data = this.bufferView.getBigInt64(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1810
|
-
this.currentByte += 8;
|
|
1811
|
-
return data;
|
|
1812
|
-
}
|
|
1813
|
-
readFloat() {
|
|
1814
|
-
let data = this.bufferView.getFloat32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1815
|
-
this.currentByte += 4;
|
|
1816
|
-
return data;
|
|
1817
|
-
}
|
|
1818
|
-
readDouble() {
|
|
1819
|
-
let data = this.bufferView.getFloat64(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1820
|
-
this.currentByte += 8;
|
|
1821
|
-
return data;
|
|
1822
|
-
}
|
|
1823
|
-
getStringInfo() {
|
|
1824
|
-
let payload = '';
|
|
1825
|
-
let counter = this.currentByte;
|
|
1826
|
-
try {
|
|
1827
|
-
let strLength = this.bufferView.getInt32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1828
|
-
counter += 4;
|
|
1829
|
-
payload = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.bufferView.getUint8(counter++))).join('');
|
|
1830
|
-
}
|
|
1831
|
-
catch (error) { }
|
|
1832
|
-
this.currentByte -= counter;
|
|
1833
|
-
return { payload, counter };
|
|
1834
|
-
}
|
|
1835
|
-
readString() {
|
|
1836
|
-
let strLength = this.readInt32();
|
|
1837
|
-
this.lastStrRead = strLength;
|
|
1838
|
-
this.currentByte;
|
|
1839
|
-
if (strLength === 0) {
|
|
1840
|
-
return '';
|
|
1841
|
-
}
|
|
1842
|
-
// Range error!
|
|
1843
|
-
if (strLength > (this.bufferView.buffer.byteLength - this.currentByte)) {
|
|
1844
|
-
let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.bufferView.buffer.byteLength}`;
|
|
1845
|
-
throw new Error(errorMessage);
|
|
1846
|
-
}
|
|
1847
|
-
// it uses UTF16 if text is non-ascii, even if it would fit into UTF8.
|
|
1848
|
-
if (strLength < 0) {
|
|
1849
|
-
const string = new Array(-strLength - 1).fill('').map(c => String.fromCharCode(this.readUint16()));
|
|
1850
|
-
this.currentByte += 2;
|
|
1851
|
-
return string.join('');
|
|
1852
|
-
}
|
|
1853
|
-
//default UTF-8
|
|
1854
|
-
try {
|
|
1855
|
-
const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8()));
|
|
1856
|
-
this.currentByte += 1;
|
|
1857
|
-
return string.join('');
|
|
1858
|
-
}
|
|
1859
|
-
catch (error) {
|
|
1860
|
-
let errorMessage = `Cannot read UTF8 string of length ${strLength} at position ${this.currentByte}.`;
|
|
1861
|
-
console.log(errorMessage, error);
|
|
1862
|
-
throw error;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
const projectionFilterApplies = (config, object) => {
|
|
1868
|
-
let cond1 = false;
|
|
1869
|
-
if (isSaveEntity(object) || (config.includeComponents && isSaveComponent(object))) {
|
|
1870
|
-
cond1 = true;
|
|
1871
|
-
}
|
|
1872
|
-
let cond2 = true;
|
|
1873
|
-
if (isSaveEntity(object)) {
|
|
1874
|
-
cond2 = false;
|
|
1875
|
-
if (config.entityPathFilter.behavior === 'whitelist') {
|
|
1876
|
-
cond2 = config.entityPathFilter.list.find(en => object.typePath.startsWith(en)) !== undefined;
|
|
1877
|
-
}
|
|
1878
|
-
else if (config.entityPathFilter.behavior === 'blacklist') {
|
|
1879
|
-
cond2 = config.entityPathFilter.list.find(en => object.typePath.startsWith(en)) === undefined;
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
return cond1 && cond2;
|
|
1883
|
-
};
|
|
1884
|
-
class SaveReader extends ByteReader {
|
|
1885
|
-
constructor(fileBuffer, onProgressCallback = () => { }) {
|
|
1886
|
-
super(fileBuffer, Alignment.LITTLE_ENDIAN);
|
|
1887
|
-
this.onProgressCallback = onProgressCallback;
|
|
1888
|
-
this.levels = [];
|
|
1889
|
-
this.trailingCollectedObjects = [];
|
|
1890
|
-
this.compressionInfo = {
|
|
1891
|
-
packageFileTag: 0,
|
|
1892
|
-
maxChunkContentSize: 0,
|
|
1893
|
-
chunkHeaderSize: 48
|
|
1894
|
-
};
|
|
1895
|
-
}
|
|
1896
|
-
readHeader() {
|
|
1897
|
-
this.header = {
|
|
1898
|
-
saveHeaderType: 0,
|
|
1899
|
-
saveVersion: 0,
|
|
1900
|
-
buildVersion: 0,
|
|
1901
|
-
mapName: "DEFAULT",
|
|
1902
|
-
mapOptions: "",
|
|
1903
|
-
sessionName: "",
|
|
1904
|
-
playDurationSeconds: 0,
|
|
1905
|
-
saveDateTime: "0",
|
|
1906
|
-
sessionVisibility: 0
|
|
1907
|
-
};
|
|
1908
|
-
this.header.saveHeaderType = this.readInt32();
|
|
1909
|
-
this.header.saveVersion = this.readInt32();
|
|
1910
|
-
this.header.buildVersion = this.readInt32();
|
|
1911
|
-
this.header.mapName = this.readString();
|
|
1912
|
-
this.header.mapOptions = this.readString();
|
|
1913
|
-
this.header.sessionName = this.readString();
|
|
1914
|
-
this.header.playDurationSeconds = this.readInt32();
|
|
1915
|
-
const rawSaveDateTimeInTicks = this.readLong();
|
|
1916
|
-
const unixMilliseconds = (rawSaveDateTimeInTicks - SaveReader.EPOCH_TICKS) / 10000n;
|
|
1917
|
-
this.header.saveDateTime = unixMilliseconds.toString();
|
|
1918
|
-
this.header.sessionVisibility = this.readByte();
|
|
1919
|
-
if (this.header.saveHeaderType >= 7) {
|
|
1920
|
-
this.header.fEditorObjectVersion = this.readInt32();
|
|
1921
|
-
}
|
|
1922
|
-
if (this.header.saveHeaderType >= 8) {
|
|
1923
|
-
this.header.rawModMetadataString = this.readString();
|
|
1924
|
-
try {
|
|
1925
|
-
this.header.modMetadata = JSON.parse(this.header.rawModMetadataString);
|
|
1926
|
-
}
|
|
1927
|
-
catch (error) {
|
|
1928
|
-
//
|
|
1929
|
-
}
|
|
1930
|
-
this.header.isModdedSave = this.readInt32();
|
|
1931
|
-
}
|
|
1932
|
-
if (this.header.saveHeaderType >= 10) {
|
|
1933
|
-
this.header.saveIdentifier = this.readString();
|
|
1934
|
-
}
|
|
1935
|
-
if (this.header.saveVersion >= 21) ;
|
|
1936
|
-
else {
|
|
1937
|
-
throw new UnsupportedVersionError("The save version is too old to support encoding currently. Save in newer game version.");
|
|
1938
|
-
}
|
|
1939
|
-
return this.header;
|
|
1940
|
-
}
|
|
1941
|
-
inflateChunks() {
|
|
1942
|
-
// free memory
|
|
1943
|
-
this.fileBuffer = this.fileBuffer.slice(this.currentByte);
|
|
1944
|
-
this.handledByte = 0;
|
|
1945
|
-
this.currentByte = 0;
|
|
1946
|
-
this.maxByte = this.fileBuffer.byteLength;
|
|
1947
|
-
let currentChunks = [];
|
|
1948
|
-
let totalUncompressedBodySize = 0;
|
|
1949
|
-
// read while we can handle
|
|
1950
|
-
while (this.handledByte < this.maxByte) {
|
|
1951
|
-
// Read chunk info size...
|
|
1952
|
-
let chunkHeader = new DataView(this.fileBuffer.slice(0, this.compressionInfo.chunkHeaderSize));
|
|
1953
|
-
this.currentByte = this.compressionInfo.chunkHeaderSize;
|
|
1954
|
-
this.handledByte += this.compressionInfo.chunkHeaderSize;
|
|
1955
|
-
if (this.compressionInfo.packageFileTag <= 0) {
|
|
1956
|
-
this.compressionInfo.packageFileTag = chunkHeader.getInt32(0, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1957
|
-
}
|
|
1958
|
-
if (this.compressionInfo.maxChunkContentSize <= 0) {
|
|
1959
|
-
this.compressionInfo.maxChunkContentSize = chunkHeader.getInt32(8, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1960
|
-
}
|
|
1961
|
-
const chunkCompressedLength = chunkHeader.getInt32(32, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1962
|
-
const chunkUncompressedLength = chunkHeader.getInt32(40, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
1963
|
-
totalUncompressedBodySize += chunkUncompressedLength;
|
|
1964
|
-
const currentChunkSize = chunkCompressedLength;
|
|
1965
|
-
let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
|
|
1966
|
-
this.handledByte += currentChunkSize;
|
|
1967
|
-
this.currentByte += currentChunkSize;
|
|
1968
|
-
// Free memory from previous chunk...
|
|
1969
|
-
this.fileBuffer = this.fileBuffer.slice(this.currentByte);
|
|
1970
|
-
this.currentByte = 0;
|
|
1971
|
-
// Unzip!
|
|
1972
|
-
try {
|
|
1973
|
-
// Inflate current chunk
|
|
1974
|
-
let currentInflatedChunk = null;
|
|
1975
|
-
currentInflatedChunk = Pako.inflate(currentChunk);
|
|
1976
|
-
currentChunks.push(currentInflatedChunk);
|
|
1977
|
-
}
|
|
1978
|
-
catch (err) {
|
|
1979
|
-
throw new CompressionLibraryError("Failed to inflate compressed save data. " + err);
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
//TODO we can get rid of file buffer here.
|
|
1983
|
-
// Create one big chunk out of the little chunks
|
|
1984
|
-
let newChunkLength = currentChunks.map(cc => cc.length).reduce((prev, cur) => prev + cur);
|
|
1985
|
-
const bigWholeChunk = new Uint8Array(newChunkLength);
|
|
1986
|
-
let currentLength = 0;
|
|
1987
|
-
for (let i = 0; i < currentChunks.length; i++) {
|
|
1988
|
-
bigWholeChunk.set(currentChunks[i], currentLength);
|
|
1989
|
-
currentLength += currentChunks[i].length;
|
|
1990
|
-
}
|
|
1991
|
-
this.currentByte = 0;
|
|
1992
|
-
this.maxByte = bigWholeChunk.buffer.byteLength;
|
|
1993
|
-
this.bufferView = new DataView(bigWholeChunk.buffer);
|
|
1994
|
-
const dataLength = this.readInt32();
|
|
1995
|
-
if (totalUncompressedBodySize !== dataLength + 4) {
|
|
1996
|
-
throw new CorruptSaveError(`Possibly corrupt. Indicated size of total save body (${dataLength}) does not match the uncompressed real size of ${totalUncompressedBodySize}.`);
|
|
1997
|
-
}
|
|
1998
|
-
return {
|
|
1999
|
-
concatenatedChunkLength: newChunkLength,
|
|
2000
|
-
numChunks: currentChunks.length
|
|
2001
|
-
};
|
|
2002
|
-
}
|
|
2003
|
-
readLevels() {
|
|
2004
|
-
if (!this.header) {
|
|
2005
|
-
throw new ParserError('ParserError', 'Header must be set before objects can be read.');
|
|
2006
|
-
}
|
|
2007
|
-
if (this.header.saveVersion < 29) {
|
|
2008
|
-
throw new UnsupportedVersionError('Support for < U6 is not yet implemented.');
|
|
2009
|
-
}
|
|
2010
|
-
const numSubLevels = this.readInt32();
|
|
2011
|
-
this.levels = new Array(numSubLevels + 1);
|
|
2012
|
-
// read levels
|
|
2013
|
-
for (let j = 0; j <= numSubLevels; j++) {
|
|
2014
|
-
let levelName = (j === numSubLevels) ? '' + this.header.mapName : this.readString();
|
|
2015
|
-
this.onProgressCallback(this.getBufferProgress(), `reading level [${(j + 1)}/${(numSubLevels + 1)}] ${levelName}`);
|
|
2016
|
-
this.levels[j] = Level.ReadLevel(this, levelName, this.header.buildVersion);
|
|
2017
|
-
}
|
|
2018
|
-
// in U6/U7 there were more collectibles added that do not belong to a particular level
|
|
2019
|
-
if (this.getBufferPosition() < this.bufferView.byteLength) {
|
|
2020
|
-
this.trailingCollectedObjects = Level.ReadCollectablesList(this);
|
|
2021
|
-
}
|
|
2022
|
-
return this.levels;
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
// the number of .net ticks at the unix epoch
|
|
2026
|
-
SaveReader.EPOCH_TICKS = 621355968000000000n;
|
|
2027
|
-
|
|
2028
|
-
class ByteWriter {
|
|
2029
|
-
constructor(alignment, bufferSize = 500) {
|
|
2030
|
-
this.getBufferPosition = () => this.currentByte;
|
|
2031
|
-
this.getBufferSlice = (start, end) => this.bufferArray.slice(start, end);
|
|
2032
|
-
this.alignment = alignment;
|
|
2033
|
-
this.bufferArray = new ArrayBuffer(bufferSize);
|
|
2034
|
-
this.bufferView = new DataView(this.bufferArray);
|
|
2035
|
-
this.currentByte = 0;
|
|
2036
|
-
}
|
|
2037
|
-
/*
|
|
2038
|
-
* Byte Manipulation
|
|
2039
|
-
*/
|
|
2040
|
-
skipBytes(count = 1) {
|
|
2041
|
-
this.extendBufferIfNeeded(count);
|
|
2042
|
-
this.currentByte += count;
|
|
2043
|
-
}
|
|
2044
|
-
jumpTo(pos) {
|
|
2045
|
-
const count = pos - this.getBufferPosition();
|
|
2046
|
-
this.skipBytes(count);
|
|
2047
|
-
}
|
|
2048
|
-
writeByte(value) {
|
|
2049
|
-
this.extendBufferIfNeeded(1);
|
|
2050
|
-
this.bufferView.setUint8(this.currentByte, value);
|
|
2051
|
-
this.currentByte += 1;
|
|
2052
|
-
}
|
|
2053
|
-
writeBytesArray(bytes) {
|
|
2054
|
-
this.writeBytes(new Uint8Array(bytes));
|
|
2055
|
-
}
|
|
2056
|
-
writeBytes(bytes) {
|
|
2057
|
-
this.extendBufferIfNeeded(bytes.length);
|
|
2058
|
-
bytes.forEach(byte => this.bufferView.setUint8(this.currentByte++, byte));
|
|
2059
|
-
}
|
|
2060
|
-
writeInt8(value) {
|
|
2061
|
-
this.extendBufferIfNeeded(1);
|
|
2062
|
-
this.bufferView.setInt8(this.currentByte, value);
|
|
2063
|
-
this.currentByte += 1;
|
|
2064
|
-
}
|
|
2065
|
-
writeUint8(value) {
|
|
2066
|
-
this.writeByte(value);
|
|
2067
|
-
}
|
|
2068
|
-
writeInt16(value) {
|
|
2069
|
-
this.extendBufferIfNeeded(2);
|
|
2070
|
-
this.bufferView.setInt16(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2071
|
-
this.currentByte += 2;
|
|
2072
|
-
}
|
|
2073
|
-
writeUint16(value) {
|
|
2074
|
-
this.extendBufferIfNeeded(2);
|
|
2075
|
-
this.bufferView.setUint16(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2076
|
-
this.currentByte += 2;
|
|
2077
|
-
}
|
|
2078
|
-
writeInt32(value) {
|
|
2079
|
-
this.extendBufferIfNeeded(4);
|
|
2080
|
-
this.bufferView.setInt32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2081
|
-
this.currentByte += 4;
|
|
2082
|
-
}
|
|
2083
|
-
writeUint32(value) {
|
|
2084
|
-
this.extendBufferIfNeeded(4);
|
|
2085
|
-
this.bufferView.setUint32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2086
|
-
this.currentByte += 4;
|
|
2087
|
-
}
|
|
2088
|
-
writeInt64(value) {
|
|
2089
|
-
this.extendBufferIfNeeded(8);
|
|
2090
|
-
this.bufferView.setBigInt64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2091
|
-
this.currentByte += 8;
|
|
2092
|
-
}
|
|
2093
|
-
writeUint64(value) {
|
|
2094
|
-
this.extendBufferIfNeeded(8);
|
|
2095
|
-
this.bufferView.setBigUint64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2096
|
-
this.currentByte += 8;
|
|
2097
|
-
}
|
|
2098
|
-
writeFloat(value) {
|
|
2099
|
-
this.extendBufferIfNeeded(4);
|
|
2100
|
-
this.bufferView.setFloat32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2101
|
-
this.currentByte += 4;
|
|
2102
|
-
}
|
|
2103
|
-
writeDouble(value) {
|
|
2104
|
-
this.extendBufferIfNeeded(8);
|
|
2105
|
-
this.bufferView.setFloat64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
2106
|
-
this.currentByte += 8;
|
|
2107
|
-
}
|
|
2108
|
-
writeString(value) {
|
|
2109
|
-
if (value.length === 0) {
|
|
2110
|
-
this.writeInt32(0);
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
// if it's safe to use ASCII, use UTF8.
|
|
2114
|
-
if (ByteWriter.IsASCIICompatible(value)) {
|
|
2115
|
-
this.writeInt32(value.length + 1);
|
|
2116
|
-
for (let i = 0; i < value.length; i++) {
|
|
2117
|
-
this.writeByte(value.charCodeAt(i));
|
|
2118
|
-
}
|
|
2119
|
-
this.writeUint8(0);
|
|
2120
|
-
}
|
|
2121
|
-
// write UTF16
|
|
2122
|
-
else {
|
|
2123
|
-
this.writeInt32(-value.length - 1);
|
|
2124
|
-
for (let i = 0; i < value.length; i++) {
|
|
2125
|
-
this.writeUint16(value.charCodeAt(i));
|
|
2126
|
-
}
|
|
2127
|
-
this.writeUint16(0);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
writeBinarySizeFromPosition(lenIndicatorPos, start) {
|
|
2131
|
-
const after = this.getBufferPosition();
|
|
2132
|
-
const writtenBytes = after - start;
|
|
2133
|
-
this.jumpTo(lenIndicatorPos);
|
|
2134
|
-
this.writeInt32(writtenBytes);
|
|
2135
|
-
this.jumpTo(after);
|
|
2136
|
-
}
|
|
2137
|
-
/**
|
|
2138
|
-
* automatically extends the current buffer if the given space exceeds the available rest capacity of the current buffer.
|
|
2139
|
-
* @param countNeededBytes the needed space
|
|
2140
|
-
* @param factor how big the appended buffer should be in comparison to the current one. Values >= 1 make sense.
|
|
2141
|
-
*/
|
|
2142
|
-
extendBufferIfNeeded(countNeededBytes, factor = 1.5) {
|
|
2143
|
-
if (this.currentByte + countNeededBytes > this.bufferView.byteLength) {
|
|
2144
|
-
this.bufferArray = ByteWriter.AppendBuffer(this.bufferArray, new ArrayBuffer(factor * this.bufferArray.byteLength));
|
|
2145
|
-
this.bufferView = new DataView(this.bufferArray);
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
truncateBuffer() {
|
|
2149
|
-
this.bufferArray = this.bufferArray.slice(0, this.currentByte);
|
|
2150
|
-
}
|
|
2151
|
-
endWriting() {
|
|
2152
|
-
this.truncateBuffer();
|
|
2153
|
-
return this.bufferArray;
|
|
2154
|
-
}
|
|
2155
|
-
static ToInt32(num) {
|
|
2156
|
-
return new Uint8Array([
|
|
2157
|
-
(num & 0xff000000) >> 24,
|
|
2158
|
-
(num & 0x00ff0000) >> 16,
|
|
2159
|
-
(num & 0x0000ff00) >> 8,
|
|
2160
|
-
(num & 0x000000ff)
|
|
2161
|
-
]);
|
|
2162
|
-
}
|
|
2163
|
-
static AppendBuffer(buffer1, buffer2) {
|
|
2164
|
-
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
|
2165
|
-
tmp.set(new Uint8Array(buffer1), 0);
|
|
2166
|
-
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
|
2167
|
-
return tmp.buffer;
|
|
2168
|
-
}
|
|
2169
|
-
;
|
|
2170
|
-
}
|
|
2171
|
-
ByteWriter.IsASCIICompatible = (value) => /^[\x00-\x7F]*$/.test(value);
|
|
2172
|
-
|
|
2173
|
-
class SaveWriter extends ByteWriter {
|
|
2174
|
-
constructor() {
|
|
2175
|
-
super(Alignment.LITTLE_ENDIAN);
|
|
2176
|
-
}
|
|
2177
|
-
static WriteHeader(writer, header) {
|
|
2178
|
-
writer.writeInt32(header.saveHeaderType);
|
|
2179
|
-
writer.writeInt32(header.saveVersion);
|
|
2180
|
-
writer.writeInt32(header.buildVersion);
|
|
2181
|
-
writer.writeString(header.mapName);
|
|
2182
|
-
writer.writeString(header.mapOptions);
|
|
2183
|
-
writer.writeString(header.sessionName);
|
|
2184
|
-
writer.writeInt32(header.playDurationSeconds);
|
|
2185
|
-
writer.writeInt64(BigInt(header.saveDateTime));
|
|
2186
|
-
writer.writeByte(header.sessionVisibility);
|
|
2187
|
-
if (header.saveHeaderType >= 7) {
|
|
2188
|
-
writer.writeInt32(header.fEditorObjectVersion);
|
|
2189
|
-
}
|
|
2190
|
-
if (header.saveHeaderType >= 8) {
|
|
2191
|
-
if (header.modMetadata) {
|
|
2192
|
-
writer.writeString(JSON.stringify(header.modMetadata));
|
|
2193
|
-
}
|
|
2194
|
-
else {
|
|
2195
|
-
writer.writeString(header.rawModMetadataString);
|
|
2196
|
-
}
|
|
2197
|
-
writer.writeInt32(header.isModdedSave);
|
|
2198
|
-
}
|
|
2199
|
-
if (header.saveHeaderType >= 10) {
|
|
2200
|
-
writer.writeString(header.saveIdentifier);
|
|
2201
|
-
}
|
|
2202
|
-
if (header.saveVersion >= 21) ;
|
|
2203
|
-
else {
|
|
2204
|
-
throw new UnsupportedVersionError("The save version is too old to be supported currently.");
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
static WriteLevels(writer, save, buildVersion) {
|
|
2208
|
-
writer.writeInt32(save.levels.length - 1);
|
|
2209
|
-
for (const level of save.levels) {
|
|
2210
|
-
if (level.name !== save.header.mapName) {
|
|
2211
|
-
writer.writeString(level.name);
|
|
2212
|
-
}
|
|
2213
|
-
Level.WriteLevel(writer, level, buildVersion);
|
|
2214
|
-
}
|
|
2215
|
-
Level.SerializeCollectablesList(writer, save.trailingCollectedObjects);
|
|
2216
|
-
}
|
|
2217
|
-
static GenerateCompressedChunksFromData(bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, onFinish, alignment = Alignment.LITTLE_ENDIAN) {
|
|
2218
|
-
const errors = [];
|
|
2219
|
-
const totalUncompressedSize = bufferArray.byteLength;
|
|
2220
|
-
const saveBody = new Uint8Array(bufferArray.byteLength + 4);
|
|
2221
|
-
saveBody.set(new Uint8Array(bufferArray), 4);
|
|
2222
|
-
const miniView = new DataView(saveBody.buffer);
|
|
2223
|
-
miniView.setInt32(0, totalUncompressedSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
2224
|
-
onBinaryBeforeCompressing(saveBody.buffer);
|
|
2225
|
-
// collect slices of chunks with help of compression info for max chunk size
|
|
2226
|
-
let handledByte = 0;
|
|
2227
|
-
const chunkSummary = [];
|
|
2228
|
-
while (handledByte < saveBody.byteLength) {
|
|
2229
|
-
// create uncompressed chunk.
|
|
2230
|
-
const uncompressedContentSize = Math.min(compressionInfo.maxChunkContentSize, saveBody.byteLength - handledByte);
|
|
2231
|
-
const uncompressedChunk = saveBody.buffer.slice(handledByte, handledByte + uncompressedContentSize);
|
|
2232
|
-
// deflate chunk while we're at it.
|
|
2233
|
-
let compressedChunk = new Uint8Array(0);
|
|
2234
|
-
try {
|
|
2235
|
-
compressedChunk = Pako.deflate(uncompressedChunk);
|
|
2236
|
-
}
|
|
2237
|
-
catch (err) {
|
|
2238
|
-
errors.push("An error occurred while calling pako deflate. -> " + err);
|
|
2239
|
-
}
|
|
2240
|
-
const chunk = new Uint8Array(compressionInfo.chunkHeaderSize + compressedChunk.byteLength);
|
|
2241
|
-
chunk.set(compressedChunk, compressionInfo.chunkHeaderSize);
|
|
2242
|
-
// write header
|
|
2243
|
-
const view = new DataView(chunk.buffer);
|
|
2244
|
-
view.setInt32(0, compressionInfo.packageFileTag, alignment === Alignment.LITTLE_ENDIAN);
|
|
2245
|
-
view.setInt32(4, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2246
|
-
view.setInt32(8, compressionInfo.maxChunkContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
2247
|
-
view.setInt32(12, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2248
|
-
view.setInt32(16, compressedChunk.byteLength, alignment === Alignment.LITTLE_ENDIAN);
|
|
2249
|
-
view.setInt32(20, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2250
|
-
view.setInt32(24, uncompressedContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
2251
|
-
view.setInt32(28, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2252
|
-
view.setInt32(32, compressedChunk.byteLength, alignment === Alignment.LITTLE_ENDIAN);
|
|
2253
|
-
view.setInt32(36, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2254
|
-
view.setInt32(40, uncompressedContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
2255
|
-
view.setInt32(44, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
2256
|
-
onChunk(chunk);
|
|
2257
|
-
chunkSummary.push({
|
|
2258
|
-
uncompressedSize: uncompressedContentSize + compressionInfo.chunkHeaderSize,
|
|
2259
|
-
compressedSize: compressedChunk.byteLength + compressionInfo.chunkHeaderSize
|
|
2260
|
-
});
|
|
2261
|
-
handledByte += uncompressedContentSize;
|
|
2262
|
-
}
|
|
2263
|
-
onFinish({
|
|
2264
|
-
errors,
|
|
2265
|
-
chunkSummary
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
generateChunks(compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk, onFinish) {
|
|
2269
|
-
if (posAfterHeader <= 0) {
|
|
2270
|
-
throw new ParserError('ParserError', 'Seems like this buffer has no header. Please write the header first before you can generate chunks.');
|
|
2271
|
-
}
|
|
2272
|
-
// send plain header first.
|
|
2273
|
-
const header = new Uint8Array(this.bufferArray.slice(0, posAfterHeader));
|
|
2274
|
-
onHeader(header);
|
|
2275
|
-
// create save body
|
|
2276
|
-
this.bufferArray = this.bufferArray.slice(posAfterHeader);
|
|
2277
|
-
SaveWriter.GenerateCompressedChunksFromData(this.bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, onFinish, this.alignment);
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
class SaveParser {
|
|
2282
|
-
/**
|
|
2283
|
-
* serializes a save file into binary and reports back on individual callbacks.
|
|
2284
|
-
* @param save the save file to serialize into binary.
|
|
2285
|
-
* @param onBinaryBeforeCompressing gets called on the binary save body before it is compressed.
|
|
2286
|
-
* @param onHeader gets called on the binary save header, which is always uncompressed.
|
|
2287
|
-
* @param onChunk gets called when a chunk of the compressed save body was generated. Often, files' save bodies consist of multiple chunks.
|
|
2288
|
-
* @param onFinish gets called when all chunks have been generated. A summary of the chunk sizes and count is reported.
|
|
2289
|
-
*/
|
|
2290
|
-
static WriteSave(save, onBinaryBeforeCompressing, onHeader, onChunk, onFinish) {
|
|
2291
|
-
const writer = new SaveWriter();
|
|
2292
|
-
SaveWriter.WriteHeader(writer, save.header);
|
|
2293
|
-
const posAfterHeader = writer.getBufferPosition();
|
|
2294
|
-
SaveWriter.WriteLevels(writer, save, save.header.buildVersion);
|
|
2295
|
-
writer.endWriting();
|
|
2296
|
-
writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk, onFinish);
|
|
2297
|
-
}
|
|
2298
|
-
/**
|
|
2299
|
-
* parses a save object from a binary buffer and reports back on decompressing the save body in a callback.
|
|
2300
|
-
* @param file the binary content to be parsed.
|
|
2301
|
-
* @param additionalData additional static data that is not contained in save files. For example, power slugs and resource nodes.
|
|
2302
|
-
* @param onSaveDecompressed gets called when the entire binary save body has been decompressed.
|
|
2303
|
-
* @returns a brief summary
|
|
2304
|
-
*/
|
|
2305
|
-
static ParseSaveFile(file, onSaveDecompressed = () => { }, onProgress = () => { }) {
|
|
2306
|
-
const reader = new SaveReader(file.buffer, onProgress);
|
|
2307
|
-
// read header
|
|
2308
|
-
const header = reader.readHeader();
|
|
2309
|
-
const save = new SatisfactorySave(header);
|
|
2310
|
-
// inflate chunks
|
|
2311
|
-
reader.inflateChunks();
|
|
2312
|
-
// call callback on decompressed save body
|
|
2313
|
-
onSaveDecompressed(reader.getBuffer());
|
|
2314
|
-
// parse levels
|
|
2315
|
-
reader.readLevels();
|
|
2316
|
-
save.levels = reader.levels;
|
|
2317
|
-
save.compressionInfo = reader.compressionInfo;
|
|
2318
|
-
save.trailingCollectedObjects = reader.trailingCollectedObjects;
|
|
2319
|
-
return save;
|
|
2320
|
-
}
|
|
2321
|
-
/**
|
|
2322
|
-
* Does Projection on a Save Object in order to reduce it's complexity.
|
|
2323
|
-
* @param save the save object in question.
|
|
2324
|
-
* @param config the projection config to apply.
|
|
2325
|
-
* @returns a save object that has been projected / modified. Do not try to serialize it into an actual save tho! Since information has been transformed.
|
|
2326
|
-
*/
|
|
2327
|
-
static ProjectSave(save, config) {
|
|
2328
|
-
return {
|
|
2329
|
-
header: save.header,
|
|
2330
|
-
trailingCollectedObjects: save.trailingCollectedObjects,
|
|
2331
|
-
levels: save.levels.map(level => {
|
|
2332
|
-
const lvl = new Level(level.name);
|
|
2333
|
-
lvl.collectables = level.collectables;
|
|
2334
|
-
lvl.objects = level.objects.filter(obj => projectionFilterApplies(config, obj));
|
|
2335
|
-
return lvl;
|
|
2336
|
-
})
|
|
2337
|
-
};
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
/**
|
|
2341
|
-
* It JSON.stringifies any parsed content safely. The original JSON.stringify() has some flaws that get in the way, so it is custom wrapped. The original has some problems:
|
|
2342
|
-
* 1. it cannot stringify bigints. So we help out by converting it into a string.
|
|
2343
|
-
* 2. It cannot distinguish between 0 and -0. But a float32 is encoded in a uint8Array for 0 to be [0,0,0,0] (0x00000000) and -0 to be [0,0,0,128] (0x00000080) in little endian.
|
|
2344
|
-
* @param obj
|
|
2345
|
-
* @param indent
|
|
2346
|
-
* @returns a string that is safely stringified.
|
|
2347
|
-
*/
|
|
2348
|
-
SaveParser.JSONStringifyModified = (obj, indent = 0) => JSON.stringify(obj, (key, value) => {
|
|
2349
|
-
if (typeof value === 'bigint') {
|
|
2350
|
-
return value.toString();
|
|
2351
|
-
}
|
|
2352
|
-
else if (value === 0 && 1 / value < 0) { // -0
|
|
2353
|
-
return '-0';
|
|
2354
|
-
}
|
|
2355
|
-
return value;
|
|
2356
|
-
}, indent);
|
|
2357
|
-
|
|
2358
|
-
exports.CompressionLibraryError = CompressionLibraryError;
|
|
2359
|
-
exports.CorruptSaveError = CorruptSaveError;
|
|
2360
|
-
exports.Level = Level;
|
|
2361
|
-
exports.ParserError = ParserError;
|
|
2362
|
-
exports.SatisfactorySave = SatisfactorySave;
|
|
2363
|
-
exports.SaveParser = SaveParser;
|
|
2364
|
-
exports.UnsupportedVersionError = UnsupportedVersionError;
|