@etothepii/satisfactory-file-parser 0.0.10 → 0.0.11

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 (46) hide show
  1. package/build/index.d.ts +11 -0
  2. package/build/index.js +12 -0
  3. package/build/parser/blueprint-reader.d.ts +18 -0
  4. package/build/parser/blueprint-reader.js +128 -0
  5. package/build/parser/blueprint-writer.d.ts +21 -0
  6. package/build/parser/blueprint-writer.js +64 -0
  7. package/build/parser/byte/alignment.enum.d.ts +4 -0
  8. package/build/parser/byte/alignment.enum.js +5 -0
  9. package/build/parser/byte/byte-reader.class.d.ts +38 -0
  10. package/build/parser/byte/byte-reader.class.js +128 -0
  11. package/build/parser/byte/byte-writer.class.d.ts +38 -0
  12. package/build/parser/byte/byte-writer.class.js +146 -0
  13. package/build/parser/error/parser.error.d.ts +12 -0
  14. package/build/parser/error/parser.error.js +21 -0
  15. package/build/parser/parser.d.ts +44 -0
  16. package/build/parser/parser.js +81 -0
  17. package/build/parser/satisfactory/blueprint/blueprint.d.ts +20 -0
  18. package/build/parser/satisfactory/blueprint/blueprint.js +1 -0
  19. package/build/parser/satisfactory/level.class.d.ts +21 -0
  20. package/build/parser/satisfactory/level.class.js +144 -0
  21. package/build/parser/satisfactory/objects/DataFields.d.ts +13 -0
  22. package/build/parser/satisfactory/objects/DataFields.js +245 -0
  23. package/build/parser/satisfactory/objects/ObjectReference.d.ts +9 -0
  24. package/build/parser/satisfactory/objects/ObjectReference.js +13 -0
  25. package/build/parser/satisfactory/objects/Property.d.ts +270 -0
  26. package/build/parser/satisfactory/objects/Property.js +1119 -0
  27. package/build/parser/satisfactory/objects/SaveComponent.d.ts +16 -0
  28. package/build/parser/satisfactory/objects/SaveComponent.js +27 -0
  29. package/build/parser/satisfactory/objects/SaveEntity.d.ts +25 -0
  30. package/build/parser/satisfactory/objects/SaveEntity.js +66 -0
  31. package/build/parser/satisfactory/objects/SaveObject.d.ts +14 -0
  32. package/build/parser/satisfactory/objects/SaveObject.js +25 -0
  33. package/build/parser/satisfactory/satisfactory-save.d.ts +57 -0
  34. package/build/parser/satisfactory/satisfactory-save.js +7 -0
  35. package/build/parser/satisfactory/static-data.d.ts +7 -0
  36. package/build/parser/satisfactory/static-data.js +1 -0
  37. package/build/parser/satisfactory/structs/util.types.d.ts +40 -0
  38. package/build/parser/satisfactory/structs/util.types.js +75 -0
  39. package/build/parser/save-reader.d.ts +43 -0
  40. package/build/parser/save-reader.js +243 -0
  41. package/build/parser/save-writer.d.ts +22 -0
  42. package/build/parser/save-writer.js +114 -0
  43. package/package.json +4 -3
  44. package/index.d.ts +0 -2356
  45. package/index.js +0 -2364
  46. package/rollup.config.mjs +0 -11
@@ -0,0 +1,11 @@
1
+ export { Level } from './parser/satisfactory/level.class';
2
+ export { DataFields } from './parser/satisfactory/objects/DataFields';
3
+ export { ObjectReference } from './parser/satisfactory/objects/ObjectReference';
4
+ export * from './parser/satisfactory/objects/Property';
5
+ export { SaveComponent } from './parser/satisfactory/objects/SaveComponent';
6
+ export type * from './parser/satisfactory/satisfactory-save';
7
+ export { StaticData } from './parser/satisfactory/static-data';
8
+ export type * from './parser/satisfactory/structs/util.types';
9
+ export { SaveEntity } from './parser/satisfactory/objects/SaveEntity';
10
+ export { CompressionLibraryError, CorruptSaveError, ParserError, UnsupportedVersionError } from './parser/error/parser.error';
11
+ export { SaveParser } from './parser/parser';
package/build/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // types & classes for convenience
2
+ export { Level } from './parser/satisfactory/level.class';
3
+ export { DataFields } from './parser/satisfactory/objects/DataFields';
4
+ export { ObjectReference } from './parser/satisfactory/objects/ObjectReference';
5
+ export * from './parser/satisfactory/objects/Property';
6
+ export { SaveComponent } from './parser/satisfactory/objects/SaveComponent';
7
+ // should better be removed
8
+ export { SaveEntity } from './parser/satisfactory/objects/SaveEntity';
9
+ // errors
10
+ export { CompressionLibraryError, CorruptSaveError, ParserError, UnsupportedVersionError } from './parser/error/parser.error';
11
+ // facade
12
+ export { SaveParser } from './parser/parser';
@@ -0,0 +1,18 @@
1
+ import { ByteReader } from "./byte/byte-reader.class";
2
+ import { BlueprintConfig, BlueprintHeader } from "./satisfactory/blueprint/blueprint";
3
+ import { SaveComponent } from "./satisfactory/objects/SaveComponent";
4
+ import { SaveEntity } from "./satisfactory/objects/SaveEntity";
5
+ import { ChunkCompressionInfo } from "./satisfactory/satisfactory-save";
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,128 @@
1
+ import Pako from "pako";
2
+ import { Alignment } from "./byte/alignment.enum";
3
+ import { ByteReader } from "./byte/byte-reader.class";
4
+ import { ParserError } from "./error/parser.error";
5
+ import { Level } from "./satisfactory/level.class";
6
+ import { ParseCol4RGBA, ParseVec3 } from "./satisfactory/structs/util.types";
7
+ export class BlueprintReader extends ByteReader {
8
+ constructor(bluePrintBuffer) {
9
+ super(bluePrintBuffer, Alignment.LITTLE_ENDIAN);
10
+ this.compressionInfo = {
11
+ packageFileTag: 0,
12
+ maxChunkContentSize: 0,
13
+ chunkHeaderSize: 48
14
+ };
15
+ }
16
+ static ReadHeader(reader) {
17
+ const unk = reader.readBytes(3 * 4); // 02 00 00 00, 24 00 00 00, 7F 3B 03 00 - always
18
+ const positionThingOrWhat = ParseVec3(reader); // 04 00 00 00, 04 00 00 00, 04 00 00 00 - always
19
+ // list of item costs.
20
+ let itemTypeCount = reader.readInt32();
21
+ const itemCosts = new Array(itemTypeCount).fill(['', 0]);
22
+ for (let i = 0; i < itemTypeCount; i++) {
23
+ let indexOrWhat = reader.readInt32();
24
+ let itemPathName = reader.readString();
25
+ let itemCount = reader.readInt32();
26
+ itemCosts[i] = [itemPathName, itemCount];
27
+ }
28
+ // list of recipes
29
+ let recipeCount = reader.readInt32();
30
+ const recipeRefs = new Array(recipeCount).fill('');
31
+ for (let i = 0; i < recipeCount; i++) {
32
+ let indexOrWhat = reader.readInt32();
33
+ const recipeName = reader.readString();
34
+ recipeRefs[i] = recipeName;
35
+ }
36
+ return {
37
+ recipeReferences: recipeRefs,
38
+ itemCosts
39
+ };
40
+ }
41
+ inflateChunks() {
42
+ // free memory
43
+ this.fileBuffer = this.fileBuffer.slice(this.currentByte);
44
+ this.handledByte = 0;
45
+ this.currentByte = 0;
46
+ this.maxByte = this.fileBuffer.byteLength;
47
+ let currentChunks = [];
48
+ let totalUncompressedBodySize = 0;
49
+ // read while we can handle
50
+ while (this.handledByte < this.maxByte) {
51
+ // Read chunk info size...
52
+ let chunkHeader = new DataView(this.fileBuffer.slice(0, this.compressionInfo.chunkHeaderSize));
53
+ this.currentByte = this.compressionInfo.chunkHeaderSize;
54
+ this.handledByte += this.compressionInfo.chunkHeaderSize;
55
+ if (this.compressionInfo.packageFileTag <= 0) {
56
+ this.compressionInfo.packageFileTag = chunkHeader.getUint32(0, this.alignment === Alignment.LITTLE_ENDIAN);
57
+ }
58
+ if (this.compressionInfo.maxChunkContentSize <= 0) {
59
+ this.compressionInfo.maxChunkContentSize = chunkHeader.getInt32(8, this.alignment === Alignment.LITTLE_ENDIAN); //00 00 02 00 = 131072
60
+ }
61
+ const chunkCompressedLength = chunkHeader.getInt32(32, this.alignment === Alignment.LITTLE_ENDIAN);
62
+ const chunkUncompressedLength = chunkHeader.getInt32(40, this.alignment === Alignment.LITTLE_ENDIAN);
63
+ totalUncompressedBodySize += chunkUncompressedLength;
64
+ const currentChunkSize = chunkCompressedLength;
65
+ let currentChunk = this.fileBuffer.slice(this.currentByte, this.currentByte + currentChunkSize);
66
+ this.handledByte += currentChunkSize;
67
+ this.currentByte += currentChunkSize;
68
+ // Free memory from previous chunk...
69
+ this.fileBuffer = this.fileBuffer.slice(this.currentByte);
70
+ this.currentByte = 0;
71
+ try {
72
+ // Inflate current chunk
73
+ let currentInflatedChunk = null;
74
+ currentInflatedChunk = Pako.inflate(currentChunk);
75
+ currentChunks.push(currentInflatedChunk);
76
+ }
77
+ catch (err) {
78
+ throw new ParserError('ParserError', 'An error occurred while calling pako inflate.' + err);
79
+ }
80
+ }
81
+ //TODO we can get rid of file buffer here.
82
+ // Create one big chunk out of the little chunks
83
+ let newChunkLength = currentChunks.map(cc => cc.length).reduce((prev, cur) => prev + cur);
84
+ const bigWholeChunk = new Uint8Array(newChunkLength);
85
+ let currentLength = 0;
86
+ for (let i = 0; i < currentChunks.length; i++) {
87
+ bigWholeChunk.set(currentChunks[i], currentLength);
88
+ currentLength += currentChunks[i].length;
89
+ }
90
+ // reset reader on this body content.
91
+ this.currentByte = 0;
92
+ this.maxByte = bigWholeChunk.buffer.byteLength;
93
+ this.bufferView = new DataView(bigWholeChunk.buffer);
94
+ return {
95
+ newChunkLength,
96
+ numChunks: currentChunks.length,
97
+ inflatedData: bigWholeChunk
98
+ };
99
+ }
100
+ static ParseObjects(reader) {
101
+ const totalBodyRestSize = reader.readInt32();
102
+ // object headers
103
+ const objectHeadersBinarySize = reader.readInt32();
104
+ let objects = [];
105
+ Level.ReadObjectHeaders(reader, objects, () => { });
106
+ // objects contents
107
+ Level.ReadObjectContents(reader, objects, 0, () => { });
108
+ return objects;
109
+ }
110
+ }
111
+ export class BlueprintConfigReader extends ByteReader {
112
+ constructor(bluePrintConfigBuffer) {
113
+ super(bluePrintConfigBuffer, Alignment.LITTLE_ENDIAN);
114
+ this.bluePrintConfigBuffer = bluePrintConfigBuffer;
115
+ this.parse = () => BlueprintConfigReader.ParseConfig(this);
116
+ }
117
+ static ParseConfig(reader) {
118
+ const unk = reader.readInt32(); // unknown, seems to always be 02.
119
+ const description = reader.readString();
120
+ const unk3 = reader.readInt32(); // iconID. default is 0x0E03 = 782
121
+ const colorMaybe = ParseCol4RGBA(reader); // LinearColor, which is 4x float32 as RGBA
122
+ return {
123
+ description,
124
+ color: colorMaybe,
125
+ iconID: unk3
126
+ };
127
+ }
128
+ }
@@ -0,0 +1,21 @@
1
+ import { SaveComponent } from "../parser/satisfactory/objects/SaveComponent";
2
+ import { SaveEntity } from "../parser/satisfactory/objects/SaveEntity";
3
+ import { ByteWriter } from "./byte/byte-writer.class";
4
+ import { BlueprintConfig, BlueprintHeader } from "./satisfactory/blueprint/blueprint";
5
+ import { ChunkCompressionInfo } from "./satisfactory/satisfactory-save";
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, onFinish: (summary: {
10
+ errors: string[];
11
+ chunkSummary: {
12
+ uncompressedSize: number;
13
+ compressedSize: number;
14
+ }[];
15
+ }) => void): void;
16
+ static SerializeObjects(writer: ByteWriter, objects: (SaveEntity | SaveComponent)[]): void;
17
+ }
18
+ export declare class BlueprintConfigWriter extends ByteWriter {
19
+ constructor();
20
+ static SerializeConfig(writer: ByteWriter, config: BlueprintConfig): void;
21
+ }
@@ -0,0 +1,64 @@
1
+ import { Level } from "../parser/satisfactory/level.class";
2
+ import { SerializeCol4RGBA } from "../parser/satisfactory/structs/util.types";
3
+ import { Alignment } from "./byte/alignment.enum";
4
+ import { ByteWriter } from "./byte/byte-writer.class";
5
+ import { ParserError } from "./error/parser.error";
6
+ import { SaveWriter } from "./save-writer";
7
+ export class BlueprintWriter extends ByteWriter {
8
+ constructor() {
9
+ super(Alignment.LITTLE_ENDIAN);
10
+ }
11
+ static SerializeHeader(writer, header) {
12
+ writer.writeInt32(2); // unknown, seems to always be 02.
13
+ writer.writeInt32(36); // TODO - unknown what it is, but its constant so far.
14
+ writer.writeInt32(211839); // TODO - unknown what it is, but its constant so far.
15
+ // TODO - whatever those are, but they are constant so far.
16
+ writer.writeInt32(4);
17
+ writer.writeInt32(4);
18
+ writer.writeInt32(4);
19
+ // list of item costs.
20
+ writer.writeInt32(header.itemCosts.length);
21
+ for (const itemCost of header.itemCosts) {
22
+ writer.writeInt32(0); //TODO: unk or what
23
+ writer.writeString(itemCost[0]);
24
+ writer.writeInt32(itemCost[1]);
25
+ }
26
+ // list of recipes.
27
+ writer.writeInt32(header.recipeReferences.length);
28
+ for (const recipeReference of header.recipeReferences) {
29
+ writer.writeInt32(0); //TODO: unk or what
30
+ writer.writeString(recipeReference);
31
+ }
32
+ }
33
+ generateChunks(compressionInfo, posAfterHeader, onBinaryBeforeCompressing, onHeader, onChunk, onFinish) {
34
+ if (posAfterHeader <= 0) {
35
+ throw new ParserError('ParserError', 'seems like this buffer has no header. Please write the header first before you can generate chunks.');
36
+ }
37
+ // send plain header first.
38
+ const header = new Uint8Array(this.bufferArray.slice(0, posAfterHeader));
39
+ onHeader(header);
40
+ // create save body
41
+ this.bufferArray = this.bufferArray.slice(posAfterHeader);
42
+ SaveWriter.GenerateCompressedChunksFromData(this.bufferArray, compressionInfo, onBinaryBeforeCompressing, onChunk, onFinish, this.alignment);
43
+ }
44
+ static SerializeObjects(writer, objects) {
45
+ // object headers
46
+ const headersLenIndicator = writer.getBufferPosition();
47
+ writer.writeInt32(0);
48
+ Level.SerializeObjectHeaders(writer, objects);
49
+ writer.writeBinarySizeFromPosition(headersLenIndicator, headersLenIndicator + 4);
50
+ // objects contents
51
+ Level.SerializeObjectContents(writer, objects, 0);
52
+ }
53
+ }
54
+ export class BlueprintConfigWriter extends ByteWriter {
55
+ constructor() {
56
+ super(Alignment.LITTLE_ENDIAN);
57
+ }
58
+ static SerializeConfig(writer, config) {
59
+ writer.writeInt32(2); // unknown, seems to always be 02.
60
+ writer.writeString(config.description);
61
+ writer.writeInt32(config.iconID);
62
+ SerializeCol4RGBA(writer, config.color);
63
+ }
64
+ }
@@ -0,0 +1,4 @@
1
+ export declare enum Alignment {
2
+ BIG_ENDIAN = 0,
3
+ LITTLE_ENDIAN = 1
4
+ }
@@ -0,0 +1,5 @@
1
+ export var Alignment;
2
+ (function (Alignment) {
3
+ Alignment[Alignment["BIG_ENDIAN"] = 0] = "BIG_ENDIAN";
4
+ Alignment[Alignment["LITTLE_ENDIAN"] = 1] = "LITTLE_ENDIAN";
5
+ })(Alignment || (Alignment = {}));
@@ -0,0 +1,38 @@
1
+ import { Alignment } from "./alignment.enum";
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
+ }
@@ -0,0 +1,128 @@
1
+ import { Alignment } from "./alignment.enum";
2
+ export class ByteReader {
3
+ constructor(fileBuffer, alignment) {
4
+ this.currentByte = 0;
5
+ this.handledByte = 0;
6
+ this.maxByte = 0;
7
+ this.lastStrRead = 0;
8
+ this.getBufferPosition = () => this.currentByte;
9
+ this.getBufferSlice = (begin, end) => this.bufferView.buffer.slice(begin, end);
10
+ this.getBufferProgress = () => this.currentByte / this.bufferView.buffer.byteLength;
11
+ this.getBufferLength = () => this.bufferView.buffer.byteLength;
12
+ this.getBuffer = () => this.bufferView.buffer;
13
+ this.alignment = alignment;
14
+ this.reset(fileBuffer);
15
+ }
16
+ /**
17
+ * Resets the reader on the given arraybuffer to start from the beginning.
18
+ * @param newFileBuffer the new array buffer to be read.
19
+ */
20
+ reset(newFileBuffer) {
21
+ this.fileBuffer = newFileBuffer;
22
+ this.bufferView = new DataView(newFileBuffer, 0, Math.min(102400, this.fileBuffer.byteLength));
23
+ this.maxByte = newFileBuffer.byteLength;
24
+ this.currentByte = 0;
25
+ this.handledByte = 0;
26
+ }
27
+ /*
28
+ * Byte Manipulation
29
+ */
30
+ skipBytes(byteLength = 1) {
31
+ this.currentByte += byteLength;
32
+ }
33
+ readByte() {
34
+ // TODO can i not just readByte() ??
35
+ return parseInt(this.bufferView.getUint8(this.currentByte++).toString());
36
+ }
37
+ readBytes(count) {
38
+ return new Uint8Array(new Array(count).fill(0).map(pl => this.bufferView.getUint8(this.currentByte++)));
39
+ }
40
+ readHex(hexLength) {
41
+ let hexPart = [];
42
+ for (let i = 0; i < hexLength; i++) {
43
+ let currentHex = String.fromCharCode(this.bufferView.getUint8(this.currentByte++));
44
+ hexPart.push(currentHex);
45
+ }
46
+ return hexPart.join('');
47
+ }
48
+ readInt8() {
49
+ let data = this.bufferView.getInt8(this.currentByte++);
50
+ return data;
51
+ }
52
+ readUint8() {
53
+ let data = this.bufferView.getUint8(this.currentByte++);
54
+ return data;
55
+ }
56
+ readUint16() {
57
+ let data = this.bufferView.getUint16(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
58
+ this.currentByte += 2;
59
+ return data;
60
+ }
61
+ readInt32() {
62
+ let data = this.bufferView.getInt32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
63
+ this.currentByte += 4;
64
+ return data;
65
+ }
66
+ readUint32() {
67
+ let data = this.bufferView.getUint32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
68
+ this.currentByte += 4;
69
+ return data;
70
+ }
71
+ readLong() {
72
+ let data = this.bufferView.getBigInt64(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
73
+ this.currentByte += 8;
74
+ return data;
75
+ }
76
+ readFloat() {
77
+ let data = this.bufferView.getFloat32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
78
+ this.currentByte += 4;
79
+ return data;
80
+ }
81
+ readDouble() {
82
+ let data = this.bufferView.getFloat64(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
83
+ this.currentByte += 8;
84
+ return data;
85
+ }
86
+ getStringInfo() {
87
+ let payload = '';
88
+ let counter = this.currentByte;
89
+ try {
90
+ let strLength = this.bufferView.getInt32(this.currentByte, this.alignment === Alignment.LITTLE_ENDIAN);
91
+ counter += 4;
92
+ payload = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.bufferView.getUint8(counter++))).join('');
93
+ }
94
+ catch (error) { }
95
+ this.currentByte -= counter;
96
+ return { payload, counter };
97
+ }
98
+ readString() {
99
+ let strLength = this.readInt32();
100
+ this.lastStrRead = strLength;
101
+ let startBytes = this.currentByte;
102
+ if (strLength === 0) {
103
+ return '';
104
+ }
105
+ // Range error!
106
+ if (strLength > (this.bufferView.buffer.byteLength - this.currentByte)) {
107
+ let errorMessage = `Cannot read string of length ${strLength} at position ${this.currentByte} as it exceeds the end at ${this.bufferView.buffer.byteLength}`;
108
+ throw new Error(errorMessage);
109
+ }
110
+ // it uses UTF16 if text is non-ascii, even if it would fit into UTF8.
111
+ if (strLength < 0) {
112
+ const string = new Array(-strLength - 1).fill('').map(c => String.fromCharCode(this.readUint16()));
113
+ this.currentByte += 2;
114
+ return string.join('');
115
+ }
116
+ //default UTF-8
117
+ try {
118
+ const string = new Array(strLength - 1).fill('').map(c => String.fromCharCode(this.readUint8()));
119
+ this.currentByte += 1;
120
+ return string.join('');
121
+ }
122
+ catch (error) {
123
+ let errorMessage = `Cannot read UTF8 string of length ${strLength} at position ${this.currentByte}.`;
124
+ console.log(errorMessage, error);
125
+ throw error;
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,38 @@
1
+ import { Alignment } from "./alignment.enum";
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
+ }
@@ -0,0 +1,146 @@
1
+ import { Alignment } from "./alignment.enum";
2
+ class ByteWriter {
3
+ constructor(alignment, bufferSize = 500) {
4
+ this.getBufferPosition = () => this.currentByte;
5
+ this.getBufferSlice = (start, end) => this.bufferArray.slice(start, end);
6
+ this.alignment = alignment;
7
+ this.bufferArray = new ArrayBuffer(bufferSize);
8
+ this.bufferView = new DataView(this.bufferArray);
9
+ this.currentByte = 0;
10
+ }
11
+ /*
12
+ * Byte Manipulation
13
+ */
14
+ skipBytes(count = 1) {
15
+ this.extendBufferIfNeeded(count);
16
+ this.currentByte += count;
17
+ }
18
+ jumpTo(pos) {
19
+ const count = pos - this.getBufferPosition();
20
+ this.skipBytes(count);
21
+ }
22
+ writeByte(value) {
23
+ this.extendBufferIfNeeded(1);
24
+ this.bufferView.setUint8(this.currentByte, value);
25
+ this.currentByte += 1;
26
+ }
27
+ writeBytesArray(bytes) {
28
+ this.writeBytes(new Uint8Array(bytes));
29
+ }
30
+ writeBytes(bytes) {
31
+ this.extendBufferIfNeeded(bytes.length);
32
+ bytes.forEach(byte => this.bufferView.setUint8(this.currentByte++, byte));
33
+ }
34
+ writeInt8(value) {
35
+ this.extendBufferIfNeeded(1);
36
+ this.bufferView.setInt8(this.currentByte, value);
37
+ this.currentByte += 1;
38
+ }
39
+ writeUint8(value) {
40
+ this.writeByte(value);
41
+ }
42
+ writeInt16(value) {
43
+ this.extendBufferIfNeeded(2);
44
+ this.bufferView.setInt16(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
45
+ this.currentByte += 2;
46
+ }
47
+ writeUint16(value) {
48
+ this.extendBufferIfNeeded(2);
49
+ this.bufferView.setUint16(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
50
+ this.currentByte += 2;
51
+ }
52
+ writeInt32(value) {
53
+ this.extendBufferIfNeeded(4);
54
+ this.bufferView.setInt32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
55
+ this.currentByte += 4;
56
+ }
57
+ writeUint32(value) {
58
+ this.extendBufferIfNeeded(4);
59
+ this.bufferView.setUint32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
60
+ this.currentByte += 4;
61
+ }
62
+ writeInt64(value) {
63
+ this.extendBufferIfNeeded(8);
64
+ this.bufferView.setBigInt64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
65
+ this.currentByte += 8;
66
+ }
67
+ writeUint64(value) {
68
+ this.extendBufferIfNeeded(8);
69
+ this.bufferView.setBigUint64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
70
+ this.currentByte += 8;
71
+ }
72
+ writeFloat(value) {
73
+ this.extendBufferIfNeeded(4);
74
+ this.bufferView.setFloat32(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
75
+ this.currentByte += 4;
76
+ }
77
+ writeDouble(value) {
78
+ this.extendBufferIfNeeded(8);
79
+ this.bufferView.setFloat64(this.currentByte, value, this.alignment === Alignment.LITTLE_ENDIAN);
80
+ this.currentByte += 8;
81
+ }
82
+ writeString(value) {
83
+ if (value.length === 0) {
84
+ this.writeInt32(0);
85
+ return;
86
+ }
87
+ // if it's safe to use ASCII, use UTF8.
88
+ if (ByteWriter.IsASCIICompatible(value)) {
89
+ this.writeInt32(value.length + 1);
90
+ for (let i = 0; i < value.length; i++) {
91
+ this.writeByte(value.charCodeAt(i));
92
+ }
93
+ this.writeUint8(0);
94
+ }
95
+ // write UTF16
96
+ else {
97
+ this.writeInt32(-value.length - 1);
98
+ for (let i = 0; i < value.length; i++) {
99
+ this.writeUint16(value.charCodeAt(i));
100
+ }
101
+ this.writeUint16(0);
102
+ }
103
+ }
104
+ writeBinarySizeFromPosition(lenIndicatorPos, start) {
105
+ const after = this.getBufferPosition();
106
+ const writtenBytes = after - start;
107
+ this.jumpTo(lenIndicatorPos);
108
+ this.writeInt32(writtenBytes);
109
+ this.jumpTo(after);
110
+ }
111
+ /**
112
+ * automatically extends the current buffer if the given space exceeds the available rest capacity of the current buffer.
113
+ * @param countNeededBytes the needed space
114
+ * @param factor how big the appended buffer should be in comparison to the current one. Values >= 1 make sense.
115
+ */
116
+ extendBufferIfNeeded(countNeededBytes, factor = 1.5) {
117
+ if (this.currentByte + countNeededBytes > this.bufferView.byteLength) {
118
+ this.bufferArray = ByteWriter.AppendBuffer(this.bufferArray, new ArrayBuffer(factor * this.bufferArray.byteLength));
119
+ this.bufferView = new DataView(this.bufferArray);
120
+ }
121
+ }
122
+ truncateBuffer() {
123
+ this.bufferArray = this.bufferArray.slice(0, this.currentByte);
124
+ }
125
+ endWriting() {
126
+ this.truncateBuffer();
127
+ return this.bufferArray;
128
+ }
129
+ static ToInt32(num) {
130
+ return new Uint8Array([
131
+ (num & 0xff000000) >> 24,
132
+ (num & 0x00ff0000) >> 16,
133
+ (num & 0x0000ff00) >> 8,
134
+ (num & 0x000000ff)
135
+ ]);
136
+ }
137
+ static AppendBuffer(buffer1, buffer2) {
138
+ var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
139
+ tmp.set(new Uint8Array(buffer1), 0);
140
+ tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
141
+ return tmp.buffer;
142
+ }
143
+ ;
144
+ }
145
+ ByteWriter.IsASCIICompatible = (value) => /^[\x00-\x7F]*$/.test(value);
146
+ export { 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,21 @@
1
+ export class ParserError extends Error {
2
+ constructor(name, message) {
3
+ super(message);
4
+ this.name = name;
5
+ }
6
+ }
7
+ export class UnsupportedVersionError extends ParserError {
8
+ constructor(message) {
9
+ super('UnsupportedVersionError', message ?? 'This save version is not supported.');
10
+ }
11
+ }
12
+ export class CorruptSaveError extends ParserError {
13
+ constructor(message) {
14
+ super('CorruptSaveError', message ?? 'This save data is most likely corrupt.');
15
+ }
16
+ }
17
+ export class CompressionLibraryError extends ParserError {
18
+ constructor(message) {
19
+ super('CompressionLibraryError', message ?? 'Failed to compress/decompress save data.');
20
+ }
21
+ }