Decompiled source of GraphicsOverdrive v1.1.3

patchers/GraphicsOverdrive.Patcher.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Logging;
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("GraphicsOverdrive.Patcher")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Client-side BepInEx preloader patcher of Graphics Overdrive: opens the native door for FSR 3 (Vulkan layers + router plugin) before Unity creates its GPU device.")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3")]
[assembly: AssemblyProduct("GraphicsOverdrive.Patcher")]
[assembly: AssemblyTitle("GraphicsOverdrive.Patcher")]
[assembly: AssemblyVersion("1.1.3.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 GraphicsOverdrive.Patcher
{
	public static class NativeDoor
	{
		private const string LayerName = "VK_LAYER_WUBARRK_graphics_overdrive";

		private const string LibBase = "VkLayer_wubarrk_graphicsoverdrive";

		private const string PluginGuid = "com.wubarrk.graphicsoverdrive";

		private const int StrikeLimit = 2;

		private const string StrikesSince = "1.1.1";

		private const int StageFull = 0;

		private const int StageReduced = 1;

		private const string DefaultDeviceExtensions = "VK_NVX_binary_import,VK_NVX_image_view_handle,VK_KHR_push_descriptor,VK_KHR_buffer_device_address,VK_KHR_timeline_semaphore,VK_KHR_synchronization2";

		private const string DefaultFeatures = "bufferDeviceAddress,timelineSemaphore,synchronization2,storageWriteNoFormat,storageReadNoFormat";

		private const string RayTracingExtensions = "VK_KHR_deferred_host_operations,VK_EXT_descriptor_indexing,VK_KHR_buffer_device_address,VK_KHR_shader_float_controls,VK_KHR_spirv_1_4,VK_KHR_acceleration_structure,VK_KHR_ray_query,VK_KHR_ray_tracing_pipeline";

		private const string RayTracingFeatures = "bufferDeviceAddress,accelerationStructure,rayQuery,rayTracingPipeline";

		private static ManualLogSource log;

		private static readonly StringBuilder pendingFile = new StringBuilder();

		private static string workDir;

		public static IEnumerable<string> TargetDLLs => new string[0];

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			try
			{
				Run();
			}
			catch (Exception ex)
			{
				Note("Native door failed and was ignored, the game starts normally: " + ex);
			}
			finally
			{
				FlushFile();
			}
		}

		private static void Run()
		{
			log = Logger.CreateLogSource("GraphicsOverdrive.Native");
			bool flag = Environment.OSVersion.Platform == PlatformID.Win32NT;
			bool flag2 = !flag && File.Exists("/System/Library/CoreServices/SystemVersion.plist");
			string text = (flag ? "windows-x64" : (flag2 ? "macos" : "linux-x64"));
			string path = (flag ? "VkLayer_wubarrk_graphicsoverdrive.dll" : "VkLayer_wubarrk_graphicsoverdrive.so");
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			string text2 = Path.Combine(Path.Combine(Path.Combine(directoryName, "native"), text), path);
			if (!File.Exists(text2))
			{
				log.LogInfo((object)("Native door not available: this package has no native files for " + text + " (" + text2 + "). FSR 3 stays off; the mod works as usual."));
				return;
			}
			workDir = Path.Combine(Path.Combine(Paths.CachePath, "GraphicsOverdrive"), "vulkan");
			Directory.CreateDirectory(workDir);
			string cfgFile = Path.Combine(Paths.ConfigPath, "com.wubarrk.graphicsoverdrive.cfg");
			string text3 = NativeFrameGen.Choice((string sec, string key, string dflt) => ReadConfigString(cfgFile, sec, key, dflt));
			bool flag3 = !(Paths.ProcessName ?? "").ToLowerInvariant().Contains("server");
			if (text3 == "NvidiaSmoothMotion" && flag3)
			{
				NativeFrameGen.SmoothMotion(Note, SetEnv);
			}
			if (!ShouldActivate(out var reason, out var switchedOff))
			{
				NotActive(reason, switchedOff ? "off" : "not active");
				if (switchedOff)
				{
					NativeRoute.Remove(Note);
				}
				return;
			}
			string text4 = NativeFiles.ReadSha256File(text2 + ".sha256");
			if (text4 != null)
			{
				string b = NativeFiles.Sha256Hex(text2);
				if (!string.Equals(text4, b, StringComparison.OrdinalIgnoreCase))
				{
					NotActive("payload hash mismatch", "not active");
					return;
				}
			}
			string state = Path.Combine(workDir, "state.txt");
			string strikesFile = Path.Combine(workDir, "strikes.txt");
			if (!ChooseStage(state, strikesFile, out var stage, out var note, out reason, out var firstStartOfVersion))
			{
				NotActive(reason, "not active");
				return;
			}
			if (stage == 0)
			{
				SetEnv("GO_NATIVE_STATUS", "active");
			}
			else
			{
				SetEnv("GO_NATIVE_STATUS", "active (reduced start: " + note + ")");
				SetEnv("GO_NATIVE_STAGE_NOTE", note);
				Note("Reduced start: " + note);
			}
			SetEnv("GO_NATIVE_STAGE", (stage == 0) ? "full" : "reduced");
			SetEnv("GO_NATIVE_DIR", workDir);
			SafeDelete(Path.Combine(workDir, "hardware-scan.txt"));
			bool flag4 = ReadConfigBool(cfgFile, "Native", "RayTracing", fallback: true);
			if (!flag4 && firstStartOfVersion && File.Exists(cfgFile))
			{
				flag4 = true;
				SetEnv("GO_NATIVE_RT_DEFAULT", "1");
				Note("[Native] RayTracing = false in the config is the default of an earlier version: switched on (ray tracing is part of the full set now; turn it off again in the settings if you do not want it)");
			}
			bool flag5 = ReadConfigBool(cfgFile, "Native", "FrameGenPreload", fallback: true);
			string text5 = Path.Combine(workDir, "VkLayer_wubarrk_graphicsoverdrive.json");
			string text6 = Path.Combine(workDir, "layer.cfg");
			File.WriteAllText(text5, BuildManifest(text2));
			File.WriteAllText(text6, BuildLayerConfig(state, stage == 0 && flag4, stage));
			char sep = (flag ? ';' : ':');
			SetEnv("VK_ADD_LAYER_PATH", Prepend(Environment.GetEnvironmentVariable("VK_ADD_LAYER_PATH"), workDir, sep));
			SetEnv("VK_LOADER_LAYERS_ENABLE", Append(Environment.GetEnvironmentVariable("VK_LOADER_LAYERS_ENABLE"), "VK_LAYER_WUBARRK_graphics_overdrive", ','));
			SetEnv("VK_INSTANCE_LAYERS", Append(Environment.GetEnvironmentVariable("VK_INSTANCE_LAYERS"), "VK_LAYER_WUBARRK_graphics_overdrive", sep));
			SetEnv("GO_LAYER_CONFIG", text6);
			SetEnv("GO_LAYER_LIB", text2);
			Note("Native Vulkan layer activated: VK_LAYER_WUBARRK_graphics_overdrive from " + text2 + " (manifest " + text5 + ")");
			NativeRoute.Install(directoryName, text, workDir, Note, SetEnv);
			if (text3 == "FSR3" || text3 == "DLSS")
			{
				NativeFrameGen.ActivatePresenter(text3, directoryName, text, workDir, Note, SetEnv, (string a, string value) => Prepend(a, value, sep), Append);
			}
			else if (text3 == "Off" && flag5 && stage == 0)
			{
				NativeFrameGen.ActivatePresenter("Off", directoryName, text, workDir, Note, SetEnv, (string a, string value) => Prepend(a, value, sep), Append);
			}
			else if (text3 == "Off" && flag5)
			{
				Note("Frame generation presenter not preloaded: reduced start (frame generation is switched on with a restart this session)");
			}
		}

		private static void NotActive(string reason, string kind)
		{
			Note("Native Vulkan layer not activated: " + reason);
			SetEnv("GO_NATIVE_STATUS", kind + ": " + reason);
		}

		private static bool ShouldActivate(out string reason, out bool switchedOff)
		{
			switchedOff = false;
			string environmentVariable = Environment.GetEnvironmentVariable("GO_NATIVE");
			if (environmentVariable == "0")
			{
				reason = "GO_NATIVE=0";
				return false;
			}
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			string[] array = commandLineArgs;
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i].ToLowerInvariant() == "-nographics")
				{
					reason = "headless process (-nographics)";
					return false;
				}
			}
			string text = Paths.ProcessName ?? "";
			if (text.ToLowerInvariant().Contains("server"))
			{
				reason = "dedicated server process (" + text + ")";
				return false;
			}
			if (IntPtr.Size != 8)
			{
				reason = "32-bit process";
				return false;
			}
			if (environmentVariable != "1" && !ReadConfigBool(Path.Combine(Paths.ConfigPath, "com.wubarrk.graphicsoverdrive.cfg"), "Native", "Enabled", fallback: true))
			{
				reason = "switched off in the config ([Native] Enabled = false)";
				switchedOff = true;
				return false;
			}
			bool flag = false;
			array = commandLineArgs;
			foreach (string text2 in array)
			{
				string text3 = text2.ToLowerInvariant();
				if (text3 == "-force-vulkan")
				{
					flag = true;
				}
				switch (text3)
				{
				case "-force-d3d11":
				case "-force-d3d12":
				case "-force-glcore":
				case "-force-opengl":
				case "-force-gles":
				case "-force-metal":
					reason = "the game was launched with " + text2 + " (native mode is Vulkan only)";
					return false;
				}
			}
			if (Environment.OSVersion.Platform == PlatformID.Win32NT && !flag)
			{
				reason = "on Windows the game runs Direct3D 11 unless it is started with -force-vulkan: add -force-vulkan to Valheim's launch options";
				return false;
			}
			reason = null;
			return true;
		}

		private static bool ChooseStage(string state, string strikesFile, out int stage, out string note, out string reason, out bool firstStartOfVersion)
		{
			reason = null;
			note = null;
			stage = 0;
			string text = Path.Combine(Path.GetDirectoryName(strikesFile), "stage.txt");
			if (Environment.GetEnvironmentVariable("GO_NATIVE_RESET") == "1")
			{
				SafeDelete(state);
				SafeDelete(strikesFile);
				SafeDelete(text);
			}
			string path = Path.Combine(Path.GetDirectoryName(strikesFile), "strikes-since.txt");
			string text2 = null;
			try
			{
				if (File.Exists(path))
				{
					text2 = File.ReadAllText(path).Trim();
				}
			}
			catch
			{
			}
			firstStartOfVersion = text2 != "1.1.1";
			if (firstStartOfVersion)
			{
				int result = 0;
				try
				{
					if (File.Exists(strikesFile))
					{
						int.TryParse(File.ReadAllText(strikesFile).Trim(), out result);
					}
				}
				catch
				{
				}
				if (result > 0 || ReadStateValue(state) == "safe_mode" || File.Exists(text))
				{
					Note("Safe-mode count from an older version cleared (" + result + " strike(s)): version 1.1.1 starts from a clean slate (it loads more by default and falls back in stages)");
				}
				SafeDelete(state);
				SafeDelete(strikesFile);
				SafeDelete(text);
				try
				{
					File.WriteAllText(path, "1.1.1\n");
				}
				catch
				{
				}
			}
			int result2 = 0;
			try
			{
				if (File.Exists(strikesFile))
				{
					int.TryParse(File.ReadAllText(strikesFile).Trim(), out result2);
				}
			}
			catch
			{
			}
			int result3 = 0;
			bool flag = false;
			try
			{
				if (File.Exists(text))
				{
					string[] array = File.ReadAllLines(text);
					foreach (string text3 in array)
					{
						if (text3.StartsWith("stage="))
						{
							int.TryParse(text3.Substring(6).Trim(), out result3);
						}
						else if (text3.StartsWith("sticky="))
						{
							flag = text3.Substring(7).Trim() == "1";
						}
					}
				}
			}
			catch
			{
			}
			string text4 = ReadStateValue(state);
			bool flag2 = text4 == "instance_created" || text4 == "device_created" || text4 == "swapchain_requested";
			if (text4 == "ok" || text4 == "clean_exit")
			{
				result2 = 0;
			}
			else if (flag2)
			{
				result2++;
			}
			try
			{
				File.WriteAllText(strikesFile, result2.ToString());
			}
			catch
			{
			}
			if (result2 >= 2)
			{
				try
				{
					File.WriteAllText(state, "state=safe_mode\n");
				}
				catch
				{
				}
				reason = "safe mode: " + result2 + " native starts in a row never reached a first frame. Delete " + strikesFile + " or start once with GO_NATIVE_RESET=1 to try again";
				return false;
			}
			string text5 = ((text4 == "instance_created") ? "while the graphics device was being created" : ((text4 == "swapchain_requested") ? "while the game's swapchain was being created" : "after the graphics device was created (or it was closed while the game loaded its plugins)"));
			if (flag2)
			{
				stage = 1;
				bool flag3 = text4 != "device_created";
				if (result3 == 0 && flag3)
				{
					flag = true;
				}
				note = "the last start never reached its first frame (it stopped " + text5 + "), so this start leaves out the ray tracing set and the frame generation preload. " + (flag ? ("Delete " + text + " or start once with GO_NATIVE_RESET=1 to try the full set again") : "If this start runs, the next one tries the full set again");
			}
			else if (flag)
			{
				stage = 1;
				note = "an earlier start stopped while the graphics device or the swapchain was being created, so ray tracing set and frame generation preload stay left out. Delete " + text + " or start once with GO_NATIVE_RESET=1 to try the full set again";
			}
			try
			{
				File.WriteAllText(text, "stage=" + stage + "\nsticky=" + (flag ? "1" : "0") + "\n");
			}
			catch
			{
			}
			SafeDelete(state);
			return true;
		}

		private static string BuildManifest(string payload)
		{
			string text = payload.Replace("\\", "\\\\").Replace("\"", "\\\"");
			return "{\n  \"file_format_version\": \"1.2.0\",\n  \"layer\": {\n    \"name\": \"VK_LAYER_WUBARRK_graphics_overdrive\",\n    \"type\": \"GLOBAL\",\n    \"library_path\": \"" + text + "\",\n    \"api_version\": \"1.3.0\",\n    \"implementation_version\": \"1\",\n    \"description\": \"Graphics Overdrive native layer (client-side)\",\n    \"functions\": { \"vkNegotiateLoaderLayerInterfaceVersion\": \"vkNegotiateLoaderLayerInterfaceVersion\" }\n  }\n}\n";
		}

		private static string BuildLayerConfig(string state, bool withRt, int stage)
		{
			string file = Path.Combine(Paths.ConfigPath, "com.wubarrk.graphicsoverdrive.cfg");
			string value = ReadConfigString(file, "Native", "DeviceExtensions", "VK_NVX_binary_import,VK_NVX_image_view_handle,VK_KHR_push_descriptor,VK_KHR_buffer_device_address,VK_KHR_timeline_semaphore,VK_KHR_synchronization2");
			string value2 = ReadConfigString(file, "Native", "Features", "bufferDeviceAddress,timelineSemaphore,synchronization2,storageWriteNoFormat,storageReadNoFormat");
			bool flag = ReadConfigBool(file, "Native", "Inject", fallback: true);
			string value3 = ReadConfigString(file, "Native", "LayerLogLevel", "1");
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("log=").Append(Path.Combine(workDir, "layer.log")).Append('\n');
			stringBuilder.Append("state_file=").Append(state).Append('\n');
			stringBuilder.Append("scan_file=").Append(Path.Combine(workDir, "hardware-scan.txt")).Append('\n');
			stringBuilder.Append("stage=").Append((stage == 0) ? "full" : "reduced").Append('\n');
			stringBuilder.Append("inject=").Append(flag ? "1" : "0").Append('\n');
			stringBuilder.Append("log_level=").Append(value3).Append('\n');
			stringBuilder.Append("device_extensions=").Append(value).Append('\n');
			stringBuilder.Append("features=").Append(value2).Append('\n');
			if (withRt)
			{
				stringBuilder.Append("rt_device_extensions=").Append("VK_KHR_deferred_host_operations,VK_EXT_descriptor_indexing,VK_KHR_buffer_device_address,VK_KHR_shader_float_controls,VK_KHR_spirv_1_4,VK_KHR_acceleration_structure,VK_KHR_ray_query,VK_KHR_ray_tracing_pipeline").Append('\n');
				stringBuilder.Append("rt_features=").Append("bufferDeviceAddress,accelerationStructure,rayQuery,rayTracingPipeline").Append('\n');
				Note("Ray tracing set requested ([Native] RayTracing): the layer adds the hardware ray tracing extensions the GPU offers, and drops them again if the driver refuses the device with them");
			}
			return stringBuilder.ToString();
		}

		private static string Prepend(string existing, string value, char sep)
		{
			if (!string.IsNullOrEmpty(existing))
			{
				return value + sep + existing;
			}
			return value;
		}

		private static string Append(string existing, string value, char sep)
		{
			if (string.IsNullOrEmpty(existing))
			{
				return value;
			}
			string[] array = existing.Split(new char[1] { sep });
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == value)
				{
					return existing;
				}
			}
			return existing + sep + value;
		}

		[DllImport("libc", EntryPoint = "setenv")]
		private static extern int c_setenv(string name, string value, int overwrite);

		private static void SetEnv(string name, string value)
		{
			Environment.SetEnvironmentVariable(name, value);
			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
			{
				try
				{
					c_setenv(name, value, 1);
				}
				catch (Exception ex)
				{
					Note("setenv(" + name + ") through libc failed: " + ex.Message);
				}
			}
		}

		private static string ReadConfigString(string file, string section, string key, string fallback)
		{
			try
			{
				if (!File.Exists(file))
				{
					return fallback;
				}
				bool flag = false;
				string[] array = File.ReadAllLines(file);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.StartsWith("[") && text.EndsWith("]"))
					{
						flag = string.Equals(text.Substring(1, text.Length - 2), section, StringComparison.OrdinalIgnoreCase);
					}
					else if (flag && !text.StartsWith("#") && text.Length != 0)
					{
						int num = text.IndexOf('=');
						if (num >= 0 && string.Equals(text.Substring(0, num).Trim(), key, StringComparison.OrdinalIgnoreCase))
						{
							return text.Substring(num + 1).Trim();
						}
					}
				}
			}
			catch
			{
			}
			return fallback;
		}

		private static bool ReadConfigBool(string file, string section, string key, bool fallback)
		{
			string text = ReadConfigString(file, section, key, null);
			if (text == null || !bool.TryParse(text, out var result))
			{
				return fallback;
			}
			return result;
		}

		private static string ReadStateValue(string state)
		{
			try
			{
				if (!File.Exists(state))
				{
					return null;
				}
				string[] array = File.ReadAllLines(state);
				foreach (string text in array)
				{
					if (text.StartsWith("state="))
					{
						return text.Substring(6).Trim();
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void SafeDelete(string path)
		{
			try
			{
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch
			{
			}
		}

		private static void Note(string message)
		{
			if (log != null)
			{
				log.LogInfo((object)message);
			}
			pendingFile.Append(DateTime.UtcNow.ToString("HH:mm:ss.fff")).Append(' ').Append(message)
				.Append('\n');
		}

		private static void FlushFile()
		{
			try
			{
				if (workDir != null)
				{
					File.AppendAllText(Path.Combine(workDir, "patcher.log"), pendingFile.ToString());
				}
			}
			catch
			{
			}
		}
	}
	internal static class NativeFiles
	{
		internal static string ReadSha256File(string path)
		{
			try
			{
				return File.Exists(path) ? File.ReadAllText(path).Trim().Split(new char[1] { ' ' })[0] : null;
			}
			catch
			{
				return null;
			}
		}

		internal static string Sha256Hex(string path)
		{
			using SHA256 sHA = SHA256.Create();
			using FileStream inputStream = File.OpenRead(path);
			byte[] array = sHA.ComputeHash(inputStream);
			StringBuilder stringBuilder = new StringBuilder();
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2"));
			}
			return stringBuilder.ToString();
		}
	}
	internal static class NativeFrameGen
	{
		internal const string LayerName = "VK_LAYER_WUBARRK_frame_generation";

		private const string LibBase = "VkLayer_wubarrk_frame_generation";

		internal static string Choice(Func<string, string, string, string> readConfig)
		{
			string a = (readConfig("Native", "FrameGeneration", "Off") ?? "Off").Trim();
			if (string.Equals(a, "NvidiaSmoothMotion", StringComparison.OrdinalIgnoreCase))
			{
				return "NvidiaSmoothMotion";
			}
			if (string.Equals(a, "FSR3", StringComparison.OrdinalIgnoreCase))
			{
				return "FSR3";
			}
			if (string.Equals(a, "DLSS", StringComparison.OrdinalIgnoreCase))
			{
				return "DLSS";
			}
			return "Off";
		}

		internal static void SmoothMotion(Action<string> note, Action<string, string> setEnv)
		{
			try
			{
				if (Environment.OSVersion.Platform == PlatformID.Win32NT)
				{
					note("NVIDIA Smooth Motion not requested: the environment variable is documented for the Linux driver only");
					return;
				}
				string[] commandLineArgs = Environment.GetCommandLineArgs();
				foreach (string text in commandLineArgs)
				{
					switch (text.ToLowerInvariant())
					{
					case "-nographics":
					case "-force-glcore":
					case "-force-opengl":
					case "-force-gles":
						note("NVIDIA Smooth Motion not requested: " + text);
						return;
					}
				}
				setEnv("NVPRESENT_ENABLE_SMOOTH_MOTION", "1");
				note("NVIDIA Smooth Motion requested (NVPRESENT_ENABLE_SMOOTH_MOTION=1): the NVIDIA driver generates frames itself on RTX 40 series and newer");
			}
			catch (Exception ex)
			{
				note("NVIDIA Smooth Motion switch ignored: " + ex.Message);
			}
		}

		internal static void ActivatePresenter(string backend, string patcherDir, string platformDir, string workDir, Action<string> note, Action<string, string> setEnv, Func<string, string, string> prepend, Func<string, string, char, string> append)
		{
			try
			{
				bool flag = Environment.OSVersion.Platform == PlatformID.Win32NT;
				string text = Path.Combine(Path.Combine(Path.Combine(patcherDir, "native"), platformDir), "VkLayer_wubarrk_frame_generation" + (flag ? ".dll" : ".so"));
				if (!File.Exists(text))
				{
					note("Frame generation not enabled: presenter payload missing (" + text + ")");
					return;
				}
				string text2 = NativeFiles.ReadSha256File(text + ".sha256");
				if (text2 != null && !string.Equals(text2, NativeFiles.Sha256Hex(text), StringComparison.OrdinalIgnoreCase))
				{
					note("Frame generation not enabled: presenter payload hash mismatch");
					return;
				}
				string text3 = text.Replace("\\", "\\\\").Replace("\"", "\\\"");
				File.WriteAllText(Path.Combine(workDir, "VkLayer_wubarrk_frame_generation.json"), "{\n  \"file_format_version\": \"1.2.0\",\n  \"layer\": {\n    \"name\": \"VK_LAYER_WUBARRK_frame_generation\",\n    \"type\": \"GLOBAL\",\n    \"library_path\": \"" + text3 + "\",\n    \"api_version\": \"1.3.0\",\n    \"implementation_version\": \"1\",\n    \"description\": \"Graphics Overdrive frame generation presenter (client-side)\",\n    \"functions\": { \"vkNegotiateLoaderLayerInterfaceVersion\": \"vkNegotiateLoaderLayerInterfaceVersion\" }\n  }\n}\n");
				string text4 = Path.Combine(workDir, "fg.cfg");
				File.WriteAllText(text4, "log=" + Path.Combine(workDir, "fg.log") + "\nlog_level=1\nbackend=" + ((backend == "DLSS") ? "dlssg" : ((backend == "FSR3") ? "fsr3" : "off")) + "\n");
				char arg = (flag ? ';' : ':');
				setEnv("VK_LOADER_LAYERS_ENABLE", append(Environment.GetEnvironmentVariable("VK_LOADER_LAYERS_ENABLE"), "VK_LAYER_WUBARRK_frame_generation", ','));
				setEnv("VK_INSTANCE_LAYERS", append(Environment.GetEnvironmentVariable("VK_INSTANCE_LAYERS"), "VK_LAYER_WUBARRK_frame_generation", arg));
				setEnv("GO_FG_CONFIG", text4);
				setEnv("GO_FG_LAYER_LIB", text);
				string environmentVariable = Environment.GetEnvironmentVariable("GO_ADDON_DIRS");
				if (!string.IsNullOrEmpty(environmentVariable))
				{
					setEnv("GO_FG_ADDON_DIRS", environmentVariable);
				}
				note("Frame generation presenter enabled: VK_LAYER_WUBARRK_frame_generation from " + text + ((backend == "Off") ? ", preloaded (frame generation is Off: it passes the game's frames through until it is switched on)" : (", initial backend " + backend)));
			}
			catch (Exception ex)
			{
				note("Frame generation not enabled (ignored, the game starts normally): " + ex.Message);
			}
		}
	}
	internal static class NativeRoute
	{
		private static string RouterFileName
		{
			get
			{
				if (!IsWindows)
				{
					return "libgo_router.so";
				}
				return "go_router.dll";
			}
		}

		private static string BackendPrefix
		{
			get
			{
				if (!IsWindows)
				{
					return "libgo_backend_";
				}
				return "go_backend_";
			}
		}

		private static string LibExtension
		{
			get
			{
				if (!IsWindows)
				{
					return ".so";
				}
				return ".dll";
			}
		}

		private static bool IsWindows => Environment.OSVersion.Platform == PlatformID.Win32NT;

		private static string UnityPluginDir => Path.Combine(Path.Combine(Path.GetDirectoryName(Paths.ManagedPath), "Plugins"), "x86_64");

		internal static void Install(string patcherDir, string platformDir, string workDir, Action<string> note, Action<string, string> setEnv)
		{
			try
			{
				string text = Path.Combine(Path.Combine(Path.Combine(patcherDir, "native"), platformDir), RouterFileName);
				if (!File.Exists(text))
				{
					note("Router not installed: payload missing (" + text + ")");
					return;
				}
				string b = NativeFiles.Sha256Hex(text);
				string text2 = NativeFiles.ReadSha256File(text + ".sha256");
				if (text2 != null && !string.Equals(text2, b, StringComparison.OrdinalIgnoreCase))
				{
					note("Router not installed: payload hash mismatch");
					return;
				}
				string unityPluginDir = UnityPluginDir;
				string text3 = Path.Combine(unityPluginDir, RouterFileName);
				if (File.Exists(text3) && string.Equals(NativeFiles.Sha256Hex(text3), b, StringComparison.OrdinalIgnoreCase))
				{
					note("Router already in place: " + text3);
				}
				else
				{
					Directory.CreateDirectory(unityPluginDir);
					string text4 = text3 + ".go-tmp";
					File.Copy(text, text4, overwrite: true);
					if (File.Exists(text3))
					{
						File.Delete(text3);
					}
					File.Move(text4, text3);
					note("Router installed: " + text3);
				}
				string text5 = Path.Combine(workDir, "router-data");
				Directory.CreateDirectory(text5);
				setEnv("GO_ROUTER_LIB", text3);
				setEnv("GO_ROUTER_LOG", Path.Combine(workDir, "router.log"));
				setEnv("GO_ROUTER_DATA", text5);
				string text6 = FindAddonDirs(platformDir, note);
				setEnv("GO_ADDON_DIRS", text6);
				note("Router add-on folders: " + ((text6.Length == 0) ? "none" : text6));
			}
			catch (Exception ex)
			{
				note("Router not installed (ignored, the game starts normally): " + ex.Message);
			}
		}

		internal static void Remove(Action<string> note)
		{
			try
			{
				string text = Path.Combine(UnityPluginDir, RouterFileName);
				if (File.Exists(text))
				{
					File.Delete(text);
					note("Router removed from " + text + " (native mode is off)");
				}
			}
			catch (Exception ex)
			{
				note("Router could not be removed: " + ex.Message);
			}
		}

		private static string FindAddonDirs(string platformDir, Action<string> note)
		{
			List<string> list = new List<string>();
			if (!Directory.Exists(Paths.PatcherPluginPath))
			{
				return "";
			}
			string[] directories = Directory.GetDirectories(Paths.PatcherPluginPath);
			for (int i = 0; i < directories.Length; i++)
			{
				string text = Path.Combine(Path.Combine(directories[i], "native"), platformDir);
				if (!Directory.Exists(text))
				{
					continue;
				}
				bool flag = false;
				bool flag2 = true;
				string[] files = Directory.GetFiles(text, BackendPrefix + "*" + LibExtension);
				foreach (string text2 in files)
				{
					flag = true;
					string text3 = NativeFiles.ReadSha256File(text2 + ".sha256");
					if (text3 != null && !string.Equals(text3, NativeFiles.Sha256Hex(text2), StringComparison.OrdinalIgnoreCase))
					{
						flag2 = false;
						note("Add-on skipped, hash mismatch: " + text2);
					}
				}
				if (flag && flag2)
				{
					list.Add(text);
				}
			}
			return string.Join(IsWindows ? ";" : ":", list.ToArray());
		}
	}
}

plugins/GraphicsOverdrive.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using AmplifyOcclusion;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GraphicsOverdrive.Benchmark;
using GraphicsOverdrive.Native;
using GraphicsOverdrive.Patches;
using GraphicsOverdrive.Presets;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Experimental.Rendering;
using UnityEngine.PostProcessing;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_postprocessing")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("GraphicsOverdrive")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Unlocks all the graphic dials in the Unity engine and Valheim into an easy to use, understand and tweak format.")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3")]
[assembly: AssemblyProduct("GraphicsOverdrive")]
[assembly: AssemblyTitle("GraphicsOverdrive")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.3.0")]
[module: UnverifiableCode]
[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 GraphicsOverdrive
{
	public static class AssetLoader
	{
		private static Texture2D s_cornerRibbonTex;

		private static Sprite s_cornerRibbonSprite;

		public static Sprite GetCornerRibbonSprite()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_cornerRibbonSprite != (Object)null)
			{
				return s_cornerRibbonSprite;
			}
			Texture2D cornerRibbonTexture = GetCornerRibbonTexture();
			if ((Object)(object)cornerRibbonTexture != (Object)null)
			{
				s_cornerRibbonSprite = Sprite.Create(cornerRibbonTexture, new Rect(0f, 0f, (float)((Texture)cornerRibbonTexture).width, (float)((Texture)cornerRibbonTexture).height), new Vector2(0f, 1f));
				((Object)s_cornerRibbonSprite).hideFlags = (HideFlags)61;
			}
			return s_cornerRibbonSprite;
		}

		public static Texture2D GetCornerRibbonTexture()
		{
			if ((Object)(object)s_cornerRibbonTex != (Object)null)
			{
				return s_cornerRibbonTex;
			}
			s_cornerRibbonTex = LoadEmbeddedTexture("GraphicsOverdrive.wubarrk_red_ribbon.png");
			if ((Object)(object)s_cornerRibbonTex == (Object)null)
			{
				s_cornerRibbonTex = CreateFallbackRibbon();
			}
			((Object)s_cornerRibbonTex).hideFlags = (HideFlags)61;
			return s_cornerRibbonTex;
		}

		private static Texture2D LoadEmbeddedTexture(string resourceName)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			Texture2D val = null;
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
				if (stream == null)
				{
					return null;
				}
				byte[] array;
				using (MemoryStream memoryStream = new MemoryStream())
				{
					stream.CopyTo(memoryStream);
					array = memoryStream.ToArray();
				}
				MethodInfo methodInfo = (Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule") ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine"))?.GetMethod("LoadImage", new Type[2]
				{
					typeof(Texture2D),
					typeof(byte[])
				});
				if (methodInfo == null)
				{
					return null;
				}
				val = new Texture2D(2, 2, (TextureFormat)4, false);
				if ((bool)methodInfo.Invoke(null, new object[2] { val, array }))
				{
					((Texture)val).wrapMode = (TextureWrapMode)1;
					((Texture)val).filterMode = (FilterMode)1;
					return val;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("[AssetLoader] Could not load embedded resource '" + resourceName + "': " + ex.Message);
			}
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
			return null;
		}

		private static Texture2D CreateFallbackRibbon()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(180, 180, (TextureFormat)4, false);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0f, 0f, 0f, 0f);
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.55f, 0.05f, 0.08f, 0.95f);
			Color val4 = default(Color);
			((Color)(ref val4))..ctor(0.85f, 0.68f, 0.25f, 1f);
			for (int i = 0; i < 180; i++)
			{
				for (int j = 0; j < 180; j++)
				{
					int num = j + i;
					if (num >= 80 && num <= 150)
					{
						val.SetPixel(j, i, (num <= 84 || num >= 146) ? val4 : val3);
					}
					else
					{
						val.SetPixel(j, i, val2);
					}
				}
			}
			val.Apply();
			return val;
		}
	}
	public static class BasicMode
	{
		public enum RtLevel
		{
			Off,
			Low,
			Medium,
			High,
			Ultra,
			Custom
		}

		private struct Recipe
		{
			public GraphicsConfig.RtShadowMode shadows;

			public GraphicsConfig.RtGiMode gi;

			public bool reflections;

			public bool water;

			public bool lights;

			public GraphicsConfig.RtResolution resolution;
		}

		public static readonly RtLevel[] Levels = new RtLevel[5]
		{
			RtLevel.Off,
			RtLevel.Low,
			RtLevel.Medium,
			RtLevel.High,
			RtLevel.Ultra
		};

		public static bool IsBasic
		{
			get
			{
				if (GraphicsConfig.MenuMode != null)
				{
					return GraphicsConfig.MenuMode.Value == GraphicsConfig.MenuModeChoice.Basic;
				}
				return true;
			}
		}

		public static bool RtOffered => GraphicsConfig.RtSunShadows != null;

		public static string Label(RtLevel level)
		{
			return level switch
			{
				RtLevel.Off => "Off", 
				RtLevel.Low => "Low", 
				RtLevel.Medium => "Medium", 
				RtLevel.High => "High", 
				RtLevel.Ultra => "Ultra", 
				_ => "Custom", 
			};
		}

		public static string Describe(RtLevel level)
		{
			return level switch
			{
				RtLevel.Off => "no ray tracing", 
				RtLevel.Low => "sun shadows", 
				RtLevel.Medium => "shadows, shade, torches, water", 
				RtLevel.High => "+ bounced light, reflections", 
				RtLevel.Ultra => "everything, full resolution", 
				_ => "set in Expert mode", 
			};
		}

		private static Recipe Of(RtLevel level)
		{
			return level switch
			{
				RtLevel.Low => new Recipe
				{
					shadows = GraphicsConfig.RtShadowMode.Hybrid,
					gi = GraphicsConfig.RtGiMode.Off,
					resolution = GraphicsConfig.RtResolution.Half
				}, 
				RtLevel.Medium => new Recipe
				{
					shadows = GraphicsConfig.RtShadowMode.Hybrid,
					gi = GraphicsConfig.RtGiMode.AmbientOcclusion,
					water = true,
					lights = true,
					resolution = GraphicsConfig.RtResolution.Half
				}, 
				RtLevel.High => new Recipe
				{
					shadows = GraphicsConfig.RtShadowMode.Hybrid,
					gi = GraphicsConfig.RtGiMode.Full,
					reflections = true,
					water = true,
					lights = true,
					resolution = GraphicsConfig.RtResolution.Half
				}, 
				RtLevel.Ultra => new Recipe
				{
					shadows = GraphicsConfig.RtShadowMode.Hybrid,
					gi = GraphicsConfig.RtGiMode.Full,
					reflections = true,
					water = true,
					lights = true,
					resolution = GraphicsConfig.RtResolution.Full
				}, 
				_ => new Recipe
				{
					shadows = GraphicsConfig.RtShadowMode.Off,
					gi = GraphicsConfig.RtGiMode.Off,
					resolution = GraphicsConfig.RtResolution.Half
				}, 
			};
		}

		public static RtLevel CurrentRtLevel()
		{
			if (!RtOffered)
			{
				return RtLevel.Off;
			}
			RtLevel[] levels = Levels;
			foreach (RtLevel rtLevel in levels)
			{
				Recipe recipe = Of(rtLevel);
				bool flag = recipe.gi != GraphicsConfig.RtGiMode.Off || recipe.reflections || recipe.water || recipe.lights;
				if (GraphicsConfig.RtSunShadows.Value == recipe.shadows && GraphicsConfig.RtGlobalIllumination.Value == recipe.gi && GraphicsConfig.RtReflections.Value == recipe.reflections && GraphicsConfig.RtWaterReflections.Value == recipe.water && GraphicsConfig.RtPointLights.Value == recipe.lights && (!flag || GraphicsConfig.RtLightingResolution.Value == recipe.resolution))
				{
					return rtLevel;
				}
			}
			return RtLevel.Custom;
		}

		public static void ApplyRtLevel(RtLevel level)
		{
			if (RtOffered && level != RtLevel.Custom)
			{
				Recipe recipe = Of(level);
				GraphicsConfig.RtSunShadows.Value = recipe.shadows;
				GraphicsConfig.RtGlobalIllumination.Value = recipe.gi;
				GraphicsConfig.RtReflections.Value = recipe.reflections;
				GraphicsConfig.RtWaterReflections.Value = recipe.water;
				GraphicsConfig.RtPointLights.Value = recipe.lights;
				if (recipe.gi != GraphicsConfig.RtGiMode.Off || recipe.reflections || recipe.water || recipe.lights)
				{
					GraphicsConfig.RtLightingResolution.Value = recipe.resolution;
				}
			}
		}
	}
	public static class CameraDials
	{
		internal enum PresetField
		{
			FieldOfView,
			Zoom,
			Shake
		}

		private sealed class Slot
		{
			public float Game;

			public float Written = float.NaN;

			public float Dial = float.NaN;

			public bool Yielding;

			public float Theirs;

			public bool Logged;
		}

		private const int Fov = 0;

		private const int Zoom = 1;

		private const int Boat = 2;

		private const int Shake = 3;

		private const int Slots = 4;

		private static readonly string[] s_names = new string[4] { "Field of view", "Zoom limit on foot", "Zoom limit on a boat", "Screen shake" };

		private static readonly float[] s_default = new float[4] { 65f, 8f, 16f, 1f };

		private static readonly Slot[] s_slots = (from i in Enumerable.Range(0, 4)
			select new Slot
			{
				Game = s_default[i]
			}).ToArray();

		private static GameCamera s_camera;

		private static bool s_summaryLogged;

		private static string s_otherMods;

		private static string s_status = "";

		private static bool s_statusDirty = true;

		private static GraphicsConfig.CameraControlMode s_statusMode;

		private static Vector4 s_loggedVerbose = new Vector4(float.NaN, 0f, 0f, 0f);

		private static GraphicsConfig.CameraControlMode Mode
		{
			get
			{
				if (!GraphicsConfig.MasterEnable.Value)
				{
					return GraphicsConfig.CameraControlMode.Off;
				}
				if (GraphicsConfig.CameraControl == null)
				{
					return GraphicsConfig.CameraControlMode.Auto;
				}
				return GraphicsConfig.CameraControl.Value;
			}
		}

		public static string StatusLine
		{
			get
			{
				GraphicsConfig.CameraControlMode mode = Mode;
				if (s_statusDirty || mode != s_statusMode)
				{
					s_statusDirty = false;
					s_statusMode = mode;
					s_status = BuildStatus(mode);
				}
				return s_status;
			}
		}

		public static string OtherCameraMods => s_otherMods ?? (s_otherMods = FindOtherCameraMods());

		private static bool Near(float a, float b)
		{
			return Mathf.Abs(a - b) <= 0.001f;
		}

		internal static void OnCameraAwake(GameCamera cam)
		{
			if (!((Object)(object)cam == (Object)null))
			{
				Begin(cam);
				s_slots[0].Game = cam.m_fov;
				s_slots[1].Game = cam.m_maxDistance;
				s_slots[2].Game = cam.m_maxDistanceBoat;
				s_slots[3].Game = cam.m_shakeMovement;
			}
		}

		private static void Begin(GameCamera cam)
		{
			s_camera = cam;
			s_summaryLogged = false;
			s_statusDirty = true;
			for (int i = 0; i < 4; i++)
			{
				Slot obj = s_slots[i];
				obj.Game = s_default[i];
				obj.Written = float.NaN;
				obj.Dial = float.NaN;
				obj.Yielding = false;
			}
		}

		internal static void Apply(GameCamera cam)
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)cam == (Object)null)
			{
				return;
			}
			if (cam != s_camera)
			{
				Begin(cam);
			}
			GraphicsConfig.CameraControlMode mode = Mode;
			if (!s_summaryLogged)
			{
				LogSummary(cam, mode);
			}
			if (mode == GraphicsConfig.CameraControlMode.Off)
			{
				Release(cam);
				return;
			}
			if ((!(cam.m_fovInertia > 0f) || Mathf.Approximately(cam.m_fovTarget, cam.m_fov)) && Keep(0, ref cam.m_fov, GraphicsConfig.CameraFieldOfView.Value, mode) && cam.m_fovBase != 0f)
			{
				cam.m_fovBase = cam.m_fov;
				cam.m_fovTarget = cam.m_fov;
			}
			float value = GraphicsConfig.CameraMaxDistance.Value;
			Keep(1, ref cam.m_maxDistance, value, mode);
			Keep(2, ref cam.m_maxDistanceBoat, value * 2f, mode);
			Keep(3, ref cam.m_shakeMovement, GraphicsConfig.CameraShakeScale.Value, mode);
			if (GraphicsConfig.VerboseLogging.Value)
			{
				Vector4 val = default(Vector4);
				((Vector4)(ref val))..ctor(cam.m_fov, cam.m_maxDistance, cam.m_maxDistanceBoat, cam.m_shakeMovement);
				if (val != s_loggedVerbose)
				{
					s_loggedVerbose = val;
					Plugin.LogInfo($"[Camera] in force: field of view {val.x:0.#}, zoom {val.y:0.#} m (boat {val.z:0.#} m), shake {val.w:0.##}x; {StatusLine}");
				}
			}
		}

		internal static void Release(GameCamera cam)
		{
			if (!((Object)(object)cam == (Object)null) && cam == s_camera)
			{
				if (ReleaseOne(0, ref cam.m_fov) && cam.m_fovBase != 0f)
				{
					cam.m_fovBase = cam.m_fov;
					cam.m_fovTarget = cam.m_fov;
				}
				ReleaseOne(1, ref cam.m_maxDistance);
				ReleaseOne(2, ref cam.m_maxDistanceBoat);
				ReleaseOne(3, ref cam.m_shakeMovement);
			}
		}

		private static bool ReleaseOne(int i, ref float field)
		{
			Slot slot = s_slots[i];
			bool result = false;
			if (!float.IsNaN(slot.Written) && Near(field, slot.Written))
			{
				field = slot.Game;
				result = true;
			}
			if (!float.IsNaN(slot.Written) || slot.Yielding)
			{
				s_statusDirty = true;
			}
			slot.Written = float.NaN;
			slot.Dial = float.NaN;
			slot.Yielding = false;
			return result;
		}

		private static bool Keep(int i, ref float field, float want, GraphicsConfig.CameraControlMode mode)
		{
			Slot slot = s_slots[i];
			float num = field;
			bool flag = !float.IsNaN(slot.Dial) && !Near(slot.Dial, want);
			slot.Dial = want;
			bool flag2 = !float.IsNaN(slot.Written) && Near(num, slot.Written);
			if (mode == GraphicsConfig.CameraControlMode.Auto && !flag2 && !Near(num, slot.Game) && !Near(num, want))
			{
				if (!slot.Yielding)
				{
					Yield(i, num, slot);
				}
				slot.Yielding = true;
				if (!flag)
				{
					return false;
				}
			}
			if (slot.Yielding)
			{
				slot.Yielding = false;
				s_statusDirty = true;
			}
			bool flag3 = mode != GraphicsConfig.CameraControlMode.On && Near(want, s_default[i]);
			if (flag3 && float.IsNaN(slot.Written))
			{
				return false;
			}
			float num2 = (flag3 ? slot.Game : want);
			float num3 = (flag3 ? float.NaN : want);
			if (!Near(slot.Written, num3) && (!float.IsNaN(slot.Written) || !float.IsNaN(num3)))
			{
				s_statusDirty = true;
			}
			slot.Written = num3;
			if (Near(num, num2))
			{
				return false;
			}
			field = num2;
			return true;
		}

		private static void Yield(int i, float theirs, Slot s)
		{
			s.Theirs = theirs;
			s_statusDirty = true;
			if (!s.Logged)
			{
				s.Logged = true;
				string text = (float.IsNaN(s.Written) ? "" : (", this mod's last value " + Fmt(i, s.Written)));
				Plugin.LogInfo("[Camera] " + s_names[i] + ": another mod set it to " + Fmt(i, theirs) + " (the game's own value is " + Fmt(i, s.Game) + text + "), so Graphics Overdrive leaves it to that mod. Moving the dial applies it once; [7 - Post-FX & Camera] CameraControl = On makes the dials always win, Off keeps this mod away from the camera. " + ((OtherCameraMods.Length > 0) ? ("Plugins that patch GameCamera: " + OtherCameraMods + ".") : "No other plugin's patch on GameCamera was found (it may write the field from its own Update)."));
			}
		}

		private static string Fmt(int i, float v)
		{
			return i switch
			{
				3 => $"{v:0.##}x", 
				0 => $"{v:0.#} degrees", 
				_ => $"{v:0.#} m", 
			};
		}

		internal static bool PresetMayWrite(PresetField field)
		{
			switch ((GraphicsConfig.CameraControl != null) ? GraphicsConfig.CameraControl.Value : GraphicsConfig.CameraControlMode.Auto)
			{
			case GraphicsConfig.CameraControlMode.Off:
				return false;
			case GraphicsConfig.CameraControlMode.On:
				return true;
			default:
				switch (field)
				{
				case PresetField.FieldOfView:
					return !s_slots[0].Yielding;
				case PresetField.Zoom:
					if (!s_slots[1].Yielding)
					{
						return !s_slots[2].Yielding;
					}
					return false;
				default:
					return !s_slots[3].Yielding;
				}
			}
		}

		private static string BuildStatus(GraphicsConfig.CameraControlMode mode)
		{
			string text = mode switch
			{
				GraphicsConfig.CameraControlMode.Off => "Off", 
				GraphicsConfig.CameraControlMode.On => "On", 
				_ => "Auto", 
			};
			if (mode == GraphicsConfig.CameraControlMode.Off)
			{
				return text + ": Graphics Overdrive leaves the camera to the game and other mods";
			}
			if ((Object)(object)s_camera == (Object)null)
			{
				return text + ": waiting for the game camera";
			}
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			for (int i = 0; i < 4; i++)
			{
				if (s_slots[i].Yielding)
				{
					list2.Add(s_names[i].ToLowerInvariant());
				}
				else if (!float.IsNaN(s_slots[i].Written))
				{
					list.Add(s_names[i].ToLowerInvariant());
				}
			}
			string text2 = text + ": ";
			text2 += ((list.Count > 0) ? ("Graphics Overdrive sets the " + string.Join(", ", list)) : "no camera value is set by Graphics Overdrive (the dials sit at the game's own values)");
			if (list2.Count > 0)
			{
				text2 = text2 + "; another mod owns the " + string.Join(", ", list2);
			}
			return text2;
		}

		private static void LogSummary(GameCamera cam, GraphicsConfig.CameraControlMode mode)
		{
			s_summaryLogged = true;
			try
			{
				List<string> list = new List<string>();
				Camera component = ((Component)cam).GetComponent<Camera>();
				Camera[] array = (Camera[])(object)new Camera[Camera.allCamerasCount];
				int allCameras = Camera.GetAllCameras(array);
				for (int i = 0; i < allCameras; i++)
				{
					Camera val = array[i];
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)component) && !((Object)(object)val == (Object)(object)cam.m_skyCamera) && (!((Object)(object)component != (Object)null) || !((Component)val).transform.IsChildOf(((Component)component).transform)))
					{
						list.Add(string.Format("{0} (depth {1:0.#}, {2}{3})", ((Object)val).name, val.depth, ((Object)(object)val.targetTexture != (Object)null) ? ("texture " + ((Texture)val.targetTexture).width + "x" + ((Texture)val.targetTexture).height) : "screen", ((Component)val).CompareTag("MainCamera") ? ", tagged MainCamera" : ""));
					}
				}
				Plugin.LogInfo($"[Camera] Game camera ready: CameraControl = {mode}; dials: field of view {GraphicsConfig.CameraFieldOfView.Value:0.#}, zoom limit {GraphicsConfig.CameraMaxDistance.Value:0.#} m, " + $"shake {GraphicsConfig.CameraShakeScale.Value:0.##}x (the game's own: {s_slots[0].Game:0.#}, {s_slots[1].Game:0.#} m, {s_slots[3].Game:0.##}x; a dial at the game's value is never written). " + ((OtherCameraMods.Length > 0) ? ("Plugins that patch GameCamera: " + OtherCameraMods + ". ") : "No other plugin patches GameCamera. ") + ((list.Count > 0) ? ("Other enabled cameras: " + string.Join("; ", list) + ".") : "No other enabled camera."));
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("[Camera] Could not list the cameras around the game camera: " + ex.Message);
			}
		}

		private static string FindOtherCameraMods()
		{
			try
			{
				SortedSet<string> sortedSet = new SortedSet<string>();
				foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods())
				{
					if (allPatchedMethod.DeclaringType != typeof(GameCamera))
					{
						continue;
					}
					Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod);
					if (patchInfo == null)
					{
						continue;
					}
					foreach (string owner in patchInfo.Owners)
					{
						if (owner != "com.wubarrk.graphicsoverdrive")
						{
							sortedSet.Add(owner);
						}
					}
				}
				PluginInfo value;
				return string.Join(", ", sortedSet.Select((string owner) => (!Chainloader.PluginInfos.TryGetValue(owner, out value)) ? owner : (value.Metadata.Name + " (" + owner + ")")));
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("[Camera] Could not list the plugins that patch GameCamera: " + ex.Message);
				return string.Empty;
			}
		}
	}
	public static class DisplayBasics
	{
		public enum WindowChoice
		{
			Fullscreen,
			Windowed
		}

		public struct Choice
		{
			public int Width;

			public int Height;

			public WindowChoice Window;

			public override string ToString()
			{
				return string.Format("{0} x {1} {2}", Width, Height, (Window == WindowChoice.Fullscreen) ? "fullscreen" : "windowed");
			}
		}

		public interface IDisplayHost
		{
			Resolution[] Modes { get; }

			Resolution Desktop { get; }

			int Width { get; }

			int Height { get; }

			FullScreenMode Mode { get; }

			bool FrameGenOn { get; }

			string BlockedReason { get; }

			float Now { get; }

			void SetResolution(int width, int height, FullScreenMode mode, RefreshRate refresh);

			int GetPref(string key, int fallback);

			void SetPref(string key, int value);

			void SavePrefs();

			bool GetPresent(out int fpsLimit, out bool vsync);

			void SetPresent(int fpsLimit, bool vsync);
		}

		private sealed class UnityHost : IDisplayHost
		{
			public Resolution[] Modes => Screen.resolutions;

			public Resolution Desktop
			{
				get
				{
					//IL_0000: Unknown result type (might be due to invalid IL or missing references)
					//IL_0005: 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)
					Resolution currentResolution = Screen.currentResolution;
					if (((Resolution)(ref currentResolution)).width <= 0 || ((Resolution)(ref currentResolution)).height <= 0)
					{
						((Resolution)(ref currentResolution)).width = Display.main.systemWidth;
						((Resolution)(ref currentResolution)).height = Display.main.systemHeight;
					}
					return currentResolution;
				}
			}

			public int Width => Screen.width;

			public int Height => Screen.height;

			public FullScreenMode Mode => Screen.fullScreenMode;

			public bool FrameGenOn
			{
				get
				{
					if ((!NativeBridge.FgPresenterLoaded && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GO_FG_LAYER_LIB"))) || GraphicsConfig.NativeFrameGeneration == null)
					{
						return false;
					}
					GraphicsConfig.FrameGenChoice value = GraphicsConfig.NativeFrameGeneration.Value;
					if (value != GraphicsConfig.FrameGenChoice.FSR3)
					{
						return value == GraphicsConfig.FrameGenChoice.DLSS;
					}
					return true;
				}
			}

			public float Now => Time.realtimeSinceStartup;

			public string BlockedReason
			{
				get
				{
					if (!GraphicsSettingsManager.CanChangePresentSettings())
					{
						return "This platform does not let the game change its display.";
					}
					if (!((Object)(object)Settings.instance != (Object)null))
					{
						return "";
					}
					return "Close the game's Settings window first: it keeps its own copy of these values.";
				}
			}

			public void SetResolution(int width, int height, FullScreenMode mode, RefreshRate refresh)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				Screen.SetResolution(width, height, mode, refresh);
			}

			public int GetPref(string key, int fallback)
			{
				return PlayerPrefs.GetInt(key, fallback);
			}

			public void SetPref(string key, int value)
			{
				PlayerPrefs.SetInt(key, value);
			}

			public void SavePrefs()
			{
				PlayerPrefs.Save();
			}

			public bool GetPresent(out int fpsLimit, out bool vsync)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				fpsLimit = 361;
				vsync = false;
				GraphicsSettingsManager instance = GraphicsSettingsManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				try
				{
					PresentSettingsState presentSettings = instance.GetCurrentSettingsWithCurrentPresetApplied(false, false).m_presentSettings;
					fpsLimit = presentSettings.m_fpsLimit;
					vsync = presentSettings.m_vsync;
					return true;
				}
				catch (Exception)
				{
					return false;
				}
			}

			public unsafe void SetPresent(int fpsLimit, bool vsync)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				GraphicsSettingsManager instance = GraphicsSettingsManager.Instance;
				GraphicsSettingsState currentPlayerSettingsRaw = Unsafe.Read<GraphicsSettingsState>((void*)instance.CurrentPlayerSettingsRaw);
				currentPlayerSettingsRaw.m_presentSettings.m_fpsLimit = fpsLimit;
				currentPlayerSettingsRaw.m_presentSettings.m_vsync = vsync;
				GraphicsModeConfiguration currentGraphicsModeConfiguration = instance.GetCurrentGraphicsModeConfiguration(false);
				GraphicsSettingsPreset val = currentGraphicsModeConfiguration.GetPresetByID(instance.CurrentPresetID);
				if ((Object)(object)val == (Object)null && !currentGraphicsModeConfiguration.HasCustomPreset)
				{
					val = currentGraphicsModeConfiguration.DefaultPreset;
				}
				if ((Object)(object)val != (Object)null)
				{
					instance.SaveAndApplyGraphicsSettingsWithPreset(ref currentPlayerSettingsRaw, val.m_type);
				}
				else
				{
					instance.SaveAndApplyGraphicsSettingsCustom(ref currentPlayerSettingsRaw);
				}
				PlatformPrefs.Save();
			}
		}

		public const float ConfirmSeconds = 10f;

		public const int FpsMin = 30;

		public const int FpsUnlimited = 361;

		private const int MinWidth = 1280;

		private const int MinHeight = 720;

		private const string PrefWidth = "Screenmanager Resolution Width";

		private const string PrefHeight = "Screenmanager Resolution Height";

		private const string PrefUseNative = "Screenmanager Resolution Use Native";

		private const string PrefFullscreenMode = "Screenmanager Fullscreen mode";

		private const float ListSeconds = 2f;

		private static IDisplayHost s_host = new UnityHost();

		private static Choice s_pending;

		private static bool s_hasPending;

		private static Choice s_applied;

		private static int s_oldW;

		private static int s_oldH;

		private static FullScreenMode s_oldMode;

		private static RefreshRate s_oldRefresh;

		private static float s_deadline;

		private static List<Resolution> s_list;

		private static float s_listAt = -100f;

		private static bool s_listPresenter;

		public static IDisplayHost Host
		{
			get
			{
				return s_host;
			}
			set
			{
				s_host = value ?? new UnityHost();
				Confirming = false;
				s_hasPending = false;
				s_list = null;
				LastResult = "";
			}
		}

		public static string LastResult { get; private set; } = "";

		public static bool Confirming { get; private set; }

		public static float Remaining
		{
			get
			{
				if (!Confirming)
				{
					return 0f;
				}
				return Math.Max(0f, s_deadline - Host.Now);
			}
		}

		private static bool GuardEnabled
		{
			get
			{
				if (GraphicsConfig.FixUnsupportedResolution != null)
				{
					return GraphicsConfig.FixUnsupportedResolution.Value;
				}
				return true;
			}
		}

		public static Choice Current => new Choice
		{
			Width = Host.Width,
			Height = Host.Height,
			Window = (((int)Host.Mode == 3) ? WindowChoice.Windowed : WindowChoice.Fullscreen)
		};

		public static Choice Pending
		{
			get
			{
				if (!s_hasPending)
				{
					return Current;
				}
				return s_pending;
			}
		}

		public static bool HasPendingChange
		{
			get
			{
				if (s_hasPending)
				{
					return !Same(s_pending, Current);
				}
				return false;
			}
		}

		public static int HiddenInFullscreen => AllModes().Count - Offered(WindowChoice.Fullscreen).Count;

		private static bool Same(Choice a, Choice b)
		{
			if (a.Width == b.Width && a.Height == b.Height)
			{
				return a.Window == b.Window;
			}
			return false;
		}

		public static List<Resolution> Offered(WindowChoice window)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			List<Resolution> list = AllModes();
			if (window == WindowChoice.Windowed)
			{
				return list;
			}
			List<Resolution> list2 = new List<Resolution>(list.Count);
			foreach (Resolution item in list)
			{
				Resolution current = item;
				if (Allowed(((Resolution)(ref current)).width, ((Resolution)(ref current)).height, window, out var _))
				{
					list2.Add(current);
				}
			}
			return list2;
		}

		private static List<Resolution> AllModes()
		{
			//IL_005e: 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_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			float now = Host.Now;
			bool frameGenOn = Host.FrameGenOn;
			if (s_list != null && now - s_listAt < 2f && frameGenOn == s_listPresenter)
			{
				return s_list;
			}
			Dictionary<long, Resolution> dictionary = new Dictionary<long, Resolution>();
			Resolution[] array = (Resolution[])(((object)Host.Modes) ?? ((object)new Resolution[0]));
			for (int i = 0; i < array.Length; i++)
			{
				Resolution value = array[i];
				if (((Resolution)(ref value)).width < 1280 || ((Resolution)(ref value)).height < 720)
				{
					continue;
				}
				long key = ((long)((Resolution)(ref value)).width << 32) | (uint)((Resolution)(ref value)).height;
				if (dictionary.TryGetValue(key, out var value2))
				{
					RefreshRate refreshRateRatio = ((Resolution)(ref value)).refreshRateRatio;
					double value3 = ((RefreshRate)(ref refreshRateRatio)).value;
					refreshRateRatio = ((Resolution)(ref value2)).refreshRateRatio;
					if (!(value3 > ((RefreshRate)(ref refreshRateRatio)).value))
					{
						continue;
					}
				}
				dictionary[key] = value;
			}
			List<Resolution> list = new List<Resolution>(dictionary.Values);
			Choice current = Current;
			if (current.Width > 0 && !dictionary.ContainsKey(((long)current.Width << 32) | (uint)current.Height))
			{
				Resolution item = default(Resolution);
				((Resolution)(ref item)).width = current.Width;
				((Resolution)(ref item)).height = current.Height;
				Resolution desktop = Host.Desktop;
				((Resolution)(ref item)).refreshRateRatio = ((Resolution)(ref desktop)).refreshRateRatio;
				list.Add(item);
			}
			list.Sort((Resolution a, Resolution b) => (((Resolution)(ref a)).width == ((Resolution)(ref b)).width) ? ((Resolution)(ref b)).height.CompareTo(((Resolution)(ref a)).height) : ((Resolution)(ref b)).width.CompareTo(((Resolution)(ref a)).width));
			s_list = list;
			s_listAt = now;
			s_listPresenter = frameGenOn;
			return list;
		}

		public static bool Allowed(int width, int height, WindowChoice window, out string why)
		{
			//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)
			why = "";
			if (window == WindowChoice.Windowed || !GuardEnabled || !Host.FrameGenOn)
			{
				return true;
			}
			Resolution desktop = Host.Desktop;
			if (((Resolution)(ref desktop)).width <= 0 || ((Resolution)(ref desktop)).height <= 0 || !ShapeDiffers(width, height, ((Resolution)(ref desktop)).width, ((Resolution)(ref desktop)).height))
			{
				return true;
			}
			why = $"{width} x {height} is another shape than the display ({((Resolution)(ref desktop)).width} x {((Resolution)(ref desktop)).height}): with frame generation on that crashed the game's graphics driver";
			return false;
		}

		private static bool ShapeDiffers(int w, int h, int nw, int nh)
		{
			return Math.Abs((double)w / (double)h - (double)nw / (double)nh) > 0.01;
		}

		public static void SelectResolution(int width, int height)
		{
			if (!Confirming)
			{
				Choice pending = Pending;
				pending.Width = width;
				pending.Height = height;
				s_pending = pending;
				s_hasPending = true;
			}
		}

		public static void SelectWindow(WindowChoice window)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!Confirming)
			{
				Choice pending = Pending;
				pending.Window = window;
				if (!Allowed(pending.Width, pending.Height, window, out var _))
				{
					Resolution desktop = Host.Desktop;
					LastResult = $"Fullscreen cannot use {pending.Width} x {pending.Height} while frame generation is loaded (another shape than the display): " + $"{((Resolution)(ref desktop)).width} x {((Resolution)(ref desktop)).height} picked.";
					pending.Width = ((Resolution)(ref desktop)).width;
					pending.Height = ((Resolution)(ref desktop)).height;
				}
				s_pending = pending;
				s_hasPending = true;
			}
		}

		public static void ClearPending()
		{
			if (!Confirming)
			{
				s_hasPending = false;
			}
		}

		public static bool CanApply(out string why)
		{
			why = Host.BlockedReason;
			if (why.Length > 0)
			{
				return false;
			}
			if (Confirming)
			{
				why = "Answer the question first: keep or revert.";
				return false;
			}
			if (!HasPendingChange)
			{
				why = "Pick a resolution or a window mode first.";
				return false;
			}
			Choice choice = s_pending;
			return Allowed(choice.Width, choice.Height, choice.Window, out why);
		}

		public static bool Apply()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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)
			if (!CanApply(out var why))
			{
				LastResult = why;
				return false;
			}
			Choice choice = s_pending;
			s_oldW = Host.Width;
			s_oldH = Host.Height;
			s_oldMode = Host.Mode;
			Resolution desktop = Host.Desktop;
			s_oldRefresh = ((Resolution)(ref desktop)).refreshRateRatio;
			FullScreenMode mode = (FullScreenMode)((choice.Window != WindowChoice.Windowed) ? 1 : 3);
			try
			{
				Host.SetResolution(choice.Width, choice.Height, mode, RefreshFor(choice.Width, choice.Height));
			}
			catch (Exception ex)
			{
				LastResult = "The game refused the change: " + ex.Message;
				Plugin.LogWarning("[Display] SetResolution failed: " + ex.Message);
				return false;
			}
			s_applied = choice;
			Confirming = true;
			s_deadline = Host.Now + 10f;
			s_list = null;
			LastResult = "";
			Plugin.LogInfo($"[Display] Applied {choice} (was {s_oldW} x {s_oldH} {s_oldMode}): kept or reverted within {10f:0} s.");
			return true;
		}

		private static RefreshRate RefreshFor(int w, int h)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_0094: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Resolution desktop = Host.Desktop;
			RefreshRate refreshRateRatio;
			if (((Resolution)(ref desktop)).width == w && ((Resolution)(ref desktop)).height == h)
			{
				refreshRateRatio = ((Resolution)(ref desktop)).refreshRateRatio;
				if (((RefreshRate)(ref refreshRateRatio)).value > 1.0)
				{
					return ((Resolution)(ref desktop)).refreshRateRatio;
				}
			}
			RefreshRate refreshRateRatio2 = ((Resolution)(ref desktop)).refreshRateRatio;
			double num = -1.0;
			Resolution[] array = (Resolution[])(((object)Host.Modes) ?? ((object)new Resolution[0]));
			for (int i = 0; i < array.Length; i++)
			{
				Resolution val = array[i];
				if (((Resolution)(ref val)).width == w && ((Resolution)(ref val)).height == h)
				{
					refreshRateRatio = ((Resolution)(ref val)).refreshRateRatio;
					if (((RefreshRate)(ref refreshRateRatio)).value > num)
					{
						refreshRateRatio2 = ((Resolution)(ref val)).refreshRateRatio;
						refreshRateRatio = ((Resolution)(ref val)).refreshRateRatio;
						num = ((RefreshRate)(ref refreshRateRatio)).value;
					}
				}
			}
			return refreshRateRatio2;
		}

		public static void Keep()
		{
			if (Confirming)
			{
				Confirming = false;
				Choice choice = s_applied;
				try
				{
					Host.SetPref("Screenmanager Resolution Width", choice.Width);
					Host.SetPref("Screenmanager Resolution Height", choice.Height);
					Host.SetPref("Screenmanager Fullscreen mode", (choice.Window != WindowChoice.Windowed) ? 1 : 3);
					Host.SetPref("Screenmanager Resolution Use Native", 0);
					Host.SavePrefs();
				}
				catch (Exception ex)
				{
					Plugin.LogWarning("[Display] Could not save the screen state: " + ex.Message);
				}
				s_hasPending = false;
				s_list = null;
				LastResult = $"Kept {choice}.";
				Plugin.LogInfo($"[Display] Kept {choice}.");
			}
		}

		public static void Revert(string reason)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (Confirming)
			{
				Confirming = false;
				try
				{
					Host.SetResolution(s_oldW, s_oldH, s_oldMode, s_oldRefresh);
				}
				catch (Exception ex)
				{
					Plugin.LogWarning("[Display] Could not switch back: " + ex.Message);
				}
				s_hasPending = false;
				s_list = null;
				LastResult = $"Reverted to {s_oldW} x {s_oldH} ({reason}).";
				Plugin.LogInfo($"[Display] Reverted to {s_oldW} x {s_oldH} {s_oldMode} ({reason}).");
			}
		}

		public static void Tick()
		{
			if (Confirming && Host.Now >= s_deadline)
			{
				Revert($"no answer in {10f:0} s");
			}
		}

		public static bool TryGetPresent(out int fpsLimit, out bool vsync)
		{
			if (!Host.GetPresent(out var fpsLimit2, out vsync))
			{
				fpsLimit = 361;
				return false;
			}
			fpsLimit = ((fpsLimit2 < 30 || fpsLimit2 > 360) ? 361 : fpsLimit2);
			return true;
		}

		public static void SetFpsLimit(int limit)
		{
			if (TryGetPresent(out var _, out var vsync) && Host.BlockedReason.Length <= 0)
			{
				Present(Mathf.Clamp(limit, 30, 361), vsync);
			}
		}

		public static void SetVsync(bool on)
		{
			if (TryGetPresent(out var fpsLimit, out var _) && Host.BlockedReason.Length <= 0)
			{
				Present(fpsLimit, on);
			}
		}

		private static void Present(int fps, bool vsync)
		{
			try
			{
				Host.SetPresent(fps, vsync);
				LastResult = "";
				Plugin.LogInfo("[Display] FPS limit " + ((fps >= 361) ? "unlimited" : fps.ToString()) + ", Vsync " + (vsync ? "on" : "off") + " saved through the game's graphics settings.");
			}
			catch (Exception ex)
			{
				LastResult = "The game's graphics settings refused the change: " + ex.Message;
				Plugin.LogWarning("[Display] Could not save the FPS limit / Vsync: " + ex.Message);
			}
		}
	}
	public static class DisplayPacing
	{
		private static FieldRef<PresentManager, bool> s_vsyncField;

		private static bool s_vsyncKnown;

		private static bool s_vsyncOn;

		private static string s_logged = "";

		public static int DisplayHz
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//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)
				Resolution currentResolution = Screen.currentResolution;
				RefreshRate refreshRateRatio = ((Resolution)(ref currentResolution)).refreshRateRatio;
				double value = ((RefreshRate)(ref refreshRateRatio)).value;
				if (!(value > 1.0))
				{
					return 60;
				}
				return Mathf.RoundToInt((float)value);
			}
		}

		public static int LimitSetting
		{
			get
			{
				if (GraphicsConfig.FrameGenLimit == null)
				{
					return 0;
				}
				return GraphicsConfig.FrameGenLimit.Value;
			}
		}

		public static int FrameGenTargetFps
		{
			get
			{
				if (LimitSetting <= 0 || VsyncOn)
				{
					return DisplayHz;
				}
				return LimitSetting;
			}
		}

		public static bool Half
		{
			get
			{
				if (GraphicsConfig.HalfRefreshRate != null)
				{
					return GraphicsConfig.HalfRefreshRate.Value;
				}
				return false;
			}
		}

		public static bool FillChosen
		{
			get
			{
				if (GraphicsConfig.FrameGenPacing != null)
				{
					return GraphicsConfig.FrameGenPacing.Value == GraphicsConfig.FrameGenPacingMode.Fill;
				}
				return false;
			}
		}

		public static bool FillActive
		{
			get
			{
				if (NativeUpscaler.FrameGenActive && FillChosen)
				{
					return NativeUpscaler.SelectedFrameGen == 1;
				}
				return false;
			}
		}

		public static bool VsyncOn
		{
			get
			{
				if (!s_vsyncKnown)
				{
					return QualitySettings.vSyncCount > 0;
				}
				return s_vsyncOn;
			}
		}

		public static int RenderCap(bool vsyncOn)
		{
			int num = Math.Max(15, DisplayHz / 2);
			if (NativeUpscaler.FrameGenActive)
			{
				if (vsyncOn)
				{
					return -1;
				}
				int num2 = (FillActive ? FrameGenTargetFps : (FrameGenTargetFps / 2));
				if (Half)
				{
					num2 = Math.Min(num2, num);
				}
				return Math.Max(15, num2);
			}
			if (!Half)
			{
				return -1;
			}
			return num;
		}

		public static void Apply(PresentManager manager, ref int limit)
		{
			if (manager != null)
			{
				if (s_vsyncField == null)
				{
					s_vsyncField = AccessTools.FieldRefAccess<PresentManager, bool>("m_setVSyncEnabled");
				}
				s_vsyncOn = s_vsyncField.Invoke(manager);
				s_vsyncKnown = true;
			}
			int num = RenderCap(VsyncOn);
			if (num > 0)
			{
				limit = ((limit < 0) ? num : Math.Min(limit, num));
			}
			bool frameGenActive = NativeUpscaler.FrameGenActive;
			string text = $"{DisplayHz}|{frameGenActive}|{FillActive}|{FrameGenTargetFps}|{Half}|{VsyncOn}|{num}";
			if (text != s_logged)
			{
				s_logged = text;
				string text2 = ((!frameGenActive) ? "off" : (FillActive ? $"on, fills up to {FrameGenTargetFps} fps shown (real frames first)" : $"on, one frame halfway, up to {FrameGenTargetFps} fps shown"));
				Plugin.LogInfo(string.Format("[Display] display {0} Hz, frame generation {1}, half refresh {2}, Vsync {3}: ", DisplayHz, text2, Half ? "on" : "off", VsyncOn ? "on" : "off") + ((num > 0) ? $"the game renders at most {num} fps" : "no cap from these settings") + " (the game's own limits still apply)");
			}
		}

		public static string StatusText(bool rich)
		{
			int displayHz = DisplayHz;
			int num = RenderCap(VsyncOn);
			int frameGenTargetFps = FrameGenTargetFps;
			int limitSetting = LimitSetting;
			bool frameGenActive = NativeUpscaler.FrameGenActive;
			bool vsyncOn = VsyncOn;
			bool fillActive = FillActive;
			string text = string.Format("Display {0} Hz • Vsync {1}", displayHz, vsyncOn ? "on" : "off");
			text = ((frameGenActive && fillActive) ? (text + $" • frame generation fills up to {frameGenTargetFps} fps shown, real frames first" + ((num > 0) ? $" (the game renders up to {num})" : "")) : ((frameGenActive && vsyncOn) ? (text + $" • frame generation in step with the display: up to {displayHz / 2} rendered + {displayHz / 2} generated") : (frameGenActive ? (text + $" • frame generation limited to {frameGenTargetFps} fps shown: the game renders up to {num}") : ((num <= 0) ? (text + " • no cap") : (text + $" • the game renders up to {num} fps (half refresh)")))));
			string text2 = "";
			if (frameGenActive && !vsyncOn && limitSetting > displayHz)
			{
				text2 = $"The limit is above the display's {displayHz} Hz: the extra frames tear.";
			}
			else if (frameGenActive && !vsyncOn && limitSetting == 0)
			{
				text2 = $"Limit 0 = the display's {displayHz} Hz. For a VRR display set it a few below (e.g. {Math.Max(30, displayHz - 3)}) so frames stay in its range.";
			}
			else if (frameGenActive && !vsyncOn && limitSetting > 0 && limitSetting < displayHz && displayHz % limitSetting != 0 && EvenLimits(displayHz).Length > 0)
			{
				text2 = $"Without VRR, {limitSetting} fps on {displayHz} Hz holds frames for uneven numbers of refreshes (judder): {EvenLimits(displayHz)} keep them even.";
			}
			else if (frameGenActive && vsyncOn && limitSetting > 0)
			{
				text2 = "With the game's Vsync on the limiter is not needed: the display's refresh rate sets the pace.";
			}
			else if (frameGenActive && !fillActive && FillChosen && NativeUpscaler.SelectedFrameGen == 2)
			{
				text2 = "DLSS Frame Generation paces one frame halfway (fill pacing is FSR 3 only).";
			}
			else if (!frameGenActive && Half && !vsyncOn)
			{
				text2 = "With Vsync off this is a frame cap: smooth with VRR, otherwise switch the game's Vsync on for every second refresh.";
			}
			if (text2.Length == 0)
			{
				return text;
			}
			if (!rich)
			{
				return text + "\n" + text2;
			}
			return text + "\n<size=9><color=#9A8A6A>" + text2 + " In menus the game caps at 60 fps.</color></size>";
		}

		private static string EvenLimits(int display)
		{
			List<int> list = new List<int>();
			for (int i = 2; i <= 8; i++)
			{
				if (list.Count >= 2)
				{
					break;
				}
				if (display % i == 0 && display / i >= 30)
				{
					list.Add(display / i);
				}
			}
			return string.Join(" or ", list);
		}
	}
	public static class DisplayResolutionGuard
	{
		private const string PrefWidth = "Screenmanager Resolution Width";

		private const string PrefHeight = "Screenmanager Resolution Height";

		private const string PrefUseNative = "Screenmanager Resolution Use Native";

		private const string PrefFullscreenMode = "Screenmanager Fullscreen mode";

		private const float WatchSeconds = 20f;

		private const float CheckInterval = 0.25f;

		private const int MaxFixes = 3;

		private static int s_badW;

		private static int s_badH;

		private static Resolution s_best;

		private static FullScreenMode s_mode;

		private static string s_reason = "";

		private static float s_watchUntil = -1f;

		private static float s_nextCheck;

		private static int s_fixes;

		public static string LastAction { get; private set; } = "";

		private static bool Enabled
		{
			get
			{
				if (GraphicsConfig.FixUnsupportedResolution != null)
				{
					return GraphicsConfig.FixUnsupportedResolution.Value;
				}
				return true;
			}
		}

		private static bool PresenterLoaded => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GO_FG_LAYER_LIB"));

		private static bool FrameGenOnAtStart
		{
			get
			{
				if (PresenterLoaded && GraphicsConfig.NativeFrameGeneration != null)
				{
					if (GraphicsConfig.NativeFrameGeneration.Value != GraphicsConfig.FrameGenChoice.FSR3)
					{
						return GraphicsConfig.NativeFrameGeneration.Value == GraphicsConfig.FrameGenChoice.DLSS;
					}
					return true;
				}
				return false;
			}
		}

		public static void OnLoad()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_0131: Invalid comparison between Unknown and I4
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = PlayerPrefs.GetInt("Screenmanager Resolution Width", 0);
				int num2 = PlayerPrefs.GetInt("Screenmanager Resolution Height", 0);
				FullScreenMode val = SavedMode();
				bool flag = PlayerPrefs.GetInt("Screenmanager Resolution Use Native", 0) != 0;
				Resolution[] resolutions = Screen.resolutions;
				Resolution currentResolution = Screen.currentResolution;
				string text = (FrameGenOnAtStart ? "on at start" : (PresenterLoaded ? "off (presenter preloaded)" : "off"));
				string text2 = string.Format("saved {0}x{1} {2}{3}, window now {4}x{5} {6}, ", num, num2, val, flag ? " (use native)" : "", Screen.width, Screen.height, Screen.fullScreenMode);
				object[] obj = new object[5]
				{
					((Resolution)(ref currentResolution)).width,
					((Resolution)(ref currentResolution)).height,
					null,
					null,
					null
				};
				RefreshRate refreshRateRatio = ((Resolution)(ref currentResolution)).refreshRateRatio;
				obj[2] = ((RefreshRate)(ref refreshRateRatio)).value;
				obj[3] = text;
				obj[4] = ModeList();
				string text3 = text2 + string.Format("desktop {0}x{1} @ {2:0.##} Hz, frame generation {3}, display modes [{4}]", obj);
				Resolution native;
				string text4 = ((!Enabled) ? "off ([Display] FixUnsupportedResolution = false)" : (((int)val == 3) ? "windowed: any size is fine" : (flag ? "Unity uses the display's own size" : ((num > 0 && num2 > 0) ? ((resolutions == null || resolutions.Length == 0) ? "the display lists no modes: not checked" : ((!IsMode(resolutions, num, num2)) ? Replace(num, num2, val, Largest(resolutions), "not a mode of this display") : ((!FrameGenOnAtStart || !Native(resolutions, out native) || !ShapeDiffers(num, num2, ((Resolution)(ref native)).width, ((Resolution)(ref native)).height)) ? "a mode of this display: kept" : Replace(num, num2, val, native, $"another shape than the display ({((Resolution)(ref native)).width}x{((Resolution)(ref native)).height}) while frame generation is on")))) : "nothing saved"))));
				Plugin.LogInfo("[Display] Startup resolution check: " + text3 + " -> " + text4);
			}
			catch (Exception ex)
			{
				Plugin.LogWarning("[Display] Could not check the saved screen resolution: " + ex.Message);
			}
		}

		private static FullScreenMode SavedMode()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			int num = PlayerPrefs.GetInt("Screenmanager Fullscreen mode", -1);
			if (num < 0 || num > 3)
			{
				return Screen.fullScreenMode;
			}
			return (FullScreenMode)num;
		}

		public static void Tick()
		{
			if (s_watchUntil < 0f)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup > s_watchUntil)
			{
				s_watchUntil = -1f;
			}
			else
			{
				if (realtimeSinceStartup < s_nextCheck)
				{
					return;
				}
				s_nextCheck = realtimeSinceStartup + 0.25f;
				try
				{
					if (Enabled && s_fixes < 3 && Screen.width == s_badW && Screen.height == s_badH)
					{
						Fix("again, Unity still applied the old value");
					}
				}
				catch (Exception ex)
				{
					s_watchUntil = -1f;
					Plugin.LogWarning("[Display] Could not check the screen resolution: " + ex.Message);
				}
			}
		}

		private static string Replace(int w, int h, FullScreenMode mode, Resolution best, string reason)
		{
			//IL_000c: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			s_badW = w;
			s_badH = h;
			s_best = best;
			s_mode = mode;
			s_reason = reason;
			Fix("at start");
			s_watchUntil = Time.realtimeSinceStartup + 20f;
			return $"{reason}: {((Resolution)(ref best)).width}x{((Resolution)(ref best)).height} set";
		}

		private static void Fix(string when)
		{
			//IL_004d: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			s_fixes++;
			PlayerPrefs.SetInt("Screenmanager Resolution Width", ((Resolution)(ref s_best)).width);
			PlayerPrefs.SetInt("Screenmanager Resolution Height", ((Resolution)(ref s_best)).height);
			PlayerPrefs.Save();
			Screen.SetResolution(((Resolution)(ref s_best)).width, ((Resolution)(ref s_best)).height, s_mode, ((Resolution)(ref s_best)).refreshRateRatio);
			object[] obj = new object[6]
			{
				s_badW,
				s_badH,
				s_reason,
				((Resolution)(ref s_best)).width,
				((Resolution)(ref s_best)).height,
				null
			};
			RefreshRate refreshRateRatio = ((Resolution)(ref s_best)).refreshRateRatio;
			obj[5] = ((RefreshRate)(ref refreshRateRatio)).value;
			LastAction = string.Format("The saved resolution {0}x{1} was {2}: switched to {3}x{4} at {5:0} Hz.", obj);
			object[] obj2 = new object[6]
			{
				s_badW,
				s_badH,
				s_reason,
				((Resolution)(ref s_best)).width,
				((Resolution)(ref s_best)).height,
				null
			};
			refreshRateRatio = ((Resolution)(ref s_best)).refreshRateRatio;
			obj2[5] = ((RefreshRate)(ref refreshRateRatio)).value;
			Plugin.LogWarning(string.Format("[Display] Saved screen resolution {0}x{1} is {2}: set {3}x{4} at {5:0.##} Hz ", obj2) + when + " ([Display] FixUnsupportedResolution = false leaves it).");
		}

		public static string FrameGenShapeProblem()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			try
			{
				if (!Enabled || (int)Screen.fullScreenMode == 3)
				{
					return "";
				}
				Resolution[] resolutions = Screen.resolutions;
				if (resolutions == null || resolutions.Length == 0 || !Native(resolutions, out var native) || !ShapeDiffers(Screen.width, Screen.height, ((Resolution)(ref native)).width, ((Resolution)(ref native)).height))
				{
					return "";
				}
				return $"not started: the fullscreen resolution {Screen.width}x{Screen.height} has another shape than the monitor ({((Resolution)(ref native)).width}x{((Resolution)(ref native)).height}), " + $"which crashed the graphics driver with frame generation (Linux, XWayland). Pick {((Resolution)(ref native)).width}x{((Resolution)(ref native)).height} (or windowed) in the graphics " + "settings, then switch frame generation on";
			}
			catch (Exception)
			{
				return "";
			}
		}

		private static bool Native(Resolution[] modes, out Resolution native)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			Resolution val = (native = Screen.currentResolution);
			RefreshRate refreshRateRatio;
			if (((Resolution)(ref val)).width > 0 && ((Resolution)(ref val)).height > 0)
			{
				refreshRateRatio = ((Resolution)(ref val)).refreshRateRatio;
				if (((RefreshRate)(ref refreshRateRatio)).value > 1.0)
				{
					return true;
				}
			}
			int num = ((((Resolution)(ref val)).width > 0) ? ((Resolution)(ref val)).width : Display.main.systemWidth);
			int num2 = ((((Resolution)(ref val)).height > 0) ? ((Resolution)(ref val)).height : Display.main.systemHeight);
			native = default(Resolution);
			bool flag = false;
			for (int i = 0; i < modes.Length; i++)
			{
				Resolution val2 = modes[i];
				if (((Resolution)(ref val2)).width != num || ((Resolution)(ref val2)).height != num2)
				{
					continue;
				}
				if (flag)
				{
					refreshRateRatio = ((Resolution)(ref val2)).refreshRateRatio;
					double value = ((RefreshRate)(ref refreshRateRatio)).value;
					refreshRateRatio = ((Resolution)(ref native)).refreshRateRatio;
					if (!(value > ((RefreshRate)(ref refreshRateRatio)).value))
					{
						continue;
					}
				}
				native = val2;
				flag = true;
			}
			return flag;
		}

		private static bool ShapeDiffers(int w, int h, int nw, int nh)
		{
			return Math.Abs((double)w / (double)h - (double)nw / (double)nh) > 0.01;
		}

		private static bool IsMode(Resolution[] modes, int w, int h)
		{
			//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)
			for (int i = 0; i < modes.Length; i++)
			{
				Resolution val = modes[i];
				if (((Resolution)(ref val)).width == w && ((Resolution)(ref val)).height == h)
				{
					return true;
				}
			}
			return false;
		}

		private static Resolution Largest(Resolution[] modes)
		{
			//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)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Resolution result = modes[0];
			for (int i = 1; i < modes.Length; i++)
			{
				Resolution val = modes[i];
				long num = (long)((Resolution)(ref result)).width * (long)((Resolution)(ref result)).height;
				long num2 = (long)((Resolution)(ref val)).width * (long)((Resolution)(ref val)).height;
				if (num2 <= num)
				{
					if (num2 != num)
					{
						continue;
					}
					RefreshRate refreshRateRatio = ((Resolution)(ref val)).refreshRateRatio;
					double value = ((RefreshRate)(ref refreshRateRatio)).value;
					refreshRateRatio = ((Resolution)(ref result)).refreshRateRatio;
					if (!(value > ((RefreshRate)(ref refreshRateRatio)).value))
					{
						continue;
					}
				}
				result = val;
			}
			return result;
		}

		private static string ModeList()
		{
			//IL_0012: 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)
			List<string> list = new List<string>();
			Resolution[] resolutions = Screen.resolutions;
			for (int i = 0; i < resolutions.Length; i++)
			{
				Resolution val = resolutions[i];
				string item = ((Resolution)(ref val)).width + "x" + ((Resolution)(ref val)).height;
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			return string.Join(", ", list);
		}
	}
	public class FpsCounterHUD : MonoBehaviour
	{
		private const int SampleCapacity = 1000;

		private const int AverageWindow = 60;

		private readonly float[] m_frameDeltas = new float[1000];

		private readonly float[] m_sortedDeltas = new float[1000];

		private int m_sampleCount;

		private int m_sampleIndex;

		private float m_updateTimer;

		private const float UpdateInterval = 0.25f;

		private const float MaxSampleSeconds = 1f;

		private float m_displayFps;

		private float m_displayMs;

		private float m_display1PercentLow;

		private float m_display01PercentLow;

		private long m_displayTotalMemMb;

		private string m_fpsLine = "";

		private string m_realLine = "";

		private float m_colorFps;

		private string m_lowsLine = "";

		private string m_memLine = "";

		private string m_vramLine = "";

		private bool m_rebuildRequested = true;

		private bool m_wasActive;

		private GUIStyle m_fpsStyle;

		private GUIStyle m_subStyle;

		private void Awake()
		{
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			GraphicsConfig.OnSettingsChanged += RequestRebuild;
		}

		private void OnDestroy()
		{
			GraphicsConfig.OnSettingsChanged -= RequestRebuild;
		}

		private void RequestRebuild()
		{
			m_rebuildRequested = true;
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(GraphicsConfig.FpsHudToggleKey.Value))
			{
				GraphicsConfig.EnableFpsHud.Value = !GraphicsConfig.EnableFpsHud.Value;
			}
			if (!GraphicsConfig.EnableFpsHud.Value || !GraphicsConfig.MasterEnable.Value)
			{
				m_wasActive = false;
				return;
			}
			if (!m_wasActive)
			{
				m_wasActive = true;
				m_sampleCount = 0;
				m_sampleIndex = 0;
				m_updateTimer = 0f;
				m_rebuildRequested = true;
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (unscaledDeltaTime < 1f)
			{
				m_frameDeltas[m_sampleIndex] = unscaledDeltaTime;
				m_sampleIndex = (m_sampleIndex + 1) % 1000;
				if (m_sampleCount < 1000)
				{
					m_sampleCount++;
				}
			}
			m_updateTimer += unscaledDeltaTime;
			if (m_updateTimer >= 0.25f || m_rebuildRequested)
			{
				m_updateTimer = 0f;
				m_rebuildRequested = false;
				CalculateMetrics();
				BuildLines();
			}
		}

		private void CalculateMetrics()
		{
			if (m_sampleCount != 0)
			{
				for (int i = 0; i < m_sampleCount; i++)
				{
					m_sortedDeltas[i] = m_frameDeltas[i];
				}
				int num = Mathf.Min(m_sampleCount, 60);
				float num2 = 0f;
				for (int j = 1; j <= num; j++)
				{
					num2 += m_frameDeltas[(m_sampleIndex - j + 1000) % 1000];
				}
				float num3 = num2 / (float)num;
				m_displayFps = ((num3 > 0f) ? (1f / num3) : 0f);
				m_displayMs = num3 * 1000f;
				Array.Sort(m_sortedDeltas, 0, m_sampleCount);
				int num4 = Mathf.Clamp(Mathf.FloorToInt((float)m_sampleCount * 0.99f), 0, m_sampleCount - 1);
				float num5 = m_sortedDeltas[num4];
				m_display1PercentLow = ((num5 > 0f) ? (1f / num5) : 0f);
				int num6 = Mathf.Clamp(Mathf.FloorToInt((float)m_sampleCount * 0.999f), 0, m_sampleCount - 1);
				float num7 = m_sortedDeltas[num6];
				m_display01PercentLow = ((num7 > 0f) ? (1f / num7) : 0f);
				m_displayTotalMemMb = Profiler.GetTotalAllocatedMemoryLong() / 1048576;
			}
		}

		private void BuildLines()
		{
			int num = Mathf.RoundToInt(m_displayFps);
			float num2 = (NativeUpscaler.FrameGenActive ? NativeUpscaler.FrameGenGeneratedFps : 0f);
			if (num2 > 0.5f)
			{
				m_colorFps = m_displayFps + num2;
				m_fpsLine = $"{Mathf.RoundToInt(m_colorFps)} FPS  <size=13><color=#C8A865>with frame gen</color></size>";
				m_realLine = (GraphicsConfig.FpsHudShowFrameTime.Value ? $"Real: <color=#F0E6D2>{num} FPS</color> ({m_displayMs:F1} ms)  |  generated <color=#F0E6D2>{Mathf.RoundToInt(num2)}</color>" : $"Real: <color=#F0E6D2>{num} FPS</color>  |  generated <color=#F0E6D2>{Mathf.RoundToInt(num2)}</color>");
			}
			else
			{
				m_colorFps = m_displayFps;
				m_fpsLine = (GraphicsConfig.FpsHudShowFrameTime.Value ? $"{num} FPS  <size=13><color=#C8A865>({m_displayMs:F1} ms)</color></size>" : $"{num} FPS");
				m_realLine = "";
			}
			m_lowsLine = $"1% Low: <color=#F5D058>{Mathf.RoundToInt(m_display1PercentLow)}</color>  |  0.1%: <color=#F87171>{Mathf.RoundToInt(m_display01PercentLow)}</color>";
			string arg = (NativeUpscaler.IsActive ? string.Format("{0} {1} {2}x{3}", (NativeUpscaler.SelectedBackend == 2) ? "DLSS" : "FSR 3", NativeUpscaler.ModeLabel, NativeUpscaler.RenderWidth, NativeUpscaler.RenderHeight) : (Fsr1Scaler.IsActive ? $"FSR 1 {Fsr1Scaler.RenderWidth}x{Fsr1Scaler.RenderHeight}" : $"{GraphicsConfig.CustomRenderScalePercent.Value}%"));
			m_memLine = $"Mem: <color=#F0E6D2>{m_displayTotalMemMb}MB</color>  |  Scale: <color=#F0E6D2>{arg}</color>";
			if (GraphicsConfig.FpsHudShowVram.Value)
			{
				VramTracker.VramProjection projection = VramTracker.GetProjection();
				string statusColorHex = VramTracker.GetStatusColorHex(projection.HealthStatus);
				if (projection.DetectedGpuDriverMB > 0f)
				{
					m_vramLine = $"VRAM: <color={statusColorHex}>{projection.DetectedGpuDriverGB:F1}G</color> / {VramTracker.TotalVramGB:F1}G  [est. <color={statusColorHex}>{projection.ProjectedTotalGB:F1}G</color>]";
				}
				else
				{
					m_vramLine = $"VRAM est.: <color={statusColorHex}>{projection.ProjectedTotalGB:F1}G</color> / {VramTracker.TotalVramGB:F1}G (<color={statusColorHex}>{projection.ProjectedUsagePercent:F0}%</color>)";
				}
			}
		}

		private void InitStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			float value = GraphicsConfig.FpsHudTextScale.Value;
			if (m_fpsStyle == null)
			{
				m_fpsStyle = new GUIStyle();
				m_fpsStyle.fontStyle = (FontStyle)1;
				m_fpsStyle.alignment = (TextAnchor)3;
				m_fpsStyle.richText = true;
			}
			if (m_subStyle == null)
			{
				m_subStyle = new GUIStyle();
				m_subStyle.normal.textColor = new Color(0.78f, 0.66f, 0.4f, 0.95f);
				m_subStyle.alignment = (TextAnchor)3;
				m_subStyle.richText = true;
			}
			m_fpsStyle.fontSize = Mathf.RoundToInt(20f * value);
			m_subStyle.fontSize = Mathf.RoundToInt(12f * value);
		}

		private void OnGUI()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			if (GraphicsConfig.EnableFpsHud.Value && GraphicsConfig.MasterEnable.Value)
			{
				InitStyles();
				Color textColor = default(Color);
				if (m_colorFps >= 59.5f)
				{
					((Color)(ref textColor))..ctor(0.32f, 0.88f, 0.55f);
				}
				else if (m_colorFps >= 29.5f)
				{
					((Color)(ref textColor))..ctor(0.96f, 0.82f, 0.35f);
				}
				else
				{
					((Color)(ref textColor))..ctor(0.95f, 0.4f, 0.4f);
				}
				m_fpsStyle.normal.textColor = textColor;
				float num = 250f * GraphicsConfig.FpsHudTextScale.Value;
				float num2 = 40f * GraphicsConfig.FpsHudTextScale.Value;
				if (m_realLine.Length > 0)
				{
					num2 += 20f * GraphicsConfig.FpsHudTextScale.Value;
				}
				if (GraphicsConfig.FpsHudShowLows.Value)
				{
					num2 += 20f * GraphicsConfig.FpsHudTextScale.Value;
				}
				if (GraphicsConfig.FpsHudShowMemory.Value)
				{
					num2 += 20f * GraphicsConfig.FpsHudTextScale.Value;
				}
				if (GraphicsConfig.FpsHudShowVram.Value)
				{
					num2 += 20f * GraphicsConfig.FpsHudTextScale.Value;
				}
				float num3 = 15f;
				float num4 = 15f;
				switch (GraphicsConfig.FpsHudPosition.Value)
				{
				case GraphicsConfig.HudCorner.TopRight:
					num3 = (float)Screen.width - num - 15f;
					num4 = 15f;
					break;
				case GraphicsConfig.HudCorner.TopLeft:
					num3 = 15f;
					num4 = 15f;
					break;
				case GraphicsConfig.HudCorner.BottomRight:
					num3 = (float)Screen.width - num - 15f;
					num4 = (float)Screen.height - num2 - 15f;
					break;
				case GraphicsConfig.HudCorner.BottomLeft:
					num3 = 15f;
					num4 = (float)Screen.height - num2 - 15f;
					break;
				}
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(num3, num4, num, num2);
				Color color = GUI.color;
				float num5 = Mathf.Clamp(GraphicsConfig.FpsHudBackgroundAlpha.Value, 0.05f, 1f);
				GUI.color = new Color(0.02f, 0.02f, 0.04f, num5);
				GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
				GUI.color = new Color(0.85f, 0.68f, 0.28f, 0.7f);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 1f), (Texture)(object)Texture2D.whiteTexture);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + ((Rect)(ref val)).height - 1f, ((Rect)(ref val)).width, 1f), (Texture)(object)Texture2D.whiteTexture);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 1f, 1f, ((Rect)(ref val)).height - 2f), (Texture)(object)Texture2D.whiteTexture);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x + ((Rect)(ref val)).width - 1f, ((Rect)(ref val)).y + 1f, 1f, ((Rect)(ref val)).height - 2f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = color;
				GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 8f, ((Rect)(ref val)).y + 5f, ((Rect)(ref val)).width - 16f, ((Rect)(ref val)).height - 8f));
				GUILayout.Label(m_fpsLine, m_fpsStyle, Array.Empty<GUILayoutOption>());
				if (m_realLine.Length > 0)
				{
					GUILayout.Label(m_realLine, m_subStyle, Array.Empty<GUILayoutOption>());
				}
				if (GraphicsConfig.FpsHudShowLows.Value)
				{
					GUILayout.Label(m_lowsLine, m_subStyle, Array.Empty<GUILayoutOption>());
				}
				if (GraphicsConfig.FpsHudShowMemory.Value)
				{
					GUILayout.Label(m_memLine, m_subStyle, Array.Empty<GUILayoutOption>());
				}
				if (GraphicsConfig.FpsHudShowVram.Value)
				{
					GUILayout.Label(m_vramLine, m_subStyle, Array.Empty<GUILayoutOption>());
				}
				GUILayout.EndArea();
			}
		}
	}
	public static class Fsr1Scaler
	{
		public enum Pick
		{
			Vanilla,
			Fsr1,
			Fsr3,
			Dlss
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static CameraCallback <0>__OnPreCull;
		}

		private static readonly int SrcSizeId = Shader.PropertyToID("_GoSrcSize");

		private static readonly int ParamsId = Shader.PropertyToID("_GoParams");

		private static UpscaledFrameBuffer s_ufb;

		private static Camera s_wantCam;

		private static Camera s_cam;

		private static CommandBuffer s_cb;

		private static bool s_attached;

		private static bool s_hooked;

		private static bool s_failed;

		private static RenderTexture s_mid;

		private static float s_nextCameraSearch;

		public static bool IsActive { get; private set; }

		public static string StatusLine { get; private set; } = "";

		public static int RenderWidth { get; private set; }

		public static int RenderHeight { get; private set; }

		public static int OutputWidth { get; private set; }

		public static int OutputHeight { get; private set; }

		public static bool Sharpening { get; private set; }

		public static RenderTexture Upscaled => s_mid;

		public static bool Chosen
		{
			get
			{
				if (GraphicsConfig.UpscaleFilter != null && GraphicsConfig.UpscaleFilter.Value == GraphicsConfig.GameScalerFilter.FSR1)
				{
					return NativeUpscaler.SelectedBackend == 0;
				}
				return false;
			}
		}

		public static bool Available
		{
			get
			{
				if ((Object)(object)DepthCopy.Fsr1EasuMaterial != (Object)null)
				{
					return (Object)(object)DepthCopy.Fsr1RcasMaterial != (Object)null;
				}
				return false;
			}
		}

		public static string Why => DepthCopy.Fsr1Why;

		public static Pick Picked
		{
			get
			{
				switch (NativeUpscaler.SelectedBackend)
				{
				case 1:
					return Pick.Fsr3;
				case 2:
					return Pick.Dlss;
				default:
					if (!Chosen)
					{
						return Pick.Vanilla;
					}
					return Pick.Fsr1;
				}
			}
		}

		public static void Choose(Pick pick)
		{
			switch (pick)
			{
			case Pick.Fsr3:
				if (GraphicsConfig.Scaler != null)
				{
					GraphicsConfig.Scaler.Value = GraphicsConfig.ScalerChoice.FSR3;
				}
				return;
			case Pick.Dlss:
				if (GraphicsConfig.Scaler != null)
				{
					GraphicsConfig.Scaler.Value = GraphicsConfig.ScalerChoice.DLSS;
				}
				return;
			}
			if (GraphicsConfig.Scaler != null)
			{
				GraphicsConfig.Scaler.Value = GraphicsConfig.ScalerChoice.Vanilla;
			}
			GraphicsConfig.UpscaleFilter.Value = ((pick == Pick.Fsr1) ? GraphicsConfig.GameScalerFilter.FSR1 : GraphicsConfig.GameScalerFilter.Game);
		}

		public static void Tick()
		{
			if (s_failed || GraphicsConfig.UpscaleFilter == null)
			{
				return;
			}
			try
			{
				RenderTexture val = null;
				string why;
				bool flag = Wanted(out why);
				if (flag && !FindCamera())
				{
					flag = false;
					why = "FSR 1 waits for the game camera";
				}
				if (flag)
				{
					val = GameFrameBuffer.RenderTextureOf(s_ufb);
					if ((Object)(object)val == (Object)null || ((Texture)val).width >= Screen.width || ((Texture)val).height >= Screen.height)
					{
						flag = false;
						why = "FSR 1 waits for a 3D render scale below 100 (it only scales up)";
					}
				}
				if (flag)
				{
					Apply(val);
					return;
				}
				Release();
				StatusLine = why;
			}
			catch (Exception ex)
			{
				s_failed = true;
				try
				{
					Release();
				}
				catch (Exception)
				{
				}
				StatusLine = "FSR 1 failed: " + ex.Message;
				Plugin.LogWarning("[FSR 1] " + ex?.ToString() + " - the game's own stretch takes over again.");
			}
		}

		public static void Shutdown()
		{
			try
			{
				Release();
			}
			catch (Exception)
			{
			}
		}

		private static bool Wanted(out string why)
		{
			why = "";
			if (!Chosen)
			{
				return false;
			}
			if (!GraphicsConfig.MasterEnable.Value)
			{
				why = "Graphics Overdrive is switched off";
				return false;
			}
			if (!GameFrameBuffer.Available)
			{
				why = "the game's 3D-resolution code has changed";
				return false;
			}
			if (NativeUpscaler.IsActive)
			{
				why = "FSR 1 waits for the other scaler to stop";
				return false;
			}
			if (!Available)
			{
				why = Why;
				return false;
			}
			return true;
		}

		private static bool FindCamera()
		{
			if ((Object)(object)s_ufb == (Object)null && Time.unscaledTime >= s_nextCameraSearch)
			{
				s_nextCameraSearch = Time.unscaledTime + 1f;
				s_ufb = Object.FindAnyObjectByType<UpscaledFrameBuffer>();
			}
			if ((Object)(object)s_ufb == (Object)null)
			{
				return false;
			}
			Camera val = GameFrameBuffer.CameraOf(s_ufb);
			s_wantCam = (((Object)(object)val != (Object)null) ? val : ((Component)s_ufb).GetComponent<Camera>());
			return (Object)(object)s_wantCam != (Object)null;
		}

		private static void SetSource(Material easu, int width, int height, bool sharpen)
		{
			//IL_001a: 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_0030: Invalid comparison between Unknown and I4
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			easu.SetVector(SrcSizeId, new Vector4((float)width, (float)height, 1f / (float)width, 1f / (float)height));
			easu.SetVector(ParamsId, new Vector4(((int)QualitySettings.activeColorSpace == 1) ? 1f : 0f, sharpen ? 1f : 0f, sharpen ? 0f : 1f, 0f));
		}

		private static void Apply(RenderTexture rt)
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Invalid comparison between Unknown and I4
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			Material fsr1EasuMaterial = DepthCopy.Fsr1EasuMaterial;
			Material fsr1RcasMaterial = DepthCopy.Fsr1RcasMaterial;
			float num = Mathf.Clamp01(GraphicsConfig.Fsr1Sharpness.Value);
			bool flag = num > 0.0005f;
			int width = Screen.width;
			int height = Screen.height;
			SetSource(fsr1EasuMaterial, ((Texture)rt).width, ((Texture)rt).height, flag);
			if (flag)
			{
				if ((Object)(object)s_mid == (Object)null || ((Texture)s_mid).width != width || ((Texture)s_mid).height != height)
				{
					ReleaseMid();
					RenderTextureFormat val = (RenderTextureFormat)(SystemInfo.SupportsRenderTextureFormat((RenderTextureFormat)8) ? 8 : 0);
					s_mid = new RenderTexture(width, height, 0, val, (RenderTextureReadWrite)1)
					{
						name = "GO FSR 1 upscaled",
						filterMode = (FilterMode)0,
						wrapMode = (TextureWrapMode)1,
						useMipMap = false,
						hideFlags = (HideFlags)61
					};
					s_mid.Create();
				}
				fsr1RcasMaterial.SetVector(SrcSizeId, new Vector4((float)width, (float)height, 1f / (float)width, 1f / (float)height));
				fsr1RcasMaterial.SetVector(ParamsId, new Vector4(((int)QualitySettings.activeColorSpace == 1) ? 1f : 0f, Mathf.Pow(2f, -2f * (1f - num)), 1f, 0f));
			}
			else
			{
				ReleaseMid();
			}
			if ((Object)(object)s_cam != (Object)(object)s_wantCam)
			{
				Detach();
				s_cam = s_wantCam;
			}
			if (s_cb == null)
			{
				s_cb = new CommandBuffer
				{
					name = "GO FSR 1 upscale"
				};
			}
			if (!s_attached)
			{
				s_cam.AddCommandBuffer((CameraEvent)20, s_cb);
				s_attached = true;
			}
			if (!s_hooked)
			{
				CameraCallback onPreCull = Camera.onPreCull;
				object obj = <>O.<0>__OnPreCull;
				if (obj == null)
				{
					CameraCallback val2 = OnPreCull;
					<>O.<0>__OnPreCull = val2;
					obj = (object)val2;
				}
				Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)onPreCull, (Delegate?)obj);
				s_hooked = true;
			}
			bool isActive = IsActive;
			IsActive = true;
			Sharpening = flag;
			RenderWidth = ((Texture)rt).width;
			RenderHeight = ((Texture)rt).height;
			OutputWidth = width;
			OutputHeight = height;
			foreach (FrameBufferScaler item in GameFrameBuffer.Subscribers(s_ufb))
			{
				Show(item, rt);
			}
			StatusLine = $"{((Texture)rt).width}x{((Texture)rt).height} to {width}x{height}" + (flag ? "" : ", sharpening off");
			if (!isActive)
			{
				Plugin.LogInfo($"[FSR 1] on: {StatusLine} ({SystemInfo.graphicsDeviceType})");
			}
		}

		private static void Show(FrameBufferScaler scaler, RenderTexture rt)
		{
			RawImage val = (((Object)(object)scaler != (Object)null) ? GameFrameBuffer.RawImageOf(scaler) : null);
			if (!((Object)(object)val == (Object)null))
			{
				Texture val2 = (Texture)(object)(Sharpening ? s_mid : rt);
				Material val3 = (Sharpening ? DepthCopy.Fsr1RcasMaterial : DepthCopy.Fsr1EasuMaterial);
				if (val.texture != val2)
				{
					val.texture = val2;
				}
				if (((Graphic)val).material != val3)
				{
					((Graphic)val).material = val3;
				}
			}
		}

		public static void OnScalerBufferCreated(FrameBufferScaler scaler)
		{
			if (!IsActive || (Object)(object)s_ufb == (Object)null || (Object)(object)scaler == (Object)null)
			{
				return;
			}
			RenderTexture val = GameFrameBuffer.RenderTextureOf(s_ufb);
			if ((Object)(object)val == (Object)null || ((Texture)val).width >= Screen.width || ((Texture)val).height >= Screen.height)
			{
				RawImage val2 = GameFrameBuffer.RawImageOf(scaler);
				if ((Object)(object)val2 != (Object)null && Ours(((Graphic)val2).material))
				{
					((Graphic)val2).material = null;
				}
			}
			else
			{
				SetSource(DepthCopy.Fsr1EasuMaterial, ((Texture)val).width, ((Texture)val).height, Sharpening);
				RenderWidth = ((Texture)val).width;
				RenderHeight = ((Texture)val).height;
				Show(scaler, val);
			}
		}

		private static void OnPreCull(Camera cam)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (s_cb == null || (Object)(object)cam == (Object)null || (Object)(object)cam != (Object)(object)s_cam)
			{
				return;
			}
			s_cb.Clear();
			if (IsActive && Sharpening && !((Object)(object)s_mid == (Object)null))
			{
				RenderTexture targetTexture = cam.targetTexture;
				Material fsr1EasuMaterial = DepthCopy.Fsr1EasuMaterial;
				if (!((Object)(object)targetTexture == (Object)null) && !((Object)(object)fsr1EasuMaterial == (Object)null))
				{
					SetSource(fsr1EasuMaterial, ((Texture)targetTexture).width, ((Texture)targetTexture).height, sharpen: true);
					s_cb.Blit((Texture)(object)targetTexture, RenderTargetIdentifier.op_Implicit((Texture)(object)s_mid), fsr1EasuMaterial);
				}
			}
		}

		private static bool Ours(Material material)
		{
			if ((Object)(object)material != (Object)null)
			{
				if (material != DepthCopy.Fsr1EasuMaterial)
				{
					return material == DepthCopy.Fsr1RcasMaterial;
				}
				return true;
			}
			return false;
		}

		private static void Detach()
		{
			if (s_attached && (Object)(object)s_cam != (Object)null && s_cb != null)
			{
				s_cam.RemoveCommandBuffer((CameraEvent)20, s_cb);
			}
			s_attached = false;
			s_cam = null;
		}

		private static void ReleaseMid()
		{
			if (!((Object)(object)s_mid == (Object)null))
			{
				s_mid.Release();
				Object.Destroy((Object)(object)s_mid);
				s_mid = null;
			}
		}

		private static void Release()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//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_002c: Expected O, but got Unknown
			Detach();
			if (s_hooked)
			{
				CameraCallback onPreCull = Camera.onPreCull;
				object obj = <>O.<0>__OnPreCull;
				if (obj == null)
				{
					CameraCallback val = OnPreCull;
					<>O.<0>__OnPreCull = val;
					obj = (object)val;
				}
				Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)onPreCull, (Delegate?)obj);
				s_hooked = false;
			}
			if (s_cb != null)
			{
				s_cb.Clear();
			}
			if (IsActive && (Object)(object)s_ufb != (Object)null)
			{
				RenderTexture texture = GameFrameBuffer.RenderTextureOf(s_ufb);
				foreach (FrameBufferScaler item in GameFrameBuffer.Subscribers(s_ufb))
				{
					RawImage val2 = (((Object)(object)item != (Object)null) ? GameFrameBuffer.RawImageOf(item) : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						if (Ours(((Graphic)val2).material))
						{
							((Graphic)val2).material = null;
						}
						if ((Object)(object)s_mid != (Object)null && (object)val2.texture == s_mid)
						{
							val2.texture = (Texture)(object)texture;
						}
					}
				}
			}
			ReleaseMid();
			if (IsActive)
			{
				Plugin.LogInfo("[FSR 1] off");
			}
			IsActive = false;
			Sharpening = false;
		}
	}
	internal static class GameFrameBuffer
	{
		private static FieldRef<uint> s_targetVertical;

		private static FieldRef<bool> s_autoTarget;

		private static FieldRef<UpscalingAlgorithm> s_algorithm;

		private static FieldRef<UpscaledFrameBuffer, Camera> s_camera;

		private static FieldRef<UpscaledFrameBuffer, RenderTexture> s_renderTexture;

		private static FieldRef<UpscaledFrameBuffer, List<FrameBufferScaler>> s_subscribers;

		private static FieldRef<UpscaledFrameBuffer, bool> s_scaledRendering;

		private static FieldRef<FrameBufferScaler, RawImage> s_rawImage;

		private static Action<UpscaledFrameBuffer> s_createClearCamera;

		private static Action<UpscaledFrameBuffer> s_reassignTexture;

		private static Action<UpscaledFrameBuffer> s_releaseTexture;

		private static Action<UpscaledFrameBuffer> s_destroyClearCamera;

		private static bool s_resolved;

		private static string s_missing = "";

		internal static bool Available
		{
			get
			{
				Resolve();
				return s_missing.Length == 0;
			}
		}

		internal static uint TargetVertical
		{
			get
			{
				return s_targetVertical.Invoke();
			}
			set
			{
				s_targetVertical.Invoke() = value;
			}
		}

		internal static bool AutoTarget
		{
			get
			{
				return s_autoTarget.Invoke();
			}
			set
			{
				s_autoTarget.Invoke() = value;
			}
		}

		internal static UpscalingAlgorithm Algorithm
		{
			get
			{
				return s_algorithm.Invoke();
			}
			set
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Expected I4, but got Unknown
				s_algorithm.Invoke() = (UpscalingAlgorithm)(int)value;
			}
		}

		internal static ref Camera CameraOf(UpscaledFrameBuffer ufb)
		{
			return ref s_camera.Invoke(ufb);
		}

		internal static Camera ControllerCamera()
		{
			GameCamera instance = GameCamera.instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return null;
			}
			return ((Component)instance).GetComponent<Camera>();
		}

		internal static RenderTexture RenderTextureOf(UpscaledFrameBuffer ufb)
		{
			return s_renderTexture.Invoke(ufb);
		}

		internal static List<FrameBufferScaler> Subscribers(UpscaledFrameBuffer ufb)
		{
			return s_subscribers.Invoke(ufb);
		}

		internal static ref bool UsingScaledRendering(UpscaledFrameBuffer ufb)
		{
			return ref s_scaledRendering.Invoke(ufb);
		}

		internal static RawImage RawImageOf(FrameBufferScaler scaler)
		{
			return s_rawImage.Invoke(scaler);
		}

		internal static void CreateClearCamera(UpscaledFrameBuffer ufb)
		{
			s_createClearCamera(ufb);
		}

		internal static void ReassignTextureIfNeeded(UpscaledFrameBuffer ufb)
		{
			s_reassignTexture(ufb);
		}

		internal static void ReleaseTextureIfExists(UpscaledFrameBuffer ufb)
		{
			s_releaseTexture(ufb);
		}

		internal static void DestroyClearCameraIfExists(UpscaledFrameBuffer ufb)
		{
			s_destroyClearCamera(ufb);
		}

		private static void Resolve()
		{
			if (!s_resolved)
			{
				s_resolved = true;
				List<string> list = new List<string>();
				s_targetVertical = Static<uint>("m_targetResolutionVertical", list);
				s_autoTarget = Static<bool>("m_autoTargetResolution", list);
				s_algorithm = Static<UpscalingAlgorithm>("m_upscalingAlgorithm", list);
				s_camera = Instance<UpscaledFrameBuffer, Camera>("m_camera", list);
				s_renderTexture = Instance<UpscaledFrameBuffer, RenderTexture>("m_renderTexture", list);
				s_subscribers = Instance<UpscaledFrameBuffer, List<FrameBufferScaler>>("m_subscribers", list);
				s_scaledRendering = Instance<UpscaledFrameBuffer, bool>("m_isUsingScaledRendering", list);
				s_rawImage = Instance<FrameBufferScaler, RawImage>("m_rawImage", list);
				s_createClearCamera = Method("CreateClearCamera", list);
				s_reassignTexture = Method("ReassignTextureIfNeeded", list);
				s_releaseTexture = Method("ReleaseTextureIfExists", list);
				s_destroyClearCamera = Method("DestroyClearCameraIfExists", list);
				s_missing = string.Join(", ", list.ToArray());
				if (list.Count > 0)
				{
					Plugin.LogWarning("[RenderScale] The game's 3D-resolution code has changed (not found: " + s_missing + "). The render scale dial, FSR 3 / DLSS and frame generation stay off; the game's own 3D resolution setting still works.");
				}
			}
		}

		internal static void ApplyHooks(Harmony harmony)
		{
			Resolve();
			Hook(harmony, typeof(UpscaledFrameBuffer), "UpdateCameraTarget", Type.EmptyTypes, AccessTools.Method(typeof(WorldObjectPatches.Patch_UpscaledFrameBuffer_UpdateCameraTarget), "Prefix", (Type[])null, (Type[])null), null);
			Hook(harmony, typeof(FrameBufferScaler), "OnBufferCreated", new Type[2]
			{
				typeof(UpscaledFrameBuffer),
				typeof(RenderTexture)
			}, null, AccessTools.Method(typeof(NativeScalerPatches.Patch_FrameBufferScaler_OnBufferCreated), "Postfix", (Type[])null, (Type[])null));
		}

		private static void Hook(Harmony harmony, Type type, string name, Type[] parameters, MethodInfo prefix, MethodInfo postfix)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			string text = type.Name + "." + name + "()";
			try
			{
				MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null);
				if (method != null)
				{
					harmony.Patch((MethodBase)method, (!(prefix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix), (!(postfix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					return;
				}
			}
			catch (Exception ex)
			{
				text = text + " (" + ex.GetType().Name + ": " + ex.Message + ")";
			}
			s_missing = ((s_missing.Length == 0) ? text : (s_missing + ", " + text));
			Plugin.LogWarning("[RenderScale] The game's 3D-resolution code has changed (hook not applied: " + text + "). The render scale dial, FSR 3 / DLSS and frame generation stay off; the game's own 3D resolution setting still works.");
		}

		private static FieldRef<T> Static<T>(string name, List<string> missing)
		{
			try
			{
				FieldInfo field = typeof(UpscaledFrameBuffer).GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(T))
				{
					return AccessTools.StaticFieldRefAccess<T>(field);
				}
			}
			catch (Exception)
			{
			}
			missing.Add("UpscaledFrameBuffer." + name);
			return null;
		}

		private static FieldRef<TOwner, T> Instance<TOwner, T>(string name, List<string> missing)
		{
			try
			{
				FieldInfo field = typeof(TOwner).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(T))
				{
					return AccessTools.FieldRefAccess<TOwner, T>(field);
				}
			}
			catch (Exception)
			{
			}
			missing.Add(typeof(TOwner).Name + "." + name);
			return null;
		}

		private static Action<UpscaledFrameBuffer> Method(string name, List<string> missing)
		{
			try
			{
				MethodInfo method = typeof(UpscaledFrameBuffer).GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method != null && method.ReturnType == typeof(void))
				{
					return (Action<UpscaledFrameBuffer>)Delegate.CreateDelegate(typeof(Action<UpscaledFrameBuffer>), method);
				}
			}
			catch (Exception)
			{
			}
			missing.Add("UpscaledFrameBuffer." + name + "()");
			return null;
		}
	}
	public static class GpuCapabilities
	{
		private const float RefreshSeconds = 0.5f;

		private static List<string> s_staticLines;

		private static List<string> s_lines;

		private static float s_nextRefresh;

		public static IReadOnlyList<string> HardwareScanLines { get; internal set; } = new List<string>();

		private static string Row(string label, string value)
		{
			return "<color=#C8A865>" + label + ":</color> " + value;
		}

		private static string YesNo(bool value)
		{
			if (!value)
			{
				return "<color=#EF4444>no</color>";
			}
			return "<color=#52E08D>yes</color>";
		}

		public static List<string> GetLines()
		{
			if (s_lines != null && Time.unscaledTime < s_nextRefresh)
			{
				return s_lines;
			}
			s_nextRefresh = Time.unscaledTime + 0.5f;
			s_lines = Build();
			return s_lines;
		}

		private static List<string> Build()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (s_staticLines == null)
			{
				s_staticLines = new List<string>
				{
					Row("Graphics API", $"{SystemInfo.graphicsDeviceType} ({SystemInfo.graphicsDeviceVersion})"),
					Row("GPU", $"{SystemInfo.graphicsDeviceName} ({SystemInfo.graphicsDeviceVendor}), {SystemInfo.graphicsMemorySize} MB"),
					Row("Compute shaders", YesNo(SystemInfo.supportsComputeShaders)) + "   " + Row("Async compute queue", YesNo(SystemInfo.supportsAsyncCompute)),
					Row("Unity's own ray tracing", YesNo(SystemInfo.supportsRayTracing)) + "   " + Row("Motion vectors", YesNo(SystemInfo.supportsMotionVectors)),
					Row("GPU fences", YesNo(SystemInfo.supportsGraphicsFence)) + "   " + Row("Async GPU readback", YesNo(SystemInfo.supportsAsyncGPUReadback))
				};
			}
			List<string> list = new List<string>(s_staticLines);
			list.Add(Row("Hardware ray tracing (game's Vulkan device)", RayTracingProbe.DeviceSummary));
			if (GraphicsConfig.RtSunShadows != null)
			{
				list.Add(Row("Ray tracing", RtShadows.Summary));
			}
			AddRow(list, DisplayRow);
			AddRow(list, FramePacingRow);
			AddRow(list, TessellationRow);
			AddRow(list, RenderTargetRow);
			list.Add(NativeBridge.Loaded ? ("<color=#8A7A5A>" + NativeOptions.UpscalerNames + " upscaling and frame generation run through the Native Door (Settings > Graphics). Unity's own ray tracing and Reflex are not exposed by this game build; " + (NativeOptions.OfferRtShadows ? "the Native Door has the GPU's ray tracing ready on the game's device ([Native] RayTracing, on by default) for the ray-traced shadows and lighting (switch them on in the settings, no restart needed).</color>" : "the Native Door has the GPU's ray tracing ready on the game's device ([Native] RayTracing, on by default); nothing is drawn with it yet.</color>")) : ("<color=#8A7A5A>Unity's own ray tracing and Reflex are not exposed by this game build. " + NativeOptions.UpscalerNames + " upscaling, frame generation and the ray tracing probe need the Native Door (Settings > Graphics, Vulkan only).</color>"));
			return list;
		}

		private static void AddRow(List<string> lines, Func<string> row)
		{
			try
			{
				lines.Add(row());
			}
			catch (Exception)
			{
				lines.Add("<color=#8A7A5A>(a row could not be read on this game version)</color>");
			}
		}

		private static string DisplayRow()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Resolution currentResolution = Screen.currentResolution;
			object[] obj = new object[4]
			{
				Screen.width,
				Screen.height,
				null,
				null
			};
			RefreshRate refreshRateRatio = ((Resolution)(ref currentResolution)).refreshRateRatio;
			obj[2] = ((RefreshRate)(ref refreshRateRatio)).value;
			obj[3] = Screen.fullScreenMode;
			return Row("Display", string.Format("{0}x{1} at {2:F0} Hz, {3}", obj));
		}

		private static string FramePacingRow()
		{
			return Row("Frame pacing", string.Format("limit {0}, vsync {1}, queued frames {2}", (Application.targetFrameRate <= 0) ? "none" : Application.targetFrameRate.ToString(), QualitySettings.vSyncCount, QualitySettings.maxQueuedFrames));
		}

		private static string TessellationRow()
		{
			return Row("Tessellation (game setting)", WorldSurfaceDials.VanillaTessellationActive ? "<color=#52E