@etothepii/satisfactory-file-parser 0.1.25 → 0.1.26
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.js +25 -60
- package/build/parser/byte/alignment.enum.js +2 -5
- package/build/parser/byte/binary-operable.interface.js +1 -2
- package/build/parser/byte/binary-readable.interface.js +1 -2
- package/build/parser/byte/byte-reader.class.js +10 -14
- package/build/parser/byte/byte-writer.class.js +10 -13
- package/build/parser/error/parser.error.js +6 -15
- package/build/parser/file.types.js +2 -5
- package/build/parser/parser.js +23 -26
- package/build/parser/satisfactory/blueprint/blueprint-reader.js +28 -36
- package/build/parser/satisfactory/blueprint/blueprint-writer.js +20 -25
- package/build/parser/satisfactory/blueprint/blueprint.types.js +1 -2
- package/build/parser/satisfactory/objects/DataFields.js +79 -83
- package/build/parser/satisfactory/objects/GUIDInfo.js +5 -8
- package/build/parser/satisfactory/objects/ObjectReference.js +2 -5
- package/build/parser/satisfactory/objects/Property.js +91 -120
- package/build/parser/satisfactory/objects/SaveComponent.js +7 -11
- package/build/parser/satisfactory/objects/SaveEntity.js +13 -17
- package/build/parser/satisfactory/objects/SaveObject.js +4 -8
- package/build/parser/satisfactory/objects/ue/GUID.js +2 -5
- package/build/parser/satisfactory/objects/ue/MD5Hash.js +2 -5
- package/build/parser/satisfactory/save/asynchronous-level.class.js +12 -16
- package/build/parser/satisfactory/save/level.class.js +24 -28
- package/build/parser/satisfactory/save/satisfactory-save.js +1 -5
- package/build/parser/satisfactory/save/save-reader.js +28 -34
- package/build/parser/satisfactory/save/save-writer.js +34 -40
- package/build/parser/satisfactory/save/save.types.js +1 -2
- package/build/parser/satisfactory/structs/util.types.js +37 -63
- package/build/parser/stream/byte-stream-reader.class.js +17 -21
- package/build/parser/stream/json-stream-state-writer.js +2 -6
- package/build/parser/stream/json-stream-writable.js +1 -5
- package/build/parser/stream/json-stream-writer.js +2 -6
- package/build/parser/stream/reworked/readable-stream-parser.d.ts +1 -1
- package/build/parser/stream/reworked/readable-stream-parser.js +21 -24
- package/build/parser/stream/reworked/stream-parser.js +17 -20
- package/build/parser/stream/save-stream-json-stringifier.js +3 -7
- package/build/parser/stream/save-stream-reader.class.js +18 -24
- package/build/parser/stream/save-stream-writer.class.js +1 -5
- package/build/parser/stream/stream-level.class.js +11 -15
- package/build/parser/stream/stream-parser.js +11 -15
- package/package.json +4 -2
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.SaveComponent = exports.isSaveComponent = void 0;
|
|
4
|
-
const SaveObject_1 = require("./SaveObject");
|
|
5
|
-
const isSaveComponent = (obj) => {
|
|
1
|
+
import { SaveObject } from "./SaveObject";
|
|
2
|
+
export const isSaveComponent = (obj) => {
|
|
6
3
|
return obj.type === 'SaveComponent';
|
|
7
4
|
};
|
|
8
|
-
|
|
9
|
-
class SaveComponent extends SaveObject_1.SaveObject {
|
|
5
|
+
class SaveComponent extends SaveObject {
|
|
10
6
|
constructor(typePath, rootObject, instanceName, parentEntityName = '') {
|
|
11
7
|
super(typePath, rootObject, instanceName);
|
|
12
8
|
this.typePath = typePath;
|
|
@@ -16,16 +12,16 @@ class SaveComponent extends SaveObject_1.SaveObject {
|
|
|
16
12
|
this.type = 'SaveComponent';
|
|
17
13
|
}
|
|
18
14
|
static ParseHeader(reader, obj) {
|
|
19
|
-
|
|
15
|
+
SaveObject.ParseHeader(reader, obj);
|
|
20
16
|
obj.parentEntityName = reader.readString();
|
|
21
17
|
}
|
|
22
18
|
static SerializeHeader(writer, component) {
|
|
23
|
-
|
|
19
|
+
SaveObject.SerializeHeader(writer, component);
|
|
24
20
|
writer.writeString(component.parentEntityName);
|
|
25
21
|
}
|
|
26
22
|
static ParseData(component, length, reader, buildVersion, typePath) {
|
|
27
|
-
|
|
23
|
+
SaveObject.ParseData(component, length, reader, buildVersion, typePath);
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
26
|
SaveComponent.TypeID = 0;
|
|
31
|
-
|
|
27
|
+
export { SaveComponent };
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const ObjectReference_1 = require("./ObjectReference");
|
|
6
|
-
const SaveObject_1 = require("./SaveObject");
|
|
7
|
-
const isSaveEntity = (obj) => {
|
|
1
|
+
import { ParseTransform, SerializeTransform } from "../structs/util.types";
|
|
2
|
+
import { ObjectReference } from "./ObjectReference";
|
|
3
|
+
import { SaveObject } from "./SaveObject";
|
|
4
|
+
export const isSaveEntity = (obj) => {
|
|
8
5
|
return obj.type === 'SaveEntity';
|
|
9
6
|
};
|
|
10
|
-
|
|
11
|
-
class SaveEntity extends SaveObject_1.SaveObject {
|
|
7
|
+
class SaveEntity extends SaveObject {
|
|
12
8
|
constructor(typePath, rootObject, instanceName, parentEntityName = '', needsTransform = false) {
|
|
13
9
|
super(typePath, rootObject, instanceName);
|
|
14
10
|
this.typePath = typePath;
|
|
@@ -28,9 +24,9 @@ class SaveEntity extends SaveObject_1.SaveObject {
|
|
|
28
24
|
this.components = [];
|
|
29
25
|
}
|
|
30
26
|
static ParseHeader(reader, obj) {
|
|
31
|
-
|
|
27
|
+
SaveObject.ParseHeader(reader, obj);
|
|
32
28
|
obj.needTransform = reader.readInt32() == 1;
|
|
33
|
-
obj.transform =
|
|
29
|
+
obj.transform = ParseTransform(reader);
|
|
34
30
|
obj.wasPlacedInLevel = reader.readInt32() == 1;
|
|
35
31
|
}
|
|
36
32
|
static ParseData(entity, length, reader, buildVersion, typePath) {
|
|
@@ -39,16 +35,16 @@ class SaveEntity extends SaveObject_1.SaveObject {
|
|
|
39
35
|
entity.parentObjectName = reader.readString();
|
|
40
36
|
var componentCount = reader.readInt32();
|
|
41
37
|
for (let i = 0; i < componentCount; i++) {
|
|
42
|
-
var componentRef =
|
|
38
|
+
var componentRef = ObjectReference.read(reader);
|
|
43
39
|
entity.components.push(componentRef);
|
|
44
40
|
}
|
|
45
41
|
const remainingSize = length - (reader.getBufferPosition() - afterSizeIndicator);
|
|
46
|
-
return
|
|
42
|
+
return SaveObject.ParseData(entity, remainingSize, reader, buildVersion, typePath);
|
|
47
43
|
}
|
|
48
44
|
static SerializeHeader(writer, entity) {
|
|
49
|
-
|
|
45
|
+
SaveObject.SerializeHeader(writer, entity);
|
|
50
46
|
writer.writeInt32(entity.needTransform ? 1 : 0);
|
|
51
|
-
|
|
47
|
+
SerializeTransform(writer, entity.transform);
|
|
52
48
|
writer.writeInt32(entity.wasPlacedInLevel ? 1 : 0);
|
|
53
49
|
}
|
|
54
50
|
static SerializeData(writer, entity, buildVersion) {
|
|
@@ -59,8 +55,8 @@ class SaveEntity extends SaveObject_1.SaveObject {
|
|
|
59
55
|
writer.writeString(com.levelName);
|
|
60
56
|
writer.writeString(com.pathName);
|
|
61
57
|
}
|
|
62
|
-
|
|
58
|
+
SaveObject.SerializeData(writer, entity, buildVersion);
|
|
63
59
|
}
|
|
64
60
|
}
|
|
65
61
|
SaveEntity.TypeID = 1;
|
|
66
|
-
|
|
62
|
+
export { SaveEntity };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.SaveObject = void 0;
|
|
4
|
-
const DataFields_1 = require("./DataFields");
|
|
5
|
-
class SaveObject {
|
|
1
|
+
import { DataFields } from "./DataFields";
|
|
2
|
+
export class SaveObject {
|
|
6
3
|
constructor(typePath, rootObject, instanceName) {
|
|
7
4
|
this.typePath = typePath;
|
|
8
5
|
this.rootObject = rootObject;
|
|
@@ -24,10 +21,9 @@ class SaveObject {
|
|
|
24
21
|
writer.writeString(obj.instanceName);
|
|
25
22
|
}
|
|
26
23
|
static ParseData(obj, length, reader, buildVersion, typePath) {
|
|
27
|
-
|
|
24
|
+
DataFields.ParseProperties(obj, length, reader, buildVersion, typePath);
|
|
28
25
|
}
|
|
29
26
|
static SerializeData(writer, obj, buildVersion) {
|
|
30
|
-
|
|
27
|
+
DataFields.Serialize(obj, writer, buildVersion, obj.typePath);
|
|
31
28
|
}
|
|
32
29
|
}
|
|
33
|
-
exports.SaveObject = SaveObject;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GUID = void 0;
|
|
4
|
-
var GUID;
|
|
1
|
+
export var GUID;
|
|
5
2
|
(function (GUID) {
|
|
6
3
|
GUID.read = (reader) => {
|
|
7
4
|
return [
|
|
@@ -17,5 +14,5 @@ var GUID;
|
|
|
17
14
|
writer.writeUint32(guid[2]);
|
|
18
15
|
writer.writeUint32(guid[3]);
|
|
19
16
|
};
|
|
20
|
-
})(GUID
|
|
17
|
+
})(GUID || (GUID = {}));
|
|
21
18
|
;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MD5Hash = void 0;
|
|
4
|
-
var MD5Hash;
|
|
1
|
+
export var MD5Hash;
|
|
5
2
|
(function (MD5Hash) {
|
|
6
3
|
MD5Hash.read = (reader) => {
|
|
7
4
|
const md5Hash = { isValid: false };
|
|
@@ -17,5 +14,5 @@ var MD5Hash;
|
|
|
17
14
|
writer.writeBytesArray(md5Hash.hash);
|
|
18
15
|
}
|
|
19
16
|
};
|
|
20
|
-
})(MD5Hash
|
|
17
|
+
})(MD5Hash || (MD5Hash = {}));
|
|
21
18
|
;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const SaveEntity_1 = require("../objects/SaveEntity");
|
|
6
|
-
const level_class_1 = require("./level.class");
|
|
7
|
-
class AsynchronousLevel {
|
|
1
|
+
import { isSaveComponent, SaveComponent } from "../objects/SaveComponent";
|
|
2
|
+
import { isSaveEntity, SaveEntity } from "../objects/SaveEntity";
|
|
3
|
+
import { Level } from "./level.class";
|
|
4
|
+
export class AsynchronousLevel {
|
|
8
5
|
static async StreamReadObjectContents(reader, objectsList, buildVersion, writer, onProgressCallback) {
|
|
9
6
|
const binarySize = reader.readInt32();
|
|
10
7
|
const posBefore = reader.getBufferPosition();
|
|
@@ -27,11 +24,11 @@ class AsynchronousLevel {
|
|
|
27
24
|
await reader.allocate(Math.max(before - binarySize, 300 * 1000 * 1000));
|
|
28
25
|
}
|
|
29
26
|
const obj = objectsList[i];
|
|
30
|
-
if (
|
|
31
|
-
|
|
27
|
+
if (isSaveEntity(obj)) {
|
|
28
|
+
SaveEntity.ParseData(obj, len, reader, buildVersion, obj.typePath);
|
|
32
29
|
}
|
|
33
|
-
else if (
|
|
34
|
-
|
|
30
|
+
else if (isSaveComponent(obj)) {
|
|
31
|
+
SaveComponent.ParseData(obj, len, reader, buildVersion, obj.typePath);
|
|
35
32
|
}
|
|
36
33
|
dynamicObjectBatch.push(obj);
|
|
37
34
|
const after = reader.getBufferPosition();
|
|
@@ -46,21 +43,20 @@ class AsynchronousLevel {
|
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
static async StreamReadLevel(reader, levelName, buildVersion, writer) {
|
|
49
|
-
const level = new
|
|
46
|
+
const level = new Level(levelName);
|
|
50
47
|
const binaryLength = reader.readInt32();
|
|
51
48
|
await reader.allocate(binaryLength);
|
|
52
49
|
await writer.openLevel(levelName);
|
|
53
|
-
|
|
50
|
+
Level.ReadObjectHeaders(reader, level.objects, reader.onProgressCallback);
|
|
54
51
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
55
|
-
level.collectables =
|
|
52
|
+
level.collectables = Level.ReadCollectablesList(reader, undefined);
|
|
56
53
|
console.log(`before object contents buffer pos ${reader.getBufferPosition()}`);
|
|
57
54
|
await AsynchronousLevel.StreamReadObjectContents(reader, level.objects, buildVersion, writer, reader.onProgressCallback);
|
|
58
55
|
await writer.switchInLevelToCollectables();
|
|
59
56
|
await writer.writeCollectables(...level.collectables);
|
|
60
57
|
reader.onProgressCallback(reader.getBufferProgress());
|
|
61
|
-
|
|
58
|
+
Level.ReadCollectablesList(reader, undefined);
|
|
62
59
|
await writer.endLevel();
|
|
63
60
|
return level;
|
|
64
61
|
}
|
|
65
62
|
}
|
|
66
|
-
exports.AsynchronousLevel = AsynchronousLevel;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const SaveComponent_1 = require("../objects/SaveComponent");
|
|
6
|
-
const SaveEntity_1 = require("../objects/SaveEntity");
|
|
7
|
-
class Level {
|
|
1
|
+
import { ObjectReference } from "../objects/ObjectReference";
|
|
2
|
+
import { SaveComponent, isSaveComponent } from "../objects/SaveComponent";
|
|
3
|
+
import { SaveEntity, isSaveEntity } from "../objects/SaveEntity";
|
|
4
|
+
export class Level {
|
|
8
5
|
constructor(name) {
|
|
9
6
|
this.name = name;
|
|
10
7
|
this.objects = [];
|
|
@@ -16,12 +13,12 @@ class Level {
|
|
|
16
13
|
for (const obj of objects) {
|
|
17
14
|
switch (obj.type) {
|
|
18
15
|
case 'SaveEntity':
|
|
19
|
-
writer.writeInt32(
|
|
20
|
-
|
|
16
|
+
writer.writeInt32(SaveEntity.TypeID);
|
|
17
|
+
SaveEntity.SerializeHeader(writer, obj);
|
|
21
18
|
break;
|
|
22
19
|
case 'SaveComponent':
|
|
23
|
-
writer.writeInt32(
|
|
24
|
-
|
|
20
|
+
writer.writeInt32(SaveComponent.TypeID);
|
|
21
|
+
SaveComponent.SerializeHeader(writer, obj);
|
|
25
22
|
break;
|
|
26
23
|
default:
|
|
27
24
|
console.log('Unknown object type', obj);
|
|
@@ -49,11 +46,11 @@ class Level {
|
|
|
49
46
|
writer.writeInt32(obj.unknownType2);
|
|
50
47
|
const lenReplacementPosition = writer.getBufferPosition();
|
|
51
48
|
writer.writeInt32(0);
|
|
52
|
-
if (
|
|
53
|
-
|
|
49
|
+
if (isSaveEntity(obj)) {
|
|
50
|
+
SaveEntity.SerializeData(writer, obj, buildVersion);
|
|
54
51
|
}
|
|
55
|
-
else if (
|
|
56
|
-
|
|
52
|
+
else if (isSaveComponent(obj)) {
|
|
53
|
+
SaveComponent.SerializeData(writer, obj, buildVersion);
|
|
57
54
|
}
|
|
58
55
|
writer.writeBinarySizeFromPosition(lenReplacementPosition, lenReplacementPosition + 4);
|
|
59
56
|
}
|
|
@@ -67,14 +64,14 @@ class Level {
|
|
|
67
64
|
}
|
|
68
65
|
let objectType = reader.readInt32();
|
|
69
66
|
switch (objectType) {
|
|
70
|
-
case
|
|
71
|
-
let object = new
|
|
72
|
-
|
|
67
|
+
case SaveEntity.TypeID:
|
|
68
|
+
let object = new SaveEntity('', '', '', '');
|
|
69
|
+
SaveEntity.ParseHeader(reader, object);
|
|
73
70
|
objectsList.push(object);
|
|
74
71
|
break;
|
|
75
|
-
case
|
|
76
|
-
let component = new
|
|
77
|
-
|
|
72
|
+
case SaveComponent.TypeID:
|
|
73
|
+
let component = new SaveComponent('', '', '', '');
|
|
74
|
+
SaveComponent.ParseHeader(reader, component);
|
|
78
75
|
objectsList.push(component);
|
|
79
76
|
break;
|
|
80
77
|
default:
|
|
@@ -97,11 +94,11 @@ class Level {
|
|
|
97
94
|
obj.unknownType2 = reader.readInt32();
|
|
98
95
|
const binarySize = reader.readInt32();
|
|
99
96
|
const before = reader.getBufferPosition();
|
|
100
|
-
if (
|
|
101
|
-
|
|
97
|
+
if (isSaveEntity(obj)) {
|
|
98
|
+
SaveEntity.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
|
|
102
99
|
}
|
|
103
|
-
else if (
|
|
104
|
-
|
|
100
|
+
else if (isSaveComponent(obj)) {
|
|
101
|
+
SaveComponent.ParseData(obj, binarySize, reader, buildVersion, obj.typePath);
|
|
105
102
|
}
|
|
106
103
|
const after = reader.getBufferPosition();
|
|
107
104
|
if (after - before !== binarySize) {
|
|
@@ -141,7 +138,7 @@ class Level {
|
|
|
141
138
|
static SerializeCollectablesList(writer, collectables) {
|
|
142
139
|
writer.writeInt32(collectables.length);
|
|
143
140
|
for (const collectable of collectables) {
|
|
144
|
-
|
|
141
|
+
ObjectReference.write(writer, collectable);
|
|
145
142
|
}
|
|
146
143
|
}
|
|
147
144
|
static ReadCollectablesList(reader, printSmthWhenItsCollectables) {
|
|
@@ -149,7 +146,7 @@ class Level {
|
|
|
149
146
|
let countSmthing = reader.readInt32();
|
|
150
147
|
if (countSmthing > 0) {
|
|
151
148
|
for (let i = 0; i < countSmthing; i++) {
|
|
152
|
-
const collectable =
|
|
149
|
+
const collectable = ObjectReference.read(reader);
|
|
153
150
|
collected.push(collectable);
|
|
154
151
|
}
|
|
155
152
|
if (process.env.NODE_ENV === 'debug' && countSmthing > 0) {
|
|
@@ -159,4 +156,3 @@ class Level {
|
|
|
159
156
|
return collected;
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
|
-
exports.Level = Level;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SatisfactorySave = void 0;
|
|
4
|
-
class SatisfactorySave {
|
|
1
|
+
export class SatisfactorySave {
|
|
5
2
|
constructor(name, header) {
|
|
6
3
|
this.gridHash = [0, 0, 0, 0];
|
|
7
4
|
this.grids = {};
|
|
@@ -11,4 +8,3 @@ class SatisfactorySave {
|
|
|
11
8
|
this.header = header;
|
|
12
9
|
}
|
|
13
10
|
}
|
|
14
|
-
exports.SatisfactorySave = SatisfactorySave;
|
|
@@ -1,27 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const parser_error_1 = require("../../error/parser.error");
|
|
11
|
-
const MD5Hash_1 = require("../objects/ue/MD5Hash");
|
|
12
|
-
const asynchronous_level_class_1 = require("./asynchronous-level.class");
|
|
13
|
-
const level_class_1 = require("./level.class");
|
|
14
|
-
exports.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
15
|
-
class SaveReader extends byte_reader_class_1.ByteReader {
|
|
1
|
+
import Pako from "pako";
|
|
2
|
+
import { Alignment } from "../../byte/alignment.enum";
|
|
3
|
+
import { ByteReader } from "../../byte/byte-reader.class";
|
|
4
|
+
import { CompressionLibraryError, CorruptSaveError, ParserError, UnsupportedVersionError } from "../../error/parser.error";
|
|
5
|
+
import { MD5Hash } from '../objects/ue/MD5Hash';
|
|
6
|
+
import { AsynchronousLevel } from "./asynchronous-level.class";
|
|
7
|
+
import { Level } from "./level.class";
|
|
8
|
+
export const DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 49;
|
|
9
|
+
class SaveReader extends ByteReader {
|
|
16
10
|
constructor(fileBuffer, onProgressCallback = () => { }) {
|
|
17
|
-
super(fileBuffer,
|
|
11
|
+
super(fileBuffer, Alignment.LITTLE_ENDIAN);
|
|
18
12
|
this.onProgressCallback = onProgressCallback;
|
|
19
13
|
this.levels = [];
|
|
20
14
|
this.trailingCollectedObjects = [];
|
|
21
15
|
this.compressionInfo = {
|
|
22
16
|
packageFileTag: 0,
|
|
23
17
|
maxUncompressedChunkContentSize: 0,
|
|
24
|
-
chunkHeaderSize:
|
|
18
|
+
chunkHeaderSize: DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
|
|
25
19
|
};
|
|
26
20
|
this.readSaveBodyHash = () => {
|
|
27
21
|
const unk = this.readInt32();
|
|
@@ -98,7 +92,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
98
92
|
this.header.partitionEnabledFlag = this.readInt32() === 1;
|
|
99
93
|
}
|
|
100
94
|
if (this.header.saveHeaderType >= 12) {
|
|
101
|
-
this.header.consistencyHashBytes =
|
|
95
|
+
this.header.consistencyHashBytes = MD5Hash.read(this);
|
|
102
96
|
}
|
|
103
97
|
if (this.header.saveHeaderType >= 13) {
|
|
104
98
|
this.header.creativeModeEnabled = this.readInt32() == 1;
|
|
@@ -106,7 +100,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
106
100
|
if (this.header.saveVersion >= 21) {
|
|
107
101
|
}
|
|
108
102
|
else {
|
|
109
|
-
throw new
|
|
103
|
+
throw new UnsupportedVersionError("The save version is too old to support encoding currently. Save in newer game version.");
|
|
110
104
|
}
|
|
111
105
|
return this.header;
|
|
112
106
|
}
|
|
@@ -122,13 +116,13 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
122
116
|
this.currentByte = this.compressionInfo.chunkHeaderSize;
|
|
123
117
|
this.handledByte += this.compressionInfo.chunkHeaderSize;
|
|
124
118
|
if (this.compressionInfo.packageFileTag <= 0) {
|
|
125
|
-
this.compressionInfo.packageFileTag = chunkHeader.getUint32(0, this.alignment ===
|
|
119
|
+
this.compressionInfo.packageFileTag = chunkHeader.getUint32(0, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
126
120
|
}
|
|
127
121
|
if (this.compressionInfo.maxUncompressedChunkContentSize <= 0) {
|
|
128
|
-
this.compressionInfo.maxUncompressedChunkContentSize = chunkHeader.getInt32(8, this.alignment ===
|
|
122
|
+
this.compressionInfo.maxUncompressedChunkContentSize = chunkHeader.getInt32(8, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
129
123
|
}
|
|
130
|
-
const chunkCompressedLength = chunkHeader.getInt32(33, this.alignment ===
|
|
131
|
-
const chunkUncompressedLength = chunkHeader.getInt32(25, this.alignment ===
|
|
124
|
+
const chunkCompressedLength = chunkHeader.getInt32(33, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
125
|
+
const chunkUncompressedLength = chunkHeader.getInt32(25, this.alignment === Alignment.LITTLE_ENDIAN);
|
|
132
126
|
totalUncompressedBodySize += chunkUncompressedLength;
|
|
133
127
|
const currentChunkSize = chunkCompressedLength;
|
|
134
128
|
let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
|
|
@@ -138,11 +132,11 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
138
132
|
this.currentByte = 0;
|
|
139
133
|
try {
|
|
140
134
|
let currentInflatedChunk = null;
|
|
141
|
-
currentInflatedChunk =
|
|
135
|
+
currentInflatedChunk = Pako.inflate(currentChunk);
|
|
142
136
|
currentChunks.push(currentInflatedChunk);
|
|
143
137
|
}
|
|
144
138
|
catch (err) {
|
|
145
|
-
throw new
|
|
139
|
+
throw new CompressionLibraryError("Failed to inflate compressed save data. " + err);
|
|
146
140
|
}
|
|
147
141
|
}
|
|
148
142
|
let newChunkLength = currentChunks.map(cc => cc.length).reduce((prev, cur) => prev + cur);
|
|
@@ -157,7 +151,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
157
151
|
this.bufferView = new DataView(bigWholeChunk.buffer);
|
|
158
152
|
const dataLength = this.readInt32();
|
|
159
153
|
if (totalUncompressedBodySize !== dataLength + 8) {
|
|
160
|
-
throw new
|
|
154
|
+
throw new CorruptSaveError(`Possibly corrupt. Indicated size of total save body (${dataLength + 8}) does not match the uncompressed real size of ${totalUncompressedBodySize}.`);
|
|
161
155
|
}
|
|
162
156
|
return {
|
|
163
157
|
concatenatedChunkLength: newChunkLength,
|
|
@@ -166,13 +160,13 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
166
160
|
}
|
|
167
161
|
readLevels() {
|
|
168
162
|
if (!this.header) {
|
|
169
|
-
throw new
|
|
163
|
+
throw new ParserError('ParserError', 'Header must be set before objects can be read.');
|
|
170
164
|
}
|
|
171
165
|
if (this.header.saveVersion < 29) {
|
|
172
|
-
throw new
|
|
166
|
+
throw new UnsupportedVersionError('Game Version < U6 is not supported.');
|
|
173
167
|
}
|
|
174
168
|
if (this.header.saveHeaderType < 13) {
|
|
175
|
-
throw new
|
|
169
|
+
throw new UnsupportedVersionError('Game Version < U8 is not supported in this package version. Consider downgrading to the latest package version supporting it, which is 0.0.34');
|
|
176
170
|
}
|
|
177
171
|
const levels = [];
|
|
178
172
|
const levelCount = this.readInt32();
|
|
@@ -182,10 +176,10 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
182
176
|
if (i % 500 === 0) {
|
|
183
177
|
this.onProgressCallback(this.getBufferProgress(), `reading level [${(i + 1)}/${(levelCount + 1)}] ${levelSingleName}`);
|
|
184
178
|
}
|
|
185
|
-
levels.push(
|
|
179
|
+
levels.push(Level.ReadLevel(this, levelSingleName, this.header.buildVersion));
|
|
186
180
|
}
|
|
187
|
-
levels.push(
|
|
188
|
-
const trailingStuffToIgnore =
|
|
181
|
+
levels.push(Level.ReadLevel(this, this.header.mapName, this.header.buildVersion));
|
|
182
|
+
const trailingStuffToIgnore = Level.ReadCollectablesList(this, 'collectables 6!');
|
|
189
183
|
this.onProgressCallback(this.getBufferProgress(), 'finished parsing.');
|
|
190
184
|
return levels;
|
|
191
185
|
}
|
|
@@ -196,7 +190,7 @@ class SaveReader extends byte_reader_class_1.ByteReader {
|
|
|
196
190
|
for (let j = 0; j <= numSubLevels; j++) {
|
|
197
191
|
let levelName = (j === numSubLevels) ? '' + this.header.mapName : this.readString();
|
|
198
192
|
this.onProgressCallback(this.getBufferProgress(), `reading level [${(j + 1)}/${(numSubLevels + 1)}] ${levelName}`);
|
|
199
|
-
this.levels[j] = await
|
|
193
|
+
this.levels[j] = await AsynchronousLevel.StreamReadLevel(this, levelName, this.header.buildVersion, writer);
|
|
200
194
|
}
|
|
201
195
|
await writer.endLevels();
|
|
202
196
|
return this.levels;
|
|
@@ -214,4 +208,4 @@ SaveReader.getRoughSaveVersion = (saveVersion, headerTypeVersion) => {
|
|
|
214
208
|
return '<U6';
|
|
215
209
|
}
|
|
216
210
|
};
|
|
217
|
-
|
|
211
|
+
export { SaveReader };
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const parser_error_1 = require("../../error/parser.error");
|
|
11
|
-
const file_types_1 = require("../../file.types");
|
|
12
|
-
const MD5Hash_1 = require("../objects/ue/MD5Hash");
|
|
13
|
-
const level_class_1 = require("./level.class");
|
|
14
|
-
const save_reader_1 = require("./save-reader");
|
|
15
|
-
class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
1
|
+
import Pako from "pako";
|
|
2
|
+
import { Alignment } from "../../byte/alignment.enum";
|
|
3
|
+
import { ByteWriter } from "../../byte/byte-writer.class";
|
|
4
|
+
import { CompressionLibraryError, ParserError, UnsupportedVersionError } from "../../error/parser.error";
|
|
5
|
+
import { CompressionAlgorithmCode } from "../../file.types";
|
|
6
|
+
import { MD5Hash } from '../objects/ue/MD5Hash';
|
|
7
|
+
import { Level } from "./level.class";
|
|
8
|
+
import { SaveReader } from "./save-reader";
|
|
9
|
+
class SaveWriter extends ByteWriter {
|
|
16
10
|
constructor() {
|
|
17
|
-
super(
|
|
11
|
+
super(Alignment.LITTLE_ENDIAN);
|
|
18
12
|
}
|
|
19
13
|
static WriteHeader(writer, header) {
|
|
20
14
|
writer.writeInt32(header.saveHeaderType);
|
|
@@ -24,7 +18,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
24
18
|
writer.writeString(header.mapOptions);
|
|
25
19
|
writer.writeString(header.sessionName);
|
|
26
20
|
writer.writeInt32(header.playDurationSeconds);
|
|
27
|
-
writer.writeInt64(BigInt(header.saveDateTime) * 10000n +
|
|
21
|
+
writer.writeInt64(BigInt(header.saveDateTime) * 10000n + SaveReader.EPOCH_TICKS);
|
|
28
22
|
writer.writeByte(header.sessionVisibility);
|
|
29
23
|
if (header.saveHeaderType >= 7) {
|
|
30
24
|
writer.writeInt32(header.fEditorObjectVersion);
|
|
@@ -45,7 +39,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
45
39
|
writer.writeInt32(header.partitionEnabledFlag ? 1 : 0);
|
|
46
40
|
}
|
|
47
41
|
if (header.saveHeaderType >= 12) {
|
|
48
|
-
|
|
42
|
+
MD5Hash.write(writer, header.consistencyHashBytes);
|
|
49
43
|
}
|
|
50
44
|
if (header.saveHeaderType >= 13) {
|
|
51
45
|
writer.writeInt32(header.creativeModeEnabled ? 1 : 0);
|
|
@@ -53,7 +47,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
53
47
|
if (header.saveVersion >= 21) {
|
|
54
48
|
}
|
|
55
49
|
else {
|
|
56
|
-
throw new
|
|
50
|
+
throw new UnsupportedVersionError("The save version is too old to be supported currently.");
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
static WriteLevels(writer, save, buildVersion) {
|
|
@@ -62,17 +56,17 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
62
56
|
if (level.name !== save.header.mapName) {
|
|
63
57
|
writer.writeString(level.name);
|
|
64
58
|
}
|
|
65
|
-
|
|
59
|
+
Level.WriteLevel(writer, level, buildVersion);
|
|
66
60
|
}
|
|
67
|
-
|
|
61
|
+
Level.SerializeCollectablesList(writer, save.trailingCollectedObjects ?? []);
|
|
68
62
|
}
|
|
69
|
-
static GenerateCompressedChunksFromData(bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, alignment =
|
|
63
|
+
static GenerateCompressedChunksFromData(bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, alignment = Alignment.LITTLE_ENDIAN) {
|
|
70
64
|
const errors = [];
|
|
71
65
|
const totalUncompressedSize = bufferArray.byteLength;
|
|
72
66
|
const saveBody = new Uint8Array(bufferArray.byteLength + 8);
|
|
73
67
|
saveBody.set(new Uint8Array(bufferArray), 4);
|
|
74
68
|
const miniView = new DataView(saveBody.buffer);
|
|
75
|
-
miniView.setInt32(0, totalUncompressedSize, alignment ===
|
|
69
|
+
miniView.setInt32(0, totalUncompressedSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
76
70
|
onBinaryBeforeCompressing(saveBody.buffer);
|
|
77
71
|
let handledByte = 0;
|
|
78
72
|
const chunkSummary = [];
|
|
@@ -81,27 +75,27 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
81
75
|
const uncompressedChunk = saveBody.buffer.slice(handledByte, handledByte + uncompressedContentSize);
|
|
82
76
|
let compressedChunk = new Uint8Array(0);
|
|
83
77
|
try {
|
|
84
|
-
compressedChunk =
|
|
78
|
+
compressedChunk = Pako.deflate(uncompressedChunk);
|
|
85
79
|
}
|
|
86
80
|
catch (err) {
|
|
87
|
-
throw new
|
|
81
|
+
throw new CompressionLibraryError("Could not compress save data. " + err);
|
|
88
82
|
}
|
|
89
83
|
const chunk = new Uint8Array(compressionInfo.chunkHeaderSize + compressedChunk.byteLength);
|
|
90
84
|
chunk.set(compressedChunk, compressionInfo.chunkHeaderSize);
|
|
91
85
|
const view = new DataView(chunk.buffer);
|
|
92
|
-
view.setInt32(0, compressionInfo.packageFileTag, alignment ===
|
|
93
|
-
view.setInt32(4, 0x22222222, alignment ===
|
|
94
|
-
view.setInt32(8, compressionInfo.maxUncompressedChunkContentSize, alignment ===
|
|
95
|
-
view.setInt32(12, 0, alignment ===
|
|
96
|
-
view.setUint8(16,
|
|
97
|
-
view.setInt32(17, compressedChunk.byteLength, alignment ===
|
|
98
|
-
view.setInt32(21, 0, alignment ===
|
|
99
|
-
view.setInt32(25, uncompressedContentSize, alignment ===
|
|
100
|
-
view.setInt32(29, 0, alignment ===
|
|
101
|
-
view.setInt32(33, compressedChunk.byteLength, alignment ===
|
|
102
|
-
view.setInt32(37, 0, alignment ===
|
|
103
|
-
view.setInt32(41, uncompressedContentSize, alignment ===
|
|
104
|
-
view.setInt32(45, 0, alignment ===
|
|
86
|
+
view.setInt32(0, compressionInfo.packageFileTag, alignment === Alignment.LITTLE_ENDIAN);
|
|
87
|
+
view.setInt32(4, 0x22222222, alignment === Alignment.LITTLE_ENDIAN);
|
|
88
|
+
view.setInt32(8, compressionInfo.maxUncompressedChunkContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
89
|
+
view.setInt32(12, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
90
|
+
view.setUint8(16, CompressionAlgorithmCode.ZLIB);
|
|
91
|
+
view.setInt32(17, compressedChunk.byteLength, alignment === Alignment.LITTLE_ENDIAN);
|
|
92
|
+
view.setInt32(21, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
93
|
+
view.setInt32(25, uncompressedContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
94
|
+
view.setInt32(29, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
95
|
+
view.setInt32(33, compressedChunk.byteLength, alignment === Alignment.LITTLE_ENDIAN);
|
|
96
|
+
view.setInt32(37, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
97
|
+
view.setInt32(41, uncompressedContentSize, alignment === Alignment.LITTLE_ENDIAN);
|
|
98
|
+
view.setInt32(45, 0, alignment === Alignment.LITTLE_ENDIAN);
|
|
105
99
|
onChunk(chunk);
|
|
106
100
|
chunkSummary.push({
|
|
107
101
|
uncompressedSize: uncompressedContentSize + compressionInfo.chunkHeaderSize,
|
|
@@ -113,7 +107,7 @@ class SaveWriter extends byte_writer_class_1.ByteWriter {
|
|
|
113
107
|
}
|
|
114
108
|
generateChunks(compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk) {
|
|
115
109
|
if (posAfterHeader <= 0) {
|
|
116
|
-
throw new
|
|
110
|
+
throw new ParserError('ParserError', 'Seems like this buffer has no header. Please write the header first before you can generate chunks.');
|
|
117
111
|
}
|
|
118
112
|
const header = new Uint8Array(this.bufferArray.slice(0, posAfterHeader));
|
|
119
113
|
onHeader(header);
|
|
@@ -144,4 +138,4 @@ SaveWriter.WriteGrids = (writer, grids) => {
|
|
|
144
138
|
}
|
|
145
139
|
writer.writeInt32(0);
|
|
146
140
|
};
|
|
147
|
-
|
|
141
|
+
export { SaveWriter };
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|