119 lines
3.8 KiB
JavaScript
119 lines
3.8 KiB
JavaScript
// About me: Writes the missing Steam libraryfolders.vdf into a Wine prefix from
|
|
// the Linux side, before Vortex is ever started. Vortex core scans Steam at
|
|
// startup and reads C:\Program Files (x86)\Steam\config\libraryfolders.vdf; when
|
|
// SteamTinkerLaunch has not created it, the unhandled ENOENT shows up as
|
|
// "unrecoverable error" and Vortex dies. Running this once repairs the prefix.
|
|
//
|
|
// Usage: node scripts/repair-prefix.mjs [--prefix <pfx>] [--force] [--dry-run]
|
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from 'node:fs';
|
|
import { homedir } from 'node:os';
|
|
import { resolve } from 'node:path';
|
|
|
|
import { findSteamLibraries, findSteamRoots } from '../src/discovery.ts';
|
|
import {
|
|
PREFIX_STEAM_DIR,
|
|
buildShimVdf,
|
|
dedupeByCanonicalPath,
|
|
ensureSteamShim,
|
|
} from '../src/steamShim.ts';
|
|
|
|
const DEFAULT_PREFIX = resolve(homedir(), '.config/steamtinkerlaunch/vortex/compatdata/pfx');
|
|
|
|
function parseArgs(argv) {
|
|
const args = { prefix: DEFAULT_PREFIX, force: false, dryRun: false };
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
if (argv[i] === '--prefix') {
|
|
args.prefix = resolve(argv[i + 1] ?? '');
|
|
i += 1;
|
|
} else if (argv[i] === '--force') {
|
|
args.force = true;
|
|
} else if (argv[i] === '--dry-run') {
|
|
args.dryRun = true;
|
|
} else {
|
|
throw new Error(`unknown argument: ${argv[i]}`);
|
|
}
|
|
}
|
|
return args;
|
|
}
|
|
|
|
/**
|
|
* Resolves the paths Vortex would see inside the prefix to real Linux paths:
|
|
* Z: is the Linux root, C: is the prefix's drive_c.
|
|
*/
|
|
function toRealPath(prefix, winPath) {
|
|
const normalised = winPath.replace(/\\/g, '/');
|
|
if (/^[zZ]:/.test(normalised)) {
|
|
return normalised.slice(2) || '/';
|
|
}
|
|
if (/^[cC]:/.test(normalised)) {
|
|
return resolve(prefix, 'drive_c', normalised.slice(3));
|
|
}
|
|
return normalised;
|
|
}
|
|
|
|
function prefixFileAccess(prefix) {
|
|
return {
|
|
exists: (p) => existsSync(toRealPath(prefix, p)),
|
|
readFile: (p) => readFileSync(toRealPath(prefix, p), 'utf8'),
|
|
readDir: (p) => {
|
|
try {
|
|
return readdirSync(toRealPath(prefix, p));
|
|
} catch {
|
|
return [];
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
const args = parseArgs(process.argv.slice(2));
|
|
|
|
if (!existsSync(args.prefix)) {
|
|
console.error(`prefix not found: ${args.prefix}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const files = prefixFileAccess(args.prefix);
|
|
|
|
// Only the Wine-side (Z:) view is useful here: the shim has to contain paths
|
|
// Vortex can open from inside the prefix.
|
|
const libraries = dedupeByCanonicalPath(
|
|
[
|
|
...new Set(
|
|
findSteamRoots(files)
|
|
.filter((root) => root.startsWith('Z:'))
|
|
.flatMap((root) => findSteamLibraries(files, root)),
|
|
),
|
|
],
|
|
(candidate) => realpathSync(toRealPath(args.prefix, candidate)),
|
|
);
|
|
|
|
console.log(`prefix: ${args.prefix}`);
|
|
console.log(`steam dir: ${toRealPath(args.prefix, PREFIX_STEAM_DIR)}`);
|
|
console.log(`libraries: ${libraries.length === 0 ? '(none found)' : ''}`);
|
|
for (const library of libraries) {
|
|
console.log(` ${library}`);
|
|
}
|
|
|
|
if (args.dryRun) {
|
|
console.log('--- shim that would be written ---');
|
|
console.log(buildShimVdf(libraries));
|
|
process.exit(0);
|
|
}
|
|
|
|
const writer = {
|
|
makeDir: (p) => mkdirSync(toRealPath(args.prefix, p), { recursive: true }),
|
|
writeFile: (p, contents) => writeFileSync(toRealPath(args.prefix, p), contents, 'utf8'),
|
|
};
|
|
|
|
const result = ensureSteamShim(files, writer, libraries, { overwrite: args.force });
|
|
console.log(`result: ${result.action} -> ${toRealPath(args.prefix, result.path)}`);
|
|
|
|
if (result.action === 'skipped') {
|
|
console.error(
|
|
libraries.length === 0
|
|
? 'no Steam libraries found under /home; pass --prefix for the right prefix or check your Steam install'
|
|
: `no Steam directory at ${toRealPath(args.prefix, PREFIX_STEAM_DIR)}; is this the Vortex prefix?`,
|
|
);
|
|
process.exit(1);
|
|
}
|