@etothepii/satisfactory-file-parser 0.1.1 → 0.1.4
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 +1 -1
- package/build/parser/parser.d.ts +0 -2
- package/build/parser/parser.js +2 -14
- package/build/parser/satisfactory/blueprint/blueprint-reader.d.ts +1 -0
- package/build/parser/satisfactory/blueprint/blueprint-reader.js +26 -4
- package/build/parser/satisfactory/objects/DataFields.d.ts +3 -6
- package/build/parser/satisfactory/objects/DataFields.js +13 -20
- package/build/parser/satisfactory/objects/SaveEntity.js +1 -9
- package/build/parser/satisfactory/objects/SaveObject.d.ts +9 -2
- package/build/parser/satisfactory/objects/SaveObject.js +7 -3
- package/build/parser/satisfactory/save/level.class.js +21 -34
- package/build/parser/satisfactory/save/satisfactory-save.d.ts +2 -0
- package/build/parser/satisfactory/save/satisfactory-save.js +1 -0
- package/build/parser/satisfactory/save/save-reader.d.ts +9 -13
- package/build/parser/satisfactory/save/save-reader.js +49 -56
- package/build/parser/satisfactory/save/save-writer.js +3 -0
- package/build/parser/satisfactory/save/save.types.d.ts +2 -0
- package/build/parser/stream/stream-parser.d.ts +40 -0
- package/build/parser/stream/stream-parser.js +225 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ U8 has only read support so far and only for save files, not for blueprint files
|
|
|
19
19
|
|:--------------:|:-----------------------------|
|
|
20
20
|
| <= U5 | ❌ |
|
|
21
21
|
| U6 + U7 | ✅ 0.0.1 - 0.0.34 |
|
|
22
|
-
| U8 | ⚠️ >= 0.1.
|
|
22
|
+
| U8 | ⚠️ >= 0.1.3 (Reading only) |
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
## Installation via npm
|
package/build/parser/parser.d.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { ChunkSummary } from "./file.types";
|
|
3
3
|
import { Blueprint } from "./satisfactory/blueprint/blueprint.types";
|
|
4
4
|
import { SatisfactorySave } from "./satisfactory/save/satisfactory-save";
|
|
5
|
-
import { SaveProjectionConfig } from "./satisfactory/save/save-reader";
|
|
6
5
|
export declare class Parser {
|
|
7
6
|
static WriteSave(save: SatisfactorySave, onBinaryBeforeCompressing: (buffer: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void): ChunkSummary[];
|
|
8
7
|
static ParseSaveFile(file: Buffer, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
|
|
@@ -11,6 +10,5 @@ export declare class Parser {
|
|
|
11
10
|
configFileBinary: ArrayBuffer;
|
|
12
11
|
};
|
|
13
12
|
static ParseBlueprintFiles(name: string, blueprintFile: Buffer, blueprintConfigFile: Buffer, onDecompressedBlueprintBody?: (buffer: ArrayBuffer) => void): Blueprint;
|
|
14
|
-
static ProjectSave(save: SatisfactorySave, config: SaveProjectionConfig): SatisfactorySave;
|
|
15
13
|
static JSONStringifyModified: (obj: any, indent?: number) => string;
|
|
16
14
|
}
|
package/build/parser/parser.js
CHANGED
|
@@ -3,7 +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 level_class_1 = require("./satisfactory/save/level.class");
|
|
7
6
|
const satisfactory_save_1 = require("./satisfactory/save/satisfactory-save");
|
|
8
7
|
const save_reader_1 = require("./satisfactory/save/save-reader");
|
|
9
8
|
const save_writer_1 = require("./satisfactory/save/save-writer");
|
|
@@ -23,7 +22,8 @@ class Parser {
|
|
|
23
22
|
const save = new satisfactory_save_1.SatisfactorySave(header);
|
|
24
23
|
const inflateResult = reader.inflateChunks();
|
|
25
24
|
onDecompressedSaveBody(reader.getBuffer());
|
|
26
|
-
|
|
25
|
+
reader.readGrids();
|
|
26
|
+
save.levels = reader.readLevels();
|
|
27
27
|
save.compressionInfo = reader.compressionInfo;
|
|
28
28
|
save.trailingCollectedObjects = reader.trailingCollectedObjects;
|
|
29
29
|
return save;
|
|
@@ -62,18 +62,6 @@ class Parser {
|
|
|
62
62
|
};
|
|
63
63
|
return blueprint;
|
|
64
64
|
}
|
|
65
|
-
static ProjectSave(save, config) {
|
|
66
|
-
return {
|
|
67
|
-
header: save.header,
|
|
68
|
-
trailingCollectedObjects: save.trailingCollectedObjects,
|
|
69
|
-
levels: save.levels.map(level => {
|
|
70
|
-
const lvl = new level_class_1.Level(level.name);
|
|
71
|
-
lvl.collectables = level.collectables;
|
|
72
|
-
lvl.objects = level.objects.filter(obj => (0, save_reader_1.projectionFilterApplies)(config, obj));
|
|
73
|
-
return lvl;
|
|
74
|
-
})
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
65
|
}
|
|
78
66
|
Parser.JSONStringifyModified = (obj, indent = 0) => JSON.stringify(obj, (key, value) => {
|
|
79
67
|
if (typeof value === 'bigint') {
|
|
@@ -10,6 +10,7 @@ export declare class BlueprintReader extends ByteReader {
|
|
|
10
10
|
static ReadHeader(reader: BinaryReadable): BlueprintHeader;
|
|
11
11
|
inflateChunks(): any;
|
|
12
12
|
static ParseObjects(reader: ByteReader): (SaveEntity | SaveComponent)[];
|
|
13
|
+
private static ReadBlueprintObjectContents;
|
|
13
14
|
}
|
|
14
15
|
export declare class BlueprintConfigReader extends ByteReader {
|
|
15
16
|
bluePrintConfigBuffer: ArrayBuffer;
|
|
@@ -8,7 +8,10 @@ const pako_1 = __importDefault(require("pako"));
|
|
|
8
8
|
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
|
+
const SaveComponent_1 = require("../objects/SaveComponent");
|
|
12
|
+
const SaveEntity_1 = require("../objects/SaveEntity");
|
|
11
13
|
const level_class_1 = require("../save/level.class");
|
|
14
|
+
const save_reader_1 = require("../save/save-reader");
|
|
12
15
|
const util_types_1 = require("../structs/util.types");
|
|
13
16
|
class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
14
17
|
constructor(bluePrintBuffer) {
|
|
@@ -16,7 +19,7 @@ class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
|
16
19
|
this.compressionInfo = {
|
|
17
20
|
packageFileTag: 0,
|
|
18
21
|
maxChunkContentSize: 0,
|
|
19
|
-
chunkHeaderSize:
|
|
22
|
+
chunkHeaderSize: save_reader_1.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
static ReadHeader(reader) {
|
|
@@ -59,8 +62,8 @@ class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
|
59
62
|
if (this.compressionInfo.maxChunkContentSize <= 0) {
|
|
60
63
|
this.compressionInfo.maxChunkContentSize = chunkHeader.getInt32(8, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
61
64
|
}
|
|
62
|
-
const chunkCompressedLength = chunkHeader.getInt32(
|
|
63
|
-
const chunkUncompressedLength = chunkHeader.getInt32(
|
|
65
|
+
const chunkCompressedLength = chunkHeader.getInt32(33, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
66
|
+
const chunkUncompressedLength = chunkHeader.getInt32(25, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
64
67
|
totalUncompressedBodySize += chunkUncompressedLength;
|
|
65
68
|
const currentChunkSize = chunkCompressedLength;
|
|
66
69
|
let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
|
|
@@ -98,9 +101,28 @@ class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
|
98
101
|
const objectHeadersBinarySize = reader.readInt32();
|
|
99
102
|
let objects = [];
|
|
100
103
|
level_class_1.Level.ReadObjectHeaders(reader, objects, () => { });
|
|
101
|
-
|
|
104
|
+
const suomeChecksumThing = reader.readInt32();
|
|
105
|
+
BlueprintReader.ReadBlueprintObjectContents('', reader, objects, 0);
|
|
106
|
+
const pos = reader.getBufferPosition();
|
|
107
|
+
console.log(pos, reader.getBufferLength(), reader.getBufferProgress());
|
|
102
108
|
return objects;
|
|
103
109
|
}
|
|
110
|
+
static ReadBlueprintObjectContents(levelName, reader, objectsList, buildVersion) {
|
|
111
|
+
const countEntities = reader.readInt32();
|
|
112
|
+
for (let i = 0; i < countEntities; i++) {
|
|
113
|
+
const len = reader.readInt32();
|
|
114
|
+
if (len === 0) {
|
|
115
|
+
throw new parser_error_1.CorruptSaveError(`check number is a wrong value (${len}). This normally indicates a corrupt entity or save.`);
|
|
116
|
+
}
|
|
117
|
+
const obj = objectsList[i];
|
|
118
|
+
if ((0, SaveEntity_1.isSaveEntity)(obj)) {
|
|
119
|
+
SaveEntity_1.SaveEntity.ParseData(obj, len, reader, buildVersion, obj.typePath);
|
|
120
|
+
}
|
|
121
|
+
else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
|
|
122
|
+
SaveComponent_1.SaveComponent.ParseData(obj, len, reader, buildVersion, obj.typePath);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
104
126
|
}
|
|
105
127
|
exports.BlueprintReader = BlueprintReader;
|
|
106
128
|
class BlueprintConfigReader extends byte_reader_class_1.ByteReader {
|
|
@@ -4,6 +4,7 @@ import { SaveWriter } from "../save/save-writer";
|
|
|
4
4
|
import { vec3 } from "../structs/util.types";
|
|
5
5
|
import { ObjectReference } from "./ObjectReference";
|
|
6
6
|
import { AbstractBaseProperty } from "./Property";
|
|
7
|
+
import { SaveObject } from "./SaveObject";
|
|
7
8
|
export type SpecialAnyProperty = {} | PowerLineSpecialProperty;
|
|
8
9
|
export type PowerLineSpecialProperty = {
|
|
9
10
|
num: number;
|
|
@@ -27,15 +28,11 @@ export type ConveyorSpecialProperty = {
|
|
|
27
28
|
items: ItemSpecialProperty[];
|
|
28
29
|
};
|
|
29
30
|
export declare class DataFields {
|
|
30
|
-
properties: AbstractBaseProperty[];
|
|
31
|
-
specialProperties: SpecialAnyProperty;
|
|
32
|
-
trailingData: number[];
|
|
33
|
-
shouldBeNulled: boolean;
|
|
34
31
|
constructor();
|
|
35
|
-
static
|
|
32
|
+
static ParseProperties(obj: SaveObject, length: number, reader: BinaryReadable, buildVersion: number, typePath: string): void;
|
|
36
33
|
static ParseAdditionalSpecialProperties(reader: BinaryReadable, typePath: string, remainingLen: number): SpecialAnyProperty;
|
|
37
34
|
static ParseProperty(reader: BinaryReadable, buildVersion: number, propertyName: string): AbstractBaseProperty | null;
|
|
38
|
-
static Serialize(
|
|
35
|
+
static Serialize(obj: SaveObject, writer: SaveWriter, buildVersion: number, typePath: string): void;
|
|
39
36
|
static SerializeAdditionalSpecialProperties(writer: ByteWriter, typePath: string, property: SpecialAnyProperty): void;
|
|
40
37
|
static SerializeProperty(writer: ByteWriter, property: AbstractBaseProperty, propertyName: string, buildVersion: number): void;
|
|
41
38
|
}
|
|
@@ -5,39 +5,32 @@ const util_types_1 = require("../structs/util.types");
|
|
|
5
5
|
const Property_1 = require("./Property");
|
|
6
6
|
class DataFields {
|
|
7
7
|
constructor() {
|
|
8
|
-
this.properties = [];
|
|
9
|
-
this.specialProperties = {};
|
|
10
|
-
this.trailingData = [];
|
|
11
|
-
this.shouldBeNulled = false;
|
|
12
8
|
}
|
|
13
|
-
static
|
|
9
|
+
static ParseProperties(obj, length, reader, buildVersion, typePath) {
|
|
14
10
|
const start = reader.getBufferPosition();
|
|
15
|
-
|
|
11
|
+
obj.properties = {};
|
|
16
12
|
if (length === 0) {
|
|
17
|
-
|
|
18
|
-
return fields;
|
|
13
|
+
return;
|
|
19
14
|
}
|
|
20
15
|
let propertyName = reader.readString();
|
|
21
16
|
while (propertyName !== 'None') {
|
|
22
17
|
const property = DataFields.ParseProperty(reader, buildVersion, propertyName);
|
|
23
|
-
|
|
18
|
+
obj.properties[property.name] = property;
|
|
24
19
|
propertyName = reader.readString();
|
|
25
20
|
}
|
|
26
|
-
let
|
|
27
|
-
if (
|
|
21
|
+
let padding = reader.readInt32();
|
|
22
|
+
if (padding !== 0) {
|
|
28
23
|
}
|
|
29
24
|
const remainingLen = length - (reader.getBufferPosition() - start);
|
|
30
|
-
|
|
31
|
-
fields.specialProperties = DataFields.ParseAdditionalSpecialProperties(reader, typePath, remainingLen);
|
|
25
|
+
obj.specialProperties = DataFields.ParseAdditionalSpecialProperties(reader, typePath, remainingLen);
|
|
32
26
|
const end = reader.getBufferPosition();
|
|
33
27
|
const newWayOfCalculatingReminingSize = length - (end - start);
|
|
34
28
|
if (newWayOfCalculatingReminingSize > 0) {
|
|
35
|
-
reader.readBytes(newWayOfCalculatingReminingSize);
|
|
29
|
+
obj.trailingData = Array.from(reader.readBytes(newWayOfCalculatingReminingSize));
|
|
36
30
|
}
|
|
37
31
|
else if (newWayOfCalculatingReminingSize < 0) {
|
|
38
|
-
|
|
32
|
+
throw new Error(`Unexpected. Read more bytes than are indicated for this etity. bytes left to read is ${newWayOfCalculatingReminingSize}`);
|
|
39
33
|
}
|
|
40
|
-
return fields;
|
|
41
34
|
}
|
|
42
35
|
static ParseAdditionalSpecialProperties(reader, typePath, remainingLen) {
|
|
43
36
|
let property;
|
|
@@ -180,15 +173,15 @@ class DataFields {
|
|
|
180
173
|
}
|
|
181
174
|
return currentProperty;
|
|
182
175
|
}
|
|
183
|
-
static Serialize(
|
|
184
|
-
for (const property of
|
|
176
|
+
static Serialize(obj, writer, buildVersion, typePath) {
|
|
177
|
+
for (const property of Object.values(obj.properties)) {
|
|
185
178
|
writer.writeString(property.name);
|
|
186
179
|
DataFields.SerializeProperty(writer, property, property.name, buildVersion);
|
|
187
180
|
}
|
|
188
181
|
writer.writeString('None');
|
|
189
182
|
writer.writeInt32(0);
|
|
190
|
-
DataFields.SerializeAdditionalSpecialProperties(writer, typePath,
|
|
191
|
-
writer.writeBytesArray(
|
|
183
|
+
DataFields.SerializeAdditionalSpecialProperties(writer, typePath, obj.specialProperties);
|
|
184
|
+
writer.writeBytesArray(obj.trailingData);
|
|
192
185
|
}
|
|
193
186
|
static SerializeAdditionalSpecialProperties(writer, typePath, property) {
|
|
194
187
|
switch (typePath) {
|
|
@@ -34,23 +34,15 @@ 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
37
|
const afterSizeIndicator = reader.getBufferPosition();
|
|
40
|
-
var newLen = length - 12;
|
|
41
38
|
entity.parentObjectRoot = reader.readString();
|
|
42
|
-
if (entity.parentObjectRoot.length > 0)
|
|
43
|
-
newLen -= entity.parentObjectRoot.length + 1;
|
|
44
39
|
entity.parentObjectName = reader.readString();
|
|
45
|
-
if (entity.parentObjectName.length > 0)
|
|
46
|
-
newLen -= entity.parentObjectName.length + 1;
|
|
47
40
|
var componentCount = reader.readInt32();
|
|
48
41
|
for (let i = 0; i < componentCount; i++) {
|
|
49
42
|
var componentRef = ObjectReference_1.ObjectReference.Parse(reader);
|
|
50
43
|
entity.components.push(componentRef);
|
|
51
|
-
newLen -= 10 + componentRef.levelName.length + componentRef.pathName.length;
|
|
52
44
|
}
|
|
53
|
-
const remainingSize =
|
|
45
|
+
const remainingSize = length - (reader.getBufferPosition() - afterSizeIndicator);
|
|
54
46
|
return SaveObject_1.SaveObject.ParseData(entity, remainingSize, reader, buildVersion, typePath);
|
|
55
47
|
}
|
|
56
48
|
static SerializeHeader(writer, entity) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BinaryReadable } from "../../byte/binary-readable.interface";
|
|
2
2
|
import { ByteWriter } from "../../byte/byte-writer.class";
|
|
3
|
-
import {
|
|
3
|
+
import { SpecialAnyProperty } from "./DataFields";
|
|
4
|
+
import { AbstractBaseProperty } from "./Property";
|
|
4
5
|
export interface SaveObjectHeader {
|
|
5
6
|
typePath: string;
|
|
6
7
|
rootObject: string;
|
|
@@ -10,7 +11,13 @@ export declare abstract class SaveObject implements SaveObjectHeader {
|
|
|
10
11
|
typePath: string;
|
|
11
12
|
rootObject: string;
|
|
12
13
|
instanceName: string;
|
|
13
|
-
|
|
14
|
+
properties: {
|
|
15
|
+
[name: string]: AbstractBaseProperty;
|
|
16
|
+
};
|
|
17
|
+
specialProperties: SpecialAnyProperty;
|
|
18
|
+
trailingData: number[];
|
|
19
|
+
unknownType1: number;
|
|
20
|
+
unknownType2: number;
|
|
14
21
|
constructor(typePath: string, rootObject: string, instanceName: string);
|
|
15
22
|
protected static ParseHeader(reader: BinaryReadable, obj: SaveObject): void;
|
|
16
23
|
protected static SerializeHeader(writer: ByteWriter, obj: SaveObject): void;
|
|
@@ -7,7 +7,11 @@ class SaveObject {
|
|
|
7
7
|
this.typePath = typePath;
|
|
8
8
|
this.rootObject = rootObject;
|
|
9
9
|
this.instanceName = instanceName;
|
|
10
|
-
this.
|
|
10
|
+
this.properties = {};
|
|
11
|
+
this.specialProperties = {};
|
|
12
|
+
this.trailingData = [];
|
|
13
|
+
this.unknownType1 = 0;
|
|
14
|
+
this.unknownType2 = 0;
|
|
11
15
|
}
|
|
12
16
|
static ParseHeader(reader, obj) {
|
|
13
17
|
obj.typePath = reader.readString();
|
|
@@ -20,10 +24,10 @@ class SaveObject {
|
|
|
20
24
|
writer.writeString(obj.instanceName);
|
|
21
25
|
}
|
|
22
26
|
static ParseData(obj, length, reader, buildVersion, typePath) {
|
|
23
|
-
|
|
27
|
+
DataFields_1.DataFields.ParseProperties(obj, length, reader, buildVersion, typePath);
|
|
24
28
|
}
|
|
25
29
|
static SerializeData(writer, obj, buildVersion) {
|
|
26
|
-
DataFields_1.DataFields.Serialize(
|
|
30
|
+
DataFields_1.DataFields.Serialize(obj, writer, buildVersion, obj.typePath);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
exports.SaveObject = SaveObject;
|
|
@@ -77,10 +77,8 @@ class Level {
|
|
|
77
77
|
break;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
const unk = reader.readInt32();
|
|
81
80
|
}
|
|
82
81
|
static async ReadObjectContents(levelName, reader, objectsList, buildVersion, onProgressCallback) {
|
|
83
|
-
const posBefore = reader.getBufferPosition();
|
|
84
82
|
const countEntities = reader.readInt32();
|
|
85
83
|
if (countEntities !== objectsList.length) {
|
|
86
84
|
throw new Error(`possibly corrupt. entity content count ${countEntities} does not equal object count of ${objectsList.length}`);
|
|
@@ -89,56 +87,45 @@ class Level {
|
|
|
89
87
|
if (i % 1000 === 0) {
|
|
90
88
|
onProgressCallback(reader.getBufferProgress());
|
|
91
89
|
}
|
|
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
|
-
}
|
|
96
|
-
const before = reader.getBufferPosition();
|
|
97
90
|
const obj = objectsList[i];
|
|
91
|
+
obj.unknownType1 = reader.readInt32();
|
|
92
|
+
obj.unknownType2 = reader.readInt32();
|
|
93
|
+
const binarySize = reader.readInt32();
|
|
94
|
+
const before = reader.getBufferPosition();
|
|
98
95
|
if ((0, SaveEntity_1.isSaveEntity)(obj)) {
|
|
99
|
-
SaveEntity_1.SaveEntity.ParseData(obj,
|
|
96
|
+
SaveEntity_1.SaveEntity.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
|
|
100
97
|
}
|
|
101
98
|
else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
|
|
102
|
-
|
|
103
|
-
const wtf2 = reader.readInt32();
|
|
104
|
-
SaveComponent_1.SaveComponent.ParseData(obj, wtf2, reader, buildVersion, obj.typePath);
|
|
99
|
+
SaveComponent_1.SaveComponent.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
|
|
105
100
|
}
|
|
106
101
|
const after = reader.getBufferPosition();
|
|
107
|
-
if (after - before !==
|
|
102
|
+
if (after - before !== binarySize) {
|
|
103
|
+
console.warn('entity may be corrupt', obj.instanceName);
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
|
-
const posAfter = reader.getBufferPosition();
|
|
111
106
|
}
|
|
112
107
|
static ReadLevel(reader, levelName, buildVersion) {
|
|
113
108
|
const level = new Level(levelName);
|
|
114
109
|
const levelStartPos = reader.getBufferPosition();
|
|
115
|
-
const
|
|
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();
|
|
110
|
+
const headersBinLen = reader.readInt32();
|
|
135
111
|
const unk = reader.readInt32();
|
|
112
|
+
const posBeforeHeaders = reader.getBufferPosition();
|
|
136
113
|
Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
|
|
114
|
+
const remainingSize = headersBinLen - (reader.getBufferPosition() - posBeforeHeaders);
|
|
115
|
+
if (remainingSize > 0) {
|
|
116
|
+
reader.readBytes(remainingSize);
|
|
117
|
+
}
|
|
118
|
+
const objectContentsBinLen = reader.readInt32();
|
|
137
119
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
138
120
|
level.collectables = Level.ReadCollectablesList(reader);
|
|
121
|
+
const posBeforeContents = reader.getBufferPosition();
|
|
139
122
|
Level.ReadObjectContents(levelName, reader, level.objects, buildVersion, reader.onProgressCallback);
|
|
123
|
+
const posAfterContents = reader.getBufferPosition();
|
|
124
|
+
if (posAfterContents - posBeforeContents !== objectContentsBinLen) {
|
|
125
|
+
console.warn('save seems corrupt.', this.name);
|
|
126
|
+
}
|
|
140
127
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
141
|
-
Level.ReadCollectablesList(reader);
|
|
128
|
+
level.collectables = Level.ReadCollectablesList(reader);
|
|
142
129
|
return level;
|
|
143
130
|
}
|
|
144
131
|
static SerializeCollectablesList(writer, collectables) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ChunkCompressionInfo } from "../../file.types";
|
|
2
2
|
import { ObjectReference } from "../objects/ObjectReference";
|
|
3
3
|
import { Level } from "./level.class";
|
|
4
|
+
import { Grids } from "./save-reader";
|
|
4
5
|
import { SatisfactorySaveHeader } from "./save.types";
|
|
5
6
|
export declare class SatisfactorySave {
|
|
6
7
|
header: SatisfactorySaveHeader;
|
|
8
|
+
grids: Grids;
|
|
7
9
|
levels: Level[];
|
|
8
10
|
trailingCollectedObjects: ObjectReference[];
|
|
9
11
|
compressionInfo?: ChunkCompressionInfo;
|
|
@@ -2,21 +2,15 @@ import { ByteReader } from "../../byte/byte-reader.class";
|
|
|
2
2
|
import { ChunkCompressionInfo } from "../../file.types";
|
|
3
3
|
import { SaveStreamWriter } from "../../stream/save-stream-writer.class";
|
|
4
4
|
import { ObjectReference } from "../objects/ObjectReference";
|
|
5
|
-
import { SaveComponent } from "../objects/SaveComponent";
|
|
6
|
-
import { SaveEntity } from "../objects/SaveEntity";
|
|
7
5
|
import { Level } from "./level.class";
|
|
8
|
-
import { SatisfactorySaveHeader } from "./save.types";
|
|
9
|
-
export type EntityPathFilter = {
|
|
10
|
-
behavior: 'whitelist' | 'blacklist';
|
|
11
|
-
list: string[];
|
|
12
|
-
};
|
|
13
|
-
export type SaveProjectionConfig = {
|
|
14
|
-
entityPathFilter: EntityPathFilter;
|
|
15
|
-
includeComponents: boolean;
|
|
16
|
-
};
|
|
17
|
-
export declare const projectionFilterApplies: (config: SaveProjectionConfig, object: SaveComponent | SaveEntity) => boolean;
|
|
6
|
+
import { RoughSaveVersion, SatisfactorySaveHeader } from "./save.types";
|
|
18
7
|
export type ReadMode = 'stream' | 'whole';
|
|
19
8
|
export declare const DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
9
|
+
export type Grids = {
|
|
10
|
+
[parentName: string]: {
|
|
11
|
+
[level: number]: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
20
14
|
export declare class SaveReader extends ByteReader {
|
|
21
15
|
onProgressCallback: (progress: number, msg?: string) => void;
|
|
22
16
|
header: SatisfactorySaveHeader | undefined;
|
|
@@ -26,10 +20,12 @@ export declare class SaveReader extends ByteReader {
|
|
|
26
20
|
static readonly EPOCH_TICKS = 621355968000000000n;
|
|
27
21
|
constructor(fileBuffer: ArrayBuffer, onProgressCallback?: (progress: number, msg?: string) => void);
|
|
28
22
|
readHeader(): SatisfactorySaveHeader;
|
|
23
|
+
static getRoughSaveVersion: (saveVersion: number, headerTypeVersion: number) => RoughSaveVersion;
|
|
29
24
|
inflateChunks(): {
|
|
30
25
|
concatenatedChunkLength: number;
|
|
31
26
|
numChunks: number;
|
|
32
27
|
};
|
|
33
|
-
|
|
28
|
+
readGrids: () => Grids;
|
|
29
|
+
readLevels(): Level[];
|
|
34
30
|
readLevelsAsynchronously(writer: SaveStreamWriter): Promise<Level[]>;
|
|
35
31
|
}
|
|
@@ -3,33 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SaveReader = exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE =
|
|
6
|
+
exports.SaveReader = exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = void 0;
|
|
7
7
|
const pako_1 = __importDefault(require("pako"));
|
|
8
8
|
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
|
-
const SaveComponent_1 = require("../objects/SaveComponent");
|
|
12
|
-
const SaveEntity_1 = require("../objects/SaveEntity");
|
|
13
11
|
const asynchronous_level_class_1 = require("./asynchronous-level.class");
|
|
14
12
|
const level_class_1 = require("./level.class");
|
|
15
|
-
const projectionFilterApplies = (config, object) => {
|
|
16
|
-
let cond1 = false;
|
|
17
|
-
if ((0, SaveEntity_1.isSaveEntity)(object) || (config.includeComponents && (0, SaveComponent_1.isSaveComponent)(object))) {
|
|
18
|
-
cond1 = true;
|
|
19
|
-
}
|
|
20
|
-
let cond2 = true;
|
|
21
|
-
if ((0, SaveEntity_1.isSaveEntity)(object)) {
|
|
22
|
-
cond2 = false;
|
|
23
|
-
if (config.entityPathFilter.behavior === 'whitelist') {
|
|
24
|
-
cond2 = config.entityPathFilter.list.find(en => object.typePath.startsWith(en)) !== undefined;
|
|
25
|
-
}
|
|
26
|
-
else if (config.entityPathFilter.behavior === 'blacklist') {
|
|
27
|
-
cond2 = config.entityPathFilter.list.find(en => object.typePath.startsWith(en)) === undefined;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return cond1 && cond2;
|
|
31
|
-
};
|
|
32
|
-
exports.projectionFilterApplies = projectionFilterApplies;
|
|
33
13
|
exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
34
14
|
class SaveReader extends byte_reader_class_1.ByteReader {
|
|
35
15
|
constructor(fileBuffer, onProgressCallback = () => { }) {
|
|
@@ -42,6 +22,40 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
42
22
|
maxChunkContentSize: 0,
|
|
43
23
|
chunkHeaderSize: exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
|
|
44
24
|
};
|
|
25
|
+
this.readGrids = () => {
|
|
26
|
+
const unk = this.readInt32();
|
|
27
|
+
const cannotBeLevelCountWhatIsIt = this.readInt32();
|
|
28
|
+
const noneString = this.readString();
|
|
29
|
+
const unk2 = this.readInt32();
|
|
30
|
+
const hm1 = this.readInt32();
|
|
31
|
+
const unk3 = this.readInt32();
|
|
32
|
+
const noneString2 = this.readString();
|
|
33
|
+
const grids = {};
|
|
34
|
+
const readLevelsList = (childrenCount) => {
|
|
35
|
+
const hmm = this.readInt32();
|
|
36
|
+
const parentName = this.readString();
|
|
37
|
+
const whatever1 = this.readInt32();
|
|
38
|
+
const whatever2 = this.readInt32();
|
|
39
|
+
grids[parentName] = {};
|
|
40
|
+
for (let i = 0; i < childrenCount; i++) {
|
|
41
|
+
const binaryLenIGuess = this.readInt32();
|
|
42
|
+
const levelInstanceName = this.readString();
|
|
43
|
+
const lod = /\_L([0-9]+)\_/.exec(levelInstanceName)[1];
|
|
44
|
+
const arr = grids[parentName][Number(lod)];
|
|
45
|
+
if (!arr) {
|
|
46
|
+
grids[parentName][Number(lod)] = [];
|
|
47
|
+
}
|
|
48
|
+
grids[parentName][Number(lod)].push(levelInstanceName);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
readLevelsList(1379);
|
|
52
|
+
readLevelsList(0);
|
|
53
|
+
readLevelsList(972);
|
|
54
|
+
readLevelsList(0);
|
|
55
|
+
readLevelsList(0);
|
|
56
|
+
const unk5 = this.readInt32();
|
|
57
|
+
return grids;
|
|
58
|
+
};
|
|
45
59
|
}
|
|
46
60
|
readHeader() {
|
|
47
61
|
this.header = {
|
|
@@ -82,10 +96,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
82
96
|
this.header.saveIdentifier = this.readString();
|
|
83
97
|
}
|
|
84
98
|
if (this.header.saveHeaderType >= 13) {
|
|
85
|
-
|
|
86
|
-
const unk2 = this.readInt32();
|
|
87
|
-
const unk3 = this.readBytes(16);
|
|
88
|
-
const unk4 = this.readInt32();
|
|
99
|
+
this.header.unknownStuff = Array.from(this.readBytes(28));
|
|
89
100
|
}
|
|
90
101
|
if (this.header.saveVersion >= 21) {
|
|
91
102
|
}
|
|
@@ -148,7 +159,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
148
159
|
numChunks: currentChunks.length
|
|
149
160
|
};
|
|
150
161
|
}
|
|
151
|
-
|
|
162
|
+
readLevels() {
|
|
152
163
|
if (!this.header) {
|
|
153
164
|
throw new parser_error_1.ParserError('ParserError', 'Header must be set before objects can be read.');
|
|
154
165
|
}
|
|
@@ -158,36 +169,6 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
158
169
|
if (this.header.saveHeaderType < 13) {
|
|
159
170
|
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
171
|
}
|
|
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
172
|
const levels = [];
|
|
192
173
|
const levelCount = this.readInt32();
|
|
193
174
|
this.onProgressCallback(this.getBufferProgress(), `reading pack of ${levelCount} levels.`);
|
|
@@ -201,6 +182,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
201
182
|
const appendedLevel = new level_class_1.Level(this.header.mapName);
|
|
202
183
|
levels.push(appendedLevel);
|
|
203
184
|
level_class_1.Level.ReadObjectHeaders(this, appendedLevel.objects, this.onProgressCallback);
|
|
185
|
+
const unk = this.readInt32();
|
|
204
186
|
appendedLevel.collectables = level_class_1.Level.ReadCollectablesList(this);
|
|
205
187
|
level_class_1.Level.ReadObjectContents(appendedLevel.name, this, appendedLevel.objects, this.header.buildVersion, this.onProgressCallback);
|
|
206
188
|
level_class_1.Level.ReadCollectablesList(this);
|
|
@@ -223,4 +205,15 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
223
205
|
}
|
|
224
206
|
}
|
|
225
207
|
SaveReader.EPOCH_TICKS = 621355968000000000n;
|
|
208
|
+
SaveReader.getRoughSaveVersion = (saveVersion, headerTypeVersion) => {
|
|
209
|
+
if (headerTypeVersion >= 13) {
|
|
210
|
+
return 'U8+';
|
|
211
|
+
}
|
|
212
|
+
else if (saveVersion >= 29) {
|
|
213
|
+
return 'U6/U7';
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
return '<U6';
|
|
217
|
+
}
|
|
218
|
+
};
|
|
226
219
|
exports.SaveReader = SaveReader;
|
|
@@ -38,6 +38,9 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
38
38
|
if (header.saveHeaderType >= 10) {
|
|
39
39
|
writer.writeString(header.saveIdentifier);
|
|
40
40
|
}
|
|
41
|
+
if (header.saveHeaderType >= 13) {
|
|
42
|
+
writer.writeBytesArray(header.unknownStuff);
|
|
43
|
+
}
|
|
41
44
|
if (header.saveVersion >= 21) {
|
|
42
45
|
}
|
|
43
46
|
else {
|
|
@@ -21,7 +21,9 @@ export interface SatisfactorySaveHeader {
|
|
|
21
21
|
isModdedSave?: number;
|
|
22
22
|
saveIdentifier?: string;
|
|
23
23
|
fEditorObjectVersion?: number;
|
|
24
|
+
unknownStuff?: number[];
|
|
24
25
|
}
|
|
26
|
+
export type RoughSaveVersion = '<U6' | 'U6/U7' | 'U8+';
|
|
25
27
|
export type ResourceNodeEntry = {
|
|
26
28
|
purity: string;
|
|
27
29
|
pathName: string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from "stream";
|
|
3
|
+
import { Alignment } from "../byte/alignment.enum";
|
|
4
|
+
export declare class StreamParserReader {
|
|
5
|
+
minBufferSize: number;
|
|
6
|
+
private buffer;
|
|
7
|
+
view: DataView;
|
|
8
|
+
private hasInputStreamEnded;
|
|
9
|
+
currentByte: number;
|
|
10
|
+
alignment: Alignment;
|
|
11
|
+
private inputBuffer;
|
|
12
|
+
private input;
|
|
13
|
+
hasDataToRead: boolean;
|
|
14
|
+
private onInputDataAvailableCallback;
|
|
15
|
+
constructor(minBufferSize: number);
|
|
16
|
+
getAmountLeftToRead: () => number;
|
|
17
|
+
parseLogic: () => Promise<void>;
|
|
18
|
+
private allocate;
|
|
19
|
+
private waitForAmountLeftToRead;
|
|
20
|
+
private wait;
|
|
21
|
+
private shiftBufferIntoOperatingDataView;
|
|
22
|
+
private readTilBufferFullOrInputEmpty;
|
|
23
|
+
startReading(input: Readable): void;
|
|
24
|
+
skipBytes(byteLength?: number): void;
|
|
25
|
+
readByte(): number;
|
|
26
|
+
readBytes(count: number): Uint8Array;
|
|
27
|
+
readHex(hexLength: number): string;
|
|
28
|
+
readInt8(): number;
|
|
29
|
+
readUint8(): number;
|
|
30
|
+
readInt16(): number;
|
|
31
|
+
readUint16(): number;
|
|
32
|
+
readInt32(): number;
|
|
33
|
+
readUint32(): number;
|
|
34
|
+
readLong(): bigint;
|
|
35
|
+
readInt64(): bigint;
|
|
36
|
+
readUint64(): bigint;
|
|
37
|
+
readFloat32(): number;
|
|
38
|
+
readDouble(): number;
|
|
39
|
+
readString(): string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamParserReader = void 0;
|
|
4
|
+
const alignment_enum_1 = require("../byte/alignment.enum");
|
|
5
|
+
class StreamParserReader {
|
|
6
|
+
constructor(minBufferSize) {
|
|
7
|
+
this.minBufferSize = minBufferSize;
|
|
8
|
+
this.alignment = alignment_enum_1.Alignment.LITTLE_ENDIAN;
|
|
9
|
+
this.getAmountLeftToRead = () => {
|
|
10
|
+
return this.view.byteLength - this.currentByte;
|
|
11
|
+
};
|
|
12
|
+
this.parseLogic = async () => {
|
|
13
|
+
console.log('before allocate 1', this.getAmountLeftToRead(), this.inputBuffer.length);
|
|
14
|
+
await this.allocate(100000);
|
|
15
|
+
const unknownStuff = [
|
|
16
|
+
this.readInt32(),
|
|
17
|
+
this.readInt32(),
|
|
18
|
+
this.readString(),
|
|
19
|
+
this.readInt32(),
|
|
20
|
+
this.readInt32(),
|
|
21
|
+
this.readInt32(),
|
|
22
|
+
this.readString()
|
|
23
|
+
];
|
|
24
|
+
console.log('before allocate 2', unknownStuff, this.getAmountLeftToRead(), this.inputBuffer.length);
|
|
25
|
+
await this.allocate(10000);
|
|
26
|
+
};
|
|
27
|
+
this.allocate = async (amount) => {
|
|
28
|
+
if (amount > this.minBufferSize) {
|
|
29
|
+
throw new Error(`Can not attempt to allocate (${amount}) more than the size of the buffer (${this.minBufferSize}) is.`);
|
|
30
|
+
}
|
|
31
|
+
if (this.getAmountLeftToRead() >= amount) {
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
console.log('Waiting for allocate', amount, this.getAmountLeftToRead());
|
|
36
|
+
this.readTilBufferFullOrInputEmpty();
|
|
37
|
+
console.log('shift?', this.inputBuffer.length);
|
|
38
|
+
if (this.inputBuffer.length > 0 && this.inputBuffer.length + this.getAmountLeftToRead() >= amount) {
|
|
39
|
+
this.shiftBufferIntoOperatingDataView();
|
|
40
|
+
return resolve();
|
|
41
|
+
}
|
|
42
|
+
this.onInputDataAvailableCallback = () => {
|
|
43
|
+
console.log('on available callback.', this.inputBuffer.length, this.getAmountLeftToRead(), amount);
|
|
44
|
+
if (this.inputBuffer.length > 0 && this.inputBuffer.length + this.getAmountLeftToRead() >= amount) {
|
|
45
|
+
this.shiftBufferIntoOperatingDataView();
|
|
46
|
+
console.log(this.getAmountLeftToRead());
|
|
47
|
+
this.onInputDataAvailableCallback = undefined;
|
|
48
|
+
return resolve();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
this.waitForAmountLeftToRead = async (amount, callback = () => { }) => {
|
|
54
|
+
while (this.getAmountLeftToRead() < amount) {
|
|
55
|
+
if (this.hasInputStreamEnded) {
|
|
56
|
+
throw new Error(`Unexpected end of stream. Waited to read ${amount} but stream ended prematurely.`);
|
|
57
|
+
}
|
|
58
|
+
console.log('###waiting... for ', amount);
|
|
59
|
+
this.readTilBufferFullOrInputEmpty();
|
|
60
|
+
if (this.inputBuffer.length > 0) {
|
|
61
|
+
console.log('###shift.');
|
|
62
|
+
this.shiftBufferIntoOperatingDataView();
|
|
63
|
+
}
|
|
64
|
+
await this.wait(20);
|
|
65
|
+
}
|
|
66
|
+
console.log('amount left to read is no longer smaller than', amount, this.getAmountLeftToRead());
|
|
67
|
+
callback();
|
|
68
|
+
};
|
|
69
|
+
this.wait = (ms) => new Promise((resolve, reject) => {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
return resolve(ms);
|
|
72
|
+
}, ms);
|
|
73
|
+
});
|
|
74
|
+
this.shiftBufferIntoOperatingDataView = (amountOfTrailingBytesToKeep = 300) => {
|
|
75
|
+
const resultingTrailingBytes = Math.min(this.currentByte, amountOfTrailingBytesToKeep);
|
|
76
|
+
const trailingPlusLeftToRead = new Uint8Array(this.buffer.slice(this.currentByte - resultingTrailingBytes));
|
|
77
|
+
const { length, ...chunks } = this.inputBuffer;
|
|
78
|
+
const inBuffer = new Uint8Array(Buffer.concat(Object.values(chunks)));
|
|
79
|
+
this.inputBuffer = { length: 0 };
|
|
80
|
+
this.buffer = new Uint8Array(trailingPlusLeftToRead.byteLength + inBuffer.byteLength);
|
|
81
|
+
this.buffer.set(trailingPlusLeftToRead, 0);
|
|
82
|
+
this.buffer.set(inBuffer, trailingPlusLeftToRead.byteLength);
|
|
83
|
+
this.currentByte = resultingTrailingBytes;
|
|
84
|
+
this.view = new DataView(this.buffer.buffer);
|
|
85
|
+
};
|
|
86
|
+
this.readTilBufferFullOrInputEmpty = () => {
|
|
87
|
+
let chunk;
|
|
88
|
+
console.log(this.hasDataToRead, this.inputBuffer.length, this.minBufferSize);
|
|
89
|
+
while (this.hasDataToRead && this.inputBuffer.length < this.minBufferSize && (chunk = this.input.read()) != null) {
|
|
90
|
+
console.log('reading chunk', chunk);
|
|
91
|
+
this.inputBuffer[chunk.byteLength] = chunk;
|
|
92
|
+
this.inputBuffer.length += chunk.length;
|
|
93
|
+
}
|
|
94
|
+
if (chunk === null) {
|
|
95
|
+
console.log('chunk is null, input empty for now.');
|
|
96
|
+
this.hasDataToRead = false;
|
|
97
|
+
}
|
|
98
|
+
else if (this.inputBuffer.length >= this.minBufferSize) {
|
|
99
|
+
console.log('buffer is full, ignoring for now.', this.inputBuffer.length);
|
|
100
|
+
}
|
|
101
|
+
console.log('data available callback?', this.onInputDataAvailableCallback);
|
|
102
|
+
if (this.onInputDataAvailableCallback) {
|
|
103
|
+
this.onInputDataAvailableCallback();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
this.buffer = new Uint8Array(0);
|
|
107
|
+
this.view = new DataView(this.buffer.buffer);
|
|
108
|
+
this.currentByte = 0;
|
|
109
|
+
this.hasInputStreamEnded = false;
|
|
110
|
+
this.inputBuffer = { length: 0 };
|
|
111
|
+
this.hasDataToRead = false;
|
|
112
|
+
}
|
|
113
|
+
startReading(input) {
|
|
114
|
+
this.input = input;
|
|
115
|
+
this.input.pause();
|
|
116
|
+
this.input.on('readable', () => {
|
|
117
|
+
console.log('on readable. Reading til buffer full or input empty.');
|
|
118
|
+
this.hasDataToRead = true;
|
|
119
|
+
this.readTilBufferFullOrInputEmpty();
|
|
120
|
+
});
|
|
121
|
+
this.input.on('end', () => {
|
|
122
|
+
this.hasDataToRead = false;
|
|
123
|
+
this.hasInputStreamEnded = true;
|
|
124
|
+
});
|
|
125
|
+
this.input.on('close', () => {
|
|
126
|
+
this.hasDataToRead = false;
|
|
127
|
+
this.hasInputStreamEnded = true;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
skipBytes(byteLength = 1) {
|
|
131
|
+
this.currentByte += byteLength;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
readByte() {
|
|
135
|
+
return parseInt(this.view.getUint8(this.currentByte++).toString());
|
|
136
|
+
}
|
|
137
|
+
readBytes(count) {
|
|
138
|
+
return new Uint8Array(new Array(count).fill(0).map(pl => this.view.getUint8(this.currentByte++)));
|
|
139
|
+
}
|
|
140
|
+
readHex(hexLength) {
|
|
141
|
+
let hexPart = [];
|
|
142
|
+
for (let i = 0; i < hexLength; i++) {
|
|
143
|
+
let currentHex = String.fromCharCode(this.view.getUint8(this.currentByte++));
|
|
144
|
+
hexPart.push(currentHex);
|
|
145
|
+
}
|
|
146
|
+
return hexPart.join('');
|
|
147
|
+
}
|
|
148
|
+
readInt8() {
|
|
149
|
+
let data = this.view.getInt8(this.currentByte++);
|
|
150
|
+
return data;
|
|
151
|
+
}
|
|
152
|
+
readUint8() {
|
|
153
|
+
let data = this.view.getUint8(this.currentByte++);
|
|
154
|
+
return data;
|
|
155
|
+
}
|
|
156
|
+
readInt16() {
|
|
157
|
+
let data = this.view.getInt16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
158
|
+
this.currentByte += 2;
|
|
159
|
+
return data;
|
|
160
|
+
}
|
|
161
|
+
readUint16() {
|
|
162
|
+
let data = this.view.getUint16(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
163
|
+
this.currentByte += 2;
|
|
164
|
+
return data;
|
|
165
|
+
}
|
|
166
|
+
readInt32() {
|
|
167
|
+
let data = this.view.getInt32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
168
|
+
this.currentByte += 4;
|
|
169
|
+
return data;
|
|
170
|
+
}
|
|
171
|
+
readUint32() {
|
|
172
|
+
let data = this.view.getUint32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
173
|
+
this.currentByte += 4;
|
|
174
|
+
return data;
|
|
175
|
+
}
|
|
176
|
+
readLong() {
|
|
177
|
+
let data = this.view.getBigInt64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
178
|
+
this.currentByte += 8;
|
|
179
|
+
return data;
|
|
180
|
+
}
|
|
181
|
+
readInt64() {
|
|
182
|
+
return this.readLong();
|
|
183
|
+
}
|
|
184
|
+
readUint64() {
|
|
185
|
+
let data = this.view.getBigUint64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
186
|
+
this.currentByte += 8;
|
|
187
|
+
return data;
|
|
188
|
+
}
|
|
189
|
+
readFloat32() {
|
|
190
|
+
let data = this.view.getFloat32(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
191
|
+
this.currentByte += 4;
|
|
192
|
+
return data;
|
|
193
|
+
}
|
|
194
|
+
readDouble() {
|
|
195
|
+
let data = this.view.getFloat64(this.currentByte, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
196
|
+
this.currentByte += 8;
|
|
197
|
+
return data;
|
|
198
|
+
}
|
|
199
|
+
readString() {
|
|
200
|
+
let strLength = this.readInt32();
|
|
201
|
+
if (strLength === 0) {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
if (strLength > (this.view.buffer.byteLength - this.currentByte)) {
|
|
205
|
+
let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.view.buffer.byteLength}`;
|
|
206
|
+
throw new Error(errorMessage);
|
|
207
|
+
}
|
|
208
|
+
if (strLength < 0) {
|
|
209
|
+
const string = new Array(-strLength - 1).fill('').map(c => String.fromCharCode(this.readUint16()));
|
|
210
|
+
this.currentByte += 2;
|
|
211
|
+
return string.join('');
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8()));
|
|
215
|
+
this.currentByte += 1;
|
|
216
|
+
return string.join('');
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
let errorMessage = `Cannot read UTF8 string of length ${strLength} at position ${this.currentByte}.`;
|
|
220
|
+
console.log(errorMessage, error);
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
exports.StreamParserReader = StreamParserReader;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etothepii/satisfactory-file-parser",
|
|
3
3
|
"author": "etothepii",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
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",
|