Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@ Windows app that keeps an OptiScaler-NR + ReShade + DLSS setup current across al
It pulls the latest releases of:

- [OptiScaler-DLSSNR-PreSR-Multipass](https://github.com/wilsjo2/OptiScaler-DLSSNR-PreSR-Multipass/releases) (standard package, prereleases included)
- [MFGAdaUnlock-RenoDx](https://github.com/mavismmg/MFGAdaUnlock-RenoDx/releases) (`renodx-mfgunlock.addon64`)
- [NVIDIA DLSS](https://github.com/NVIDIA/DLSS) runtime DLLs: `nvngx_dlss.dll` (SR), `nvngx_dlssd.dll` (RR), `nvngx_dlssg.dll` (FG)
- [NVIDIA Streamline](https://github.com/NVIDIA-RTX/Streamline) (optional, signed `sl.*.dll` set for Dynamic MFG)
- [ReShade](https://reshade.me) with full add-on support (`ReShade64.dll` taken from the official setup)
- [MFGAdaUnlock-RenoDx](https://github.com/mavismmg/MFGAdaUnlock-RenoDx/releases) (`renodx-mfgunlock.addon64`), only if you turn on [unofficial components](#unofficial-components-opt-in)

and installs them next to each game's executable, together with your own `nvngx_dlssnr.dll`.
and installs them next to each game's executable.

## Unofficial components (opt-in)

Two components are **off by default** and only installed after you turn on *Settings → General → Unofficial components* and confirm the warning:

- the **DLSSNR runtime** (`nvngx_dlssnr.dll`), a file you supply yourself; DLSS Updater never downloads or ships it;
- **MFG Unlock**, a third-party ReShade add-on that patches NVIDIA frame generation so 3x/4x runs on RTX 40.

Neither is made or supported by NVIDIA. They come from unofficial sources, may break or crash games, and may trip anti-cheat and get an account banned. You are responsible for using them.

The app checks the imported `nvngx_dlssnr.dll` for a valid NVIDIA Authenticode signature. Any other file is shown as *unverified* and is only installed because you opted in.

Turning the option off stops installing and updating both. Nothing is removed from games that already have them; **Uninstall** does that. If you upgraded from 1.5.x and already had either component, the option starts on and the app shows a one-time notice about the change.

## Download

Expand All @@ -26,7 +39,7 @@ Both keep settings and downloads in `%LocalAppData%\DLSSUpdater`, so you can swi
1. Finds the real game exe (Unreal `*-Win64-Shipping.exe`, Unity players, etc.; you can pick another folder).
2. Copies `OptiScaler.dll` as the chosen proxy (`dxgi.dll` by default) plus its `OptiScaler\` backend folder.
3. Builds `OptiScaler.ini` from the release ini plus your settings and patches `ReShade.ini` (keybinds, MFG Unlock options). Out of the box nothing is changed from what the upstream projects ship, except `LoadReshade=true` when ReShade is installed and `[DlssNr] Enabled=true` when the DLSSNR runtime is installed (both default to off upstream).
4. Places `ReShade64.dll`, `renodx-mfgunlock.addon64` and `nvngx_dlssnr.dll` beside it.
4. Places `ReShade64.dll` beside it, plus `renodx-mfgunlock.addon64` and `nvngx_dlssnr.dll` when unofficial components are on.
5. Replaces every `nvngx_dlss*.dll` in the game with the latest NVIDIA build, or with a pinned version (globally or per game, also downgrading). Games that ship no DLSS have this off by default; OptiScaler still gets an `nvngx_dlss.dll` so it can offer DLSS.
6. Optionally replaces all `sl.*.dll` the game ships with one matching Streamline release (never mixed, never adds plugins).
7. Moves anything it replaces into `.dlssupdater\backup` next to the exe. **Uninstall** and **Restore DLSS** put the originals back.
Expand All @@ -37,7 +50,7 @@ Settings also pre-configures DLSSNR (passes, model resolution, style, strengths)

Settings has key-capture fields for the OptiScaler hotkeys (menu, FPS overlay, frame generation, DLSSNR) and ReShade (overlay, effects, screenshot, reload).

**Install mode**: OptiScaler-NR (default, the main purpose) or **ReShade + add-ons**, which installs ReShade itself as the proxy (`dxgi.dll`, `d3d12.dll`, `d3d11.dll`, `d3d9.dll`, `dinput8.dll` or `opengl32.dll`) with MFG Unlock and the DLSS / Streamline updates, without OptiScaler or DLSSNR. Set the default in Settings and override it per game; switching removes the other mode's files.
**Install mode**: OptiScaler-NR (default) or **ReShade + add-ons**, which installs ReShade itself as the proxy (`dxgi.dll`, `d3d12.dll`, `d3d11.dll`, `d3d9.dll`, `dinput8.dll` or `opengl32.dll`) with its add-ons and the DLSS / Streamline updates, without OptiScaler or DLSSNR. Set the default in Settings and override it per game; switching removes the other mode's files.

**Presets**: save the whole OptiScaler / ReShade configuration (options, keybinds, overrides) under a name, load it, or assign it to single games. The built-in **Recommended (ApolloF)** preset holds the tuned setup (DLSS upscaler, preset override, overlay on **Del**, DLSSNR strengths and HDR tone mapping, ReShade tutorial skipped). **Reset to defaults** goes back to the upstream defaults.

Expand All @@ -49,11 +62,11 @@ Settings has key-capture fields for the OptiScaler hotkeys (menu, FPS overlay, f

Games are discovered from Steam, Epic, GOG, EA, Ubisoft and `XboxGames` folders. Standalone games can be added one by one or as a library folder whose subfolders are games.

Games with anti-cheat (EAC, BattlEye, GameGuard, …) are flagged and need an extra confirmation before anything is injected. DLSS-only swaps work without it.
Games with anti-cheat (EAC, BattlEye, GameGuard, …) are flagged and need an extra confirmation before anything is injected. Detection only looks for known anti-cheat files, so a game without a flag can still have anti-cheat. **DLSS only** swaps skip the confirmation because they inject nothing and are generally lower risk, but they are not guaranteed safe: some anti-cheat checks game files and may block the game or its online modes, and no anti-cheat vendor promises that a swap won't lead to a ban. Use any of this in online games at your own risk; the NVIDIA app's driver-level DLSS override changes no game files.

## First run

`nvngx_dlssnr.dll` can't be redistributed. Put it next to `DLSSUpdater.exe` on first launch, or import it under **Settings → Local components**. It's stored in `%LocalAppData%\DLSSUpdater\components`.
Out of the box the app updates DLSS / Streamline, OptiScaler and ReShade. The DLSSNR runtime and MFG Unlock stay off until you opt in (see [Unofficial components](#unofficial-components-opt-in)). If you do, import your own `nvngx_dlssnr.dll` under **Settings → Local components** (or put it next to `DLSSUpdater.exe` before the first launch). It's stored in `%LocalAppData%\DLSSUpdater\components`.

ReShade is downloaded from reshade.me on the first install that needs it (add-on build, cached per version). To use your own build, import `ReShade64.dll` the same way; **Use download** switches back.

Expand Down
26 changes: 26 additions & 0 deletions docs/NAMING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Name candidates

"DLSS Updater" leads with an NVIDIA trademark and is already taken: `Recol.DLSSUpdater` is on winget (Recol/DLSS-Updater), and Drommedhar/DlssUpdater also exists. This page lists names without "DLSS", "NVIDIA", "RTX", "GeForce" or other NVIDIA marks. "DLSS" can still describe what the app does in a tagline, e.g. "Shimsmith: keeps DLSS, OptiScaler and ReShade current". **Nothing has been renamed.**

Checked on 2026-10-04 with `winget search "<name>"`, `gh search repos "<name>"` and a web search. No formal trademark search (EUIPO / USPTO / DPMA) has been done yet.

| # | Name | winget | GitHub | Web | Collision |
|---|------|--------|--------|-----|-----------|
| 1 | **Shimsmith** | no package | no repos | no product found | None. "Shim" (a drop-in compatibility DLL) fits the job. |
| 2 | **Swapwright** | no package | samuelarogbonlo/SwapWright (0 stars, DeFi swap bot) | only that bot | Minor. It suggests crypto "swaps", and "wright" is easy to misspell. |
| 3 | **Upscalekeeper** | no package | no repos | nothing specific | None, but it is long and generic, so the brand is weak. |
| 4 | **Pixelwright** | no package | 5 small repos (0 stars, e.g. R3mmurd/PixelWright, a pixel-art editor) | Pixelwright Digital, an Apple app development and audit company | Moderate. An active software company uses the name, so check the trademark registers first. |
| 5 | **Kitkeeper** | no package | 4 small repos (0 stars) | kitkeeper.co.uk (active); KitKeeper library SaaS (shut down 2024) | Minor to moderate. Several commercial uses, and "kit" says little about graphics. |

**Top two:** Shimsmith, then Swapwright.

Rejected after the same checks:
- **Framewright**: many same-name video, photo and AI-film tools.
- **Lensforge**: an established paid lens-design app (Ripplon LensForge).
- **Fresko**: several unrelated commercial apps.
- **Modkeeper**: same niche; Nexus Mods already has a "Mod Keeper" tool.
- **Tuneframe**: several existing apps, and it reads as audio.

Before picking one:
1. Search EUIPO TMview and USPTO in classes 9 and 42, and check the domain and the GitHub org name.
2. A new name also means a new exe name, installer AppId and data folder (`%LocalAppData%\DLSSUpdater`), so plan a migration.
2 changes: 1 addition & 1 deletion src/DLSSUpdater/Addon/AddonServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static Services CreateServices()
AppPaths.Ensure();
var settings = AppSettings.Load();
var gh = new GitHubClient { Token = settings.GitHubToken };
var store = new ComponentStore(gh, () => settings.IncludePrereleases);
var store = new ComponentStore(gh, () => settings.IncludePrereleases) { AllowUnofficial = () => settings.AllowUnofficial };
return new Services { Settings = settings, Store = store, Installer = new Installer(store) };
}

Expand Down
13 changes: 10 additions & 3 deletions src/DLSSUpdater/Core/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ public sealed class AppSettings
public bool InstallReShade { get; set; } = true;
/// <summary>Download the ReShade add-on build from reshade.me when no ReShade64.dll was imported.</summary>
public bool AutoDownloadReShade { get; set; } = true;
public bool InstallMfgUnlock { get; set; } = true;
public bool InstallDlssNr { get; set; } = true;
/// <summary>Opt-in for the DLSSNR runtime and MFG Unlock (see <see cref="UnofficialComponents"/>); off by default.</summary>
public bool AllowUnofficial { get; set; }
/// <summary>The one-time notice about <see cref="AllowUnofficial"/> hasn't been shown since an upgrade.</summary>
public bool UnofficialNoticePending { get; set; }
/// <summary>0 = written by 1.5.x or earlier.</summary>
public int SettingsVersion { get; set; }
public bool InstallMfgUnlock { get; set; }
public bool InstallDlssNr { get; set; }
public bool AddMissingDlss { get; set; } = true;
/// <summary>Legacy (1.0/1.1); false maps to IniMode "fresh".</summary>
public bool CarryOverGameIni { get; set; } = true;
Expand Down Expand Up @@ -84,6 +90,7 @@ public static AppSettings Load()
var s = JsonSerializer.Deserialize(File.ReadAllText(AppPaths.SettingsFile), JsonCtx.Default.AppSettings);
if (s is not null)
{
if (UnofficialComponents.Migrate(s, UnofficialComponents.PresentOnDisk)) s.Save();
s.Games = new Dictionary<string, GameOverride>(s.Games, StringComparer.OrdinalIgnoreCase);
if (!s.CarryOverGameIni && s.IniMode == "keep") s.IniMode = "fresh";
s.CarryOverGameIni = true;
Expand All @@ -96,7 +103,7 @@ public static AppSettings Load()
{
Log.Error("settings.json unreadable, using defaults", ex);
}
return new AppSettings();
return new AppSettings { SettingsVersion = UnofficialComponents.SettingsVersion };
}

public void Save()
Expand Down
106 changes: 106 additions & 0 deletions src/DLSSUpdater/Core/Authenticode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace DLSSUpdater.Core;

public enum SignatureStatus { NvidiaSigned, Unverified }

/// <summary>
/// Checks a DLL's embedded Authenticode signature: a chain Windows trusts (WinVerifyTrust) and NVIDIA Corporation as
/// the signer. No hash lists: anything else, including a modified NVIDIA binary, is reported as unverified.
/// </summary>
public static class Authenticode
{
public const string NvidiaSigner = "NVIDIA Corporation";

public static SignatureStatus Check(string path)
{
if (!File.Exists(path) || !IsTrusted(path)) return SignatureStatus.Unverified;
try
{
using var cert = new X509Certificate2(X509Certificate.CreateFromSignedFile(path));
return IsNvidiaSigner(cert.GetNameInfo(X509NameType.SimpleName, false))
? SignatureStatus.NvidiaSigned
: SignatureStatus.Unverified;
}
catch (CryptographicException) { return SignatureStatus.Unverified; }
}

internal static bool IsNvidiaSigner(string? commonName) => string.Equals(commonName, NvidiaSigner, StringComparison.Ordinal);

public static string Label(SignatureStatus? status) => status switch
{
SignatureStatus.NvidiaSigned => "NVIDIA-signed",
SignatureStatus.Unverified => "unverified",
_ => "",
};

// ---------- WinVerifyTrust ----------

private static readonly Guid GenericVerifyV2 = new("00AAC56B-CD44-11d0-8CC2-00C04FC295EE");
private const uint UiNone = 2, RevokeNone = 0, ChoiceFile = 1, StateVerify = 1, StateClose = 2;
// Offline-safe: no network fetches for revocation lists during the check.
private const uint CacheOnlyUrlRetrieval = 0x1000;

/// <summary>The embedded signature is valid and chains to a root Windows trusts (any signer).</summary>
internal static bool IsTrusted(string path)
{
var pathPtr = Marshal.StringToHGlobalUni(path);
var file = new FileInfoStruct { Size = (uint)Marshal.SizeOf<FileInfoStruct>(), FilePath = pathPtr };
var filePtr = Marshal.AllocHGlobal(Marshal.SizeOf<FileInfoStruct>());
try
{
Marshal.StructureToPtr(file, filePtr, false);
var data = new TrustData
{
Size = (uint)Marshal.SizeOf<TrustData>(),
UiChoice = UiNone,
RevocationChecks = RevokeNone,
UnionChoice = ChoiceFile,
File = filePtr,
StateAction = StateVerify,
ProvFlags = CacheOnlyUrlRetrieval,
};
var result = WinVerifyTrust(IntPtr.Zero, GenericVerifyV2, ref data);
data.StateAction = StateClose;
WinVerifyTrust(IntPtr.Zero, GenericVerifyV2, ref data);
return result == 0;
}
finally
{
Marshal.FreeHGlobal(filePtr);
Marshal.FreeHGlobal(pathPtr);
}
}

[StructLayout(LayoutKind.Sequential)]
private struct FileInfoStruct
{
public uint Size;
public IntPtr FilePath;
public IntPtr File;
public IntPtr KnownSubject;
}

[StructLayout(LayoutKind.Sequential)]
private struct TrustData
{
public uint Size;
public IntPtr PolicyCallbackData;
public IntPtr SipClientData;
public uint UiChoice;
public uint RevocationChecks;
public uint UnionChoice;
public IntPtr File;
public uint StateAction;
public IntPtr StateData;
public IntPtr UrlReference;
public uint ProvFlags;
public uint UiContext;
public IntPtr SignatureSettings;
}

[DllImport("wintrust.dll", ExactSpelling = true)]
private static extern int WinVerifyTrust(IntPtr hwnd, [MarshalAs(UnmanagedType.LPStruct)] Guid action, ref TrustData data);
}
16 changes: 8 additions & 8 deletions src/DLSSUpdater/Core/ComponentStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ public sealed partial class ComponentStore
public const string MfgFile = "renodx-mfgunlock.addon64";
public static readonly string[] DlssFiles = ["nvngx_dlss.dll", "nvngx_dlssd.dll", "nvngx_dlssg.dll"];

/// <summary>SHA-256 values published in the fork's INSTALL-DLSSNR.md.</summary>
public static readonly Dictionary<string, string> KnownDlssNr = new(StringComparer.OrdinalIgnoreCase)
{
["e16bcf15e16e13f527491cdf7845b2fe6521a738d8f7c9c721866a8496e1fc8e"] = "NVIDIA original · RTX 50",
["e67dee209320cdafe0e93e45675d7aa34323a53acc57a72b2e40a181581c989a"] = "ShortFuse compat · RTX 20–50",
};

private readonly GitHubClient _gh;
private readonly Func<bool> _includePrereleases;
private readonly Dictionary<Component, SemaphoreSlim> _locks = new()
Expand All @@ -73,6 +66,8 @@ public ComponentStore(GitHubClient gh, Func<bool> includePrereleases)
public ReleaseInfo? ReShade { get; internal set; }
/// <summary>Download ReShade automatically when none was imported.</summary>
public Func<bool> AutoReShade { get; set; } = () => true;
/// <summary>The user opted in to unofficial components; otherwise MFG Unlock isn't even looked up.</summary>
public Func<bool> AllowUnofficial { get; set; } = () => true;
/// <summary>Every DLSS SDK release, newest first, for pinning an older version.</summary>
public IReadOnlyList<ReleaseInfo> DlssReleases { get; internal set; } = [];

Expand Down Expand Up @@ -106,9 +101,10 @@ public string? CurrentReShadePath

public async Task RefreshAsync(CancellationToken ct)
{
if (!AllowUnofficial()) Mfg = null;
await Task.WhenAll(
Resolve(Component.OptiScaler, ResolveOptiAsync, v => Opti = v, ct),
Resolve(Component.MfgUnlock, ResolveMfgAsync, v => Mfg = v, ct),
AllowUnofficial() ? Resolve(Component.MfgUnlock, ResolveMfgAsync, v => Mfg = v, ct) : Task.CompletedTask,
Resolve(Component.Dlss, ResolveDlssAsync, v => Dlss = v, ct),
Resolve(Component.Streamline, ResolveStreamlineAsync, v => Streamline = v, ct),
Resolve(Component.ReShade, ResolveReShadeAsync, v => ReShade = v, ct));
Expand Down Expand Up @@ -245,6 +241,10 @@ private static string DlssRawUrl(string refName, string file) =>

public static bool IsCached(Component c, string tag) => File.Exists(Path.Combine(TagDir(c, tag), CompleteMarker));

/// <summary>Any release of <paramref name="c"/> was ever downloaded completely.</summary>
public static bool AnyCached(Component c) =>
Directory.Exists(ComponentDir(c)) && Directory.EnumerateFiles(ComponentDir(c), CompleteMarker, SearchOption.AllDirectories).Any();

private static ReleaseInfo? NewestCached(Component c)
{
var dir = ComponentDir(c);
Expand Down
10 changes: 5 additions & 5 deletions src/DLSSUpdater/Core/ConfigProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,23 @@ public static ConfigPreset RecommendedPreset() =>
/// <summary>
/// Keys without which an installed component does nothing (upstream ships both as false): ReShade loaded by
/// OptiScaler and DLSSNR turned on. Added unless the profile sets the key itself; like profile values they
/// never replace a value the game's own ini already has.
/// never replace a value the game's own ini already has. <paramref name="keepDlssNr"/>: see <see cref="InstallOptions.KeepDlssNr"/>.
/// </summary>
public static List<IniOverride> Required(InstallOptions o)
public static List<IniOverride> Required(InstallOptions o, bool keepDlssNr = false)
{
var list = new List<IniOverride>();
if (!o.Opti) return list;
if (o.ReShade) list.Add(new("Plugins", "LoadReshade", "true"));
if (o.DlssNr) list.Add(new("DlssNr", "Enabled", "true"));
if (o.DlssNr || keepDlssNr) list.Add(new("DlssNr", "Enabled", "true"));
return list;
}

/// <summary>The profile plus every <see cref="Required"/> key it doesn't set itself.</summary>
public static List<IniOverride> WithRequired(IEnumerable<IniOverride> profile, InstallOptions o)
public static List<IniOverride> WithRequired(IEnumerable<IniOverride> profile, InstallOptions o, bool keepDlssNr = false)
{
var list = profile.ToList();
var ids = list.Select(x => x.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
list.AddRange(Required(o).Where(r => !ids.Contains(r.Id)));
list.AddRange(Required(o, keepDlssNr).Where(r => !ids.Contains(r.Id)));
return list;
}

Expand Down
Loading