diff --git a/assets/vortex.desktop b/assets/vortex.desktop new file mode 100644 index 0000000..7c5b838 --- /dev/null +++ b/assets/vortex.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Categories=Game;Utility +Name=Vortex +MimeType=x-scheme-handler/nxm;x-scheme-handler/nxm-protocol +Terminal=false +X-KeepTerminal=false +Path=%%VORTEX_PATH%% +Exec=%%VORTEX_EXEC%% +Icon=%%VORTEX_ICON%% diff --git a/assets/vortex.ico b/assets/vortex.ico new file mode 100644 index 0000000..299dab9 Binary files /dev/null and b/assets/vortex.ico differ diff --git a/build.sh b/build.sh index 9e08824..f33d117 100755 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ rm -rf tmp mkdir -p tmp bin trap 'rm -rf tmp' EXIT -esbuild main.js --bundle --platform=node --outfile=tmp/main.js +esbuild main.js --bundle --platform=node --outfile=tmp/main.js --loader:.ico=dataurl --loader:.desktop=text node --experimental-sea-config sea-config.json cp $(command -v node) bin/vortex-linux postject bin/vortex-linux NODE_SEA_BLOB tmp/sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 diff --git a/lib/config.js b/lib/config.js index 9dcd2d9..2db5dd8 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,4 +1,34 @@ import os from "os"; +import fs from "fs"; import path from "path"; export const BASE_DIR = path.join(os.homedir(), ".vortex-linux"); + +export const getConfig = () => { + const configPath = path.join(BASE_DIR, "config.json"); + try { + const fileContent = fs.readFileSync(configPath, "utf-8"); + return JSON.parse(fileContent); + } catch (error) { + return {}; + } +}; + +export const setConfig = (key, value) => { + const config = getConfig(); + + if (value === undefined) { + delete config[key]; + } else { + config[key] = value; + } + + const configPath = path.join(BASE_DIR, "config.json"); + const configContent = JSON.stringify(config, null, 2); + + try { + fs.writeFileSync(configPath, configContent, "utf-8"); + } catch (error) { + console.error("Error saving config file:", error); + } +}; diff --git a/lib/proton.js b/lib/proton.js index a9f2688..d9ce647 100644 --- a/lib/proton.js +++ b/lib/proton.js @@ -6,7 +6,7 @@ import os from "os"; import { promisify } from "util"; import { pipeline } from "stream"; -import { BASE_DIR } from "./config.js"; +import { BASE_DIR, getConfig } from "./config.js"; const pipelineAsync = promisify(pipeline); const execAsync = promisify(exec); @@ -42,12 +42,6 @@ export const downloadProton = async (downloadUrl) => { export const protonRun = async (command) => { const protonPath = path.join(BASE_DIR, "proton-builds", "active", "proton"); const compatdataPath = path.join(BASE_DIR, "compatdata"); - const vortexRuntime = process?.env?.VORTEX_RUNTIME; - let fullCommand = `${protonPath} run ${command}`; - - if (vortexRuntime) { - fullCommand = `${vortexRuntime}/run -- ${fullCommand}`; - } // Create compatdataPath directory if it doesn't exist if (!existsSync(compatdataPath)) { @@ -56,10 +50,18 @@ export const protonRun = async (command) => { const env = { ...process.env, + ...getConfig(), STEAM_COMPAT_CLIENT_INSTALL_PATH: path.join(os.homedir(), ".steam", "root"), STEAM_COMPAT_DATA_PATH: compatdataPath, }; + const vortexRuntime = env?.VORTEX_RUNTIME; + let fullCommand = `${protonPath} run ${command}`; + + if (vortexRuntime) { + fullCommand = `${vortexRuntime}/run -- ${fullCommand}`; + } + try { const { stdout, stderr } = await execAsync(fullCommand, { env }); console.log(stdout); diff --git a/lib/vortex.js b/lib/vortex.js index a8d498b..14f2f21 100644 --- a/lib/vortex.js +++ b/lib/vortex.js @@ -6,8 +6,21 @@ import { pipeline } from "stream"; import { BASE_DIR } from "./config.js"; import { protonRun } from "./proton.js"; +import vortexIcon from "../assets/vortex.ico"; +import vortexDesktop from "../assets/vortex.desktop"; + const pipelineAsync = promisify(pipeline); +const VORTEX_DIR = path.join( + BASE_DIR, + "compatdata", + "pfx", + "drive_c", + "Program Files", + "Black Tree Gaming Ltd", + "Vortex" +); + export const downloadVortex = async (downloadUrl) => { const installersPath = path.join(BASE_DIR, "vortex-installers"); @@ -44,20 +57,42 @@ export const installVortex = async (vortexInstaller) => { await protonRun(vortexInstallerPath); }; -export const launchVortex = async (args) => { - const vortexExe = path.join( - BASE_DIR, - "compatdata", - "pfx", - "drive_c", - "Program Files", - "Black Tree Gaming Ltd", - "Vortex", - "Vortex.exe" +export const setupVortexDesktop = () => { + const applicationsPath = path.join( + process.env.HOME, + ".local", + "share", + "applications" ); + const iconPath = path.join(applicationsPath, "vortex.ico"); + const desktopPath = path.join(applicationsPath, "vortex.desktop"); + + // Create applicationsPath directory if it doesn't exist + if (!existsSync(applicationsPath)) { + mkdirSync(applicationsPath, { recursive: true }); + } + + // Write icon and desktop files + const iconData = Buffer.from(vortexIcon.split(",")[1], "base64"); + const desktopData = vortexDesktop + .replace("%%VORTEX_PATH%%", VORTEX_DIR) + .replace("%%VORTEX_EXEC%%", `"${__filename}" launchVortex -- -d %u`) + .replace("%%VORTEX_ICON%%", iconPath); + + fs.writeFileSync(iconPath, iconData); + fs.writeFileSync(desktopPath, desktopData, "utf-8"); + fs.chmodSync(desktopPath, "755"); +}; + +export const launchVortex = async (args) => { + const vortexExe = path.join(VORTEX_DIR, "Vortex.exe"); let fullCommand = `"${vortexExe}"`; - if (args && (args !== "-d " || args !== "-i ")) { - fullCommand += ` ${args}`; + + if (["-d", "-i"].includes(args?.[0]) && args?.length === 1) { + console.info(`No url provided, ignoring ${args[0]}`); + } else { + fullCommand += ` ${args.join(" ")}`; } + await protonRun(fullCommand); }; diff --git a/main.js b/main.js index a1a9f05..1f20f3b 100644 --- a/main.js +++ b/main.js @@ -1,7 +1,27 @@ import { program } from "commander"; import { downloadProton, setProton } from "./lib/proton.js"; -import { downloadVortex, installVortex, launchVortex } from "./lib/vortex.js"; +import { + downloadVortex, + installVortex, + launchVortex, + setupVortexDesktop, +} from "./lib/vortex.js"; + +import { getConfig, setConfig } from "./lib/config.js"; + +const originalEmit = process.emit; +// eslint-disable-next-line +process.emit = function (name, data, ...args) { + if ( + name === `warning` && + typeof data === `object` && + data.name === `ExperimentalWarning` + ) { + return false; + } + return originalEmit.apply(process, arguments); +}; program.version("0.0.1"); @@ -40,4 +60,26 @@ program await launchVortex(args); }); +program + .command("setupVortexDesktop") + .description("Setup .desktop entry for Vortex") + .action(() => { + setupVortexDesktop(); + }); + +program + .command("getConfig") + .description("Output config") + .action(() => { + console.log(JSON.stringify(getConfig(), null, 2)); + }); + +program + .command("setConfig [value]") + .description("Set config key") + .action((key, value) => { + setConfig(key, value); + console.log(JSON.stringify(getConfig(), null, 2)); + }); + program.parse(process.argv);