summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2026-05-19 11:43:14 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2026-05-19 11:43:14 -0400
commitef0b58b8836e6e53b0a32d569159f769853413e5 (patch)
treee130ab8779b72e80094476322b2a912cc4e74517
parent94d1f366e5d7c7f8860000cce73643b2b8cfb0dd (diff)
downloadDecky-Framegen-fix/proxy-backup-idempotency.tar.gz
Decky-Framegen-fix/proxy-backup-idempotency.zip
fix: preserve proxy dll backups on repatchfix/proxy-backup-idempotency
-rwxr-xr-xdefaults/assets/fgmod-uninstaller.sh4
-rwxr-xr-xdefaults/assets/fgmod.sh62
-rw-r--r--main.py101
3 files changed, 144 insertions, 23 deletions
diff --git a/defaults/assets/fgmod-uninstaller.sh b/defaults/assets/fgmod-uninstaller.sh
index 5e5143c..3d59eda 100755
--- a/defaults/assets/fgmod-uninstaller.sh
+++ b/defaults/assets/fgmod-uninstaller.sh
@@ -150,8 +150,8 @@ rm -f "dlssg_to_fsr3_amd_is_better-3.0.dll"
# === Restore 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
+restorable_dlls=("dxgi.dll" "winmm.dll" "dbghelp.dll" "version.dll" "wininet.dll" "winhttp.dll" "OptiScaler.asi" "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 "${restorable_dlls[@]}"; do
if [[ -f "${dll}.b" ]]; then
mv "${dll}.b" "$dll"
echo " Restored original $dll"
diff --git a/defaults/assets/fgmod.sh b/defaults/assets/fgmod.sh
index 8d0b77b..ed8aaf5 100755
--- a/defaults/assets/fgmod.sh
+++ b/defaults/assets/fgmod.sh
@@ -96,8 +96,66 @@ 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}
+proxy_backup_files=(
+ "dxgi.dll"
+ "winmm.dll"
+ "dbghelp.dll"
+ "version.dll"
+ "wininet.dll"
+ "winhttp.dll"
+ "OptiScaler.asi"
+)
+
+cleanup_files=(
+ "${proxy_backup_files[@]}"
+ "OptiScaler.dll"
+ "nvngx.dll"
+ "_nvngx.dll"
+ "nvngx-wrapper.dll"
+ "nvngx.ini"
+ "dlss-enabler.dll"
+ "dlss-enabler-upscaler.dll"
+ "fakenvapi.log"
+ "OptiScaler.log"
+ "dlssg_to_fsr3.log"
+ "dlssg_to_fsr3_amd_is_better-3.0.dll"
+)
+
+is_bundled_proxy_copy() {
+ local existing_file="$1"
+ local bundled_copy="$fgmod_path/renames/$(basename "$existing_file")"
+ [[ -f "$existing_file" && -f "$bundled_copy" ]] && cmp -s "$existing_file" "$bundled_copy"
+}
+
+has_patch_fingerprint() {
+ local fingerprint
+ for fingerprint in "FRAMEGEN_PATCH" "OptiScaler.ini" "fakenvapi.dll" "fakenvapi.ini" "dlssg_to_fsr3_amd_is_better.dll" "D3D12_Optiscaler"; do
+ [[ -e "$exe_folder_path/$fingerprint" ]] && return 0
+ done
+ return 1
+}
+
+# === Backup Pre-existing Proxy DLLs Before Cleanup ===
+for dll in "${proxy_backup_files[@]}"; do
+ existing_path="$exe_folder_path/$dll"
+ backup_path="$exe_folder_path/$dll.b"
+ if [[ -f "$existing_path" && ! -f "$backup_path" ]]; then
+ if has_patch_fingerprint || is_bundled_proxy_copy "$existing_path"; then
+ logger -t fgmod "Skipping backup for managed/stale proxy copy: $dll"
+ else
+ mv -f "$existing_path" "$backup_path"
+ echo " Backed up pre-existing $dll"
+ logger -t fgmod "Backed up pre-existing proxy file: $dll"
+ fi
+ fi
+done
+unset existing_path backup_path fingerprint
+
+# === Cleanup Old Injectors / Legacy OptiScaler Artifacts ===
+for cleanup_file in "${cleanup_files[@]}"; do
+ rm -f "$exe_folder_path/$cleanup_file"
+done
+unset cleanup_file
# === Optional: Backup 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")
diff --git a/main.py b/main.py
index 6c7d338..3faa1e9 100644
--- a/main.py
+++ b/main.py
@@ -4,6 +4,7 @@ import subprocess
import json
import shutil
import re
+import filecmp
from datetime import datetime, timezone
from pathlib import Path
@@ -11,7 +12,7 @@ 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 = {
+PROXY_DLL_BACKUPS = [
"dxgi.dll",
"winmm.dll",
"dbghelp.dll",
@@ -19,11 +20,12 @@ VALID_DLL_NAMES = {
"wininet.dll",
"winhttp.dll",
"OptiScaler.asi",
-}
+]
+
+VALID_DLL_NAMES = set(PROXY_DLL_BACKUPS)
INJECTOR_FILENAMES = [
- "dxgi.dll",
- "winmm.dll",
+ *PROXY_DLL_BACKUPS,
"nvngx.dll",
"_nvngx.dll",
"nvngx-wrapper.dll",
@@ -31,6 +33,27 @@ INJECTOR_FILENAMES = [
"OptiScaler.dll",
]
+PATCH_CLEANUP_FILES = [
+ *INJECTOR_FILENAMES,
+ "nvapi64.dll",
+ "nvapi64.dll.b",
+ "nvngx.ini",
+ "dlss-enabler-upscaler.dll",
+ "fakenvapi.log",
+ "OptiScaler.log",
+ "dlssg_to_fsr3.log",
+ "dlssg_to_fsr3_amd_is_better-3.0.dll",
+]
+
+PATCH_FINGERPRINT_FILES = [
+ "FRAMEGEN_PATCH",
+ "OptiScaler.ini",
+ "fakenvapi.dll",
+ "fakenvapi.ini",
+ "dlssg_to_fsr3_amd_is_better.dll",
+ "D3D12_Optiscaler",
+]
+
ORIGINAL_DLL_BACKUPS = [
"d3dcompiler_47.dll",
"amd_fidelityfx_dx12.dll",
@@ -39,6 +62,11 @@ ORIGINAL_DLL_BACKUPS = [
"amd_fidelityfx_vk.dll",
]
+RESTORABLE_BACKUP_FILES = [
+ *PROXY_DLL_BACKUPS,
+ *ORIGINAL_DLL_BACKUPS,
+]
+
SUPPORT_FILES = [
"libxess.dll",
"libxess_dx11.dll",
@@ -81,6 +109,7 @@ LEGACY_FILES = [
"dlss-enabler.dll",
"dlss-enabler-upscaler.dll",
"dlss-enabler.log",
+ "nvngx.ini",
"nvngx-wrapper.dll",
"_nvngx.dll",
"dlssg_to_fsr3_amd_is_better-3.0.dll",
@@ -157,6 +186,33 @@ class Plugin:
decky.logger.error(f"Failed to copy launcher scripts: {e}")
return False
+ def _files_match(self, file_a: Path, file_b: Path) -> bool:
+ try:
+ return file_a.exists() and file_b.exists() and filecmp.cmp(file_a, file_b, shallow=False)
+ except Exception:
+ return False
+
+ def _is_bundled_proxy_copy(self, file_path: Path, fgmod_path: Path) -> bool:
+ bundled_copy = fgmod_path / "renames" / file_path.name
+ return self._files_match(file_path, bundled_copy)
+
+ def _has_patch_fingerprint(self, directory: Path) -> bool:
+ return any((directory / filename).exists() for filename in PATCH_FINGERPRINT_FILES)
+
+ def _backup_preexisting_proxy_files(self, directory: Path, fgmod_path: Path) -> list[str]:
+ backed_up: list[str] = []
+ already_patched = self._has_patch_fingerprint(directory)
+ for filename in PROXY_DLL_BACKUPS:
+ source = directory / filename
+ backup = directory / f"{filename}.b"
+ if not source.exists() or backup.exists():
+ continue
+ if already_patched or self._is_bundled_proxy_copy(source, fgmod_path):
+ continue
+ shutil.move(source, backup)
+ backed_up.append(filename)
+ return backed_up
+
def _migrate_optiscaler_ini(self, ini_file):
"""Migrate pre-v0.9-final OptiScaler.ini: replace FGType with FGInput + FGOutput.
@@ -575,13 +631,24 @@ class Plugin:
try:
decky.logger.info(f"Manual patch started for {directory}")
- removed_injectors = []
- for filename in INJECTOR_FILENAMES:
+ backed_up_proxies = self._backup_preexisting_proxy_files(directory, fgmod_path)
+ decky.logger.info(
+ f"Backed up pre-existing proxy files: {backed_up_proxies}"
+ if backed_up_proxies
+ else "No pre-existing proxy files required backup"
+ )
+
+ removed_patch_files = []
+ for filename in dict.fromkeys(PATCH_CLEANUP_FILES):
path = directory / filename
if path.exists():
path.unlink()
- removed_injectors.append(filename)
- decky.logger.info(f"Removed injector DLLs: {removed_injectors}" if removed_injectors else "No injector DLLs found to remove")
+ removed_patch_files.append(filename)
+ decky.logger.info(
+ f"Removed stale patch files: {removed_patch_files}"
+ if removed_patch_files
+ else "No stale patch files found to remove"
+ )
backed_up_originals = []
for dll in ORIGINAL_DLL_BACKUPS:
@@ -590,15 +657,11 @@ class Plugin:
if source.exists() and not backup.exists():
shutil.move(source, backup)
backed_up_originals.append(dll)
- decky.logger.info(f"Backed up original DLLs: {backed_up_originals}" if backed_up_originals else "No original DLLs required backup")
-
- removed_legacy = []
- for legacy in ["nvapi64.dll", "nvapi64.dll.b"]:
- legacy_path = directory / legacy
- if legacy_path.exists():
- legacy_path.unlink()
- removed_legacy.append(legacy)
- decky.logger.info(f"Removed legacy files: {removed_legacy}" if removed_legacy else "No legacy files to remove")
+ decky.logger.info(
+ f"Backed up original game DLLs: {backed_up_originals}"
+ if backed_up_originals
+ else "No original game DLLs required backup"
+ )
renamed = fgmod_path / "renames" / dll_name
destination_dll = directory / dll_name
@@ -704,7 +767,7 @@ class Plugin:
decky.logger.info(f"Removed D3D12_Optiscaler directory from {d3d12_dir}")
restored_backups = []
- for dll in ORIGINAL_DLL_BACKUPS:
+ for dll in dict.fromkeys(RESTORABLE_BACKUP_FILES):
backup = directory / f"{dll}.b"
original = directory / dll
if backup.exists():
@@ -1131,7 +1194,7 @@ class Plugin:
if result["status"] != "success":
return result
- backed_up = [dll for dll in ORIGINAL_DLL_BACKUPS if (target_dir / f"{dll}.b").exists()]
+ backed_up = [dll for dll in dict.fromkeys(RESTORABLE_BACKUP_FILES) if (target_dir / f"{dll}.b").exists()]
marker_path = target_dir / MARKER_FILENAME
self._write_marker(
marker_path,