@etothepii/satisfactory-file-parser 0.0.15 → 0.0.16
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 +10 -8
- package/build/index.js +10 -12
- package/build/parser/byte/byte-reader.class.d.ts +1 -38
- package/build/parser/byte/byte-reader.class.js +0 -11
- package/build/parser/byte/byte-writer.class.d.ts +1 -38
- package/build/parser/byte/byte-writer.class.js +0 -10
- package/build/parser/file.types.d.ts +9 -0
- package/build/parser/file.types.js +2 -0
- package/build/parser/parser.d.ts +12 -40
- package/build/parser/parser.js +51 -47
- package/build/parser/satisfactory/blueprint/blueprint-reader.d.ts +18 -0
- package/build/parser/satisfactory/blueprint/blueprint-reader.js +124 -0
- package/build/parser/satisfactory/blueprint/blueprint-writer.d.ts +15 -0
- package/build/parser/satisfactory/blueprint/blueprint-writer.js +63 -0
- package/build/parser/satisfactory/blueprint/blueprint.types.d.ts +20 -0
- package/build/parser/satisfactory/blueprint/blueprint.types.js +2 -0
- package/build/parser/satisfactory/objects/DataFields.d.ts +1 -1
- package/build/parser/satisfactory/objects/DataFields.js +0 -25
- package/build/parser/satisfactory/objects/Property.d.ts +1 -1
- package/build/parser/satisfactory/objects/Property.js +10 -159
- package/build/parser/satisfactory/save/level.class.d.ts +21 -0
- package/build/parser/satisfactory/save/level.class.js +138 -0
- package/build/parser/satisfactory/save/satisfactory-save.d.ts +11 -0
- package/build/parser/satisfactory/save/satisfactory-save.js +11 -0
- package/build/parser/satisfactory/save/save-reader.d.ts +44 -0
- package/build/parser/satisfactory/save/save-reader.js +233 -0
- package/build/parser/satisfactory/save/save-writer.d.ts +12 -0
- package/build/parser/satisfactory/save/save-writer.js +112 -0
- package/build/parser/satisfactory/save/save.types.d.ts +49 -0
- package/build/parser/satisfactory/save/save.types.js +2 -0
- package/package.json +6 -2
package/build/index.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type * from './parser/satisfactory/blueprint/blueprint.types';
|
|
2
2
|
export { DataFields } from './parser/satisfactory/objects/DataFields';
|
|
3
3
|
export { ObjectReference } from './parser/satisfactory/objects/ObjectReference';
|
|
4
4
|
export * from './parser/satisfactory/objects/Property';
|
|
5
5
|
export { SaveComponent } from './parser/satisfactory/objects/SaveComponent';
|
|
6
6
|
export { SaveEntity } from './parser/satisfactory/objects/SaveEntity';
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
9
|
-
export
|
|
10
|
-
export {
|
|
7
|
+
export { Level } from './parser/satisfactory/save/level.class';
|
|
8
|
+
export { SatisfactorySave } from './parser/satisfactory/save/satisfactory-save';
|
|
9
|
+
export type * from './parser/satisfactory/save/save.types';
|
|
10
|
+
export { BlueprintConfigReader, BlueprintReader } from './parser/satisfactory/blueprint/blueprint-reader';
|
|
11
|
+
export { BlueprintConfigWriter, BlueprintWriter } from './parser/satisfactory/blueprint/blueprint-writer';
|
|
12
|
+
export { SaveReader } from './parser/satisfactory/save/save-reader';
|
|
13
|
+
export { SaveWriter } from './parser/satisfactory/save/save-writer';
|
|
11
14
|
export type * from './parser/satisfactory/structs/util.types';
|
|
12
|
-
export { SaveReader } from './parser/save-reader';
|
|
13
|
-
export { SaveWriter } from './parser/save-writer';
|
|
14
15
|
export { CompressionLibraryError, CorruptSaveError, ParserError, UnsupportedVersionError } from './parser/error/parser.error';
|
|
15
|
-
export
|
|
16
|
+
export type * from './parser/file.types';
|
|
17
|
+
export { Parser } from './parser/parser';
|
package/build/index.js
CHANGED
|
@@ -14,10 +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.
|
|
18
|
-
// types & classes for convenience
|
|
19
|
-
var level_class_1 = require("./parser/satisfactory/level.class");
|
|
20
|
-
Object.defineProperty(exports, "Level", { enumerable: true, get: function () { return level_class_1.Level; } });
|
|
17
|
+
exports.Parser = exports.UnsupportedVersionError = exports.ParserError = exports.CorruptSaveError = exports.CompressionLibraryError = exports.SaveWriter = exports.SaveReader = exports.BlueprintWriter = exports.BlueprintConfigWriter = exports.BlueprintReader = exports.BlueprintConfigReader = exports.SatisfactorySave = exports.Level = exports.SaveEntity = exports.SaveComponent = exports.ObjectReference = exports.DataFields = void 0;
|
|
21
18
|
var DataFields_1 = require("./parser/satisfactory/objects/DataFields");
|
|
22
19
|
Object.defineProperty(exports, "DataFields", { enumerable: true, get: function () { return DataFields_1.DataFields; } });
|
|
23
20
|
var ObjectReference_1 = require("./parser/satisfactory/objects/ObjectReference");
|
|
@@ -27,23 +24,24 @@ var SaveComponent_1 = require("./parser/satisfactory/objects/SaveComponent");
|
|
|
27
24
|
Object.defineProperty(exports, "SaveComponent", { enumerable: true, get: function () { return SaveComponent_1.SaveComponent; } });
|
|
28
25
|
var SaveEntity_1 = require("./parser/satisfactory/objects/SaveEntity");
|
|
29
26
|
Object.defineProperty(exports, "SaveEntity", { enumerable: true, get: function () { return SaveEntity_1.SaveEntity; } });
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
var level_class_1 = require("./parser/satisfactory/save/level.class");
|
|
28
|
+
Object.defineProperty(exports, "Level", { enumerable: true, get: function () { return level_class_1.Level; } });
|
|
29
|
+
var satisfactory_save_1 = require("./parser/satisfactory/save/satisfactory-save");
|
|
30
|
+
Object.defineProperty(exports, "SatisfactorySave", { enumerable: true, get: function () { return satisfactory_save_1.SatisfactorySave; } });
|
|
31
|
+
var blueprint_reader_1 = require("./parser/satisfactory/blueprint/blueprint-reader");
|
|
32
32
|
Object.defineProperty(exports, "BlueprintConfigReader", { enumerable: true, get: function () { return blueprint_reader_1.BlueprintConfigReader; } });
|
|
33
33
|
Object.defineProperty(exports, "BlueprintReader", { enumerable: true, get: function () { return blueprint_reader_1.BlueprintReader; } });
|
|
34
|
-
var blueprint_writer_1 = require("./parser/blueprint-writer");
|
|
34
|
+
var blueprint_writer_1 = require("./parser/satisfactory/blueprint/blueprint-writer");
|
|
35
35
|
Object.defineProperty(exports, "BlueprintConfigWriter", { enumerable: true, get: function () { return blueprint_writer_1.BlueprintConfigWriter; } });
|
|
36
36
|
Object.defineProperty(exports, "BlueprintWriter", { enumerable: true, get: function () { return blueprint_writer_1.BlueprintWriter; } });
|
|
37
|
-
var save_reader_1 = require("./parser/save-reader");
|
|
37
|
+
var save_reader_1 = require("./parser/satisfactory/save/save-reader");
|
|
38
38
|
Object.defineProperty(exports, "SaveReader", { enumerable: true, get: function () { return save_reader_1.SaveReader; } });
|
|
39
|
-
var save_writer_1 = require("./parser/save-writer");
|
|
39
|
+
var save_writer_1 = require("./parser/satisfactory/save/save-writer");
|
|
40
40
|
Object.defineProperty(exports, "SaveWriter", { enumerable: true, get: function () { return save_writer_1.SaveWriter; } });
|
|
41
|
-
// errors
|
|
42
41
|
var parser_error_1 = require("./parser/error/parser.error");
|
|
43
42
|
Object.defineProperty(exports, "CompressionLibraryError", { enumerable: true, get: function () { return parser_error_1.CompressionLibraryError; } });
|
|
44
43
|
Object.defineProperty(exports, "CorruptSaveError", { enumerable: true, get: function () { return parser_error_1.CorruptSaveError; } });
|
|
45
44
|
Object.defineProperty(exports, "ParserError", { enumerable: true, get: function () { return parser_error_1.ParserError; } });
|
|
46
45
|
Object.defineProperty(exports, "UnsupportedVersionError", { enumerable: true, get: function () { return parser_error_1.UnsupportedVersionError; } });
|
|
47
|
-
// facade
|
|
48
46
|
var parser_1 = require("./parser/parser");
|
|
49
|
-
Object.defineProperty(exports, "
|
|
47
|
+
Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return parser_1.Parser; } });
|
|
@@ -1,38 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare abstract class ByteReader {
|
|
3
|
-
protected bufferView: DataView;
|
|
4
|
-
protected fileBuffer: ArrayBuffer;
|
|
5
|
-
protected alignment: Alignment;
|
|
6
|
-
protected currentByte: number;
|
|
7
|
-
protected handledByte: number;
|
|
8
|
-
protected maxByte: number;
|
|
9
|
-
protected lastStrRead: number;
|
|
10
|
-
constructor(fileBuffer: ArrayBuffer, alignment: Alignment);
|
|
11
|
-
/**
|
|
12
|
-
* Resets the reader on the given arraybuffer to start from the beginning.
|
|
13
|
-
* @param newFileBuffer the new array buffer to be read.
|
|
14
|
-
*/
|
|
15
|
-
reset(newFileBuffer: ArrayBuffer): void;
|
|
16
|
-
skipBytes(byteLength?: number): void;
|
|
17
|
-
readByte(): number;
|
|
18
|
-
readBytes(count: number): Uint8Array;
|
|
19
|
-
readHex(hexLength: number): string;
|
|
20
|
-
readInt8(): number;
|
|
21
|
-
readUint8(): number;
|
|
22
|
-
readUint16(): number;
|
|
23
|
-
readInt32(): number;
|
|
24
|
-
readUint32(): number;
|
|
25
|
-
readLong(): bigint;
|
|
26
|
-
readFloat(): number;
|
|
27
|
-
readDouble(): number;
|
|
28
|
-
protected getStringInfo(): {
|
|
29
|
-
payload: string;
|
|
30
|
-
counter: number;
|
|
31
|
-
};
|
|
32
|
-
getBufferPosition: () => number;
|
|
33
|
-
getBufferSlice: (begin: number, end: number | undefined) => ArrayBuffer;
|
|
34
|
-
getBufferProgress: () => number;
|
|
35
|
-
getBufferLength: () => number;
|
|
36
|
-
getBuffer: () => ArrayBuffer;
|
|
37
|
-
readString(): string;
|
|
38
|
-
}
|
|
1
|
+
export {};
|
|
@@ -16,10 +16,6 @@ class ByteReader {
|
|
|
16
16
|
this.alignment = alignment;
|
|
17
17
|
this.reset(fileBuffer);
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Resets the reader on the given arraybuffer to start from the beginning.
|
|
21
|
-
* @param newFileBuffer the new array buffer to be read.
|
|
22
|
-
*/
|
|
23
19
|
reset(newFileBuffer) {
|
|
24
20
|
this.fileBuffer = newFileBuffer;
|
|
25
21
|
this.bufferView = new DataView(newFileBuffer, 0, Math.min(102400, this.fileBuffer.byteLength));
|
|
@@ -27,14 +23,10 @@ class ByteReader {
|
|
|
27
23
|
this.currentByte = 0;
|
|
28
24
|
this.handledByte = 0;
|
|
29
25
|
}
|
|
30
|
-
/*
|
|
31
|
-
* Byte Manipulation
|
|
32
|
-
*/
|
|
33
26
|
skipBytes(byteLength = 1) {
|
|
34
27
|
this.currentByte += byteLength;
|
|
35
28
|
}
|
|
36
29
|
readByte() {
|
|
37
|
-
// TODO can i not just readByte() ??
|
|
38
30
|
return parseInt(this.bufferView.getUint8(this.currentByte++).toString());
|
|
39
31
|
}
|
|
40
32
|
readBytes(count) {
|
|
@@ -105,18 +97,15 @@ class ByteReader {
|
|
|
105
97
|
if (strLength === 0) {
|
|
106
98
|
return '';
|
|
107
99
|
}
|
|
108
|
-
// Range error!
|
|
109
100
|
if (strLength > (this.bufferView.buffer.byteLength - this.currentByte)) {
|
|
110
101
|
let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.bufferView.buffer.byteLength}`;
|
|
111
102
|
throw new Error(errorMessage);
|
|
112
103
|
}
|
|
113
|
-
// it uses UTF16 if text is non-ascii, even if it would fit into UTF8.
|
|
114
104
|
if (strLength < 0) {
|
|
115
105
|
const string = new Array(-strLength - 1).fill('').map(c => String.fromCharCode(this.readUint16()));
|
|
116
106
|
this.currentByte += 2;
|
|
117
107
|
return string.join('');
|
|
118
108
|
}
|
|
119
|
-
//default UTF-8
|
|
120
109
|
try {
|
|
121
110
|
const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8()));
|
|
122
111
|
this.currentByte += 1;
|
|
@@ -1,38 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare abstract class ByteWriter {
|
|
3
|
-
protected alignment: Alignment;
|
|
4
|
-
protected bufferArray: ArrayBuffer;
|
|
5
|
-
protected bufferView: DataView;
|
|
6
|
-
protected currentByte: number;
|
|
7
|
-
constructor(alignment: Alignment, bufferSize?: number);
|
|
8
|
-
skipBytes(count?: number): void;
|
|
9
|
-
jumpTo(pos: number): void;
|
|
10
|
-
writeByte(value: number): void;
|
|
11
|
-
writeBytesArray(bytes: number[]): void;
|
|
12
|
-
writeBytes(bytes: Uint8Array): void;
|
|
13
|
-
writeInt8(value: number): void;
|
|
14
|
-
writeUint8(value: number): void;
|
|
15
|
-
writeInt16(value: number): void;
|
|
16
|
-
writeUint16(value: number): void;
|
|
17
|
-
writeInt32(value: number): void;
|
|
18
|
-
writeUint32(value: number): void;
|
|
19
|
-
writeInt64(value: bigint): void;
|
|
20
|
-
writeUint64(value: bigint): void;
|
|
21
|
-
writeFloat(value: number): void;
|
|
22
|
-
writeDouble(value: number): void;
|
|
23
|
-
writeString(value: string): void;
|
|
24
|
-
static IsASCIICompatible: (value: string) => boolean;
|
|
25
|
-
getBufferPosition: () => number;
|
|
26
|
-
getBufferSlice: (start: number, end?: number) => ArrayBuffer;
|
|
27
|
-
writeBinarySizeFromPosition(lenIndicatorPos: number, start: number): void;
|
|
28
|
-
/**
|
|
29
|
-
* automatically extends the current buffer if the given space exceeds the available rest capacity of the current buffer.
|
|
30
|
-
* @param countNeededBytes the needed space
|
|
31
|
-
* @param factor how big the appended buffer should be in comparison to the current one. Values >= 1 make sense.
|
|
32
|
-
*/
|
|
33
|
-
protected extendBufferIfNeeded(countNeededBytes: number, factor?: number): void;
|
|
34
|
-
protected truncateBuffer(): void;
|
|
35
|
-
endWriting(): ArrayBuffer;
|
|
36
|
-
static ToInt32(num: number): Uint8Array;
|
|
37
|
-
static AppendBuffer(buffer1: ArrayBuffer, buffer2: ArrayBuffer): ArrayBuffer;
|
|
38
|
-
}
|
|
1
|
+
export {};
|
|
@@ -11,9 +11,6 @@ class ByteWriter {
|
|
|
11
11
|
this.bufferView = new DataView(this.bufferArray);
|
|
12
12
|
this.currentByte = 0;
|
|
13
13
|
}
|
|
14
|
-
/*
|
|
15
|
-
* Byte Manipulation
|
|
16
|
-
*/
|
|
17
14
|
skipBytes(count = 1) {
|
|
18
15
|
this.extendBufferIfNeeded(count);
|
|
19
16
|
this.currentByte += count;
|
|
@@ -87,7 +84,6 @@ class ByteWriter {
|
|
|
87
84
|
this.writeInt32(0);
|
|
88
85
|
return;
|
|
89
86
|
}
|
|
90
|
-
// if it's safe to use ASCII, use UTF8.
|
|
91
87
|
if (ByteWriter.IsASCIICompatible(value)) {
|
|
92
88
|
this.writeInt32(value.length + 1);
|
|
93
89
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -95,7 +91,6 @@ class ByteWriter {
|
|
|
95
91
|
}
|
|
96
92
|
this.writeUint8(0);
|
|
97
93
|
}
|
|
98
|
-
// write UTF16
|
|
99
94
|
else {
|
|
100
95
|
this.writeInt32(-value.length - 1);
|
|
101
96
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -111,11 +106,6 @@ class ByteWriter {
|
|
|
111
106
|
this.writeInt32(writtenBytes);
|
|
112
107
|
this.jumpTo(after);
|
|
113
108
|
}
|
|
114
|
-
/**
|
|
115
|
-
* automatically extends the current buffer if the given space exceeds the available rest capacity of the current buffer.
|
|
116
|
-
* @param countNeededBytes the needed space
|
|
117
|
-
* @param factor how big the appended buffer should be in comparison to the current one. Values >= 1 make sense.
|
|
118
|
-
*/
|
|
119
109
|
extendBufferIfNeeded(countNeededBytes, factor = 1.5) {
|
|
120
110
|
if (this.currentByte + countNeededBytes > this.bufferView.byteLength) {
|
|
121
111
|
this.bufferArray = ByteWriter.AppendBuffer(this.bufferArray, new ArrayBuffer(factor * this.bufferArray.byteLength));
|
package/build/parser/parser.d.ts
CHANGED
|
@@ -1,44 +1,16 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
static
|
|
14
|
-
errors: string[];
|
|
15
|
-
chunkSummary: {
|
|
16
|
-
uncompressedSize: number;
|
|
17
|
-
compressedSize: number;
|
|
18
|
-
}[];
|
|
19
|
-
}) => void): void;
|
|
20
|
-
/**
|
|
21
|
-
* parses a save object from a binary buffer and reports back on decompressing the save body in a callback.
|
|
22
|
-
* @param file the binary content to be parsed.
|
|
23
|
-
* @param additionalData additional static data that is not contained in save files. For example, power slugs and resource nodes.
|
|
24
|
-
* @param onSaveDecompressed gets called when the entire binary save body has been decompressed.
|
|
25
|
-
* @returns a brief summary
|
|
26
|
-
*/
|
|
27
|
-
static ParseSaveFile(file: Buffer, onSaveDecompressed?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
|
|
28
|
-
/**
|
|
29
|
-
* Does Projection on a Save Object in order to reduce it's complexity.
|
|
30
|
-
* @param save the save object in question.
|
|
31
|
-
* @param config the projection config to apply.
|
|
32
|
-
* @returns a save object that has been projected / modified. Do not try to serialize it into an actual save tho! Since information has been transformed.
|
|
33
|
-
*/
|
|
2
|
+
import { ChunkSummary } from "./file.types";
|
|
3
|
+
import { Blueprint } from "./satisfactory/blueprint/blueprint.types";
|
|
4
|
+
import { SatisfactorySave } from "./satisfactory/save/satisfactory-save";
|
|
5
|
+
import { SaveProjectionConfig } from "./satisfactory/save/save-reader";
|
|
6
|
+
export declare class Parser {
|
|
7
|
+
static WriteSave(save: SatisfactorySave, onBinaryBeforeCompressing: (buffer: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void): ChunkSummary[];
|
|
8
|
+
static ParseSaveFile(file: Buffer, onDecompressedSaveBody?: (buffer: ArrayBuffer) => void, onProgress?: (progress: number, message?: string) => void): SatisfactorySave;
|
|
9
|
+
static WriteBlueprintFiles(blueprint: Blueprint, onMainFileBinaryBeforeCompressing?: (binary: ArrayBuffer) => void, onMainFileHeader?: (header: Uint8Array) => void, onMainFileChunk?: (chunk: Uint8Array) => void): {
|
|
10
|
+
mainFileChunkSummary: ChunkSummary[];
|
|
11
|
+
configFileBinary: ArrayBuffer;
|
|
12
|
+
};
|
|
13
|
+
static ParseBlueprintFiles(name: string, blueprintFile: Buffer, blueprintConfigFile: Buffer, onDecompressedBlueprintBody?: (buffer: ArrayBuffer) => void): Blueprint;
|
|
34
14
|
static ProjectSave(save: SatisfactorySave, config: SaveProjectionConfig): SatisfactorySave;
|
|
35
|
-
/**
|
|
36
|
-
* It JSON.stringifies any parsed content safely. The original JSON.stringify() has some flaws that get in the way, so it is custom wrapped. The original has some problems:
|
|
37
|
-
* 1. it cannot stringify bigints. So we help out by converting it into a string.
|
|
38
|
-
* 2. It cannot distinguish between 0 and -0. But a float32 is encoded in a uint8Array for 0 to be [0,0,0,0] (0x00000000) and -0 to be [0,0,0,128] (0x00000080) in little endian.
|
|
39
|
-
* @param obj
|
|
40
|
-
* @param indent
|
|
41
|
-
* @returns a string that is safely stringified.
|
|
42
|
-
*/
|
|
43
15
|
static JSONStringifyModified: (obj: any, indent?: number) => string;
|
|
44
16
|
}
|
package/build/parser/parser.js
CHANGED
|
@@ -1,56 +1,68 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* @param onBinaryBeforeCompressing gets called on the binary save body before it is compressed.
|
|
13
|
-
* @param onHeader gets called on the binary save header, which is always uncompressed.
|
|
14
|
-
* @param onChunk gets called when a chunk of the compressed save body was generated. Often, files' save bodies consist of multiple chunks.
|
|
15
|
-
* @param onFinish gets called when all chunks have been generated. A summary of the chunk sizes and count is reported.
|
|
16
|
-
*/
|
|
17
|
-
static WriteSave(save, onBinaryBeforeCompressing, onHeader, onChunk, onFinish) {
|
|
3
|
+
exports.Parser = void 0;
|
|
4
|
+
const blueprint_reader_1 = require("./satisfactory/blueprint/blueprint-reader");
|
|
5
|
+
const blueprint_writer_1 = require("./satisfactory/blueprint/blueprint-writer");
|
|
6
|
+
const level_class_1 = require("./satisfactory/save/level.class");
|
|
7
|
+
const satisfactory_save_1 = require("./satisfactory/save/satisfactory-save");
|
|
8
|
+
const save_reader_1 = require("./satisfactory/save/save-reader");
|
|
9
|
+
const save_writer_1 = require("./satisfactory/save/save-writer");
|
|
10
|
+
class Parser {
|
|
11
|
+
static WriteSave(save, onBinaryBeforeCompressing, onHeader, onChunk) {
|
|
18
12
|
const writer = new save_writer_1.SaveWriter();
|
|
19
13
|
save_writer_1.SaveWriter.WriteHeader(writer, save.header);
|
|
20
14
|
const posAfterHeader = writer.getBufferPosition();
|
|
21
15
|
save_writer_1.SaveWriter.WriteLevels(writer, save, save.header.buildVersion);
|
|
22
16
|
writer.endWriting();
|
|
23
|
-
writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk
|
|
17
|
+
const chunkSummary = writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk);
|
|
18
|
+
return chunkSummary;
|
|
24
19
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* @param file the binary content to be parsed.
|
|
28
|
-
* @param additionalData additional static data that is not contained in save files. For example, power slugs and resource nodes.
|
|
29
|
-
* @param onSaveDecompressed gets called when the entire binary save body has been decompressed.
|
|
30
|
-
* @returns a brief summary
|
|
31
|
-
*/
|
|
32
|
-
static ParseSaveFile(file, onSaveDecompressed = () => { }, onProgress = () => { }) {
|
|
33
|
-
const reader = new save_reader_1.SaveReader(file.buffer, onProgress);
|
|
34
|
-
// read header
|
|
20
|
+
static ParseSaveFile(file, onDecompressedSaveBody = () => { }, onProgress = () => { }) {
|
|
21
|
+
const reader = new save_reader_1.SaveReader(new Uint8Array(file).buffer, onProgress);
|
|
35
22
|
const header = reader.readHeader();
|
|
36
23
|
const save = new satisfactory_save_1.SatisfactorySave(header);
|
|
37
|
-
// inflate chunks
|
|
38
24
|
const inflateResult = reader.inflateChunks();
|
|
39
|
-
|
|
40
|
-
onSaveDecompressed(reader.getBuffer());
|
|
41
|
-
// parse levels
|
|
25
|
+
onDecompressedSaveBody(reader.getBuffer());
|
|
42
26
|
const levelParseResult = reader.readLevels();
|
|
43
27
|
save.levels = reader.levels;
|
|
44
28
|
save.compressionInfo = reader.compressionInfo;
|
|
45
29
|
save.trailingCollectedObjects = reader.trailingCollectedObjects;
|
|
46
30
|
return save;
|
|
47
31
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
static WriteBlueprintFiles(blueprint, onMainFileBinaryBeforeCompressing = () => { }, onMainFileHeader = () => { }, onMainFileChunk = () => { }) {
|
|
33
|
+
const blueprintWriter = new blueprint_writer_1.BlueprintWriter();
|
|
34
|
+
blueprint_writer_1.BlueprintWriter.SerializeHeader(blueprintWriter, blueprint.header);
|
|
35
|
+
const saveBodyPos = blueprintWriter.getBufferPosition();
|
|
36
|
+
blueprint_writer_1.BlueprintWriter.SerializeObjects(blueprintWriter, blueprint.objects);
|
|
37
|
+
blueprintWriter.endWriting();
|
|
38
|
+
let binaryChunks = [];
|
|
39
|
+
let binaryHeader;
|
|
40
|
+
const mainFileChunkSummary = blueprintWriter.generateChunks(blueprint.compressionInfo, saveBodyPos, onMainFileBinaryBeforeCompressing, onMainFileHeader, onMainFileChunk);
|
|
41
|
+
const configWriter = new blueprint_writer_1.BlueprintConfigWriter();
|
|
42
|
+
blueprint_writer_1.BlueprintConfigWriter.SerializeConfig(configWriter, blueprint.config);
|
|
43
|
+
const configFileBinary = configWriter.endWriting();
|
|
44
|
+
return {
|
|
45
|
+
mainFileChunkSummary,
|
|
46
|
+
configFileBinary
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
static ParseBlueprintFiles(name, blueprintFile, blueprintConfigFile, onDecompressedBlueprintBody = () => { }) {
|
|
50
|
+
const blueprintConfigReader = new blueprint_reader_1.BlueprintConfigReader(new Uint8Array(blueprintConfigFile).buffer);
|
|
51
|
+
const config = blueprint_reader_1.BlueprintConfigReader.ParseConfig(blueprintConfigReader);
|
|
52
|
+
const blueprintReader = new blueprint_reader_1.BlueprintReader(new Uint8Array(blueprintFile).buffer);
|
|
53
|
+
const header = blueprint_reader_1.BlueprintReader.ReadHeader(blueprintReader);
|
|
54
|
+
const inflateResult = blueprintReader.inflateChunks();
|
|
55
|
+
onDecompressedBlueprintBody(inflateResult.inflatedData);
|
|
56
|
+
const blueprintObjects = blueprint_reader_1.BlueprintReader.ParseObjects(blueprintReader);
|
|
57
|
+
const blueprint = {
|
|
58
|
+
name,
|
|
59
|
+
compressionInfo: blueprintReader.compressionInfo,
|
|
60
|
+
header: header,
|
|
61
|
+
config,
|
|
62
|
+
objects: blueprintObjects
|
|
63
|
+
};
|
|
64
|
+
return blueprint;
|
|
65
|
+
}
|
|
54
66
|
static ProjectSave(save, config) {
|
|
55
67
|
return {
|
|
56
68
|
header: save.header,
|
|
@@ -64,21 +76,13 @@ class SaveParser {
|
|
|
64
76
|
};
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
|
-
|
|
68
|
-
* It JSON.stringifies any parsed content safely. The original JSON.stringify() has some flaws that get in the way, so it is custom wrapped. The original has some problems:
|
|
69
|
-
* 1. it cannot stringify bigints. So we help out by converting it into a string.
|
|
70
|
-
* 2. It cannot distinguish between 0 and -0. But a float32 is encoded in a uint8Array for 0 to be [0,0,0,0] (0x00000000) and -0 to be [0,0,0,128] (0x00000080) in little endian.
|
|
71
|
-
* @param obj
|
|
72
|
-
* @param indent
|
|
73
|
-
* @returns a string that is safely stringified.
|
|
74
|
-
*/
|
|
75
|
-
SaveParser.JSONStringifyModified = (obj, indent = 0) => JSON.stringify(obj, (key, value) => {
|
|
79
|
+
Parser.JSONStringifyModified = (obj, indent = 0) => JSON.stringify(obj, (key, value) => {
|
|
76
80
|
if (typeof value === 'bigint') {
|
|
77
81
|
return value.toString();
|
|
78
82
|
}
|
|
79
|
-
else if (value === 0 && 1 / value < 0) {
|
|
83
|
+
else if (value === 0 && 1 / value < 0) {
|
|
80
84
|
return '-0';
|
|
81
85
|
}
|
|
82
86
|
return value;
|
|
83
87
|
}, indent);
|
|
84
|
-
exports.
|
|
88
|
+
exports.Parser = Parser;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ByteReader } from "../../byte/byte-reader.class";
|
|
2
|
+
import { ChunkCompressionInfo } from "../../file.types";
|
|
3
|
+
import { SaveComponent } from "../objects/SaveComponent";
|
|
4
|
+
import { SaveEntity } from "../objects/SaveEntity";
|
|
5
|
+
import { BlueprintConfig, BlueprintHeader } from "./blueprint.types";
|
|
6
|
+
export declare class BlueprintReader extends ByteReader {
|
|
7
|
+
compressionInfo: ChunkCompressionInfo;
|
|
8
|
+
constructor(bluePrintBuffer: ArrayBuffer);
|
|
9
|
+
static ReadHeader(reader: ByteReader): BlueprintHeader;
|
|
10
|
+
inflateChunks(): any;
|
|
11
|
+
static ParseObjects(reader: ByteReader): (SaveEntity | SaveComponent)[];
|
|
12
|
+
}
|
|
13
|
+
export declare class BlueprintConfigReader extends ByteReader {
|
|
14
|
+
bluePrintConfigBuffer: ArrayBuffer;
|
|
15
|
+
constructor(bluePrintConfigBuffer: ArrayBuffer);
|
|
16
|
+
parse: () => BlueprintConfig;
|
|
17
|
+
static ParseConfig(reader: ByteReader): BlueprintConfig;
|
|
18
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BlueprintConfigReader = exports.BlueprintReader = void 0;
|
|
7
|
+
const pako_1 = __importDefault(require("pako"));
|
|
8
|
+
const alignment_enum_1 = require("../../byte/alignment.enum");
|
|
9
|
+
const byte_reader_class_1 = require("../../byte/byte-reader.class");
|
|
10
|
+
const parser_error_1 = require("../../error/parser.error");
|
|
11
|
+
const level_class_1 = require("../save/level.class");
|
|
12
|
+
const util_types_1 = require("../structs/util.types");
|
|
13
|
+
class BlueprintReader extends byte_reader_class_1.ByteReader {
|
|
14
|
+
constructor(bluePrintBuffer) {
|
|
15
|
+
super(bluePrintBuffer, alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
16
|
+
this.compressionInfo = {
|
|
17
|
+
packageFileTag: 0,
|
|
18
|
+
maxChunkContentSize: 0,
|
|
19
|
+
chunkHeaderSize: 48
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
static ReadHeader(reader) {
|
|
23
|
+
const unk = reader.readBytes(3 * 4);
|
|
24
|
+
const positionThingOrWhat = (0, util_types_1.ParseVec3)(reader);
|
|
25
|
+
let itemTypeCount = reader.readInt32();
|
|
26
|
+
const itemCosts = new Array(itemTypeCount).fill(['', 0]);
|
|
27
|
+
for (let i = 0; i < itemTypeCount; i++) {
|
|
28
|
+
let indexOrWhat = reader.readInt32();
|
|
29
|
+
let itemPathName = reader.readString();
|
|
30
|
+
let itemCount = reader.readInt32();
|
|
31
|
+
itemCosts[i] = [itemPathName, itemCount];
|
|
32
|
+
}
|
|
33
|
+
let recipeCount = reader.readInt32();
|
|
34
|
+
const recipeRefs = new Array(recipeCount).fill('');
|
|
35
|
+
for (let i = 0; i < recipeCount; i++) {
|
|
36
|
+
let indexOrWhat = reader.readInt32();
|
|
37
|
+
const recipeName = reader.readString();
|
|
38
|
+
recipeRefs[i] = recipeName;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
recipeReferences: recipeRefs,
|
|
42
|
+
itemCosts
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
inflateChunks() {
|
|
46
|
+
this.fileBuffer = this.fileBuffer.slice(this.currentByte);
|
|
47
|
+
this.handledByte = 0;
|
|
48
|
+
this.currentByte = 0;
|
|
49
|
+
this.maxByte = this.fileBuffer.byteLength;
|
|
50
|
+
let currentChunks = [];
|
|
51
|
+
let totalUncompressedBodySize = 0;
|
|
52
|
+
while (this.handledByte < this.maxByte) {
|
|
53
|
+
let chunkHeader = new DataView(this.fileBuffer.slice(0, this.compressionInfo.chunkHeaderSize));
|
|
54
|
+
this.currentByte = this.compressionInfo.chunkHeaderSize;
|
|
55
|
+
this.handledByte += this.compressionInfo.chunkHeaderSize;
|
|
56
|
+
if (this.compressionInfo.packageFileTag <= 0) {
|
|
57
|
+
this.compressionInfo.packageFileTag = chunkHeader.getUint32(0, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
58
|
+
}
|
|
59
|
+
if (this.compressionInfo.maxChunkContentSize <= 0) {
|
|
60
|
+
this.compressionInfo.maxChunkContentSize = chunkHeader.getInt32(8, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
61
|
+
}
|
|
62
|
+
const chunkCompressedLength = chunkHeader.getInt32(32, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
63
|
+
const chunkUncompressedLength = chunkHeader.getInt32(40, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
64
|
+
totalUncompressedBodySize += chunkUncompressedLength;
|
|
65
|
+
const currentChunkSize = chunkCompressedLength;
|
|
66
|
+
let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
|
|
67
|
+
this.handledByte += currentChunkSize;
|
|
68
|
+
this.currentByte += currentChunkSize;
|
|
69
|
+
this.fileBuffer = this.fileBuffer.slice(this.currentByte);
|
|
70
|
+
this.currentByte = 0;
|
|
71
|
+
try {
|
|
72
|
+
let currentInflatedChunk = null;
|
|
73
|
+
currentInflatedChunk = pako_1.default.inflate(currentChunk);
|
|
74
|
+
currentChunks.push(currentInflatedChunk);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
throw new parser_error_1.ParserError('ParserError', 'An error occurred while calling pako inflate.' + err);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let newChunkLength = currentChunks.map(cc => cc.length).reduce((prev, cur) => prev + cur);
|
|
81
|
+
const bigWholeChunk = new Uint8Array(newChunkLength);
|
|
82
|
+
let currentLength = 0;
|
|
83
|
+
for (let i = 0; i < currentChunks.length; i++) {
|
|
84
|
+
bigWholeChunk.set(currentChunks[i], currentLength);
|
|
85
|
+
currentLength += currentChunks[i].length;
|
|
86
|
+
}
|
|
87
|
+
this.currentByte = 0;
|
|
88
|
+
this.maxByte = bigWholeChunk.buffer.byteLength;
|
|
89
|
+
this.bufferView = new DataView(bigWholeChunk.buffer);
|
|
90
|
+
return {
|
|
91
|
+
newChunkLength,
|
|
92
|
+
numChunks: currentChunks.length,
|
|
93
|
+
inflatedData: bigWholeChunk
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
static ParseObjects(reader) {
|
|
97
|
+
const totalBodyRestSize = reader.readInt32();
|
|
98
|
+
const objectHeadersBinarySize = reader.readInt32();
|
|
99
|
+
let objects = [];
|
|
100
|
+
level_class_1.Level.ReadObjectHeaders(reader, objects, () => { });
|
|
101
|
+
level_class_1.Level.ReadObjectContents(reader, objects, 0, () => { });
|
|
102
|
+
return objects;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.BlueprintReader = BlueprintReader;
|
|
106
|
+
class BlueprintConfigReader extends byte_reader_class_1.ByteReader {
|
|
107
|
+
constructor(bluePrintConfigBuffer) {
|
|
108
|
+
super(bluePrintConfigBuffer, alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
109
|
+
this.bluePrintConfigBuffer = bluePrintConfigBuffer;
|
|
110
|
+
this.parse = () => BlueprintConfigReader.ParseConfig(this);
|
|
111
|
+
}
|
|
112
|
+
static ParseConfig(reader) {
|
|
113
|
+
const unk = reader.readInt32();
|
|
114
|
+
const description = reader.readString();
|
|
115
|
+
const unk3 = reader.readInt32();
|
|
116
|
+
const colorMaybe = (0, util_types_1.ParseCol4RGBA)(reader);
|
|
117
|
+
return {
|
|
118
|
+
description,
|
|
119
|
+
color: colorMaybe,
|
|
120
|
+
iconID: unk3
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.BlueprintConfigReader = BlueprintConfigReader;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ByteWriter } from "../../byte/byte-writer.class";
|
|
2
|
+
import { ChunkCompressionInfo, ChunkSummary } from "../../file.types";
|
|
3
|
+
import { SaveComponent } from "../objects/SaveComponent";
|
|
4
|
+
import { SaveEntity } from "../objects/SaveEntity";
|
|
5
|
+
import { BlueprintConfig, BlueprintHeader } from "./blueprint.types";
|
|
6
|
+
export declare class BlueprintWriter extends ByteWriter {
|
|
7
|
+
constructor();
|
|
8
|
+
static SerializeHeader(writer: ByteWriter, header: BlueprintHeader): void;
|
|
9
|
+
generateChunks(compressionInfo: ChunkCompressionInfo, posAfterHeader: number, onBinaryBeforeCompressing: (binary: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void): ChunkSummary[];
|
|
10
|
+
static SerializeObjects(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[]): void;
|
|
11
|
+
}
|
|
12
|
+
export declare class BlueprintConfigWriter extends ByteWriter {
|
|
13
|
+
constructor();
|
|
14
|
+
static SerializeConfig(writer: ByteWriter, config: BlueprintConfig): void;
|
|
15
|
+
}
|