From d6bddf23bdd3874da4d255732a5132d20fda5ede Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 22 Jun 2026 21:14:25 -0400 Subject: feat: add RDNA2 pre10 FSR4 variant --- main.py | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 3 deletions(-) (limited to 'main.py') diff --git a/main.py b/main.py index 70f1359..f657cfe 100644 --- a/main.py +++ b/main.py @@ -27,6 +27,18 @@ FSR4_OFFICIAL_411_ASSET = { "version": "4.1.1-official", } +FSR4_VALVE_411_ASSET = { + "name": "amdxcffx64_valve_2.3.0.2913.dll", + "sha256": "4e7dc37aebea3a90e3d3cc43e24cb2b54176b2535315f20dbe63b3b7cfc56b1e", + "version": "4.1.1-valve-2.3.0.2913", +} + +OPTISCALER_PRE10_ASSET = { + "name": "OptiScaler_0.10.0-pre1.20260622_135940.dll", + "sha256": "b374b19081cc066365d0c6da4808d768e16469b0cbdfc478b6e95999947d5364", + "version": "0.10.0-pre1.20260622_135940", +} + OPTIPATCHER_ASSET = { "name": "OptiPatcher_rolling.asi", "sha256": "88b9e1be3559737cd205fdf5f2c8550cf1923fb1def4c603e5bf03c3e84131b1", @@ -73,6 +85,32 @@ FSR4_VARIANTS = { "source_version": FSR4_OFFICIAL_411_ASSET["version"], } ], + "config_overrides": {}, + }, + "rdna2-valve-411-pre10": { + "label": "4.1.1 Valve RDNA2 compatibility", + "dir_name": "fsr4-rdna2-valve-411-pre10", + "sha256": "ec7ed3ca674e288240e6f04b986342aece47454c41d9b0959449e82e22bd7f6d", + "source_asset_name": OPTISCALER_ARCHIVE_ASSET["name"], + "source_version": OPTISCALER_ARCHIVE_ASSET["version"], + "uses_archive_native": True, + "injector": { + "name": "OptiScaler.dll", + "sha256": OPTISCALER_PRE10_ASSET["sha256"], + "source_asset_name": OPTISCALER_PRE10_ASSET["name"], + "source_version": OPTISCALER_PRE10_ASSET["version"], + }, + "extra_files": [ + { + "name": FSR4_DRIVER_OVERRIDE_FILENAME, + "sha256": FSR4_VALVE_411_ASSET["sha256"], + "source_asset_name": FSR4_VALVE_411_ASSET["name"], + "source_version": FSR4_VALVE_411_ASSET["version"], + } + ], + "config_overrides": { + "Fsr4ForceModel": "2", + }, }, } VARIANT_EXTRA_FILENAMES = sorted( @@ -375,6 +413,51 @@ class Plugin: def _fsr4_variant_extra_file_path(self, fgmod_path: Path, fsr4_variant: str | None, filename: str) -> Path: return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / filename + def _fsr4_variant_injector_info(self, fsr4_variant: str | None) -> dict | None: + variant = self._fsr4_variant_info(fsr4_variant) + injector = variant.get("injector") + return injector if isinstance(injector, dict) else None + + def _fsr4_variant_injector_path(self, fgmod_path: Path, fsr4_variant: str | None) -> Path | None: + injector = self._fsr4_variant_injector_info(fsr4_variant) + if not injector: + return None + return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / injector.get("name", "OptiScaler.dll") + + def _fsr4_variant_renamed_proxy_path(self, fgmod_path: Path, fsr4_variant: str | None, dll_name: str) -> Path | None: + if not self._fsr4_variant_injector_info(fsr4_variant): + return None + return self._fsr4_variant_dir(fgmod_path, fsr4_variant) / "renames" / dll_name + + def _fsr4_variant_config_overrides(self, fsr4_variant: str | None) -> dict: + variant = self._fsr4_variant_info(fsr4_variant) + overrides = variant.get("config_overrides") or {} + return dict(overrides) if isinstance(overrides, dict) else {} + + def _apply_optiscaler_ini_overrides(self, ini_file: Path, overrides: dict) -> bool: + if not overrides: + return True + try: + content = ini_file.read_text(encoding="utf-8", errors="replace") + for key, value in overrides.items(): + key = str(key).strip() + value = str(value).strip() + if not key: + continue + pattern = re.compile(rf"^{re.escape(key)}\s*=.*$", re.MULTILINE) + replacement = f"{key}={value}" + if pattern.search(content): + content = pattern.sub(replacement, content) + else: + if content and not content.endswith("\n"): + content += "\n" + content += f"{replacement}\n" + ini_file.write_text(content, encoding="utf-8") + return True + except Exception as exc: + decky.logger.error(f"Failed to apply OptiScaler.ini overrides to {ini_file}: {exc}") + return False + def _sync_variant_root_extra_files(self, fgmod_path: Path, fsr4_variant: str | None) -> None: selected_extra_files = {extra_file["name"]: extra_file for extra_file in self._fsr4_variant_extra_files(fsr4_variant)} for filename in VARIANT_EXTRA_FILENAMES: @@ -580,11 +663,15 @@ class Plugin: optiscaler_archive = bin_path / OPTISCALER_ARCHIVE_ASSET["name"] fsr4_int8_src = bin_path / FSR4_INT8_ASSET["name"] fsr4_official_411_src = bin_path / FSR4_OFFICIAL_411_ASSET["name"] + fsr4_valve_411_src = bin_path / FSR4_VALVE_411_ASSET["name"] + optiscaler_pre10_src = bin_path / OPTISCALER_PRE10_ASSET["name"] optipatcher_src = bin_path / OPTIPATCHER_ASSET["name"] for required_path, asset in [ (optiscaler_archive, OPTISCALER_ARCHIVE_ASSET), (fsr4_int8_src, FSR4_INT8_ASSET), (fsr4_official_411_src, FSR4_OFFICIAL_411_ASSET), + (fsr4_valve_411_src, FSR4_VALVE_411_ASSET), + (optiscaler_pre10_src, OPTISCALER_PRE10_ASSET), (optipatcher_src, OPTIPATCHER_ASSET), ]: if not required_path.exists(): @@ -660,6 +747,42 @@ class Plugin: "Prepared rdna34-official-411 driver override", ) + rdna2_valve_dir = extract_path / FSR4_VARIANTS["rdna2-valve-411-pre10"]["dir_name"] + rdna2_valve_dir.mkdir(parents=True, exist_ok=True) + rdna2_valve_upscaler = rdna2_valve_dir / FSR4_UPSCALER_FILENAME + shutil.copy2(native_upscaler_root, rdna2_valve_upscaler) + self._verify_bundled_asset( + rdna2_valve_upscaler, + FSR4_VARIANTS["rdna2-valve-411-pre10"]["sha256"], + "Prepared rdna2-valve-411-pre10 FSR4 upscaler", + ) + self._verify_bundled_asset( + fsr4_valve_411_src, + FSR4_VALVE_411_ASSET["sha256"], + "Bundled rdna2-valve-411-pre10 driver override", + ) + rdna2_valve_driver = rdna2_valve_dir / FSR4_DRIVER_OVERRIDE_FILENAME + shutil.copy2(fsr4_valve_411_src, rdna2_valve_driver) + self._verify_bundled_asset( + rdna2_valve_driver, + FSR4_VALVE_411_ASSET["sha256"], + "Prepared rdna2-valve-411-pre10 driver override", + ) + self._verify_bundled_asset( + optiscaler_pre10_src, + OPTISCALER_PRE10_ASSET["sha256"], + "Bundled rdna2-valve-411-pre10 OptiScaler injector", + ) + rdna2_valve_injector = rdna2_valve_dir / "OptiScaler.dll" + shutil.copy2(optiscaler_pre10_src, rdna2_valve_injector) + self._verify_bundled_asset( + rdna2_valve_injector, + OPTISCALER_PRE10_ASSET["sha256"], + "Prepared rdna2-valve-411-pre10 OptiScaler injector", + ) + if not self._create_renamed_copies(rdna2_valve_injector, rdna2_valve_dir / "renames"): + return {"status": "error", "message": "Failed to prepare renamed pre10 OptiScaler proxies."} + rdna23_dir = extract_path / FSR4_VARIANTS["rdna23-int8"]["dir_name"] rdna23_dir.mkdir(parents=True, exist_ok=True) self._verify_bundled_asset( @@ -704,6 +827,18 @@ class Plugin: "source_asset_name": variant["source_asset_name"], "source_version": variant["source_version"], "uses_archive_native": bool(variant["uses_archive_native"]), + "injector": ( + { + "name": variant["injector"]["name"], + "sha256": variant["injector"]["sha256"], + "source_asset_name": variant["injector"]["source_asset_name"], + "source_version": variant["injector"]["source_version"], + "path": str((Path(variant["dir_name"]) / variant["injector"]["name"]).as_posix()), + } + if isinstance(variant.get("injector"), dict) + else None + ), + "config_overrides": dict(variant.get("config_overrides") or {}), "extra_files": [ { "name": extra_file["name"], @@ -864,11 +999,17 @@ class Plugin: if not plugins_dir.exists() or not (plugins_dir / "OptiPatcher.asi").exists(): return {"exists": False} - for variant in FSR4_VARIANTS.values(): + for variant_id, variant in FSR4_VARIANTS.items(): variant_dir = path / variant["dir_name"] variant_path = variant_dir / FSR4_UPSCALER_FILENAME if not variant_path.exists(): return {"exists": False} + injector = variant.get("injector") + if isinstance(injector, dict): + if not (variant_dir / injector.get("name", "OptiScaler.dll")).exists(): + return {"exists": False} + if not (variant_dir / "renames" / "dxgi.dll").exists(): + return {"exists": False} for extra_file in variant.get("extra_files", []): if not (variant_dir / extra_file["name"]).exists(): return {"exists": False} @@ -917,8 +1058,13 @@ class Plugin: } preserve_ini = True + previous_marker_metadata = self._read_marker(directory / MARKER_FILENAME) + previous_variant = str(previous_marker_metadata.get("fsr4_variant") or "").strip() selected_variant = self._selected_fsr4_variant(fgmod_path, fsr4_variant) selected_variant_info = FSR4_VARIANTS[selected_variant] + selected_config_overrides = self._fsr4_variant_config_overrides(selected_variant) + if previous_variant == "rdna2-valve-411-pre10" and selected_variant != previous_variant: + selected_config_overrides.setdefault("Fsr4ForceModel", "auto") selected_upscaler_src = self._fsr4_variant_path(fgmod_path, selected_variant) if not selected_upscaler_src.exists(): selected_upscaler_src = fgmod_path / FSR4_UPSCALER_FILENAME @@ -976,9 +1122,18 @@ class Plugin: else "No original game DLLs required backup" ) - renamed = fgmod_path / "renames" / dll_name + variant_renamed = self._fsr4_variant_renamed_proxy_path(fgmod_path, selected_variant, dll_name) + variant_injector = self._fsr4_variant_injector_path(fgmod_path, selected_variant) + root_renamed = fgmod_path / "renames" / dll_name destination_dll = directory / dll_name - source_for_copy = renamed if renamed.exists() else optiscaler_dll + if variant_renamed and variant_renamed.exists(): + source_for_copy = variant_renamed + elif variant_injector and variant_injector.exists(): + source_for_copy = variant_injector + elif root_renamed.exists(): + source_for_copy = root_renamed + else: + source_for_copy = optiscaler_dll shutil.copy2(source_for_copy, destination_dll) decky.logger.info(f"Copied injector DLL from {source_for_copy} to {destination_dll}") @@ -995,6 +1150,7 @@ class Plugin: if target_ini.exists(): self._migrate_optiscaler_ini(target_ini) self._disable_hq_font_auto(target_ini) + self._apply_optiscaler_ini_overrides(target_ini, selected_config_overrides) plugins_src = fgmod_path / "plugins" plugins_dest = directory / "plugins" -- cgit v1.2.3