@etothepii/satisfactory-file-parser 0.1.22 → 0.1.25
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/build/index.d.ts +1 -0
- package/build/index.js +3 -1
- package/build/parser/parser.d.ts +1 -1
- package/build/parser/parser.js +2 -2
- package/build/parser/satisfactory/save/level.class.js +2 -2
- package/build/parser/satisfactory/save/satisfactory-save.d.ts +2 -1
- package/build/parser/satisfactory/save/satisfactory-save.js +2 -1
- package/build/parser/satisfactory/save/save-reader.js +3 -0
- package/build/parser/satisfactory/save/save-writer.js +1 -1
- package/build/parser/stream/reworked/readable-stream-parser.d.ts +12 -0
- package/build/parser/stream/reworked/readable-stream-parser.js +204 -0
- package/build/parser/stream/reworked/stream-parser.d.ts +3 -1
- package/build/parser/stream/reworked/stream-parser.js +83 -5
- package/build/parser/stream/save-stream-writer.class.d.ts +15 -13
- package/build/parser/stream/save-stream-writer.class.js +70 -103
- package/package.json +1 -1
- package/build/parser/satisfactory/objects/ue/FMD5Hash.d.ts +0 -7
- package/build/parser/satisfactory/objects/ue/FMD5Hash.js +0 -19
package/build/index.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export { SaveStreamWriter } from './parser/stream/save-stream-writer.class';
|
|
|
24
24
|
export * from './parser/error/parser.error';
|
|
25
25
|
export * from './parser/file.types';
|
|
26
26
|
export { Parser } from './parser/parser';
|
|
27
|
+
export { ReadableStreamParser } from './parser/stream/reworked/readable-stream-parser';
|
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.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.ObjectReference = 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.SaveEntity = exports.SaveComponent = exports.ObjectReference = 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; } });
|
|
@@ -56,3 +56,5 @@ __exportStar(require("./parser/error/parser.error"), exports);
|
|
|
56
56
|
__exportStar(require("./parser/file.types"), exports);
|
|
57
57
|
var parser_1 = require("./parser/parser");
|
|
58
58
|
Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return parser_1.Parser; } });
|
|
59
|
+
var readable_stream_parser_1 = require("./parser/stream/reworked/readable-stream-parser");
|
|
60
|
+
Object.defineProperty(exports, "ReadableStreamParser", { enumerable: true, get: function () { return readable_stream_parser_1.ReadableStreamParser; } });
|
package/build/parser/parser.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Blueprint } from "./satisfactory/blueprint/blueprint.types";
|
|
|
4
4
|
import { SatisfactorySave } from "./satisfactory/save/satisfactory-save";
|
|
5
5
|
export declare class Parser {
|
|
6
6
|
static WriteSave(save: SatisfactorySave, onBinaryBeforeCompressing: (buffer: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void): ChunkSummary[];
|
|
7
|
-
static ParseSaveFile(file: Buffer, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
|
|
7
|
+
static ParseSaveFile(name: string, file: Buffer, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
|
|
8
8
|
static WriteBlueprintFiles(blueprint: Blueprint, onMainFileBinaryBeforeCompressing?: (binary: ArrayBuffer) => void, onMainFileHeader?: (header: Uint8Array) => void, onMainFileChunk?: (chunk: Uint8Array) => void): {
|
|
9
9
|
mainFileChunkSummary: ChunkSummary[];
|
|
10
10
|
configFileBinary: ArrayBuffer;
|
package/build/parser/parser.js
CHANGED
|
@@ -18,10 +18,10 @@ class Parser {
|
|
|
18
18
|
const chunkSummary = writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk);
|
|
19
19
|
return chunkSummary;
|
|
20
20
|
}
|
|
21
|
-
static ParseSaveFile(file, onDecompressedSaveBody = () => { }, onProgress = () => { }) {
|
|
21
|
+
static ParseSaveFile(name, file, onDecompressedSaveBody = () => { }, onProgress = () => { }) {
|
|
22
22
|
const reader = new save_reader_1.SaveReader(new Uint8Array(file).buffer, onProgress);
|
|
23
23
|
const header = reader.readHeader();
|
|
24
|
-
const save = new satisfactory_save_1.SatisfactorySave(header);
|
|
24
|
+
const save = new satisfactory_save_1.SatisfactorySave(name, header);
|
|
25
25
|
const inflateResult = reader.inflateChunks();
|
|
26
26
|
onDecompressedSaveBody(reader.getBuffer());
|
|
27
27
|
const gridHash = reader.readSaveBodyHash();
|
|
@@ -89,8 +89,8 @@ class Level {
|
|
|
89
89
|
throw new Error(`possibly corrupt. entity content count ${countEntities} does not equal object count of ${objectsList.length}`);
|
|
90
90
|
}
|
|
91
91
|
for (let i = 0; i < countEntities; i++) {
|
|
92
|
-
if (i %
|
|
93
|
-
onProgressCallback(reader.getBufferProgress());
|
|
92
|
+
if (countEntities >= 10000 && i % 10000 === 0) {
|
|
93
|
+
onProgressCallback(reader.getBufferProgress(), `read object count [${(i + 1)}/${(countEntities + 1)}] in level ${levelName}`);
|
|
94
94
|
}
|
|
95
95
|
const obj = objectsList[i];
|
|
96
96
|
obj.saveOrBlueprintIndicator = reader.readInt32();
|
|
@@ -4,11 +4,12 @@ import { Level } from "./level.class";
|
|
|
4
4
|
import { ByteArray4, Grids } from "./save-reader";
|
|
5
5
|
import { SatisfactorySaveHeader } from "./save.types";
|
|
6
6
|
export declare class SatisfactorySave {
|
|
7
|
+
name: string;
|
|
7
8
|
header: SatisfactorySaveHeader;
|
|
8
9
|
gridHash: ByteArray4;
|
|
9
10
|
grids: Grids;
|
|
10
11
|
levels: Level[];
|
|
11
12
|
trailingCollectedObjects: ObjectReference[];
|
|
12
13
|
compressionInfo?: ChunkCompressionInfo;
|
|
13
|
-
constructor(header: SatisfactorySaveHeader);
|
|
14
|
+
constructor(name: string, header: SatisfactorySaveHeader);
|
|
14
15
|
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SatisfactorySave = void 0;
|
|
4
4
|
class SatisfactorySave {
|
|
5
|
-
constructor(header) {
|
|
5
|
+
constructor(name, header) {
|
|
6
6
|
this.gridHash = [0, 0, 0, 0];
|
|
7
7
|
this.grids = {};
|
|
8
8
|
this.levels = [];
|
|
9
9
|
this.trailingCollectedObjects = [];
|
|
10
|
+
this.name = name;
|
|
10
11
|
this.header = header;
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -179,6 +179,9 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
179
179
|
this.onProgressCallback(this.getBufferProgress(), `reading pack of ${levelCount} levels.`);
|
|
180
180
|
for (let i = 0; i < levelCount; i++) {
|
|
181
181
|
let levelSingleName = this.readString();
|
|
182
|
+
if (i % 500 === 0) {
|
|
183
|
+
this.onProgressCallback(this.getBufferProgress(), `reading level [${(i + 1)}/${(levelCount + 1)}] ${levelSingleName}`);
|
|
184
|
+
}
|
|
182
185
|
levels.push(level_class_1.Level.ReadLevel(this, levelSingleName, this.header.buildVersion));
|
|
183
186
|
}
|
|
184
187
|
levels.push(level_class_1.Level.ReadLevel(this, this.header.mapName, this.header.buildVersion));
|
|
@@ -69,7 +69,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
69
69
|
static GenerateCompressedChunksFromData(bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, alignment = alignment_enum_1.Alignment.LITTLE_ENDIAN) {
|
|
70
70
|
const errors = [];
|
|
71
71
|
const totalUncompressedSize = bufferArray.byteLength;
|
|
72
|
-
const saveBody = new Uint8Array(bufferArray.byteLength +
|
|
72
|
+
const saveBody = new Uint8Array(bufferArray.byteLength + 8);
|
|
73
73
|
saveBody.set(new Uint8Array(bufferArray), 4);
|
|
74
74
|
const miniView = new DataView(saveBody.buffer);
|
|
75
75
|
miniView.setInt32(0, totalUncompressedSize, alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReadableStream } from "stream/web";
|
|
2
|
+
export declare class ReadableStreamParser {
|
|
3
|
+
private static CreateReadableStreamForParsingSave;
|
|
4
|
+
static CreateReadableStreamFromSaveToJson: (name: string, bytes: Uint8Array, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void) => {
|
|
5
|
+
stream: ReadableStream<string>;
|
|
6
|
+
startStreaming: () => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
private static WriteHeaderAndGrids;
|
|
9
|
+
private static WriteLevels;
|
|
10
|
+
private static ReadNObjectHeaders;
|
|
11
|
+
private static ReadNObjects;
|
|
12
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ReadableStreamParser = void 0;
|
|
5
|
+
const web_1 = require("stream/web");
|
|
6
|
+
const satisfactory_save_1 = require("../../satisfactory/save/satisfactory-save");
|
|
7
|
+
const save_reader_1 = require("../../satisfactory/save/save-reader");
|
|
8
|
+
const SaveComponent_1 = require("../../satisfactory/objects/SaveComponent");
|
|
9
|
+
const SaveEntity_1 = require("../../satisfactory/objects/SaveEntity");
|
|
10
|
+
const level_class_1 = require("../../satisfactory/save/level.class");
|
|
11
|
+
const DEFAULT_BYTE_HIGHWATERMARK = 1024 * 1024 * 200;
|
|
12
|
+
const createStringLengthQueuingStrategy = (highWaterMark = DEFAULT_BYTE_HIGHWATERMARK / 4) => ({
|
|
13
|
+
highWaterMark,
|
|
14
|
+
size: (chunk) => {
|
|
15
|
+
if (chunk === undefined) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
return chunk.length;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
class SimpleWaitForConsumerLock {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.locked = false;
|
|
24
|
+
this.unlockWasCalledInTheMeantime = false;
|
|
25
|
+
this.executionFn = undefined;
|
|
26
|
+
}
|
|
27
|
+
lock(executionFn) {
|
|
28
|
+
this.executionFn = executionFn;
|
|
29
|
+
this.locked = true;
|
|
30
|
+
if (this.unlockWasCalledInTheMeantime) {
|
|
31
|
+
this.unlock();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
unlock() {
|
|
35
|
+
this.locked = false;
|
|
36
|
+
if (this.executionFn) {
|
|
37
|
+
this.executionFn();
|
|
38
|
+
this.executionFn = undefined;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.unlockWasCalledInTheMeantime = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
;
|
|
46
|
+
class ReadableStreamParser {
|
|
47
|
+
static async WriteLevels(write, reader, mapName, buildVersion) {
|
|
48
|
+
const batchingSizeOfObjects = 1000;
|
|
49
|
+
const thresholdOfWrittenObjectsUntilWaitingForConsumerAgain = 3 * batchingSizeOfObjects;
|
|
50
|
+
const levelCount = reader.readInt32();
|
|
51
|
+
reader.onProgressCallback(reader.getBufferProgress(), `reading pack of ${levelCount + 1} levels.`);
|
|
52
|
+
let writtenTotalObjectsSinceConsumerSync = 0;
|
|
53
|
+
for (let j = 0; j <= levelCount; j++) {
|
|
54
|
+
let levelName = (j === levelCount) ? '' + mapName : reader.readString();
|
|
55
|
+
if (j % 500 === 0) {
|
|
56
|
+
reader.onProgressCallback(reader.getBufferProgress(), `reading level [${(j + 1)}/${(levelCount + 1)}] ${levelName}`);
|
|
57
|
+
}
|
|
58
|
+
await write(`${j > 0 ? ', ' : ''}{"name": "${levelName}", "objects": [`, false);
|
|
59
|
+
const headersBinLen = reader.readInt32();
|
|
60
|
+
const unk = reader.readInt32();
|
|
61
|
+
const posBeforeHeaders = reader.getBufferPosition();
|
|
62
|
+
const afterAllHeaders = posBeforeHeaders + headersBinLen;
|
|
63
|
+
let countObjectHeaders = reader.readInt32();
|
|
64
|
+
let totalReadObjectsInLevel = 0;
|
|
65
|
+
let writtenObjectsInLevel = 0;
|
|
66
|
+
let afterHeadersOfBatch = reader.getBufferPosition();
|
|
67
|
+
let afterObjectsOfBatch = -1;
|
|
68
|
+
do {
|
|
69
|
+
reader.skipBytes(afterHeadersOfBatch - reader.getBufferPosition());
|
|
70
|
+
const objectCountToRead = Math.min(countObjectHeaders - totalReadObjectsInLevel, batchingSizeOfObjects);
|
|
71
|
+
const objects = ReadableStreamParser.ReadNObjectHeaders(reader, objectCountToRead);
|
|
72
|
+
afterHeadersOfBatch = reader.getBufferPosition();
|
|
73
|
+
if (totalReadObjectsInLevel === 0) {
|
|
74
|
+
reader.skipBytes(afterAllHeaders - reader.getBufferPosition());
|
|
75
|
+
const objectContentsBinLen = reader.readInt32();
|
|
76
|
+
const unk2 = reader.readInt32();
|
|
77
|
+
const posBeforeContents = reader.getBufferPosition();
|
|
78
|
+
const countEntities = reader.readInt32();
|
|
79
|
+
afterObjectsOfBatch = reader.getBufferPosition();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
reader.skipBytes(afterObjectsOfBatch - reader.getBufferPosition());
|
|
83
|
+
}
|
|
84
|
+
ReadableStreamParser.ReadNObjects(reader, objectCountToRead, objects, buildVersion);
|
|
85
|
+
afterObjectsOfBatch = reader.getBufferPosition();
|
|
86
|
+
totalReadObjectsInLevel += objectCountToRead;
|
|
87
|
+
if (countObjectHeaders > 10000 && totalReadObjectsInLevel % 10000 === 0) {
|
|
88
|
+
reader.onProgressCallback(reader.getBufferProgress(), `read object count [${(totalReadObjectsInLevel + 1)}/${(countObjectHeaders + 1)}] in level ${levelName}`);
|
|
89
|
+
}
|
|
90
|
+
let shouldWait = false;
|
|
91
|
+
if (writtenTotalObjectsSinceConsumerSync >= thresholdOfWrittenObjectsUntilWaitingForConsumerAgain) {
|
|
92
|
+
shouldWait = true;
|
|
93
|
+
writtenTotalObjectsSinceConsumerSync = 0;
|
|
94
|
+
}
|
|
95
|
+
await write(`${writtenObjectsInLevel > 0 ? ', ' : ''}${objects.map(obj => JSON.stringify(obj)).join(', ')}`, shouldWait);
|
|
96
|
+
writtenTotalObjectsSinceConsumerSync += objectCountToRead;
|
|
97
|
+
writtenObjectsInLevel += objectCountToRead;
|
|
98
|
+
} while (totalReadObjectsInLevel < countObjectHeaders);
|
|
99
|
+
await write('], "collectables": [', false);
|
|
100
|
+
const collectables = level_class_1.Level.ReadCollectablesList(reader);
|
|
101
|
+
await write(`${collectables.map(obj => JSON.stringify(obj)).join(', ')}`, true);
|
|
102
|
+
await write(']', false);
|
|
103
|
+
await write('}', false);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
_a = ReadableStreamParser;
|
|
108
|
+
ReadableStreamParser.CreateReadableStreamForParsingSave = (onStart, onCancel, onPullRequest, highWaterMark = DEFAULT_BYTE_HIGHWATERMARK / 4) => {
|
|
109
|
+
let ourController = null;
|
|
110
|
+
const stream = new web_1.ReadableStream({
|
|
111
|
+
start: (controller) => {
|
|
112
|
+
ourController = controller;
|
|
113
|
+
onStart(ourController);
|
|
114
|
+
},
|
|
115
|
+
pull: (controller) => {
|
|
116
|
+
onPullRequest(ourController.desiredSize ?? 1);
|
|
117
|
+
},
|
|
118
|
+
cancel: (reason) => {
|
|
119
|
+
console.warn('parsing stream was canceled!', reason);
|
|
120
|
+
if (ourController !== null) {
|
|
121
|
+
ourController.close();
|
|
122
|
+
}
|
|
123
|
+
onCancel(reason);
|
|
124
|
+
}
|
|
125
|
+
}, createStringLengthQueuingStrategy(highWaterMark));
|
|
126
|
+
const finish = () => {
|
|
127
|
+
if (ourController !== null) {
|
|
128
|
+
ourController?.close();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return { stream, controller: ourController, finish };
|
|
132
|
+
};
|
|
133
|
+
ReadableStreamParser.CreateReadableStreamFromSaveToJson = (name, bytes, onDecompressedSaveBody = () => { }, onProgress = () => { }) => {
|
|
134
|
+
const waitForConsumerLock = new SimpleWaitForConsumerLock();
|
|
135
|
+
const waitForConsumer = async () => {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
waitForConsumerLock.lock(resolve);
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
const { stream, controller, finish } = ReadableStreamParser.CreateReadableStreamForParsingSave((controller) => {
|
|
141
|
+
}, (reason) => { }, (desiredSize) => {
|
|
142
|
+
waitForConsumerLock.unlock();
|
|
143
|
+
});
|
|
144
|
+
const write = async (value, waitForConsumerToBeReady = true) => {
|
|
145
|
+
if (waitForConsumerToBeReady) {
|
|
146
|
+
await waitForConsumer();
|
|
147
|
+
}
|
|
148
|
+
controller.enqueue(value);
|
|
149
|
+
};
|
|
150
|
+
const startStreaming = async () => {
|
|
151
|
+
const reader = new save_reader_1.SaveReader(bytes.buffer, onProgress);
|
|
152
|
+
const header = reader.readHeader();
|
|
153
|
+
const save = new satisfactory_save_1.SatisfactorySave(name, header);
|
|
154
|
+
const inflateResult = reader.inflateChunks();
|
|
155
|
+
onDecompressedSaveBody(reader.getBuffer());
|
|
156
|
+
const gridHash = reader.readSaveBodyHash();
|
|
157
|
+
const grids = reader.readGrids();
|
|
158
|
+
await ReadableStreamParser.WriteHeaderAndGrids(write, reader.compressionInfo, header, grids, gridHash);
|
|
159
|
+
await ReadableStreamParser.WriteLevels(write, reader, save.header.mapName, save.header.buildVersion);
|
|
160
|
+
await write(`]}`);
|
|
161
|
+
finish();
|
|
162
|
+
};
|
|
163
|
+
return { stream, startStreaming };
|
|
164
|
+
};
|
|
165
|
+
ReadableStreamParser.WriteHeaderAndGrids = async (write, compressionInfo, header, grids, gridHash) => {
|
|
166
|
+
return write(`{"header": ${JSON.stringify(header)}, "compressionInfo": ${JSON.stringify(compressionInfo)}, "gridHash": ${JSON.stringify(gridHash)}, "grids": ${JSON.stringify(grids)}, "levels": [`, false);
|
|
167
|
+
};
|
|
168
|
+
ReadableStreamParser.ReadNObjectHeaders = (reader, count) => {
|
|
169
|
+
let objects = [];
|
|
170
|
+
let objectsRead = 0;
|
|
171
|
+
for (; objectsRead < count; objectsRead++) {
|
|
172
|
+
let obj;
|
|
173
|
+
let objectType = reader.readInt32();
|
|
174
|
+
switch (objectType) {
|
|
175
|
+
case SaveEntity_1.SaveEntity.TypeID:
|
|
176
|
+
obj = new SaveEntity_1.SaveEntity('', '', '', '');
|
|
177
|
+
SaveEntity_1.SaveEntity.ParseHeader(reader, obj);
|
|
178
|
+
break;
|
|
179
|
+
case SaveComponent_1.SaveComponent.TypeID:
|
|
180
|
+
obj = new SaveComponent_1.SaveComponent('', '', '', '');
|
|
181
|
+
SaveComponent_1.SaveComponent.ParseHeader(reader, obj);
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
throw new Error('Unknown object type' + objectType);
|
|
185
|
+
}
|
|
186
|
+
objects.push(obj);
|
|
187
|
+
}
|
|
188
|
+
return objects;
|
|
189
|
+
};
|
|
190
|
+
ReadableStreamParser.ReadNObjects = (reader, count, objects, buildVersion) => {
|
|
191
|
+
for (let i = 0; i < count; i++) {
|
|
192
|
+
objects[i].saveOrBlueprintIndicator = reader.readInt32();
|
|
193
|
+
objects[i].unknownType2 = reader.readInt32();
|
|
194
|
+
const binarySize = reader.readInt32();
|
|
195
|
+
const before = reader.getBufferPosition();
|
|
196
|
+
if ((0, SaveEntity_1.isSaveEntity)(objects[i])) {
|
|
197
|
+
SaveEntity_1.SaveEntity.ParseData(objects[i], binarySize, reader, buildVersion, objects[i].typePath);
|
|
198
|
+
}
|
|
199
|
+
else if ((0, SaveComponent_1.isSaveComponent)(objects[i])) {
|
|
200
|
+
SaveComponent_1.SaveComponent.ParseData(objects[i], binarySize, reader, buildVersion, objects[i].typePath);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
exports.ReadableStreamParser = ReadableStreamParser;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { WritableStream } from 'stream/web';
|
|
3
3
|
export declare class StreamParser {
|
|
4
|
-
static
|
|
4
|
+
static ParseSaveFileToJsonStream(name: string, bytes: Uint8Array, outputJson: WritableStream<string>, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): Promise<void>;
|
|
5
5
|
private static readLevelsAsync;
|
|
6
|
+
private static ReadNObjectHeaders;
|
|
7
|
+
private static ReadNObjects;
|
|
6
8
|
}
|
|
@@ -2,30 +2,108 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StreamParser = void 0;
|
|
4
4
|
const __1 = require("../../..");
|
|
5
|
+
const SaveComponent_1 = require("../../satisfactory/objects/SaveComponent");
|
|
6
|
+
const SaveEntity_1 = require("../../satisfactory/objects/SaveEntity");
|
|
5
7
|
class StreamParser {
|
|
6
|
-
static async
|
|
8
|
+
static async ParseSaveFileToJsonStream(name, bytes, outputJson, onDecompressedSaveBody = () => { }, onProgress = () => { }) {
|
|
7
9
|
const reader = new __1.SaveReader(bytes.buffer, onProgress);
|
|
8
10
|
const writer = outputJson.getWriter();
|
|
9
11
|
const header = reader.readHeader();
|
|
10
|
-
const save = new __1.SatisfactorySave(header);
|
|
12
|
+
const save = new __1.SatisfactorySave(name, header);
|
|
11
13
|
const inflateResult = reader.inflateChunks();
|
|
12
14
|
onDecompressedSaveBody(reader.getBuffer());
|
|
13
15
|
const gridHash = reader.readSaveBodyHash();
|
|
14
16
|
const grids = reader.readGrids();
|
|
15
|
-
await writer.write(`{"header:
|
|
17
|
+
await writer.write(`{"header": ${JSON.stringify(header)}, "compressionInfo": ${JSON.stringify(reader.compressionInfo)}, "gridHash": ${JSON.stringify(gridHash)}, "grids": ${JSON.stringify(grids)}, "levels": [`);
|
|
16
18
|
await StreamParser.readLevelsAsync(reader, writer, save.header.mapName, save.header.buildVersion);
|
|
17
19
|
await writer.write(`]}`);
|
|
20
|
+
await writer.ready;
|
|
18
21
|
await writer.close();
|
|
19
22
|
}
|
|
20
23
|
static async readLevelsAsync(reader, writer, mapName, buildVersion) {
|
|
21
24
|
const levelCount = reader.readInt32();
|
|
22
|
-
reader.onProgressCallback(reader.getBufferProgress(), `reading pack of ${levelCount} levels.`);
|
|
25
|
+
reader.onProgressCallback(reader.getBufferProgress(), `reading pack of ${levelCount + 1} levels.`);
|
|
26
|
+
const batchingSizeOfObjects = 1000;
|
|
23
27
|
for (let j = 0; j <= levelCount; j++) {
|
|
24
28
|
let levelName = (j === levelCount) ? '' + mapName : reader.readString();
|
|
25
|
-
if (j %
|
|
29
|
+
if (j % 500 === 0) {
|
|
26
30
|
reader.onProgressCallback(reader.getBufferProgress(), `reading level [${(j + 1)}/${(levelCount + 1)}] ${levelName}`);
|
|
27
31
|
}
|
|
32
|
+
await writer.write(`${j > 0 ? ', ' : ''}{"name": "${levelName}", "objects": [`);
|
|
33
|
+
const headersBinLen = reader.readInt32();
|
|
34
|
+
const unk = reader.readInt32();
|
|
35
|
+
const posBeforeHeaders = reader.getBufferPosition();
|
|
36
|
+
const afterAllHeaders = posBeforeHeaders + headersBinLen;
|
|
37
|
+
let countObjectHeaders = reader.readInt32();
|
|
38
|
+
let totalReadObjects = 0;
|
|
39
|
+
let afterHeadersOfBatch = reader.getBufferPosition();
|
|
40
|
+
let afterObjectsOfBatch = -1;
|
|
41
|
+
do {
|
|
42
|
+
reader.skipBytes(afterHeadersOfBatch - reader.getBufferPosition());
|
|
43
|
+
const objectCountToRead = Math.min(countObjectHeaders - totalReadObjects, batchingSizeOfObjects);
|
|
44
|
+
const objects = StreamParser.ReadNObjectHeaders(reader, objectCountToRead);
|
|
45
|
+
afterHeadersOfBatch = reader.getBufferPosition();
|
|
46
|
+
if (totalReadObjects === 0) {
|
|
47
|
+
reader.skipBytes(afterAllHeaders - reader.getBufferPosition());
|
|
48
|
+
const objectContentsBinLen = reader.readInt32();
|
|
49
|
+
const unk2 = reader.readInt32();
|
|
50
|
+
const posBeforeContents = reader.getBufferPosition();
|
|
51
|
+
const countEntities = reader.readInt32();
|
|
52
|
+
afterObjectsOfBatch = reader.getBufferPosition();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
reader.skipBytes(afterObjectsOfBatch - reader.getBufferPosition());
|
|
56
|
+
}
|
|
57
|
+
StreamParser.ReadNObjects(reader, objectCountToRead, objects, buildVersion);
|
|
58
|
+
afterObjectsOfBatch = reader.getBufferPosition();
|
|
59
|
+
totalReadObjects += objectCountToRead;
|
|
60
|
+
if (countObjectHeaders > 10000 && totalReadObjects % 10000 === 0) {
|
|
61
|
+
reader.onProgressCallback(reader.getBufferProgress(), `read object count [${(totalReadObjects + 1)}/${(countObjectHeaders + 1)}] in level ${levelName}`);
|
|
62
|
+
}
|
|
63
|
+
await writer.write(`${objects.map(obj => JSON.stringify(obj)).join(', ')}`);
|
|
64
|
+
} while (totalReadObjects < countObjectHeaders);
|
|
65
|
+
await writer.write('], "collectables": [');
|
|
66
|
+
const collectables = __1.Level.ReadCollectablesList(reader);
|
|
67
|
+
await writer.write(`${collectables.map(obj => JSON.stringify(obj)).join(', ')}`);
|
|
68
|
+
await writer.write(']');
|
|
69
|
+
await writer.write('}');
|
|
28
70
|
}
|
|
29
71
|
}
|
|
30
72
|
}
|
|
73
|
+
StreamParser.ReadNObjectHeaders = (reader, count) => {
|
|
74
|
+
let objects = [];
|
|
75
|
+
let objectsRead = 0;
|
|
76
|
+
for (; objectsRead < count; objectsRead++) {
|
|
77
|
+
let obj;
|
|
78
|
+
let objectType = reader.readInt32();
|
|
79
|
+
switch (objectType) {
|
|
80
|
+
case __1.SaveEntity.TypeID:
|
|
81
|
+
obj = new __1.SaveEntity('', '', '', '');
|
|
82
|
+
__1.SaveEntity.ParseHeader(reader, obj);
|
|
83
|
+
break;
|
|
84
|
+
case __1.SaveComponent.TypeID:
|
|
85
|
+
obj = new __1.SaveComponent('', '', '', '');
|
|
86
|
+
__1.SaveComponent.ParseHeader(reader, obj);
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
throw new Error('Unknown object type' + objectType);
|
|
90
|
+
}
|
|
91
|
+
objects.push(obj);
|
|
92
|
+
}
|
|
93
|
+
return objects;
|
|
94
|
+
};
|
|
95
|
+
StreamParser.ReadNObjects = (reader, count, objects, buildVersion) => {
|
|
96
|
+
for (let i = 0; i < count; i++) {
|
|
97
|
+
objects[i].saveOrBlueprintIndicator = reader.readInt32();
|
|
98
|
+
objects[i].unknownType2 = reader.readInt32();
|
|
99
|
+
const binarySize = reader.readInt32();
|
|
100
|
+
const before = reader.getBufferPosition();
|
|
101
|
+
if ((0, SaveEntity_1.isSaveEntity)(objects[i])) {
|
|
102
|
+
__1.SaveEntity.ParseData(objects[i], binarySize, reader, buildVersion, objects[i].typePath);
|
|
103
|
+
}
|
|
104
|
+
else if ((0, SaveComponent_1.isSaveComponent)(objects[i])) {
|
|
105
|
+
__1.SaveComponent.ParseData(objects[i], binarySize, reader, buildVersion, objects[i].typePath);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
31
109
|
exports.StreamParser = StreamParser;
|
|
@@ -6,20 +6,22 @@ import { ByteArray4, Grids } from '../satisfactory/save/save-reader';
|
|
|
6
6
|
export declare class SaveStreamWriter {
|
|
7
7
|
private writer;
|
|
8
8
|
private mode;
|
|
9
|
+
private tracker;
|
|
9
10
|
private formatTracker;
|
|
10
11
|
constructor(writer: WritableStreamDefaultWriter<string>);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
private createExecutionFunction;
|
|
13
|
+
beginSave: () => Promise<void>;
|
|
14
|
+
writeHeader: (header: SatisfactorySaveHeader) => Promise<void>;
|
|
15
|
+
writeCompressionInfo: (compressionInfo: ChunkCompressionInfo) => Promise<void>;
|
|
16
|
+
writeGridHash: (gridHash: ByteArray4) => Promise<void>;
|
|
17
|
+
writeGrids: (grids: Grids) => Promise<void>;
|
|
18
|
+
openLevels: () => Promise<void>;
|
|
19
|
+
openLevel: (levelName: string) => Promise<void>;
|
|
20
|
+
writeObjects: (...objects: SaveObject[]) => Promise<void>;
|
|
21
|
+
switchInLevelToCollectables: (...objects: SaveObject[]) => Promise<void>;
|
|
22
|
+
writeCollectables: (...collectables: ObjectReference[]) => Promise<void>;
|
|
23
|
+
endLevel: () => Promise<void>;
|
|
24
|
+
endLevels: () => Promise<void>;
|
|
25
|
+
endSave: () => Promise<void>;
|
|
24
26
|
close(): Promise<void>;
|
|
25
27
|
}
|
|
@@ -1,117 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SaveStreamWriter = void 0;
|
|
4
|
+
class ModeStateTracker {
|
|
5
|
+
constructor(mode) {
|
|
6
|
+
this.mode = mode;
|
|
7
|
+
}
|
|
8
|
+
checkIsComingFrom(...allowedPredecessors) {
|
|
9
|
+
if (!allowedPredecessors.includes(this.mode)) {
|
|
10
|
+
throw new Error(`Wrong order of commands. mode is ${this.mode}. but only ${allowedPredecessors.join(', ')} is/are allowed.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
advance(newMode) {
|
|
14
|
+
this.mode = newMode;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
class SaveStreamWriter {
|
|
5
18
|
constructor(writer) {
|
|
6
19
|
this.writer = writer;
|
|
20
|
+
this.createExecutionFunction = async (allowedInputModes, fn, targetMode) => {
|
|
21
|
+
this.tracker.checkIsComingFrom(...allowedInputModes);
|
|
22
|
+
await fn();
|
|
23
|
+
this.tracker.advance(targetMode);
|
|
24
|
+
};
|
|
25
|
+
this.beginSave = () => this.createExecutionFunction(['BEFORE_START'], async () => {
|
|
26
|
+
await this.writer.write('{');
|
|
27
|
+
}, 'OPENED_SAVE');
|
|
28
|
+
this.writeHeader = (header) => this.createExecutionFunction(['OPENED_SAVE'], async () => {
|
|
29
|
+
await this.writer.write(`"header": ${JSON.stringify(header)}`);
|
|
30
|
+
}, 'FINISHED_HEADER');
|
|
31
|
+
this.writeCompressionInfo = (compressionInfo) => this.createExecutionFunction(['FINISHED_HEADER'], async () => {
|
|
32
|
+
await this.writer.write(`, "compressionInfo": ${JSON.stringify(compressionInfo)}`);
|
|
33
|
+
}, 'WROTE_COMPRESSION_INFO');
|
|
34
|
+
this.writeGridHash = (gridHash) => this.createExecutionFunction(['WROTE_COMPRESSION_INFO'], async () => {
|
|
35
|
+
await this.writer.write(`, "gridHash": ${JSON.stringify(gridHash)}`);
|
|
36
|
+
}, 'WROTE_GRID_HASH');
|
|
37
|
+
this.writeGrids = (grids) => this.createExecutionFunction(['WROTE_GRID_HASH'], async () => {
|
|
38
|
+
await this.writer.write(`, "grids": ${JSON.stringify(grids)}`);
|
|
39
|
+
}, 'WROTE_GRIDS');
|
|
40
|
+
this.openLevels = () => this.createExecutionFunction(['WROTE_GRIDS'], async () => {
|
|
41
|
+
await this.writer.write(`, "levels": [`);
|
|
42
|
+
}, 'OPENED_LEVELS');
|
|
43
|
+
this.openLevel = (levelName) => this.createExecutionFunction(['OPENED_LEVELS', 'FINISHED_LEVEL'], async () => {
|
|
44
|
+
this.formatTracker.levels.push({
|
|
45
|
+
objectCount: 0, collectablesCount: 0
|
|
46
|
+
});
|
|
47
|
+
const prefix = this.formatTracker.levels.length > 1 ? ', ' : '';
|
|
48
|
+
await this.writer.write(`${prefix}{"name": "${levelName}", "objects": [`);
|
|
49
|
+
}, 'OPENED_LEVEL');
|
|
50
|
+
this.writeObjects = (...objects) => this.createExecutionFunction(['OPENED_LEVEL', 'WROTE_OBJECT'], async () => {
|
|
51
|
+
const stringified = objects.map(saveObj => JSON.stringify(saveObj));
|
|
52
|
+
for (const obj of stringified) {
|
|
53
|
+
await this.writer.write(`${this.formatTracker.levels.at(-1).objectCount >= 1 ? ', ' : ''}${obj}`);
|
|
54
|
+
this.formatTracker.levels.at(-1).objectCount++;
|
|
55
|
+
}
|
|
56
|
+
}, 'WROTE_OBJECT');
|
|
57
|
+
this.switchInLevelToCollectables = (...objects) => this.createExecutionFunction(['OPENED_LEVEL', 'WROTE_OBJECT'], async () => {
|
|
58
|
+
await this.writer.write(`], "collectables": [`);
|
|
59
|
+
}, 'SWITCH_TO_COLLECTABLES');
|
|
60
|
+
this.writeCollectables = (...collectables) => this.createExecutionFunction(['SWITCH_TO_COLLECTABLES', 'WROTE_COLLECTABLE'], async () => {
|
|
61
|
+
const stringified = collectables.map(coll => JSON.stringify(coll));
|
|
62
|
+
for (const obj of stringified) {
|
|
63
|
+
await this.writer.write(`${this.formatTracker.levels.at(-1).collectablesCount >= 1 ? ', ' : ''}${obj}`);
|
|
64
|
+
this.formatTracker.levels.at(-1).collectablesCount++;
|
|
65
|
+
}
|
|
66
|
+
}, 'WROTE_COLLECTABLE');
|
|
67
|
+
this.endLevel = () => this.createExecutionFunction(['SWITCH_TO_COLLECTABLES', 'WROTE_COLLECTABLE'], async () => {
|
|
68
|
+
await this.writer.write(`]}`);
|
|
69
|
+
}, 'FINISHED_LEVEL');
|
|
70
|
+
this.endLevels = () => this.createExecutionFunction(['OPENED_LEVELS', 'FINISHED_LEVEL'], async () => {
|
|
71
|
+
await this.writer.write(`]`);
|
|
72
|
+
}, 'FINISHED_LEVELS');
|
|
73
|
+
this.endSave = () => this.createExecutionFunction(['FINISHED_LEVELS'], async () => {
|
|
74
|
+
await this.writer.write('}');
|
|
75
|
+
}, 'FINISHED_SAVE');
|
|
7
76
|
this.mode = 'BEFORE_START';
|
|
77
|
+
this.tracker = new ModeStateTracker(this.mode);
|
|
8
78
|
this.formatTracker = {
|
|
9
79
|
levels: []
|
|
10
80
|
};
|
|
11
81
|
}
|
|
12
|
-
async beginSave() {
|
|
13
|
-
if (this.mode !== 'BEFORE_START') {
|
|
14
|
-
throw new Error(`Wrong order of commands. Already opened save. mode is ${this.mode}.`);
|
|
15
|
-
}
|
|
16
|
-
await this.writer.write('{');
|
|
17
|
-
this.mode = 'OPENED_SAVE';
|
|
18
|
-
}
|
|
19
|
-
async writeHeader(header) {
|
|
20
|
-
if (this.mode !== 'OPENED_SAVE') {
|
|
21
|
-
throw new Error(`Wrong order of commands. Header has to come after save open. mode is ${this.mode}.`);
|
|
22
|
-
}
|
|
23
|
-
await this.writer.write(`"header": ${JSON.stringify(header)}`);
|
|
24
|
-
this.mode = 'FINISHED_HEADER';
|
|
25
|
-
}
|
|
26
|
-
async writeCompressionInfo(compressionInfo) {
|
|
27
|
-
if (this.mode !== 'FINISHED_HEADER') {
|
|
28
|
-
throw new Error(`Wrong order of commands. Compression info has to come after header. mode is ${this.mode}.`);
|
|
29
|
-
}
|
|
30
|
-
await this.writer.write(`, "compressionInfo": ${JSON.stringify(compressionInfo)}`);
|
|
31
|
-
this.mode = 'WROTE_COMPRESSION_INFO';
|
|
32
|
-
}
|
|
33
|
-
async writeGridHash(gridHash) {
|
|
34
|
-
if (this.mode !== 'WROTE_COMPRESSION_INFO') {
|
|
35
|
-
throw new Error(`Wrong order of commands. Save hbody hash has to come after compression info. mode is ${this.mode}.`);
|
|
36
|
-
}
|
|
37
|
-
await this.writer.write(`, "gridHash": ${JSON.stringify(gridHash)}`);
|
|
38
|
-
this.mode = 'WROTE_GRID_HASH';
|
|
39
|
-
}
|
|
40
|
-
async writeGrids(grids) {
|
|
41
|
-
if (this.mode !== 'WROTE_GRID_HASH') {
|
|
42
|
-
throw new Error(`Wrong order of commands. Grids have to come after save body hash info. mode is ${this.mode}.`);
|
|
43
|
-
}
|
|
44
|
-
await this.writer.write(`, "grids": ${JSON.stringify(grids)}`);
|
|
45
|
-
this.mode = 'WROTE_GRIDS';
|
|
46
|
-
}
|
|
47
|
-
async openLevels() {
|
|
48
|
-
if (this.mode !== 'WROTE_GRIDS') {
|
|
49
|
-
throw new Error(`Wrong order of commands. Levels have to come after grids info. mode is ${this.mode}.`);
|
|
50
|
-
}
|
|
51
|
-
await this.writer.write(`, "levels": [`);
|
|
52
|
-
this.mode = 'OPENED_LEVELS';
|
|
53
|
-
}
|
|
54
|
-
async openLevel(levelName) {
|
|
55
|
-
if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
|
|
56
|
-
throw new Error(`Wrong order of commands. Single level can only come after opening levels array or after finishing single level. mode is ${this.mode}.`);
|
|
57
|
-
}
|
|
58
|
-
this.formatTracker.levels.push({
|
|
59
|
-
objectCount: 0, collectablesCount: 0
|
|
60
|
-
});
|
|
61
|
-
const prefix = this.formatTracker.levels.length > 1 ? ', ' : '';
|
|
62
|
-
await this.writer.write(`${prefix}{"name": "${levelName}", "objects": [`);
|
|
63
|
-
this.mode = 'OPENED_LEVEL';
|
|
64
|
-
}
|
|
65
|
-
async writeObjects(...objects) {
|
|
66
|
-
if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
|
|
67
|
-
throw new Error(`Wrong order of commands. An object can not be written into the level if it was not opened or other objects were not written. mode is ${this.mode}.`);
|
|
68
|
-
}
|
|
69
|
-
const stringified = objects.map(saveObj => JSON.stringify(saveObj));
|
|
70
|
-
for (const obj of stringified) {
|
|
71
|
-
await this.writer.write(`${this.formatTracker.levels.at(-1).objectCount >= 1 ? ', ' : ''}${obj}`);
|
|
72
|
-
this.formatTracker.levels.at(-1).objectCount++;
|
|
73
|
-
}
|
|
74
|
-
this.mode = 'WROTE_OBJECT';
|
|
75
|
-
}
|
|
76
|
-
async switchInLevelToCollectables() {
|
|
77
|
-
if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
|
|
78
|
-
throw new Error(`Wrong order of commands. The level structure can not be switched to collectables if the level was not opened recently or has not written objects. mode is ${this.mode}.`);
|
|
79
|
-
}
|
|
80
|
-
await this.writer.write(`], "collectables": [`);
|
|
81
|
-
this.mode = 'SWITCH_TO_COLLECTABLES';
|
|
82
|
-
}
|
|
83
|
-
async writeCollectables(...collectables) {
|
|
84
|
-
if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
|
|
85
|
-
throw new Error(`Wrong order of commands. A collectable can not be written into the level if we did not switch to collectables or other collectables were not written. mode is ${this.mode}.`);
|
|
86
|
-
}
|
|
87
|
-
const stringified = collectables.map(coll => JSON.stringify(coll));
|
|
88
|
-
for (const obj of stringified) {
|
|
89
|
-
await this.writer.write(`${this.formatTracker.levels.at(-1).collectablesCount >= 1 ? ', ' : ''}${obj}`);
|
|
90
|
-
this.formatTracker.levels.at(-1).collectablesCount++;
|
|
91
|
-
}
|
|
92
|
-
this.mode = 'WROTE_COLLECTABLE';
|
|
93
|
-
}
|
|
94
|
-
async endLevel() {
|
|
95
|
-
if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
|
|
96
|
-
throw new Error(`Wrong order of commands. Single level can not be closed if a switch to collectibles or writing of collectible did not happen. mode is ${this.mode}.`);
|
|
97
|
-
}
|
|
98
|
-
await this.writer.write(`]}`);
|
|
99
|
-
this.mode = 'FINISHED_LEVEL';
|
|
100
|
-
}
|
|
101
|
-
async endLevels() {
|
|
102
|
-
if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
|
|
103
|
-
throw new Error(`Wrong order of commands. Levels can only be closed after opening levels array or after finishing single level. mode is ${this.mode}.`);
|
|
104
|
-
}
|
|
105
|
-
await this.writer.write(`]`);
|
|
106
|
-
this.mode = 'FINISHED_LEVELS';
|
|
107
|
-
}
|
|
108
|
-
async endSave() {
|
|
109
|
-
if (this.mode !== 'FINISHED_LEVELS') {
|
|
110
|
-
throw new Error(`Wrong order of commands. Save has to end after levels. mode is ${this.mode}.`);
|
|
111
|
-
}
|
|
112
|
-
await this.writer.write('}');
|
|
113
|
-
this.mode = 'FINISHED_SAVE';
|
|
114
|
-
}
|
|
115
82
|
async close() {
|
|
116
83
|
return this.writer.close();
|
|
117
84
|
}
|
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.25",
|
|
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",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { ByteReader, ByteWriter } from '../../../..';
|
|
2
|
-
export type MD5Hash = {
|
|
3
|
-
isValid: boolean;
|
|
4
|
-
hash?: number[];
|
|
5
|
-
};
|
|
6
|
-
export declare const readMD5Hash: (reader: ByteReader) => MD5Hash;
|
|
7
|
-
export declare const writeMD5Hash: (writer: ByteWriter, md5Hash: MD5Hash) => void;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.writeMD5Hash = exports.readMD5Hash = void 0;
|
|
4
|
-
const readMD5Hash = (reader) => {
|
|
5
|
-
const md5Hash = { isValid: false };
|
|
6
|
-
md5Hash.isValid = reader.readInt32() === 1;
|
|
7
|
-
if (md5Hash.isValid) {
|
|
8
|
-
md5Hash.hash = Array.from(reader.readBytes(16));
|
|
9
|
-
}
|
|
10
|
-
return md5Hash;
|
|
11
|
-
};
|
|
12
|
-
exports.readMD5Hash = readMD5Hash;
|
|
13
|
-
const writeMD5Hash = (writer, md5Hash) => {
|
|
14
|
-
writer.writeInt32(md5Hash.isValid ? 1 : 0);
|
|
15
|
-
if (md5Hash.isValid) {
|
|
16
|
-
writer.writeBytesArray(md5Hash.hash);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
exports.writeMD5Hash = writeMD5Hash;
|