diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..0248e91 Binary files /dev/null and b/assets/icon.png differ diff --git a/forge.config.cjs b/forge.config.cjs index fb9ba24..f7308c2 100644 --- a/forge.config.cjs +++ b/forge.config.cjs @@ -2,6 +2,7 @@ const { FusesPlugin } = require("@electron-forge/plugin-fuses"); const { FuseV1Options, FuseVersion } = require("@electron/fuses"); const fs = require("fs/promises"); const path = require("path"); +const packageJson = require("./package.json"); const buildTarget = process.env.BUILD_TARGET || "all"; @@ -20,10 +21,34 @@ if (buildTarget == "msi" || buildTarget == "all") { if (buildTarget == "zip" || buildTarget == "all") { makers.push({ name: "@electron-forge/maker-zip", - platforms: ["win32"], + platforms: ["win32", "linux"], }); } +if (buildTarget == "deb" || buildTarget == "all") { + makers.push({ + name: "@electron-forge/maker-deb", + config: { + options: { + bin: packageJson.productName, + name: packageJson.productName, + icon: "./assets/icon.png", + maintainer: "GabiZar", + homepage: "https://github.com/Gabi-Zar/Silk-Fly-Launcher", + }, + }, + }); +} + +async function fileExists(filePath) { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + module.exports = { packagerConfig: { asar: true, @@ -67,6 +92,9 @@ module.exports = { postMake: async (forgeConfig, makeResults) => { if (buildTarget == "msi" || buildTarget == "all") { const outDir = path.join(__dirname, "out", "make", "wix", "x64"); + if (!(await fileExists(outDir))) { + return; + } const files = await fs.readdir(outDir, { recursive: true }); const msiFile = path.join( outDir, diff --git a/main.js b/main.js index 47a9b68..5ec0647 100644 --- a/main.js +++ b/main.js @@ -13,7 +13,7 @@ const { extractFull } = node7z; import packageJson from "./package.json" with { type: "json" }; import semverGt from "semver/functions/gt.js"; import { randomUUID } from "crypto"; -import { unzip } from "zlib"; +import { spawn } from "child_process"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -42,7 +42,7 @@ let onlineTotalModsCount; let thunderstoreCachedModList; let thunderstoreTotalModsCount; -const bepinexFiles = [".doorstop_version", "changelog.txt", "doorstop_config.ini", "winhttp.dll"]; +const bepinexFiles = [".doorstop_version", "changelog.txt", "doorstop_config.ini", "winhttp.dll", "libdoorstop.so", "run_bepinex.sh"]; let bepinexVersion = bepinexStore.get("bepinex-version"); let bepinexBackupVersion; @@ -90,12 +90,12 @@ async function createWindow() { }); } -app.whenReady().then(() => { +app.whenReady().then(async () => { if (isDev) { app.setAsDefaultProtocolClient("nxm", process.execPath, [path.resolve(process.argv[1])]); } else { app.setAsDefaultProtocolClient("nxm"); - sevenZipPath = path7za.replace("\\app.asar\\node_modules", ""); + sevenZipPath = path7za.replace(path.join("app.asar", "node_modules"), ""); Menu.setApplicationMenu(null); } @@ -105,7 +105,10 @@ app.whenReady().then(() => { createWindow(); } - app.on("activate", () => { + app.on("activate", async () => { + if (process.platform === "linux") { + await fs.chmod(sevenZipPath, 0o755); + } if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } @@ -378,7 +381,12 @@ async function installBepinex() { const release = await res.json(); - const asset = release.assets.find((a) => a.name.endsWith(".zip") && a.name.toLowerCase().includes("win_x64")); + let asset; + if (process.platform === "win32") { + asset = release.assets.find((a) => a.name.endsWith(".zip") && a.name.toLowerCase().includes("win_x64")); + } else if (process.platform === "linux") { + asset = release.assets.find((a) => a.name.endsWith(".zip") && a.name.toLowerCase().includes("linux_x64")); + } await downloadAndUnzip(asset.browser_download_url, silksongPath); @@ -945,7 +953,15 @@ ipcMain.handle("open-window", async (event, file) => { }); ipcMain.handle("launch-game", async (event, mode) => { - const silksongExecutablePath = path.join(loadSilksongPath(), "Hollow Knight Silksong.exe"); + let silksongExecutable; + if (process.platform === "win32") { + silksongExecutable = "Hollow Knight Silksong.exe"; + } else if (process.platform === "linux") { + silksongExecutable = "Hollow Knight Silksong"; + } + + const silksongExecutablePath = path.join(loadSilksongPath(), silksongExecutable); + const silksongScriptPath = path.join(loadSilksongPath(), "run_bepinex.sh"); const bepinexFolderPath = path.join(loadSilksongPath(), "BepInEx"); if (!fileExists(silksongExecutablePath)) { mainWindow.webContents.send("showToast", "Path to the game invalid", "warning"); @@ -954,22 +970,42 @@ ipcMain.handle("launch-game", async (event, mode) => { if (mode === "modded") { if (await fileExists(bepinexFolderPath)) { - await shell.openExternal(silksongExecutablePath); + if (process.platform === "win32") { + executeGame(silksongExecutablePath); + } + if (process.platform === "linux") { + executeGame(silksongScriptPath, [silksongExecutable]); + } } else { await installBepinex(); - await shell.openExternal(silksongExecutablePath); + if (process.platform === "win32") { + executeGame(silksongExecutablePath); + } + if (process.platform === "linux") { + executeGame(silksongScriptPath, [silksongExecutable]); + } } } if (mode === "vanilla") { if (await fileExists(bepinexFolderPath)) { await backupBepinex(); - await shell.openExternal(silksongExecutablePath); + executeGame(silksongExecutablePath); } else { - await shell.openExternal(silksongExecutablePath); + executeGame(silksongExecutablePath); } } }); +async function executeGame(path, args = []) { + await fs.chmod(path, 0o755); + const game = spawn(path, args, { + cwd: loadSilksongPath(), + detached: true, + stdio: "ignore", + }); + game.unref(); +} + async function downloadAndUnzip(url, toPath) { url = new URL(url); const fileName = url.pathname.split("/").pop(); diff --git a/package-lock.json b/package-lock.json index 0e72d76..f025766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@electron-forge/cli": "^7.11.1", + "@electron-forge/maker-deb": "^7.11.1", "@electron-forge/maker-wix": "^7.11.1", "@electron-forge/maker-zip": "^7.11.1", "@electron-forge/plugin-auto-unpack-natives": "^7.11.1", @@ -170,6 +171,23 @@ "node": ">= 16.4.0" } }, + "node_modules/@electron-forge/maker-deb": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.11.1.tgz", + "integrity": "sha512-QTYiryQLYPDkq6pIfBmx0GQ6D8QatUkowH7rTlW5MnCUa0uumX0Xu7yGIjesuwW37fxT3Lv4xi+FSXMCm2eC1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.11.1", + "@electron-forge/shared-types": "7.11.1" + }, + "engines": { + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-installer-debian": "^3.2.0" + } + }, "node_modules/@electron-forge/maker-wix": { "version": "7.11.1", "resolved": "https://registry.npmjs.org/@electron-forge/maker-wix/-/maker-wix-7.11.1.tgz", @@ -1317,6 +1335,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -2795,6 +2824,242 @@ "node": ">= 12.20.55" } }, + "node_modules/electron-installer-common": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.4.tgz", + "integrity": "sha512-8gMNPXfAqUE5CfXg8RL0vXpLE9HAaPkgLXVoHE3BMUzogMWenf4LmwQ27BdCUrEhkjrKl+igs2IHJibclR3z3Q==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@electron/asar": "^3.2.5", + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "glob": "^7.1.4", + "lodash": "^4.17.15", + "parse-author": "^2.0.0", + "semver": "^7.1.1", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1" + }, + "optionalDependencies": { + "@types/fs-extra": "^9.0.1" + } + }, + "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-installer-common/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", + "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux" + ], + "dependencies": { + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "electron-installer-common": "^0.10.2", + "fs-extra": "^9.0.0", + "get-folder-size": "^2.0.1", + "lodash": "^4.17.4", + "word-wrap": "^1.2.3", + "yargs": "^16.0.2" + }, + "bin": { + "electron-installer-debian": "src/cli.js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-installer-debian/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/electron-installer-debian/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/electron-installer-debian/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-debian/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-debian/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/electron-installer-debian/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, "node_modules/electron-store": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-11.0.2.tgz", @@ -3538,6 +3803,15 @@ "node": ">= 12" } }, + "node_modules/gar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", + "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3548,6 +3822,21 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-folder-size": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", + "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "gar": "^1.0.4", + "tiny-each-async": "2.0.3" + }, + "bin": { + "get-folder-size": "bin/get-folder-size" + } + }, "node_modules/get-package-info": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", @@ -6779,6 +7068,14 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-each-async": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", + "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -6792,6 +7089,28 @@ "node": ">=0.6.0" } }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tmp-promise/node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7173,6 +7492,17 @@ "node": ">= 8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", diff --git a/package.json b/package.json index 136024f..2895d5f 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ "package": "electron-forge package", "make": "electron-forge make", "make:msi": "cross-env BUILD_TARGET=msi electron-forge make", - "make:zip": "cross-env BUILD_TARGET=zip electron-forge make" + "make:zip": "cross-env BUILD_TARGET=zip electron-forge make", + "make:deb": "cross-env BUILD_TARGET=deb electron-forge make" }, "author": "GabiZar", "license": "GPL-3.0", "devDependencies": { "@electron-forge/cli": "^7.11.1", + "@electron-forge/maker-deb": "^7.11.1", "@electron-forge/maker-wix": "^7.11.1", "@electron-forge/maker-zip": "^7.11.1", "@electron-forge/plugin-auto-unpack-natives": "^7.11.1",