PLWL/src/paths.js
2026-09-26 11:40:04 +02:00

98 lines
3.2 KiB
JavaScript

'use strict'
const path = require('path')
const fs = require('fs')
// Packaged builds run from <portable root>/olcxja.plwl/resources/app.asar:
// the portable layout (<root>/files) is two levels above the resources dir
// and files bundled with the release are unpacked next to the asar.
function computeRoots() {
const m = __dirname.match(/^(.*)[/\\]app\.asar([/\\]|$)/)
if (m) {
const RESOURCES = path.resolve(m[1])
const APP_ROOT = path.resolve(RESOURCES, '..', '..')
return {
RESOURCES,
APP_ROOT,
// FILES = BUILD/files, so BUILD is the portable root itself.
BUILD: process.env.PLWL_BUILD_DIR || APP_ROOT,
// Bundled release files live inside the asar (Electron's patched fs
// can read them directly from the archive).
BUNDLED: fs.existsSync(path.join(RESOURCES, 'app.asar', 'files', 'translations'))
? path.join(RESOURCES, 'app.asar', 'files')
: path.join(APP_ROOT, 'files')
}
}
const APP_ROOT = path.resolve(__dirname, '..')
return {
RESOURCES: APP_ROOT,
APP_ROOT,
BUILD: process.env.PLWL_BUILD_DIR || path.join(APP_ROOT, 'build'),
BUNDLED: path.join(APP_ROOT, 'files')
}
}
const { RESOURCES, APP_ROOT, BUILD, BUNDLED } = computeRoots()
const FILES = path.join(BUILD, 'files')
const DATA = path.join(FILES, 'data')
const DIRS = {
BUILD,
APP_ROOT,
FILES,
DATA,
WINES: path.join(FILES, 'wines'),
PREFIXES: path.join(FILES, 'wineprefixes'),
APPS: path.join(FILES, 'apps'),
TRANSLATIONS: path.join(FILES, 'translations'),
BUNDLED_TRANSLATIONS: path.join(BUNDLED, 'translations'),
FAKE_STEAM: path.join(DATA, 'fake-steam')
}
function ensureDirs() {
for (const dir of [FILES, DATA, DIRS.WINES, DIRS.PREFIXES, DIRS.APPS, DIRS.TRANSLATIONS, DIRS.FAKE_STEAM]) {
fs.mkdirSync(dir, { recursive: true })
}
if (DIRS.TRANSLATIONS !== DIRS.BUNDLED_TRANSLATIONS) {
try {
for (const f of fs.readdirSync(DIRS.BUNDLED_TRANSLATIONS)) {
const dst = path.join(DIRS.TRANSLATIONS, f)
if (!fs.existsSync(dst)) fs.copyFileSync(path.join(DIRS.BUNDLED_TRANSLATIONS, f), dst)
}
} catch {}
}
}
// Portable paths: anything stored under FILES/ goes into the registry as a
// path RELATIVE to FILES (e.g. apps/<id>/game.exe), so moving the whole
// launcher folder keeps the library working. Absolute paths outside FILES
// (games left in place) are stored as-is. Resolve on read, normalize on write.
function toPortablePath(p) {
if (typeof p !== 'string' || !p || !path.isAbsolute(p)) return p
const rel = path.relative(FILES, p)
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return p
return rel
}
function resolvePortablePath(p) {
if (typeof p !== 'string' || !p || path.isAbsolute(p)) return p
return path.join(FILES, p)
}
function readJson(file, fallback) {
try {
return JSON.parse(fs.readFileSync(file, 'utf8'))
} catch {
return fallback
}
}
function writeJson(file, data) {
fs.mkdirSync(path.dirname(file), { recursive: true })
const tmp = file + '.tmp'
fs.writeFileSync(tmp, JSON.stringify(data, null, 2))
fs.renameSync(tmp, file)
}
module.exports = { RESOURCES, APP_ROOT, BUILD, FILES, DATA, DIRS, ensureDirs, readJson, writeJson, toPortablePath, resolvePortablePath }