From a6955e828b1dee7b14f8021a8a470dd51d77e33e Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:52:39 -0400 Subject: feat: proxy DLL name picker Expose the proxy DLL rename as a user-selectable option across all injection paths. Previously hardcoded to dxgi.dll with no way to change it short of manually prepending DLL= to the Steam launch option. src/utils/constants.ts - Add PROXY_DLL_OPTIONS (7 entries matching _create_renamed_copies) each with a label and one-line hint - Add DEFAULT_PROXY_DLL constant (dxgi.dll) and ProxyDllValue type src/api/index.ts - runManualPatch now takes [directory, dll_name] so the chosen name reaches the backend src/components/OptiScalerControls.tsx - Own dllName state (default: dxgi.dll) - Render a DropdownItem (visible when installed) showing the 7 options with the selected option's hint as the description - Pass dllName down to both ClipboardCommands and ManualPatchControls src/components/ClipboardCommands.tsx - Accept dllName prop - Patch command is plain ~/fgmod/fgmod %command% for the default; prefixed DLL= ~/fgmod/fgmod %command% for any other choice src/components/CustomPathOverride.tsx - Accept dllName prop - Pass it to runManualPatch - Manual launch cmd clipboard button builds WINEDLLOVERRIDES="=n,b" dynamically; emits bare SteamDeck=0 %command% for OptiScaler.asi (ASI loader path needs no Wine DLL override) main.py - Add VALID_DLL_NAMES set (whitelist matching the renames dir) - manual_patch_directory validates dll_name against the whitelist and returns an error for unknown values - _manual_patch_directory_impl accepts dll_name param; removes the hardcoded "dxgi.dll" line defaults/assets/fgmod.sh - Fix longstanding bug: WINEDLLOVERRIDES was hardcoded to dxgi=n,b regardless of the DLL= env var selection. Now derives the stem from $dll_name and skips the override entirely for .asi files. --- src/api/index.ts | 2 +- src/components/ClipboardCommands.tsx | 17 ++++++++++++----- src/components/CustomPathOverride.tsx | 13 +++++++++---- src/components/OptiScalerControls.tsx | 23 +++++++++++++++++++---- src/utils/constants.ts | 14 ++++++++++++++ 5 files changed, 55 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/api/index.ts b/src/api/index.ts index df52fee..226f29f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -28,7 +28,7 @@ export const getPathDefaults = callable< >("get_path_defaults"); export const runManualPatch = callable< - [string], + [string, string], { status: string; message?: string; output?: string } >("manual_patch_directory"); diff --git a/src/components/ClipboardCommands.tsx b/src/components/ClipboardCommands.tsx index 5a6f38f..b8cf6bf 100644 --- a/src/components/ClipboardCommands.tsx +++ b/src/components/ClipboardCommands.tsx @@ -1,20 +1,27 @@ import { SmartClipboardButton } from "./SmartClipboardButton"; +import { DEFAULT_PROXY_DLL } from "../utils/constants"; interface ClipboardCommandsProps { pathExists: boolean | null; + dllName: string; } -export function ClipboardCommands({ pathExists }: ClipboardCommandsProps) { +export function ClipboardCommands({ pathExists, dllName }: ClipboardCommandsProps) { if (pathExists !== true) return null; + const launchCommand = + dllName === DEFAULT_PROXY_DLL + ? "~/fgmod/fgmod %command%" + : `DLL=${dllName} ~/fgmod/fgmod %command%`; + return ( <> - - - diff --git a/src/components/CustomPathOverride.tsx b/src/components/CustomPathOverride.tsx index ffc4b1f..14a0905 100644 --- a/src/components/CustomPathOverride.tsx +++ b/src/components/CustomPathOverride.tsx @@ -36,6 +36,7 @@ const ensureDirectory = (value: string) => { interface ManualPatchControlsProps { isAvailable: boolean; onManualModeChange?: (enabled: boolean) => void; + dllName: string; } interface PickerState { @@ -56,7 +57,7 @@ const formatResultMessage = (result: ApiResponse | null) => { return result.message || result.output || "Operation failed."; }; -export const ManualPatchControls = ({ isAvailable, onManualModeChange }: ManualPatchControlsProps) => { +export const ManualPatchControls = ({ isAvailable, onManualModeChange, dllName }: ManualPatchControlsProps) => { const [isEnabled, setEnabled] = useState(false); const [defaults, setDefaults] = useState(INITIAL_DEFAULTS); const [pickerState, setPickerState] = useState(INITIAL_PICKER_STATE); @@ -165,7 +166,7 @@ export const ManualPatchControls = ({ isAvailable, onManualModeChange }: ManualP try { const response = action === "patch" - ? await runManualPatch(selectedPath) + ? await runManualPatch(selectedPath, dllName) : await runManualUnpatch(selectedPath); setOperationResult(response ?? { status: "error", message: "No response from backend." }); } catch (err) { @@ -177,7 +178,7 @@ export const ManualPatchControls = ({ isAvailable, onManualModeChange }: ManualP setBusy(false); } }, - [selectedPath] + [selectedPath, dllName] ); const handleToggle = (value: boolean) => { @@ -216,7 +217,11 @@ export const ManualPatchControls = ({ isAvailable, onManualModeChange }: ManualP {canInteract && ( <> diff --git a/src/components/OptiScalerControls.tsx b/src/components/OptiScalerControls.tsx index 468683c..fb5d2f8 100644 --- a/src/components/OptiScalerControls.tsx +++ b/src/components/OptiScalerControls.tsx @@ -1,9 +1,9 @@ import { useState, useEffect } from "react"; -import { PanelSection } from "@decky/ui"; +import { DropdownItem, PanelSection, PanelSectionRow } from "@decky/ui"; import { runInstallFGMod, runUninstallFGMod } from "../api"; import { OperationResult } from "./ResultDisplay"; import { createAutoCleanupTimer } from "../utils"; -import { TIMEOUTS } from "../utils/constants"; +import { TIMEOUTS, PROXY_DLL_OPTIONS, DEFAULT_PROXY_DLL } from "../utils/constants"; import { InstallationStatus } from "./InstallationStatus"; import { OptiScalerHeader } from "./OptiScalerHeader"; import { ClipboardCommands } from "./ClipboardCommands"; @@ -23,6 +23,7 @@ export function OptiScalerControls({ pathExists, setPathExists }: OptiScalerCont const [installResult, setInstallResult] = useState(null); const [uninstallResult, setUninstallResult] = useState(null); const [manualModeEnabled, setManualModeEnabled] = useState(false); + const [dllName, setDllName] = useState(DEFAULT_PROXY_DLL); useEffect(() => { if (installResult) { return createAutoCleanupTimer(() => setInstallResult(null), TIMEOUTS.resultDisplay); @@ -76,15 +77,29 @@ export function OptiScalerControls({ pathExists, setPathExists }: OptiScalerCont /> - + + {pathExists === true && ( + + o.value === dllName)?.hint} + menuLabel="Proxy DLL name" + selectedOption={dllName} + rgOptions={PROXY_DLL_OPTIONS.map((o) => ({ data: o.value, label: o.label }))} + onChange={(option) => setDllName(String(option.data))} + /> + + )} + {!manualModeEnabled && ( <> - + diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 1f583c0..ce61263 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -45,6 +45,20 @@ export const STYLES = { } }; +// Proxy DLL name options for OptiScaler injection +export const PROXY_DLL_OPTIONS = [ + { value: "dxgi.dll", label: "dxgi.dll", hint: "Works for most DX12 games. Default." }, + { value: "winmm.dll", label: "winmm.dll", hint: "Use when dxgi.dll conflicts with an existing game file." }, + { value: "version.dll", label: "version.dll", hint: "Common fallback; works well with many launchers." }, + { value: "dbghelp.dll", label: "dbghelp.dll", hint: "Use for debug helper hook paths." }, + { value: "winhttp.dll", label: "winhttp.dll", hint: "Use when other DLL names conflict." }, + { value: "wininet.dll", label: "wininet.dll", hint: "Use when other DLL names conflict." }, + { value: "OptiScaler.asi", label: "OptiScaler.asi", hint: "For ASI loaders. Requires an ASI loader already installed in the game." }, +] as const; + +export type ProxyDllValue = typeof PROXY_DLL_OPTIONS[number]["value"]; +export const DEFAULT_PROXY_DLL: ProxyDllValue = "dxgi.dll"; + // Common timeout values export const TIMEOUTS = { resultDisplay: 5000, // 5 seconds -- cgit v1.2.3