Decompiled source of DSPAASR v1.2.0

DSPAAMod.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using BepInEx;
using BepInEx.Configuration;
using DSPAAMod.Core;
using DSPAAMod.Game;
using DSPAAMod.Interop;
using DSPAAMod.UI;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Experimental.Rendering;
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
using UnityEngine.PostProcessing;
using UnityEngine.Rendering;
using UnityEngine.UI;
using UnityStandardAssets.ImageEffects;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("NordLandeW")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds anti-aliasing, DLSS/AMD FSR super resolution and frame generation to Dyson Sphere Program.")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+d9acbc2eda5311a22be61cd68392d9aaa4407c0e")]
[assembly: AssemblyProduct("DSPAASR")]
[assembly: AssemblyTitle("DSPAAMod")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DSPAAMod
{
	[BepInPlugin("dspaa.mod", "DSPAASR", "1.2.0")]
	[BepInProcess("DSPGAME.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "dspaa.mod";

		private ConfigEntry<AaTechnique> technique;

		private ConfigEntry<ModelSelection> model;

		private ConfigEntry<ResolutionMode> resolution;

		private ConfigEntry<float> fsrSharpness;

		private ConfigEntry<FrameGenerationBackend> frameGeneration;

		private ConfigEntry<FrameGenerationMode> frameGenerationMode;

		private ConfigEntry<uint> generatedFrames;

		private ConfigEntry<uint> frameLimitMicroseconds;

		private ConfigEntry<ReflexMode> reflex;

		private ConfigEntry<float> dynamicTargetFrameRate;

		private ConfigEntry<KeyboardShortcut> captureShortcut;

		private NativeBridge native;

		private Harmony harmony;

		private bool stopped;

		internal static Plugin Instance { get; private set; }

		internal RenderController Renderer { get; private set; }

		internal SettingsSession Settings { get; private set; }

		internal GraphicsOptions Options { get; private set; }

		internal FrameGenerationController Presentation { get; private set; }

		internal FrameGenerationSettingsSession FrameGeneration { get; private set; }

		internal PresentationStartupState StartupState => PresentationStartup.Read(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location)).State;

		internal bool CanRequestFrameGeneration(FrameGenerationBackend backend)
		{
			if (Presentation == null)
			{
				return backend == FrameGenerationBackend.Off;
			}
			NativePresentationStatus caps = Presentation?.Status ?? default(NativePresentationStatus);
			if (caps.Available || backend == FrameGenerationBackend.Off)
			{
				return PresentationAvailability.CanRequest(caps, StartupState, backend, runtimeFilesPresent: false);
			}
			string directory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			bool runtimeFilesPresent = ((backend != FrameGenerationBackend.Fsr) ? (Has("sl.interposer.dll") && Has("sl.common.dll") && Has("sl.dlss_g.dll") && Has("sl.reflex.dll") && Has("sl.pcl.dll") && Has("nvngx_dlssg.dll")) : (Has("amd_fidelityfx_loader_dx12.dll") && Has("amd_fidelityfx_framegeneration_dx12.dll")));
			return PresentationAvailability.CanRequest(caps, StartupState, backend, runtimeFilesPresent);
			bool Has(string name)
			{
				return File.Exists(Path.Combine(directory, name));
			}
		}

		private void Awake()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Expected O, but got Unknown
			Instance = this;
			try
			{
				technique = ((BaseUnityPlugin)this).Config.Bind<AaTechnique>("Antialiasing", "Technique", AaTechnique.Original, "Original preserves game AA; Fxaa/Taa use the game's filters; Dlss/Fsr use the selected resolution mode. Legacy Dlaa migrates to Dlss with native-resolution DLAA. Changing this file requires restart.");
				model = ((BaseUnityPlugin)this).Config.Bind<ModelSelection>("Antialiasing", "Model", ModelSelection.Recommended, "Recommended: K for DLAA/Quality/Balanced, M for Performance, L for UltraPerformance. Explicit CNN/K/L/M overrides persist independently of resolution mode.");
				resolution = ((BaseUnityPlugin)this).Config.Bind<ResolutionMode>("Antialiasing", "Resolution", ResolutionMode.Dlaa, "DLSS/FSR resolution mode: Dlaa means native-resolution AA (DLAA or FSR Native AA); SR modes query the selected SDK for actual lower world-render dimensions. Model selection remains independent. Changing this file requires restart.");
				fsrSharpness = ((BaseUnityPlugin)this).Config.Bind<float>("FSR", "Sharpness", 0f, "FSR RCAS sharpening, 0 (disabled) to 1 (maximum). Independent of DLSS model. Changing this file requires restart.");
				captureShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Diagnostics", "CaptureShortcut", new KeyboardShortcut((KeyCode)291, (KeyCode[])(object)new KeyCode[2]
				{
					(KeyCode)306,
					(KeyCode)304
				}), "Explicitly trace two Unity frames in LogOutput.log and capture consecutive DLSS submissions (raw color/depth/motion/output) into BepInEx/cache/DSPAAMod/captures. An incomplete pair fails at the end of the trace window. Nothing runs until this shortcut is pressed; captures can be large. Changing this file requires restart.");
				AaSettings initial;
				try
				{
					initial = new AaSettings(technique.Value, model.Value, resolution.Value, fsrSharpness.Value);
				}
				catch (ArgumentOutOfRangeException)
				{
					initial = AaSettings.Default;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Invalid settings; using safe defaults without overwriting the config.");
				}
				Settings = new SettingsSession(initial);
				frameGeneration = ((BaseUnityPlugin)this).Config.Bind<FrameGenerationBackend>("FrameGeneration", "Backend", FrameGenerationBackend.Off, "Off/Fsr/Dlss, independently of AA/SR. The preloader creates the presentation bridge only when FG is selected at startup. Enabling FG without an active bridge requires restart; an existing bridge supports live switching and remains until game exit even after FG is turned Off.");
				frameGenerationMode = ((BaseUnityPlugin)this).Config.Bind<FrameGenerationMode>("FrameGeneration", "Mode", FrameGenerationMode.Fixed, "Fixed or DLSS Dynamic; availability is queried from the current SDK/device. Dynamic is not eAuto.");
				generatedFrames = ((BaseUnityPlugin)this).Config.Bind<uint>("FrameGeneration", "AdditionalFrames", 1u, "Number of additional frames, not display multiplier. FSR=1; DLSS upper limit comes from the runtime (MFG hardware required above 1).");
				reflex = ((BaseUnityPlugin)this).Config.Bind<ReflexMode>("FrameGeneration", "Reflex", ReflexMode.On, "DLSS Reflex Off/On/OnWithBoost. Off retains before-input Sleep/PCL but pauses FG; this never changes driver settings.");
				dynamicTargetFrameRate = ((BaseUnityPlugin)this).Config.Bind<float>("FrameGeneration", "DynamicTargetFrameRate", 0f, "DLSS Dynamic target; 0 uses the monitor. VSync/driver restrictions still apply. No vendor-independent performance gate.");
				frameLimitMicroseconds = ((BaseUnityPlugin)this).Config.Bind<uint>("FrameGeneration", "FrameLimitMicroseconds", 0u, "Reflex frame limiter interval in microseconds; 0 disables that limiter. Does not rewrite the game's own frame-limit or VSync options.");
				FrameGenerationSettings frameGenerationSettings;
				try
				{
					frameGenerationSettings = new FrameGenerationSettings(frameGeneration.Value, frameGenerationMode.Value, generatedFrames.Value, reflex.Value, dynamicTargetFrameRate.Value, frameLimitMicroseconds.Value);
				}
				catch (ArgumentException)
				{
					frameGenerationSettings = FrameGenerationSettings.Default;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Invalid FG settings; using Off without overwriting the config.");
				}
				FrameGeneration = new FrameGenerationSettingsSession(frameGenerationSettings);
				try
				{
					Presentation = new FrameGenerationController(this, AcquireNative(), frameGenerationSettings, delegate(string text)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)text);
					}, delegate(string text)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)text);
					});
				}
				catch (Exception ex3)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Frame generation unavailable; AA/SR remain independent: " + ex3.Message));
				}
				Renderer = new RenderController(initial, AcquireNative, delegate(string text)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)text);
				}, delegate(string text)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)text);
				});
				Renderer.Presentation = Presentation;
				Renderer.Capture = new FrameCapture(Path.Combine(Paths.CachePath, "DSPAAMod", "captures"), delegate(string text)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)text);
				});
				Options = new GraphicsOptions(this);
				harmony = new Harmony("dspaa.mod");
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"DSPAASR loaded. DLSS and FSR use the selected AA/SR resolution mode; animated objects may exhibit visual artifacts. No driver settings are modified.");
			}
			catch (Exception ex4)
			{
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				Guard(delegate
				{
					Presentation?.Dispose();
				});
				Guard(delegate
				{
					Renderer?.Shutdown();
				});
				Instance = null;
				((BaseUnityPlugin)this).Logger.LogError((object)ex4);
			}
		}

		private NativeBridge AcquireNative()
		{
			if (native == null)
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
				string dataDirectory = Path.Combine(Paths.CachePath, "DSPAAMod");
				try
				{
					native = new NativeBridge(directoryName, dataDirectory);
				}
				catch (NativeModuleLoadException ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)ex);
					throw;
				}
			}
			return native;
		}

		internal void ApplySettings()
		{
			Settings.Apply();
			technique.Value = Settings.Applied.Technique;
			model.Value = Settings.Applied.Model;
			resolution.Value = Settings.Applied.Resolution;
			fsrSharpness.Value = Settings.Applied.FsrSharpness;
			((BaseUnityPlugin)this).Config.Save();
			Renderer.Configure(Settings.Applied);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("AA settings applied: " + Settings.Applied.Technique.ToString() + ", resolution " + Settings.Applied.Resolution.ToString() + ", model " + Settings.Applied.Model));
		}

		private void StoreFrameGeneration(FrameGenerationSettings value)
		{
			frameGeneration.Value = value.Backend;
			frameGenerationMode.Value = value.Mode;
			generatedFrames.Value = value.GeneratedFrames;
			reflex.Value = value.Reflex;
			dynamicTargetFrameRate.Value = value.DynamicTargetFrameRate;
			frameLimitMicroseconds.Value = value.FrameLimitMicroseconds;
		}

		internal void ApplyFrameGeneration()
		{
			FrameGeneration.Apply(delegate(FrameGenerationSettings value)
			{
				bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
				try
				{
					StoreFrameGeneration(value);
					((BaseUnityPlugin)this).Config.Save();
				}
				catch
				{
					StoreFrameGeneration(FrameGeneration.Applied);
					throw;
				}
				finally
				{
					((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				}
			});
			FrameGenerationSettings applied = FrameGeneration.Applied;
			NativePresentationStatus nativePresentationStatus = Presentation?.Status ?? default(NativePresentationStatus);
			if (Presentation != null && !nativePresentationStatus.Available && !nativePresentationStatus.Quarantined && StartupState == PresentationStartupState.Inactive)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)((applied.Backend == FrameGenerationBackend.Off) ? "Frame generation remains Off; native presentation retained." : "Frame generation request saved; restart the game to check support and enable the presentation bridge."));
			}
			else if (Presentation == null || !Presentation.Apply(applied))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Frame generation request could not be activated; check presentation status.");
			}
		}

		internal void Guard(Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		private void Update()
		{
			if ((Object)(object)Instance != (Object)(object)this || stopped)
			{
				return;
			}
			Guard(delegate
			{
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				Renderer.Update();
				Presentation?.Update();
				Options.Update();
				KeyboardShortcut value = captureShortcut.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					if (Settings.Applied.Technique == AaTechnique.Dlss)
					{
						Renderer.RequestCapture();
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Select DLSS before requesting a frame capture.");
					}
				}
			});
		}

		private void OnDestroy()
		{
			if (!((Object)(object)Instance != (Object)(object)this) && !stopped)
			{
				stopped = true;
				Guard(delegate
				{
					Presentation?.Dispose();
				});
				Guard(delegate
				{
					Renderer.Shutdown();
				});
				Guard(delegate
				{
					Options.Dispose();
				});
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				Instance = null;
			}
		}
	}
}
namespace DSPAAMod.UI
{
	internal sealed class FrameGenerationOptions : IDisposable
	{
		private readonly Plugin plugin;

		private readonly UIComboBox backend;

		private readonly UIComboBox multiplier;

		private readonly UIComboBox reflex;

		private FrameGenerationBackend[] backendChoices = Array.Empty<FrameGenerationBackend>();

		private FrameMultiplierChoice[] multiplierChoices = Array.Empty<FrameMultiplierChoice>();

		private readonly ReflexMode[] reflexChoices = GraphicsMenuChoices.ReflexModes();

		private readonly Text backendLabel;

		private readonly Text multiplierLabel;

		private readonly Text reflexLabel;

		private readonly Text status;

		private readonly RectTransform root;

		private readonly float step;

		private bool synchronizing;

		private bool showDetails;

		private int statusRows;

		private uint shownFlags;

		private uint shownMaximum;

		private uint shownActive;

		private uint shownRequested;

		private uint shownSdkStatus;

		private FrameGenerationSettings shownDraft;

		private FrameGenerationSettings shownApplied;

		private PresentationStartupState shownStartup;

		private readonly Color warningColor;

		private static bool Chinese => (Localization.CurrentLanguageLCID & 0x3FF) == 4;

		private NativePresentationStatus Capabilities => plugin.Presentation?.Status ?? default(NativePresentationStatus);

		public int Rows => 1 + (showDetails ? 2 : 0) + statusRows;

		public bool Changed
		{
			get
			{
				NativePresentationStatus capabilities = Capabilities;
				if ((capabilities.Flags & 0xFFFFFF9Fu) == shownFlags && capabilities.MaximumGeneratedFrames == shownMaximum && capabilities.ActiveBackend == shownActive && capabilities.RequestedBackend == shownRequested && capabilities.SdkStatus == shownSdkStatus && plugin.StartupState == shownStartup && plugin.FrameGeneration.Draft.Equals(shownDraft))
				{
					return !plugin.FrameGeneration.Applied.Equals(shownApplied);
				}
				return true;
			}
		}

		public FrameGenerationOptions(Plugin owner, UIComboBox template, Text label, Text statusTemplate, RectTransform content, float spacing)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			plugin = owner;
			root = content;
			step = spacing;
			try
			{
				backend = GraphicsOptions.Clone(template, "DSPAASR Frame Generation");
				multiplier = GraphicsOptions.Clone(template, "DSPAASR FG Multiplier");
				reflex = GraphicsOptions.Clone(template, "DSPAASR Reflex");
				backendLabel = CloneLabel(label, "DSPAASR FG Label");
				multiplierLabel = CloneLabel(label, "DSPAASR FG Multiplier Label");
				reflexLabel = CloneLabel(label, "DSPAASR Reflex Label");
				status = Object.Instantiate<Text>(statusTemplate, ((Component)label).transform.parent);
				((Object)status).name = "DSPAASR FG Status";
				warningColor = ((Graphic)statusTemplate).color;
				((Graphic)status).color = ((Graphic)label).color;
				((UnityEvent)backend.onItemIndexChange).AddListener(new UnityAction(BackendChanged));
				((UnityEvent)multiplier.onItemIndexChange).AddListener(new UnityAction(MultiplierChanged));
				((UnityEvent)reflex.onItemIndexChange).AddListener(new UnityAction(ReflexChanged));
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		private static Text CloneLabel(Text source, string name)
		{
			Text val = Object.Instantiate<Text>(source, ((Component)source).transform.parent);
			((Object)val).name = name;
			Localizer[] components = ((Component)val).GetComponents<Localizer>();
			for (int i = 0; i < components.Length; i++)
			{
				((Behaviour)components[i]).enabled = false;
			}
			return val;
		}

		public void Refresh()
		{
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			NativePresentationStatus capabilities = Capabilities;
			FrameGenerationSettings draft = plugin.FrameGeneration.Draft;
			shownFlags = capabilities.Flags & 0xFFFFFF9Fu;
			shownMaximum = capabilities.MaximumGeneratedFrames;
			shownActive = capabilities.ActiveBackend;
			shownRequested = capabilities.RequestedBackend;
			shownSdkStatus = capabilities.SdkStatus;
			shownDraft = draft;
			shownApplied = plugin.FrameGeneration.Applied;
			shownStartup = plugin.StartupState;
			bool flag = plugin.CanRequestFrameGeneration(FrameGenerationBackend.Dlss);
			bool flag2 = DlssDetailsKnown(capabilities);
			synchronizing = true;
			try
			{
				backendLabel.text = (Chinese ? "帧生成" : "Frame generation");
				multiplierLabel.text = (Chinese ? "帧生成倍率" : "Frame multiplier");
				reflexLabel.text = "NVIDIA Reflex";
				backendChoices = GraphicsMenuChoices.Backends(plugin.CanRequestFrameGeneration(FrameGenerationBackend.Fsr), flag);
				GraphicsOptions.SetItems(backend, Array.ConvertAll(backendChoices, BackendLabel), Array.IndexOf(backendChoices, draft.Backend), BackendLabel(draft.Backend));
				multiplierChoices = GraphicsMenuChoices.Multipliers(flag, flag2, capabilities.MaximumGeneratedFrames, capabilities.DynamicSupported);
				GraphicsOptions.SetItems(multiplier, Array.ConvertAll(multiplierChoices, (FrameMultiplierChoice choice) => MultiplierLabel(choice.Mode, choice.GeneratedFrames)), GraphicsMenuChoices.FindMultiplier(multiplierChoices, draft), MultiplierLabel(draft.Mode, draft.GeneratedFrames));
				GraphicsOptions.SetItems(reflex, Array.ConvertAll(reflexChoices, ReflexLabel), Array.IndexOf(reflexChoices, draft.Reflex), ReflexLabel(draft.Reflex));
				showDetails = draft.Backend == FrameGenerationBackend.Dlss;
				((Component)multiplier).gameObject.SetActive(showDetails);
				((Component)multiplierLabel).gameObject.SetActive(showDetails);
				((Component)reflex).gameObject.SetActive(showDetails);
				((Component)reflexLabel).gameObject.SetActive(showDetails);
				status.text = StatusText(capabilities, draft, out var warning);
				if (draft.Backend == FrameGenerationBackend.Dlss && flag && GraphicsMenuChoices.FindMultiplier(multiplierChoices, draft) < 0)
				{
					string text = ((!flag2) ? (Chinese ? "所请求的帧生成倍率尚未检测。" : "The requested frame multiplier has not been checked.") : (Chinese ? "所请求的帧生成倍率不可用。" : "The requested frame multiplier is unavailable."));
					Text obj = status;
					obj.text = obj.text + ((status.text.Length == 0) ? "" : "\n") + text;
					warning = warning || flag2;
				}
				((Graphic)status).color = (warning ? warningColor : ((Graphic)backendLabel).color);
				((Component)status).gameObject.SetActive(status.text.Length != 0);
				statusRows = GraphicsOptions.MeasureNoteRows(status, step);
			}
			finally
			{
				synchronizing = false;
			}
		}

		private static bool DlssDetailsKnown(NativePresentationStatus caps)
		{
			if (caps.Available && !caps.Quarantined)
			{
				return caps.ActiveBackend == 2;
			}
			return false;
		}

		private static string BackendLabel(FrameGenerationBackend value)
		{
			switch (value)
			{
			default:
				return "DLSS";
			case FrameGenerationBackend.Fsr:
				return "FSR 2×";
			case FrameGenerationBackend.Off:
				if (!Chinese)
				{
					return "Off";
				}
				return "关闭";
			}
		}

		private static string MultiplierLabel(FrameGenerationMode mode, uint generatedFrames)
		{
			if (mode != FrameGenerationMode.Dynamic)
			{
				return (ulong)generatedFrames + 1uL + "×";
			}
			if (!Chinese)
			{
				return "Dynamic";
			}
			return "自适应";
		}

		private static string ReflexLabel(ReflexMode value)
		{
			switch (value)
			{
			default:
				if (!Chinese)
				{
					return "On + Boost";
				}
				return "开启并增强";
			case ReflexMode.On:
				if (!Chinese)
				{
					return "On";
				}
				return "开启";
			case ReflexMode.Off:
				if (!Chinese)
				{
					return "Off";
				}
				return "关闭";
			}
		}

		private string StatusText(NativePresentationStatus caps, FrameGenerationSettings value, out bool warning)
		{
			warning = false;
			if (caps.Quarantined)
			{
				warning = true;
				if (!Chinese)
				{
					return "Frame generation was disabled after an error. Restart the game.";
				}
				return "帧生成因错误已停用,请重启游戏。";
			}
			if (!caps.Available)
			{
				if (plugin.Presentation != null && plugin.StartupState == PresentationStartupState.Inactive && value.Backend == FrameGenerationBackend.Off)
				{
					return string.Empty;
				}
				if (PresentationAvailability.NeedsRestart(caps, plugin.StartupState, value) && plugin.CanRequestFrameGeneration(value.Backend))
				{
					string text = ((!value.Equals(plugin.FrameGeneration.Applied)) ? (Chinese ? "应用设置并重启游戏,以检测支持并启用帧生成。" : "Apply settings and restart the game to check support and enable frame generation.") : (Chinese ? "设置已保存。重启游戏以检测支持并启用帧生成。" : "Settings saved. Restart the game to check support and enable frame generation."));
					if (value.Backend == FrameGenerationBackend.Dlss && value.Reflex == ReflexMode.Off)
					{
						return (Chinese ? "DLSS 帧生成需要开启 NVIDIA Reflex。" : "DLSS frame generation requires NVIDIA Reflex.") + "\n" + text;
					}
					return text;
				}
				warning = true;
				if (!Chinese)
				{
					return "Frame generation is unavailable. Check the mod installation and restart the game.";
				}
				return "帧生成不可用,请检查模组安装并重启游戏。";
			}
			if (value.Backend == FrameGenerationBackend.Off)
			{
				if (!caps.FsrRuntimePresent && !caps.DlssSupported)
				{
					warning = true;
					if (!Chinese)
					{
						return "Frame generation is unavailable. Check the GPU, driver and mod installation.";
					}
					return "帧生成不可用,请检查显卡、驱动与模组安装。";
				}
				return string.Empty;
			}
			if (value.Backend == FrameGenerationBackend.Fsr && !caps.FsrRuntimePresent)
			{
				warning = true;
				if (!Chinese)
				{
					return "FSR frame generation files are missing. Reinstall the mod.";
				}
				return "缺少 FSR 帧生成组件,请重新安装模组。";
			}
			if (value.Backend == FrameGenerationBackend.Dlss && !caps.DlssSupported)
			{
				warning = true;
				if (!Chinese)
				{
					return "DLSS frame generation is unavailable. Check the GPU, driver and mod installation.";
				}
				return "DLSS 帧生成不可用,请检查显卡、驱动与模组安装。";
			}
			if (!value.Equals(plugin.FrameGeneration.Applied))
			{
				if (value.Backend == FrameGenerationBackend.Dlss && value.Reflex == ReflexMode.Off)
				{
					if (!Chinese)
					{
						return "DLSS frame generation requires NVIDIA Reflex.";
					}
					return "DLSS 帧生成需要开启 NVIDIA Reflex。";
				}
				if (!Chinese)
				{
					return "Apply settings to use these changes.";
				}
				return "应用设置后生效。";
			}
			if (value.Backend == FrameGenerationBackend.Dlss && value.Reflex == ReflexMode.Off)
			{
				if (!Chinese)
				{
					return "Enable NVIDIA Reflex to use DLSS frame generation.";
				}
				return "开启 NVIDIA Reflex 以启用 DLSS 帧生成。";
			}
			if (caps.ActiveBackend == 2 && value.Backend == FrameGenerationBackend.Dlss && caps.SdkStatus != 0)
			{
				warning = true;
				if (!Chinese)
				{
					return "DLSS frame generation is currently unavailable. Check the log for details.";
				}
				return "DLSS 帧生成暂不可用,请检查日志。";
			}
			if (caps.RequestedBackend != (uint)value.Backend || caps.ActiveBackend != (uint)value.Backend)
			{
				if (!Chinese)
				{
					return "Settings applied. Frame generation is not active yet.";
				}
				return "设置已应用,帧生成尚未启用。";
			}
			return string.Empty;
		}

		public void Layout(float firstCenter, float left)
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			Position(((Component)backend).transform, firstCenter);
			Position(((Component)backendLabel).transform, firstCenter);
			Position(((Component)multiplier).transform, firstCenter - step);
			Position(((Component)multiplierLabel).transform, firstCenter - step);
			Position(((Component)reflex).transform, firstCenter - 2f * step);
			Position(((Component)reflexLabel).transform, firstCenter - 2f * step);
			Position(((Component)status).transform, firstCenter - step * ((float)((!showDetails) ? 1 : 3) + (float)(statusRows - 1) * 0.5f));
			Vector3 val = ((Transform)root).InverseTransformPoint(((Transform)((Graphic)status).rectTransform).position);
			val.x = left;
			((Transform)((Graphic)status).rectTransform).position = ((Transform)root).TransformPoint(val);
		}

		private void Position(Transform transform, float y)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)transform;
			RectTransform obj = root;
			Rect rect = val.rect;
			float y2 = ((Transform)obj).InverseTransformPoint(((Transform)val).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))).y;
			Vector3 val2 = ((Transform)root).InverseTransformPoint(((Transform)val).position);
			val2.y += y - y2;
			((Transform)val).position = ((Transform)root).TransformPoint(val2);
		}

		private void BackendChanged()
		{
			if (!synchronizing && backend.itemIndex >= 0 && backend.itemIndex < backendChoices.Length)
			{
				FrameGenerationBackend frameGenerationBackend = backendChoices[backend.itemIndex];
				if (!plugin.CanRequestFrameGeneration(frameGenerationBackend))
				{
					Refresh();
					return;
				}
				FrameGenerationSettings draft = plugin.FrameGeneration.Draft;
				plugin.FrameGeneration.Draft = new FrameGenerationSettings(frameGenerationBackend, (frameGenerationBackend != FrameGenerationBackend.Fsr) ? draft.Mode : FrameGenerationMode.Fixed, (frameGenerationBackend == FrameGenerationBackend.Fsr) ? 1u : draft.GeneratedFrames, draft.Reflex, draft.DynamicTargetFrameRate, draft.FrameLimitMicroseconds);
				plugin.Options.Refresh();
			}
		}

		private void MultiplierChanged()
		{
			if (!synchronizing && showDetails && multiplier.itemIndex >= 0 && multiplier.itemIndex < multiplierChoices.Length)
			{
				NativePresentationStatus capabilities = Capabilities;
				FrameGenerationSettings draft = plugin.FrameGeneration.Draft;
				FrameMultiplierChoice frameMultiplierChoice = multiplierChoices[multiplier.itemIndex];
				FrameGenerationSettings frameGenerationSettings = new FrameGenerationSettings(draft.Backend, frameMultiplierChoice.Mode, frameMultiplierChoice.GeneratedFrames, draft.Reflex, draft.DynamicTargetFrameRate, draft.FrameLimitMicroseconds);
				if (GraphicsMenuChoices.FindMultiplier(GraphicsMenuChoices.Multipliers(plugin.CanRequestFrameGeneration(FrameGenerationBackend.Dlss), DlssDetailsKnown(capabilities), capabilities.MaximumGeneratedFrames, capabilities.DynamicSupported), frameGenerationSettings) < 0)
				{
					Refresh();
				}
				else
				{
					plugin.FrameGeneration.Draft = frameGenerationSettings;
				}
			}
		}

		private void ReflexChanged()
		{
			if (!synchronizing && showDetails && reflex.itemIndex >= 0 && reflex.itemIndex < reflexChoices.Length)
			{
				FrameGenerationSettings draft = plugin.FrameGeneration.Draft;
				plugin.FrameGeneration.Draft = new FrameGenerationSettings(draft.Backend, draft.Mode, draft.GeneratedFrames, reflexChoices[reflex.itemIndex], draft.DynamicTargetFrameRate, draft.FrameLimitMicroseconds);
			}
		}

		private static void Remove(Component value)
		{
			if (Object.op_Implicit((Object)(object)value))
			{
				value.gameObject.SetActive(false);
				Object.Destroy((Object)(object)value.gameObject);
			}
		}

		public void Dispose()
		{
			Remove((Component)(object)backend);
			Remove((Component)(object)multiplier);
			Remove((Component)(object)reflex);
			Remove((Component)(object)backendLabel);
			Remove((Component)(object)multiplierLabel);
			Remove((Component)(object)reflexLabel);
			Remove((Component)(object)status);
		}
	}
	internal sealed class GraphicsOptions : IDisposable
	{
		private readonly Plugin plugin;

		private UIOptionWindow window;

		private UIComboBox technique;

		private UIComboBox resolution;

		private UIComboBox configuration;

		private AaChoice[] techniqueChoices = Array.Empty<AaChoice>();

		private FrameGenerationOptions frameGeneration;

		private RectTransform layoutRoot;

		private Vector2 originalContentSize;

		private Vector2 configurationPosition;

		private Vector2 configurationLabelPosition;

		private Vector2 availabilityPosition;

		private Text availabilityLabel;

		private UpscalerAvailability shownAvailability;

		private UpscalerAvailability shownFsrAvailability;

		private float rowStep;

		private bool originalConfigurationLabelActive;

		private readonly Dictionary<RectTransform, Vector2> movedRows = new Dictionary<RectTransform, Vector2>();

		private AaMenuDraft draft;

		private Text aaLabel;

		private Text resolutionLabel;

		private Text configurationLabel;

		private Localizer aaLocalizer;

		private Localizer configurationLocalizer;

		private string originalAaLabel;

		private string originalConfigurationLabel;

		private bool originalAaActive;

		private bool originalFxaaActive;

		private bool originalAaLocalized;

		private bool originalConfigurationLocalized;

		private bool synchronizing;

		private int nativeRefreshMsaa;

		private bool nativeRefreshFxaa;

		private UIComboBox openedDropdown;

		private Vector3 dropdownPosition;

		private readonly Vector3[] dropdownCorners = (Vector3[])(object)new Vector3[4];

		private static bool Chinese => (Localization.CurrentLanguageLCID & 0x3FF) == 4;

		public GraphicsOptions(Plugin owner)
		{
			plugin = owner;
		}

		private static int MsaaFromIndex(int index)
		{
			return index switch
			{
				3 => 8, 
				2 => 4, 
				1 => 2, 
				_ => 0, 
			};
		}

		public void Open(UIOptionWindow value)
		{
			plugin.Settings.Open();
			plugin.FrameGeneration.Open();
			plugin.Renderer.RequestUpscalerSupport();
			if ((Object)(object)window != (Object)(object)value)
			{
				Dispose();
				try
				{
					Install(value);
				}
				catch
				{
					Dispose();
					throw;
				}
			}
			draft = new AaMenuDraft(plugin.Settings.Draft, MsaaFromIndex(window.msaaComp.itemIndex), window.fxaaComp.isOn);
			Refresh();
		}

		private void Install(UIOptionWindow value)
		{
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Expected O, but got Unknown
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Expected O, but got Unknown
			//IL_0574: Unknown result type (might be due to invalid IL or missing references)
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_057f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0586: Unknown result type (might be due to invalid IL or missing references)
			//IL_0598: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_062c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0636: Expected O, but got Unknown
			//IL_0648: Unknown result type (might be due to invalid IL or missing references)
			//IL_0652: Expected O, but got Unknown
			//IL_0664: Unknown result type (might be due to invalid IL or missing references)
			//IL_066e: Expected O, but got Unknown
			//IL_069f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a9: Expected O, but got Unknown
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0526: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Unknown result type (might be due to invalid IL or missing references)
			window = value;
			UIComboBox msaaComp = window.msaaComp;
			originalAaActive = ((Component)msaaComp).gameObject.activeSelf;
			originalFxaaActive = ((Component)window.fxaaComp).gameObject.activeSelf;
			if (msaaComp.Items.Count != 4)
			{
				throw new InvalidOperationException("Another mod changed the MSAA list; refusing to overwrite its controls.");
			}
			aaLabel = FindLabel(window, "MSAA");
			configurationLabel = FindLabel(window, "FXAA");
			if (!Object.op_Implicit((Object)(object)aaLabel) || !Object.op_Implicit((Object)(object)configurationLabel))
			{
				throw new InvalidOperationException("Cannot identify the native AA labels by their localization keys.");
			}
			originalAaLabel = aaLabel.text;
			originalConfigurationLabel = configurationLabel.text;
			originalConfigurationLabelActive = ((Component)configurationLabel).gameObject.activeSelf;
			aaLocalizer = ((Component)aaLabel).GetComponent<Localizer>();
			configurationLocalizer = ((Component)configurationLabel).GetComponent<Localizer>();
			if (Object.op_Implicit((Object)(object)aaLocalizer))
			{
				originalAaLocalized = ((Behaviour)aaLocalizer).enabled;
				((Behaviour)aaLocalizer).enabled = false;
			}
			if (Object.op_Implicit((Object)(object)configurationLocalizer))
			{
				originalConfigurationLocalized = ((Behaviour)configurationLocalizer).enabled;
				((Behaviour)configurationLocalizer).enabled = false;
			}
			Transform parent = ((Component)msaaComp).transform.parent;
			Transform parent2 = ((Component)aaLabel).transform.parent;
			if (!((Object)(object)((Component)configurationLabel).transform.parent != (Object)(object)parent2) && !((Object)(object)((Component)window.fxaaComp).transform.parent != (Object)(object)parent) && !((Object)(object)parent.parent != (Object)(object)parent2.parent))
			{
				Transform parent3 = parent.parent;
				RectTransform val = (RectTransform)(object)((parent3 is RectTransform) ? parent3 : null);
				if (val != null)
				{
					layoutRoot = val;
					originalContentSize = val.sizeDelta;
					RectTransform val2 = (RectTransform)((Component)aaLabel).transform;
					RectTransform val3 = (RectTransform)((Component)configurationLabel).transform;
					Rect rect = val2.rect;
					float y = ((Transform)val).InverseTransformPoint(((Transform)val2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))).y;
					rect = val3.rect;
					float y2 = ((Transform)val).InverseTransformPoint(((Transform)val3).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))).y;
					float num = y - y2;
					if (num <= 0f)
					{
						throw new InvalidOperationException("Invalid graphics-row spacing.");
					}
					rowStep = num;
					ShiftRows(parent2, y2, num);
					ShiftRows(parent, y2, num);
					val.sizeDelta = originalContentSize + new Vector2(0f, num);
					technique = Clone(msaaComp, "DSPAAMod Antialiasing");
					resolution = Clone(msaaComp, "DSPAAMod Resolution");
					configuration = Clone(msaaComp, "DSPAAMod Configuration");
					Transform transform = ((Component)resolution).transform;
					transform.localPosition += new Vector3(0f, 0f - num, 0f);
					Transform transform2 = ((Component)configuration).transform;
					transform2.localPosition += new Vector3(0f, -2f * num, 0f);
					configurationPosition = ((RectTransform)((Component)configuration).transform).anchoredPosition;
					configurationLabelPosition = ((RectTransform)((Component)configurationLabel).transform).anchoredPosition;
					resolutionLabel = Object.Instantiate<Text>(configurationLabel, parent2);
					((Object)resolutionLabel).name = "DSPAAMod Resolution Label";
					Transform transform3 = ((Component)resolutionLabel).transform;
					transform3.localPosition += new Vector3(0f, num, 0f);
					Localizer[] components = ((Component)resolutionLabel).GetComponents<Localizer>();
					for (int i = 0; i < components.Length; i++)
					{
						((Behaviour)components[i]).enabled = false;
					}
					availabilityLabel = Object.Instantiate<Text>(aaLabel, parent2);
					((Object)availabilityLabel).name = "DSPAASR Availability";
					components = ((Component)availabilityLabel).GetComponents<Localizer>();
					for (int i = 0; i < components.Length; i++)
					{
						((Behaviour)components[i]).enabled = false;
					}
					availabilityLabel.alignment = (TextAnchor)3;
					availabilityLabel.supportRichText = false;
					((Graphic)availabilityLabel).raycastTarget = false;
					availabilityLabel.horizontalOverflow = (HorizontalWrapMode)0;
					availabilityLabel.verticalOverflow = (VerticalWrapMode)0;
					availabilityLabel.resizeTextForBestFit = false;
					availabilityLabel.fontSize = aaLabel.fontSize;
					((Graphic)availabilityLabel).color = new Color(1f, 0.82f, 0.55f, 1f);
					Vector3[] array = (Vector3[])(object)new Vector3[4];
					float num2 = float.PositiveInfinity;
					float num3 = float.NegativeInfinity;
					RectTransform[] array2 = (RectTransform[])(object)new RectTransform[2]
					{
						val2,
						(RectTransform)((Component)technique).transform
					};
					for (int i = 0; i < array2.Length; i++)
					{
						array2[i].GetWorldCorners(array);
						Vector3[] array3 = array;
						foreach (Vector3 val4 in array3)
						{
							float x = ((Transform)layoutRoot).InverseTransformPoint(val4).x;
							num2 = Mathf.Min(num2, x);
							num3 = Mathf.Max(num3, x);
						}
					}
					RectTransform rectTransform = ((Graphic)availabilityLabel).rectTransform;
					Vector2 anchorMin = (rectTransform.anchorMax = ((RectTransform)parent2).pivot);
					rectTransform.anchorMin = anchorMin;
					rectTransform.pivot = new Vector2(0f, 0.5f);
					((Transform)rectTransform).localScale = Vector3.one;
					rectTransform.sizeDelta = new Vector2(num3 - num2, 2f * num - 4f);
					availabilityPosition = new Vector2(num2, y);
					((Transform)rectTransform).position = ((Transform)layoutRoot).TransformPoint(new Vector3(num2, y, 0f));
					((Component)msaaComp).gameObject.SetActive(false);
					((Component)window.fxaaComp).gameObject.SetActive(false);
					((UnityEvent)technique.onItemIndexChange).AddListener(new UnityAction(TechniqueChanged));
					((UnityEvent)resolution.onItemIndexChange).AddListener(new UnityAction(ResolutionChanged));
					((UnityEvent)configuration.onItemIndexChange).AddListener(new UnityAction(ConfigurationChanged));
					frameGeneration = new FrameGenerationOptions(plugin, msaaComp, aaLabel, availabilityLabel, layoutRoot, rowStep);
					Canvas.preWillRenderCanvases += new WillRenderCanvases(ConstrainDropdown);
					return;
				}
			}
			throw new InvalidOperationException("Unexpected graphics-row hierarchy; refusing to shift unrelated controls.");
		}

		private void ShiftRows(Transform column, float fromCenter, float distance)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			foreach (Transform item in column)
			{
				object obj = (object)item;
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if (val != null)
				{
					RectTransform obj2 = layoutRoot;
					Rect rect = val.rect;
					if (!(((Transform)obj2).InverseTransformPoint(((Transform)val).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center))).y > fromCenter + 0.5f))
					{
						movedRows.Add(val, val.anchoredPosition);
						val.anchoredPosition += new Vector2(0f, 0f - distance);
					}
				}
			}
		}

		internal static UIComboBox Clone(UIComboBox source, string name)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			UIComboBox val = Object.Instantiate<UIComboBox>(source, ((Component)source).transform.parent);
			try
			{
				((Object)val).name = name;
				val.InitItemIndexSelf = false;
				val.CanInput = false;
				val.translated = true;
				val.autoWidth = false;
				val.DoubleClickChange = false;
				val.isDroppedDown = false;
				val.onItemIndexChange = new ChangeEvent();
				val.onSubmit = new SubmitEvent();
				val.Items = new List<string>();
				val.ItemsData = new List<int>();
				Localizer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Localizer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					((Behaviour)componentsInChildren[i]).enabled = false;
				}
				foreach (Transform item in (Transform)val.m_DropDownContent)
				{
					Transform val2 = item;
					if (!((Object)(object)val2 == (Object)(object)((Component)val.m_ListItemRes).transform) && !((Object)(object)val2 == (Object)(object)((Component)val.m_EmptyItemRes).transform) && !((Object)(object)val2 == (Object)(object)((Component)val.m_SelectionBG).transform))
					{
						((Component)val2).gameObject.SetActive(false);
						Object.Destroy((Object)(object)((Component)val2).gameObject);
					}
				}
				((Component)val).gameObject.SetActive(true);
				return val;
			}
			catch
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
				throw;
			}
		}

		private static Text FindLabel(UIOptionWindow owner, string key)
		{
			Localizer[] componentsInChildren = ((Component)owner).GetComponentsInChildren<Localizer>(true);
			foreach (Localizer val in componentsInChildren)
			{
				if (AaLabels.Matches(val.stringKey, key) || AaLabels.Matches(((Object)val).name, key))
				{
					Text component = ((Component)val).GetComponent<Text>();
					if (Object.op_Implicit((Object)(object)component))
					{
						return component;
					}
				}
			}
			string text = Localization.Translate(key);
			Text[] componentsInChildren2 = ((Component)owner).GetComponentsInChildren<Text>(true);
			foreach (Text val2 in componentsInChildren2)
			{
				if (val2.text.Trim() == text || AaLabels.Matches(val2.text, key))
				{
					return val2;
				}
			}
			return null;
		}

		internal static void SetItems(UIComboBox control, string[] items, int index, string missingCaption = "")
		{
			control.isDroppedDown = false;
			control.Items = new List<string>(items);
			control.ItemsData = new List<int>();
			for (int i = 0; i < items.Length; i++)
			{
				control.ItemsData.Add(i);
			}
			control.UpdateItems();
			control.itemIndex = index;
			if (index < 0)
			{
				control.m_Input.text = missingCaption;
				control.m_Text.text = missingCaption;
			}
		}

		private void TechniqueChanged()
		{
			if (!synchronizing && draft != null && Object.op_Implicit((Object)(object)technique) && technique.itemIndex >= 0 && technique.itemIndex < techniqueChoices.Length)
			{
				AaChoice choice = techniqueChoices[technique.itemIndex];
				if (!draft.TrySelectTechnique(choice, plugin.Renderer.GetAvailability(choice)))
				{
					Refresh();
					return;
				}
				plugin.Settings.Draft = draft.Settings;
				Refresh();
			}
		}

		private void ResolutionChanged()
		{
			if (!synchronizing && draft != null && Object.op_Implicit((Object)(object)resolution) && draft.ResolutionEnabled && plugin.Renderer.GetAvailability(draft.Choice).Available && resolution.itemIndex >= 0 && resolution.itemIndex <= 4)
			{
				draft.SelectResolution((ResolutionMode)resolution.itemIndex);
				plugin.Settings.Draft = draft.Settings;
			}
		}

		private void ConfigurationChanged()
		{
			if (!synchronizing && draft != null && Object.op_Implicit((Object)(object)configuration) && draft.ConfigurationEnabled && configuration.itemIndex >= 0 && (draft.Choice != AaChoice.Dlss || plugin.Renderer.Availability.Available))
			{
				draft.SelectConfiguration(configuration.itemIndex);
				plugin.Settings.Draft = draft.Settings;
			}
		}

		public void DropdownOpened(UIComboBox control)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)window) || !Object.op_Implicit((Object)(object)layoutRoot) || !Object.op_Implicit((Object)(object)control) || !((Component)control).transform.IsChildOf((Transform)(object)layoutRoot))
			{
				return;
			}
			RestoreDropdown();
			if (control.isDroppedDown)
			{
				Transform val = ((Component)control).transform;
				while (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)layoutRoot)
				{
					val.SetAsLastSibling();
					val = val.parent;
				}
				openedDropdown = control;
				dropdownPosition = ((Transform)control.m_DropDownList).localPosition;
			}
		}

		private void ConstrainDropdown()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)openedDropdown))
			{
				return;
			}
			if (!((Behaviour)openedDropdown).isActiveAndEnabled || !openedDropdown.isDroppedDown)
			{
				RestoreDropdown();
				return;
			}
			ScrollRect componentInParent = ((Component)openedDropdown).GetComponentInParent<ScrollRect>();
			if (Object.op_Implicit((Object)(object)componentInParent))
			{
				RectTransform val = (RectTransform)(Object.op_Implicit((Object)(object)componentInParent.viewport) ? ((object)componentInParent.viewport) : ((object)(RectTransform)((Component)componentInParent).transform));
				RectTransform dropDownList = openedDropdown.m_DropDownList;
				((Transform)dropDownList).localPosition = dropdownPosition;
				BoundsIn(val, dropDownList, out var bottom, out var top);
				Rect rect = val.rect;
				float num = ((Rect)(ref rect)).yMin + 2f;
				rect = val.rect;
				float num2 = ((Rect)(ref rect)).yMax - 2f;
				float num3 = 0f;
				if (bottom < num)
				{
					BoundsIn(val, (RectTransform)((Component)openedDropdown).transform, out var _, out var top2);
					float num4 = top2 - bottom;
					num3 = ((top + num4 <= num2) ? num4 : (num - bottom));
				}
				if (top + num3 > num2)
				{
					num3 = num2 - top;
				}
				((Transform)dropDownList).position = ((Transform)dropDownList).position + ((Transform)val).TransformVector(new Vector3(0f, num3, 0f));
			}
		}

		private void BoundsIn(RectTransform relativeTo, RectTransform rect, out float bottom, out float top)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			rect.GetWorldCorners(dropdownCorners);
			bottom = float.PositiveInfinity;
			top = float.NegativeInfinity;
			Vector3[] array = dropdownCorners;
			foreach (Vector3 val in array)
			{
				float y = ((Transform)relativeTo).InverseTransformPoint(val).y;
				bottom = Mathf.Min(bottom, y);
				top = Mathf.Max(top, y);
			}
		}

		private void RestoreDropdown()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)openedDropdown) && Object.op_Implicit((Object)(object)openedDropdown.m_DropDownList))
			{
				((Transform)openedDropdown.m_DropDownList).localPosition = dropdownPosition;
			}
			openedDropdown = null;
		}

		public void BeginNativeRefresh()
		{
			synchronizing = true;
		}

		public void EndNativeRefresh()
		{
			if (Object.op_Implicit((Object)(object)window))
			{
				nativeRefreshMsaa = MsaaFromIndex(window.msaaComp.itemIndex);
				nativeRefreshFxaa = window.fxaaComp.isOn;
			}
			synchronizing = false;
			Refresh();
		}

		public void Update()
		{
			if (!Object.op_Implicit((Object)(object)window) || draft == null)
			{
				return;
			}
			if (shownAvailability == plugin.Renderer.Availability && shownFsrAvailability == plugin.Renderer.FsrAvailability)
			{
				FrameGenerationOptions frameGenerationOptions = frameGeneration;
				if (frameGenerationOptions == null || !frameGenerationOptions.Changed)
				{
					return;
				}
			}
			Refresh();
		}

		public void Refresh()
		{
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)window) || !Object.op_Implicit((Object)(object)technique) || !Object.op_Implicit((Object)(object)resolution) || !Object.op_Implicit((Object)(object)configuration) || draft == null)
			{
				return;
			}
			synchronizing = true;
			try
			{
				aaLabel.text = (Chinese ? "抗锯齿 / 超分辨率" : "AA / Super Resolution");
				resolutionLabel.text = (Chinese ? "超分辨率档位" : "Resolution mode");
				configurationLabel.text = (Chinese ? "配置" : "Configuration");
				shownAvailability = plugin.Renderer.Availability;
				shownFsrAvailability = plugin.Renderer.FsrAvailability;
				techniqueChoices = GraphicsMenuChoices.Antialiasing(shownAvailability.Available, shownFsrAvailability.Available);
				SetItems(technique, Array.ConvertAll(techniqueChoices, TechniqueLabel), Array.IndexOf(techniqueChoices, draft.Choice), TechniqueLabel(draft.Choice));
				string text = AvailabilityReason(shownAvailability);
				string text2 = AvailabilityReason(shownFsrAvailability);
				availabilityLabel.text = text + ((text.Length > 0 && text2.Length > 0) ? "\n" : "") + text2;
				bool flag = (!shownAvailability.Available && !shownAvailability.Pending) || (!shownFsrAvailability.Available && !shownFsrAvailability.Pending);
				((Graphic)availabilityLabel).color = (Color)(flag ? new Color(1f, 0.82f, 0.55f, 1f) : ((Graphic)aaLabel).color);
				SetItems(resolution, new string[5]
				{
					(draft.Choice == AaChoice.Fsr) ? "Native AA" : "DLAA",
					Chinese ? "质量" : "Quality",
					Chinese ? "平衡" : "Balanced",
					Chinese ? "性能" : "Performance",
					Chinese ? "超级性能" : "Ultra Performance"
				}, (int)draft.Settings.Resolution);
				string[] items = ((draft.Choice != AaChoice.Dlss) ? ((draft.Choice != AaChoice.Msaa) ? new string[1] { Chinese ? "无" : "None" } : new string[3] { "2×", "4×", "8×" }) : new string[5]
				{
					Chinese ? "推荐" : "Recommended",
					"CNN",
					"Transformer K",
					"Transformer L",
					"Transformer M"
				});
				SetItems(configuration, items, draft.ConfigurationIndex);
				frameGeneration?.Refresh();
				RefreshLayout();
			}
			finally
			{
				synchronizing = false;
			}
		}

		private string TechniqueLabel(AaChoice choice)
		{
			if (choice != AaChoice.None)
			{
				if (choice != AaChoice.Msaa)
				{
					if (choice != AaChoice.Fxaa)
					{
						if (choice != AaChoice.Taa)
						{
							return CapabilityLabel((choice == AaChoice.Fsr) ? shownFsrAvailability : shownAvailability);
						}
						return "TAA";
					}
					return "FXAA";
				}
				return "MSAA";
			}
			if (!Chinese)
			{
				return "Off";
			}
			return "关闭";
		}

		private static string CapabilityLabel(UpscalerAvailability state)
		{
			return state.Name + (state.Available ? "" : ((!state.Pending) ? (Chinese ? "(不可用)" : " (unavailable)") : (Chinese ? "(检测中)" : " (checking)")));
		}

		private static string AvailabilityReason(UpscalerAvailability state)
		{
			if (state.Reason != UpscalerSupportReason.Runtime)
			{
				return state.Describe(Chinese);
			}
			return state.Name + (Chinese ? " 不可用,请检查显卡、驱动与模组安装。" : " is unavailable. Check the GPU, driver and mod installation.");
		}

		internal static int MeasureNoteRows(Text note, float spacing)
		{
			if (string.IsNullOrEmpty(note.text))
			{
				return 0;
			}
			int num = Mathf.Max(1, Mathf.CeilToInt((note.preferredHeight + 8f) / spacing));
			((Graphic)note).rectTransform.SetSizeWithCurrentAnchors((Axis)1, (float)num * spacing - 8f);
			return num;
		}

		private void RefreshLayout()
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			bool active = !string.IsNullOrEmpty(availabilityLabel.text);
			((Component)availabilityLabel).gameObject.SetActive(active);
			int num = MeasureNoteRows(availabilityLabel, rowStep);
			bool available = plugin.Renderer.GetAvailability(draft.Choice).Available;
			bool flag = draft.ResolutionEnabled && available;
			bool flag2 = draft.ConfigurationEnabled && (draft.Choice != AaChoice.Dlss || available);
			((Component)resolution).gameObject.SetActive(flag);
			((Component)resolutionLabel).gameObject.SetActive(flag);
			((Component)configuration).gameObject.SetActive(flag2);
			((Component)configurationLabel).gameObject.SetActive(flag2);
			int num2 = (flag ? 1 : 0) + (flag2 ? 1 : 0);
			int num3 = num2 + num + (frameGeneration?.Rows ?? 0);
			RectTransform rectTransform = ((Graphic)aaLabel).rectTransform;
			RectTransform obj = layoutRoot;
			Rect rect = rectTransform.rect;
			float x = ((Transform)obj).InverseTransformPoint(((Transform)rectTransform).TransformPoint(new Vector3(((Rect)(ref rect)).xMin, 0f, 0f))).x;
			frameGeneration?.Layout(availabilityPosition.y - rowStep * (float)(num2 + num + 1), x);
			((Transform)((Graphic)availabilityLabel).rectTransform).position = ((Transform)layoutRoot).TransformPoint(new Vector3(x, availabilityPosition.y - rowStep * ((float)num2 + 0.5f + (float)num * 0.5f), 0f));
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, rowStep * (float)(1 - num3));
			foreach (KeyValuePair<RectTransform, Vector2> movedRow in movedRows)
			{
				if (Object.op_Implicit((Object)(object)movedRow.Key))
				{
					movedRow.Key.anchoredPosition = movedRow.Value + val;
				}
			}
			((RectTransform)((Component)configuration).transform).anchoredPosition = configurationPosition + new Vector2(0f, flag ? 0f : rowStep);
			((RectTransform)((Component)configurationLabel).transform).anchoredPosition = configurationLabelPosition + new Vector2(0f, flag ? 0f : rowStep);
			layoutRoot.sizeDelta = originalContentSize - val;
		}

		public void Read(ref GameOption option)
		{
			if (draft != null && Object.op_Implicit((Object)(object)window))
			{
				option.msaa = draft.NativeMsaa;
				option.fxaa = draft.NativeFxaa;
				plugin.Settings.Draft = draft.Settings;
			}
		}

		public void Close()
		{
			RestoreDropdown();
			plugin.Settings.Cancel();
			plugin.FrameGeneration.Cancel();
			draft = null;
		}

		public void Defaults(int tab)
		{
			if (tab == 0)
			{
				plugin.Settings.Defaults();
				plugin.FrameGeneration.Defaults();
				draft = new AaMenuDraft(plugin.Settings.Draft, nativeRefreshMsaa, nativeRefreshFxaa);
				Refresh();
			}
		}

		public void Dispose()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			Canvas.preWillRenderCanvases -= new WillRenderCanvases(ConstrainDropdown);
			RestoreDropdown();
			frameGeneration?.Dispose();
			frameGeneration = null;
			if (Object.op_Implicit((Object)(object)technique))
			{
				((Component)technique).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)technique).gameObject);
			}
			if (Object.op_Implicit((Object)(object)resolution))
			{
				((Component)resolution).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)resolution).gameObject);
			}
			if (Object.op_Implicit((Object)(object)resolutionLabel))
			{
				((Component)resolutionLabel).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)resolutionLabel).gameObject);
			}
			if (Object.op_Implicit((Object)(object)availabilityLabel))
			{
				((Component)availabilityLabel).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)availabilityLabel).gameObject);
			}
			availabilityLabel = null;
			shownAvailability = (shownFsrAvailability = null);
			foreach (KeyValuePair<RectTransform, Vector2> movedRow in movedRows)
			{
				if (Object.op_Implicit((Object)(object)movedRow.Key))
				{
					movedRow.Key.anchoredPosition = movedRow.Value;
				}
			}
			movedRows.Clear();
			if (Object.op_Implicit((Object)(object)layoutRoot))
			{
				layoutRoot.sizeDelta = originalContentSize;
			}
			layoutRoot = null;
			if (Object.op_Implicit((Object)(object)configuration))
			{
				((Component)configuration).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)configuration).gameObject);
			}
			if (Object.op_Implicit((Object)(object)window))
			{
				((Component)window.msaaComp).gameObject.SetActive(originalAaActive);
				((Component)window.fxaaComp).gameObject.SetActive(originalFxaaActive);
			}
			if (Object.op_Implicit((Object)(object)aaLabel) && originalAaLabel != null)
			{
				aaLabel.text = originalAaLabel;
			}
			if (Object.op_Implicit((Object)(object)configurationLabel) && originalConfigurationLabel != null)
			{
				configurationLabel.text = originalConfigurationLabel;
				((Component)configurationLabel).gameObject.SetActive(originalConfigurationLabelActive);
			}
			if (Object.op_Implicit((Object)(object)aaLocalizer))
			{
				((Behaviour)aaLocalizer).enabled = originalAaLocalized;
			}
			if (Object.op_Implicit((Object)(object)configurationLocalizer))
			{
				((Behaviour)configurationLocalizer).enabled = originalConfigurationLocalized;
			}
			window = null;
			technique = (resolution = (configuration = null));
			aaLabel = (resolutionLabel = (configurationLabel = null));
			aaLocalizer = (configurationLocalizer = null);
			draft = null;
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "_OnOpen")]
	internal static class OptionsOpenPatch
	{
		private static void Postfix(UIOptionWindow __instance)
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.Open(__instance);
			});
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "_OnClose")]
	internal static class OptionsClosePatch
	{
		private static void Postfix()
		{
			Plugin.Instance?.Options.Close();
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "TempOptionToUI")]
	internal static class OptionsRefreshPatch
	{
		private static void Prefix()
		{
			Plugin.Instance?.Options.BeginNativeRefresh();
		}

		private static void Postfix()
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.EndNativeRefresh();
			});
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "UIToTempOption")]
	internal static class OptionsReadPatch
	{
		private static void Postfix(ref GameOption ___tempOption)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			GameOption value = ___tempOption;
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.Read(ref value);
			});
			___tempOption = value;
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "ApplyOptions")]
	internal static class OptionsApplyPatch
	{
		private static void Prefix(ref GameOption ___tempOption)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			GameOption value = ___tempOption;
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.Read(ref value);
			});
			___tempOption = value;
		}

		private static void Postfix()
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.ApplySettings();
			});
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.ApplyFrameGeneration();
			});
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "OnLanguageChange")]
	internal static class OptionsLanguagePatch
	{
		private static void Postfix()
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.Refresh();
			});
		}
	}
	[HarmonyPatch(typeof(UIComboBox), "OnPopButtonClick")]
	internal static class AaDropdownPatch
	{
		private static void Postfix(UIComboBox __instance)
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.DropdownOpened(__instance);
			});
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "OnRevertButtonClick")]
	internal static class OptionsDefaultsPatch
	{
		private static void Postfix(int idx)
		{
			Plugin.Instance?.Guard(delegate
			{
				Plugin.Instance.Options.Defaults(idx);
			});
		}
	}
}
namespace DSPAAMod.Interop
{
	public struct NativeFrame
	{
		public uint Size;

		public uint Version;

		public ulong Camera;

		public ulong Frame;

		public IntPtr Color;

		public IntPtr Output;

		public IntPtr Depth;

		public IntPtr Motion;

		public uint Width;

		public uint Height;

		public uint Preset;

		public uint Flags;

		public float JitterX;

		public float JitterY;

		public float MotionScaleX;

		public float MotionScaleY;

		public float FrameTimeMilliseconds;

		public uint Reserved;

		public uint OutputWidth;

		public uint OutputHeight;

		public uint Quality;

		public uint Reserved2;
	}
	public struct NativeFsrParameters
	{
		public uint Size;

		public float CameraNear;

		public float CameraFar;

		public float VerticalFov;

		public float PreExposure;

		public float ViewSpaceToMeters;

		public float Sharpness;

		public uint Reserved;

		public IntPtr OpaqueColor;
	}
	public struct NativeFsrOptimalSettings
	{
		public NativeOptimalSettings Settings;

		public uint JitterPhases;
	}
	public struct NativeStatus
	{
		public uint Size;

		public int Result;

		public ulong Frame;

		public uint RequestedPreset;

		public uint ObservedPreset;

		public uint Verification;

		public uint Reserved;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
		public byte[] MessageBytes;

		public string Message
		{
			get
			{
				if (MessageBytes == null)
				{
					return string.Empty;
				}
				int num = Array.IndexOf(MessageBytes, (byte)0);
				return Encoding.UTF8.GetString(MessageBytes, 0, (num < 0) ? MessageBytes.Length : num);
			}
		}
	}
	public struct NativeOptimalSettings
	{
		public uint Size;

		public int Result;

		public uint OutputWidth;

		public uint OutputHeight;

		public uint Quality;

		public uint OptimalWidth;

		public uint OptimalHeight;

		public uint MinWidth;

		public uint MinHeight;

		public uint MaxWidth;

		public uint MaxHeight;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
		public byte[] MessageBytes;

		public string Message
		{
			get
			{
				if (MessageBytes == null)
				{
					return string.Empty;
				}
				int num = Array.IndexOf(MessageBytes, (byte)0);
				return Encoding.UTF8.GetString(MessageBytes, 0, (num < 0) ? MessageBytes.Length : num);
			}
		}
	}
	public struct NativeSupport
	{
		public uint Size;

		public int Result;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
		public byte[] MessageBytes;

		public string Message
		{
			get
			{
				if (MessageBytes == null)
				{
					return string.Empty;
				}
				int num = Array.IndexOf(MessageBytes, (byte)0);
				return Encoding.UTF8.GetString(MessageBytes, 0, (num < 0) ? MessageBytes.Length : num);
			}
		}
	}
	public sealed class NativeBridge
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate uint Abi();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		private delegate int Initialize(string runtime, string data);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr GetEvent();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueFrame(ref NativeFrame frame);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueRelease(ulong camera);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueShutdown();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void CancelCommand(IntPtr token);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int ReadStatus(ulong camera, ref NativeStatus status);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueOptimal(ulong camera, IntPtr deviceResource, uint width, uint height, uint quality);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int ReadOptimal(ulong camera, ref NativeOptimalSettings settings);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueSupport(IntPtr deviceResource);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int ReadSupport(IntPtr token, ref NativeSupport support);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueFsrFrame(ref NativeFrame frame, ref NativeFsrParameters parameters);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueBackendSupport(IntPtr resource, uint backend);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueBackendOptimal(ulong camera, IntPtr resource, uint width, uint height, uint quality, uint backend);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int ReadFsrOptimal(ulong camera, ref NativeFsrOptimalSettings result);

		private readonly IntPtr module;

		private readonly QueueFrame queueFrame;

		private readonly QueueRelease queueRelease;

		private readonly QueueShutdown queueShutdown;

		private readonly CancelCommand cancel;

		private readonly ReadStatus readStatus;

		private readonly QueueOptimal queueOptimal;

		private readonly ReadOptimal readOptimal;

		private readonly QueueSupport queueSupport;

		private readonly ReadSupport readSupport;

		private readonly QueueFsrFrame queueFsrFrame;

		private readonly QueueBackendSupport queueBackendSupport;

		private readonly QueueBackendOptimal queueBackendOptimal;

		private readonly ReadFsrOptimal readFsrOptimal;

		public IntPtr RenderEvent { get; }

		public static uint FrameSize => (uint)Marshal.SizeOf(typeof(NativeFrame));

		public static uint StatusSize => (uint)Marshal.SizeOf(typeof(NativeStatus));

		public static uint OptimalSize => (uint)Marshal.SizeOf(typeof(NativeOptimalSettings));

		public static uint SupportSize => (uint)Marshal.SizeOf(typeof(NativeSupport));

		public static uint FsrParametersSize => (uint)Marshal.SizeOf(typeof(NativeFsrParameters));

		public static uint FsrOptimalSize => (uint)Marshal.SizeOf(typeof(NativeFsrOptimalSettings));

		[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
		private static extern IntPtr GetProcAddress(IntPtr module, string name);

		public NativeBridge(string directory, string dataDirectory)
		{
			if (!Environment.Is64BitProcess)
			{
				throw new PlatformNotSupportedException("DSPAAMod requires x64.");
			}
			module = NativeModule.Load(Path.Combine(directory, "DSPAANative.dll"));
			if (Get<Abi>("DspAaGetAbiVersion")() != 2 || FrameSize != 112 || StatusSize != 288 || OptimalSize != 300 || SupportSize != 264 || FsrParametersSize != 40 || FsrOptimalSize != 304)
			{
				throw new InvalidOperationException("Native/managed DSPAAMod ABI mismatch.");
			}
			queueFrame = Get<QueueFrame>("DspAaQueueFrame");
			queueRelease = Get<QueueRelease>("DspAaQueueRelease");
			queueShutdown = Get<QueueShutdown>("DspAaQueueShutdown");
			cancel = Get<CancelCommand>("DspAaCancel");
			readStatus = Get<ReadStatus>("DspAaGetStatus");
			queueOptimal = Get<QueueOptimal>("DspAaQueueOptimalSettings");
			readOptimal = Get<ReadOptimal>("DspAaGetOptimalSettings");
			queueSupport = Get<QueueSupport>("DspAaQueueSupport");
			readSupport = Get<ReadSupport>("DspAaGetSupport");
			queueFsrFrame = Get<QueueFsrFrame>("DspAaQueueFsrFrame");
			queueBackendSupport = Get<QueueBackendSupport>("DspAaQueueSupportForBackend");
			queueBackendOptimal = Get<QueueBackendOptimal>("DspAaQueueOptimalSettingsForBackend");
			readFsrOptimal = Get<ReadFsrOptimal>("DspAaGetFsrOptimalSettings");
			RenderEvent = Get<GetEvent>("DspAaGetRenderEvent")();
			if (RenderEvent == IntPtr.Zero || Get<Initialize>("DspAaInitialize")(directory, dataDirectory) != 1)
			{
				throw new InvalidOperationException("Native bridge initialization failed; check runtime path and log permissions.");
			}
		}

		internal T Get<T>(string name) where T : Delegate
		{
			IntPtr procAddress = GetProcAddress(module, name);
			if (procAddress == IntPtr.Zero)
			{
				throw new EntryPointNotFoundException(name);
			}
			return (T)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T));
		}

		public IntPtr Submit(ref NativeFrame frame)
		{
			frame.Size = FrameSize;
			frame.Version = 2u;
			return queueFrame(ref frame);
		}

		public IntPtr SubmitFsr(ref NativeFrame frame, ref NativeFsrParameters parameters)
		{
			frame.Size = FrameSize;
			frame.Version = 2u;
			parameters.Size = FsrParametersSize;
			return queueFsrFrame(ref frame, ref parameters);
		}

		public IntPtr RequestOptimal(ulong camera, IntPtr resource, uint width, uint height, uint quality, uint backend)
		{
			return queueBackendOptimal(camera, resource, width, height, quality, backend);
		}

		public bool TryGetFsrOptimal(ulong camera, out NativeFsrOptimalSettings result)
		{
			result = new NativeFsrOptimalSettings
			{
				Settings = new NativeOptimalSettings
				{
					Size = FsrOptimalSize,
					MessageBytes = new byte[256]
				}
			};
			return readFsrOptimal(camera, ref result) == 1;
		}

		public IntPtr RequestSupport(IntPtr resource, uint backend)
		{
			return queueBackendSupport(resource, backend);
		}

		public IntPtr RequestOptimal(ulong camera, IntPtr deviceResource, uint width, uint height, uint quality)
		{
			return queueOptimal(camera, deviceResource, width, height, quality);
		}

		public bool TryGetOptimal(ulong camera, out NativeOptimalSettings result)
		{
			result = new NativeOptimalSettings
			{
				Size = OptimalSize,
				MessageBytes = new byte[256]
			};
			return readOptimal(camera, ref result) == 1;
		}

		public IntPtr RequestSupport(IntPtr deviceResource)
		{
			return queueSupport(deviceResource);
		}

		public bool TryGetSupport(IntPtr token, out NativeSupport result)
		{
			result = new NativeSupport
			{
				Size = SupportSize,
				MessageBytes = new byte[256]
			};
			return readSupport(token, ref result) == 1;
		}

		public IntPtr Release(ulong camera)
		{
			return queueRelease(camera);
		}

		public IntPtr Shutdown()
		{
			return queueShutdown();
		}

		public void Cancel(IntPtr token)
		{
			if (token != IntPtr.Zero)
			{
				cancel(token);
			}
		}

		public bool TryGetStatus(ulong camera, out NativeStatus status)
		{
			status = new NativeStatus
			{
				Size = StatusSize,
				MessageBytes = new byte[256]
			};
			return readStatus(camera, ref status) == 1;
		}
	}
	internal static class NativeModule
	{
		[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryExW", ExactSpelling = true, SetLastError = true)]
		private static extern IntPtr LoadLibraryEx(string path, IntPtr file, uint flags);

		internal static IntPtr Load(string path)
		{
			path = Path.GetFullPath(path);
			IntPtr intPtr = LoadLibraryEx(path, IntPtr.Zero, 4352u);
			if (intPtr == IntPtr.Zero)
			{
				throw new NativeModuleLoadException(Marshal.GetLastWin32Error(), path, File.Exists(path));
			}
			return intPtr;
		}
	}
	internal sealed class NativeModuleLoadException : Win32Exception
	{
		internal string ModulePath { get; }

		internal bool ModuleFound { get; }

		internal NativeModuleLoadException(int error, string path, bool found)
			: base(error, Describe(error, found))
		{
			ModulePath = path;
			ModuleFound = found;
		}

		private static string Describe(int error, bool found)
		{
			string text;
			if ((error == 2 || error == 3 || error == 126) && !found)
			{
				text = "Keep the complete package beside DSPAAMod.dll in the plugin folder.";
			}
			else
			{
				switch (error)
				{
				case 126:
					text = "The file exists; a required dependency may be missing.";
					break;
				case 193:
				case 216:
					text = "Check for a damaged or non-x64 DLL and reinstall the complete package.";
					break;
				default:
					text = "Check the installation and Windows file access.";
					break;
				}
			}
			return "Cannot load DSPAANative.dll (Windows error " + error + ": " + new Win32Exception(error).Message + "). " + text + " See BepInEx/LogOutput.log.";
		}

		public override string ToString()
		{
			return ((ExternalException)this).ToString() + Environment.NewLine + "Expected native module: " + ModulePath + Environment.NewLine + "File present at diagnostic check: " + ModuleFound;
		}
	}
	internal static class PresentationAvailability
	{
		internal static bool CanRequest(NativePresentationStatus caps, PresentationStartupState startup, FrameGenerationBackend backend, bool runtimeFilesPresent)
		{
			if (backend == FrameGenerationBackend.Off)
			{
				return true;
			}
			if (caps.Quarantined)
			{
				return false;
			}
			if (caps.Available)
			{
				return backend switch
				{
					FrameGenerationBackend.Dlss => caps.DlssSupported, 
					FrameGenerationBackend.Fsr => caps.FsrRuntimePresent, 
					_ => false, 
				};
			}
			if (startup == PresentationStartupState.Inactive && runtimeFilesPresent)
			{
				if (backend != FrameGenerationBackend.Fsr)
				{
					return backend == FrameGenerationBackend.Dlss;
				}
				return true;
			}
			return false;
		}

		internal static bool NeedsRestart(NativePresentationStatus caps, PresentationStartupState startup, FrameGenerationSettings request)
		{
			if (!caps.Available && !caps.Quarantined && startup == PresentationStartupState.Inactive)
			{
				return request.Backend != FrameGenerationBackend.Off;
			}
			return false;
		}
	}
	public struct NativePresentationConfiguration
	{
		public uint Size;

		public uint Version;

		public uint Backend;

		public uint Mode;

		public uint GeneratedFrames;

		public uint Reflex;

		public float DynamicTargetFrameRate;

		public uint FrameLimitMicroseconds;
	}
	public struct NativePresentationBegin
	{
		public uint Size;

		public uint Version;

		public ulong ApplicationFrameId;

		public ulong Generation;
	}
	public struct NativePresentationInputs
	{
		public uint Size;

		public uint Version;

		public ulong ApplicationFrameId;

		public ulong Generation;

		public IntPtr Hudless;

		public IntPtr Depth;

		public IntPtr Motion;

		public IntPtr FsrDistortion;

		public IntPtr SlDistortion;

		public uint RenderWidth;

		public uint RenderHeight;

		public uint Flags;

		public uint Reserved;

		public float JitterX;

		public float JitterY;

		public float MotionScaleX;

		public float MotionScaleY;

		public float Milliseconds;

		public float CameraNear;

		public float CameraFar;

		public float VerticalFov;

		public float PreExposure;

		public float ViewSpaceToMeters;

		public float MinLuminance;

		public float MaxLuminance;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CameraPosition;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CameraUp;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CameraRight;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
		public float[] CameraForward;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] CameraViewToClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] ClipToCameraView;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] ClipToPrevClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
		public float[] PrevClipToClip;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
		public int[] GenerationRect;

		public uint LogicalOutputWidth;

		public uint LogicalOutputHeight;

		public static NativePresentationInputs Create()
		{
			return new NativePresentationInputs
			{
				CameraPosition = new float[3],
				CameraUp = new float[3],
				CameraRight = new float[3],
				CameraForward = new float[3],
				CameraViewToClip = new float[16],
				ClipToCameraView = new float[16],
				ClipToPrevClip = new float[16],
				PrevClipToClip = new float[16],
				GenerationRect = new int[4],
				PreExposure = 1f,
				ViewSpaceToMeters = 1f,
				MaxLuminance = 100f
			};
		}
	}
	public struct NativePresentationStatus
	{
		public uint Size;

		public uint Version;

		public uint Flags;

		public uint RequestedBackend;

		public uint ActiveBackend;

		public uint ActiveMode;

		public uint MaximumGeneratedFrames;

		public uint Width;

		public uint Height;

		public uint SdkStatus;

		public ulong Generation;

		public ulong LastApplicationFrameId;

		public ulong LastPresentedFrameId;

		public ulong ApplicationPresents;

		public ulong GeneratedSubmissions;

		public ulong SdkReportedPresents;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 384)]
		public byte[] MessageBytes;

		public bool Available => (Flags & 1) != 0;

		public bool FsrRuntimePresent => (Flags & 2) != 0;

		public bool DlssSupported => (Flags & 4) != 0;

		public bool ReflexSupported => (Flags & 8) != 0;

		public bool DynamicSupported => (Flags & 0x10) != 0;

		public bool Generating => (Flags & 0x40) != 0;

		public bool Quarantined => (Flags & 0x80) != 0;

		public string Message
		{
			get
			{
				if (MessageBytes == null)
				{
					return string.Empty;
				}
				int num = Array.IndexOf(MessageBytes, (byte)0);
				return Encoding.UTF8.GetString(MessageBytes, 0, (num < 0) ? MessageBytes.Length : num);
			}
		}
	}
	public sealed class PresentationBridge
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate uint Abi();

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int ConfigureNative(ref NativePresentationConfiguration value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int BeginNative(ref NativePresentationBegin value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int EndSimulationNative(ulong id);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr QueueNative(ref NativePresentationInputs value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate void CancelNative(IntPtr token);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate int StatusNative(ref NativePresentationStatus value);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate IntPtr EventNative();

		public const uint AbiVersion = 3u;

		private readonly ConfigureNative configure;

		private readonly BeginNative begin;

		private readonly EndSimulationNative simulationEnd;

		private readonly QueueNative queue;

		private readonly CancelNative cancel;

		private readonly StatusNative status;

		public IntPtr RenderEvent { get; }

		public static uint ConfigurationSize => (uint)Marshal.SizeOf(typeof(NativePresentationConfiguration));

		public static uint BeginSize => (uint)Marshal.SizeOf(typeof(NativePresentationBegin));

		public static uint InputsSize => (uint)Marshal.SizeOf(typeof(NativePresentationInputs));

		public static uint StatusSize => (uint)Marshal.SizeOf(typeof(NativePresentationStatus));

		public PresentationBridge(NativeBridge library)
		{
			if (library.Get<Abi>("DspAaGetPresentationAbiVersion")() != 3 || ConfigurationSize != 32 || BeginSize != 24 || InputsSize != 456 || StatusSize != 472)
			{
				throw new InvalidOperationException("Native/managed presentation ABI mismatch.");
			}
			configure = library.Get<ConfigureNative>("DspAaConfigurePresentation");
			begin = library.Get<BeginNative>("DspAaBeginPresentationFrame");
			simulationEnd = library.Get<EndSimulationNative>("DspAaPresentationSimulationEnd");
			queue = library.Get<QueueNative>("DspAaQueuePresentationInputs");
			cancel = library.Get<CancelNative>("DspAaCancelPresentationInputs");
			status = library.Get<StatusNative>("DspAaGetPresentationStatus");
			RenderEvent = library.Get<EventNative>("DspAaGetPresentationRenderEvent")();
			if (RenderEvent == IntPtr.Zero)
			{
				throw new InvalidOperationException("Missing presentation render event.");
			}
		}

		public bool Configure(NativePresentationConfiguration value)
		{
			value.Size = ConfigurationSize;
			value.Version = 3u;
			return configure(ref value) == 1;
		}

		public bool Begin(out NativePresentationBegin value)
		{
			value = new NativePresentationBegin
			{
				Size = BeginSize,
				Version = 3u
			};
			return begin(ref value) == 1;
		}

		public bool SimulationEnd(ulong id)
		{
			return simulationEnd(id) == 1;
		}

		public IntPtr Queue(ref NativePresentationInputs value)
		{
			value.Size = InputsSize;
			value.Version = 3u;
			return queue(ref value);
		}

		public void Cancel(IntPtr token)
		{
			if (token != IntPtr.Zero)
			{
				cancel(token);
			}
		}

		public bool TryGetStatus(out NativePresentationStatus value)
		{
			value = new NativePresentationStatus
			{
				Size = StatusSize,
				MessageBytes = new byte[384]
			};
			return status(ref value) == 1;
		}
	}
}
namespace DSPAAMod.Game
{
	internal sealed class FrameCapture
	{
		private sealed class Resource
		{
			public string Name;

			public string File;

			public string Error;

			public string Hash;

			public int Width;

			public int Height;

			public readonly Dictionary<string, string> Metadata = new Dictionary<string, string>();

			public byte[] Bytes;

			public bool Claimed;
		}

		private sealed class Frame
		{
			public NativeFrame Native;

			public int UnityFrame;

			public readonly Dictionary<string, string> Metadata = new Dictionary<string, string>();

			public readonly List<Resource> Resources = new List<Resource>();
		}

		private sealed class Group
		{
			public string Id;

			public string Directory;

			public RenderTargets Targets;

			public readonly List<Frame> Frames = new List<Frame>();

			public readonly List<string> Errors = new List<string>();

			public int Pending;

			public bool Sealed;

			public bool Writing;
		}

		private readonly string directory;

		private readonly Action<string> log;

		private readonly object gate = new object();

		private readonly Dictionary<RenderTargets, int> pending = new Dictionary<RenderTargets, int>();

		private Group active;

		public FrameCapture(string directory, Action<string> log)
		{
			this.directory = directory;
			this.log = log;
		}

		public bool Request()
		{
			lock (gate)
			{
				if (active != null)
				{
					Log("Frame capture request ignored: a capture is already armed or in progress.");
					return false;
				}
				try
				{
					string id = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fff", CultureInfo.InvariantCulture) + "-" + Guid.NewGuid().ToString("N");
					Group obj = new Group
					{
						Id = id,
						Directory = Path.Combine(directory, id)
					};
					if (Directory.Exists(obj.Directory))
					{
						throw new IOException("Capture directory already exists.");
					}
					Directory.CreateDirectory(obj.Directory);
					WriteDocument(Path.Combine(obj.Directory, "request.xml"), "captureRequest", delegate(XmlWriter writer)
					{
						Value(writer, "id", id);
						Value(writer, "requestedUtc", DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture));
						Value(writer, "frameCount", 2);
						Value(writer, "state", "armed; result.xml is the terminal record");
					});
					active = obj;
					if (!SystemInfo.supportsAsyncGPUReadback)
					{
						Fail(obj, "AsyncGPUReadback is unsupported on this device.");
						FinishIfReady(obj);
					}
					else
					{
						Log("Frame capture armed for two consecutive submissions from the first captured camera: " + obj.Directory);
					}
					return true;
				}
				catch (Exception ex)
				{
					Log("Cannot arm frame capture: " + ex.Message);
					if (active != null)
					{
						Fail(active, "Cannot finish arming capture: " + ex.Message);
						FinishIfReady(active);
					}
					return false;
				}
			}
		}

		public bool IsBusy(RenderTargets targets)
		{
			lock (gate)
			{
				int value;
				return targets != null && pending.TryGetValue(targets, out value) && value != 0;
			}
		}

		public void Cancel(string reason)
		{
			lock (gate)
			{
				if (active != null && !active.Sealed)
				{
					Fail(active, "Capture cancelled: " + reason);
					FinishIfReady(active);
				}
			}
		}

		public void Cancel(RenderTargets targets, string reason)
		{
			lock (gate)
			{
				if (active != null && active.Targets == targets)
				{
					Cancel(reason);
				}
			}
		}

		public void EnqueueIfRequested(NativeFrame native, RenderTargets targets, Camera camera, Matrix4x4 renderedProjection, int deferredWorldTextCount)
		{
			//IL_0582: Unknown result type (might be due to invalid IL or missing references)
			//IL_0588: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_0525: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ca: Expected I4, but got Unknown
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: Invalid comparison between Unknown and I4
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			lock (gate)
			{
				Group group = active;
				if (group == null || group.Sealed)
				{
					return;
				}
				if (group.Frames.Count != 0)
				{
					Frame frame = group.Frames[0];
					int frameCount = Time.frameCount;
					if (native.Camera != frame.Native.Camera && frameCount <= frame.UnityFrame + 1)
					{
						return;
					}
					if (native.Camera != frame.Native.Camera || targets != group.Targets || native.Frame != frame.Native.Frame + 1 || frameCount != frame.UnityFrame + 1)
					{
						Fail(group, "Expected the same camera/targets on consecutive native and Unity frames; got native " + frame.Native.Frame + " -> " + native.Frame + ", Unity " + frame.UnityFrame + " -> " + frameCount + ".");
						FinishIfReady(group);
						return;
					}
				}
				try
				{
					if (targets == null || (Object)(object)camera == (Object)null)
					{
						throw new InvalidOperationException("Capture has no live targets/camera.");
					}
					Frame frame2 = new Frame
					{
						Native = native
					};
					SnapshotFrame(frame2, camera, renderedProjection, deferredWorldTextCount);
					RenderTexture[] array = (RenderTexture[])(object)new RenderTexture[4] { targets.Color, targets.Depth, targets.Motion, targets.Output };
					string[] array2 = new string[4] { "color", "depth", "motion", "output" };
					for (int i = 0; i < array.Length; i++)
					{
						RenderTexture val = array[i];
						if (!Object.op_Implicit((Object)(object)val) || !val.IsCreated())
						{
							throw new InvalidOperationException(array2[i] + " texture is not created.");
						}
						if (!SystemInfo.IsFormatSupported(val.graphicsFormat, (FormatUsage)9))
						{
							throw new NotSupportedException(array2[i] + " format does not support unconverted readback: " + ((object)val.graphicsFormat/*cast due to .constrained prefix*/).ToString());
						}
						Resource resource = new Resource();
						resource.Name = array2[i];
						resource.File = "frame-" + group.Frames.Count + "-" + array2[i] + ".raw";
						resource.Width = ((Texture)val).width;
						resource.Height = ((Texture)val).height;
						Resource resource2 = resource;
						Add(resource2.Metadata, "width", ((Texture)val).width);
						Add(resource2.Metadata, "height", ((Texture)val).height);
						Add(resource2.Metadata, "volumeDepth", val.volumeDepth);
						Add(resource2.Metadata, "dimension", ((Texture)val).dimension);
						Add(resource2.Metadata, "renderTextureFormat", val.format);
						Add(resource2.Metadata, "graphicsFormat", val.graphicsFormat);
						Add(resource2.Metadata, "graphicsFormatValue", (int)val.graphicsFormat);
						Add(resource2.Metadata, "antiAliasing", val.antiAliasing);
						Add(resource2.Metadata, "sRGB", val.sRGB);
						Add(resource2.Metadata, "mip", 0);
						if ((int)((Texture)val).dimension != 2 || val.volumeDepth != 1)
						{
							throw new NotSupportedException("Capture expects single-layer 2D render textures.");
						}
						frame2.Resources.Add(resource2);
					}
					group.Targets = targets;
					group.Frames.Add(frame2);
					group.Sealed = group.Frames.Count == 2;
					group.Pending += array.Length;
					pending.TryGetValue(targets, out var value);
					pending[targets] = value + array.Length;
					for (int j = 0; j < array.Length; j++)
					{
						Resource resource3 = frame2.Resources[j];
						try
						{
							AsyncGPUReadbackRequest request = AsyncGPUReadback.Request((Texture)(object)array[j], 0, (Action<AsyncGPUReadbackRequest>)delegate(AsyncGPUReadbackRequest completed)
							{
								//IL_0027: Unknown result type (might be due to invalid IL or missing references)
								Complete(group, targets, resource3, completed, null);
							});
							if (((AsyncGPUReadbackRequest)(ref request)).hasError)
							{
								Complete(group, targets, resource3, request, "GPU readback request failed.");
							}
						}
						catch (Exception ex)
						{
							Complete(group, targets, resource3, default(AsyncGPUReadbackRequest), "Cannot submit readback: " + ex.Message);
						}
					}
				}
				catch (Exception ex2)
				{
					Fail(group, "Cannot capture submitted frame: " + ex2.Message);
					FinishIfReady(group);
				}
			}
		}

		private void Complete(Group group, RenderTargets targets, Resource resource, AsyncGPUReadbackRequest request, string failure)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			lock (gate)
			{
				if (resource.Claimed)
				{
					return;
				}
				resource.Claimed = true;
			}
			try
			{
				if (failure != null)
				{
					throw new InvalidOperationEx

patchers/DSPAASR.Preloader.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DSPAAMod.Core;
using DSPAAMod.Interop;
using Microsoft.CodeAnalysis;
using Mono.Cecil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("NordLandeW")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Early presentation entry for DSPAASR.")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+d9acbc2eda5311a22be61cd68392d9aaa4407c0e")]
[assembly: AssemblyProduct("DSPAASR")]
[assembly: AssemblyTitle("DSPAASR.Preloader")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DSPAAMod.Interop
{
	internal static class NativeModule
	{
		[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryExW", ExactSpelling = true, SetLastError = true)]
		private static extern IntPtr LoadLibraryEx(string path, IntPtr file, uint flags);

		internal static IntPtr Load(string path)
		{
			path = Path.GetFullPath(path);
			IntPtr intPtr = LoadLibraryEx(path, IntPtr.Zero, 4352u);
			if (intPtr == IntPtr.Zero)
			{
				throw new NativeModuleLoadException(Marshal.GetLastWin32Error(), path, File.Exists(path));
			}
			return intPtr;
		}
	}
	internal sealed class NativeModuleLoadException : Win32Exception
	{
		internal string ModulePath { get; }

		internal bool ModuleFound { get; }

		internal NativeModuleLoadException(int error, string path, bool found)
			: base(error, Describe(error, found))
		{
			ModulePath = path;
			ModuleFound = found;
		}

		private static string Describe(int error, bool found)
		{
			string text;
			if ((error == 2 || error == 3 || error == 126) && !found)
			{
				text = "Keep the complete package beside DSPAAMod.dll in the plugin folder.";
			}
			else
			{
				switch (error)
				{
				case 126:
					text = "The file exists; a required dependency may be missing.";
					break;
				case 193:
				case 216:
					text = "Check for a damaged or non-x64 DLL and reinstall the complete package.";
					break;
				default:
					text = "Check the installation and Windows file access.";
					break;
				}
			}
			return "Cannot load DSPAANative.dll (Windows error " + error + ": " + new Win32Exception(error).Message + "). " + text + " See BepInEx/LogOutput.log.";
		}

		public override string ToString()
		{
			return ((ExternalException)this).ToString() + Environment.NewLine + "Expected native module: " + ModulePath + Environment.NewLine + "File present at diagnostic check: " + ModuleFound;
		}
	}
}
namespace DSPAAMod.Core
{
	public enum PresentationStartupState
	{
		Missing,
		Inactive,
		Started,
		Failed
	}
	public sealed class PresentationStartup
	{
		private const string ReceiptKey = "DSPAASR.PresentationStartup";

		private const int ReceiptVersion = 1;

		public PresentationStartupState State { get; }

		public string RuntimeDirectory { get; }

		public int RequestedBackend { get; }

		public string Error { get; }

		private PresentationStartup(PresentationStartupState state, string directory, int backend, string error)
		{
			State = state;
			RuntimeDirectory = directory;
			RequestedBackend = backend;
			Error = error ?? string.Empty;
		}

		public static PresentationStartup Read(string runtimeDirectory)
		{
			string text;
			try
			{
				text = NormalizeDirectory(runtimeDirectory);
			}
			catch (Exception ex)
			{
				return Failure(string.Empty, "Invalid plugin directory: " + ex.Message);
			}
			object data;
			lock (AppDomain.CurrentDomain)
			{
				data = AppDomain.CurrentDomain.GetData("DSPAASR.PresentationStartup");
			}
			if (data == null)
			{
				return new PresentationStartup(PresentationStartupState.Missing, text, -1, "The presentation preloader did not report a result in this process.");
			}
			if (!(data is object[] array) || array.Length != 5 || !(array[0] is int num) || num != 1 || !(array[1] is int num2) || num2 < 1 || num2 > 3 || !(array[2] is string text2) || !(array[3] is int num3) || num3 < -1 || num3 > 2 || !(array[4] is string error))
			{
				return Failure(text, "Invalid presentation preloader receipt.");
			}
			PresentationStartupState presentationStartupState = (PresentationStartupState)num2;
			if ((presentationStartupState == PresentationStartupState.Inactive && num3 != 0) || (presentationStartupState == PresentationStartupState.Started && num3 != 1 && num3 != 2))
			{
				return Failure(text, "Inconsistent presentation preloader intent.");
			}
			if (!string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
			{
				if (presentationStartupState == PresentationStartupState.Failed && text2.Length == 0)
				{
					return new PresentationStartup(presentationStartupState, text, num3, error);
				}
				return Failure(text, "The presentation preloader selected a different plugin directory.");
			}
			return new PresentationStartup(presentationStartupState, text2, num3, error);
		}

		private static PresentationStartup Failure(string directory, string error)
		{
			return new PresentationStartup(PresentationStartupState.Failed, directory, -1, error);
		}

		internal static string NormalizeDirectory(string directory)
		{
			if (string.IsNullOrWhiteSpace(directory))
			{
				throw new ArgumentException("A plugin directory is required.");
			}
			string fullPath = Path.GetFullPath(directory);
			string pathRoot = Path.GetPathRoot(fullPath);
			if (fullPath.Length <= pathRoot.Length)
			{
				return fullPath;
			}
			return fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
		}

		internal static bool TryClaim()
		{
			lock (AppDomain.CurrentDomain)
			{
				if (AppDomain.CurrentDomain.GetData("DSPAASR.PresentationStartup") != null)
				{
					return false;
				}
				AppDomain.CurrentDomain.SetData("DSPAASR.PresentationStartup", new object[5]
				{
					1,
					3,
					string.Empty,
					-1,
					"Presentation preloader initialization did not complete."
				});
				return true;
			}
		}

		internal static void Publish(PresentationStartupState state, string directory, int backend, string error = "")
		{
			if (state == PresentationStartupState.Missing)
			{
				throw new ArgumentOutOfRangeException("state");
			}
			string text = (string.IsNullOrEmpty(directory) ? string.Empty : NormalizeDirectory(directory));
			object[] data = new object[5]
			{
				1,
				(int)state,
				text,
				backend,
				error ?? string.Empty
			};
			lock (AppDomain.CurrentDomain)
			{
				AppDomain.CurrentDomain.SetData("DSPAASR.PresentationStartup", data);
			}
		}
	}
	public enum FrameGenerationBackend
	{
		Off,
		Fsr,
		Dlss
	}
	public enum FrameGenerationMode
	{
		Fixed,
		Dynamic
	}
	public enum ReflexMode
	{
		Off,
		On,
		OnWithBoost
	}
	public readonly struct FrameGenerationSettings : IEquatable<FrameGenerationSettings>
	{
		public readonly FrameGenerationBackend Backend;

		public readonly FrameGenerationMode Mode;

		public readonly uint GeneratedFrames;

		public readonly ReflexMode Reflex;

		public readonly float DynamicTargetFrameRate;

		public readonly uint FrameLimitMicroseconds;

		public static FrameGenerationSettings Default => new FrameGenerationSettings(FrameGenerationBackend.Off);

		public FrameGenerationSettings(FrameGenerationBackend backend, FrameGenerationMode mode = FrameGenerationMode.Fixed, uint generatedFrames = 1u, ReflexMode reflex = ReflexMode.On, float dynamicTargetFrameRate = 0f, uint frameLimitMicroseconds = 0u)
		{
			if (!Enum.IsDefined(typeof(FrameGenerationBackend), backend))
			{
				throw new ArgumentOutOfRangeException("backend");
			}
			if (!Enum.IsDefined(typeof(FrameGenerationMode), mode))
			{
				throw new ArgumentOutOfRangeException("mode");
			}
			if (!Enum.IsDefined(typeof(ReflexMode), reflex))
			{
				throw new ArgumentOutOfRangeException("reflex");
			}
			if (generatedFrames == 0 || generatedFrames == uint.MaxValue)
			{
				throw new ArgumentOutOfRangeException("generatedFrames");
			}
			if (float.IsNaN(dynamicTargetFrameRate) || float.IsInfinity(dynamicTargetFrameRate) || dynamicTargetFrameRate < 0f)
			{
				throw new ArgumentOutOfRangeException("dynamicTargetFrameRate");
			}
			if (backend == FrameGenerationBackend.Fsr && (mode != FrameGenerationMode.Fixed || generatedFrames != 1))
			{
				throw new ArgumentException("Analytical FSR supports one additional frame, not DLSS Multi/Dynamic generation.");
			}
			Backend = backend;
			Mode = mode;
			GeneratedFrames = generatedFrames;
			Reflex = reflex;
			DynamicTargetFrameRate = dynamicTargetFrameRate;
			FrameLimitMicroseconds = frameLimitMicroseconds;
		}

		public bool Equals(FrameGenerationSettings other)
		{
			if (Backend == other.Backend && Mode == other.Mode && GeneratedFrames == other.GeneratedFrames && Reflex == other.Reflex && DynamicTargetFrameRate == other.DynamicTargetFrameRate)
			{
				return FrameLimitMicroseconds == other.FrameLimitMicroseconds;
			}
			return false;
		}

		public override bool Equals(object value)
		{
			if (value is FrameGenerationSettings other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			uint num = ((((((uint)((int)Backend * 397) ^ (uint)Mode) * 397) ^ GeneratedFrames) * 397) ^ (uint)Reflex) * 397;
			float dynamicTargetFrameRate = DynamicTargetFrameRate;
			return (int)(((num ^ (uint)dynamicTargetFrameRate.GetHashCode()) * 397) ^ FrameLimitMicroseconds);
		}
	}
	public sealed class FrameGenerationSettingsSession
	{
		public FrameGenerationSettings Applied { get; private set; }

		public FrameGenerationSettings Draft { get; set; }

		public FrameGenerationSettingsSession(FrameGenerationSettings value)
		{
			Applied = (Draft = value);
		}

		public void Open()
		{
			Draft = Applied;
		}

		public void Cancel()
		{
			Draft = Applied;
		}

		public void Defaults()
		{
			Draft = FrameGenerationSettings.Default;
		}

		public void Apply()
		{
			Apply(null);
		}

		public void Apply(Action<FrameGenerationSettings> persist)
		{
			FrameGenerationSettings draft = Draft;
			persist?.Invoke(draft);
			Applied = draft;
		}
	}
}
namespace DSPAASR.Preloader
{
	public static class Patcher
	{
		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
		private delegate int Bootstrap(uint abi, [MarshalAs(UnmanagedType.LPWStr)] string runtime, [MarshalAs(UnmanagedType.LPWStr)] string data, uint mode);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		private delegate uint GetAbi();

		public static IEnumerable<string> TargetDLLs => Array.Empty<string>();

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			if (!PresentationStartup.TryClaim())
			{
				return;
			}
			string text = string.Empty;
			int num = -1;
			ManualLogSource val = Logger.CreateLogSource("DSPAASR.Preloader");
			try
			{
				if (Environment.OSVersion.Platform != PlatformID.Win32NT || !Environment.Is64BitProcess || !string.Equals(Path.GetFileName(Paths.ExecutablePath), "DSPGAME.exe", StringComparison.OrdinalIgnoreCase))
				{
					throw new PlatformNotSupportedException("DSPAASR presentation requires the Windows x64 DSPGAME process.");
				}
				text = FindRuntimeDirectory(Paths.PluginPath);
				num = ReadBackend(Path.Combine(Paths.ConfigPath, "dspaa.mod.cfg"));
				if (num == 0)
				{
					PresentationStartup.Publish(PresentationStartupState.Inactive, text, num);
					val.LogInfo((object)"Frame generation is off at startup; this preloader did not initialize the presentation bridge.");
				}
				else
				{
					StartPresentation(text, Path.Combine(Paths.CachePath, "DSPAAMod"));
					PresentationStartup.Publish(PresentationStartupState.Started, text, num);
					val.LogInfo((object)"Early presentation entry installed; awaiting the game's presentation channel.");
				}
			}
			catch (Exception ex)
			{
				PresentationStartup.Publish(PresentationStartupState.Failed, text, num, ex.Message);
				val.LogError((object)("Presentation startup failed; no retry or native unload will be attempted: " + ex));
			}
		}

		private static string FindRuntimeDirectory(string pluginPath)
		{
			string path = PresentationStartup.NormalizeDirectory(pluginPath);
			if (!Directory.Exists(path))
			{
				throw new DirectoryNotFoundException("The active profile has no plugin directory.");
			}
			string text = null;
			string[] files = Directory.GetFiles(path, "DSPAAMod.dll", SearchOption.AllDirectories);
			foreach (string text2 in files)
			{
				if (string.Equals(Path.GetFileName(text2), "DSPAAMod.dll", StringComparison.OrdinalIgnoreCase))
				{
					if (text != null)
					{
						throw new InvalidOperationException("Multiple enabled DSPAASR plugin installations were found in this profile.");
					}
					text = text2;
				}
			}
			if (text == null)
			{
				throw new FileNotFoundException("The DSPAASR plugin is disabled or absent from this profile.");
			}
			AssemblyName assemblyName = AssemblyName.GetAssemblyName(text);
			Version version = typeof(Patcher).Assembly.GetName().Version;
			if (assemblyName.Name != "DSPAAMod" || assemblyName.Version != version)
			{
				throw new InvalidOperationException("The DSPAASR plugin and preloader versions do not match.");
			}
			string text3 = PresentationStartup.NormalizeDirectory(Path.GetDirectoryName(text));
			if (!File.Exists(Path.Combine(text3, "DSPAANative.dll")))
			{
				throw new FileNotFoundException("DSPAANative.dll is disabled or missing beside the active DSPAASR plugin.", Path.Combine(text3, "DSPAANative.dll"));
			}
			return text3;
		}

		private static int ReadBackend(string path)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			string value = new ConfigFile(path, false)
			{
				SaveOnConfigSet = false
			}.Bind<string>("FrameGeneration", "Backend", "Off", (ConfigDescription)null).Value;
			if (value != value.Trim() || !Enum.TryParse<FrameGenerationBackend>(value, ignoreCase: true, out var result) || !Enum.IsDefined(typeof(FrameGenerationBackend), result))
			{
				throw new InvalidDataException("Unknown saved FrameGeneration.Backend: " + value);
			}
			return (int)result;
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandleExW", ExactSpelling = true, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool GetModuleHandleEx(uint flags, string path, out IntPtr module);

		[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
		private static extern IntPtr GetProcAddress(IntPtr module, string name);

		private static T Export<T>(IntPtr module, string name) where T : Delegate
		{
			IntPtr procAddress = GetProcAddress(module, name);
			if (procAddress == IntPtr.Zero)
			{
				throw new EntryPointNotFoundException("Missing DSPAANative export: " + name);
			}
			return (T)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T));
		}

		private static void StartPresentation(string runtime, string data)
		{
			string path = Path.Combine(runtime, "DSPAANative.dll");
			IntPtr module = NativeModule.Load(path);
			if (!GetModuleHandleEx(1u, path, out var _))
			{
				throw new Win32Exception(Marshal.GetLastWin32Error(), "Cannot pin DSPAANative.dll.");
			}
			if (Export<GetAbi>(module, "DspAaGetAbiVersion")() != 2 || Export<GetAbi>(module, "DspAaGetPresentationAbiVersion")() != 3)
			{
				throw new InvalidOperationException("The installed native DSPAASR ABI does not match this preloader.");
			}
			if (Export<Bootstrap>(module, "DspAaBootstrapPresentation")(1u, runtime, data, 1u) != 1)
			{
				throw new InvalidOperationException("Native early presentation initialization was rejected.");
			}
		}
	}
}