@etothepii/satisfactory-file-parser 0.0.32 → 0.0.34

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.
Files changed (35) hide show
  1. package/build/index.js +41 -51
  2. package/build/parser/byte/alignment.enum.js +8 -18
  3. package/build/parser/byte/binary-operable.interface.js +2 -12
  4. package/build/parser/byte/binary-readable.interface.js +2 -12
  5. package/build/parser/byte/byte-reader.class.js +122 -132
  6. package/build/parser/byte/byte-writer.class.js +133 -143
  7. package/build/parser/error/parser.error.js +40 -50
  8. package/build/parser/file.types.js +2 -12
  9. package/build/parser/parser.d.ts +0 -9
  10. package/build/parser/parser.js +84 -192
  11. package/build/parser/satisfactory/blueprint/blueprint-reader.js +109 -119
  12. package/build/parser/satisfactory/blueprint/blueprint-writer.js +56 -66
  13. package/build/parser/satisfactory/blueprint/blueprint.types.js +2 -12
  14. package/build/parser/satisfactory/objects/DataFields.js +297 -307
  15. package/build/parser/satisfactory/objects/ObjectReference.js +14 -24
  16. package/build/parser/satisfactory/objects/Property.js +967 -977
  17. package/build/parser/satisfactory/objects/SaveComponent.js +28 -38
  18. package/build/parser/satisfactory/objects/SaveEntity.js +65 -75
  19. package/build/parser/satisfactory/objects/SaveObject.js +26 -36
  20. package/build/parser/satisfactory/save/asynchronous-level.class.js +60 -70
  21. package/build/parser/satisfactory/save/level.class.js +122 -133
  22. package/build/parser/satisfactory/save/satisfactory-save.js +10 -20
  23. package/build/parser/satisfactory/save/save-reader.js +158 -168
  24. package/build/parser/satisfactory/save/save-writer.js +97 -107
  25. package/build/parser/satisfactory/save/save.types.js +2 -12
  26. package/build/parser/satisfactory/structs/util.types.js +89 -99
  27. package/build/parser/stream/byte-stream-reader.class.js +217 -227
  28. package/build/parser/stream/json-stream-state-writer.js +15 -25
  29. package/build/parser/stream/json-stream-writable.js +72 -82
  30. package/build/parser/stream/json-stream-writer.js +122 -132
  31. package/build/parser/stream/save-stream-json-stringifier.js +29 -39
  32. package/build/parser/stream/save-stream-reader.class.js +106 -116
  33. package/build/parser/stream/save-stream-writer.class.js +90 -100
  34. package/build/parser/stream/stream-level.class.js +93 -103
  35. package/package.json +1 -1
@@ -1,131 +1,121 @@
1
+ "use strict";
1
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
2
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
3
4
  };
4
- (function (factory) {
5
- if (typeof module === "object" && typeof module.exports === "object") {
6
- var v = factory(require, exports);
7
- if (v !== undefined) module.exports = v;
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SaveStreamReader = void 0;
7
+ const pako_1 = __importDefault(require("pako"));
8
+ const __1 = require("../..");
9
+ const alignment_enum_1 = require("../byte/alignment.enum");
10
+ const byte_stream_reader_class_1 = require("./byte-stream-reader.class");
11
+ const stream_level_class_1 = require("./stream-level.class");
12
+ class SaveStreamReader extends byte_stream_reader_class_1.ByteStreamReader {
13
+ constructor(reader, maxBufferThreshold, onCloseCallback = async () => { }) {
14
+ super(reader, onCloseCallback, 30000, maxBufferThreshold, alignment_enum_1.Alignment.LITTLE_ENDIAN);
15
+ this.compressionInfo = {
16
+ packageFileTag: 0,
17
+ maxChunkContentSize: 0,
18
+ chunkHeaderSize: SaveStreamReader.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
19
+ };
8
20
  }
9
- else if (typeof define === "function" && define.amd) {
10
- define(["require", "exports", "pako", "../..", "../byte/alignment.enum", "./byte-stream-reader.class", "./stream-level.class"], factory);
11
- }
12
- })(function (require, exports) {
13
- "use strict";
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.SaveStreamReader = void 0;
16
- const pako_1 = __importDefault(require("pako"));
17
- const __1 = require("../..");
18
- const alignment_enum_1 = require("../byte/alignment.enum");
19
- const byte_stream_reader_class_1 = require("./byte-stream-reader.class");
20
- const stream_level_class_1 = require("./stream-level.class");
21
- class SaveStreamReader extends byte_stream_reader_class_1.ByteStreamReader {
22
- constructor(reader, maxBufferThreshold, onCloseCallback = async () => { }) {
23
- super(reader, onCloseCallback, 30000, maxBufferThreshold, alignment_enum_1.Alignment.LITTLE_ENDIAN);
24
- this.compressionInfo = {
25
- packageFileTag: 0,
26
- maxChunkContentSize: 0,
27
- chunkHeaderSize: SaveStreamReader.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE
28
- };
21
+ async readHeader() {
22
+ const header = {
23
+ saveHeaderType: 0,
24
+ saveVersion: 0,
25
+ buildVersion: 0,
26
+ mapName: "DEFAULT",
27
+ mapOptions: "",
28
+ sessionName: "",
29
+ playDurationSeconds: 0,
30
+ saveDateTime: "0",
31
+ sessionVisibility: 0
32
+ };
33
+ await this.allocate(400);
34
+ console.log(`safely reading header stuff now. ${this.currentByte} ${this.operatingDataView.byteLength} ${this.operatingStreamBuffer.byteLength}`);
35
+ header.saveHeaderType = this.readInt32();
36
+ header.saveVersion = this.readInt32();
37
+ header.buildVersion = this.readInt32();
38
+ header.mapName = this.readString();
39
+ header.mapOptions = this.readString();
40
+ header.sessionName = this.readString();
41
+ header.playDurationSeconds = this.readInt32();
42
+ const rawSaveDateTimeInTicks = this.readLong();
43
+ const unixMilliseconds = (rawSaveDateTimeInTicks - SaveStreamReader.EPOCH_TICKS) / 10000n;
44
+ header.saveDateTime = unixMilliseconds.toString();
45
+ header.sessionVisibility = this.readByte();
46
+ if (header.saveHeaderType >= 7) {
47
+ header.fEditorObjectVersion = this.readInt32();
29
48
  }
30
- async readHeader() {
31
- const header = {
32
- saveHeaderType: 0,
33
- saveVersion: 0,
34
- buildVersion: 0,
35
- mapName: "DEFAULT",
36
- mapOptions: "",
37
- sessionName: "",
38
- playDurationSeconds: 0,
39
- saveDateTime: "0",
40
- sessionVisibility: 0
41
- };
42
- await this.allocate(400);
43
- console.log(`safely reading header stuff now. ${this.currentByte} ${this.operatingDataView.byteLength} ${this.operatingStreamBuffer.byteLength}`);
44
- header.saveHeaderType = this.readInt32();
45
- header.saveVersion = this.readInt32();
46
- header.buildVersion = this.readInt32();
47
- header.mapName = this.readString();
48
- header.mapOptions = this.readString();
49
- header.sessionName = this.readString();
50
- header.playDurationSeconds = this.readInt32();
51
- const rawSaveDateTimeInTicks = this.readLong();
52
- const unixMilliseconds = (rawSaveDateTimeInTicks - SaveStreamReader.EPOCH_TICKS) / 10000n;
53
- header.saveDateTime = unixMilliseconds.toString();
54
- header.sessionVisibility = this.readByte();
55
- if (header.saveHeaderType >= 7) {
56
- header.fEditorObjectVersion = this.readInt32();
57
- }
58
- if (header.saveHeaderType >= 8) {
59
- header.rawModMetadataString = this.readString();
60
- try {
61
- header.modMetadata = JSON.parse(header.rawModMetadataString);
62
- }
63
- catch (error) {
64
- }
65
- header.isModdedSave = this.readInt32();
66
- }
67
- if (header.saveHeaderType >= 10) {
68
- header.saveIdentifier = this.readString();
69
- }
70
- if (header.saveVersion >= 21) {
49
+ if (header.saveHeaderType >= 8) {
50
+ header.rawModMetadataString = this.readString();
51
+ try {
52
+ header.modMetadata = JSON.parse(header.rawModMetadataString);
71
53
  }
72
- else {
73
- throw new __1.UnsupportedVersionError("The save version is too old to support encoding currently. Save in newer game version.");
54
+ catch (error) {
74
55
  }
75
- return header;
56
+ header.isModdedSave = this.readInt32();
76
57
  }
77
- async readBodyChunk() {
78
- await this.allocate(this.compressionInfo.chunkHeaderSize);
79
- this.operatingStreamBuffer = this.operatingStreamBuffer.slice(this.currentByte);
80
- this.operatingDataView = new DataView(this.operatingStreamBuffer.buffer);
81
- this.currentByte = 0;
82
- if (this.compressionInfo.packageFileTag <= 0) {
83
- this.compressionInfo.packageFileTag = this.operatingDataView.getInt32(0, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
84
- }
85
- if (this.compressionInfo.maxChunkContentSize <= 0) {
86
- this.compressionInfo.maxChunkContentSize = this.operatingDataView.getInt32(8, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
87
- }
88
- const chunkCompressedLength = this.operatingDataView.getInt32(32, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
89
- const chunkUncompressedLength = this.operatingDataView.getInt32(40, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
90
- this.currentByte = this.compressionInfo.chunkHeaderSize;
91
- await this.allocate(chunkCompressedLength);
92
- let currentChunk = this.operatingStreamBuffer.slice(this.currentByte, this.currentByte + chunkCompressedLength);
93
- this.currentByte += chunkCompressedLength;
94
- this.totalNumberOfBytesRead += chunkUncompressedLength;
95
- try {
96
- const uncompressedChunk = pako_1.default.inflate(currentChunk);
97
- if (uncompressedChunk.byteLength !== chunkUncompressedLength) {
98
- throw new __1.CorruptSaveError('indicated save body chunk size does not match the inflated result. Save is possibly corrupt.');
99
- }
100
- if (this.debug) {
101
- console.log(`${new Date().toString()}: decompressed a chunk from ${currentChunk.byteLength} to ${uncompressedChunk.byteLength} bytes.`);
102
- }
103
- return uncompressedChunk;
58
+ if (header.saveHeaderType >= 10) {
59
+ header.saveIdentifier = this.readString();
60
+ }
61
+ if (header.saveVersion >= 21) {
62
+ }
63
+ else {
64
+ throw new __1.UnsupportedVersionError("The save version is too old to support encoding currently. Save in newer game version.");
65
+ }
66
+ return header;
67
+ }
68
+ async readBodyChunk() {
69
+ await this.allocate(this.compressionInfo.chunkHeaderSize);
70
+ this.operatingStreamBuffer = this.operatingStreamBuffer.slice(this.currentByte);
71
+ this.operatingDataView = new DataView(this.operatingStreamBuffer.buffer);
72
+ this.currentByte = 0;
73
+ if (this.compressionInfo.packageFileTag <= 0) {
74
+ this.compressionInfo.packageFileTag = this.operatingDataView.getInt32(0, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
75
+ }
76
+ if (this.compressionInfo.maxChunkContentSize <= 0) {
77
+ this.compressionInfo.maxChunkContentSize = this.operatingDataView.getInt32(8, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
78
+ }
79
+ const chunkCompressedLength = this.operatingDataView.getInt32(32, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
80
+ const chunkUncompressedLength = this.operatingDataView.getInt32(40, this.alignment === alignment_enum_1.Alignment.LITTLE_ENDIAN);
81
+ this.currentByte = this.compressionInfo.chunkHeaderSize;
82
+ await this.allocate(chunkCompressedLength);
83
+ let currentChunk = this.operatingStreamBuffer.slice(this.currentByte, this.currentByte + chunkCompressedLength);
84
+ this.currentByte += chunkCompressedLength;
85
+ this.totalNumberOfBytesRead += chunkUncompressedLength;
86
+ try {
87
+ const uncompressedChunk = pako_1.default.inflate(currentChunk);
88
+ if (uncompressedChunk.byteLength !== chunkUncompressedLength) {
89
+ throw new __1.CorruptSaveError('indicated save body chunk size does not match the inflated result. Save is possibly corrupt.');
104
90
  }
105
- catch (err) {
106
- throw new __1.CompressionLibraryError("Failed to inflate compressed save data. " + err);
91
+ if (this.debug) {
92
+ console.log(`${new Date().toString()}: decompressed a chunk from ${currentChunk.byteLength} to ${uncompressedChunk.byteLength} bytes.`);
107
93
  }
94
+ return uncompressedChunk;
108
95
  }
109
- debugInfo() {
110
- return `operating buffer size is ${this.operatingStreamBuffer.byteLength}/${this.operatingDataView.byteLength}, at ${this.currentByte} means ${this.getBufferPosition()} pos, with ${this.operatingStreamBuffer.byteLength - this.currentByte} left until end of operating buffer.`;
96
+ catch (err) {
97
+ throw new __1.CompressionLibraryError("Failed to inflate compressed save data. " + err);
111
98
  }
112
- async streamLevelsToOutput(writer, header) {
113
- if (header.saveVersion < 29) {
114
- throw new __1.UnsupportedVersionError('Support for < U6 is not yet implemented.');
115
- }
116
- await this.allocate(100);
117
- const numSubLevels = this.readInt32();
118
- for (let j = 0; j <= numSubLevels; j++) {
119
- let levelName = (j === numSubLevels) ? '' + header.mapName : this.readString();
120
- if (this.debug) {
121
- console.log(`${new Date().toString()}: reading level [${(j)}/${numSubLevels}] ${levelName}`);
122
- }
123
- await stream_level_class_1.StreamLevel.ReadLevelAsync(this, writer, levelName, header.buildVersion);
99
+ }
100
+ debugInfo() {
101
+ return `operating buffer size is ${this.operatingStreamBuffer.byteLength}/${this.operatingDataView.byteLength}, at ${this.currentByte} means ${this.getBufferPosition()} pos, with ${this.operatingStreamBuffer.byteLength - this.currentByte} left until end of operating buffer.`;
102
+ }
103
+ async streamLevelsToOutput(writer, header) {
104
+ if (header.saveVersion < 29) {
105
+ throw new __1.UnsupportedVersionError('Support for < U6 is not yet implemented.');
106
+ }
107
+ await this.allocate(100);
108
+ const numSubLevels = this.readInt32();
109
+ for (let j = 0; j <= numSubLevels; j++) {
110
+ let levelName = (j === numSubLevels) ? '' + header.mapName : this.readString();
111
+ if (this.debug) {
112
+ console.log(`${new Date().toString()}: reading level [${(j)}/${numSubLevels}] ${levelName}`);
124
113
  }
125
- return;
114
+ await stream_level_class_1.StreamLevel.ReadLevelAsync(this, writer, levelName, header.buildVersion);
126
115
  }
116
+ return;
127
117
  }
128
- SaveStreamReader.EPOCH_TICKS = 621355968000000000n;
129
- SaveStreamReader.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 48;
130
- exports.SaveStreamReader = SaveStreamReader;
131
- });
118
+ }
119
+ SaveStreamReader.EPOCH_TICKS = 621355968000000000n;
120
+ SaveStreamReader.DEFAULT_SATISFACTORY_CHUNK_HEADER_SIZE = 48;
121
+ exports.SaveStreamReader = SaveStreamReader;
@@ -1,115 +1,105 @@
1
- (function (factory) {
2
- if (typeof module === "object" && typeof module.exports === "object") {
3
- var v = factory(require, exports);
4
- if (v !== undefined) module.exports = v;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SaveStreamWriter = void 0;
4
+ class SaveStreamWriter {
5
+ constructor(writer) {
6
+ this.writer = writer;
7
+ this.mode = 'BEFORE_START';
8
+ this.formatTracker = {
9
+ levels: []
10
+ };
5
11
  }
6
- else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports"], factory);
8
- }
9
- })(function (require, exports) {
10
- "use strict";
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.SaveStreamWriter = void 0;
13
- class SaveStreamWriter {
14
- constructor(writer) {
15
- this.writer = writer;
16
- this.mode = 'BEFORE_START';
17
- this.formatTracker = {
18
- levels: []
19
- };
12
+ async beginSave() {
13
+ if (this.mode !== 'BEFORE_START') {
14
+ throw new Error(`Wrong order of commands. Already opened save. mode is ${this.mode}.`);
20
15
  }
21
- async beginSave() {
22
- if (this.mode !== 'BEFORE_START') {
23
- throw new Error(`Wrong order of commands. Already opened save. mode is ${this.mode}.`);
24
- }
25
- await this.writer.write('{');
26
- this.mode = 'OPENED_SAVE';
16
+ await this.writer.write('{');
17
+ this.mode = 'OPENED_SAVE';
18
+ }
19
+ async writeHeader(header) {
20
+ if (this.mode !== 'OPENED_SAVE') {
21
+ throw new Error(`Wrong order of commands. Header has to come after save open. mode is ${this.mode}.`);
27
22
  }
28
- async writeHeader(header) {
29
- if (this.mode !== 'OPENED_SAVE') {
30
- throw new Error(`Wrong order of commands. Header has to come after save open. mode is ${this.mode}.`);
31
- }
32
- await this.writer.write(`"header": ${JSON.stringify(header)}`);
33
- this.mode = 'FINISHED_HEADER';
23
+ await this.writer.write(`"header": ${JSON.stringify(header)}`);
24
+ this.mode = 'FINISHED_HEADER';
25
+ }
26
+ async writeCompressionInfo(compressionInfo) {
27
+ if (this.mode !== 'FINISHED_HEADER') {
28
+ throw new Error(`Wrong order of commands. Compression info has to come after header. mode is ${this.mode}.`);
34
29
  }
35
- async writeCompressionInfo(compressionInfo) {
36
- if (this.mode !== 'FINISHED_HEADER') {
37
- throw new Error(`Wrong order of commands. Compression info has to come after header. mode is ${this.mode}.`);
38
- }
39
- await this.writer.write(`, "compressionInfo": ${JSON.stringify(compressionInfo)}`);
40
- this.mode = 'WROTE_COMPRESSION_INFO';
30
+ await this.writer.write(`, "compressionInfo": ${JSON.stringify(compressionInfo)}`);
31
+ this.mode = 'WROTE_COMPRESSION_INFO';
32
+ }
33
+ async openLevels() {
34
+ if (this.mode !== 'WROTE_COMPRESSION_INFO') {
35
+ throw new Error(`Wrong order of commands. Levels have to come after compression info. mode is ${this.mode}.`);
41
36
  }
42
- async openLevels() {
43
- if (this.mode !== 'WROTE_COMPRESSION_INFO') {
44
- throw new Error(`Wrong order of commands. Levels have to come after compression info. mode is ${this.mode}.`);
45
- }
46
- await this.writer.write(`, "levels": [`);
47
- this.mode = 'OPENED_LEVELS';
37
+ await this.writer.write(`, "levels": [`);
38
+ this.mode = 'OPENED_LEVELS';
39
+ }
40
+ async openLevel(levelName) {
41
+ if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
42
+ throw new Error(`Wrong order of commands. Single level can only come after opening levels array or after finishing single level. mode is ${this.mode}.`);
48
43
  }
49
- async openLevel(levelName) {
50
- if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
51
- throw new Error(`Wrong order of commands. Single level can only come after opening levels array or after finishing single level. mode is ${this.mode}.`);
52
- }
53
- this.formatTracker.levels.push({
54
- objectCount: 0, collectablesCount: 0
55
- });
56
- const prefix = this.formatTracker.levels.length > 1 ? ', ' : '';
57
- await this.writer.write(`${prefix}{"name": "${levelName}", "objects": [`);
58
- this.mode = 'OPENED_LEVEL';
44
+ this.formatTracker.levels.push({
45
+ objectCount: 0, collectablesCount: 0
46
+ });
47
+ const prefix = this.formatTracker.levels.length > 1 ? ', ' : '';
48
+ await this.writer.write(`${prefix}{"name": "${levelName}", "objects": [`);
49
+ this.mode = 'OPENED_LEVEL';
50
+ }
51
+ async writeObjects(...objects) {
52
+ if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
53
+ throw new Error(`Wrong order of commands. An object can not be written into the level if it was not opened or other objects were not written. mode is ${this.mode}.`);
59
54
  }
60
- async writeObjects(...objects) {
61
- if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
62
- throw new Error(`Wrong order of commands. An object can not be written into the level if it was not opened or other objects were not written. mode is ${this.mode}.`);
63
- }
64
- const stringified = objects.map(saveObj => JSON.stringify(saveObj));
65
- for (const obj of stringified) {
66
- await this.writer.write(`${this.formatTracker.levels.at(-1).objectCount >= 1 ? ', ' : ''}${obj}`);
67
- this.formatTracker.levels.at(-1).objectCount++;
68
- }
69
- this.mode = 'WROTE_OBJECT';
55
+ const stringified = objects.map(saveObj => JSON.stringify(saveObj));
56
+ for (const obj of stringified) {
57
+ await this.writer.write(`${this.formatTracker.levels.at(-1).objectCount >= 1 ? ', ' : ''}${obj}`);
58
+ this.formatTracker.levels.at(-1).objectCount++;
70
59
  }
71
- async switchInLevelToCollectables() {
72
- if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
73
- throw new Error(`Wrong order of commands. The level structure can not be switched to collectables if the level was not opened recently or has not written objects. mode is ${this.mode}.`);
74
- }
75
- await this.writer.write(`], "collectables": [`);
76
- this.mode = 'SWITCH_TO_COLLECTABLES';
60
+ this.mode = 'WROTE_OBJECT';
61
+ }
62
+ async switchInLevelToCollectables() {
63
+ if (this.mode !== 'OPENED_LEVEL' && this.mode !== 'WROTE_OBJECT') {
64
+ throw new Error(`Wrong order of commands. The level structure can not be switched to collectables if the level was not opened recently or has not written objects. mode is ${this.mode}.`);
77
65
  }
78
- async writeCollectables(...collectables) {
79
- if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
80
- throw new Error(`Wrong order of commands. A collectable can not be written into the level if we did not switch to collectables or other collectables were not written. mode is ${this.mode}.`);
81
- }
82
- const stringified = collectables.map(coll => JSON.stringify(coll));
83
- for (const obj of stringified) {
84
- await this.writer.write(`${this.formatTracker.levels.at(-1).collectablesCount >= 1 ? ', ' : ''}${obj}`);
85
- this.formatTracker.levels.at(-1).collectablesCount++;
86
- }
87
- this.mode = 'WROTE_COLLECTABLE';
66
+ await this.writer.write(`], "collectables": [`);
67
+ this.mode = 'SWITCH_TO_COLLECTABLES';
68
+ }
69
+ async writeCollectables(...collectables) {
70
+ if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
71
+ throw new Error(`Wrong order of commands. A collectable can not be written into the level if we did not switch to collectables or other collectables were not written. mode is ${this.mode}.`);
88
72
  }
89
- async endLevel() {
90
- if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
91
- throw new Error(`Wrong order of commands. Single level can not be closed if a switch to collectibles or writing of collectible did not happen. mode is ${this.mode}.`);
92
- }
93
- await this.writer.write(`]}`);
94
- this.mode = 'FINISHED_LEVEL';
73
+ const stringified = collectables.map(coll => JSON.stringify(coll));
74
+ for (const obj of stringified) {
75
+ await this.writer.write(`${this.formatTracker.levels.at(-1).collectablesCount >= 1 ? ', ' : ''}${obj}`);
76
+ this.formatTracker.levels.at(-1).collectablesCount++;
95
77
  }
96
- async endLevels() {
97
- if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
98
- throw new Error(`Wrong order of commands. Levels can only be closed after opening levels array or after finishing single level. mode is ${this.mode}.`);
99
- }
100
- await this.writer.write(`]`);
101
- this.mode = 'FINISHED_LEVELS';
78
+ this.mode = 'WROTE_COLLECTABLE';
79
+ }
80
+ async endLevel() {
81
+ if (this.mode !== 'SWITCH_TO_COLLECTABLES' && this.mode !== 'WROTE_COLLECTABLE') {
82
+ throw new Error(`Wrong order of commands. Single level can not be closed if a switch to collectibles or writing of collectible did not happen. mode is ${this.mode}.`);
102
83
  }
103
- async endSave() {
104
- if (this.mode !== 'FINISHED_LEVELS') {
105
- throw new Error(`Wrong order of commands. Save has to end after levels. mode is ${this.mode}.`);
106
- }
107
- await this.writer.write('}');
108
- this.mode = 'FINISHED_SAVE';
84
+ await this.writer.write(`]}`);
85
+ this.mode = 'FINISHED_LEVEL';
86
+ }
87
+ async endLevels() {
88
+ if (this.mode !== 'OPENED_LEVELS' && this.mode !== 'FINISHED_LEVEL') {
89
+ throw new Error(`Wrong order of commands. Levels can only be closed after opening levels array or after finishing single level. mode is ${this.mode}.`);
109
90
  }
110
- async close() {
111
- return this.writer.close();
91
+ await this.writer.write(`]`);
92
+ this.mode = 'FINISHED_LEVELS';
93
+ }
94
+ async endSave() {
95
+ if (this.mode !== 'FINISHED_LEVELS') {
96
+ throw new Error(`Wrong order of commands. Save has to end after levels. mode is ${this.mode}.`);
112
97
  }
98
+ await this.writer.write('}');
99
+ this.mode = 'FINISHED_SAVE';
100
+ }
101
+ async close() {
102
+ return this.writer.close();
113
103
  }
114
- exports.SaveStreamWriter = SaveStreamWriter;
115
- });
104
+ }
105
+ exports.SaveStreamWriter = SaveStreamWriter;