@etothepii/satisfactory-file-parser 0.4.15 → 0.4.17

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
@@ -18,7 +18,7 @@ Game Version Files of U5 and below are NOT supported.
18
18
  | <= U5 | ❌ |
19
19
  | U6 + U7 | ✅ 0.0.1 - 0.0.34 |
20
20
  | U8 | ✅ 0.1.20 - 0.3.7 |
21
- | U1.0 | ✅ >= 0.4.14 |
21
+ | U1.0 | ✅ >= 0.4.16 |
22
22
 
23
23
  # Installation via npm
24
24
  `npm install @etothepii/satisfactory-file-parser`
@@ -86,14 +86,19 @@ fs.writeFileSync('./MyModifiedSave.sav', Buffer.concat([fileHeader!, ...bodyChun
86
86
  ```
87
87
 
88
88
 
89
- ## Old Save Parsing. Deprecated.
90
- For reading a save file (`.sav`) and parse In-Memory, just pass a Buffer to the parser with the file content.
91
- ```js
92
- import * as fs from 'fs';
93
- import { Parser } from "@etothepii/satisfactory-file-parser";
89
+ ## Inspecting Save Objects
90
+ Once you have the complete parsing done, the JSON is a `SatisfactorySave` object.
91
+ You can for example loop through players and print their cached names and positions.
94
92
 
95
- const file = fs.readFileSync('./MySave.sav') as Buffer;
96
- const parsedSave = Parser.ParseSaveFile('MySave', file);
93
+ ```js
94
+ import { isSaveEntity, SatisfactorySave, SaveEntity, StrProperty } from '@etothepii/satisfactory-file-parser';
95
+
96
+ const objects = save.levels.flatMap(level => level.objects);
97
+ const players = objects.filter(obj => isSaveEntity(obj) && obj.typePath === '/Game/FactoryGame/Character/Player/Char_Player.Char_Player_C') as SaveEntity[];
98
+ for (const player of players) {
99
+ const name = (player.properties.mCachedPlayerName as StrProperty).value;
100
+ console.log(name, player.transform.translation);
101
+ }
97
102
  ```
98
103
 
99
104
 
@@ -138,6 +143,16 @@ So far this was just a private hobby project. But i figure some people actually
138
143
  If you find a bug or have feedback about the parser, you can just hit me up on the satisfactory discord `etothepii`.
139
144
 
140
145
  # Changelog
146
+ ### [0.4.17] (2024-10-05)
147
+ #### updated README
148
+ #### bugfix
149
+ * `ClientIdentityInfo` field names and structure got changed, since the meaning is now more clear.
150
+ * removed trailing object list from satisfactory save object.
151
+ * deleted entities references get serialized again, just based on collectables list.
152
+
153
+ ### [0.4.16] (2024-10-03)
154
+ #### bugfix
155
+ * exporting `isSaveEntity` and `isSaveComponent` again.
141
156
 
142
157
  ### [0.4.15] (2024-10-02)
143
158
  #### updated README
@@ -146,8 +161,7 @@ If you find a bug or have feedback about the parser, you can just hit me up on t
146
161
  ### [0.4.14] (2024-10-02)
147
162
  #### updated README
148
163
  * updated the code examples with more context
149
-
150
- ### Internal renamings (won't affect you if you stick to the code examples)
164
+ #### Internal renamings (won't affect you if you stick to the code examples)
151
165
  * improved the interface for abstract parser classes
152
166
  * extended some error log messages
153
167
  * added an additional check when parsing struct property InventoryItem, since ported saves often have a few more bytes.
package/build/index.d.ts CHANGED
@@ -21,8 +21,8 @@ export * from './parser/satisfactory/objects/property/generic/Uint32Property';
21
21
  export * from './parser/satisfactory/objects/property/generic/Uint64Property';
22
22
  export * from './parser/satisfactory/objects/property/generic/Uint8Property';
23
23
  export * from './parser/satisfactory/objects/property/special/SpecialAnyProperty';
24
- export { SaveComponent } from './parser/satisfactory/objects/SaveComponent';
25
- export { SaveEntity } from './parser/satisfactory/objects/SaveEntity';
24
+ export * from './parser/satisfactory/objects/SaveComponent';
25
+ export * from './parser/satisfactory/objects/SaveEntity';
26
26
  export * from './parser/satisfactory/objects/ue/MD5Hash';
27
27
  export * from './parser/satisfactory/objects/values/GUID';
28
28
  export * from './parser/satisfactory/objects/values/ObjectReference';
package/build/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.ReadableStreamParser = exports.Parser = exports.SaveStreamWriter = exports.SaveStreamJsonStringifier = exports.SaveWriter = exports.SaveReader = exports.BlueprintWriter = exports.BlueprintConfigWriter = exports.BlueprintReader = exports.BlueprintConfigReader = exports.ByteWriter = exports.ByteReader = exports.SatisfactorySave = exports.Level = exports.SaveEntity = exports.SaveComponent = exports.DataFields = void 0;
17
+ exports.ReadableStreamParser = exports.Parser = exports.SaveStreamWriter = exports.SaveStreamJsonStringifier = exports.SaveWriter = exports.SaveReader = exports.BlueprintWriter = exports.BlueprintConfigWriter = exports.BlueprintReader = exports.BlueprintConfigReader = exports.ByteWriter = exports.ByteReader = exports.SatisfactorySave = exports.Level = exports.DataFields = void 0;
18
18
  __exportStar(require("./parser/satisfactory/blueprint/blueprint.types"), exports);
19
19
  var DataFields_1 = require("./parser/satisfactory/objects/DataFields");
20
20
  Object.defineProperty(exports, "DataFields", { enumerable: true, get: function () { return DataFields_1.DataFields; } });
@@ -39,10 +39,8 @@ __exportStar(require("./parser/satisfactory/objects/property/generic/Uint32Prope
39
39
  __exportStar(require("./parser/satisfactory/objects/property/generic/Uint64Property"), exports);
40
40
  __exportStar(require("./parser/satisfactory/objects/property/generic/Uint8Property"), exports);
41
41
  __exportStar(require("./parser/satisfactory/objects/property/special/SpecialAnyProperty"), exports);
42
- var SaveComponent_1 = require("./parser/satisfactory/objects/SaveComponent");
43
- Object.defineProperty(exports, "SaveComponent", { enumerable: true, get: function () { return SaveComponent_1.SaveComponent; } });
44
- var SaveEntity_1 = require("./parser/satisfactory/objects/SaveEntity");
45
- Object.defineProperty(exports, "SaveEntity", { enumerable: true, get: function () { return SaveEntity_1.SaveEntity; } });
42
+ __exportStar(require("./parser/satisfactory/objects/SaveComponent"), exports);
43
+ __exportStar(require("./parser/satisfactory/objects/SaveEntity"), exports);
46
44
  __exportStar(require("./parser/satisfactory/objects/ue/MD5Hash"), exports);
47
45
  __exportStar(require("./parser/satisfactory/objects/values/GUID"), exports);
48
46
  __exportStar(require("./parser/satisfactory/objects/values/ObjectReference"), exports);
@@ -3,7 +3,6 @@ import { Blueprint } from "./satisfactory/blueprint/blueprint.types";
3
3
  import { SatisfactorySave } from "./satisfactory/save/satisfactory-save";
4
4
  export declare class Parser {
5
5
  static WriteSave(save: SatisfactorySave, onBinaryBeforeCompressing: (buffer: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void): ChunkSummary[];
6
- static ParseSaveFile(name: string, file: Buffer, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
7
6
  static WriteBlueprintFiles(blueprint: Blueprint, onMainFileBinaryBeforeCompressing?: (binary: ArrayBuffer) => void, onMainFileHeader?: (header: Uint8Array) => void, onMainFileChunk?: (chunk: Uint8Array) => void): {
8
7
  mainFileChunkSummary: ChunkSummary[];
9
8
  configFileBinary: ArrayBuffer;
@@ -3,8 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Parser = void 0;
4
4
  const blueprint_reader_1 = require("./satisfactory/blueprint/blueprint-reader");
5
5
  const blueprint_writer_1 = require("./satisfactory/blueprint/blueprint-writer");
6
- const satisfactory_save_1 = require("./satisfactory/save/satisfactory-save");
7
- const save_reader_1 = require("./satisfactory/save/save-reader");
8
6
  const save_writer_1 = require("./satisfactory/save/save-writer");
9
7
  class Parser {
10
8
  static WriteSave(save, onBinaryBeforeCompressing, onHeader, onChunk) {
@@ -18,21 +16,6 @@ class Parser {
18
16
  const chunkSummary = writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk);
19
17
  return chunkSummary;
20
18
  }
21
- static ParseSaveFile(name, file, onDecompressedSaveBody = () => { }, onProgress = () => { }) {
22
- const reader = new save_reader_1.SaveReader(new Uint8Array(file).buffer, onProgress);
23
- const header = reader.readHeader();
24
- const save = new satisfactory_save_1.SatisfactorySave(name, header);
25
- const inflateResult = reader.inflateChunks();
26
- onDecompressedSaveBody(reader.getBuffer());
27
- const gridHash = reader.readSaveBodyHash();
28
- save.gridHash = gridHash;
29
- const grids = reader.readGrids();
30
- save.grids = grids;
31
- save.levels = reader.readLevels();
32
- save.compressionInfo = reader.compressionInfo;
33
- save.trailingCollectedObjects = reader.trailingCollectedObjects;
34
- return save;
35
- }
36
19
  static WriteBlueprintFiles(blueprint, onMainFileBinaryBeforeCompressing = () => { }, onMainFileHeader = () => { }, onMainFileChunk = () => { }) {
37
20
  const blueprintWriter = new blueprint_writer_1.BlueprintWriter();
38
21
  blueprint_writer_1.BlueprintWriter.SerializeHeader(blueprintWriter, blueprint.header);
@@ -39,11 +39,8 @@ export type DynamicStructPropertyValue = {
39
39
  properties: PropertiesMap;
40
40
  };
41
41
  export type ClientIdentityInfo = {
42
- id: string;
43
- clientUnk1: number;
44
- clientUnk2: number;
45
- clientHashSize: number;
46
- someClientHash: number[];
42
+ offlineId: string;
43
+ accountIds: Record<number, number[]>;
47
44
  };
48
45
  export type GENERIC_STRUCT_PROPERTY_VALUE = BasicMultipleStructPropertyValue | BasicStructPropertyValue | BoxStructPropertyValue | RailroadTrackPositionStructPropertyValue | InventoryItemStructPropertyValue | FICFrameRangeStructPropertyValue | ClientIdentityInfo | DynamicStructPropertyValue | col4 | vec3 | vec4 | string;
49
46
  export declare class StructProperty extends AbstractBaseProperty {
@@ -35,7 +35,7 @@ class StructProperty extends BasicProperty_1.AbstractBaseProperty {
35
35
  struct.value = StructProperty.ParseValue(reader, struct.subtype, size);
36
36
  const readBytes = reader.getBufferPosition() - before;
37
37
  if (readBytes !== size) {
38
- if (size - readBytes === 4 || struct.subtype === 'ClientIdentityInfo') {
38
+ if (size - readBytes === 4) {
39
39
  reader.skipBytes(size - readBytes);
40
40
  }
41
41
  else {
@@ -64,7 +64,7 @@ class StructProperty extends BasicProperty_1.AbstractBaseProperty {
64
64
  value = (size === 16) ? util_types_1.vec4.ParseF(reader) : util_types_1.vec4.Parse(reader);
65
65
  break;
66
66
  case 'Box':
67
- value = (size === 25) ? {
67
+ value = ((size === 25) ? {
68
68
  min: util_types_1.vec3.ParseF(reader),
69
69
  max: util_types_1.vec3.ParseF(reader),
70
70
  isValid: reader.readByte() >= 1
@@ -72,7 +72,7 @@ class StructProperty extends BasicProperty_1.AbstractBaseProperty {
72
72
  min: util_types_1.vec3.Parse(reader),
73
73
  max: util_types_1.vec3.Parse(reader),
74
74
  isValid: reader.readByte() >= 1
75
- };
75
+ });
76
76
  break;
77
77
  case 'RailroadTrackPosition':
78
78
  value = {
@@ -89,12 +89,19 @@ class StructProperty extends BasicProperty_1.AbstractBaseProperty {
89
89
  value = reader.readString();
90
90
  break;
91
91
  case 'ClientIdentityInfo':
92
- const id = reader.readString();
93
- const clientUnk1 = reader.readInt32();
94
- const clientUnk2 = reader.readByte();
95
- const clientHashSize = reader.readInt32();
96
- const someClientHash = Array.from(reader.readBytes(clientHashSize));
97
- value = { id, clientUnk1, clientUnk2, clientHashSize, someClientHash };
92
+ const offlineId = reader.readString();
93
+ const numAccountIds = reader.readInt32();
94
+ const accountIds = {};
95
+ for (let i = 0; i < numAccountIds; i++) {
96
+ const platformFlagMaybe = reader.readByte();
97
+ const idSize = reader.readInt32();
98
+ const accountId = Array.from(reader.readBytes(idSize));
99
+ accountIds[platformFlagMaybe] = accountId;
100
+ }
101
+ value = {
102
+ offlineId,
103
+ accountIds
104
+ };
98
105
  break;
99
106
  case 'InventoryItem':
100
107
  const before = reader.getBufferPosition();
@@ -209,11 +216,13 @@ class StructProperty extends BasicProperty_1.AbstractBaseProperty {
209
216
  break;
210
217
  case 'ClientIdentityInfo':
211
218
  value = value;
212
- writer.writeString(value.id);
213
- writer.writeInt32(value.clientUnk1);
214
- writer.writeByte(value.clientUnk2);
215
- writer.writeInt32(value.clientHashSize);
216
- writer.writeBytesArray(value.someClientHash);
219
+ writer.writeString(value.offlineId);
220
+ writer.writeInt32(Object.values(value.accountIds).length);
221
+ for (const [platformFlagMaybe, accountId] of Object.entries(value.accountIds)) {
222
+ writer.writeByte(Number(platformFlagMaybe));
223
+ writer.writeInt32(accountId.length);
224
+ writer.writeBytesArray(accountId);
225
+ }
217
226
  break;
218
227
  case 'InventoryItem':
219
228
  value = value;
@@ -4,7 +4,6 @@ import { SaveComponent } from "../objects/SaveComponent";
4
4
  import { SaveEntity } from "../objects/SaveEntity";
5
5
  import { SaveObject } from "../objects/SaveObject";
6
6
  import { ObjectReference } from "../objects/values/ObjectReference";
7
- import { SaveReader } from "./save-reader";
8
7
  export declare class Level {
9
8
  name: string;
10
9
  objects: (SaveEntity | SaveComponent)[];
@@ -14,8 +13,6 @@ export declare class Level {
14
13
  static WriteLevel(writer: ByteWriter, level: Level, buildVersion: number): void;
15
14
  static SerializeObjectContents(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[], buildVersion: number, levelName: string): void;
16
15
  static ReadObjectHeaders(reader: BinaryReadable, objectsList: SaveObject[], onProgressCallback: (progress: number, msg?: string) => void): void;
17
- static ReadObjectContents(levelName: string, reader: BinaryReadable, objectsList: SaveObject[], buildVersion: number, onProgressCallback: (progress: number, msg?: string) => void): Promise<void>;
18
- static ReadLevel(reader: SaveReader, levelName: string, buildVersion: number): Level;
19
16
  static SerializeCollectablesList(writer: ByteWriter, collectables: ObjectReference[]): void;
20
17
  static ReadCollectablesList(reader: BinaryReadable): ObjectReference[];
21
18
  }
@@ -33,6 +33,7 @@ class Level {
33
33
  writer.writeInt32(0);
34
34
  writer.writeInt32(0);
35
35
  Level.SerializeObjectHeaders(writer, level.objects);
36
+ Level.SerializeCollectablesList(writer, level.collectables);
36
37
  writer.writeBinarySizeFromPosition(lenIndicatorHeaderAndDestroyedEntitiesSize, lenIndicatorHeaderAndDestroyedEntitiesSize + 8);
37
38
  Level.SerializeObjectContents(writer, level.objects, buildVersion, level.name);
38
39
  Level.SerializeCollectablesList(writer, level.collectables);
@@ -81,61 +82,6 @@ class Level {
81
82
  }
82
83
  }
83
84
  }
84
- static async ReadObjectContents(levelName, reader, objectsList, buildVersion, onProgressCallback) {
85
- const countEntities = reader.readInt32();
86
- if (countEntities !== objectsList.length) {
87
- throw new Error(`possibly corrupt. entity content count ${countEntities} does not equal object count of ${objectsList.length}`);
88
- }
89
- for (let i = 0; i < countEntities; i++) {
90
- if (countEntities >= 10000 && i % 10000 === 0) {
91
- onProgressCallback(reader.getBufferProgress(), `read object count [${(i + 1)}/${(countEntities + 1)}] in level ${levelName}`);
92
- }
93
- const obj = objectsList[i];
94
- obj.objectVersion = reader.readInt32();
95
- obj.unknownType2 = reader.readInt32();
96
- const binarySize = reader.readInt32();
97
- const before = reader.getBufferPosition();
98
- if ((0, SaveEntity_1.isSaveEntity)(obj)) {
99
- SaveEntity_1.SaveEntity.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
100
- }
101
- else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
102
- SaveComponent_1.SaveComponent.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
103
- }
104
- const after = reader.getBufferPosition();
105
- if (after - before !== binarySize) {
106
- console.warn('entity may be corrupt', obj.instanceName);
107
- }
108
- }
109
- }
110
- static ReadLevel(reader, levelName, buildVersion) {
111
- const level = new Level(levelName);
112
- const headersBinLen = reader.readInt32();
113
- const unk = reader.readInt32();
114
- const posBeforeHeaders = reader.getBufferPosition();
115
- Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
116
- let remainingSize = headersBinLen - (reader.getBufferPosition() - posBeforeHeaders);
117
- if (remainingSize > 0) {
118
- const doubledCollectablesIgnored = Level.ReadCollectablesList(reader);
119
- }
120
- else {
121
- }
122
- remainingSize = headersBinLen - (reader.getBufferPosition() - posBeforeHeaders);
123
- if (remainingSize !== 0) {
124
- console.warn('remaining size not 0. Save may be corrupt.', remainingSize, levelName);
125
- }
126
- const objectContentsBinLen = reader.readInt32();
127
- const unk2 = reader.readInt32();
128
- reader.onProgressCallback(reader.getBufferProgress());
129
- const posBeforeContents = reader.getBufferPosition();
130
- Level.ReadObjectContents(levelName, reader, level.objects, buildVersion, reader.onProgressCallback);
131
- const posAfterContents = reader.getBufferPosition();
132
- if (posAfterContents - posBeforeContents !== objectContentsBinLen) {
133
- console.warn('save seems corrupt.', this.name);
134
- }
135
- reader.onProgressCallback(reader.getBufferProgress());
136
- level.collectables = Level.ReadCollectablesList(reader);
137
- return level;
138
- }
139
85
  static SerializeCollectablesList(writer, collectables) {
140
86
  writer.writeInt32(collectables.length);
141
87
  for (const collectable of collectables) {
@@ -1,5 +1,4 @@
1
1
  import { ChunkCompressionInfo } from "../../file.types";
2
- import { ObjectReference } from "../objects/values/ObjectReference";
3
2
  import { Level } from "./level.class";
4
3
  import { ByteArray4, Grids } from "./save-reader";
5
4
  import { SatisfactorySaveHeader } from "./save.types";
@@ -9,7 +8,6 @@ export declare class SatisfactorySave {
9
8
  gridHash: ByteArray4;
10
9
  grids: Grids;
11
10
  levels: Level[];
12
- trailingCollectedObjects: ObjectReference[];
13
11
  compressionInfo?: ChunkCompressionInfo;
14
12
  constructor(name: string, header: SatisfactorySaveHeader);
15
13
  }
@@ -6,7 +6,6 @@ class SatisfactorySave {
6
6
  this.gridHash = [0, 0, 0, 0];
7
7
  this.grids = {};
8
8
  this.levels = [];
9
- this.trailingCollectedObjects = [];
10
9
  this.name = name;
11
10
  this.header = header;
12
11
  }
@@ -32,5 +32,4 @@ export declare class SaveReader extends ByteReader {
32
32
  };
33
33
  readSaveBodyHash: () => ByteArray4;
34
34
  readGrids: () => Grids;
35
- readLevels(): Level[];
36
35
  }
@@ -9,7 +9,6 @@ const alignment_enum_1 = require("../../byte/alignment.enum");
9
9
  const byte_reader_class_1 = require("../../byte/byte-reader.class");
10
10
  const parser_error_1 = require("../../error/parser.error");
11
11
  const MD5Hash_1 = require("../objects/ue/MD5Hash");
12
- const level_class_1 = require("./level.class");
13
12
  exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
14
13
  class SaveReader extends byte_reader_class_1.ByteReader {
15
14
  constructor(fileBuffer, onProgressCallback = () => { }) {
@@ -23,10 +22,10 @@ class SaveReader extends byte_reader_class_1.ByteReader {
23
22
  chunkHeaderSize: exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
24
23
  };
25
24
  this.readSaveBodyHash = () => {
26
- const unk = this.readInt32();
25
+ this.readInt32();
27
26
  const cannotBeLevelCountWhatIsIt = this.readInt32();
28
27
  const noneString = this.readString();
29
- const unk2 = this.readInt32();
28
+ this.readInt32();
30
29
  const gridValidationHash = Array.from(this.readBytes(4));
31
30
  const unk3 = this.readInt32();
32
31
  const noneString2 = this.readString();
@@ -163,35 +162,6 @@ class SaveReader extends byte_reader_class_1.ByteReader {
163
162
  numChunks: currentChunks.length
164
163
  };
165
164
  }
166
- readLevels() {
167
- if (!this.header) {
168
- throw new parser_error_1.ParserError('ParserError', 'Header must be set before objects can be read.');
169
- }
170
- const roughSaveVersion = SaveReader.getRoughSaveVersion(this.header.saveVersion, this.header.saveHeaderType);
171
- if (roughSaveVersion === '<U6') {
172
- throw new parser_error_1.UnsupportedVersionError('Game Version < U6 is not supported.');
173
- }
174
- else if (roughSaveVersion === 'U6/U7') {
175
- throw new parser_error_1.UnsupportedVersionError('Game Version U6/U7 is not supported in this package version. Consider downgrading to the latest package version supporting it, which is 0.0.34');
176
- }
177
- else if (roughSaveVersion === 'U8') {
178
- 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.3.7');
179
- }
180
- const levels = [];
181
- const levelCount = this.readInt32();
182
- this.onProgressCallback(this.getBufferProgress(), `reading pack of ${levelCount} levels.`);
183
- for (let i = 0; i < levelCount; i++) {
184
- let levelSingleName = this.readString();
185
- if (i % 500 === 0) {
186
- this.onProgressCallback(this.getBufferProgress(), `reading level [${(i + 1)}/${(levelCount + 1)}] ${levelSingleName}`);
187
- }
188
- levels.push(level_class_1.Level.ReadLevel(this, levelSingleName, this.header.buildVersion));
189
- }
190
- levels.push(level_class_1.Level.ReadLevel(this, this.header.mapName, this.header.buildVersion));
191
- const trailingStuffToIgnore = level_class_1.Level.ReadCollectablesList(this);
192
- this.onProgressCallback(this.getBufferProgress(), 'finished parsing.');
193
- return levels;
194
- }
195
165
  }
196
166
  exports.SaveReader = SaveReader;
197
167
  SaveReader.EPOCH_TICKS = 621355968000000000n;
@@ -72,6 +72,12 @@ class ReadableStreamParser {
72
72
  const objectCountToRead = Math.min(countObjectHeaders - totalReadObjectsInLevel, batchingSizeOfObjects);
73
73
  const objects = _a.ReadNObjectHeaders(reader, objectCountToRead);
74
74
  afterHeadersOfBatch = reader.getBufferPosition();
75
+ if (countObjectHeaders === totalReadObjectsInLevel + objects.length) {
76
+ const bytesLeft = afterAllHeaders - reader.getBufferPosition();
77
+ if (bytesLeft > 0) {
78
+ _a.ReadDestroyedEntityReferences(reader);
79
+ }
80
+ }
75
81
  if (totalReadObjectsInLevel === 0) {
76
82
  reader.skipBytes(afterAllHeaders - reader.getBufferPosition());
77
83
  const objectContentsBinLen = reader.readInt32();
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.15",
4
+ "version": "0.4.17",
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",