Decompiled source of SuperSampling DSR v0.1.3

BepInEx/plugins/ValheimSupersampling/ValheimSupersampling.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SuperSampling (DSR)")]
[assembly: AssemblyDescription("Client-side world supersampling and optional system DSR resolution selection.")]
[assembly: AssemblyProduct("SuperSampling (DSR)")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.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 ValheimSupersampling
{
	public enum SamplingMode
	{
		Off,
		InGame,
		SystemDSR
	}
	[Serializable]
	public sealed class DisplayMode
	{
		public int Width;

		public int Height;

		public int Fullscreen;

		public uint RateNumerator;

		public uint RateDenominator;

		public double Hz
		{
			get
			{
				if (RateDenominator != 0)
				{
					return (double)RateNumerator / (double)RateDenominator;
				}
				return 0.0;
			}
		}

		public bool Valid
		{
			get
			{
				if (Width >= 320 && Height >= 240 && Width <= 16384 && Height <= 16384 && Fullscreen >= 0)
				{
					return Fullscreen <= 3;
				}
				return false;
			}
		}

		public string Key => Width + "x" + Height + "@" + RateNumerator + "/" + RateDenominator;

		public bool SameSizeAndMode(DisplayMode other)
		{
			if (other != null && Width == other.Width && Height == other.Height)
			{
				return Fullscreen == other.Fullscreen;
			}
			return false;
		}

		public override string ToString()
		{
			return Width + " x " + Height + "  @ " + Hz.ToString("0.###", CultureInfo.InvariantCulture) + " Hz";
		}
	}
	public static class RenderMath
	{
		public const long MaxPixels = 67108864L;

		public static bool TrySize(int width, int height, float percent, int maxDimension, out int rw, out int rh, out string error)
		{
			rw = (rh = 0);
			error = "";
			if (width < 8 || height < 8 || float.IsNaN(percent) || float.IsInfinity(percent) || percent < 100f || percent > 300f)
			{
				error = "Use a render scale from 100% to 300%.";
				return false;
			}
			rw = (int)Math.Round((double)width * (double)percent / 100.0, MidpointRounding.AwayFromZero);
			rh = (int)Math.Round((double)height * (double)percent / 100.0, MidpointRounding.AwayFromZero);
			if (rw > maxDimension || rh > maxDimension || (long)rw * (long)rh > 67108864)
			{
				error = "That resolution exceeds the texture size limit. Choose a lower scale.";
				return false;
			}
			return true;
		}

		public static bool IsHigher(DisplayMode mode, int nativeWidth, int nativeHeight)
		{
			if (mode == null || nativeWidth < 1 || nativeHeight < 1 || !mode.Valid)
			{
				return false;
			}
			double num = Math.Abs((double)mode.Width / (double)mode.Height / ((double)nativeWidth / (double)nativeHeight) - 1.0);
			if (mode.Width > nativeWidth && mode.Height > nativeHeight)
			{
				return num < 0.015;
			}
			return false;
		}

		public static List<DisplayMode> HigherModes(IEnumerable<DisplayMode> modes, int width, int height)
		{
			return (from x in modes
				where IsHigher(x, width, height)
				group x by x.Key into x
				select x.First() into x
				orderby (long)x.Width * (long)x.Height, x.Hz descending
				select x).ToList();
		}

		public static float UiRatio(int outputHeight, int nativeHeight, float adjustment)
		{
			if (nativeHeight <= 0 || outputHeight <= 0)
			{
				return 1f;
			}
			return (float)((double)outputHeight / (double)nativeHeight) * adjustment;
		}
	}
	public interface IDisplayHost
	{
		DisplayMode Capture();

		void Set(DisplayMode mode);

		void SaveRecovery(DisplayMode original);

		void ClearRecovery();
	}
	public enum SwitchPhase
	{
		Idle,
		Applying,
		Confirming,
		Active,
		Restoring,
		Failed
	}
	public sealed class DisplayTransaction
	{
		private readonly IDisplayHost host;

		private DisplayMode original;

		private DisplayMode rollback;

		private DisplayMode target;

		private bool previouslyActive;

		private bool restoreToActive;

		private int matchingFrames;

		private double deadline;

		public SwitchPhase Phase { get; private set; }

		public string Notice { get; private set; } = "";

		public bool InDsr
		{
			get
			{
				if (Phase != SwitchPhase.Applying && Phase != SwitchPhase.Confirming && Phase != SwitchPhase.Active)
				{
					if (Phase == SwitchPhase.Restoring)
					{
						return restoreToActive;
					}
					return false;
				}
				return true;
			}
		}

		public bool Busy
		{
			get
			{
				if (Phase != SwitchPhase.Applying && Phase != SwitchPhase.Confirming)
				{
					return Phase == SwitchPhase.Restoring;
				}
				return true;
			}
		}

		public bool NeedsConfirmation
		{
			get
			{
				if (Phase != SwitchPhase.Applying)
				{
					return Phase == SwitchPhase.Confirming;
				}
				return true;
			}
		}

		public DisplayMode Original => original;

		public DisplayMode Target => target;

		public double SecondsLeft(double now)
		{
			return Math.Max(0.0, deadline - now);
		}

		public DisplayTransaction(IDisplayHost host)
		{
			this.host = host;
		}

		public void Begin(DisplayMode requested, double now)
		{
			if (requested == null || !requested.Valid)
			{
				throw new ArgumentException("Invalid display mode.");
			}
			if (Busy)
			{
				throw new InvalidOperationException("Wait for the current display change.");
			}
			if (Phase == SwitchPhase.Failed)
			{
				throw new InvalidOperationException("Display recovery has not been verified. Restart Valheim before another DSR switch.");
			}
			rollback = host.Capture();
			previouslyActive = Phase == SwitchPhase.Active;
			if (!previouslyActive)
			{
				original = rollback;
			}
			host.SaveRecovery(original);
			target = requested;
			Phase = SwitchPhase.Applying;
			matchingFrames = 0;
			deadline = now + 20.0;
			Notice = "Switching resolution...";
			host.Set(target);
		}

		public bool Keep()
		{
			if (Phase != SwitchPhase.Confirming || !target.SameSizeAndMode(host.Capture()))
			{
				return false;
			}
			Phase = SwitchPhase.Active;
			Notice = "DSR resolution confirmed.";
			return true;
		}

		public void Cancel(double now)
		{
			if (NeedsConfirmation)
			{
				Restore(rollback, previouslyActive, now, "Display change reverted.");
			}
		}

		public void Exit(double now)
		{
			if (original != null && Phase != SwitchPhase.Idle)
			{
				Restore(original, activeAfter: false, now, "Previous resolution restored.");
			}
		}

		public void Recover(DisplayMode saved, double now)
		{
			if (saved == null || !saved.Valid)
			{
				throw new ArgumentException("Invalid recovery resolution.");
			}
			original = saved;
			Restore(saved, activeAfter: false, now, "Recovered the display settings from the previous session.");
		}

		private void Restore(DisplayMode value, bool activeAfter, double now, string notice)
		{
			target = value;
			restoreToActive = activeAfter;
			Phase = SwitchPhase.Restoring;
			matchingFrames = 0;
			deadline = now + 15.0;
			Notice = notice;
			host.Set(target);
		}

		public void Tick(double now)
		{
			if (!Busy)
			{
				return;
			}
			if (Phase == SwitchPhase.Confirming)
			{
				if (now >= deadline)
				{
					Cancel(now);
				}
				return;
			}
			matchingFrames = (target.SameSizeAndMode(host.Capture()) ? (matchingFrames + 1) : 0);
			if (matchingFrames >= 3)
			{
				if (Phase == SwitchPhase.Applying)
				{
					Phase = SwitchPhase.Confirming;
					deadline = now + 15.0;
					Notice = "Keep this resolution?";
					return;
				}
				Phase = (restoreToActive ? SwitchPhase.Active : SwitchPhase.Idle);
				if (!restoreToActive)
				{
					host.ClearRecovery();
					original = null;
				}
			}
			else if (now >= deadline)
			{
				if (Phase == SwitchPhase.Applying)
				{
					Cancel(now);
					return;
				}
				Phase = SwitchPhase.Failed;
				Notice = "The game did not confirm display restoration. Recovery is saved for the next launch.";
			}
		}
	}
	internal sealed class ApiMember
	{
		public readonly string Owner;

		public readonly string Name;

		public readonly string ValueType;

		public readonly bool IsField;

		public readonly bool IsStatic;

		public readonly string[] Parameters;

		public ApiMember(string owner, string name, string valueType, bool isField = false, bool isStatic = false, params string[] parameters)
		{
			Owner = owner;
			Name = name;
			ValueType = valueType;
			IsField = isField;
			IsStatic = isStatic;
			Parameters = parameters;
		}

		public MemberInfo Resolve(Func<string, Type> findType)
		{
			Type type = findType(Owner) ?? throw new TypeLoadException("Missing game type: " + Owner);
			if (IsField)
			{
				FieldInfo field = type.GetField(Name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.IsStatic == IsStatic && TypeName(field.FieldType) == ValueType)
				{
					return field;
				}
			}
			else
			{
				MethodInfo[] array = (from m in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
					where m.Name == Name && !m.IsGenericMethod && m.IsStatic == IsStatic && TypeName(m.ReturnType) == ValueType && (from p in m.GetParameters()
						select TypeName(p.ParameterType)).SequenceEqual(Parameters)
					select m).ToArray();
				if (array.Length == 1)
				{
					return array[0];
				}
			}
			throw new MissingMemberException("Incompatible game API: " + this);
		}

		private static string TypeName(Type type)
		{
			if (!type.IsGenericType)
			{
				return type.FullName;
			}
			string fullName = type.GetGenericTypeDefinition().FullName;
			return fullName.Substring(0, fullName.IndexOf('`')) + "<" + string.Join(",", type.GetGenericArguments().Select(TypeName)) + ">";
		}

		public override string ToString()
		{
			return (IsStatic ? "static " : "instance ") + Owner + "." + Name + (IsField ? "" : ("(" + string.Join(", ", Parameters) + ")")) + " : " + ValueType;
		}
	}
	internal static class GameApiContract
	{
		public const string ReferenceGame = "1.0.12";

		public const string ReferenceUnity = "6000.0.75f1";

		public const string RequiredBepInExPack = "5.4.2350";

		public static readonly ApiMember WorldCamera = new ApiMember("UpscaledFrameBuffer", "m_camera", "UnityEngine.Camera", true, false);

		public static readonly ApiMember WorldTexture = new ApiMember("UpscaledFrameBuffer", "m_renderTexture", "UnityEngine.RenderTexture", true, false);

		public static readonly ApiMember ClearCamera = new ApiMember("UpscaledFrameBuffer", "m_clearCamera", "UnityEngine.Camera", true, false);

		public static readonly ApiMember IsScaled = new ApiMember("UpscaledFrameBuffer", "m_isUsingScaledRendering", "System.Boolean", true, false);

		public static readonly ApiMember Subscribers = new ApiMember("UpscaledFrameBuffer", "m_subscribers", "System.Collections.Generic.List<FrameBufferScaler>", true, false);

		public static readonly ApiMember UpdateTarget = new ApiMember("UpscaledFrameBuffer", "UpdateCameraTarget", "System.Void", false, false);

		public static readonly ApiMember ReleaseTexture = new ApiMember("UpscaledFrameBuffer", "ReleaseTextureIfExists", "System.Void", false, false);

		public static readonly ApiMember CreateClearCamera = new ApiMember("UpscaledFrameBuffer", "CreateClearCamera", "System.Void", false, false);

		public static readonly ApiMember DestroyBuffer = new ApiMember("UpscaledFrameBuffer", "OnDestroy", "System.Void", false, false);

		public static readonly ApiMember BufferCreated = new ApiMember("FrameBufferScaler", "OnBufferCreated", "System.Void", false, false, "UpscaledFrameBuffer", "UnityEngine.RenderTexture");

		public static readonly ApiMember UiSize = new ApiMember("GuiScaler", "GetScreenSizeFactor", "System.Single", false, false);

		public static readonly ApiMember MenuVisible = new ApiMember("Menu", "IsVisible", "System.Boolean", false, true);

		public static readonly ApiMember TakeInput = new ApiMember("PlayerController", "TakeInput", "System.Boolean", false, false, "System.Boolean");

		public static readonly ApiMember MouseCapture = new ApiMember("GameCamera", "UpdateMouseCapture", "System.Void", false, false);

		public static readonly ApiMember UpdateCamera = new ApiMember("GameCamera", "UpdateCamera", "System.Void", false, false, "System.Single");

		public static readonly ApiMember[] All = new ApiMember[15]
		{
			WorldCamera, WorldTexture, ClearCamera, IsScaled, Subscribers, UpdateTarget, ReleaseTexture, CreateClearCamera, DestroyBuffer, BufferCreated,
			UiSize, MenuVisible, TakeInput, MouseCapture, UpdateCamera
		};
	}
	internal sealed class NativeRenderer
	{
		private readonly Plugin plugin;

		private readonly Dictionary<Component, RenderTexture> owned = new Dictionary<Component, RenderTexture>();

		private Type bufferType;

		private FieldInfo cameraField;

		private FieldInfo textureField;

		private FieldInfo clearCameraField;

		private FieldInfo scaledField;

		private FieldInfo subscribersField;

		private MethodInfo updateMethod;

		private MethodInfo destroyMethod;

		private MethodInfo releaseMethod;

		private MethodInfo createClearMethod;

		private MethodInfo bufferCreated;

		public bool Supported { get; private set; }

		public string Detail { get; private set; } = "Not initialized.";

		public int Width { get; private set; }

		public int Height { get; private set; }

		public int Count => owned.Count((KeyValuePair<Component, RenderTexture> x) => Object.op_Implicit((Object)(object)x.Key) && Object.op_Implicit((Object)(object)x.Value));

		public NativeRenderer(Plugin plugin)
		{
			this.plugin = plugin;
		}

		public void Install(Harmony harmony)
		{
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Expected O, but got Unknown
			try
			{
				cameraField = (FieldInfo)GameApiContract.WorldCamera.Resolve((Func<string, Type>)AccessTools.TypeByName);
				bufferType = cameraField.DeclaringType;
				if (!typeof(Component).IsAssignableFrom(bufferType))
				{
					throw new NotSupportedException("The world framebuffer is no longer a Unity component.");
				}
				textureField = (FieldInfo)GameApiContract.WorldTexture.Resolve((Func<string, Type>)AccessTools.TypeByName);
				clearCameraField = (FieldInfo)GameApiContract.ClearCamera.Resolve((Func<string, Type>)AccessTools.TypeByName);
				scaledField = (FieldInfo)GameApiContract.IsScaled.Resolve((Func<string, Type>)AccessTools.TypeByName);
				subscribersField = (FieldInfo)GameApiContract.Subscribers.Resolve((Func<string, Type>)AccessTools.TypeByName);
				updateMethod = (MethodInfo)GameApiContract.UpdateTarget.Resolve((Func<string, Type>)AccessTools.TypeByName);
				releaseMethod = (MethodInfo)GameApiContract.ReleaseTexture.Resolve((Func<string, Type>)AccessTools.TypeByName);
				createClearMethod = (MethodInfo)GameApiContract.CreateClearCamera.Resolve((Func<string, Type>)AccessTools.TypeByName);
				destroyMethod = (MethodInfo)GameApiContract.DestroyBuffer.Resolve((Func<string, Type>)AccessTools.TypeByName);
				bufferCreated = (MethodInfo)GameApiContract.BufferCreated.Resolve((Func<string, Type>)AccessTools.TypeByName);
				harmony.Patch((MethodBase)updateMethod, new HarmonyMethod(typeof(NativeRenderer), "UpdatePrefix", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)destroyMethod, new HarmonyMethod(typeof(NativeRenderer), "DestroyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Supported = true;
				Detail = "Valheim world framebuffer adapter ready.";
			}
			catch (Exception ex)
			{
				Supported = false;
				plugin.RemoveOwnPatches(updateMethod, destroyMethod);
				Detail = "Unsupported framebuffer API: " + ex.GetBaseException().Message;
				plugin.LogWarning(Detail);
			}
		}

		private static bool UpdatePrefix(Component __instance)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null || instance.Renderer == null || !instance.Renderer.Supported || instance.RuntimeMode == SamplingMode.Off)
			{
				return true;
			}
			try
			{
				return instance.Renderer.Update(__instance);
			}
			catch (Exception ex)
			{
				try
				{
					instance.Renderer.Release(__instance);
				}
				catch (Exception ex2)
				{
					instance.LogWarning(ex2.GetBaseException().Message);
				}
				instance.RenderingFailed(ex.GetBaseException().Message);
				return true;
			}
		}

		private static void DestroyPrefix(Component __instance)
		{
			NativeRenderer nativeRenderer = Plugin.Instance?.Renderer;
			if (nativeRenderer == null || !nativeRenderer.owned.ContainsKey(__instance))
			{
				return;
			}
			try
			{
				nativeRenderer.Release(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Instance?.LogWarning("Framebuffer cleanup: " + ex.GetBaseException().Message);
			}
		}

		private bool Update(Component instance)
		{
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return true;
			}
			if (Screen.width < 8 || Screen.height < 8)
			{
				return false;
			}
			if ((Object)(object)GraphicsSettings.currentRenderPipeline != (Object)null)
			{
				throw new NotSupportedException("This build requires Valheim's built-in render pipeline.");
			}
			object? value = cameraField.GetValue(instance);
			Camera val = (Camera)((value is Camera) ? value : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = instance.GetComponent<Camera>();
				cameraField.SetValue(instance, val);
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return true;
			}
			if (val.stereoEnabled)
			{
				throw new NotSupportedException("Stereo/VR rendering is not supported.");
			}
			object? value2 = textureField.GetValue(instance);
			RenderTexture val2 = (RenderTexture)((value2 is RenderTexture) ? value2 : null);
			if (Object.op_Implicit((Object)(object)val.targetTexture) && (Object)(object)val.targetTexture != (Object)(object)val2)
			{
				throw new InvalidOperationException("Another renderer owns this camera. Supersampling was disabled.");
			}
			IList list = (IList)subscribersField.GetValue(instance);
			if (plugin.RuntimeMode == SamplingMode.SystemDSR || plugin.RenderScale <= 100f || list.Count == 0)
			{
				if (Object.op_Implicit((Object)(object)val2) || (bool)scaledField.GetValue(instance))
				{
					ReleaseNative(instance);
				}
				owned[instance] = null;
				Width = Screen.width;
				Height = Screen.height;
				Detail = ((list.Count == 0 && plugin.RuntimeMode == SamplingMode.InGame) ? "Waiting for the world display surface." : "World renders at the current output resolution.");
				return false;
			}
			if (!RenderMath.TrySize(Screen.width, Screen.height, plugin.RenderScale, SystemInfo.maxTextureSize, out var rw, out var rh, out var error))
			{
				throw new NotSupportedException(error);
			}
			bool mipmapped = plugin.Mipmapped;
			if (!owned.TryGetValue(instance, out var value3) || !Object.op_Implicit((Object)(object)value3) || (Object)(object)value3 != (Object)(object)val2 || ((Texture)value3).width != rw || ((Texture)value3).height != rh || value3.useMipMap != mipmapped || !value3.IsCreated())
			{
				RenderTexture val3 = new RenderTexture(rw, rh, 24, (DefaultFormat)0)
				{
					name = "Valheim Supersampling World",
					hideFlags = (HideFlags)52,
					antiAliasing = 1,
					useMipMap = mipmapped,
					autoGenerateMips = mipmapped,
					filterMode = (FilterMode)((!mipmapped) ? 1 : 2),
					wrapMode = (TextureWrapMode)1,
					anisoLevel = 0,
					mipMapBias = 0f
				};
				try
				{
					if (!val3.Create())
					{
						throw new NotSupportedException("The GPU rejected the world render texture. Try a lower scale.");
					}
					ReleaseNative(instance);
					createClearMethod.Invoke(instance, null);
					textureField.SetValue(instance, val3);
					val.targetTexture = val3;
					scaledField.SetValue(instance, true);
					owned[instance] = val3;
					for (int i = 0; i < list.Count; i++)
					{
						object? obj = list[i];
						Component val4 = (Component)((obj is Component) ? obj : null);
						if (val4 != null && Object.op_Implicit((Object)(object)val4))
						{
							bufferCreated.Invoke(val4, new object[2] { instance, val3 });
						}
					}
				}
				catch
				{
					if (Object.op_Implicit((Object)(object)val3) && (Object)(object)val3 != (Object)/*isinst with value type is only supported in some contexts*/)
					{
						val3.Release();
						Object.Destroy((Object)(object)val3);
					}
					throw;
				}
				plugin.LogInfo("World " + rw + "x" + rh + " -> output/UI " + Screen.width + "x" + Screen.height + (mipmapped ? "; trilinear mipmaps." : "; bilinear."));
			}
			Width = rw;
			Height = rh;
			Detail = "World " + rw + " x " + rh + "  |  UI " + Screen.width + " x " + Screen.height;
			return false;
		}

		private void ReleaseNative(Component instance)
		{
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			object? value = textureField.GetValue(instance);
			RenderTexture val = (RenderTexture)((value is RenderTexture) ? value : null);
			object? value2 = cameraField.GetValue(instance);
			Camera val2 = (Camera)((value2 is Camera) ? value2 : null);
			RenderTexture val3 = ((Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.targetTexture) && (Object)(object)val2.targetTexture != (Object)(object)val) ? val2.targetTexture : null);
			try
			{
				releaseMethod.Invoke(instance, null);
			}
			finally
			{
				if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val3))
				{
					val2.targetTexture = val3;
				}
				textureField.SetValue(instance, null);
				if (Object.op_Implicit((Object)(object)val))
				{
					val.Release();
					Object.Destroy((Object)(object)val);
				}
				object? value3 = clearCameraField.GetValue(instance);
				Camera val4 = (Camera)((value3 is Camera) ? value3 : null);
				if (Object.op_Implicit((Object)(object)val4))
				{
					((Behaviour)val4).enabled = false;
					Object.Destroy((Object)(object)((Component)val4).gameObject);
				}
				clearCameraField.SetValue(instance, null);
				scaledField.SetValue(instance, false);
				if (owned.TryGetValue(instance, out var value4) && Object.op_Implicit((Object)(object)value4) && (Object)(object)value4 != (Object)(object)val)
				{
					value4.Release();
					Object.Destroy((Object)(object)value4);
				}
				owned.Remove(instance);
			}
		}

		private void Release(Component instance)
		{
			ReleaseNative(instance);
			owned.Remove(instance);
		}

		public void ReleaseAll()
		{
			KeyValuePair<Component, RenderTexture>[] array = owned.ToArray();
			int i;
			for (i = 0; i < array.Length; i++)
			{
				KeyValuePair<Component, RenderTexture> keyValuePair = array[i];
				try
				{
					if (Object.op_Implicit((Object)(object)keyValuePair.Key))
					{
						Release(keyValuePair.Key);
					}
					else if (Object.op_Implicit((Object)(object)keyValuePair.Value))
					{
						keyValuePair.Value.Release();
						Object.Destroy((Object)(object)keyValuePair.Value);
					}
				}
				catch (Exception ex)
				{
					plugin.LogWarning("Framebuffer restore: " + ex.GetBaseException().Message);
				}
			}
			owned.Clear();
			i = (Height = 0);
			Width = i;
			Detail = (Supported ? "Supersampling off." : Detail);
		}
	}
	[BepInPlugin("mods.valheim.supersampling", "SuperSampling (DSR)", "0.1.3")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim.x86_64")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "mods.valheim.supersampling";

		public const string Name = "SuperSampling (DSR)";

		public const string Version = "0.1.3";

		internal static Plugin Instance;

		internal NativeRenderer Renderer;

		internal UiCompensation Ui;

		private ConfigEntry<SamplingMode> modeEntry;

		private ConfigEntry<float> scaleEntry;

		private ConfigEntry<float> uiAdjustmentEntry;

		private ConfigEntry<bool> keepUiEntry;

		private ConfigEntry<bool> mipEntry;

		private ConfigEntry<int> nativeWidthEntry;

		private ConfigEntry<int> nativeHeightEntry;

		private ConfigEntry<string> resolutionEntry;

		private ConfigEntry<KeyboardShortcut> menuKey;

		private ConfigEntry<KeyboardShortcut> resetKey;

		private Harmony harmony;

		private DisplayTransaction display;

		private UnityDisplayHost displayHost;

		private bool initialized;

		private bool shutdown;

		private bool renderFailed;

		private string notice = "Starting...";

		private SamplingMode returnMode;

		private SamplingMode afterRestoreMode;

		private bool windowOpen;

		private CursorLockMode previousCursorLock;

		private bool previousCursorVisible;

		private readonly HashSet<EventSystem> blockedEvents = new HashSet<EventSystem>();

		private SamplingMode draftMode;

		private string scaleText = "150";

		private string nativeWidthText = "1920";

		private string nativeHeightText = "1080";

		private bool draftKeepUi = true;

		private bool draftMipmaps = true;

		private bool showReference;

		private float draftUiAdjustment = 1f;

		private List<DisplayMode> resolutions = new List<DisplayMode>();

		private int selectedResolution = -1;

		private Vector2 scroll;

		private Vector2 resolutionScroll;

		private Rect windowRect;

		private int lastWidth;

		private int lastHeight;

		private GUIStyle textStyle;

		private GUIStyle titleStyle;

		private GUIStyle buttonStyle;

		private GUIStyle boxStyle;

		private GUIStyle toggleStyle;

		private GUIStyle fieldStyle;

		internal SamplingMode RuntimeMode { get; private set; }

		internal float RenderScale => scaleEntry.Value;

		internal bool Mipmapped => mipEntry.Value;

		internal int NativeWidth
		{
			get
			{
				if (nativeWidthEntry.Value <= 0)
				{
					return Screen.width;
				}
				return nativeWidthEntry.Value;
			}
		}

		internal int NativeHeight
		{
			get
			{
				if (nativeHeightEntry.Value <= 0)
				{
					return Screen.height;
				}
				return nativeHeightEntry.Value;
			}
		}

		internal float UiAdjustment => uiAdjustmentEntry.Value;

		internal bool CompensateUi
		{
			get
			{
				if (RuntimeMode == SamplingMode.SystemDSR && keepUiEntry.Value)
				{
					return Ui.Supported;
				}
				return false;
			}
		}

		internal static double Now => (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;

		internal void LogInfo(string text)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)text);
		}

		internal void LogWarning(string text)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)text);
		}

		private void Awake()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			if (Application.isBatchMode || (int)SystemInfo.graphicsDeviceType == 4)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			modeEntry = ((BaseUnityPlugin)this).Config.Bind<SamplingMode>("General", "Mode", SamplingMode.Off, "Off or InGame can resume on launch. SystemDSR is applied manually with F8 each session.");
			scaleEntry = ((BaseUnityPlugin)this).Config.Bind<float>("In-game", "RenderScale", 150f, new ConfigDescription("Percentage of output width AND height. 200% renders four times the pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 300f), Array.Empty<object>()));
			mipEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("In-game", "MipmappedDownsampling", true, "Trilinear mipmapped downsampling. Disable for a sharper basic bilinear filter.");
			keepUiEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("System DSR", "KeepUiAtNativeSize", true, "Compensate Valheim's GuiScaler to preserve native apparent UI size. InGame always keeps normal UI rendering.");
			uiAdjustmentEntry = ((BaseUnityPlugin)this).Config.Bind<float>("System DSR", "UiSizeAdjustment", 1f, new ConfigDescription("Extra UI size adjustment. 1 = automatic compensation only.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			resolutionEntry = ((BaseUnityPlugin)this).Config.Bind<string>("System DSR", "Resolution", "", "Previously selected system mode; does not enable DSR in the driver.");
			nativeWidthEntry = ((BaseUnityPlugin)this).Config.Bind<int>("Display reference", "NativeWidth", 0, "Monitor native width. 0 captures the desktop/output size at startup; verify in F8.");
			nativeHeightEntry = ((BaseUnityPlugin)this).Config.Bind<int>("Display reference", "NativeHeight", 0, "Monitor native height. 0 captures the desktop/output size at startup; verify in F8.");
			menuKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "Settings", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Open settings.");
			resetKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "EmergencyOff", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Disable supersampling and restore the pre-DSR resolution.");
			Renderer = new NativeRenderer(this);
			Ui = new UiCompensation();
			harmony = new Harmony("mods.valheim.supersampling");
			Renderer.Install(harmony);
			Ui.Install(harmony, this);
			InstallInputHooks();
			displayHost = new UnityDisplayHost(Path.Combine(Paths.ConfigPath, "mods.valheim.supersampling.recovery.json"));
			display = new DisplayTransaction(displayHost);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("SuperSampling (DSR) 0.1.3; Unity " + Application.unityVersion + "; " + SystemInfo.graphicsDeviceName + "; " + ((object)SystemInfo.graphicsDeviceType/*cast due to .constrained prefix*/).ToString()));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Reference target: Valheim 1.0.12; Unity 6000.0.75f1; BepInExPack 5.4.2350. Runtime hooks are checked separately.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Press " + ((object)menuKey.Value/*cast due to .constrained prefix*/).ToString() + " for settings; " + ((object)resetKey.Value/*cast due to .constrained prefix*/).ToString() + " for emergency Off."));
		}

		private IEnumerator Start()
		{
			if (!((Behaviour)this).enabled || display == null)
			{
				yield break;
			}
			yield return (object)new WaitForSecondsRealtime(2f);
			try
			{
				DisplayMode displayMode = displayHost.ReadRecovery();
				if (nativeWidthEntry.Value < 320 || nativeHeightEntry.Value < 240)
				{
					Resolution currentResolution = Screen.currentResolution;
					nativeWidthEntry.Value = displayMode?.Width ?? Math.Max(Screen.width, ((Resolution)(ref currentResolution)).width);
					nativeHeightEntry.Value = displayMode?.Height ?? Math.Max(Screen.height, ((Resolution)(ref currentResolution)).height);
				}
				initialized = true;
				if (displayMode != null)
				{
					modeEntry.Value = SamplingMode.Off;
					afterRestoreMode = SamplingMode.Off;
					display.Recover(displayMode, Now);
					notice = "Restoring display settings from the previous session.";
				}
				else if (modeEntry.Value == SamplingMode.InGame && Renderer.Supported)
				{
					RuntimeMode = SamplingMode.InGame;
					notice = "In-game supersampling enabled.";
				}
				else
				{
					modeEntry.Value = SamplingMode.Off;
					notice = "Press " + ((object)menuKey.Value/*cast due to .constrained prefix*/).ToString() + " to choose supersampling.";
				}
				((BaseUnityPlugin)this).Config.Save();
			}
			catch (Exception ex)
			{
				initialized = true;
				notice = "Startup recovery: " + ex.GetBaseException().Message;
				LogWarning(notice);
			}
		}

		private void Update()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!initialized || shutdown)
			{
				return;
			}
			KeyboardShortcut value = resetKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				TurnOff();
				SetWindow(open: false);
			}
			else
			{
				value = menuKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					SetWindow(!windowOpen);
				}
			}
			if (renderFailed)
			{
				renderFailed = false;
				TurnOff(replaceNotice: false);
				SetWindow(open: true);
			}
			try
			{
				SwitchPhase phase = display.Phase;
				display.Tick(Now);
				if (phase != display.Phase)
				{
					notice = display.Notice;
					if (display.Phase == SwitchPhase.Idle)
					{
						RuntimeMode = afterRestoreMode;
						modeEntry.Value = afterRestoreMode;
						((BaseUnityPlugin)this).Config.Save();
						LoadDraft();
					}
					else if (display.Phase == SwitchPhase.Restoring || display.Phase == SwitchPhase.Failed)
					{
						RuntimeMode = (display.InDsr ? SamplingMode.SystemDSR : SamplingMode.Off);
						Renderer.ReleaseAll();
					}
					else if (display.Phase == SwitchPhase.Active)
					{
						RuntimeMode = SamplingMode.SystemDSR;
					}
				}
			}
			catch (Exception ex)
			{
				notice = "Display switch: " + ex.GetBaseException().Message;
				LogWarning(notice);
				TurnOff(replaceNotice: false);
			}
			if (windowOpen)
			{
				EventSystem current = EventSystem.current;
				if (Object.op_Implicit((Object)(object)current) && ((Behaviour)current).enabled)
				{
					((Behaviour)current).enabled = false;
					blockedEvents.Add(current);
				}
			}
		}

		private void LateUpdate()
		{
			if (windowOpen)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		internal void RenderingFailed(string error)
		{
			if (!renderFailed)
			{
				notice = "Supersampling disabled: " + error;
				LogWarning(notice);
				renderFailed = true;
				RuntimeMode = SamplingMode.Off;
			}
		}

		private void SetWindow(bool open)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (open == windowOpen)
			{
				return;
			}
			if (!open && display != null && display.NeedsConfirmation)
			{
				CancelDisplay();
			}
			windowOpen = open;
			if (open)
			{
				previousCursorLock = Cursor.lockState;
				previousCursorVisible = Cursor.visible;
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				LoadDraft();
				return;
			}
			foreach (EventSystem blockedEvent in blockedEvents)
			{
				if (Object.op_Implicit((Object)(object)blockedEvent))
				{
					((Behaviour)blockedEvent).enabled = true;
				}
			}
			blockedEvents.Clear();
			Cursor.lockState = previousCursorLock;
			Cursor.visible = previousCursorVisible;
		}

		private void TurnOff(bool replaceNotice = true)
		{
			RuntimeMode = SamplingMode.Off;
			afterRestoreMode = SamplingMode.Off;
			Renderer?.ReleaseAll();
			if (modeEntry != null)
			{
				modeEntry.Value = SamplingMode.Off;
				((BaseUnityPlugin)this).Config.Save();
			}
			try
			{
				if (display != null && display.Phase != SwitchPhase.Idle && (display.Phase != SwitchPhase.Restoring || display.InDsr))
				{
					display.Exit(Now);
				}
			}
			catch (Exception ex)
			{
				LogWarning("Restore request failed: " + ex.GetBaseException().Message);
			}
			if (replaceNotice)
			{
				notice = "Supersampling off. Restoring the previous display mode if needed.";
			}
			if (windowOpen)
			{
				LoadDraft();
			}
		}

		private void CancelDisplay()
		{
			afterRestoreMode = returnMode;
			display.Cancel(Now);
			RuntimeMode = (display.InDsr ? SamplingMode.SystemDSR : SamplingMode.Off);
			Renderer.ReleaseAll();
			notice = display.Notice;
		}

		private void ApplySettings()
		{
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Invalid comparison between Unknown and I4
			if (display.Busy)
			{
				notice = "Finish the current display change first.";
				return;
			}
			if (draftMode == SamplingMode.Off)
			{
				TurnOff();
				return;
			}
			if (!Renderer.Supported)
			{
				notice = Renderer.Detail;
				return;
			}
			if (!float.TryParse(scaleText, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				notice = "Enter a render scale such as 150 or 162.5.";
				return;
			}
			int width = ((display.InDsr && display.Original != null) ? display.Original.Width : Screen.width);
			int height = ((display.InDsr && display.Original != null) ? display.Original.Height : Screen.height);
			if (!RenderMath.TrySize(width, height, (draftMode == SamplingMode.InGame) ? result : 100f, SystemInfo.maxTextureSize, out var _, out var _, out var error))
			{
				notice = error;
				return;
			}
			if (result < 100f || result > 300f || float.IsNaN(result) || float.IsInfinity(result))
			{
				notice = "Use a render scale from 100% to 300%.";
				return;
			}
			if (!int.TryParse(nativeWidthText, out var result2) || !int.TryParse(nativeHeightText, out var result3) || result2 < 320 || result3 < 240 || result2 > 16384 || result3 > 16384)
			{
				notice = "Enter the monitor's native width and height.";
				return;
			}
			if (display.InDsr && (result2 != NativeWidth || result3 != NativeHeight))
			{
				notice = "Turn DSR off before changing the native display reference.";
				return;
			}
			DisplayMode selected = null;
			if (draftMode == SamplingMode.SystemDSR)
			{
				if ((int)Application.platform != 2)
				{
					notice = "System DSR switching requires Windows. Use In-Game on this platform.";
					return;
				}
				if (draftKeepUi && !Ui.Supported)
				{
					notice = Ui.Detail;
					return;
				}
				if (selectedResolution < 0 || selectedResolution >= resolutions.Count)
				{
					notice = "No higher system resolution is selected. Enable DSR/DLDSR in the driver, then refresh.";
					return;
				}
				selected = resolutions[selectedResolution];
				if (!RenderMath.IsHigher(selected, result2, result3))
				{
					notice = "Choose a resolution above the native reference.";
					return;
				}
				if (!UnityDisplayHost.Available().Any((DisplayMode x) => x.Key == selected.Key))
				{
					notice = "That display mode is no longer available. Refresh the list.";
					return;
				}
			}
			scaleEntry.Value = result;
			keepUiEntry.Value = draftKeepUi;
			mipEntry.Value = draftMipmaps;
			uiAdjustmentEntry.Value = draftUiAdjustment;
			nativeWidthEntry.Value = result2;
			nativeHeightEntry.Value = result3;
			Renderer.ReleaseAll();
			if (draftMode == SamplingMode.SystemDSR)
			{
				if (!display.InDsr)
				{
					returnMode = RuntimeMode;
				}
				afterRestoreMode = returnMode;
				resolutionEntry.Value = selected.Key;
				modeEntry.Value = SamplingMode.Off;
				((BaseUnityPlugin)this).Config.Save();
				RuntimeMode = SamplingMode.SystemDSR;
				try
				{
					display.Begin(selected, Now);
					notice = display.Notice;
					return;
				}
				catch (Exception ex)
				{
					RuntimeMode = (display.InDsr ? SamplingMode.SystemDSR : returnMode);
					notice = "Could not switch display: " + ex.GetBaseException().Message;
					LogWarning(notice);
					try
					{
						if (display.NeedsConfirmation)
						{
							CancelDisplay();
						}
					}
					catch (Exception ex2)
					{
						LogWarning(ex2.GetBaseException().Message);
					}
					modeEntry.Value = ((RuntimeMode != SamplingMode.SystemDSR) ? RuntimeMode : SamplingMode.Off);
					((BaseUnityPlugin)this).Config.Save();
					return;
				}
			}
			if (display.InDsr)
			{
				afterRestoreMode = SamplingMode.InGame;
				RuntimeMode = SamplingMode.Off;
				modeEntry.Value = SamplingMode.InGame;
				((BaseUnityPlugin)this).Config.Save();
				display.Exit(Now);
				notice = "Restoring the previous display resolution before enabling In-Game.";
			}
			else
			{
				RuntimeMode = SamplingMode.InGame;
				modeEntry.Value = SamplingMode.InGame;
				((BaseUnityPlugin)this).Config.Save();
				notice = "In-game supersampling enabled. HUD and menus keep their normal resolution.";
			}
		}

		private void InstallInputHooks()
		{
			PatchIfPresent(GameApiContract.MenuVisible, "MenuVisible", prefix: false);
			PatchIfPresent(GameApiContract.TakeInput, "TakeInput", prefix: false);
			PatchIfPresent(GameApiContract.MouseCapture, "MouseCapture", prefix: true);
			PatchIfPresent(GameApiContract.UpdateCamera, "CameraUpdate", prefix: true);
		}

		private void PatchIfPresent(ApiMember target, string patch, bool prefix)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			MethodInfo methodInfo = null;
			try
			{
				methodInfo = (MethodInfo)target.Resolve((Func<string, Type>)AccessTools.TypeByName);
				HarmonyMethod val = new HarmonyMethod(typeof(Plugin), patch, (Type[])null);
				harmony.Patch((MethodBase)methodInfo, prefix ? val : null, prefix ? null : val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				RemoveOwnPatches(methodInfo);
				LogWarning("Optional settings input hook unavailable: " + target?.ToString() + "; " + ex.GetBaseException().Message);
			}
		}

		internal void RemoveOwnPatches(params MethodBase[] methods)
		{
			foreach (MethodBase methodBase in methods)
			{
				if (!(methodBase == null))
				{
					try
					{
						harmony.Unpatch(methodBase, (HarmonyPatchType)0, "mods.valheim.supersampling");
					}
					catch (Exception ex)
					{
						LogWarning("Patch rollback: " + ex.GetBaseException().Message);
					}
				}
			}
		}

		private static void MenuVisible(ref bool __result)
		{
			if ((Object)(object)Instance != (Object)null && Instance.windowOpen)
			{
				__result = true;
			}
		}

		private static void TakeInput(ref bool __result)
		{
			if ((Object)(object)Instance != (Object)null && Instance.windowOpen)
			{
				__result = false;
			}
		}

		private static bool MouseCapture()
		{
			if ((Object)(object)Instance == (Object)null || !Instance.windowOpen)
			{
				return true;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			return false;
		}

		private static bool CameraUpdate()
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				return !Instance.windowOpen;
			}
			return true;
		}

		private void OnApplicationQuit()
		{
			Shutdown();
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void OnDisable()
		{
			if (initialized && !shutdown)
			{
				Shutdown();
			}
		}

		private void Shutdown()
		{
			if (shutdown)
			{
				return;
			}
			shutdown = true;
			SetWindow(open: false);
			RuntimeMode = SamplingMode.Off;
			Renderer?.ReleaseAll();
			try
			{
				if (display?.Original != null)
				{
					displayHost.Set(display.Original);
				}
			}
			catch (Exception ex)
			{
				LogWarning("Display restoration is saved for next launch: " + ex.GetBaseException().Message);
			}
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void LoadDraft()
		{
			if (scaleEntry != null)
			{
				draftMode = ((RuntimeMode != SamplingMode.Off) ? RuntimeMode : modeEntry.Value);
				scaleText = RenderScale.ToString("0.####", CultureInfo.InvariantCulture);
				draftKeepUi = keepUiEntry.Value;
				draftMipmaps = Mipmapped;
				draftUiAdjustment = UiAdjustment;
				nativeWidthText = NativeWidth.ToString();
				nativeHeightText = NativeHeight.ToString();
				RefreshModes();
			}
		}

		private void RefreshModes()
		{
			int result;
			int width = (int.TryParse(nativeWidthText, out result) ? result : NativeWidth);
			int result2;
			int height = (int.TryParse(nativeHeightText, out result2) ? result2 : NativeHeight);
			try
			{
				resolutions = RenderMath.HigherModes(UnityDisplayHost.Available(), width, height);
				selectedResolution = resolutions.FindIndex((DisplayMode x) => x.Key == resolutionEntry.Value);
				if (selectedResolution < 0 && resolutions.Count > 0)
				{
					selectedResolution = 0;
				}
			}
			catch (Exception ex)
			{
				resolutions.Clear();
				selectedResolution = -1;
				notice = "Display mode list unavailable: " + ex.GetBaseException().Message;
			}
		}

		private void OnGUI()
		{
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0032: Invalid comparison between Unknown and I4
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			//IL_0143: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Expected O, but got Unknown
			//IL_0193: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Expected O, but got Unknown
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			if (!initialized || !windowOpen || shutdown)
			{
				return;
			}
			if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27)
			{
				Event.current.Use();
				SetWindow(open: false);
				return;
			}
			Matrix4x4 matrix = GUI.matrix;
			int depth = GUI.depth;
			try
			{
				float num = Mathf.Clamp((float)Screen.height / 1080f, 0.65f, 8f);
				GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one * num);
				GUI.depth = -10000;
				if (textStyle == null)
				{
					textStyle = new GUIStyle(GUI.skin.label)
					{
						fontSize = 16,
						wordWrap = true
					};
					titleStyle = new GUIStyle(textStyle)
					{
						fontSize = 20,
						fontStyle = (FontStyle)1
					};
					buttonStyle = new GUIStyle(GUI.skin.button)
					{
						fontSize = 16,
						padding = new RectOffset(12, 12, 8, 8),
						wordWrap = true
					};
					boxStyle = new GUIStyle(GUI.skin.box)
					{
						padding = new RectOffset(16, 16, 12, 12)
					};
					toggleStyle = new GUIStyle(GUI.skin.toggle)
					{
						fontSize = 16,
						wordWrap = true
					};
					fieldStyle = new GUIStyle(GUI.skin.textField)
					{
						fontSize = 16,
						padding = new RectOffset(8, 8, 6, 6)
					};
				}
				if (lastWidth != Screen.width || lastHeight != Screen.height)
				{
					lastWidth = Screen.width;
					lastHeight = Screen.height;
					float num2 = Mathf.Min(780f, (float)Screen.width / num - 24f);
					float num3 = Mathf.Min(870f, (float)Screen.height / num - 24f);
					windowRect = new Rect(((float)Screen.width / num - num2) / 2f, ((float)Screen.height / num - num3) / 2f, num2, num3);
				}
				windowRect = GUILayout.Window(1446204243, windowRect, new WindowFunction(DrawWindow), "SuperSampling (DSR)  |  0.1.3", GUI.skin.window, Array.Empty<GUILayoutOption>());
			}
			finally
			{
				GUI.matrix = matrix;
				GUI.depth = depth;
			}
		}

		private void Label(string value)
		{
			GUILayout.Label(value, textStyle, Array.Empty<GUILayoutOption>());
		}

		private bool Button(string value, params GUILayoutOption[] options)
		{
			return GUILayout.Button(value, buttonStyle, options);
		}

		private void DrawWindow(int id)
		{
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: 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_0884: Unknown result type (might be due to invalid IL or missing references)
			//IL_0889: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(8f);
			GUILayout.Label("Sharper world. Readable UI.", titleStyle, Array.Empty<GUILayoutOption>());
			Label("Active: " + ModeLabel(RuntimeMode) + "   |   Output: " + displayHost.Capture());
			if (display.NeedsConfirmation)
			{
				GUILayout.BeginVertical(boxStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Label((display.Phase == SwitchPhase.Applying) ? "Testing display mode..." : "Keep this resolution?", titleStyle, Array.Empty<GUILayoutOption>());
				Label("Automatic revert in " + Math.Ceiling(display.SecondsLeft(Now)) + " seconds.");
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				GUI.enabled = display.Phase == SwitchPhase.Confirming;
				if (Button("Keep") && display.Keep())
				{
					notice = display.Notice;
				}
				GUI.enabled = true;
				if (Button("Revert"))
				{
					CancelDisplay();
				}
				GUILayout.EndHorizontal();
				GUILayout.EndVertical();
			}
			scroll = GUILayout.BeginScrollView(scroll, Array.Empty<GUILayoutOption>());
			GUI.enabled = !display.Busy;
			draftMode = (SamplingMode)GUILayout.Toolbar((int)draftMode, new string[3] { "Off", "In-Game", "System DSR" }, buttonStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(12f);
			if (draftMode == SamplingMode.InGame)
			{
				Label("Renders the 3D world above the current output resolution, then downsamples it. HUD and menus remain at normal resolution and size.");
				GUILayout.Space(8f);
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				int[] array = new int[4] { 125, 150, 175, 200 };
				for (int i = 0; i < array.Length; i++)
				{
					int num = array[i];
					if (Button(num + "%"))
					{
						scaleText = num.ToString();
					}
				}
				GUILayout.EndHorizontal();
				float.TryParse(scaleText, NumberStyles.Float, CultureInfo.InvariantCulture, out var result);
				float num2 = ((float.IsNaN(result) || float.IsInfinity(result)) ? 100f : Mathf.Clamp(result, 100f, 300f));
				float num3 = GUILayout.HorizontalSlider(num2, 100f, 300f, Array.Empty<GUILayoutOption>());
				if (Math.Abs(num3 - num2) > 0.01f)
				{
					scaleText = Mathf.RoundToInt(num3).ToString();
				}
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				Label("Render scale (%)");
				scaleText = GUILayout.TextField(scaleText, 8, fieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(115f) });
				GUILayout.EndHorizontal();
				int num4 = ((display.InDsr && display.Original != null) ? display.Original.Width : Screen.width);
				int num5 = ((display.InDsr && display.Original != null) ? display.Original.Height : Screen.height);
				if (RenderMath.TrySize(num4, num5, result, SystemInfo.maxTextureSize, out var rw, out var rh, out var error))
				{
					Label(rw + " x " + rh + " world -> " + num4 + " x " + num5 + " output\n" + ((double)rw * (double)rh / ((double)num4 * (double)num5)).ToString("0.####", CultureInfo.InvariantCulture) + "x as many world pixels. Higher scales use more GPU time and VRAM.");
				}
				else
				{
					Label(error);
				}
				draftMipmaps = GUILayout.Toggle(draftMipmaps, "Prefilter downsampling (smoother edges)", toggleStyle, Array.Empty<GUILayoutOption>());
				Label(draftMipmaps ? "Trilinear mipmaps; 200% provides a 2 x 2 downsampling footprint." : "Basic bilinear filter; can look sharper at fractional scales.");
				bool enabled = GUI.enabled;
				GUI.enabled = false;
				GUILayout.Toggle(true, "Keep UI at native size (automatic in this mode)", toggleStyle, Array.Empty<GUILayoutOption>());
				GUI.enabled = enabled;
			}
			else if (draftMode == SamplingMode.SystemDSR)
			{
				Label("NVIDIA DSR / DLDSR or other higher system resolutions. Enable the desired factors in your driver first. This mode uses exclusive fullscreen and restores your previous window/fullscreen mode when you leave it.");
				Label("Unity lists available modes, but cannot label them as DSR versus DLDSR. Driver settings choose the downsampling method.");
				GUILayout.Space(8f);
				Label("Native reference: " + nativeWidthText + " x " + nativeHeightText);
				if (Button("Refresh available resolutions"))
				{
					RefreshModes();
				}
				if (resolutions.Count == 0)
				{
					Label("No higher modes found. Check the native reference below, enable DSR/DLDSR in the driver, then restart Valheim if needed.");
				}
				else
				{
					resolutionScroll = GUILayout.BeginScrollView(resolutionScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(145f) });
					selectedResolution = GUILayout.SelectionGrid(selectedResolution, resolutions.Select((DisplayMode x) => x.ToString()).ToArray(), 1, buttonStyle, Array.Empty<GUILayoutOption>());
					GUILayout.EndScrollView();
				}
				draftKeepUi = GUILayout.Toggle(draftKeepUi, "Keep UI at native size", toggleStyle, Array.Empty<GUILayoutOption>());
				Label("DSR UI size adjustment: " + (draftUiAdjustment * 100f).ToString("0", CultureInfo.InvariantCulture) + "%");
				draftUiAdjustment = Mathf.Round(GUILayout.HorizontalSlider(draftUiAdjustment, 0.5f, 2f, Array.Empty<GUILayoutOption>()) * 100f) / 100f;
				Label("100% preserves the normal apparent size. Raise it for larger text and HUD elements.");
				if (!Ui.Supported)
				{
					Label(Ui.Detail);
				}
			}
			else
			{
				Label("Valheim uses its own graphics settings. Turning this off also restores the resolution used before System DSR.");
			}
			GUILayout.Space(10f);
			showReference = GUILayout.Toggle(showReference, "Display reference and diagnostics", toggleStyle, Array.Empty<GUILayoutOption>());
			if (showReference)
			{
				Label("Set these to the monitor's native resolution. The first-run estimate can be wrong if the desktop already uses DSR or you change monitors.");
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				Label("Width");
				nativeWidthText = GUILayout.TextField(nativeWidthText, 5, fieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
				Label("Height");
				nativeHeightText = GUILayout.TextField(nativeHeightText, 5, fieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
				GUILayout.EndHorizontal();
				if (!display.InDsr && Button("Use current game output as native reference"))
				{
					nativeWidthText = Screen.width.ToString();
					nativeHeightText = Screen.height.ToString();
					RefreshModes();
				}
				Label(Renderer.Detail);
				Label(Ui.Detail);
				if (Button("Write diagnostic report"))
				{
					WriteDiagnosticReport();
				}
				if (Button("Reload configuration file"))
				{
					try
					{
						((BaseUnityPlugin)this).Config.Reload();
						LoadDraft();
						notice = "Configuration loaded. Select a mode and Apply.";
					}
					catch (Exception ex)
					{
						notice = ex.Message;
					}
				}
			}
			GUI.enabled = true;
			GUILayout.EndScrollView();
			GUILayout.Space(8f);
			GUILayout.BeginVertical(boxStyle, Array.Empty<GUILayoutOption>());
			Label(notice);
			GUILayout.EndVertical();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.enabled = !display.Busy;
			if (Button("Apply"))
			{
				try
				{
					ApplySettings();
				}
				catch (Exception ex2)
				{
					notice = "Could not apply: " + ex2.GetBaseException().Message;
					LogWarning(notice);
				}
			}
			GUI.enabled = true;
			if (Button("Off / restore"))
			{
				TurnOff();
			}
			if (Button("Close"))
			{
				SetWindow(open: false);
			}
			GUILayout.EndHorizontal();
			Label(((object)menuKey.Value/*cast due to .constrained prefix*/).ToString() + ": settings   |   " + ((object)resetKey.Value/*cast due to .constrained prefix*/).ToString() + ": emergency Off");
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 24f));
		}

		private static string ModeLabel(SamplingMode mode)
		{
			return mode switch
			{
				SamplingMode.SystemDSR => "System DSR", 
				SamplingMode.InGame => "In-Game", 
				_ => "Off", 
			};
		}

		private void WriteDiagnosticReport()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("SuperSampling (DSR) 0.1.3");
				stringBuilder.AppendLine("UTC: " + DateTime.UtcNow.ToString("O"));
				stringBuilder.AppendLine("Unity: " + Application.unityVersion + "; game: " + Application.version);
				stringBuilder.AppendLine("Reference target: Valheim 1.0.12; Unity 6000.0.75f1; BepInExPack 5.4.2350");
				stringBuilder.AppendLine("Loaded BepInEx: " + typeof(BaseUnityPlugin).Assembly.GetName().Version);
				stringBuilder.AppendLine("GPU: " + SystemInfo.graphicsDeviceName + "; VRAM MiB: " + SystemInfo.graphicsMemorySize);
				stringBuilder.AppendLine("Graphics: " + ((object)SystemInfo.graphicsDeviceType/*cast due to .constrained prefix*/).ToString() + "; " + SystemInfo.graphicsDeviceVersion);
				stringBuilder.AppendLine("Output: " + displayHost.Capture()?.ToString() + "; fullscreen: " + ((object)Screen.fullScreenMode/*cast due to .constrained prefix*/).ToString());
				stringBuilder.AppendLine("Desktop/current display: " + ((object)Screen.currentResolution/*cast due to .constrained prefix*/).ToString());
				stringBuilder.AppendLine("Native reference: " + NativeWidth + "x" + NativeHeight);
				stringBuilder.AppendLine("Mode: " + RuntimeMode.ToString() + "; scale: " + RenderScale + "; mipmaps: " + Mipmapped + "; UI compensation: " + CompensateUi);
				stringBuilder.AppendLine("Framebuffer: " + Renderer.Detail + "; owned targets: " + Renderer.Count);
				stringBuilder.AppendLine("UI: " + Ui.Detail);
				stringBuilder.AppendLine("Display transaction: " + display.Phase.ToString() + "; " + notice);
				stringBuilder.AppendLine("Cameras:");
				Camera[] allCameras = Camera.allCameras;
				foreach (Camera val in allCameras)
				{
					stringBuilder.AppendLine("  " + ((Object)val).name + "; depth " + val.depth + "; target " + (Object.op_Implicit((Object)(object)val.targetTexture) ? (((Texture)val.targetTexture).width + "x" + ((Texture)val.targetTexture).height) : "screen") + "; " + ((object)val.actualRenderingPath/*cast due to .constrained prefix*/).ToString());
				}
				stringBuilder.AppendLine("BepInEx plugins:");
				foreach (PluginInfo value in Chainloader.PluginInfos.Values)
				{
					stringBuilder.AppendLine("  " + value.Metadata.GUID + " " + value.Metadata.Version);
				}
				stringBuilder.AppendLine("Available higher display modes:");
				foreach (DisplayMode item in RenderMath.HigherModes(UnityDisplayHost.Available(), NativeWidth, NativeHeight))
				{
					stringBuilder.AppendLine("  " + item?.ToString() + " [" + item.Key + "]");
				}
				File.WriteAllText(Path.Combine(Paths.ConfigPath, "mods.valheim.supersampling.diagnostics.txt"), stringBuilder.ToString());
				notice = "Saved BepInEx/config/mods.valheim.supersampling.diagnostics.txt";
			}
			catch (Exception ex)
			{
				notice = "Could not save diagnostics: " + ex.Message;
			}
		}
	}
	internal sealed class UnityDisplayHost : IDisplayHost
	{
		private readonly string path;

		public UnityDisplayHost(string path)
		{
			this.path = path;
		}

		public DisplayMode Capture()
		{
			//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)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected I4, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Resolution currentResolution = Screen.currentResolution;
			RefreshRate refreshRateRatio = ((Resolution)(ref currentResolution)).refreshRateRatio;
			return new DisplayMode
			{
				Width = Screen.width,
				Height = Screen.height,
				Fullscreen = (int)Screen.fullScreenMode,
				RateNumerator = refreshRateRatio.numerator,
				RateDenominator = refreshRateRatio.denominator
			};
		}

		public void Set(DisplayMode mode)
		{
			//IL_002a: 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)
			if (mode == null || !mode.Valid)
			{
				throw new ArgumentException("Invalid resolution.");
			}
			Screen.SetResolution(mode.Width, mode.Height, (FullScreenMode)mode.Fullscreen, new RefreshRate
			{
				numerator = mode.RateNumerator,
				denominator = ((mode.RateDenominator == 0) ? 1u : mode.RateDenominator)
			});
		}

		public void SaveRecovery(DisplayMode original)
		{
			if (original == null || !original.Valid)
			{
				throw new ArgumentException("Current display settings could not be captured.");
			}
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			File.WriteAllText(path, JsonUtility.ToJson((object)original, true));
		}

		public void ClearRecovery()
		{
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}

		public DisplayMode ReadRecovery()
		{
			if (!File.Exists(path))
			{
				return null;
			}
			DisplayMode displayMode = JsonUtility.FromJson<DisplayMode>(File.ReadAllText(path));
			if (displayMode == null || !displayMode.Valid)
			{
				throw new InvalidDataException("Saved display recovery data is invalid; automatic switching was skipped.");
			}
			return displayMode;
		}

		public static List<DisplayMode> Available()
		{
			return Screen.resolutions.Select((Resolution x) => new DisplayMode
			{
				Width = ((Resolution)(ref x)).width,
				Height = ((Resolution)(ref x)).height,
				Fullscreen = 0,
				RateNumerator = ((Resolution)(ref x)).refreshRateRatio.numerator,
				RateDenominator = ((Resolution)(ref x)).refreshRateRatio.denominator
			}).ToList();
		}
	}
	internal sealed class UiCompensation
	{
		public bool Supported { get; private set; }

		public string Detail { get; private set; } = "UI adapter unavailable.";

		private static bool Active
		{
			get
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					return Plugin.Instance.CompensateUi;
				}
				return false;
			}
		}

		public void Install(Harmony harmony, Plugin plugin)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			MethodInfo methodInfo = null;
			try
			{
				methodInfo = (MethodInfo)GameApiContract.UiSize.Resolve((Func<string, Type>)AccessTools.TypeByName);
				MethodInfo methodInfo2 = methodInfo;
				HarmonyMethod val = new HarmonyMethod(typeof(UiCompensation), "UseReferenceSize", (Type[])null);
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(UiCompensation), "ScaleResult", (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null);
				Supported = true;
				Detail = "Native UI size compensation ready.";
			}
			catch (Exception ex)
			{
				Supported = false;
				plugin.RemoveOwnPatches(methodInfo);
				Detail = "UI compensation unavailable: " + ex.GetBaseException().Message;
				plugin.LogWarning(Detail);
			}
		}

		public static int ReferenceWidth()
		{
			if (!Active)
			{
				return Screen.width;
			}
			return Plugin.Instance.NativeWidth;
		}

		public static int ReferenceHeight()
		{
			if (!Active)
			{
				return Screen.height;
			}
			return Plugin.Instance.NativeHeight;
		}

		private static IEnumerable<CodeInstruction> UseReferenceSize(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Screen), "width");
			MethodInfo methodInfo2 = AccessTools.PropertyGetter(typeof(Screen), "height");
			int num = 0;
			int num2 = 0;
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, methodInfo))
				{
					item.opcode = OpCodes.Call;
					item.operand = AccessTools.Method(typeof(UiCompensation), "ReferenceWidth", (Type[])null, (Type[])null);
					num++;
				}
				else if (CodeInstructionExtensions.Calls(item, methodInfo2))
				{
					item.opcode = OpCodes.Call;
					item.operand = AccessTools.Method(typeof(UiCompensation), "ReferenceHeight", (Type[])null, (Type[])null);
					num2++;
				}
			}
			if (num == 0 || num2 == 0)
			{
				throw new NotSupportedException("The game's UI sizing method has changed.");
			}
			return list;
		}

		private static void ScaleResult(ref float __result)
		{
			if (Active)
			{
				__result *= RenderMath.UiRatio(Screen.height, Plugin.Instance.NativeHeight, Plugin.Instance.UiAdjustment);
			}
		}
	}
}