mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 23:04:52 +09:00
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:
1 parent
fafdac21fb
commit
53a5d7ea6f
2 files changed
+167
No files matched your search
@@ -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;
|
||||
@@ -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 */
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user