@etothepii/satisfactory-file-parser 0.0.34 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/build/parser/parser.js +1 -2
- package/build/parser/satisfactory/blueprint/blueprint-reader.js +2 -2
- package/build/parser/satisfactory/objects/DataFields.js +15 -6
- package/build/parser/satisfactory/objects/Property.js +23 -16
- package/build/parser/satisfactory/objects/SaveEntity.js +5 -1
- package/build/parser/satisfactory/save/level.class.d.ts +1 -1
- package/build/parser/satisfactory/save/level.class.js +42 -13
- package/build/parser/satisfactory/save/save-reader.d.ts +4 -3
- package/build/parser/satisfactory/save/save-reader.js +75 -16
- package/build/parser/satisfactory/save/save-writer.js +1 -1
- package/build/parser/satisfactory/save/save.types.d.ts +1 -0
- package/build/parser/satisfactory/structs/util.types.d.ts +6 -0
- package/build/parser/satisfactory/structs/util.types.js +50 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,14 +9,18 @@ This parser can read, modify and write:
|
|
|
9
9
|
- Blueprint Files `.sbp`, `.sbpcfg`
|
|
10
10
|
|
|
11
11
|
## Supported Versions
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
The version support of the packages is indicated below.
|
|
13
|
+
|
|
14
|
+
Game Version Files of U5 and below are NOT supported.
|
|
15
|
+
|
|
16
|
+
U8 has only read support so far and only for save files, not for blueprint files. More coming soon.
|
|
17
|
+
|
|
18
|
+
| Game Version | Package |
|
|
19
|
+
|:--------------:|:-----------------------------|
|
|
20
|
+
| <= U5 | ❌ |
|
|
21
|
+
| U6 + U7 | ✅ 0.0.1 - 0.0.34 |
|
|
22
|
+
| U8 | ⚠️ >= 0.1.0 ( .sav file Reading only so far) |
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
## Installation via npm
|
|
22
26
|
`npm install @etothepii/satisfactory-file-parser`
|
package/build/parser/parser.js
CHANGED
|
@@ -23,8 +23,7 @@ class Parser {
|
|
|
23
23
|
const save = new satisfactory_save_1.SatisfactorySave(header);
|
|
24
24
|
const inflateResult = reader.inflateChunks();
|
|
25
25
|
onDecompressedSaveBody(reader.getBuffer());
|
|
26
|
-
|
|
27
|
-
save.levels = reader.levels;
|
|
26
|
+
save.levels = reader.readLevelsU8();
|
|
28
27
|
save.compressionInfo = reader.compressionInfo;
|
|
29
28
|
save.trailingCollectedObjects = reader.trailingCollectedObjects;
|
|
30
29
|
return save;
|
|
@@ -21,7 +21,7 @@ class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
|
21
21
|
}
|
|
22
22
|
static ReadHeader(reader) {
|
|
23
23
|
const unk = reader.readBytes(3 * 4);
|
|
24
|
-
const positionThingOrWhat = (0, util_types_1.
|
|
24
|
+
const positionThingOrWhat = (0, util_types_1.ParseVec3f)(reader);
|
|
25
25
|
let itemTypeCount = reader.readInt32();
|
|
26
26
|
const itemCosts = new Array(itemTypeCount).fill(['', 0]);
|
|
27
27
|
for (let i = 0; i < itemTypeCount; i++) {
|
|
@@ -98,7 +98,7 @@ class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
|
98
98
|
const objectHeadersBinarySize = reader.readInt32();
|
|
99
99
|
let objects = [];
|
|
100
100
|
level_class_1.Level.ReadObjectHeaders(reader, objects, () => { });
|
|
101
|
-
level_class_1.Level.ReadObjectContents(reader, objects, 0, () => { });
|
|
101
|
+
level_class_1.Level.ReadObjectContents('', reader, objects, 0, () => { });
|
|
102
102
|
return objects;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -26,14 +26,22 @@ class DataFields {
|
|
|
26
26
|
let int1 = reader.readInt32();
|
|
27
27
|
if (int1 !== 0) {
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
const remainingLen = length - (reader.getBufferPosition() - start);
|
|
30
|
+
const oldRemainingLen = start + length - reader.getBufferPosition() - 4;
|
|
31
|
+
fields.specialProperties = DataFields.ParseAdditionalSpecialProperties(reader, typePath, remainingLen);
|
|
30
32
|
const end = reader.getBufferPosition();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const newWayOfCalculatingReminingSize = length - (end - start);
|
|
34
|
+
if (newWayOfCalculatingReminingSize > 0) {
|
|
35
|
+
reader.readBytes(newWayOfCalculatingReminingSize);
|
|
36
|
+
}
|
|
37
|
+
else if (newWayOfCalculatingReminingSize < 0) {
|
|
38
|
+
console.warn(`Unexpected. Did i read too much or is save corrupt !? bytes left to read is ${newWayOfCalculatingReminingSize}`);
|
|
39
|
+
}
|
|
33
40
|
return fields;
|
|
34
41
|
}
|
|
35
42
|
static ParseAdditionalSpecialProperties(reader, typePath, remainingLen) {
|
|
36
43
|
let property;
|
|
44
|
+
const start = reader.getBufferPosition();
|
|
37
45
|
switch (typePath) {
|
|
38
46
|
case '/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk1/Build_ConveyorBeltMk1.Build_ConveyorBeltMk1_C':
|
|
39
47
|
case '/Game/FactoryGame/Buildable/Factory/ConveyorBeltMk2/Build_ConveyorBeltMk2.Build_ConveyorBeltMk2_C':
|
|
@@ -53,9 +61,9 @@ class DataFields {
|
|
|
53
61
|
source: Property_1.ObjectProperty.ReadValue(reader),
|
|
54
62
|
target: Property_1.ObjectProperty.ReadValue(reader)
|
|
55
63
|
};
|
|
56
|
-
if (remainingLen >= 24) {
|
|
57
|
-
property.sourceTranslation = (0, util_types_1.
|
|
58
|
-
property.targetTranslation = (0, util_types_1.
|
|
64
|
+
if (remainingLen - (reader.getBufferPosition() - start) >= 24) {
|
|
65
|
+
property.sourceTranslation = (0, util_types_1.ParseVec3f)(reader);
|
|
66
|
+
property.targetTranslation = (0, util_types_1.ParseVec3f)(reader);
|
|
59
67
|
}
|
|
60
68
|
break;
|
|
61
69
|
case '/Game/FactoryGame/Character/Player/BP_PlayerState.BP_PlayerState_C':
|
|
@@ -167,6 +175,7 @@ class DataFields {
|
|
|
167
175
|
currentProperty.name = propertyName;
|
|
168
176
|
const readBytes = reader.getBufferPosition() - before - overhead;
|
|
169
177
|
if (readBytes !== binarySize) {
|
|
178
|
+
console.warn(`possibly corrupt. Read ${readBytes} for ${propertyType} ${propertyName}, but ${binarySize} were indicated.`);
|
|
170
179
|
throw new Error(`possibly corrupt. Read ${readBytes} for ${propertyType} ${propertyName}, but ${binarySize} were indicated.`);
|
|
171
180
|
}
|
|
172
181
|
return currentProperty;
|
|
@@ -202,7 +202,7 @@ class Uint32Property extends BasicProperty {
|
|
|
202
202
|
return reader.readUint32();
|
|
203
203
|
}
|
|
204
204
|
static CalcOverhead(property) {
|
|
205
|
-
|
|
205
|
+
return 1;
|
|
206
206
|
}
|
|
207
207
|
static Serialize(writer, property) {
|
|
208
208
|
(0, exports.SerializeGUID)(writer, property.guidInfo);
|
|
@@ -493,7 +493,7 @@ class StructProperty extends AbstractBaseProperty {
|
|
|
493
493
|
struct.unk2 = unk2;
|
|
494
494
|
}
|
|
495
495
|
const unk3 = reader.readInt32();
|
|
496
|
-
if (
|
|
496
|
+
if (unk3 !== 0) {
|
|
497
497
|
struct.unk3 = unk3;
|
|
498
498
|
}
|
|
499
499
|
const unk4 = reader.readByte();
|
|
@@ -518,19 +518,27 @@ class StructProperty extends AbstractBaseProperty {
|
|
|
518
518
|
value = (0, util_types_1.ParseCol4RGBA)(reader);
|
|
519
519
|
break;
|
|
520
520
|
case 'Vector':
|
|
521
|
+
value = (0, util_types_1.ParseVec3)(reader);
|
|
522
|
+
break;
|
|
521
523
|
case 'Rotator':
|
|
522
524
|
case 'Vector2D':
|
|
523
|
-
value = (0, util_types_1.
|
|
525
|
+
value = (0, util_types_1.ParseVec3f)(reader);
|
|
524
526
|
break;
|
|
525
527
|
case 'Quat':
|
|
528
|
+
value = (0, util_types_1.ParseVec4)(reader);
|
|
529
|
+
break;
|
|
526
530
|
case 'Vector4':
|
|
527
531
|
case 'Vector4D':
|
|
528
|
-
value = (0, util_types_1.
|
|
532
|
+
value = (0, util_types_1.ParseVec4f)(reader);
|
|
529
533
|
break;
|
|
530
534
|
case 'Box':
|
|
535
|
+
const otherBoxLike = {
|
|
536
|
+
other1: (0, util_types_1.ParseVec3f)(reader),
|
|
537
|
+
other2: (0, util_types_1.ParseVec3f)(reader)
|
|
538
|
+
};
|
|
531
539
|
value = {
|
|
532
|
-
min: (0, util_types_1.
|
|
533
|
-
max: (0, util_types_1.
|
|
540
|
+
min: (0, util_types_1.ParseVec3f)(reader),
|
|
541
|
+
max: (0, util_types_1.ParseVec3f)(reader),
|
|
534
542
|
isValid: reader.readByte() >= 1
|
|
535
543
|
};
|
|
536
544
|
break;
|
|
@@ -613,18 +621,18 @@ class StructProperty extends AbstractBaseProperty {
|
|
|
613
621
|
case 'Rotator':
|
|
614
622
|
case 'Vector2D':
|
|
615
623
|
value = value;
|
|
616
|
-
(0, util_types_1.
|
|
624
|
+
(0, util_types_1.SerializeVec3f)(writer, value);
|
|
617
625
|
break;
|
|
618
626
|
case 'Quat':
|
|
619
627
|
case 'Vector4':
|
|
620
628
|
case 'Vector4D':
|
|
621
629
|
value = value;
|
|
622
|
-
(0, util_types_1.
|
|
630
|
+
(0, util_types_1.SerializeVec4f)(writer, value);
|
|
623
631
|
break;
|
|
624
632
|
case 'Box':
|
|
625
633
|
value = value;
|
|
626
|
-
(0, util_types_1.
|
|
627
|
-
(0, util_types_1.
|
|
634
|
+
(0, util_types_1.SerializeVec3f)(writer, value.min);
|
|
635
|
+
(0, util_types_1.SerializeVec3f)(writer, value.max);
|
|
628
636
|
writer.writeByte(value.isValid ? 1 : 0);
|
|
629
637
|
break;
|
|
630
638
|
case 'RailroadTrackPosition':
|
|
@@ -755,8 +763,8 @@ class ArrayProperty extends BasicProperty {
|
|
|
755
763
|
struct.value = StructProperty.ParseValue(reader, allStructType, binarySize);
|
|
756
764
|
return struct;
|
|
757
765
|
});
|
|
758
|
-
const
|
|
759
|
-
if (
|
|
766
|
+
const readBytes = reader.getBufferPosition() - before;
|
|
767
|
+
if (readBytes !== binarySize) {
|
|
760
768
|
throw new Error('possibly corrupt in array of struct.');
|
|
761
769
|
}
|
|
762
770
|
property = new ArrayProperty(subtype, maArr, ueType, index, innerStructValueFields);
|
|
@@ -823,8 +831,7 @@ class ArrayProperty extends BasicProperty {
|
|
|
823
831
|
writer.writeBinarySizeFromPosition(lenIndicator, before);
|
|
824
832
|
break;
|
|
825
833
|
default:
|
|
826
|
-
|
|
827
|
-
throw new Error();
|
|
834
|
+
throw new Error(`Unknown array property ${property.ueType}, ${property.type}`);
|
|
828
835
|
}
|
|
829
836
|
}
|
|
830
837
|
}
|
|
@@ -853,7 +860,7 @@ class SetProperty extends BasicProperty {
|
|
|
853
860
|
break;
|
|
854
861
|
case "StructProperty":
|
|
855
862
|
if (propertyName === 'mRemovalLocations') {
|
|
856
|
-
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(() => (0, util_types_1.
|
|
863
|
+
property = new SetProperty(subtype, new Array(elementCount).fill(0).map(() => (0, util_types_1.ParseVec3f)(reader)), ueType, index);
|
|
857
864
|
}
|
|
858
865
|
break;
|
|
859
866
|
default:
|
|
@@ -880,7 +887,7 @@ class SetProperty extends BasicProperty {
|
|
|
880
887
|
property.values.forEach(v => StrProperty.SerializeValue(writer, v));
|
|
881
888
|
break;
|
|
882
889
|
case "StructProperty":
|
|
883
|
-
property.values.forEach(v => (0, util_types_1.
|
|
890
|
+
property.values.forEach(v => (0, util_types_1.SerializeVec3f)(writer, v));
|
|
884
891
|
break;
|
|
885
892
|
default:
|
|
886
893
|
throw new Error('Not Implemented.');
|
|
@@ -34,6 +34,9 @@ class SaveEntity extends SaveObject_1.SaveObject {
|
|
|
34
34
|
obj.wasPlacedInLevel = reader.readInt32() == 1;
|
|
35
35
|
}
|
|
36
36
|
static ParseData(entity, length, reader, buildVersion, typePath) {
|
|
37
|
+
const typeOrWhat = reader.readInt32();
|
|
38
|
+
const something = reader.readInt32();
|
|
39
|
+
const afterSizeIndicator = reader.getBufferPosition();
|
|
37
40
|
var newLen = length - 12;
|
|
38
41
|
entity.parentObjectRoot = reader.readString();
|
|
39
42
|
if (entity.parentObjectRoot.length > 0)
|
|
@@ -47,7 +50,8 @@ class SaveEntity extends SaveObject_1.SaveObject {
|
|
|
47
50
|
entity.components.push(componentRef);
|
|
48
51
|
newLen -= 10 + componentRef.levelName.length + componentRef.pathName.length;
|
|
49
52
|
}
|
|
50
|
-
|
|
53
|
+
const remainingSize = something - (reader.getBufferPosition() - afterSizeIndicator);
|
|
54
|
+
return SaveObject_1.SaveObject.ParseData(entity, remainingSize, reader, buildVersion, typePath);
|
|
51
55
|
}
|
|
52
56
|
static SerializeHeader(writer, entity) {
|
|
53
57
|
SaveObject_1.SaveObject.SerializeHeader(writer, entity);
|
|
@@ -14,7 +14,7 @@ export declare class Level {
|
|
|
14
14
|
static WriteLevel(writer: ByteWriter, level: Level, buildVersion: number): void;
|
|
15
15
|
static SerializeObjectContents(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[], buildVersion: number, levelName: string): void;
|
|
16
16
|
static ReadObjectHeaders(reader: BinaryReadable, objectsList: SaveObject[], onProgressCallback: (progress: number, msg?: string) => void): void;
|
|
17
|
-
static ReadObjectContents(reader: BinaryReadable, objectsList: SaveObject[], buildVersion: number, onProgressCallback: (progress: number, msg?: string) => void): Promise<void>;
|
|
17
|
+
static ReadObjectContents(levelName: string, reader: BinaryReadable, objectsList: SaveObject[], buildVersion: number, onProgressCallback: (progress: number, msg?: string) => void): Promise<void>;
|
|
18
18
|
static ReadLevel(reader: SaveReader, levelName: string, buildVersion: number): Level;
|
|
19
19
|
static SerializeCollectablesList(writer: ByteWriter, collectables: ObjectReference[]): void;
|
|
20
20
|
static ReadCollectablesList(reader: BinaryReadable): ObjectReference[];
|
|
@@ -77,9 +77,9 @@ class Level {
|
|
|
77
77
|
break;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
const unk = reader.readInt32();
|
|
80
81
|
}
|
|
81
|
-
static async ReadObjectContents(reader, objectsList, buildVersion, onProgressCallback) {
|
|
82
|
-
const binarySize = reader.readInt32();
|
|
82
|
+
static async ReadObjectContents(levelName, reader, objectsList, buildVersion, onProgressCallback) {
|
|
83
83
|
const posBefore = reader.getBufferPosition();
|
|
84
84
|
const countEntities = reader.readInt32();
|
|
85
85
|
if (countEntities !== objectsList.length) {
|
|
@@ -90,31 +90,53 @@ class Level {
|
|
|
90
90
|
onProgressCallback(reader.getBufferProgress());
|
|
91
91
|
}
|
|
92
92
|
const len = reader.readInt32();
|
|
93
|
+
if (len === 0) {
|
|
94
|
+
throw new Error(`check number is a wrong value (${len}). This normally indicates a corrupt entity or save.`);
|
|
95
|
+
}
|
|
93
96
|
const before = reader.getBufferPosition();
|
|
94
97
|
const obj = objectsList[i];
|
|
95
98
|
if ((0, SaveEntity_1.isSaveEntity)(obj)) {
|
|
96
99
|
SaveEntity_1.SaveEntity.ParseData(obj, len, reader, buildVersion, obj.typePath);
|
|
97
100
|
}
|
|
98
101
|
else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
|
|
99
|
-
|
|
102
|
+
const wtf1 = reader.readInt32();
|
|
103
|
+
const wtf2 = reader.readInt32();
|
|
104
|
+
SaveComponent_1.SaveComponent.ParseData(obj, wtf2, reader, buildVersion, obj.typePath);
|
|
100
105
|
}
|
|
101
106
|
const after = reader.getBufferPosition();
|
|
102
107
|
if (after - before !== len) {
|
|
103
|
-
console.warn('entity may be corrupt', this.name, i);
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
const posAfter = reader.getBufferPosition();
|
|
107
|
-
if (posAfter - posBefore !== binarySize) {
|
|
108
|
-
console.warn('save seems corrupt.', this.name);
|
|
109
|
-
}
|
|
110
111
|
}
|
|
111
112
|
static ReadLevel(reader, levelName, buildVersion) {
|
|
112
113
|
const level = new Level(levelName);
|
|
113
|
-
reader.
|
|
114
|
+
const levelStartPos = reader.getBufferPosition();
|
|
115
|
+
const placeholderHead = reader.readInt32();
|
|
116
|
+
if (placeholderHead === 8) {
|
|
117
|
+
const morePlaceholder = reader.readBytes(28);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
reader.skipBytes(-4);
|
|
121
|
+
}
|
|
122
|
+
const lookahead1 = reader.readInt32();
|
|
123
|
+
const lookahead2 = reader.readInt32();
|
|
124
|
+
if (lookahead1 !== 0 && lookahead2 !== 0) {
|
|
125
|
+
reader.skipBytes(-8);
|
|
126
|
+
return level;
|
|
127
|
+
}
|
|
128
|
+
else if (lookahead1 !== 0 && lookahead2 === 0) {
|
|
129
|
+
reader.skipBytes(-8);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
throw new Error('Unexpected. what now? TODO FIXME');
|
|
133
|
+
}
|
|
134
|
+
const binLen = reader.readInt32();
|
|
135
|
+
const unk = reader.readInt32();
|
|
114
136
|
Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
|
|
115
137
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
116
138
|
level.collectables = Level.ReadCollectablesList(reader);
|
|
117
|
-
Level.ReadObjectContents(reader, level.objects, buildVersion, reader.onProgressCallback);
|
|
139
|
+
Level.ReadObjectContents(levelName, reader, level.objects, buildVersion, reader.onProgressCallback);
|
|
118
140
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
119
141
|
Level.ReadCollectablesList(reader);
|
|
120
142
|
return level;
|
|
@@ -126,11 +148,18 @@ class Level {
|
|
|
126
148
|
}
|
|
127
149
|
}
|
|
128
150
|
static ReadCollectablesList(reader) {
|
|
129
|
-
let countCollected = reader.readInt32();
|
|
130
151
|
const collected = [];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
let countSmthing = reader.readInt32();
|
|
153
|
+
if (countSmthing > 0) {
|
|
154
|
+
const lookahead = reader.readInt32();
|
|
155
|
+
if (lookahead === 0) {
|
|
156
|
+
return collected;
|
|
157
|
+
}
|
|
158
|
+
reader.skipBytes(-4);
|
|
159
|
+
for (let i = 0; i < countSmthing; i++) {
|
|
160
|
+
const collectable = ObjectReference_1.ObjectReference.Parse(reader);
|
|
161
|
+
collected.push(collectable);
|
|
162
|
+
}
|
|
134
163
|
}
|
|
135
164
|
return collected;
|
|
136
165
|
}
|
|
@@ -5,7 +5,7 @@ import { ObjectReference } from "../objects/ObjectReference";
|
|
|
5
5
|
import { SaveComponent } from "../objects/SaveComponent";
|
|
6
6
|
import { SaveEntity } from "../objects/SaveEntity";
|
|
7
7
|
import { Level } from "./level.class";
|
|
8
|
-
import { SatisfactorySaveHeader } from "./save.types";
|
|
8
|
+
import { RoughSaveVersion, SatisfactorySaveHeader } from "./save.types";
|
|
9
9
|
export type EntityPathFilter = {
|
|
10
10
|
behavior: 'whitelist' | 'blacklist';
|
|
11
11
|
list: string[];
|
|
@@ -16,7 +16,7 @@ export type SaveProjectionConfig = {
|
|
|
16
16
|
};
|
|
17
17
|
export declare const projectionFilterApplies: (config: SaveProjectionConfig, object: SaveComponent | SaveEntity) => boolean;
|
|
18
18
|
export type ReadMode = 'stream' | 'whole';
|
|
19
|
-
export declare const DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE =
|
|
19
|
+
export declare const DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
20
20
|
export declare class SaveReader extends ByteReader {
|
|
21
21
|
onProgressCallback: (progress: number, msg?: string) => void;
|
|
22
22
|
header: SatisfactorySaveHeader | undefined;
|
|
@@ -26,10 +26,11 @@ export declare class SaveReader extends ByteReader {
|
|
|
26
26
|
static readonly EPOCH_TICKS = 621355968000000000n;
|
|
27
27
|
constructor(fileBuffer: ArrayBuffer, onProgressCallback?: (progress: number, msg?: string) => void);
|
|
28
28
|
readHeader(): SatisfactorySaveHeader;
|
|
29
|
+
static getRoughSaveVersion: (saveVersion: number, headerTypeVersion: number) => RoughSaveVersion;
|
|
29
30
|
inflateChunks(): {
|
|
30
31
|
concatenatedChunkLength: number;
|
|
31
32
|
numChunks: number;
|
|
32
33
|
};
|
|
33
|
-
|
|
34
|
+
readLevelsU8(): Level[];
|
|
34
35
|
readLevelsAsynchronously(writer: SaveStreamWriter): Promise<Level[]>;
|
|
35
36
|
}
|
|
@@ -30,7 +30,7 @@ const projectionFilterApplies = (config, object) => {
|
|
|
30
30
|
return cond1 && cond2;
|
|
31
31
|
};
|
|
32
32
|
exports.projectionFilterApplies = projectionFilterApplies;
|
|
33
|
-
exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE =
|
|
33
|
+
exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
34
34
|
class SaveReader extends byte_reader_class_1.ByteReader {
|
|
35
35
|
constructor(fileBuffer, onProgressCallback = () => { }) {
|
|
36
36
|
super(fileBuffer, alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
@@ -81,6 +81,12 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
81
81
|
if (this.header.saveHeaderType >= 10) {
|
|
82
82
|
this.header.saveIdentifier = this.readString();
|
|
83
83
|
}
|
|
84
|
+
if (this.header.saveHeaderType >= 13) {
|
|
85
|
+
const unk1 = this.readInt32();
|
|
86
|
+
const unk2 = this.readInt32();
|
|
87
|
+
const unk3 = this.readBytes(16);
|
|
88
|
+
const unk4 = this.readInt32();
|
|
89
|
+
}
|
|
84
90
|
if (this.header.saveVersion >= 21) {
|
|
85
91
|
}
|
|
86
92
|
else {
|
|
@@ -105,8 +111,8 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
105
111
|
if (this.compressionInfo.maxChunkContentSize <= 0) {
|
|
106
112
|
this.compressionInfo.maxChunkContentSize = chunkHeader.getInt32(8, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
107
113
|
}
|
|
108
|
-
const chunkCompressedLength = chunkHeader.getInt32(
|
|
109
|
-
const chunkUncompressedLength = chunkHeader.getInt32(
|
|
114
|
+
const chunkCompressedLength = chunkHeader.getInt32(33, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
115
|
+
const chunkUncompressedLength = chunkHeader.getInt32(25, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
110
116
|
totalUncompressedBodySize += chunkUncompressedLength;
|
|
111
117
|
const currentChunkSize = chunkCompressedLength;
|
|
112
118
|
let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
|
|
@@ -134,32 +140,74 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
134
140
|
this.maxByte = bigWholeChunk.buffer.byteLength;
|
|
135
141
|
this.bufferView = new DataView(bigWholeChunk.buffer);
|
|
136
142
|
const dataLength = this.readInt32();
|
|
137
|
-
if (totalUncompressedBodySize !== dataLength +
|
|
138
|
-
throw new parser_error_1.CorruptSaveError(`Possibly corrupt. Indicated size of total save body (${dataLength}) does not match the uncompressed real size of ${totalUncompressedBodySize}.`);
|
|
143
|
+
if (totalUncompressedBodySize !== dataLength + 8) {
|
|
144
|
+
throw new parser_error_1.CorruptSaveError(`Possibly corrupt. Indicated size of total save body (${dataLength + 8}) does not match the uncompressed real size of ${totalUncompressedBodySize}.`);
|
|
139
145
|
}
|
|
140
146
|
return {
|
|
141
147
|
concatenatedChunkLength: newChunkLength,
|
|
142
148
|
numChunks: currentChunks.length
|
|
143
149
|
};
|
|
144
150
|
}
|
|
145
|
-
|
|
151
|
+
readLevelsU8() {
|
|
146
152
|
if (!this.header) {
|
|
147
153
|
throw new parser_error_1.ParserError('ParserError', 'Header must be set before objects can be read.');
|
|
148
154
|
}
|
|
149
155
|
if (this.header.saveVersion < 29) {
|
|
150
|
-
throw new parser_error_1.UnsupportedVersionError('
|
|
156
|
+
throw new parser_error_1.UnsupportedVersionError('Game Version < U6 is not supported.');
|
|
151
157
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
if (this.header.saveHeaderType < 13) {
|
|
159
|
+
throw new parser_error_1.UnsupportedVersionError('Game Version < U8 is not supported in this package version. Consider downgrading to the latest package version supporting it, which is 0.0.34');
|
|
160
|
+
}
|
|
161
|
+
const unk = this.readInt32();
|
|
162
|
+
const cannotBeLevelCountWhatIsIt = this.readInt32();
|
|
163
|
+
const noneString = this.readString();
|
|
164
|
+
const unk2 = this.readInt32();
|
|
165
|
+
const hm1 = this.readInt32();
|
|
166
|
+
const unk3 = this.readInt32();
|
|
167
|
+
const noneString2 = this.readString();
|
|
168
|
+
const grids = {};
|
|
169
|
+
const readLevelsList = (childrenCount) => {
|
|
170
|
+
const hmm = this.readInt32();
|
|
171
|
+
const parentName = this.readString();
|
|
172
|
+
const whatever = this.readBytes(8);
|
|
173
|
+
grids[parentName] = {};
|
|
174
|
+
for (let i = 0; i < childrenCount; i++) {
|
|
175
|
+
const binaryLenIGuess = this.readInt32();
|
|
176
|
+
const levelInstanceName = this.readString();
|
|
177
|
+
const lod = /\_L([0-9]+)\_/.exec(levelInstanceName)[1];
|
|
178
|
+
const arr = grids[parentName][Number(lod)];
|
|
179
|
+
if (!arr) {
|
|
180
|
+
grids[parentName][Number(lod)] = [];
|
|
181
|
+
}
|
|
182
|
+
grids[parentName][Number(lod)].push(levelInstanceName);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
readLevelsList(1379);
|
|
186
|
+
readLevelsList(0);
|
|
187
|
+
readLevelsList(972);
|
|
188
|
+
readLevelsList(0);
|
|
189
|
+
readLevelsList(0);
|
|
190
|
+
const unk5 = this.readInt32();
|
|
191
|
+
const levels = [];
|
|
192
|
+
const levelCount = this.readInt32();
|
|
193
|
+
this.onProgressCallback(this.getBufferProgress(), `reading pack of ${levelCount} levels.`);
|
|
194
|
+
for (let i = 0; i < levelCount; i++) {
|
|
195
|
+
let levelSingleName = this.readString();
|
|
196
|
+
levels.push(level_class_1.Level.ReadLevel(this, levelSingleName, this.header.buildVersion));
|
|
158
197
|
}
|
|
159
|
-
|
|
160
|
-
|
|
198
|
+
level_class_1.Level.ReadCollectablesList(this);
|
|
199
|
+
this.onProgressCallback(this.getBufferProgress(), `finished reading levels pack of ${levelCount}.`);
|
|
200
|
+
if (this.getBufferPosition() < this.getBufferLength()) {
|
|
201
|
+
const appendedLevel = new level_class_1.Level(this.header.mapName);
|
|
202
|
+
levels.push(appendedLevel);
|
|
203
|
+
level_class_1.Level.ReadObjectHeaders(this, appendedLevel.objects, this.onProgressCallback);
|
|
204
|
+
appendedLevel.collectables = level_class_1.Level.ReadCollectablesList(this);
|
|
205
|
+
level_class_1.Level.ReadObjectContents(appendedLevel.name, this, appendedLevel.objects, this.header.buildVersion, this.onProgressCallback);
|
|
206
|
+
level_class_1.Level.ReadCollectablesList(this);
|
|
207
|
+
level_class_1.Level.ReadCollectablesList(this);
|
|
161
208
|
}
|
|
162
|
-
|
|
209
|
+
this.onProgressCallback(this.getBufferProgress(), 'finished parsing.');
|
|
210
|
+
return levels;
|
|
163
211
|
}
|
|
164
212
|
async readLevelsAsynchronously(writer) {
|
|
165
213
|
const numSubLevels = this.readInt32();
|
|
@@ -175,4 +223,15 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
175
223
|
}
|
|
176
224
|
}
|
|
177
225
|
SaveReader.EPOCH_TICKS = 621355968000000000n;
|
|
226
|
+
SaveReader.getRoughSaveVersion = (saveVersion, headerTypeVersion) => {
|
|
227
|
+
if (headerTypeVersion >= 13) {
|
|
228
|
+
return 'U8+';
|
|
229
|
+
}
|
|
230
|
+
else if (saveVersion >= 29) {
|
|
231
|
+
return 'U6/U7';
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
return '<U6';
|
|
235
|
+
}
|
|
236
|
+
};
|
|
178
237
|
exports.SaveReader = SaveReader;
|
|
@@ -78,7 +78,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
78
78
|
chunk.set(compressedChunk, compressionInfo.chunkHeaderSize);
|
|
79
79
|
const view = new DataView(chunk.buffer);
|
|
80
80
|
view.setInt32(0, compressionInfo.packageFileTag, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
81
|
-
view.setInt32(4,
|
|
81
|
+
view.setInt32(4, 572662306, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
82
82
|
view.setInt32(8, compressionInfo.maxChunkContentSize, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
83
83
|
view.setInt32(12, 0, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
84
84
|
view.setInt32(16, compressedChunk.byteLength, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
@@ -15,6 +15,8 @@ export type vec4 = vec3 & {
|
|
|
15
15
|
};
|
|
16
16
|
export declare const ParseVec4: (reader: BinaryReadable) => vec4;
|
|
17
17
|
export declare const SerializeVec4: (writer: ByteWriter, vec: vec4) => void;
|
|
18
|
+
export declare const ParseVec4f: (reader: BinaryReadable) => vec4;
|
|
19
|
+
export declare const SerializeVec4f: (writer: ByteWriter, vec: vec4) => void;
|
|
18
20
|
export declare const sub: (other: vec3, vec: vec3) => vec3;
|
|
19
21
|
export declare const add: (vec: vec3, other: vec3) => vec3;
|
|
20
22
|
export declare const length: (vec: vec3) => number;
|
|
@@ -25,12 +27,16 @@ export type vec3 = vec2 & {
|
|
|
25
27
|
};
|
|
26
28
|
export declare const ParseVec3: (reader: BinaryReadable) => vec3;
|
|
27
29
|
export declare const SerializeVec3: (writer: ByteWriter, vec: vec3) => void;
|
|
30
|
+
export declare const ParseVec3f: (reader: BinaryReadable) => vec3;
|
|
31
|
+
export declare const SerializeVec3f: (writer: ByteWriter, vec: vec3) => void;
|
|
28
32
|
export type vec2 = {
|
|
29
33
|
x: number;
|
|
30
34
|
y: number;
|
|
31
35
|
};
|
|
32
36
|
export declare const ParseVec2: (reader: BinaryReadable) => vec2;
|
|
33
37
|
export declare const SerializeVec2: (writer: ByteWriter, vec: vec2) => void;
|
|
38
|
+
export declare const ParseVec2f: (reader: BinaryReadable) => vec2;
|
|
39
|
+
export declare const SerializeVec2f: (writer: ByteWriter, vec: vec2) => void;
|
|
34
40
|
export type Transform = {
|
|
35
41
|
rotation: vec4;
|
|
36
42
|
translation: vec3;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SerializeTransform = exports.ParseTransform = exports.SerializeVec2 = exports.ParseVec2 = exports.SerializeVec3 = exports.ParseVec3 = exports.norm = exports.mult = exports.length = exports.add = exports.sub = exports.SerializeVec4 = exports.ParseVec4 = exports.ParseCol4BGRA = exports.SerializeCol4BGRA = exports.ParseCol4RGBA = exports.SerializeCol4RGBA = void 0;
|
|
3
|
+
exports.SerializeTransform = exports.ParseTransform = exports.SerializeVec2f = exports.ParseVec2f = exports.SerializeVec2 = exports.ParseVec2 = exports.SerializeVec3f = exports.ParseVec3f = exports.SerializeVec3 = exports.ParseVec3 = exports.norm = exports.mult = exports.length = exports.add = exports.sub = exports.SerializeVec4f = exports.ParseVec4f = exports.SerializeVec4 = exports.ParseVec4 = exports.ParseCol4BGRA = exports.SerializeCol4BGRA = exports.ParseCol4RGBA = exports.SerializeCol4RGBA = void 0;
|
|
4
4
|
const SerializeCol4RGBA = (writer, value) => {
|
|
5
5
|
writer.writeFloat(value.r);
|
|
6
6
|
writer.writeFloat(value.g);
|
|
@@ -36,15 +36,27 @@ exports.ParseCol4BGRA = ParseCol4BGRA;
|
|
|
36
36
|
const ParseVec4 = (reader) => {
|
|
37
37
|
return {
|
|
38
38
|
...((0, exports.ParseVec3)(reader)),
|
|
39
|
-
w: reader.
|
|
39
|
+
w: reader.readDouble()
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
42
|
exports.ParseVec4 = ParseVec4;
|
|
43
43
|
const SerializeVec4 = (writer, vec) => {
|
|
44
44
|
(0, exports.SerializeVec3)(writer, vec);
|
|
45
|
-
writer.
|
|
45
|
+
writer.writeDouble(vec.w);
|
|
46
46
|
};
|
|
47
47
|
exports.SerializeVec4 = SerializeVec4;
|
|
48
|
+
const ParseVec4f = (reader) => {
|
|
49
|
+
return {
|
|
50
|
+
...((0, exports.ParseVec3f)(reader)),
|
|
51
|
+
w: reader.readFloat32()
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
exports.ParseVec4f = ParseVec4f;
|
|
55
|
+
const SerializeVec4f = (writer, vec) => {
|
|
56
|
+
(0, exports.SerializeVec3f)(writer, vec);
|
|
57
|
+
writer.writeFloat(vec.w);
|
|
58
|
+
};
|
|
59
|
+
exports.SerializeVec4f = SerializeVec4f;
|
|
48
60
|
const sub = (other, vec) => ({ x: other.x - vec.x, y: other.y - vec.y, z: other.z - vec.z });
|
|
49
61
|
exports.sub = sub;
|
|
50
62
|
const add = (vec, other) => ({ x: vec.x + other.x, y: vec.y + other.y, z: vec.z + other.z });
|
|
@@ -58,38 +70,62 @@ exports.norm = norm;
|
|
|
58
70
|
const ParseVec3 = (reader) => {
|
|
59
71
|
return {
|
|
60
72
|
...((0, exports.ParseVec2)(reader)),
|
|
61
|
-
z: reader.
|
|
73
|
+
z: reader.readDouble()
|
|
62
74
|
};
|
|
63
75
|
};
|
|
64
76
|
exports.ParseVec3 = ParseVec3;
|
|
65
77
|
const SerializeVec3 = (writer, vec) => {
|
|
66
78
|
(0, exports.SerializeVec2)(writer, vec);
|
|
67
|
-
writer.
|
|
79
|
+
writer.writeDouble(vec.z);
|
|
68
80
|
};
|
|
69
81
|
exports.SerializeVec3 = SerializeVec3;
|
|
82
|
+
const ParseVec3f = (reader) => {
|
|
83
|
+
return {
|
|
84
|
+
...((0, exports.ParseVec2f)(reader)),
|
|
85
|
+
z: reader.readFloat32()
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
exports.ParseVec3f = ParseVec3f;
|
|
89
|
+
const SerializeVec3f = (writer, vec) => {
|
|
90
|
+
(0, exports.SerializeVec2f)(writer, vec);
|
|
91
|
+
writer.writeFloat(vec.z);
|
|
92
|
+
};
|
|
93
|
+
exports.SerializeVec3f = SerializeVec3f;
|
|
70
94
|
const ParseVec2 = (reader) => {
|
|
71
95
|
return {
|
|
72
|
-
x: reader.
|
|
73
|
-
y: reader.
|
|
96
|
+
x: reader.readDouble(),
|
|
97
|
+
y: reader.readDouble(),
|
|
74
98
|
};
|
|
75
99
|
};
|
|
76
100
|
exports.ParseVec2 = ParseVec2;
|
|
77
101
|
const SerializeVec2 = (writer, vec) => {
|
|
102
|
+
writer.writeDouble(vec.x);
|
|
103
|
+
writer.writeDouble(vec.y);
|
|
104
|
+
};
|
|
105
|
+
exports.SerializeVec2 = SerializeVec2;
|
|
106
|
+
const ParseVec2f = (reader) => {
|
|
107
|
+
return {
|
|
108
|
+
x: reader.readFloat32(),
|
|
109
|
+
y: reader.readFloat32(),
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
exports.ParseVec2f = ParseVec2f;
|
|
113
|
+
const SerializeVec2f = (writer, vec) => {
|
|
78
114
|
writer.writeFloat(vec.x);
|
|
79
115
|
writer.writeFloat(vec.y);
|
|
80
116
|
};
|
|
81
|
-
exports.
|
|
117
|
+
exports.SerializeVec2f = SerializeVec2f;
|
|
82
118
|
const ParseTransform = (reader) => {
|
|
83
119
|
return {
|
|
84
|
-
rotation: (0, exports.
|
|
85
|
-
translation: (0, exports.
|
|
86
|
-
scale3d: (0, exports.
|
|
120
|
+
rotation: (0, exports.ParseVec4f)(reader),
|
|
121
|
+
translation: (0, exports.ParseVec3f)(reader),
|
|
122
|
+
scale3d: (0, exports.ParseVec3f)(reader),
|
|
87
123
|
};
|
|
88
124
|
};
|
|
89
125
|
exports.ParseTransform = ParseTransform;
|
|
90
126
|
const SerializeTransform = (writer, transform) => {
|
|
91
|
-
(0, exports.
|
|
92
|
-
(0, exports.
|
|
93
|
-
(0, exports.
|
|
127
|
+
(0, exports.SerializeVec4f)(writer, transform.rotation);
|
|
128
|
+
(0, exports.SerializeVec3f)(writer, transform.translation);
|
|
129
|
+
(0, exports.SerializeVec3f)(writer, transform.scale3d);
|
|
94
130
|
};
|
|
95
131
|
exports.SerializeTransform = SerializeTransform;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etothepii/satisfactory-file-parser",
|
|
3
3
|
"author": "etothepii",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "A file parser for satisfactory files. Includes save files and blueprint files.",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
7
7
|
"main": "./build/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"typescript"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "jest --config=jest.config.json",
|
|
17
|
+
"test": "jest --config=jest.config.json --max_old_space_size=30720",
|
|
18
18
|
"build": "tsc && npm run bundle",
|
|
19
19
|
"bundle": "npx webpack && dts-bundle-generator -o dist/index.d.ts src/index.ts",
|
|
20
20
|
"prepublishOnly": "npm run build && npm version patch"
|