zip_ops: Deduce root folder from first zip entry (#739)

Some mods, for example cubium, includes bogus folders like __MACOSX in
addition to the root folder where everything is located.  This only
works by chance as Factorio will check for the folder of the first
entry in the zip file and use that.

Match Factorio's logic in findRoot so that these mods work with
Clusterio.
This commit is contained in:
Hornwitser authored and GitHub committed 2025-03-21 23:00:30 +00:00
1 parent 6180a0b27b
commit aeac7eedec
2 files changed
+17 -26

No files matched your search

+13 -19
View File
@@ -3,31 +3,25 @@ import type JSZip from "jszip";
/**
* Returns the root folder in the zip file
*
* Returns the name of the folder that all files in the zip file is
* contained in. Throws an error if there are multiple such folders.
* Returns the name of the folder that the first entry in the zip file is
* contained in. Throws an error if this is not in a folder.
*
* This matches the logic Factorio uses when determining the folder to look
* for content in a zip file.
*
* @param zip - Zip to search through.
* @returns name of the root folder.
*/
export function findRoot(zip: JSZip) {
let root: undefined | string;
zip.forEach((relativePath, file) => {
let index = relativePath.indexOf("/");
if (index === -1) {
throw new Error(`Zip contains file '${relativePath}' in root dir`);
}
let pathRoot = relativePath.slice(0, index);
if (root === undefined) {
root = pathRoot;
} else if (root !== pathRoot) {
throw new Error("Zip contains multiple root folders");
}
});
if (root === undefined) {
const relativePath = Object.keys(zip.files)[0];
if (relativePath === undefined) {
throw new Error("Empty zip file");
}
return root;
let index = relativePath.indexOf("/");
if (index === -1) {
throw new Error(`Zip contains file '${relativePath}' in root dir`);
}
return relativePath.slice(0, index);
}
+4 -7
View File
@@ -15,10 +15,10 @@ describe("lib/factorio", function() {
assert.equal(findRoot(zip), "root");
});
it("should throw if files are at the root of the zip", function() {
it("should throw if the first file is at the root of the zip", function() {
let zip = new JSZip();
zip.file("root/foo.txt", "");
zip.file("bar.txt", "");
zip.file("root/foo.txt", "");
assert.throws(
() => findRoot(zip),
@@ -26,15 +26,12 @@ describe("lib/factorio", function() {
);
});
it("should throw if there are multiple root dirs", function() {
it("should return directory of first file if there are multiple root dirs", function() {
let zip = new JSZip();
zip.file("root-1/foo.txt", "");
zip.file("root-2/bar.txt", "");
assert.throws(
() => findRoot(zip),
new Error("Zip contains multiple root folders")
);
assert.equal(findRoot(zip), "root-1");
});
it("should throw if given an empty zip file", function() {