@etothepii/satisfactory-file-parser 0.0.10 → 0.0.12
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 +15 -0
- package/build/index.js +49 -0
- package/build/parser/blueprint-reader.d.ts +18 -0
- package/build/parser/blueprint-reader.js +136 -0
- package/build/parser/blueprint-writer.d.ts +21 -0
- package/build/parser/blueprint-writer.js +69 -0
- package/build/parser/byte/alignment.enum.d.ts +4 -0
- package/build/parser/byte/alignment.enum.js +8 -0
- package/build/parser/byte/byte-reader.class.d.ts +38 -0
- package/build/parser/byte/byte-reader.class.js +132 -0
- package/build/parser/byte/byte-writer.class.d.ts +38 -0
- package/build/parser/byte/byte-writer.class.js +149 -0
- package/build/parser/error/parser.error.d.ts +12 -0
- package/build/parser/error/parser.error.js +28 -0
- package/build/parser/parser.d.ts +44 -0
- package/build/parser/parser.js +84 -0
- package/build/parser/satisfactory/blueprint/blueprint.d.ts +20 -0
- package/build/parser/satisfactory/blueprint/blueprint.js +2 -0
- package/build/parser/satisfactory/level.class.d.ts +21 -0
- package/build/parser/satisfactory/level.class.js +148 -0
- package/build/parser/satisfactory/objects/DataFields.d.ts +13 -0
- package/build/parser/satisfactory/objects/DataFields.js +249 -0
- package/build/parser/satisfactory/objects/ObjectReference.d.ts +9 -0
- package/build/parser/satisfactory/objects/ObjectReference.js +17 -0
- package/build/parser/satisfactory/objects/Property.d.ts +270 -0
- package/build/parser/satisfactory/objects/Property.js +1150 -0
- package/build/parser/satisfactory/objects/SaveComponent.d.ts +16 -0
- package/build/parser/satisfactory/objects/SaveComponent.js +31 -0
- package/build/parser/satisfactory/objects/SaveEntity.d.ts +25 -0
- package/build/parser/satisfactory/objects/SaveEntity.js +70 -0
- package/build/parser/satisfactory/objects/SaveObject.d.ts +14 -0
- package/build/parser/satisfactory/objects/SaveObject.js +29 -0
- package/build/parser/satisfactory/satisfactory-save.d.ts +57 -0
- package/build/parser/satisfactory/satisfactory-save.js +11 -0
- package/build/parser/satisfactory/static-data.d.ts +7 -0
- package/build/parser/satisfactory/static-data.js +2 -0
- package/build/parser/satisfactory/structs/util.types.d.ts +40 -0
- package/build/parser/satisfactory/structs/util.types.js +95 -0
- package/build/parser/save-reader.d.ts +43 -0
- package/build/parser/save-reader.js +251 -0
- package/build/parser/save-writer.d.ts +22 -0
- package/build/parser/save-writer.js +121 -0
- package/package.json +3 -3
- package/index.d.ts +0 -2356
- package/index.js +0 -2364
- package/rollup.config.mjs +0 -11
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ByteWriter = void 0;
|
|
4
|
+
const alignment_enum_1 = require("./alignment.enum");
|
|
5
|
+
class ByteWriter {
|
|
6
|
+
constructor(alignment, bufferSize = 500) {
|
|
7
|
+
this.getBufferPosition = () => this.currentByte;
|
|
8
|
+
this.getBufferSlice = (start, end) => this.bufferArray.slice(start, end);
|
|
9
|
+
this.alignment = alignment;
|
|
10
|
+
this.bufferArray = new ArrayBuffer(bufferSize);
|
|
11
|
+
this.bufferView = new DataView(this.bufferArray);
|
|
12
|
+
this.currentByte = 0;
|
|
13
|
+
}
|
|
14
|
+
/*
|
|
15
|
+
* Byte Manipulation
|
|
16
|
+
*/
|
|
17
|
+
skipBytes(count = 1) {
|
|
18
|
+
this.extendBufferIfNeeded(count);
|
|
19
|
+
this.currentByte += count;
|
|
20
|
+
}
|
|
21
|
+
jumpTo(pos) {
|
|
22
|
+
const count = pos - this.getBufferPosition();
|
|
23
|
+
this.skipBytes(count);
|
|
24
|
+
}
|
|
25
|
+
writeByte(value) {
|
|
26
|
+
this.extendBufferIfNeeded(1);
|
|
27
|
+
this.bufferView.setUint8(this.currentByte, value);
|
|
28
|
+
this.currentByte += 1;
|
|
29
|
+
}
|
|
30
|
+
writeBytesArray(bytes) {
|
|
31
|
+
this.writeBytes(new Uint8Array(bytes));
|
|
32
|
+
}
|
|
33
|
+
writeBytes(bytes) {
|
|
34
|
+
this.extendBufferIfNeeded(bytes.length);
|
|
35
|
+
bytes.forEach(byte => this.bufferView.setUint8(this.currentByte++, byte));
|
|
36
|
+
}
|
|
37
|
+
writeInt8(value) {
|
|
38
|
+
this.extendBufferIfNeeded(1);
|
|
39
|
+
this.bufferView.setInt8(this.currentByte, value);
|
|
40
|
+
this.currentByte += 1;
|
|
41
|
+
}
|
|
42
|
+
writeUint8(value) {
|
|
43
|
+
this.writeByte(value);
|
|
44
|
+
}
|
|
45
|
+
writeInt16(value) {
|
|
46
|
+
this.extendBufferIfNeeded(2);
|
|
47
|
+
this.bufferView.setInt16(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
48
|
+
this.currentByte += 2;
|
|
49
|
+
}
|
|
50
|
+
writeUint16(value) {
|
|
51
|
+
this.extendBufferIfNeeded(2);
|
|
52
|
+
this.bufferView.setUint16(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
53
|
+
this.currentByte += 2;
|
|
54
|
+
}
|
|
55
|
+
writeInt32(value) {
|
|
56
|
+
this.extendBufferIfNeeded(4);
|
|
57
|
+
this.bufferView.setInt32(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
58
|
+
this.currentByte += 4;
|
|
59
|
+
}
|
|
60
|
+
writeUint32(value) {
|
|
61
|
+
this.extendBufferIfNeeded(4);
|
|
62
|
+
this.bufferView.setUint32(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
63
|
+
this.currentByte += 4;
|
|
64
|
+
}
|
|
65
|
+
writeInt64(value) {
|
|
66
|
+
this.extendBufferIfNeeded(8);
|
|
67
|
+
this.bufferView.setBigInt64(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
68
|
+
this.currentByte += 8;
|
|
69
|
+
}
|
|
70
|
+
writeUint64(value) {
|
|
71
|
+
this.extendBufferIfNeeded(8);
|
|
72
|
+
this.bufferView.setBigUint64(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
73
|
+
this.currentByte += 8;
|
|
74
|
+
}
|
|
75
|
+
writeFloat(value) {
|
|
76
|
+
this.extendBufferIfNeeded(4);
|
|
77
|
+
this.bufferView.setFloat32(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
78
|
+
this.currentByte += 4;
|
|
79
|
+
}
|
|
80
|
+
writeDouble(value) {
|
|
81
|
+
this.extendBufferIfNeeded(8);
|
|
82
|
+
this.bufferView.setFloat64(this.currentByte, value, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
|
|
83
|
+
this.currentByte += 8;
|
|
84
|
+
}
|
|
85
|
+
writeString(value) {
|
|
86
|
+
if (value.length === 0) {
|
|
87
|
+
this.writeInt32(0);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// if it's safe to use ASCII, use UTF8.
|
|
91
|
+
if (ByteWriter.IsASCIICompatible(value)) {
|
|
92
|
+
this.writeInt32(value.length + 1);
|
|
93
|
+
for (let i = 0; i < value.length; i++) {
|
|
94
|
+
this.writeByte(value.charCodeAt(i));
|
|
95
|
+
}
|
|
96
|
+
this.writeUint8(0);
|
|
97
|
+
}
|
|
98
|
+
// write UTF16
|
|
99
|
+
else {
|
|
100
|
+
this.writeInt32(-value.length - 1);
|
|
101
|
+
for (let i = 0; i < value.length; i++) {
|
|
102
|
+
this.writeUint16(value.charCodeAt(i));
|
|
103
|
+
}
|
|
104
|
+
this.writeUint16(0);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
writeBinarySizeFromPosition(lenIndicatorPos, start) {
|
|
108
|
+
const after = this.getBufferPosition();
|
|
109
|
+
const writtenBytes = after - start;
|
|
110
|
+
this.jumpTo(lenIndicatorPos);
|
|
111
|
+
this.writeInt32(writtenBytes);
|
|
112
|
+
this.jumpTo(after);
|
|
113
|
+
}
|
|
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
|
+
extendBufferIfNeeded(countNeededBytes, factor = 1.5) {
|
|
120
|
+
if (this.currentByte + countNeededBytes > this.bufferView.byteLength) {
|
|
121
|
+
this.bufferArray = ByteWriter.AppendBuffer(this.bufferArray, new ArrayBuffer(factor * this.bufferArray.byteLength));
|
|
122
|
+
this.bufferView = new DataView(this.bufferArray);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
truncateBuffer() {
|
|
126
|
+
this.bufferArray = this.bufferArray.slice(0, this.currentByte);
|
|
127
|
+
}
|
|
128
|
+
endWriting() {
|
|
129
|
+
this.truncateBuffer();
|
|
130
|
+
return this.bufferArray;
|
|
131
|
+
}
|
|
132
|
+
static ToInt32(num) {
|
|
133
|
+
return new Uint8Array([
|
|
134
|
+
(num & 0xff000000) >> 24,
|
|
135
|
+
(num & 0x00ff0000) >> 16,
|
|
136
|
+
(num & 0x0000ff00) >> 8,
|
|
137
|
+
(num & 0x000000ff)
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
static AppendBuffer(buffer1, buffer2) {
|
|
141
|
+
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
|
142
|
+
tmp.set(new Uint8Array(buffer1), 0);
|
|
143
|
+
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
|
144
|
+
return tmp.buffer;
|
|
145
|
+
}
|
|
146
|
+
;
|
|
147
|
+
}
|
|
148
|
+
ByteWriter.IsASCIICompatible = (value) => /^[\x00-\x7F]*$/.test(value);
|
|
149
|
+
exports.ByteWriter = ByteWriter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class ParserError extends Error {
|
|
2
|
+
constructor(name: string, message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class UnsupportedVersionError extends ParserError {
|
|
5
|
+
constructor(message?: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class CorruptSaveError extends ParserError {
|
|
8
|
+
constructor(message?: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class CompressionLibraryError extends ParserError {
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompressionLibraryError = exports.CorruptSaveError = exports.UnsupportedVersionError = exports.ParserError = void 0;
|
|
4
|
+
class ParserError extends Error {
|
|
5
|
+
constructor(name, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.ParserError = ParserError;
|
|
11
|
+
class UnsupportedVersionError extends ParserError {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super('UnsupportedVersionError', message ?? 'This save version is not supported.');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.UnsupportedVersionError = UnsupportedVersionError;
|
|
17
|
+
class CorruptSaveError extends ParserError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super('CorruptSaveError', message ?? 'This save data is most likely corrupt.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.CorruptSaveError = CorruptSaveError;
|
|
23
|
+
class CompressionLibraryError extends ParserError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super('CompressionLibraryError', message ?? 'Failed to compress/decompress save data.');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.CompressionLibraryError = CompressionLibraryError;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { SatisfactorySave } from "./satisfactory/satisfactory-save";
|
|
3
|
+
import { SaveProjectionConfig } from "./save-reader";
|
|
4
|
+
export declare class SaveParser {
|
|
5
|
+
/**
|
|
6
|
+
* serializes a save file into binary and reports back on individual callbacks.
|
|
7
|
+
* @param save the save file to serialize into binary.
|
|
8
|
+
* @param onBinaryBeforeCompressing gets called on the binary save body before it is compressed.
|
|
9
|
+
* @param onHeader gets called on the binary save header, which is always uncompressed.
|
|
10
|
+
* @param onChunk gets called when a chunk of the compressed save body was generated. Often, files' save bodies consist of multiple chunks.
|
|
11
|
+
* @param onFinish gets called when all chunks have been generated. A summary of the chunk sizes and count is reported.
|
|
12
|
+
*/
|
|
13
|
+
static WriteSave(save: SatisfactorySave, onBinaryBeforeCompressing: (buffer: ArrayBuffer) => void, onHeader: (header: Uint8Array) => void, onChunk: (chunk: Uint8Array) => void, onFinish: (summary: {
|
|
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
|
+
*/
|
|
34
|
+
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
|
+
static JSONStringifyModified: (obj: any, indent?: number) => string;
|
|
44
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SaveParser = void 0;
|
|
4
|
+
const level_class_1 = require("./satisfactory/level.class");
|
|
5
|
+
const satisfactory_save_1 = require("./satisfactory/satisfactory-save");
|
|
6
|
+
const save_reader_1 = require("./save-reader");
|
|
7
|
+
const save_writer_1 = require("./save-writer");
|
|
8
|
+
class SaveParser {
|
|
9
|
+
/**
|
|
10
|
+
* serializes a save file into binary and reports back on individual callbacks.
|
|
11
|
+
* @param save the save file to serialize into binary.
|
|
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) {
|
|
18
|
+
const writer = new save_writer_1.SaveWriter();
|
|
19
|
+
save_writer_1.SaveWriter.WriteHeader(writer, save.header);
|
|
20
|
+
const posAfterHeader = writer.getBufferPosition();
|
|
21
|
+
save_writer_1.SaveWriter.WriteLevels(writer, save, save.header.buildVersion);
|
|
22
|
+
writer.endWriting();
|
|
23
|
+
writer.generateChunks(save.compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk, onFinish);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* parses a save object from a binary buffer and reports back on decompressing the save body in a callback.
|
|
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
|
|
35
|
+
const header = reader.readHeader();
|
|
36
|
+
const save = new satisfactory_save_1.SatisfactorySave(header);
|
|
37
|
+
// inflate chunks
|
|
38
|
+
const inflateResult = reader.inflateChunks();
|
|
39
|
+
// call callback on decompressed save body
|
|
40
|
+
onSaveDecompressed(reader.getBuffer());
|
|
41
|
+
// parse levels
|
|
42
|
+
const levelParseResult = reader.readLevels();
|
|
43
|
+
save.levels = reader.levels;
|
|
44
|
+
save.compressionInfo = reader.compressionInfo;
|
|
45
|
+
save.trailingCollectedObjects = reader.trailingCollectedObjects;
|
|
46
|
+
return save;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Does Projection on a Save Object in order to reduce it's complexity.
|
|
50
|
+
* @param save the save object in question.
|
|
51
|
+
* @param config the projection config to apply.
|
|
52
|
+
* @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.
|
|
53
|
+
*/
|
|
54
|
+
static ProjectSave(save, config) {
|
|
55
|
+
return {
|
|
56
|
+
header: save.header,
|
|
57
|
+
trailingCollectedObjects: save.trailingCollectedObjects,
|
|
58
|
+
levels: save.levels.map(level => {
|
|
59
|
+
const lvl = new level_class_1.Level(level.name);
|
|
60
|
+
lvl.collectables = level.collectables;
|
|
61
|
+
lvl.objects = level.objects.filter(obj => (0, save_reader_1.projectionFilterApplies)(config, obj));
|
|
62
|
+
return lvl;
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
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) => {
|
|
76
|
+
if (typeof value === 'bigint') {
|
|
77
|
+
return value.toString();
|
|
78
|
+
}
|
|
79
|
+
else if (value === 0 && 1 / value < 0) { // -0
|
|
80
|
+
return '-0';
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}, indent);
|
|
84
|
+
exports.SaveParser = SaveParser;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SaveComponent } from "../objects/SaveComponent";
|
|
2
|
+
import { SaveEntity } from "../objects/SaveEntity";
|
|
3
|
+
import { ChunkCompressionInfo } from "../satisfactory-save";
|
|
4
|
+
import { col4 } from "../structs/util.types";
|
|
5
|
+
export interface BlueprintConfig {
|
|
6
|
+
description: string;
|
|
7
|
+
color: col4;
|
|
8
|
+
iconID: number;
|
|
9
|
+
}
|
|
10
|
+
export type BlueprintHeader = {
|
|
11
|
+
itemCosts: [string, number][];
|
|
12
|
+
recipeReferences: string[];
|
|
13
|
+
};
|
|
14
|
+
export interface Blueprint {
|
|
15
|
+
name: string;
|
|
16
|
+
compressionInfo: ChunkCompressionInfo;
|
|
17
|
+
header: BlueprintHeader;
|
|
18
|
+
config: BlueprintConfig;
|
|
19
|
+
objects: (SaveEntity | SaveComponent)[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ByteReader } from "../byte/byte-reader.class";
|
|
2
|
+
import { ByteWriter } from "../byte/byte-writer.class";
|
|
3
|
+
import { SaveReader } from "../save-reader";
|
|
4
|
+
import { ObjectReference } from "./objects/ObjectReference";
|
|
5
|
+
import { SaveComponent } from "./objects/SaveComponent";
|
|
6
|
+
import { SaveEntity } from "./objects/SaveEntity";
|
|
7
|
+
import { SaveObject } from "./objects/SaveObject";
|
|
8
|
+
export declare class Level {
|
|
9
|
+
name: string;
|
|
10
|
+
objects: (SaveEntity | SaveComponent)[];
|
|
11
|
+
collectables: ObjectReference[];
|
|
12
|
+
constructor(name: string);
|
|
13
|
+
static SerializeObjectHeaders(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[]): void;
|
|
14
|
+
static WriteLevel(writer: ByteWriter, level: Level, buildVersion: number): void;
|
|
15
|
+
static SerializeObjectContents(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[], buildVersion: number): void;
|
|
16
|
+
static ReadObjectHeaders(reader: ByteReader, objectsList: SaveObject[], onProgressCallback: (progress: number, msg?: string) => void): void;
|
|
17
|
+
static ReadObjectContents(reader: ByteReader, objectsList: SaveObject[], buildVersion: number, onProgressCallback: (progress: number, msg?: string) => void): void;
|
|
18
|
+
static ReadLevel(reader: SaveReader, levelName: string, buildVersion: number): Level;
|
|
19
|
+
static SerializeCollectablesList(writer: ByteWriter, collectables: ObjectReference[]): void;
|
|
20
|
+
static ReadCollectablesList(reader: SaveReader): ObjectReference[];
|
|
21
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Level = void 0;
|
|
4
|
+
const ObjectReference_1 = require("./objects/ObjectReference");
|
|
5
|
+
const SaveComponent_1 = require("./objects/SaveComponent");
|
|
6
|
+
const SaveEntity_1 = require("./objects/SaveEntity");
|
|
7
|
+
class Level {
|
|
8
|
+
constructor(name) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.objects = [];
|
|
11
|
+
this.collectables = [];
|
|
12
|
+
}
|
|
13
|
+
static SerializeObjectHeaders(writer, objects) {
|
|
14
|
+
writer.writeInt32(objects.length);
|
|
15
|
+
for (const obj of objects) {
|
|
16
|
+
switch (obj.type) {
|
|
17
|
+
case 'SaveEntity':
|
|
18
|
+
writer.writeInt32(SaveEntity_1.SaveEntity.TypeID);
|
|
19
|
+
SaveEntity_1.SaveEntity.SerializeHeader(writer, obj);
|
|
20
|
+
break;
|
|
21
|
+
case 'SaveComponent':
|
|
22
|
+
writer.writeInt32(SaveComponent_1.SaveComponent.TypeID);
|
|
23
|
+
SaveComponent_1.SaveComponent.SerializeHeader(writer, obj);
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
console.log('Unknown object type', obj);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
static WriteLevel(writer, level, buildVersion) {
|
|
32
|
+
const lenIndicatorHeaderAndCollectableSize = writer.getBufferPosition();
|
|
33
|
+
writer.writeInt32(0);
|
|
34
|
+
Level.SerializeObjectHeaders(writer, level.objects);
|
|
35
|
+
Level.SerializeCollectablesList(writer, level.collectables);
|
|
36
|
+
// replace binary size from earlier for - object headers + collectables
|
|
37
|
+
writer.writeBinarySizeFromPosition(lenIndicatorHeaderAndCollectableSize, lenIndicatorHeaderAndCollectableSize + 4);
|
|
38
|
+
// write entities
|
|
39
|
+
Level.SerializeObjectContents(writer, level.objects, buildVersion);
|
|
40
|
+
Level.SerializeCollectablesList(writer, level.collectables);
|
|
41
|
+
}
|
|
42
|
+
static SerializeObjectContents(writer, objects, buildVersion) {
|
|
43
|
+
const lenIndicatorEntities = writer.getBufferPosition();
|
|
44
|
+
writer.writeInt32(0);
|
|
45
|
+
writer.writeInt32(objects.length);
|
|
46
|
+
for (const obj of objects) {
|
|
47
|
+
const lenReplacementPosition = writer.getBufferPosition();
|
|
48
|
+
writer.writeInt32(0);
|
|
49
|
+
if ((0, SaveEntity_1.isSaveEntity)(obj)) {
|
|
50
|
+
SaveEntity_1.SaveEntity.SerializeData(writer, obj, buildVersion);
|
|
51
|
+
}
|
|
52
|
+
else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
|
|
53
|
+
SaveComponent_1.SaveComponent.SerializeData(writer, obj, buildVersion);
|
|
54
|
+
}
|
|
55
|
+
writer.writeBinarySizeFromPosition(lenReplacementPosition, lenReplacementPosition + 4);
|
|
56
|
+
}
|
|
57
|
+
writer.writeBinarySizeFromPosition(lenIndicatorEntities, lenIndicatorEntities + 4);
|
|
58
|
+
}
|
|
59
|
+
static ReadObjectHeaders(reader, objectsList, onProgressCallback) {
|
|
60
|
+
let countObjectHeaders = reader.readInt32();
|
|
61
|
+
for (let i = 0; i < countObjectHeaders; i++) {
|
|
62
|
+
if (i % 1000 === 0) {
|
|
63
|
+
onProgressCallback(reader.getBufferProgress());
|
|
64
|
+
}
|
|
65
|
+
let objectType = reader.readInt32();
|
|
66
|
+
switch (objectType) {
|
|
67
|
+
case SaveEntity_1.SaveEntity.TypeID:
|
|
68
|
+
let object = new SaveEntity_1.SaveEntity('', '', '', '');
|
|
69
|
+
SaveEntity_1.SaveEntity.ParseHeader(reader, object);
|
|
70
|
+
objectsList.push(object);
|
|
71
|
+
break;
|
|
72
|
+
case SaveComponent_1.SaveComponent.TypeID:
|
|
73
|
+
let component = new SaveComponent_1.SaveComponent('', '', '', '');
|
|
74
|
+
SaveComponent_1.SaveComponent.ParseHeader(reader, component);
|
|
75
|
+
objectsList.push(component);
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
console.log('Unknown object type', objectType);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
static ReadObjectContents(reader, objectsList, buildVersion, onProgressCallback) {
|
|
84
|
+
const binarySize = reader.readInt32(); // entities binary length
|
|
85
|
+
const posBefore = reader.getBufferPosition();
|
|
86
|
+
const countEntities = reader.readInt32();
|
|
87
|
+
if (countEntities !== objectsList.length) {
|
|
88
|
+
throw new Error(`possibly corrupt. entity content count ${countEntities} does not object count of ${objectsList.length}`);
|
|
89
|
+
}
|
|
90
|
+
// definitely after i=25467
|
|
91
|
+
for (let i = 0; i < countEntities; i++) {
|
|
92
|
+
if (i % 1000 === 0) {
|
|
93
|
+
onProgressCallback(reader.getBufferProgress());
|
|
94
|
+
}
|
|
95
|
+
const len = reader.readInt32();
|
|
96
|
+
const before = reader.getBufferPosition();
|
|
97
|
+
const obj = objectsList[i];
|
|
98
|
+
if ((0, SaveEntity_1.isSaveEntity)(obj)) {
|
|
99
|
+
SaveEntity_1.SaveEntity.ParseData(obj, len, reader, buildVersion);
|
|
100
|
+
}
|
|
101
|
+
else if ((0, SaveComponent_1.isSaveComponent)(obj)) {
|
|
102
|
+
SaveComponent_1.SaveComponent.ParseData(obj, len, reader, buildVersion);
|
|
103
|
+
}
|
|
104
|
+
const after = reader.getBufferPosition();
|
|
105
|
+
if (after - before !== len) {
|
|
106
|
+
console.warn('entity may be corrupt', this.name, i);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const posAfter = reader.getBufferPosition();
|
|
110
|
+
if (posAfter - posBefore !== binarySize) {
|
|
111
|
+
//WARNING
|
|
112
|
+
console.warn('save seems corrupt.', this.name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
static ReadLevel(reader, levelName, buildVersion) {
|
|
116
|
+
const level = new Level(levelName);
|
|
117
|
+
reader.readInt32(); // object headers + collectables binary length
|
|
118
|
+
// object headers
|
|
119
|
+
Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
|
|
120
|
+
// collectables
|
|
121
|
+
reader.onProgressCallback(reader.getBufferProgress());
|
|
122
|
+
level.collectables = Level.ReadCollectablesList(reader);
|
|
123
|
+
// objects contents
|
|
124
|
+
Level.ReadObjectContents(reader, level.objects, buildVersion, reader.onProgressCallback);
|
|
125
|
+
// collectables 2nd time.
|
|
126
|
+
reader.onProgressCallback(reader.getBufferProgress());
|
|
127
|
+
Level.ReadCollectablesList(reader);
|
|
128
|
+
return level;
|
|
129
|
+
}
|
|
130
|
+
// collected stuff
|
|
131
|
+
static SerializeCollectablesList(writer, collectables) {
|
|
132
|
+
writer.writeInt32(collectables.length);
|
|
133
|
+
for (const collectable of collectables) {
|
|
134
|
+
ObjectReference_1.ObjectReference.Serialize(writer, collectable);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// collected stuff
|
|
138
|
+
static ReadCollectablesList(reader) {
|
|
139
|
+
let countCollected = reader.readInt32();
|
|
140
|
+
const collected = [];
|
|
141
|
+
for (let i = 0; i < countCollected; i++) {
|
|
142
|
+
const collectable = ObjectReference_1.ObjectReference.Parse(reader);
|
|
143
|
+
collected.push(collectable);
|
|
144
|
+
}
|
|
145
|
+
return collected;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.Level = Level;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ByteReader } from "../../byte/byte-reader.class";
|
|
2
|
+
import { SaveWriter } from "../../save-writer";
|
|
3
|
+
import { AbstractBaseProperty } from "./Property";
|
|
4
|
+
export declare class DataFields {
|
|
5
|
+
properties: any[];
|
|
6
|
+
trailingData: number[];
|
|
7
|
+
shouldBeNulled: boolean;
|
|
8
|
+
constructor();
|
|
9
|
+
static Parse(length: number, reader: ByteReader, buildVersion: number): DataFields;
|
|
10
|
+
static ParseProperty(reader: ByteReader, buildVersion: number, propertyName: string): AbstractBaseProperty | null;
|
|
11
|
+
static Serialize(writer: SaveWriter, fields: DataFields, buildVersion: number): void;
|
|
12
|
+
static SerializeProperty(writer: SaveWriter, property: AbstractBaseProperty, propertyName: string, buildVersion: number): void;
|
|
13
|
+
}
|