@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 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
- Game Version Files of U5 and below are NOT supported. However, U6 and above is perfectly fine and parsable.
13
- - ✅ U7
14
- - U6
15
- - ❌ <= U5
16
-
17
- ### U8 is on the Horizon!
18
- We can not promise that U8 saves will work out of the box!
19
- If there will be changes, those will come very soon as soon as U8 is out on Experimental.
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`
@@ -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
- const levelParseResult = reader.readLevels();
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.ParseVec3)(reader);
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
- fields.specialProperties = DataFields.ParseAdditionalSpecialProperties(reader, typePath, start + length - reader.getBufferPosition() - 4);
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
- let remainingBytes = start + length - end;
32
- fields.trailingData = Array.from(reader.readBytes(remainingBytes));
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.ParseVec3)(reader);
58
- property.targetTranslation = (0, util_types_1.ParseVec3)(reader);
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
- throw new Error('unimplemented');
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 (unk2 !== 0) {
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.ParseVec3)(reader);
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.ParseVec4)(reader);
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.ParseVec3)(reader),
533
- max: (0, util_types_1.ParseVec3)(reader),
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.SerializeVec3)(writer, value);
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.SerializeVec4)(writer, value);
630
+ (0, util_types_1.SerializeVec4f)(writer, value);
623
631
  break;
624
632
  case 'Box':
625
633
  value = value;
626
- (0, util_types_1.SerializeVec3)(writer, value.min);
627
- (0, util_types_1.SerializeVec3)(writer, value.max);
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 readBytees = reader.getBufferPosition() - before;
759
- if (readBytees !== binarySize) {
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
- console.log(property.type, property.ueType);
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.ParseVec3)(reader)), ueType, index);
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.SerializeVec3)(writer, v));
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
- return SaveObject_1.SaveObject.ParseData(entity, newLen, reader, buildVersion, typePath);
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
- SaveComponent_1.SaveComponent.ParseData(obj, len, reader, buildVersion, obj.typePath);
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.readInt32();
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
- for (let i = 0; i < countCollected; i++) {
132
- const collectable = ObjectReference_1.ObjectReference.Parse(reader);
133
- collected.push(collectable);
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 = 48;
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
- readLevels(): Level[];
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 = 48;
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(32, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
109
- const chunkUncompressedLength = chunkHeader.getInt32(40, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
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 + 4) {
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
- readLevels() {
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('Support for < U6 is not yet implemented.');
156
+ throw new parser_error_1.UnsupportedVersionError('Game Version < U6 is not supported.');
151
157
  }
152
- const numSubLevels = this.readInt32();
153
- this.levels = new Array(numSubLevels + 1);
154
- for (let j = 0; j <= numSubLevels; j++) {
155
- let levelName = (j === numSubLevels) ? '' + this.header.mapName : this.readString();
156
- this.onProgressCallback(this.getBufferProgress(), `reading level [${(j + 1)}/${(numSubLevels + 1)}] ${levelName}`);
157
- this.levels[j] = level_class_1.Level.ReadLevel(this, levelName, this.header.buildVersion);
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
- if (this.getBufferPosition() < this.bufferView.byteLength) {
160
- this.trailingCollectedObjects = level_class_1.Level.ReadCollectablesList(this);
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
- return this.levels;
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, 0, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
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);
@@ -22,6 +22,7 @@ export interface SatisfactorySaveHeader {
22
22
  saveIdentifier?: string;
23
23
  fEditorObjectVersion?: number;
24
24
  }
25
+ export type RoughSaveVersion = '<U6' | 'U6/U7' | 'U8+';
25
26
  export type ResourceNodeEntry = {
26
27
  purity: string;
27
28
  pathName: string;
@@ -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.readFloat32()
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.writeFloat(vec.w);
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.readFloat32()
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.writeFloat(vec.z);
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.readFloat32(),
73
- y: reader.readFloat32(),
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.SerializeVec2 = SerializeVec2;
117
+ exports.SerializeVec2f = SerializeVec2f;
82
118
  const ParseTransform = (reader) => {
83
119
  return {
84
- rotation: (0, exports.ParseVec4)(reader),
85
- translation: (0, exports.ParseVec3)(reader),
86
- scale3d: (0, exports.ParseVec3)(reader),
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.SerializeVec4)(writer, transform.rotation);
92
- (0, exports.SerializeVec3)(writer, transform.translation);
93
- (0, exports.SerializeVec3)(writer, transform.scale3d);
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.0.34",
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"