From bb35f18ccf17437ee484f92319da314164b4499b Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:38:53 -0400 Subject: chore: update OptiScaler to v0.9.0-final - package.json: point remote_binary at official optiscaler/OptiScaler v0.9 release (Optiscaler_0.9.0-final.20260401._AF.7z) with updated sha256 hash; drop staging-repo pre11 URL - main.py (_modify_optiscaler_ini): FGType was split into FGInput + FGOutput in the final release INI; replace the old FGType=nukems substitution with FGInput=nukems and FGOutput=nukems so defaults are actually applied - main.py (_manual_patch_directory_impl): copy D3D12_Optiscaler/ directory to the game folder (OptiScaler.ini explicitly requires it next to the exe for FSR4/FidelityFX DX12 path) - main.py (_manual_unpatch_directory_impl): remove D3D12_Optiscaler/ directory when cleaning a game folder - fgmod.sh: cp -r D3D12_Optiscaler/ to game folder during launch-time install, matching the per-game patch behaviour above - Scrub stale pre3/pre4/pre11 references from comments throughout --- defaults/assets/fgmod.sh | 13 +++++++++++-- main.py | 30 +++++++++++++++++++++++------- package.json | 6 +++--- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh index fa36558..cffce14 100755 --- a/defaults/assets/fgmod.sh +++ b/defaults/assets/fgmod.sh @@ -146,6 +146,15 @@ else echo "โš ๏ธ No plugins directory found in fgmod" fi +# === D3D12_Optiscaler Directory (required for FSR4/FidelityFX DX12 path) === +if [[ -d "$fgmod_path/D3D12_Optiscaler" ]]; then + echo "๐Ÿ“ฆ Installing D3D12_Optiscaler directory" + cp -r "$fgmod_path/D3D12_Optiscaler" "$exe_folder_path/" || true + logger -t fgmod "๐Ÿ“ฆ D3D12_Optiscaler directory installed to $exe_folder_path" +else + echo "โš ๏ธ No D3D12_Optiscaler directory found in fgmod" +fi + # === Supporting Libraries === cp -f "$fgmod_path/libxess.dll" "$exe_folder_path/" || true cp -f "$fgmod_path/libxess_dx11.dll" "$exe_folder_path/" || true @@ -159,7 +168,7 @@ cp -f "$fgmod_path/nvngx.dll" "$exe_folder_path/" || true # === Nukem FG Mod Files (now in fgmod directory) === cp -f "$fgmod_path/dlssg_to_fsr3_amd_is_better.dll" "$exe_folder_path/" || true -# Note: dlssg_to_fsr3.ini is not included in v0.9.0-pre4 archive +# Note: dlssg_to_fsr3.ini is not included in v0.9.0-final archive # === FakeNVAPI Files === # Remove legacy nvapi64.dll to avoid conflicts @@ -174,7 +183,7 @@ echo "๐Ÿ“ฆ Installed fakenvapi.dll and fakenvapi.ini" # === Additional Support Files === # cp -f "$fgmod_path/d3dcompiler_47.dll" "$exe_folder_path/" || true -# Note: d3dcompiler_47.dll is not included in v0.9.0-pre4 archive +# Note: d3dcompiler_47.dll is not included in v0.9.0-final archive echo "โœ… Installation completed successfully!" echo "๐Ÿ“„ For Steam, add this to the launch options: \"$fgmod_path/fgmod\" %COMMAND%" diff --git a/main.py b/main.py index ddee8ba..3d63849 100644 --- a/main.py +++ b/main.py @@ -156,8 +156,11 @@ class Plugin: with open(ini_file, 'r') as f: content = f.read() - # Replace FGType=auto with FGType=nukems - updated_content = re.sub(r'FGType\s*=\s*auto', 'FGType=nukems', content) + # Replace FGInput=auto with FGInput=nukems (final v0.9+ split FGType into FGInput/FGOutput) + updated_content = re.sub(r'FGInput\s*=\s*auto', 'FGInput=nukems', content) + + # Replace FGOutput=auto with FGOutput=nukems + updated_content = re.sub(r'FGOutput\s*=\s*auto', 'FGOutput=nukems', updated_content) # Replace Fsr4Update=auto with Fsr4Update=true updated_content = re.sub(r'Fsr4Update\s*=\s*auto', 'Fsr4Update=true', updated_content) @@ -174,7 +177,7 @@ class Plugin: with open(ini_file, 'w') as f: f.write(updated_content) - decky.logger.info("Modified OptiScaler.ini to set FGType=nukems, Fsr4Update=true, LoadAsiPlugins=true, Path=plugins, UseHQFont=false") + decky.logger.info("Modified OptiScaler.ini to set FGInput=nukems, FGOutput=nukems, Fsr4Update=true, LoadAsiPlugins=true, Path=plugins, UseHQFont=false") return True else: decky.logger.warning(f"OptiScaler.ini not found at {ini_file}") @@ -267,7 +270,7 @@ class Plugin: } # Copy additional individual files from bin directory - # Note: v0.9.0-pre3+ includes dlssg_to_fsr3_amd_is_better.dll, fakenvapi.dll, and fakenvapi.ini in the 7z + # Note: v0.9.0-final includes dlssg_to_fsr3_amd_is_better.dll, fakenvapi.dll, and fakenvapi.ini in the 7z # Only copy files that aren't already in the archive (separate remote binaries) additional_files = [ "nvngx.dll", # nvidia dll from streamline sdk, not bundled in opti @@ -433,7 +436,7 @@ class Plugin: "OptiScaler.dll", "OptiScaler.ini", "dlssg_to_fsr3_amd_is_better.dll", - "fakenvapi.dll", # v0.9.0-pre3+ includes fakenvapi.dll in archive + "fakenvapi.dll", # v0.9.0-final includes fakenvapi.dll in archive "fakenvapi.ini", "nvngx.dll", "amd_fidelityfx_dx12.dll", @@ -442,8 +445,8 @@ class Plugin: "amd_fidelityfx_vk.dll", "libxess.dll", "libxess_dx11.dll", - "libxess_fg.dll", # New in v0.9.0-pre4 - "libxell.dll", # New in v0.9.0-pre4 + "libxess_fg.dll", # added in v0.9.0 + "libxell.dll", # added in v0.9.0 "fgmod", "fgmod-uninstaller.sh", "update-optiscaler-config.py" @@ -549,6 +552,14 @@ class Plugin: else: decky.logger.warning("Plugins directory missing in fgmod bundle") + d3d12_src = fgmod_path / "D3D12_Optiscaler" + d3d12_dest = directory / "D3D12_Optiscaler" + if d3d12_src.exists(): + shutil.copytree(d3d12_src, d3d12_dest, dirs_exist_ok=True) + decky.logger.info(f"Copied D3D12_Optiscaler directory to {d3d12_dest}") + else: + decky.logger.warning("D3D12_Optiscaler directory missing in fgmod bundle") + copied_support = [] missing_support = [] for filename in SUPPORT_FILES: @@ -611,6 +622,11 @@ class Plugin: shutil.rmtree(plugins_dir, ignore_errors=True) decky.logger.info(f"Removed plugins directory at {plugins_dir}") + d3d12_dir = directory / "D3D12_Optiscaler" + if d3d12_dir.exists(): + shutil.rmtree(d3d12_dir, ignore_errors=True) + decky.logger.info(f"Removed D3D12_Optiscaler directory from {d3d12_dir}") + restored_backups = [] for dll in ORIGINAL_DLL_BACKUPS: backup = directory / f"{dll}.b" diff --git a/package.json b/package.json index 9c95117..312d0fc 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,9 @@ "remote_binary": [ { - "sha256hash": "d30d98b9f58e340b8dcd5aa93fc59432e803e071cd6b207d55acc667685d79dc", - "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/opti-9-pre-11/Optiscaler_0.9.0-pre11.20260311._RC4.5.7z", - "name": "Optiscaler_0.9.0-pre11.20260311._RC4.5.7z" + "sha256hash": "a988ce2c0a86bba58a6313659d1ed2ab78f994dbdfab246394a2e4293ac68010", + "url": "https://github.com/optiscaler/OptiScaler/releases/download/v0.9/Optiscaler_0.9.0-final.20260401._AF.7z", + "name": "Optiscaler_0.9.0-final.20260401._AF.7z" }, { "sha256hash": "2604c0b392072d715b400b2f89434274de31995a4b6e68ce38250ebbd3f6c5fc", -- cgit v1.2.3 From d1ce48eba2a38909f33df965ab672249156dc47d Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:43:16 -0400 Subject: =?UTF-8?q?fix:=20migrate=20per-game=20FGType=20=E2=86=92=20FGInpu?= =?UTF-8?q?t/FGOutput=20on=20patch=20and=20launch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Already-patched games have OptiScaler.ini entries using the old FGType key (e.g. FGType=nukems). The v0.9-final DLL no longer recognises FGType and silently falls back to nofg, breaking frame gen without any error. Add _migrate_optiscaler_ini() in main.py which: - detects FGType= in a per-game INI - if FGInput is absent: replaces the single FGType line with both FGInput= and FGOutput= - if FGInput is already present (INI already migrated): just drops the stale FGType line - is a no-op when FGType is not present (fresh installs, already migrated) Call the migration from _manual_patch_directory_impl immediately before _disable_hq_font_auto so any re-patch via the GUI heals the INI. Mirror the same logic in fgmod.sh so that games using the launch wrapper are migrated automatically on the very next launch, with no manual re-patch required. --- defaults/assets/fgmod.sh | 19 +++++++++++++++++++ main.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh index cffce14..efc1d59 100755 --- a/defaults/assets/fgmod.sh +++ b/defaults/assets/fgmod.sh @@ -137,6 +137,25 @@ fi # an external TTF that is not present. Only normalize the default auto value. sed -i 's/^UseHQFont[[:space:]]*=[[:space:]]*auto$/UseHQFont=false/' "$exe_folder_path/OptiScaler.ini" || true +# === Migrate FGType โ†’ FGInput/FGOutput (pre-v0.9-final INIs) === +# v0.9-final split the single FGType key into FGInput + FGOutput. Games that were +# patched with an older build will have FGType= with no FGInput/FGOutput, +# causing the new DLL to silently use nofg. Fix that here on every launch. +_fgtype_ini="$exe_folder_path/OptiScaler.ini" +if grep -q '^FGType=' "$_fgtype_ini" 2>/dev/null; then + _fgtype_val=$(sed -n 's/^FGType=\(.*\)/\1/p' "$_fgtype_ini") + echo "๐Ÿ”„ Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput in OptiScaler.ini" + logger -t fgmod "๐Ÿ”„ Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput" + if grep -q '^FGInput=' "$_fgtype_ini"; then + # FGInput already present โ€” INI already in v0.9-final format; just drop FGType + sed -i '/^FGType=/d' "$_fgtype_ini" || true + else + # Replace FGType=X with FGInput=X + FGOutput=X + sed -i "s/^FGType=.*$/FGInput=$_fgtype_val\nFGOutput=$_fgtype_val/" "$_fgtype_ini" || true + fi +fi +unset _fgtype_ini _fgtype_val + # === ASI Plugins Directory === if [[ -d "$fgmod_path/plugins" ]]; then echo "๐Ÿ”Œ Installing ASI plugins directory" diff --git a/main.py b/main.py index 3d63849..4092ab0 100644 --- a/main.py +++ b/main.py @@ -128,6 +128,50 @@ class Plugin: decky.logger.error(f"Failed to copy launcher scripts: {e}") return False + def _migrate_optiscaler_ini(self, ini_file): + """Migrate pre-v0.9-final OptiScaler.ini: replace FGType with FGInput + FGOutput. + + v0.9-final split the single FGType key into separate FGInput and FGOutput keys. + Games already patched with an older build will have FGType= in their + per-game INI but no FGInput/FGOutput entries, causing the new DLL to silently + fall back to nofg. This migration runs at patch-time and at every fgmod.sh + launch so users never have to manually touch their INI. + """ + try: + if not ini_file.exists(): + return False + + with open(ini_file, 'r') as f: + content = f.read() + + fg_type_match = re.search(r'^FGType\s*=\s*(\S+)', content, re.MULTILINE) + if not fg_type_match: + return True # Nothing to migrate + + fg_value = fg_type_match.group(1) + + if re.search(r'^FGInput\s*=', content, re.MULTILINE): + # FGInput already present (INI already in v0.9-final format); + # just remove the now-unknown FGType line. + content = re.sub(r'^FGType\s*=\s*\S+\n?', '', content, flags=re.MULTILINE) + decky.logger.info(f"Removed stale FGType from {ini_file} (FGInput already present)") + else: + # Replace the single FGType=X line with FGInput=X then FGOutput=X + content = re.sub( + r'^FGType\s*=\s*\S+', + f'FGInput={fg_value}\nFGOutput={fg_value}', + content, + flags=re.MULTILINE + ) + decky.logger.info(f"Migrated FGType={fg_value} โ†’ FGInput={fg_value}, FGOutput={fg_value} in {ini_file}") + + with open(ini_file, 'w') as f: + f.write(content) + return True + except Exception as e: + decky.logger.error(f"Failed to migrate OptiScaler.ini: {e}") + return False + def _disable_hq_font_auto(self, ini_file): """Disable the new HQ font auto mode to avoid missing font assertions on Wine/Proton.""" try: @@ -542,6 +586,7 @@ class Plugin: decky.logger.warning("No OptiScaler.ini found to copy") if target_ini.exists(): + self._migrate_optiscaler_ini(target_ini) self._disable_hq_font_auto(target_ini) plugins_src = fgmod_path / "plugins" -- cgit v1.2.3 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. --- defaults/assets/fgmod.sh | 8 +++++++- main.py | 19 +++++++++++++++---- 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 ++++++++++++++ 7 files changed, 77 insertions(+), 19 deletions(-) diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh index efc1d59..99ea447 100755 --- a/defaults/assets/fgmod.sh +++ b/defaults/assets/fgmod.sh @@ -223,7 +223,13 @@ if [[ $# -gt 1 ]]; then # Execute the original command export SteamDeck=0 - export WINEDLLOVERRIDES="$WINEDLLOVERRIDES,dxgi=n,b" + # Build WINEDLLOVERRIDES from the actual proxy DLL name (strip extension to get the stem) + if [[ "$dll_name" == *.dll ]]; then + _wine_dll="${dll_name%.dll}" + export WINEDLLOVERRIDES="$WINEDLLOVERRIDES,${_wine_dll}=n,b" + unset _wine_dll + fi + # .asi files are loaded by an ASI loader โ€” no WINEDLLOVERRIDES entry needed # Filter out leading -- separators (from Steam launch options) while [[ $# -gt 0 && "$1" == "--" ]]; do diff --git a/main.py b/main.py index 4092ab0..ea80561 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,16 @@ from pathlib import Path # Set to False or comment out this constant to skip the overwrite by default. UPSCALER_OVERWRITE_ENABLED = True +VALID_DLL_NAMES = { + "dxgi.dll", + "winmm.dll", + "dbghelp.dll", + "version.dll", + "wininet.dll", + "winhttp.dll", + "OptiScaler.asi", +} + INJECTOR_FILENAMES = [ "dxgi.dll", "winmm.dll", @@ -523,7 +533,7 @@ class Plugin: decky.logger.info(f"Resolved directory {directory} to absolute path {target}") return target - def _manual_patch_directory_impl(self, directory: Path) -> dict: + def _manual_patch_directory_impl(self, directory: Path, dll_name: str = "dxgi.dll") -> dict: fgmod_path = Path(decky.HOME) / "fgmod" if not fgmod_path.exists(): return { @@ -538,7 +548,6 @@ class Plugin: "message": "OptiScaler.dll not found in ~/fgmod. Reinstall OptiScaler.", } - dll_name = "dxgi.dll" preserve_ini = True try: @@ -772,14 +781,16 @@ class Plugin: async def log_error(self, error: str) -> None: decky.logger.error(f"FRONTEND: {error}") - async def manual_patch_directory(self, directory: str) -> dict: + async def manual_patch_directory(self, directory: str, dll_name: str = "dxgi.dll") -> dict: + if dll_name not in VALID_DLL_NAMES: + return {"status": "error", "message": f"Invalid proxy DLL name: {dll_name}"} try: target_dir = self._resolve_target_directory(directory) except (FileNotFoundError, NotADirectoryError, PermissionError) as exc: decky.logger.error(f"Manual patch validation failed: {exc}") return {"status": "error", "message": str(exc)} - return self._manual_patch_directory_impl(target_dir) + return self._manual_patch_directory_impl(target_dir, dll_name) async def manual_unpatch_directory(self, directory: str) -> dict: try: 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 From 51793b3b54ff771685b25ca0b67b7bb24df95aad Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:54:40 -0400 Subject: chore: bump amd_fidelityfx_upscaler_dx12.dll to bins-for-4.0.2.c --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 312d0fc..6448a0d 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,8 @@ "name": "Optiscaler_0.9.0-final.20260401._AF.7z" }, { - "sha256hash": "2604c0b392072d715b400b2f89434274de31995a4b6e68ce38250ebbd3f6c5fc", - "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/opti-9-pre-11/amd_fidelityfx_upscaler_dx12.dll", + "sha256hash": "c7720bc16bede334f59a1a32cd22edbcbbb159685ed5240e61350a5fb0bc8a94", + "url": "https://github.com/xXJSONDeruloXx/OptiScaler-Bleeding-Edge/releases/download/bins-for-4.0.2.c/amd_fidelityfx_upscaler_dx12.dll", "name": "amd_fidelityfx_upscaler_dx12.dll" }, { -- cgit v1.2.3 From 089c858feb1b6f4756bc241b14edcb2ad55dc0a0 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:55:41 -0400 Subject: fix: always emit DLL= in patch command, even for default dxgi.dll --- src/components/ClipboardCommands.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/ClipboardCommands.tsx b/src/components/ClipboardCommands.tsx index b8cf6bf..7bbd12d 100644 --- a/src/components/ClipboardCommands.tsx +++ b/src/components/ClipboardCommands.tsx @@ -1,5 +1,4 @@ import { SmartClipboardButton } from "./SmartClipboardButton"; -import { DEFAULT_PROXY_DLL } from "../utils/constants"; interface ClipboardCommandsProps { pathExists: boolean | null; @@ -9,15 +8,10 @@ interface 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 ( <> -- cgit v1.2.3 From 9e45eca97948c77b3d443258419412e9914ffa46 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 09:58:22 -0400 Subject: fix: label dxgi.dll as default in proxy DLL dropdown --- src/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index ce61263..64b98c4 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -47,7 +47,7 @@ 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: "dxgi.dll", label: "dxgi.dll (default)", 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." }, -- cgit v1.2.3 From 3d813ea87335298be5a47de3441f410651851b71 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 10:06:44 -0400 Subject: feat: add Steam game picker with one-click launch option setter --- src/components/OptiScalerControls.tsx | 13 +- src/components/SteamGamePatcher.tsx | 277 ++++++++++++++++++++++++++++++++++ src/components/index.ts | 1 + src/types.d.ts | 10 ++ 4 files changed, 296 insertions(+), 5 deletions(-) create mode 100644 src/components/SteamGamePatcher.tsx diff --git a/src/components/OptiScalerControls.tsx b/src/components/OptiScalerControls.tsx index fb5d2f8..f88e8f9 100644 --- a/src/components/OptiScalerControls.tsx +++ b/src/components/OptiScalerControls.tsx @@ -11,6 +11,7 @@ import { InstructionCard } from "./InstructionCard"; import { OptiScalerWiki } from "./OptiScalerWiki"; import { UninstallButton } from "./UninstallButton"; import { ManualPatchControls } from "./CustomPathOverride"; +import { SteamGamePatcher } from "./SteamGamePatcher"; interface OptiScalerControlsProps { pathExists: boolean | null; @@ -91,6 +92,12 @@ export function OptiScalerControls({ pathExists, setPathExists }: OptiScalerCont )} + {pathExists === true && ( + + )} + + + {!manualModeEnabled && ( - <> - - - - + )} diff --git a/src/components/SteamGamePatcher.tsx b/src/components/SteamGamePatcher.tsx new file mode 100644 index 0000000..5947f01 --- /dev/null +++ b/src/components/SteamGamePatcher.tsx @@ -0,0 +1,277 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { ButtonItem, DropdownItem, Field, PanelSectionRow } from "@decky/ui"; +import { listInstalledGames } from "../api"; +import { createAutoCleanupTimer } from "../utils"; +import { TIMEOUTS } from "../utils/constants"; + +// โ”€โ”€โ”€ SteamClient helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * Wrap the callback-based RegisterForAppDetails in a Promise. + * Resolves with the current launch options string, or "" if SteamClient is + * unavailable (e.g. desktop / dev mode). Times out after 5 seconds. + */ +const getSteamLaunchOptions = (appId: number): Promise => + new Promise((resolve, reject) => { + if ( + typeof SteamClient === "undefined" || + !SteamClient?.Apps?.RegisterForAppDetails + ) { + resolve(""); + return; + } + + let settled = false; + let unregister = () => {}; + + const timeout = window.setTimeout(() => { + if (settled) return; + settled = true; + unregister(); + reject(new Error("Timed out reading launch options.")); + }, 5000); + + const registration = SteamClient.Apps.RegisterForAppDetails( + appId, + (details: { strLaunchOptions?: string }) => { + if (settled) return; + settled = true; + window.clearTimeout(timeout); + unregister(); + resolve(details?.strLaunchOptions ?? ""); + } + ); + + unregister = registration.unregister; + }); + +const setSteamLaunchOptions = (appId: number, options: string): void => { + if ( + typeof SteamClient === "undefined" || + !SteamClient?.Apps?.SetAppLaunchOptions + ) { + throw new Error("SteamClient.Apps.SetAppLaunchOptions is not available."); + } + SteamClient.Apps.SetAppLaunchOptions(appId, options); +}; + +// โ”€โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Remove any fgmod invocation from a launch options string, keeping the rest. */ +const stripFgmod = (opts: string): string => + opts + .replace(/DLL=\S+\s+~\/fgmod\/fgmod\s+%command%/g, "") + .replace(/~\/fgmod\/fgmod\s+%command%/g, "") + .trim(); + +/** Extract the DLL= value from a launch options string, if present. */ +const extractDllName = (opts: string): string | null => { + const m = opts.match(/DLL=(\S+)\s+~\/fgmod\/fgmod/); + return m ? m[1] : null; +}; + +// โ”€โ”€โ”€ Component โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +interface SteamGamePatcherProps { + dllName: string; +} + +type GameEntry = { appid: string; name: string }; + +export function SteamGamePatcher({ dllName }: SteamGamePatcherProps) { + const [games, setGames] = useState([]); + const [gamesLoading, setGamesLoading] = useState(true); + const [selectedAppId, setSelectedAppId] = useState(""); + const [launchOptions, setLaunchOptions] = useState(""); + const [launchOptionsLoading, setLaunchOptionsLoading] = useState(false); + const [busy, setBusy] = useState(false); + const [resultMessage, setResultMessage] = useState(""); + + // Auto-clear result message + useEffect(() => { + if (resultMessage) { + return createAutoCleanupTimer( + () => setResultMessage(""), + TIMEOUTS.resultDisplay + ); + } + return undefined; + }, [resultMessage]); + + // Load game list on mount + useEffect(() => { + let cancelled = false; + (async () => { + setGamesLoading(true); + try { + const result = await listInstalledGames(); + if (cancelled) return; + if (result.status === "success" && result.games.length > 0) { + setGames(result.games); + setSelectedAppId(result.games[0].appid); + } + } catch (e) { + console.error("SteamGamePatcher: failed to load games", e); + } finally { + if (!cancelled) setGamesLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, []); + + // Reload launch options when selected game changes + useEffect(() => { + if (!selectedAppId) { + setLaunchOptions(""); + return; + } + let cancelled = false; + (async () => { + setLaunchOptionsLoading(true); + try { + const opts = await getSteamLaunchOptions(Number(selectedAppId)); + if (!cancelled) setLaunchOptions(opts); + } catch { + if (!cancelled) setLaunchOptions(""); + } finally { + if (!cancelled) setLaunchOptionsLoading(false); + } + })(); + return () => { + cancelled = true; + }; + }, [selectedAppId]); + + const targetCommand = `DLL=${dllName} ~/fgmod/fgmod %command%`; + const isManaged = launchOptions.includes("fgmod/fgmod"); + const activeDll = useMemo(() => extractDllName(launchOptions), [launchOptions]); + const selectedGame = useMemo( + () => games.find((g) => g.appid === selectedAppId) ?? null, + [games, selectedAppId] + ); + + const handleSet = useCallback(() => { + if (!selectedAppId || busy) return; + setBusy(true); + try { + setSteamLaunchOptions(Number(selectedAppId), targetCommand); + setLaunchOptions(targetCommand); + setResultMessage( + `โœ… Launch options set for ${selectedGame?.name ?? selectedAppId}` + ); + } catch (e) { + setResultMessage(`โŒ ${e instanceof Error ? e.message : String(e)}`); + } finally { + setBusy(false); + } + }, [selectedAppId, targetCommand, selectedGame, busy]); + + const handleRemove = useCallback(() => { + if (!selectedAppId || busy) return; + setBusy(true); + try { + const stripped = stripFgmod(launchOptions); + setSteamLaunchOptions(Number(selectedAppId), stripped); + setLaunchOptions(stripped); + setResultMessage( + `โœ… Removed fgmod from ${selectedGame?.name ?? selectedAppId}` + ); + } catch (e) { + setResultMessage(`โŒ ${e instanceof Error ? e.message : String(e)}`); + } finally { + setBusy(false); + } + }, [selectedAppId, launchOptions, selectedGame, busy]); + + // โ”€โ”€ Status display โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const statusText = useMemo(() => { + if (!selectedGame) return "โ€”"; + if (launchOptionsLoading) return "Loading..."; + if (!isManaged) return "Not set"; + if (activeDll && activeDll !== dllName) + return `Active โ€” ${activeDll} ยท switch to apply ${dllName}`; + return `Active โ€” ${activeDll ?? dllName}`; + }, [selectedGame, launchOptionsLoading, isManaged, activeDll, dllName]); + + const statusColor = useMemo(() => { + if (!isManaged || launchOptionsLoading) return undefined; + if (activeDll && activeDll !== dllName) return "#ffd866"; // yellow โ€” different DLL selected + return "#3fb950"; // green โ€” active and matching + }, [isManaged, launchOptionsLoading, activeDll, dllName]); + + const setButtonLabel = useMemo(() => { + if (busy) return "Applying..."; + if (!isManaged) return "Enable for this game"; + if (activeDll && activeDll !== dllName) return `Switch to ${dllName}`; + return "Re-apply"; + }, [busy, isManaged, activeDll, dllName]); + + return ( + <> + + ({ data: g.appid, label: g.name }))} + onChange={(option) => { + setSelectedAppId(String(option.data)); + setResultMessage(""); + }} + /> + + + {selectedGame && ( + <> + + + {statusColor ? ( + + {statusText} + + ) : ( + statusText + )} + + + + + + {setButtonLabel} + + + + {isManaged && ( + + + {busy ? "Removing..." : "Remove from launch options"} + + + )} + + {resultMessage && ( + + + {resultMessage} + + + )} + + )} + + ); +} diff --git a/src/components/index.ts b/src/components/index.ts index cd599ba..ad47347 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,3 +9,4 @@ export { UninstallButton } from './UninstallButton'; export { SmartClipboardButton } from './SmartClipboardButton'; export { ResultDisplay } from './ResultDisplay'; export { ManualPatchControls } from './CustomPathOverride'; +export { SteamGamePatcher } from './SteamGamePatcher'; diff --git a/src/types.d.ts b/src/types.d.ts index dfc0472..4077a9e 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -12,3 +12,13 @@ declare module "*.jpg" { const content: string; export default content; } + +declare const SteamClient: { + Apps: { + RegisterForAppDetails( + appId: number, + callback: (details: { strLaunchOptions?: string }) => void + ): { unregister: () => void }; + SetAppLaunchOptions(appId: number, options: string): void; + }; +}; -- cgit v1.2.3 From d845c74d039fd08449ce723d25958cd96a72ee06 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 10:10:36 -0400 Subject: chore: remove all emojis from source files --- defaults/assets/fgmod-uninstaller.sh | 36 +++++++++---------- defaults/assets/fgmod.sh | 62 ++++++++++++++++---------------- src/components/CustomPathOverride.tsx | 4 +-- src/components/InstalledGamesSection.tsx | 8 ++--- src/components/ResultDisplay.tsx | 4 +-- src/components/SteamGamePatcher.tsx | 8 ++--- src/utils/constants.ts | 8 ++--- 7 files changed, 64 insertions(+), 66 deletions(-) diff --git a/defaults/assets/fgmod-uninstaller.sh b/defaults/assets/fgmod-uninstaller.sh index 8c5e7b9..2fd853f 100755 --- a/defaults/assets/fgmod-uninstaller.sh +++ b/defaults/assets/fgmod-uninstaller.sh @@ -4,13 +4,13 @@ set -x exec > >(tee -i /tmp/fgmod-uninstaller.log) 2>&1 error_exit() { - echo "โŒ $1" + echo " $1" if [[ -n $STEAM_ZENITY ]]; then $STEAM_ZENITY --error --text "$1" else zenity --error --text "$1" || echo "Zenity failed to display error" fi - logger -t fgmod-uninstaller "โŒ ERROR: $1" + logger -t fgmod-uninstaller "ERROR: $1" exit 1 } @@ -101,20 +101,20 @@ cd "$exe_folder_path" || error_exit "Failed to change directory to $exe_folder_p # Verify current directory before proceeding [[ "$(pwd)" != "$exe_folder_path" ]] && error_exit "Unexpected working directory: $(pwd)" -logger -t fgmod-uninstaller "๐ŸŸข Uninstalling from: $exe_folder_path" +logger -t fgmod-uninstaller "Uninstalling from: $exe_folder_path" # === Remove OptiScaler Files === -echo "๐Ÿงน Removing OptiScaler files..." +echo " Removing OptiScaler files..." rm -f "OptiScaler.dll" "dxgi.dll" "winmm.dll" "dbghelp.dll" "version.dll" "wininet.dll" "winhttp.dll" "OptiScaler.asi" rm -f "OptiScaler.ini" "OptiScaler.log" # === Remove Nukem FG Mod Files === -echo "๐Ÿงน Removing Nukem FG Mod files..." +echo " Removing Nukem FG Mod files..." rm -f "dlssg_to_fsr3_amd_is_better.dll" "dlssg_to_fsr3.ini" "dlssg_to_fsr3.log" rm -f "nvapi64.dll" "fakenvapi.ini" "fakenvapi.log" # === Remove Supporting Libraries === -echo "๐Ÿงน Removing supporting libraries..." +echo " Removing supporting libraries..." rm -f "nvngx.dll" "nvngx.ini" # Only remove files if backups exist (to avoid removing restored originals) [[ -f "libxess.dll.b" ]] && rm -f "libxess.dll" @@ -127,49 +127,49 @@ rm -f "nvngx.dll" "nvngx.ini" [[ -f "amd_fidelityfx_vk.dll.b" ]] && rm -f "amd_fidelityfx_vk.dll" # === Remove FG Mod Files === -echo "๐Ÿงน Removing frame generation mod files..." +echo " Removing frame generation mod files..." rm -f "dlssg_to_fsr3_amd_is_better.dll" "dlssg_to_fsr3.ini" # === Remove NVAPI Files (Current and Legacy) === -echo "๐Ÿงน Removing NVAPI files..." +echo " Removing NVAPI files..." rm -f "fakenvapi.dll" "fakenvapi.ini" # Current v0.9.0-pre4 approach rm -f "nvapi64.dll" "nvapi64.dll.b" # Legacy cleanup for older versions and backups # === Remove ASI Plugins === -echo "๐Ÿงน Removing ASI plugins directory..." +echo " Removing ASI plugins directory..." rm -rf "plugins" # === Remove Legacy Files === -echo "๐Ÿงน Removing legacy files..." +echo " Removing legacy files..." rm -f "dlss-enabler.dll" "dlss-enabler-upscaler.dll" "dlss-enabler.log" rm -f "nvngx-wrapper.dll" "_nvngx.dll" rm -f "dlssg_to_fsr3_amd_is_better-3.0.dll" # === Restore Original DLLs === -echo "๐Ÿ”„ Restoring original DLLs..." +echo " Restoring original DLLs..." original_dlls=("d3dcompiler_47.dll" "amd_fidelityfx_dx12.dll" "amd_fidelityfx_framegeneration_dx12.dll" "amd_fidelityfx_upscaler_dx12.dll" "amd_fidelityfx_vk.dll" "libxess.dll" "libxess_dx11.dll" "libxess_fg.dll" "libxell.dll") for dll in "${original_dlls[@]}"; do if [[ -f "${dll}.b" ]]; then mv "${dll}.b" "$dll" - echo "โœ… Restored original $dll" - logger -t fgmod-uninstaller "โœ… Restored original $dll" + echo " Restored original $dll" + logger -t fgmod-uninstaller "Restored original $dll" fi done # === Self-remove uninstaller === -echo "๐Ÿ—‘๏ธ Removing uninstaller..." +echo " Removing uninstaller..." rm -f "fgmod-uninstaller.sh" -echo "โœ… fgmod removed from this game successfully!" -logger -t fgmod-uninstaller "โœ… fgmod removed from $exe_folder_path" +echo " fgmod removed from this game successfully!" +logger -t fgmod-uninstaller "fgmod removed from $exe_folder_path" # === Execute original command if provided === if [[ $# -gt 1 ]]; then - echo "๐Ÿš€ Launching the game..." + echo " Launching the game..." export SteamDeck=0 export WINEDLLOVERRIDES="${WINEDLLOVERRIDES},dxgi=n,b" exec >/dev/null 2>&1 exec "$@" else - echo "โœ… Uninstallation complete. No game specified to run." + echo " Uninstallation complete. No game specified to run." fi \ No newline at end of file diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh index 99ea447..decb981 100755 --- a/defaults/assets/fgmod.sh +++ b/defaults/assets/fgmod.sh @@ -4,13 +4,13 @@ set -x exec > >(tee -i /tmp/fgmod-install.log) 2>&1 error_exit() { - echo "โŒ $1" + echo " $1" if [[ -n $STEAM_ZENITY ]]; then $STEAM_ZENITY --error --text "$1" else zenity --error --text "$1" || echo "Zenity failed to display error" fi - logger -t fgmod "โŒ ERROR: $1" + logger -t fgmod "ERROR: $1" exit 1 } @@ -89,12 +89,12 @@ if [[ -d "$exe_folder_path/Engine" ]]; then exe_folder_path=$(dirname "$ue_exe") fi -[[ ! -d "$exe_folder_path" ]] && error_exit "โŒ Could not resolve game directory!" -[[ ! -w "$exe_folder_path" ]] && error_exit "๐Ÿ›‘ No write permission to the game folder!" +[[ ! -d "$exe_folder_path" ]] && error_exit " Could not resolve game directory!" +[[ ! -w "$exe_folder_path" ]] && error_exit " No write permission to the game folder!" -logger -t fgmod "๐ŸŸข Target directory: $exe_folder_path" -logger -t fgmod "๐Ÿงฉ Using DLL name: $dll_name" -logger -t fgmod "๐Ÿ“„ Preserve INI: $preserve_ini" +logger -t fgmod "Target directory: $exe_folder_path" +logger -t fgmod "Using DLL name: $dll_name" +logger -t fgmod "Preserve INI: $preserve_ini" # === Cleanup Old Injectors === rm -f "$exe_folder_path"/{dxgi.dll,winmm.dll,nvngx.dll,_nvngx.dll,nvngx-wrapper.dll,dlss-enabler.dll,OptiScaler.dll} @@ -107,25 +107,25 @@ done # === Remove nvapi64.dll and its backup (conflicts from previous fakenvapi versions) === rm -f "$exe_folder_path/nvapi64.dll" "$exe_folder_path/nvapi64.dll.b" -echo "๐Ÿงน Cleaned up nvapi64.dll and backup (legacy fakenvapi conflicts)" +echo " Cleaned up nvapi64.dll and backup (legacy fakenvapi conflicts)" # === Core Install === if [[ -f "$fgmod_path/renames/$dll_name" ]]; then - echo "โœ… Using pre-renamed $dll_name" - cp "$fgmod_path/renames/$dll_name" "$exe_folder_path/$dll_name" || error_exit "โŒ Failed to copy $dll_name" + echo " Using pre-renamed $dll_name" + cp "$fgmod_path/renames/$dll_name" "$exe_folder_path/$dll_name" || error_exit " Failed to copy $dll_name" else - echo "โš ๏ธ Pre-renamed $dll_name not found, falling back to OptiScaler.dll" - cp "$fgmod_path/OptiScaler.dll" "$exe_folder_path/$dll_name" || error_exit "โŒ Failed to copy OptiScaler.dll as $dll_name" + echo " Pre-renamed $dll_name not found, falling back to OptiScaler.dll" + cp "$fgmod_path/OptiScaler.dll" "$exe_folder_path/$dll_name" || error_exit " Failed to copy OptiScaler.dll as $dll_name" fi # === OptiScaler.ini Handling === if [[ "$preserve_ini" == "true" && -f "$exe_folder_path/OptiScaler.ini" ]]; then - echo "๐Ÿ“„ Preserving existing OptiScaler.ini (user settings retained)" - logger -t fgmod "๐Ÿ“„ Existing OptiScaler.ini preserved in $exe_folder_path" + echo " Preserving existing OptiScaler.ini (user settings retained)" + logger -t fgmod "Existing OptiScaler.ini preserved in $exe_folder_path" else - echo "๐Ÿ“„ Installing OptiScaler.ini from plugin defaults" - cp "$fgmod_path/OptiScaler.ini" "$exe_folder_path/OptiScaler.ini" || error_exit "โŒ Failed to copy OptiScaler.ini" - logger -t fgmod "๐Ÿ“„ OptiScaler.ini installed to $exe_folder_path" + echo " Installing OptiScaler.ini from plugin defaults" + cp "$fgmod_path/OptiScaler.ini" "$exe_folder_path/OptiScaler.ini" || error_exit " Failed to copy OptiScaler.ini" + logger -t fgmod "OptiScaler.ini installed to $exe_folder_path" fi # === OptiScaler env variables Handling === @@ -144,8 +144,8 @@ sed -i 's/^UseHQFont[[:space:]]*=[[:space:]]*auto$/UseHQFont=false/' "$exe_folde _fgtype_ini="$exe_folder_path/OptiScaler.ini" if grep -q '^FGType=' "$_fgtype_ini" 2>/dev/null; then _fgtype_val=$(sed -n 's/^FGType=\(.*\)/\1/p' "$_fgtype_ini") - echo "๐Ÿ”„ Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput in OptiScaler.ini" - logger -t fgmod "๐Ÿ”„ Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput" + echo " Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput in OptiScaler.ini" + logger -t fgmod "Migrating FGType=$_fgtype_val โ†’ FGInput/FGOutput" if grep -q '^FGInput=' "$_fgtype_ini"; then # FGInput already present โ€” INI already in v0.9-final format; just drop FGType sed -i '/^FGType=/d' "$_fgtype_ini" || true @@ -158,20 +158,20 @@ unset _fgtype_ini _fgtype_val # === ASI Plugins Directory === if [[ -d "$fgmod_path/plugins" ]]; then - echo "๐Ÿ”Œ Installing ASI plugins directory" + echo " Installing ASI plugins directory" cp -r "$fgmod_path/plugins" "$exe_folder_path/" || true - logger -t fgmod "๐Ÿ”Œ ASI plugins directory installed to $exe_folder_path" + logger -t fgmod "ASI plugins directory installed to $exe_folder_path" else - echo "โš ๏ธ No plugins directory found in fgmod" + echo " No plugins directory found in fgmod" fi # === D3D12_Optiscaler Directory (required for FSR4/FidelityFX DX12 path) === if [[ -d "$fgmod_path/D3D12_Optiscaler" ]]; then - echo "๐Ÿ“ฆ Installing D3D12_Optiscaler directory" + echo " Installing D3D12_Optiscaler directory" cp -r "$fgmod_path/D3D12_Optiscaler" "$exe_folder_path/" || true - logger -t fgmod "๐Ÿ“ฆ D3D12_Optiscaler directory installed to $exe_folder_path" + logger -t fgmod "D3D12_Optiscaler directory installed to $exe_folder_path" else - echo "โš ๏ธ No D3D12_Optiscaler directory found in fgmod" + echo " No D3D12_Optiscaler directory found in fgmod" fi # === Supporting Libraries === @@ -192,22 +192,22 @@ cp -f "$fgmod_path/dlssg_to_fsr3_amd_is_better.dll" "$exe_folder_path/" || true # === FakeNVAPI Files === # Remove legacy nvapi64.dll to avoid conflicts # rm -f "$exe_folder_path/nvapi64.dll" -# echo "๐Ÿงน Removed legacy nvapi64.dll" +# echo " Removed legacy nvapi64.dll" # Copy fakenvapi.dll with original name (v1.3.8.1) cp -f "$fgmod_path/fakenvapi.dll" "$exe_folder_path/" || true cp -f "$fgmod_path/fakenvapi.ini" "$exe_folder_path/" || true -echo "๐Ÿ“ฆ Installed fakenvapi.dll and fakenvapi.ini" +echo " Installed fakenvapi.dll and fakenvapi.ini" # === Additional Support Files === # cp -f "$fgmod_path/d3dcompiler_47.dll" "$exe_folder_path/" || true # Note: d3dcompiler_47.dll is not included in v0.9.0-final archive -echo "โœ… Installation completed successfully!" -echo "๐Ÿ“„ For Steam, add this to the launch options: \"$fgmod_path/fgmod\" %COMMAND%" -echo "๐Ÿ“„ For Heroic, add this as a new wrapper: \"$fgmod_path/fgmod\"" -logger -t fgmod "๐ŸŸข Installation completed successfully for $exe_folder_path" +echo " Installation completed successfully!" +echo " For Steam, add this to the launch options: \"$fgmod_path/fgmod\" %COMMAND%" +echo " For Heroic, add this as a new wrapper: \"$fgmod_path/fgmod\"" +logger -t fgmod "Installation completed successfully for $exe_folder_path" # === Execute original command === if [[ $# -gt 1 ]]; then diff --git a/src/components/CustomPathOverride.tsx b/src/components/CustomPathOverride.tsx index 14a0905..4effc6c 100644 --- a/src/components/CustomPathOverride.tsx +++ b/src/components/CustomPathOverride.tsx @@ -239,9 +239,7 @@ export const ManualPatchControls = ({ isAvailable, onManualModeChange, dllName } - โš ๏ธ - + /> )} diff --git a/src/components/InstalledGamesSection.tsx b/src/components/InstalledGamesSection.tsx index 71278d7..04d653b 100644 --- a/src/components/InstalledGamesSection.tsx +++ b/src/components/InstalledGamesSection.tsx @@ -42,14 +42,14 @@ export function InstalledGamesSection() { { try { await SteamClient.Apps.SetAppLaunchOptions(selectedGame.appid, '~/fgmod/fgmod %COMMAND%'); - setResult(`โœ“ Frame generation enabled for ${selectedGame.name}. Launch the game, enable DLSS in graphics settings, then press Insert to access OptiScaler options.`); + setResult(`Frame generation enabled for ${selectedGame.name}. Launch the game, enable DLSS in graphics settings, then press Insert to access OptiScaler options.`); } catch (error) { logError('handlePatchClick: ' + String(error)); setResult(error instanceof Error ? `Error: ${error.message}` : 'Error enabling frame generation'); @@ -64,7 +64,7 @@ export function InstalledGamesSection() { try { await SteamClient.Apps.SetAppLaunchOptions(selectedGame.appid, '~/fgmod/fgmod-uninstaller.sh %COMMAND%'); - setResult(`โœ“ Frame generation will be disabled on next launch of ${selectedGame.name}.`); + setResult(`Frame generation will be disabled on next launch of ${selectedGame.name}.`); } catch (error) { logError('handleUnpatchClick: ' + String(error)); setResult(error instanceof Error ? `Error: ${error.message}` : 'Error disabling frame generation'); @@ -96,7 +96,7 @@ export function InstalledGamesSection() { ...STYLES.preWrap, ...(result.includes('Error') ? STYLES.statusNotInstalled : STYLES.statusInstalled) }}> - {result.includes('Error') ? 'โŒ' : 'โœ…'} {result} + {result} ) : null} diff --git a/src/components/ResultDisplay.tsx b/src/components/ResultDisplay.tsx index bcd66c0..b54e41d 100644 --- a/src/components/ResultDisplay.tsx +++ b/src/components/ResultDisplay.tsx @@ -19,13 +19,13 @@ export const ResultDisplay: FC = ({ result }) => {
{isSuccess ? ( <> - โœ… {result.output?.includes("uninstall") || result.output?.includes("remov") + {result.output?.includes("uninstall") || result.output?.includes("remov") ? "OptiScaler mod removed successfully" : "OptiScaler mod installed successfully"} ) : ( <> - โŒ Error: {result.message || "Operation failed"} + Error: {result.message || "Operation failed"} )} {result.output && !isSuccess && ( diff --git a/src/components/SteamGamePatcher.tsx b/src/components/SteamGamePatcher.tsx index 5947f01..06c373c 100644 --- a/src/components/SteamGamePatcher.tsx +++ b/src/components/SteamGamePatcher.tsx @@ -159,10 +159,10 @@ export function SteamGamePatcher({ dllName }: SteamGamePatcherProps) { setSteamLaunchOptions(Number(selectedAppId), targetCommand); setLaunchOptions(targetCommand); setResultMessage( - `โœ… Launch options set for ${selectedGame?.name ?? selectedAppId}` + `Launch options set for ${selectedGame?.name ?? selectedAppId}` ); } catch (e) { - setResultMessage(`โŒ ${e instanceof Error ? e.message : String(e)}`); + setResultMessage(`Error: ${e instanceof Error ? e.message : String(e)}`); } finally { setBusy(false); } @@ -176,10 +176,10 @@ export function SteamGamePatcher({ dllName }: SteamGamePatcherProps) { setSteamLaunchOptions(Number(selectedAppId), stripped); setLaunchOptions(stripped); setResultMessage( - `โœ… Removed fgmod from ${selectedGame?.name ?? selectedAppId}` + `Removed fgmod from ${selectedGame?.name ?? selectedAppId}` ); } catch (e) { - setResultMessage(`โŒ ${e instanceof Error ? e.message : String(e)}`); + setResultMessage(`Error: ${e instanceof Error ? e.message : String(e)}`); } finally { setBusy(false); } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 64b98c4..7fa6970 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -67,14 +67,14 @@ export const TIMEOUTS = { // Message strings export const MESSAGES = { - modInstalled: "โœ… OptiScaler Mod Installed", - modNotInstalled: "โŒ OptiScaler Mod Not Installed", + modInstalled: "OptiScaler Mod Installed", + modNotInstalled: "OptiScaler Mod Not Installed", installing: "Installing OptiScaler...", installButton: "Setup OptiScaler Mod", uninstalling: "Removing OptiScaler...", uninstallButton: "Remove OptiScaler Mod", - installSuccess: "โœ… OptiScaler mod setup successfully!", - uninstallSuccess: "โœ… OptiScaler mod removed successfully.", + installSuccess: "OptiScaler mod setup successfully!", + uninstallSuccess: "OptiScaler mod removed successfully.", instructionTitle: "How to Use:", instructionText: "Click 'Copy Patch Command' or 'Copy Unpatch Command', then go to your game's properties, and paste the command into the Launch Options field.\n\nIn-game: Enable DLSS in graphics settings to unlock FSR 3.1/XeSS 2.0 in DirectX12 Games.\n\nFor extended OptiScaler options, assign a back button to a keyboard's 'Insert' key." }; -- cgit v1.2.3 From b8eed9a4f3d98d887a9cc8f18b821d6a2af4598d Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 3 Apr 2026 10:20:47 -0400 Subject: fix: remove D3D12_Optiscaler dir in fgmod-uninstaller.sh --- defaults/assets/fgmod-uninstaller.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/defaults/assets/fgmod-uninstaller.sh b/defaults/assets/fgmod-uninstaller.sh index 2fd853f..5e5143c 100755 --- a/defaults/assets/fgmod-uninstaller.sh +++ b/defaults/assets/fgmod-uninstaller.sh @@ -132,13 +132,16 @@ rm -f "dlssg_to_fsr3_amd_is_better.dll" "dlssg_to_fsr3.ini" # === Remove NVAPI Files (Current and Legacy) === echo " Removing NVAPI files..." -rm -f "fakenvapi.dll" "fakenvapi.ini" # Current v0.9.0-pre4 approach +rm -f "fakenvapi.dll" "fakenvapi.ini" # v0.9.0-final rm -f "nvapi64.dll" "nvapi64.dll.b" # Legacy cleanup for older versions and backups # === Remove ASI Plugins === echo " Removing ASI plugins directory..." rm -rf "plugins" +# === Remove D3D12_Optiscaler directory (required by v0.9.0-final) === +rm -rf "D3D12_Optiscaler" + # === Remove Legacy Files === echo " Removing legacy files..." rm -f "dlss-enabler.dll" "dlss-enabler-upscaler.dll" "dlss-enabler.log" -- cgit v1.2.3