diff options
| author | Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> | 2026-09-10 15:40:57 -0400 |
|---|---|---|
| committer | Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> | 2026-09-10 15:40:57 -0400 |
| commit | 6c22d5ee540bc4c71a72c1e83ffc40e6b0494bc1 (patch) | |
| tree | 8b985ac9b1a7c867aa0d153ace943009eab84adb /src | |
| parent | 2a000183846522d02df182d797c519aebbc1d9da (diff) | |
| download | decky-lsfg-vk-6c22d5ee540bc4c71a72c1e83ffc40e6b0494bc1.tar.gz decky-lsfg-vk-6c22d5ee540bc4c71a72c1e83ffc40e6b0494bc1.zip | |
refactor: use explicit direct flatpak target wrapper
Diffstat (limited to 'src')
| -rw-r--r-- | src/utils/steamLaunchOptions.ts | 114 |
1 files changed, 48 insertions, 66 deletions
diff --git a/src/utils/steamLaunchOptions.ts b/src/utils/steamLaunchOptions.ts index 82cf116..08ae136 100644 --- a/src/utils/steamLaunchOptions.ts +++ b/src/utils/steamLaunchOptions.ts @@ -1,5 +1,3 @@ -import type { TargetTransport } from "../api/lsfgApi"; - const DEFAULT_WRAPPER_PATH = "~/.lsfg"; const COMMAND_TOKEN = "%command%"; @@ -31,7 +29,6 @@ export interface SteamLaunchOptionsSnapshot { } export interface WrapperIntegrationResult { snapshot: SteamLaunchOptionsSnapshot; - originalExecutable?: string; commandTokenAdded: boolean; changed: boolean; } @@ -162,14 +159,28 @@ const commandIndex = (tokens: readonly LaunchToken[]) => tokens.findIndex((token const isAssignment = (token: LaunchToken) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(token.value); const isLegacyToken = (value: string) => LEGACY_WRAPPER_TOKENS.has(value) || LEGACY_ABSOLUTE_WRAPPER.test(value); const isWrapperToken = (value: string, wrapperPath: string) => decodeToken(value) === wrapperPath || isLegacyWrapperToken(value); -const usesShortcutTarget = (nonSteam: boolean, transport: TargetTransport) => nonSteam && transport.kind === "flatpak"; -function selectFlatpakExecutable(transport: TargetTransport, candidate?: string | null): string | undefined { - if (transport.kind !== "flatpak") return undefined; - const value = candidate?.trim() ? decodeToken(candidate.trim()) : ""; - if (value === "flatpak") return "/usr/bin/flatpak"; - if (value === "/usr/bin/flatpak") return value; - return undefined; +function flatpakExecutable(value: string): string | undefined { + const decoded = decodeToken(value.trim()); + return decoded === "flatpak" || decoded === "/usr/bin/flatpak" ? "/usr/bin/flatpak" : undefined; +} + +function wrappedFlatpakExecutable(target: string, wrapperPath: string, includeLegacy = true): string | undefined { + const tokens = tokenize(target); + if (tokens.length !== 2) return undefined; + const wrapper = tokens[0].value; + if (wrapper !== wrapperPath && !(includeLegacy && isLegacyToken(wrapper))) return undefined; + return flatpakExecutable(tokens[1].value); +} + +function directFlatpakExecutable(target: string, wrapperPath: string): string | undefined { + const tokens = tokenize(target); + if (tokens.length === 1) return flatpakExecutable(tokens[0].value); + return wrappedFlatpakExecutable(target, wrapperPath); +} + +function managedFlatpakTarget(wrapperPath: string, executable: string): string { + return `${wrapperPath} "${executable.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; } export const normalizeLaunchOptions = (options: string) => serialize(tokenize(options)); @@ -268,24 +279,14 @@ export function hasWrapperLaunchIntegration(options: string, wrapperPath = DEFAU export function isWrapperIntegrationInstalled( steam: SteamLaunchOptionsSnapshot, nonSteam: boolean, - transport: TargetTransport, + directFlatpak = false, wrapperPath = DEFAULT_WRAPPER_PATH, ): boolean { - return usesShortcutTarget(nonSteam, transport) - ? steam.target === wrapperPath - : hasWrapperLaunchIntegration(steam.options, wrapperPath); -} - -export function assertKnownShortcutTarget( - steam: SteamLaunchOptionsSnapshot, - nonSteam: boolean, - transport: TargetTransport, - wrapperPath: string, - originalExecutable?: string | null, -): void { - if (usesShortcutTarget(nonSteam, transport) && steam.target === wrapperPath && !originalExecutable) { - throw new Error("Managed shortcut Target has no saved original executable"); + if (nonSteam && directFlatpak) { + const tokens = tokenize(steam.target); + return tokens.length === 2 && tokens[0].value === wrapperPath && flatpakExecutable(tokens[1].value) !== undefined; } + return hasWrapperLaunchIntegration(steam.options, wrapperPath); } const queues = new Map<string, Promise<unknown>>(); @@ -328,7 +329,7 @@ async function writeVerified( read: (value: SteamLaunchOptionsSnapshot) => string, message: string, ): Promise<SteamLaunchOptionsSnapshot> { - const normalized = read === readOptions ? normalizeLaunchOptions : (value: string) => value; + const normalized = read === readOptions ? normalizeLaunchOptions : (value: string) => normalizeLaunchOptions(value); try { await write(next); return await waitFor(appId, nonSteam, (value) => normalized(read(value)) === normalized(next), message); @@ -379,16 +380,11 @@ export function installWrapperIntegration( nonSteam: boolean, wrapperPath: string, commandTokenAdded = false, - transport: TargetTransport = { kind: "host" }, - originalExecutable?: string, + directFlatpak = false, ): Promise<WrapperIntegrationResult> { return queued(appId, nonSteam, async () => { let current = await readSteamLaunchOptions(appId, nonSteam); - if (usesShortcutTarget(nonSteam, transport)) { - if (!current.target) throw new Error("Steam shortcut Target is empty; refusing to replace it"); - if (current.target !== wrapperPath && isWrapperToken(current.target, wrapperPath)) { - throw new Error("The shortcut Target points to a legacy frame-generation wrapper; restore it first"); - } + if (nonSteam && directFlatpak) { const cleaned = cleanupPluginLaunchOptions(current.options, wrapperPath); const launchOptionsChanged = cleaned !== current.options; if (launchOptionsChanged) { @@ -398,24 +394,18 @@ export function installWrapperIntegration( "Steam did not accept shortcut launch options", ); } - const savedOriginal = selectFlatpakExecutable(transport, originalExecutable); - if (current.target === wrapperPath) { - if (!savedOriginal) throw new Error("Managed shortcut Target has no saved original executable"); - return { snapshot: current, originalExecutable: savedOriginal, commandTokenAdded: false, changed: launchOptionsChanged }; - } - if (savedOriginal && selectFlatpakExecutable(transport, current.target) !== savedOriginal) { - throw new Error("Shortcut Target changed externally; refusing to replace it"); - } - const currentOriginal = selectFlatpakExecutable(transport, current.target); - if (!currentOriginal || (originalExecutable && !savedOriginal)) { - throw new Error("Flatpak shortcut Target is not a supported executable"); + const executable = directFlatpakExecutable(current.target, wrapperPath); + if (!executable) throw new Error("Flatpak shortcut Target is not a supported direct Flatpak executable"); + const target = managedFlatpakTarget(wrapperPath, executable); + if (normalizeLaunchOptions(current.target) === normalizeLaunchOptions(target)) { + return { snapshot: current, commandTokenAdded: false, changed: launchOptionsChanged }; } const value = await writeVerified( - appId, true, current.target, wrapperPath, - (target) => writeTarget(appId, target), readTarget, + appId, true, current.target, target, + (next) => writeTarget(appId, next), readTarget, "Steam did not accept the shortcut Target", ); - return { snapshot: value, originalExecutable: currentOriginal, commandTokenAdded: false, changed: true }; + return { snapshot: value, commandTokenAdded: false, changed: true }; } const cleaned = cleanupPluginAssignments(cleanupLegacyLaunchOptions(current.options)); @@ -435,13 +425,12 @@ export function removeWrapperIntegration( appId: number, nonSteam: boolean, wrapperPath: string, - originalExecutable?: string, commandTokenAdded = false, - transport: TargetTransport = { kind: "host" }, + directFlatpak = false, ): Promise<SteamLaunchOptionsSnapshot> { return queued(appId, nonSteam, async () => { let current = await readSteamLaunchOptions(appId, nonSteam); - if (usesShortcutTarget(nonSteam, transport)) { + if (nonSteam && directFlatpak) { const cleaned = cleanupPluginLaunchOptions(current.options, wrapperPath); if (cleaned !== current.options) { current = await writeVerified( @@ -450,23 +439,16 @@ export function removeWrapperIntegration( "Steam did not clean shortcut launch options", ); } - if (current.target !== wrapperPath) { - if (isWrapperToken(current.target, wrapperPath)) { - throw new Error("Original shortcut Target is unavailable; refusing to overwrite the current Target"); - } - if (originalExecutable && selectFlatpakExecutable(transport, current.target) !== selectFlatpakExecutable(transport, originalExecutable)) { - throw new Error("Shortcut Target changed externally; refusing to restore it"); - } - return current; - } - if (!originalExecutable || isWrapperToken(originalExecutable, wrapperPath)) { - throw new Error("Original shortcut Target is unavailable; refusing to overwrite the current Target"); + const wrapped = wrappedFlatpakExecutable(current.target, wrapperPath); + if (wrapped) { + return writeVerified( + appId, true, current.target, wrapped, + (target) => writeTarget(appId, target), readTarget, + "Steam did not restore the shortcut Target", + ); } - return writeVerified( - appId, true, wrapperPath, originalExecutable, - (target) => writeTarget(appId, target), readTarget, - "Steam did not restore the shortcut Target", - ); + if (flatpakExecutable(current.target)) return current; + throw new Error("Shortcut Target changed externally; refusing to restore it"); } const next = cleanupPluginAssignments(removeWrapperLaunchOption(current.options, wrapperPath, commandTokenAdded)); return next === current.options ? current : writeVerified( |
