From aeac7eedecf32c2acff1b6a215e131693bd90b11 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Sat, 22 Mar 2025 00:00:30 +0100 Subject: [PATCH] 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. --- packages/lib/src/zip_ops.ts | 32 +++++++++++++------------------- test/lib/zip_ops.js | 11 ++++------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/packages/lib/src/zip_ops.ts b/packages/lib/src/zip_ops.ts index 492d2072..1c96a6b8 100644 --- a/packages/lib/src/zip_ops.ts +++ b/packages/lib/src/zip_ops.ts @@ -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); } diff --git a/test/lib/zip_ops.js b/test/lib/zip_ops.js index 76b28dd1..f9227021 100644 --- a/test/lib/zip_ops.js +++ b/test/lib/zip_ops.js @@ -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() {