cleanup comments, update translations, bump to 0.4.1
This commit is contained in:
parent
9edcc9841b
commit
7d8ab87eee
28 changed files with 412 additions and 1114 deletions
|
|
@ -13,9 +13,8 @@ const { copyTree, treeSize } = require('./fsutil')
|
|||
// Directories that typically hold installers/redistributables, not the game.
|
||||
const SKIPPED_DIRS = new Set(['redist', 'directx', '_commonredist', 'support', 'dotnet', 'vc'])
|
||||
|
||||
// The copy itself lives in fsutil (copyTree): symlinks recreated, permissions
|
||||
// carried over, byte-level progress. moveFolder only falls back to it across
|
||||
// filesystems; a same-device rename reports nothing (nothing is copied).
|
||||
// The copy itself lives in fsutil.copyTree; moveFolder falls back to it
|
||||
// across filesystems only.
|
||||
function moveFolder(src, dst, onProgress) {
|
||||
const fallback = () => copyTree(src, dst, onProgress).then(() => fsp.rm(src, { recursive: true, force: true }))
|
||||
if (path.parse(src).root !== path.parse(dst).root) return fallback()
|
||||
|
|
@ -23,16 +22,13 @@ function moveFolder(src, dst, onProgress) {
|
|||
}
|
||||
|
||||
async function importGame(srcDir, mode, onProgress) {
|
||||
// a prefix (or its drive_c / pfx) is not an app folder: copy/move would take
|
||||
// the prefix's C: drive with it and leave the prefix gutted
|
||||
// a prefix (or its drive_c/pfx) is not an app folder
|
||||
if (isPrefixRoot(srcDir)) return { ok: false, error: 'prefix-root' }
|
||||
const name = path.basename(srcDir)
|
||||
// Validate before touching the user's files: refuse an import without any
|
||||
// .exe (and don't half-move a folder we'd then have to roll back).
|
||||
// validate before touching anything: no .exe -> refuse
|
||||
const preExe = await findDefaultExe(srcDir)
|
||||
if (!preExe) return { ok: false, error: 'no-exe-found' }
|
||||
// The id doubles as the imported folder name, so generate it first and
|
||||
// create files/apps/<id> - no dedupe suffixes, ids can never collide.
|
||||
// the id doubles as the imported folder name
|
||||
const id = newGameId()
|
||||
let workdir = srcDir
|
||||
if (mode === 'copy' || mode === 'move') {
|
||||
|
|
@ -47,9 +43,7 @@ async function importGame(srcDir, mode, onProgress) {
|
|||
await copyTree(srcDir, dst, p => emit('copy', { files: p.done, bytes: p.bytes, totalFiles: total.files, totalBytes: total.bytes }))
|
||||
} else await moveFolder(srcDir, dst, onProgress)
|
||||
workdir = dst
|
||||
// anything the game linked to INSIDE the launcher data (its own folder, an
|
||||
// icon, a shared prefix file) becomes a relative link, so the imported app
|
||||
// survives the launcher being moved - the same healing wine builds get
|
||||
// in-data link targets become relative, so the import survives a move
|
||||
try {
|
||||
await rewireSymlinks(dst, [DIRS.FILES])
|
||||
} catch {}
|
||||
|
|
@ -60,8 +54,7 @@ async function importGame(srcDir, mode, onProgress) {
|
|||
}
|
||||
const game = newGame({ id, name, exePath, workdir, mode })
|
||||
game.imported = mode === 'copy' || mode === 'move'
|
||||
// the exe was picked automatically -> grab its icon right away; a missing
|
||||
// icon is not an import failure (game can still get one from config)
|
||||
// auto-picked exe -> grab its icon; a missing icon is not a failure
|
||||
const icon = path.join(DIRS.DATA, 'icons', id, 'icon.ico')
|
||||
if (extractIconFromExe(resolvePortablePath(exePath), icon).ok) {
|
||||
game.iconSource = icon
|
||||
|
|
@ -69,22 +62,14 @@ async function importGame(srcDir, mode, onProgress) {
|
|||
return addGame(game)
|
||||
}
|
||||
|
||||
// Import from a wine prefix: the folder is picked under the prefix's
|
||||
// drive_c and left in place - the game paths are stored relative to the
|
||||
// prefix, so wine sees the app exactly as if it had been installed
|
||||
// normally into C:\...
|
||||
// Import from a prefix: the folder stays in place, paths stored prefix-relative.
|
||||
async function importGameFromPrefix(prefix, srcDir, onProgress) {
|
||||
if (!listPrefixes().prefixes.some(p => p.name === prefix)) return { ok: false, error: 'prefix-not-found' }
|
||||
// The folder must live inside one of the prefix's C: drives so the C:\
|
||||
// mapping holds - drive_c sits at the prefix top for plain wine, under pfx/
|
||||
// for Proton-managed prefixes, and a prefix used with BOTH kinds has both.
|
||||
// Every existing drive_c counts; the stored path keeps its drive_c segment
|
||||
// either way, so it resolves in the layout the app will run in.
|
||||
// the folder must live inside one of the prefix's C: drives
|
||||
const driveRoots = prefixDriveRoots(prefix)
|
||||
if (!driveRoots.length) return { ok: false, error: 'no-drive-c' }
|
||||
const src = path.resolve(String(srcDir))
|
||||
// realpath on both sides: a unified prefix reaches drive_c through the pfx
|
||||
// symlink too, and that path is the same folder (see ensureUnifiedPrefix)
|
||||
// realpath both sides: a unified prefix reaches drive_c through pfx too
|
||||
let realSrc
|
||||
try {
|
||||
realSrc = fs.realpathSync(src)
|
||||
|
|
@ -108,8 +93,7 @@ async function importGameFromPrefix(prefix, srcDir, onProgress) {
|
|||
const game = newGame({ id, name: path.basename(src), exePath: preExe, workdir: src, mode: 'prefix' })
|
||||
game.importSource = 'prefix'
|
||||
game.prefix = prefix
|
||||
// exe picked automatically -> grab its icon right away; a missing icon is
|
||||
// not an import failure (game can still get one from config)
|
||||
// auto-picked exe -> grab its icon; a missing icon is not a failure
|
||||
const icon = path.join(DIRS.DATA, 'icons', id, 'icon.ico')
|
||||
if (extractIconFromExe(resolvePortablePath(preExe), icon).ok) {
|
||||
game.iconSource = icon
|
||||
|
|
@ -117,9 +101,7 @@ async function importGameFromPrefix(prefix, srcDir, onProgress) {
|
|||
return addGame(game)
|
||||
}
|
||||
|
||||
// Largest .exe in the tree, skipping redistributable folders. Files are
|
||||
// stat'ed in parallel and folders walked breadth-first so big games don't
|
||||
// turn the picker into a sequential crawl.
|
||||
// Largest .exe in the tree, skipping redistributable folders.
|
||||
async function findDefaultExe(dir) {
|
||||
let best = ''
|
||||
let bestSize = -1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue