summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/ConfigurationTab.tsx6
-rw-r--r--src/components/GameConfigurationSelector.tsx29
-rw-r--r--src/components/ProfileDetails.tsx4
-rw-r--r--src/components/WorkaroundsSection.tsx21
-rw-r--r--src/hooks/useGameConfiguration.ts46
-rw-r--r--src/i18n/languages.json12
-rw-r--r--src/utils/steamLaunchOptionParser.ts40
-rw-r--r--src/utils/steamLaunchOptions.ts15
8 files changed, 140 insertions, 33 deletions
diff --git a/src/components/ConfigurationTab.tsx b/src/components/ConfigurationTab.tsx
index 2bb0f26..c41c941 100644
--- a/src/components/ConfigurationTab.tsx
+++ b/src/components/ConfigurationTab.tsx
@@ -33,6 +33,7 @@ export function ConfigurationTab({
const [detailAppId, setDetailAppId] = useState<string | null>(null);
const [focusFpsMultiplier, setFocusFpsMultiplier] = useState(false);
const [focusDetailAction, setFocusDetailAction] = useState<"enable" | "fps" | null>(null);
+ const [focusConfiguredToggle, setFocusConfiguredToggle] = useState(false);
const enableRef = useRef<HTMLDivElement>(null);
const promptedRunningAppId = useRef<string | null>(null);
const closeDetails = useCallback(() => {
@@ -41,6 +42,7 @@ export function ConfigurationTab({
setDetailAppId(null);
}, []);
const clearFpsFocusRequest = useCallback(() => setFocusFpsMultiplier(false), []);
+ const clearConfiguredToggleFocusRequest = useCallback(() => setFocusConfiguredToggle(false), []);
useEffect(() => {
if (!focusDetailAction) return;
@@ -77,12 +79,15 @@ export function ConfigurationTab({
targets={targets}
runningGame={runningGame}
onSelect={(appid) => {
+ setFocusConfiguredToggle(false);
setFocusDetailAction(targets.find((target) => target.appid === appid)?.configured ? "fps" : "enable");
onSelect(appid);
setDetailAppId(appid);
}}
onEnableAll={onEnableAll}
onResetAll={onResetAll}
+ focusConfiguredToggle={focusConfiguredToggle}
+ onConfiguredToggleFocused={clearConfiguredToggleFocusRequest}
/>
</PanelSection>
);
@@ -96,6 +101,7 @@ export function ConfigurationTab({
if (selectedTarget?.configured) {
promptedRunningAppId.current = detailAppId;
await onReset();
+ setFocusConfiguredToggle(true);
closeDetails();
} else if (detailAppId && await onEnable(detailAppId)) {
setFocusFpsMultiplier(true);
diff --git a/src/components/GameConfigurationSelector.tsx b/src/components/GameConfigurationSelector.tsx
index 5f23b91..50b2cf8 100644
--- a/src/components/GameConfigurationSelector.tsx
+++ b/src/components/GameConfigurationSelector.tsx
@@ -1,5 +1,5 @@
import { ButtonItem, ConfirmModal, Field, PanelSectionRow, showModal } from "@decky/ui";
-import { useEffect, useState } from "react";
+import { useEffect, useRef, useState, type RefObject } from "react";
import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri";
import { GameTarget } from "../hooks/useGameConfiguration";
@@ -9,6 +9,8 @@ interface Props {
onSelect: (appid: string) => void;
onEnableAll: () => Promise<void>;
onResetAll: () => Promise<void>;
+ focusConfiguredToggle?: boolean;
+ onConfiguredToggleFocused?: () => void;
}
const CONFIGURED_COLLAPSED_KEY = "lsfg-configured-games-collapsed-v3";
@@ -40,12 +42,14 @@ function GameGroup({
collapsed,
onToggle,
onSelect,
+ toggleRef,
}: {
title: string;
games: GameTarget[];
collapsed: boolean;
onToggle: () => void;
onSelect: (appid: string) => void;
+ toggleRef?: RefObject<HTMLDivElement>;
}) {
if (games.length === 0) return null;
@@ -56,6 +60,7 @@ function GameGroup({
</PanelSectionRow>
<PanelSectionRow>
<div
+ ref={toggleRef}
className="LSFG_GameGroupCollapseButton_Container"
style={{ marginTop: "-2px", marginBottom: "4px" }}
>
@@ -86,7 +91,15 @@ function GameGroup({
);
}
-export function GameConfigurationSelector({ targets, runningGame, onSelect, onEnableAll, onResetAll }: Props) {
+export function GameConfigurationSelector({
+ targets,
+ runningGame,
+ onSelect,
+ onEnableAll,
+ onResetAll,
+ focusConfiguredToggle = false,
+ onConfiguredToggleFocused,
+}: Props) {
const sortGames = (games: GameTarget[]) => [...games].sort((a, b) => {
if (a.appid === runningGame?.appid) return -1;
if (b.appid === runningGame?.appid) return 1;
@@ -102,6 +115,17 @@ export function GameConfigurationSelector({ targets, runningGame, onSelect, onEn
const [configuredNonSteamCollapsed, toggleConfiguredNonSteam] = usePersistentCollapsed(`${CONFIGURED_COLLAPSED_KEY}-non-steam`);
const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(AVAILABLE_COLLAPSED_KEY);
const [availableNonSteamCollapsed, toggleAvailableNonSteam] = usePersistentCollapsed(`${AVAILABLE_COLLAPSED_KEY}-non-steam`);
+ const configuredToggleRef = useRef<HTMLDivElement>(null);
+
+ useEffect(() => {
+ if (!focusConfiguredToggle) return;
+ const frame = requestAnimationFrame(() => {
+ configuredToggleRef.current?.querySelector<HTMLElement>('[role="button"], button')?.focus();
+ onConfiguredToggleFocused?.();
+ });
+ return () => cancelAnimationFrame(frame);
+ }, [configuredGames.length, focusConfiguredToggle, onConfiguredToggleFocused]);
+
const confirmResetAll = () => {
showModal(
<ConfirmModal
@@ -163,6 +187,7 @@ export function GameConfigurationSelector({ targets, runningGame, onSelect, onEn
collapsed={configuredCollapsed}
onToggle={toggleConfigured}
onSelect={onSelect}
+ toggleRef={configuredToggleRef}
/>
<GameGroup
title="LSFG-VK Enabled (Non-Steam)"
diff --git a/src/components/ProfileDetails.tsx b/src/components/ProfileDetails.tsx
index 056abbb..4dd8891 100644
--- a/src/components/ProfileDetails.tsx
+++ b/src/components/ProfileDetails.tsx
@@ -24,12 +24,12 @@ export function ProfileDetails({ description }: ProfileDetailsProps) {
bottomSeparator={expanded ? "none" : "standard"}
onClick={() => setExpanded((value) => !value)}
>
- {expanded ? <RiArrowUpSFill /> : <RiArrowDownSFill />} Details
+ {expanded ? <RiArrowUpSFill /> : <RiArrowDownSFill />} Game Details
</ButtonItem>
</PanelSectionRow>
{expanded && (
<PanelSectionRow>
- <Field label="Details" description={description} />
+ <Field label="Game Details" description={description} />
</PanelSectionRow>
)}
</Focusable>
diff --git a/src/components/WorkaroundsSection.tsx b/src/components/WorkaroundsSection.tsx
index e990784..d783620 100644
--- a/src/components/WorkaroundsSection.tsx
+++ b/src/components/WorkaroundsSection.tsx
@@ -10,7 +10,7 @@ interface WorkaroundsSectionProps {
nonSteam: boolean;
}
-const WORKAROUNDS_COLLAPSED_KEY = "lsfg-workarounds-collapsed";
+const WORKAROUNDS_COLLAPSED_KEY = "lsfg-workarounds-collapsed-v2";
type ToggleWorkaroundField = Exclude<WorkaroundField, "dxvkFrameRate">;
const TOGGLE_ROWS: readonly {
@@ -21,18 +21,25 @@ const TOGGLE_ROWS: readonly {
description: string;
}[] = [
{
+ field: "disableSteamdeckMode",
+ labelKey: "CONFIG_DISABLE_STEAMDECK_MODE",
+ label: "Disable Steam Deck Mode",
+ descriptionKey: "CONFIG_DISABLE_STEAMDECK_MODE_DESC",
+ description: "Disables a game-specific Steam Deck compatibility switch. Requires game restart to apply.",
+ },
+ {
field: "disableGamescopeWsi",
labelKey: "CONFIG_DISABLE_GAMESCOPE_WSI",
label: "Disable Gamescope WSI",
descriptionKey: "CONFIG_DISABLE_GAMESCOPE_WSI_DESC",
- description: "Adds ENABLE_GAMESCOPE_WSI=0 without changing HDR. Requires game restart to apply.",
+ description: "Adds ENABLE_GAMESCOPE_WSI=0. Requires game restart to apply.",
},
{
- field: "disableSteamdeckMode",
- labelKey: "CONFIG_DISABLE_STEAMDECK_MODE",
- label: "Disable Steam Deck Mode",
- descriptionKey: "CONFIG_DISABLE_STEAMDECK_MODE_DESC",
- description: "Disables a game-specific Steam Deck compatibility switch. Requires game restart to apply.",
+ field: "disableHdr",
+ labelKey: "CONFIG_DISABLE_HDR",
+ label: "Disable HDR",
+ descriptionKey: "CONFIG_DISABLE_HDR_DESC",
+ description: "Prevents DXVK from exposing HDR to the game. Requires game restart to apply.",
},
{
field: "disableVkbasalt",
diff --git a/src/hooks/useGameConfiguration.ts b/src/hooks/useGameConfiguration.ts
index 46607cb..6d1fe6a 100644
--- a/src/hooks/useGameConfiguration.ts
+++ b/src/hooks/useGameConfiguration.ts
@@ -3,7 +3,7 @@ import { useQuickAccessVisible } from "@decky/api";
import { Router } from "@decky/ui";
import { getGameConfigs, getInstalledGames, updateGameConfig, resetGameConfig, resetAllGameConfigs, type GameConfigEntry, type GlobalConfig, type InstalledGame } from "../api/lsfgApi";
import { ConfigurationData, getDefaults } from "../config/configSchema";
-import { cleanupSteamLaunchOptions } from "../utils/steamLaunchOptions";
+import { applyWorkaroundState, cleanupLegacySteamLaunchOptions, cleanupSteamLaunchOptions, getDefaultWorkaroundState, updateSteamLaunchOptions } from "../utils/steamLaunchOptions";
import { showErrorToast } from "../utils/toastUtils";
export interface GameTarget extends InstalledGame { configured: boolean; }
@@ -98,7 +98,7 @@ export function useGameConfiguration() {
const cleanupTargetLaunchOptions = useCallback(async (target: GameTarget): Promise<boolean> => {
if (!installedGames.some((game) => game.appid === target.appid)) return true;
try {
- await cleanupSteamLaunchOptions(Number(target.appid), target.nonSteam);
+ await cleanupLegacySteamLaunchOptions(Number(target.appid), target.nonSteam);
return true;
} catch (error) {
showErrorToast("Could not update Steam launch options", error instanceof Error ? error.message : String(error));
@@ -106,6 +106,32 @@ export function useGameConfiguration() {
}
}, [installedGames]);
+ const removeTargetLaunchOptions = useCallback(async (target: GameTarget): Promise<boolean> => {
+ if (!installedGames.some((game) => game.appid === target.appid)) return true;
+ try {
+ await cleanupSteamLaunchOptions(Number(target.appid), target.nonSteam);
+ return true;
+ } catch (error) {
+ showErrorToast("Could not clean up Steam launch options", error instanceof Error ? error.message : String(error));
+ return false;
+ }
+ }, [installedGames]);
+
+ const initializeTargetLaunchOptions = useCallback(async (target: GameTarget): Promise<boolean> => {
+ if (!installedGames.some((game) => game.appid === target.appid)) return true;
+ try {
+ await updateSteamLaunchOptions(
+ Number(target.appid),
+ target.nonSteam,
+ (options) => applyWorkaroundState(options, getDefaultWorkaroundState()),
+ );
+ return true;
+ } catch (error) {
+ showErrorToast("Could not initialize Steam launch options", error instanceof Error ? error.message : String(error));
+ return false;
+ }
+ }, [installedGames]);
+
const save = useCallback(async (next: ConfigurationData, cleanupLaunchOptions = false) => {
const selectedTarget = targets.find((target) => target.appid === selectedAppId);
if (!selectedTarget?.name) return;
@@ -117,16 +143,16 @@ export function useGameConfiguration() {
const enable = useCallback(async (appid: string) => {
const target = targets.find((item) => item.appid === appid);
if (!target?.name) return false;
- if (!(await cleanupTargetLaunchOptions(target))) return false;
+ if (!(await initializeTargetLaunchOptions(target))) return false;
const result = await updateGameConfig(appid, target.name, template);
if (result.success) await load();
return result.success;
- }, [cleanupTargetLaunchOptions, load, targets, template]);
+ }, [initializeTargetLaunchOptions, load, targets, template]);
const enableAll = useCallback(async (): Promise<void> => {
const available = targets.filter((target) => !target.configured && target.name);
if (available.length === 0) return;
for (const target of available) {
- if (!(await cleanupTargetLaunchOptions(target))) return;
+ if (!(await initializeTargetLaunchOptions(target))) return;
const result = await updateGameConfig(target.appid, target.name, template);
if (!result.success) {
showErrorToast("Could not enable all games", result.error || "A game profile could not be created");
@@ -134,12 +160,12 @@ export function useGameConfiguration() {
}
}
await load();
- }, [cleanupTargetLaunchOptions, load, targets, template]);
+ }, [initializeTargetLaunchOptions, load, targets, template]);
const resetSelected = useCallback(async () => {
if (selectedAppId) {
const selectedTarget = targets.find((target) => target.appid === selectedAppId);
- if (selectedTarget && !(await cleanupTargetLaunchOptions(selectedTarget))) return;
+ if (selectedTarget && !(await removeTargetLaunchOptions(selectedTarget))) return;
const result = await resetGameConfig(selectedAppId);
if (result.success) {
setRunningGame((current) => current?.appid === selectedAppId ? { ...current, configured: false } : current);
@@ -147,10 +173,10 @@ export function useGameConfiguration() {
await load();
}
}
- }, [cleanupTargetLaunchOptions, load, selectedAppId, targets]);
+ }, [load, removeTargetLaunchOptions, selectedAppId, targets]);
const resetAll = useCallback(async () => {
for (const target of targets.filter((item) => item.configured)) {
- if (!(await cleanupTargetLaunchOptions(target))) return;
+ if (!(await removeTargetLaunchOptions(target))) return;
}
const result = await resetAllGameConfigs();
if (result.success) {
@@ -158,7 +184,7 @@ export function useGameConfiguration() {
setSelectedAppId("");
await load();
}
- }, [cleanupTargetLaunchOptions, load, targets]);
+ }, [load, removeTargetLaunchOptions, targets]);
return { config, games, targets, runningGame, selectedAppId, setSelectedAppId, save, enable, enableAll, resetSelected, resetAll, reload: load };
}
diff --git a/src/i18n/languages.json b/src/i18n/languages.json
index 3f1992c..7e5676b 100644
--- a/src/i18n/languages.json
+++ b/src/i18n/languages.json
@@ -22,7 +22,9 @@
"CONFIG_ENABLE_WSI": "WSIを有効化",
"CONFIG_ENABLE_WSI_DESC": "Gamescope WSIレイヤーを再有効化します。ゲームの再起動が必要。",
"CONFIG_DISABLE_GAMESCOPE_WSI": "Gamescope WSIを無効化",
- "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "HDRを変更せずENABLE_GAMESCOPE_WSI=0を追加します。ゲームの再起動が必要です。",
+ "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "ENABLE_GAMESCOPE_WSI=0を追加します。ゲームの再起動が必要です。",
+ "CONFIG_DISABLE_HDR": "HDRを無効化",
+ "CONFIG_DISABLE_HDR_DESC": "DXVKがゲームにHDRを公開しないようにします。ゲームの再起動が必要です。",
"CONFIG_DISABLE_STEAMDECK_MODE": "Steam Deckモードを無効化",
"CONFIG_DISABLE_STEAMDECK_MODE_DESC": "ゲーム固有のSteam Deck互換スイッチを無効化します。ゲームの再起動が必要です。",
"CONFIG_DISABLE_VKBASALT": "vkBasaltを無効化",
@@ -109,7 +111,9 @@
"CONFIG_ENABLE_WSI": "WSI 활성화",
"CONFIG_ENABLE_WSI_DESC": "Gamescope WSI 레이어를 다시 활성화합니다. 게임 재시작 필요.",
"CONFIG_DISABLE_GAMESCOPE_WSI": "Gamescope WSI 비활성화",
- "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "HDR을 변경하지 않고 ENABLE_GAMESCOPE_WSI=0을 추가합니다. 게임 재시작 필요.",
+ "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "ENABLE_GAMESCOPE_WSI=0을 추가합니다. 게임 재시작 필요.",
+ "CONFIG_DISABLE_HDR": "HDR 비활성화",
+ "CONFIG_DISABLE_HDR_DESC": "DXVK가 게임에 HDR을 노출하지 않도록 합니다. 게임 재시작 필요.",
"CONFIG_DISABLE_STEAMDECK_MODE": "Steam Deck 모드 비활성화",
"CONFIG_DISABLE_STEAMDECK_MODE_DESC": "게임별 Steam Deck 호환 스위치를 비활성화합니다. 게임 재시작 필요.",
"CONFIG_DISABLE_VKBASALT": "vkBasalt 비활성화",
@@ -224,7 +228,9 @@
"CONFIG_ENABLE_WSI": "Enable WSI",
"CONFIG_ENABLE_WSI_DESC": "Re-Enable Gamescope WSI Layer. Requires game restart to apply.",
"CONFIG_DISABLE_GAMESCOPE_WSI": "Disable Gamescope WSI",
- "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "Adds ENABLE_GAMESCOPE_WSI=0 without changing HDR. Requires game restart to apply.",
+ "CONFIG_DISABLE_GAMESCOPE_WSI_DESC": "Adds ENABLE_GAMESCOPE_WSI=0. Requires game restart to apply.",
+ "CONFIG_DISABLE_HDR": "Disable HDR",
+ "CONFIG_DISABLE_HDR_DESC": "Prevents DXVK from exposing HDR to the game. Requires game restart to apply.",
"CONFIG_DISABLE_STEAMDECK_MODE": "Disable Steam Deck Mode",
"CONFIG_DISABLE_STEAMDECK_MODE_DESC": "Disables a game-specific Steam Deck compatibility switch. Requires game restart to apply.",
"CONFIG_DISABLE_VKBASALT": "Disable vkBasalt",
diff --git a/src/utils/steamLaunchOptionParser.ts b/src/utils/steamLaunchOptionParser.ts
index 5b8156c..9449b33 100644
--- a/src/utils/steamLaunchOptionParser.ts
+++ b/src/utils/steamLaunchOptionParser.ts
@@ -1,6 +1,7 @@
export interface WorkaroundState {
dxvkFrameRate: number;
disableGamescopeWsi: boolean;
+ disableHdr: boolean;
disableSteamdeckMode: boolean;
disableVkbasalt: boolean;
enableZink: boolean;
@@ -41,8 +42,10 @@ const LEGACY_WRAPPER_TOKENS = new Set([
"/home/deck/.local/bin/lsfg-vk-experimental",
"~/.local/bin/mako-run",
"/home/deck/.local/bin/mako-run",
+ "mako-run",
"~/.local/bin/mako-launch",
"/home/deck/.local/bin/mako-launch",
+ "mako-launch",
]);
const DXVK_FRAME_RATE_KEYS: readonly DxvkFrameRateKey[] = [
"dxvk.maxFrameRate",
@@ -53,7 +56,12 @@ const DXVK_MANAGED_KEYS = new Set(["DXVK_CONFIG", "DXVK_FRAME_RATE"]);
const WORKAROUND_DEFINITIONS = {
disableGamescopeWsi: {
spec: ["ENABLE_GAMESCOPE_WSI", "0"],
- clear: ["DISABLE_GAMESCOPE_WSI", "ENABLE_GAMESCOPE_WSI", "DXVK_HDR"],
+ clear: ["DISABLE_GAMESCOPE_WSI", "ENABLE_GAMESCOPE_WSI"],
+ },
+ disableHdr: {
+ spec: ["DXVK_HDR", "0"],
+ clear: ["DXVK_HDR"],
+ label: "Disable HDR",
},
disableSteamdeckMode: {
spec: ["SteamDeck", "0"],
@@ -72,24 +80,34 @@ const WORKAROUND_DEFINITIONS = {
} as const satisfies Record<BooleanWorkaroundField, WorkaroundDefinition>;
const BOOLEAN_WORKAROUND_FIELDS: readonly BooleanWorkaroundField[] = [
"disableGamescopeWsi",
+ "disableHdr",
"disableSteamdeckMode",
"disableVkbasalt",
"enableZink",
];
const WSI_DISABLE_KEY = "DISABLE_GAMESCOPE_WSI";
const WSI_ENABLE_KEY = "ENABLE_GAMESCOPE_WSI";
+const WORKAROUND_ENV_KEYS = new Set(
+ BOOLEAN_WORKAROUND_FIELDS.flatMap((field) => WORKAROUND_DEFINITIONS[field].clear),
+);
const MANAGED_ENV_KEYS = new Set([
...DXVK_MANAGED_KEYS,
- ...BOOLEAN_WORKAROUND_FIELDS.flatMap((field) => WORKAROUND_DEFINITIONS[field].clear),
+ ...WORKAROUND_ENV_KEYS,
]);
-const DEFAULT_WORKAROUND_STATE: WorkaroundState = {
+const EMPTY_WORKAROUND_STATE: WorkaroundState = {
dxvkFrameRate: 0,
disableGamescopeWsi: false,
+ disableHdr: false,
disableSteamdeckMode: false,
disableVkbasalt: false,
enableZink: false,
};
+const DEFAULT_WORKAROUND_STATE: WorkaroundState = {
+ ...EMPTY_WORKAROUND_STATE,
+ disableGamescopeWsi: true,
+ disableHdr: true,
+};
export function getDefaultWorkaroundState(): WorkaroundState {
return { ...DEFAULT_WORKAROUND_STATE };
@@ -117,8 +135,6 @@ function decodeToken(raw: string): string {
return value;
}
-// Steam stores one shell-like line. Keep each token's raw spelling beside its
-// decoded value so managed edits leave unrelated quoting and arguments alone.
function tokenize(options: string): LaunchToken[] {
const tokens: LaunchToken[] = [];
let start = -1;
@@ -358,7 +374,7 @@ function readBoolean(
export function parseWorkaroundOptions(options: string): ParsedWorkaroundOptions {
const tokens = tokenize(options);
const entries = effectiveEnvironmentEntries(tokens);
- const state = getDefaultWorkaroundState();
+ const state = { ...EMPTY_WORKAROUND_STATE };
const issues: string[] = [];
for (const [key, entry] of entries) {
@@ -418,7 +434,7 @@ export function parseWorkaroundOptions(options: string): ParsedWorkaroundOptions
state.disableGamescopeWsi = wsiSignals.some(Boolean);
}
- for (const field of ["disableSteamdeckMode", "disableVkbasalt"] as const) {
+ for (const field of ["disableHdr", "disableSteamdeckMode", "disableVkbasalt"] as const) {
const { spec, label } = WORKAROUND_DEFINITIONS[field];
state[field] = readBoolean(entries, spec[0], label || field, spec[1], issues);
}
@@ -455,7 +471,7 @@ export function applyWorkaroundState(options: string, state: WorkaroundState): s
removeLegacyWrapperFromTokens(tokens);
rewriteDxvkFrameRate(tokens, state.dxvkFrameRate);
const keysToClear = new Set<string>(
- BOOLEAN_WORKAROUND_FIELDS.flatMap((field) => WORKAROUND_DEFINITIONS[field].clear),
+ WORKAROUND_ENV_KEYS,
);
if (state.disableVkbasalt) keysToClear.add("ENABLE_VKBASALT");
removeAllAssignments(tokens, keysToClear);
@@ -496,6 +512,14 @@ export function cleanupLegacyLaunchOptions(options: string): string {
return serialize(tokens);
}
+export function cleanupPluginLaunchOptions(options: string): string {
+ const tokens = tokenize(options);
+ removeLegacyWrapperFromTokens(tokens);
+ rewriteDxvkFrameRate(tokens, 0);
+ removeAllAssignments(tokens, WORKAROUND_ENV_KEYS);
+ return serialize(tokens);
+}
+
export function cleanupLegacyWrapper(options: string): string {
return cleanupLegacyLaunchOptions(options);
}
diff --git a/src/utils/steamLaunchOptions.ts b/src/utils/steamLaunchOptions.ts
index 39125bd..82ff94b 100644
--- a/src/utils/steamLaunchOptions.ts
+++ b/src/utils/steamLaunchOptions.ts
@@ -1,5 +1,5 @@
// @ts-expect-error Node's built-in TypeScript loader requires explicit source extensions in tests.
-import { cleanupLegacyLaunchOptions, isLegacyWrapperToken, normalizeLaunchOptions } from "./steamLaunchOptionParser.ts";
+import { cleanupLegacyLaunchOptions, cleanupPluginLaunchOptions, isLegacyWrapperToken, normalizeLaunchOptions } from "./steamLaunchOptionParser.ts";
// @ts-expect-error Node's built-in TypeScript loader requires explicit source extensions in tests.
export * from "./steamLaunchOptionParser.ts";
@@ -203,6 +203,19 @@ export function cleanupSteamLaunchOptions(
): Promise<SteamLaunchOptionsSnapshot> {
return queueSteamAppOperation(appId, nonSteam, async () => {
const current = await readSteamLaunchOptions(appId, nonSteam);
+ const next = cleanupPluginLaunchOptions(current.options);
+ if (next === current.options) return current;
+ await setSteamLaunchOptions(appId, nonSteam, next);
+ return waitForLaunchOptions(appId, nonSteam, next);
+ });
+}
+
+export function cleanupLegacySteamLaunchOptions(
+ appId: number,
+ nonSteam: boolean,
+): Promise<SteamLaunchOptionsSnapshot> {
+ return queueSteamAppOperation(appId, nonSteam, async () => {
+ const current = await readSteamLaunchOptions(appId, nonSteam);
const next = cleanupLegacyLaunchOptions(current.options);
if (next === current.options) return current;
await setSteamLaunchOptions(appId, nonSteam, next);