Add ModPack.toModSettingsDat

Add utility function to convert the settings representation of a ModPack
into the mod-settings.dat format which Factorio reads.
This commit is contained in:
Hornwitser committed 2022-10-15 18:01:54 +02:00
1 parent fafdac21fb
commit 53a5d7ea6f
2 files changed
+167

No files matched your search

+97
View File
@@ -208,6 +208,103 @@ class ModPack {
return new this(json);
}
toModSettingsDat() {
function uint8Byte(int) {
return Buffer.from(Uint8Array.from([int]).buffer);
}
function int16Bytes(int) {
return Buffer.from(Int16Array.from([int]).buffer);
}
function uint32Bytes(int) {
return Buffer.from(Uint32Array.from([int]).buffer);
}
function uint32SpaceOptimizedBytes(int) {
if (0 <= int && int < 0xff) {
return uint8Byte(int);
}
return Buffer.concat([
uint8Byte(0xff),
uint32Bytes(int),
]);
}
function doubleBytes(double) {
return Buffer.from(Float64Array.from([double]).buffer);
}
function stringBytes(str) {
const buf = Buffer.from(str, "utf8");
return Buffer.concat([
uint32SpaceOptimizedBytes(buf.length),
buf,
]);
}
function immutableStringBytes(str) {
if (str === undefined) {
return uint8Byte(1); // empty
}
return Buffer.concat([
uint8Byte(0), // empty
stringBytes(str),
]);
}
function versionBytes(version) {
const [main, major, minor] = version.split(".").map(n => Number.parseInt(n, 10));
return Buffer.concat([
int16Bytes(main),
int16Bytes(major),
int16Bytes(minor),
Buffer.alloc(2), // developer
Buffer.alloc(1), // reserved byte
]);
}
function propertyTreeListBytes(entries) {
const sizeBytes = uint32Bytes(entries.length);
const itemBytes = entries.flatMap(([key, item]) => [
immutableStringBytes(key),
// eslint-disable-next-line no-use-before-define
propertyTreeBytes(item),
]);
return Buffer.concat([
sizeBytes,
...itemBytes,
]);
}
function propertyTreeBytes(element) {
let type;
let dataBytes;
if (typeof element === null) {
type = 1;
dataBytes = Buffer.alloc(0);
} else if (typeof element === "boolean") {
type = 1;
dataBytes = uint8Byte(element);
} else if (typeof element === "number") {
type = 2;
dataBytes = doubleBytes(element);
} else if (typeof element === "string") {
type = 3;
dataBytes = immutableStringBytes(element);
} else if (element instanceof Array) {
type = 4;
dataBytes = propertyTreeListBytes(element.map(item => [undefined, item]));
} else if (typeof element === "object") {
type = 5;
dataBytes = propertyTreeListBytes(Object.entries(element));
} else {
throw new Error("Bad element passed to propertyTreeBytes");
}
return Buffer.concat([
uint8Byte(type),
Buffer.alloc(1), // anyTypeFlag
dataBytes,
]);
}
return Buffer.concat([
versionBytes(this.factorioVersion),
propertyTreeBytes(this.toJSON().settings),
]);
}
}
module.exports = ModPack;
+70
View File
@@ -90,5 +90,75 @@ describe("lib/data/ModPack", function() {
);
});
});
describe("toModSettingsDat()", function() {
it("should properly serialise settings", function() {
const pack = new ModPack({ settings: {
"startup": {
"bool-setting": { "value": true },
},
"runtime-global": {
"number-setting": { "value": 123 },
},
"runtime-per-user": {
"string-setting": { "value": "a string" },
},
}});
function istr(str) {
return [
Uint8Array.from([0, str.length]),
Buffer.from(str),
];
}
/* eslint-disable indent */
assert.deepEqual(
pack.toModSettingsDat(),
Buffer.concat([
new Uint8Array(Uint16Array.from([1, 1, 0, 0]).buffer), // version
Uint8Array.from([0]), // reserved
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([3]).buffer), // items
...istr("startup"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("bool-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([1, 0, 1]), // boolean
...istr("runtime-global"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("number-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([2, 0]), // number
new Uint8Array(Float64Array.from([123]).buffer),
...istr("runtime-per-user"), // name
Uint8Array.from([5, 0]), // dictionary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("string-setting"), // name
Uint8Array.from([5, 0]), // dictinary
new Uint8Array(Uint32Array.from([1]).buffer), // items
...istr("value"), // name
Uint8Array.from([3, 0, 0, "a string".length]), // string
Buffer.from("a string"),
])
);
/* eslint-enable indent */
});
});
});
});