From 9d128f799462b25ece39c5ce401eb7a796788c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathis=20Dr=C3=B6ge?= Date: Sun, 7 Jan 2024 14:20:47 +0100 Subject: [PATCH] [FIX] Only try to do EOS operations on a path if there's actually a Wineprefix there (#3410) Previously, trying to enable/disable/check the status of the EOS overlay for a game with an invalid prefix directory would lead to an error message. We're now handling this correctly (not enabling/disabling it, always reporting that it's disabled) --- .../storeManagers/legendary/commands/base.ts | 6 ++ .../legendary/commands/eos_overlay.ts | 4 +- .../legendary/eos_overlay/eos_overlay.ts | 79 +++++++++++-------- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/backend/storeManagers/legendary/commands/base.ts b/src/backend/storeManagers/legendary/commands/base.ts index eff5e12801..59aa1e5e22 100644 --- a/src/backend/storeManagers/legendary/commands/base.ts +++ b/src/backend/storeManagers/legendary/commands/base.ts @@ -1,6 +1,7 @@ import { z } from 'zod' import path from 'path' import { hasGame } from '../library' +import { existsSync } from 'graceful-fs' export const LegendaryAppName = z .string() @@ -35,3 +36,8 @@ export type URL = z.infer // FIXME: This doesn't feel right export const URI = z.union([Path, URL]) export type URI = z.infer + +export const ValidWinePrefix = Path.refine((potPath) => + existsSync(path.join(potPath, 'user.reg')) +).brand('ValidWinePrefix') +export type ValidWinePrefix = z.infer diff --git a/src/backend/storeManagers/legendary/commands/eos_overlay.ts b/src/backend/storeManagers/legendary/commands/eos_overlay.ts index b0659ff14e..0183e79e46 100644 --- a/src/backend/storeManagers/legendary/commands/eos_overlay.ts +++ b/src/backend/storeManagers/legendary/commands/eos_overlay.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import { LegendaryAppName, NonEmptyString, Path } from './base' +import { LegendaryAppName, NonEmptyString, Path, ValidWinePrefix } from './base' const EosOverlayAction = z.enum([ 'install', @@ -15,7 +15,7 @@ interface EosOverlayCommand { subcommand: 'eos-overlay' action: EosOverlayAction '--path'?: Path - '--prefix'?: Path + '--prefix'?: ValidWinePrefix '--app'?: LegendaryAppName '--bottle'?: NonEmptyString } diff --git a/src/backend/storeManagers/legendary/eos_overlay/eos_overlay.ts b/src/backend/storeManagers/legendary/eos_overlay/eos_overlay.ts index 7740e5988e..169a0a2b23 100644 --- a/src/backend/storeManagers/legendary/eos_overlay/eos_overlay.ts +++ b/src/backend/storeManagers/legendary/eos_overlay/eos_overlay.ts @@ -1,18 +1,19 @@ -import { gameManagerMap } from '../../index' -import { callAbortController } from '../../../utils/aborthandler/aborthandler' import { dialog } from 'electron' import { existsSync, readFileSync } from 'graceful-fs' import { t } from 'i18next' import { join } from 'path' -import { toolsPath, isLinux, legendaryConfigPath } from '../../../constants' -import { logError, LogPrefix, logWarning } from '../../../logger/logger' -import { runRunnerCommand as runLegendaryCommand } from '../library' -import { verifyWinePrefix } from '../../../launcher' -import { setCurrentDownloadSize } from '../games' -import { Path } from '../commands/base' -import { LegendaryCommand } from '../commands' +import { toolsPath, isLinux, legendaryConfigPath } from 'backend/constants' +import { logError, LogPrefix, logWarning } from 'backend/logger/logger' +import { callAbortController } from 'backend/utils/aborthandler/aborthandler' import { sendGameStatusUpdate } from 'backend/utils' +import { gameManagerMap } from '../..' +import { LegendaryCommand } from '../commands' +import { Path, ValidWinePrefix } from '../commands/base' +import { setCurrentDownloadSize } from '../games' +import { runRunnerCommand as runLegendaryCommand } from '../library' + +import type { Runner } from 'common/types' const currentVersionPath = () => join(legendaryConfigPath, 'overlay_version.json') @@ -187,14 +188,6 @@ async function remove(): Promise { async function enable( appName: string ): Promise<{ wasEnabled: boolean; installNow?: boolean }> { - let prefix = '' - if (isLinux) { - const gameSettings = await gameManagerMap['legendary'].getSettings(appName) - await verifyWinePrefix(gameSettings) - const { winePrefix, wineVersion } = gameSettings - prefix = - wineVersion.type === 'proton' ? join(winePrefix, 'pfx') : winePrefix - } if (!isInstalled()) { const { response } = await dialog.showMessageBox({ title: t('setting.eosOverlay.notInstalledTitle', 'Overlay not installed'), @@ -208,11 +201,16 @@ async function enable( return { wasEnabled: false, installNow: response === 0 } } + const prefix = await getWinePrefixFolder(appName) + // Can't install the overlay if we don't have a valid prefix + // FIXME: Notify the user about this + if (prefix === false) return { wasEnabled: false } + const command: LegendaryCommand = { subcommand: 'eos-overlay', action: 'enable' } - if (prefix) command['--prefix'] = Path.parse(prefix) + if (prefix) command['--prefix'] = prefix await runLegendaryCommand(command, { abortId: eosOverlayAppName, @@ -223,19 +221,15 @@ async function enable( } async function disable(appName: string) { - let prefix = '' - if (isLinux) { - const { winePrefix, wineVersion } = - await gameManagerMap['legendary'].getSettings(appName) - prefix = - wineVersion.type === 'proton' ? join(winePrefix, 'pfx') : winePrefix - } + const prefix = await getWinePrefixFolder(appName) + // If we don't have a valid prefix anymore, we have nothing to disable + if (prefix === false) return const command: LegendaryCommand = { subcommand: 'eos-overlay', action: 'disable' } - if (prefix) command['--prefix'] = Path.parse(prefix) + if (prefix) command['--prefix'] = prefix await runLegendaryCommand(command, { abortId: eosOverlayAppName, @@ -252,22 +246,17 @@ function isInstalled() { * @param appName required on Linux, does nothing on Windows * @returns Enabled = True; Disabled = False */ -async function isEnabled(appName?: string) { +async function isEnabled(appName?: string): Promise { let enabled = false - let prefix = '' - if (isLinux && appName) { - const { winePrefix, wineVersion } = - await gameManagerMap['legendary'].getSettings(appName) - prefix = - wineVersion.type === 'proton' ? join(winePrefix, 'pfx') : winePrefix - } + const prefix = await getWinePrefixFolder(appName) + if (prefix === false) return false const command: LegendaryCommand = { subcommand: 'eos-overlay', action: 'info' } - if (prefix) command['--prefix'] = Path.parse(prefix) + if (prefix) command['--prefix'] = prefix await runLegendaryCommand(command, { abortId: eosOverlayAppName, @@ -282,6 +271,26 @@ async function isEnabled(appName?: string) { return enabled } +/** + * Returns the path to the "real" Wineprefix folder (where "drive_c" and "user.reg" is) for a game + * @returns null if a prefix can't be returned (we're not on Linux / don't have an AppName) + * @returns false if parsing the prefix path failed (in other words there is a prefix path set, but it doesn't contain a valid prefix) + * @returns ValidWinePrefix (a folder that is verified to contain a Wineprefix) otherwise + */ +async function getWinePrefixFolder( + appName?: string, + runner: Runner = 'legendary' +): Promise { + if (!isLinux || !appName) return null + + const { winePrefix, wineVersion } = + await gameManagerMap[runner].getSettings(appName) + const prefixPath = + wineVersion.type === 'proton' ? join(winePrefix, 'pfx') : winePrefix + const maybePrefix = ValidWinePrefix.safeParse(prefixPath) + return maybePrefix.success ? maybePrefix.data : false +} + export { getStatus, getLatestVersion,