cleanup comments, update translations, bump to 0.4.1

This commit is contained in:
olcxja 2026-10-05 15:42:44 +02:00
commit 7d8ab87eee
28 changed files with 412 additions and 1114 deletions

View file

@ -7,9 +7,8 @@ const { rewireSymlinks } = require('./symlinks')
const { removeTree, treeSize } = require('./fsutil')
const { normalizePathName } = require('./names')
// An app path stored relative to its PREFIX ROOT ("drive_c/..." for plain
// wine, "pfx/drive_c/..." for Proton-managed prefixes): wine resolves it
// under C:\, everything else is FILES-relative or absolute.
// An app path stored relative to its prefix root ("drive_c/..." or
// "pfx/drive_c/..."); everything else is FILES-relative or absolute.
const PREFIX_REL_RE = /^(pfx[/\\])?drive_c([/\\]|$)/i
const isPrefixRel = v => typeof v === 'string' && PREFIX_REL_RE.test(v)
@ -48,12 +47,8 @@ function prefixExists(name) {
}
}
// Every Windows C: drive a prefix has: plain wine keeps one at the top
// (WINEPREFIX=<prefix>), legacy Proton-managed prefixes nest one under pfx/
// (WINEPREFIX=<prefix>/pfx). A prefix used with BOTH wine kinds has both, and
// an import may come from either one - so callers that can accept several get
// them all, top level first. A unified prefix (pfx -> .) reaches the same
// drive twice; it is reported once.
// Every Windows C: drive a prefix has (top-level for plain wine, under pfx/
// for Proton); a unified prefix reports its single drive once.
function prefixDriveRoots(name) {
const root = prefixPath(name)
const found = []
@ -79,22 +74,9 @@ function lstatOrNull(p) {
}
}
// Wine keeps its C: drive at <prefix>/drive_c, Proton always runs its prefix
// from <compatdata>/pfx - and PLWL points compatdata at the launcher's prefix
// folder, so without a link a prefix used with both wine kinds grows TWO C:
// drives with separate registries, saves and installed DLLs. Making `pfx` a
// relative symlink back to the prefix itself collapses them: Proton's
// <compatdata>/pfx resolves to the prefix root, the exact folder wine uses.
// Every walker PLWL owns (fsutil, symlinks, LAN sync) treats symlinks as
// links and never follows them, so the self-reference is safe.
//
// Only a prefix that has no pfx AT ALL gets the link - that covers a fresh
// prefix and a plain-wine prefix first used through Proton. An existing
// layout is left exactly as it is: a prefix from an older PLWL keeps its real
// pfx/ folder (and here the two wine kinds keep two C: drives), and a prefix
// with both real drive_c trees keeps them - moving Windows installs around
// behind the user's back loses data and breaks the absolute/relative links
// inside them.
// Without a link, a prefix used with both wine kinds grows two C: drives;
// `pfx` -> prefix root collapses them. Only a prefix with no pfx at all gets
// the link - an existing layout is left exactly as it is.
function ensureUnifiedPrefix(name) {
if (!name) return false
const pfx = path.join(prefixPath(name), 'pfx')
@ -108,12 +90,8 @@ function ensureUnifiedPrefix(name) {
}
}
// True when a path IS one of a prefix's own roots: the prefix folder, its
// pfx/ or either drive_c. A copy/move import of such a folder would move the
// prefix's C: drive (or the whole prefix) into files/apps, gutting it - the
// "import from a prefix" flow is the one that works in place. Both drive
// layouts are checked literally, not through prefixDriveRoots(): a prefix used
// with wine AND Proton has a drive_c each and BOTH are off limits.
// True when a path IS one of a prefix's own roots (folder, pfx/, drive_c):
// importing one would gut the prefix.
function isPrefixRoot(p) {
if (!p) return false
const abs = path.resolve(String(p))
@ -140,9 +118,7 @@ function createPrefix(name) {
return { ok: true, name: clean }
}
// A prefix is a whole windows drive - removal is measured before it starts
// (staging) so the UI can show a bar that actually moves while the tree is
// walked file by file (removeTree), instead of one blocking rmSync.
// Removal is measured (staging) so the UI shows a bar that moves.
async function deletePrefix(name, onProgress) {
if (String(name).toLowerCase() === MAIN.toLowerCase()) return { ok: false, error: 'isMain' }
const defs = listPrefixes()
@ -158,19 +134,13 @@ async function deletePrefix(name, onProgress) {
return { ok: true }
}
// A prefix that arrived from another machine (or a launcher that was moved) is
// full of absolute symlinks into its wine/proton build - fonts, built-in
// program stubs, dlls. Rewire them to relative paths so the prefix keeps
// working here and stays relocatable (same healing wine builds get on import).
// Rewire absolute links to relative so a moved prefix keeps working.
async function healPrefix(name) {
if (!name) return 0
return rewireSymlinks(prefixPath(name), [DIRS.FILES])
}
// Renaming a prefix moves its folder on disk and follows the name into the
// registry (default included) and every app that runs in it. Prefix imports
// keep their stored paths (they are relative to the prefix root), so only the
// reference changes - the files move with the folder.
// Rename moves the folder and follows the name through the registry.
function renamePrefix(oldName, newName) {
const clean = normalizePathName(newName)
if (!clean) return { ok: false, error: 'invalidName' }
@ -180,8 +150,7 @@ function renamePrefix(oldName, newName) {
if (entry.locked || String(oldName).toLowerCase() === MAIN.toLowerCase()) return { ok: false, error: 'isMain' }
if (clean === oldName) return { ok: true, name: clean, games: 0 }
if (defs.prefixes.some(p => p.name === clean) || fs.existsSync(prefixPath(clean))) return { ok: false, error: 'alreadyExists' }
// a registered prefix whose folder wine has not created yet can still be
// renamed - only the registry entry exists
// a registered prefix without a folder yet can still be renamed
if (fs.existsSync(prefixPath(oldName))) {
try {
fs.renameSync(prefixPath(oldName), prefixPath(clean))