@etothepii/satisfactory-file-parser 0.4.16 → 0.4.19

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 etothepii
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -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.18] (2024-10-06)
147
+ #### Migrated repo to public github
148
+
149
+ ### [0.4.17] (2024-10-05)
150
+ #### updated README
151
+ #### bugfix
152
+ * `ClientIdentityInfo` field names and structure got changed, since the meaning is now more clear.
153
+ * removed trailing object list from satisfactory save object.
154
+ * deleted entities references get serialized again, just based on collectables list.
155
+
141
156
  ### [0.4.16] (2024-10-03)
142
157
  #### bugfix
143
158
  * exporting `isSaveEntity` and `isSaveComponent` again.
@@ -149,8 +164,7 @@ If you find a bug or have feedback about the parser, you can just hit me up on t
149
164
  ### [0.4.14] (2024-10-02)
150
165
  #### updated README
151
166
  * updated the code examples with more context
152
-
153
- ### Internal renamings (won't affect you if you stick to the code examples)
167
+ #### Internal renamings (won't affect you if you stick to the code examples)
154
168
  * improved the interface for abstract parser classes
155
169
  * extended some error log messages
156
170
  * added an additional check when parsing struct property InventoryItem, since ported saves often have a few more bytes.
@@ -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);
@@ -1807,6 +1807,10 @@ export declare const EDIT: {
1807
1807
  readonly TYPE_PATH: "/Game/FactoryGame/Buildable/Factory/ProjectAssembly/BP_ProjectAssembly.BP_ProjectAssembly_C";
1808
1808
  readonly CLASS_NAME: "BP_ProjectAssembly_C";
1809
1809
  };
1810
+ readonly SPACE_ELEVATOR: {
1811
+ readonly TYPE_PATH: "/Game/FactoryGame/Buildable/Factory/SpaceElevator/Build_SpaceElevator.Build_SpaceElevator_C";
1812
+ readonly CLASS_NAME: "Build_SpaceElevator_C";
1813
+ };
1810
1814
  readonly EXTRACTOR: {
1811
1815
  readonly MINER_MK_1: {
1812
1816
  readonly TYPE_PATH: "/Game/FactoryGame/Buildable/Factory/MinerMK1/Build_MinerMk1.Build_MinerMk1_C";
@@ -1813,6 +1813,10 @@ exports.EDIT = {
1813
1813
  TYPE_PATH: '/Game/FactoryGame/Buildable/Factory/ProjectAssembly/BP_ProjectAssembly.BP_ProjectAssembly_C',
1814
1814
  CLASS_NAME: 'BP_ProjectAssembly_C'
1815
1815
  },
1816
+ SPACE_ELEVATOR: {
1817
+ TYPE_PATH: '/Game/FactoryGame/Buildable/Factory/SpaceElevator/Build_SpaceElevator.Build_SpaceElevator_C',
1818
+ CLASS_NAME: 'Build_SpaceElevator_C'
1819
+ },
1816
1820
  EXTRACTOR: {
1817
1821
  MINER_MK_1: {
1818
1822
  TYPE_PATH: '/Game/FactoryGame/Buildable/Factory/MinerMK1/Build_MinerMk1.Build_MinerMk1_C'
@@ -50,8 +50,7 @@ class DataFields {
50
50
  reader.readInt32();
51
51
  const remainingLen = length - (reader.getBufferPosition() - start);
52
52
  obj.specialProperties = DataFields.ParseAdditionalSpecialProperties(reader, typePath, remainingLen);
53
- const end = reader.getBufferPosition();
54
- const remainingSize = length - (end - start);
53
+ const remainingSize = length - (reader.getBufferPosition() - start);
55
54
  if (remainingSize > 0) {
56
55
  obj.trailingData = Array.from(reader.readBytes(remainingSize));
57
56
  }
@@ -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
  }
@@ -1,9 +1,6 @@
1
1
  import { ByteReader } from "../../byte/byte-reader.class";
2
2
  import { ChunkCompressionInfo } from "../../file.types";
3
- import { ObjectReference } from "../objects/values/ObjectReference";
4
- import { Level } from "./level.class";
5
3
  import { RoughSaveVersion, SatisfactorySaveHeader } from "./save.types";
6
- export type ReadMode = 'stream' | 'whole';
7
4
  export declare const DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
8
5
  export type ByteArray4 = [number, number, number, number];
9
6
  export type Grids = {
@@ -19,8 +16,6 @@ export type Grids = {
19
16
  export declare class SaveReader extends ByteReader {
20
17
  onProgressCallback: (progress: number, msg?: string) => void;
21
18
  header: SatisfactorySaveHeader | undefined;
22
- levels: Level[];
23
- trailingCollectedObjects: ObjectReference[];
24
19
  compressionInfo: ChunkCompressionInfo;
25
20
  static readonly EPOCH_TICKS = 621355968000000000n;
26
21
  constructor(fileBuffer: ArrayBuffer, onProgressCallback?: (progress: number, msg?: string) => void);
@@ -32,5 +27,4 @@ export declare class SaveReader extends ByteReader {
32
27
  };
33
28
  readSaveBodyHash: () => ByteArray4;
34
29
  readGrids: () => Grids;
35
- readLevels(): Level[];
36
30
  }
@@ -9,24 +9,21 @@ 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 = () => { }) {
16
15
  super(fileBuffer, alignment_enum_1.Alignment.LITTLE_ENDIAN);
17
16
  this.onProgressCallback = onProgressCallback;
18
- this.levels = [];
19
- this.trailingCollectedObjects = [];
20
17
  this.compressionInfo = {
21
18
  packageFileTag: 0,
22
19
  maxUncompressedChunkContentSize: 0,
23
20
  chunkHeaderSize: exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
24
21
  };
25
22
  this.readSaveBodyHash = () => {
26
- const unk = this.readInt32();
23
+ this.readInt32();
27
24
  const cannotBeLevelCountWhatIsIt = this.readInt32();
28
25
  const noneString = this.readString();
29
- const unk2 = this.readInt32();
26
+ this.readInt32();
30
27
  const gridValidationHash = Array.from(this.readBytes(4));
31
28
  const unk3 = this.readInt32();
32
29
  const noneString2 = this.readString();
@@ -51,7 +48,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
51
48
  readGrid(758);
52
49
  readGrid(0);
53
50
  readGrid(0);
54
- const unk5 = this.readInt32();
51
+ this.readInt32();
55
52
  return grids;
56
53
  };
57
54
  }
@@ -163,35 +160,6 @@ class SaveReader extends byte_reader_class_1.ByteReader {
163
160
  numChunks: currentChunks.length
164
161
  };
165
162
  }
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
163
  }
196
164
  exports.SaveReader = SaveReader;
197
165
  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.16",
4
+ "version": "0.4.19",
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",
@@ -21,13 +21,13 @@
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "git+ssh://git@gitlab.com/satisfactory-modding/satisfactory-file-parser.git"
24
+ "url": "https://github.com/etothepii4/satisfactory-file-parser.git"
25
25
  },
26
26
  "license": "MIT",
27
27
  "bugs": {
28
- "url": "https://gitlab.com/satisfactory-modding/satisfactory-file-parser/issues"
28
+ "url": "https://github.com/etothepii4/satisfactory-file-parser/issues"
29
29
  },
30
- "homepage": "https://gitlab.com/satisfactory-modding/satisfactory-file-parser#readme",
30
+ "homepage": "https://github.com/etothepii4/satisfactory-file-parser#readme",
31
31
  "devDependencies": {
32
32
  "@types/jest": "^29.5.0",
33
33
  "@types/node": "^18.15.10",