Decompiled source of Monstrum Mac v1.0.0

plugins/Monstrum-Mac.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Monstrum-Mac")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Monstrum-Mac")]
[assembly: AssemblyTitle("Monstrum-Mac")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MonstrumMac
{
	internal static class Bundles
	{
		private class Id
		{
			public int Bundle;
		}

		private static readonly HashSet<int> mods = new HashSet<int>();

		private static readonly Dictionary<int, string> names = new Dictionary<int, string>();

		private static readonly HashSet<int> noMetal = new HashSet<int>();

		private static readonly Dictionary<int, int> shaderBundle = new Dictionary<int, int>();

		private static readonly Dictionary<int, int> shaderCount = new Dictionary<int, int>();

		private static readonly HashSet<int> seen = new HashSet<int>();

		private static readonly HashSet<int> walked = new HashSet<int>();

		private static readonly ConditionalWeakTable<AssetBundleCreateRequest, Id> creating = new ConditionalWeakTable<AssetBundleCreateRequest, Id>();

		private static readonly ConditionalWeakTable<AssetBundleRequest, Id> loading = new ConditionalWeakTable<AssetBundleRequest, Id>();

		private static string gameData;

		internal static bool NewAssets;

		internal static void Patch(Harmony harmony)
		{
			try
			{
				gameData = Path.GetFullPath(Application.dataPath);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Game data folder unknown, so bundles loaded from files are not tracked: " + ex.Message));
			}
			Postfix(harmony, typeof(AssetBundle), "LoadFromMemory_Internal", "BundleLoaded");
			Postfix(harmony, typeof(AssetBundle), "LoadFromStreamInternal", "BundleLoaded");
			Postfix(harmony, typeof(AssetBundle), "LoadFromFile_Internal", "FileBundleLoaded");
			Postfix(harmony, typeof(AssetBundle), "LoadFromMemoryAsync_Internal", "BundleRequested");
			Postfix(harmony, typeof(AssetBundle), "LoadFromStreamAsyncInternal", "BundleRequested");
			Postfix(harmony, typeof(AssetBundle), "LoadFromFileAsync_Internal", "FileBundleRequested");
			Postfix(harmony, typeof(AssetBundleCreateRequest), "get_assetBundle", "BundleReady");
			Postfix(harmony, typeof(AssetBundle), "LoadAsset_Internal", "AssetLoaded");
			Postfix(harmony, typeof(AssetBundle), "LoadAssetWithSubAssets_Internal", "AssetsLoaded");
			Postfix(harmony, typeof(AssetBundle), "LoadAssetAsync_Internal", "AssetRequested");
			Postfix(harmony, typeof(AssetBundle), "LoadAssetWithSubAssetsAsync_Internal", "AssetRequested");
			Postfix(harmony, typeof(AssetBundleRequest), "GetResult", "AssetReady");
			Postfix(harmony, typeof(AssetBundleRequest), "get_allAssets", "AssetsReady");
		}

		internal static bool IsModMaterial(Material m)
		{
			if ((Object)(object)m != (Object)null)
			{
				return seen.Contains(((Object)m).GetInstanceID());
			}
			return false;
		}

		internal static bool FromMod(Shader s)
		{
			if ((Object)(object)s != (Object)null)
			{
				return shaderBundle.ContainsKey(((Object)s).GetInstanceID());
			}
			return false;
		}

		internal static bool FromNoMetalBundle(Shader s)
		{
			if ((Object)(object)s != (Object)null && shaderBundle.TryGetValue(((Object)s).GetInstanceID(), out var value))
			{
				return noMetal.Contains(value);
			}
			return false;
		}

		internal static void Broken(Shader s)
		{
			if (!((Object)(object)s == (Object)null) && shaderBundle.TryGetValue(((Object)s).GetInstanceID(), out var value) && noMetal.Add(value))
			{
				shaderCount.TryGetValue(value, out var value2);
				names.TryGetValue(value, out var value3);
				Plugin.Log.LogInfo((object)$"Bundle {value3} #{value} has no Metal shaders ({((Object)s).name} failed); treating its {value2} shaders as broken");
			}
		}

		internal static string Describe()
		{
			return $"Mod bundles: {mods.Count} tracked, {noMetal.Count} without Metal; {shaderBundle.Count} shaders and {seen.Count} materials attributed to them";
		}

		private static void Postfix(Harmony harmony, Type type, string target, string postfix)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, target, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)("Not found: " + type.Name + "." + target));
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Bundles), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Plugin.Log.LogDebug((object)("Hooked " + type.Name + "." + target));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static bool IsModFile(object[] args)
		{
			if (gameData == null || args == null || args.Length == 0 || !(args[0] is string { Length: not 0 } text))
			{
				return false;
			}
			string fullPath = Path.GetFullPath(text);
			if (!fullPath.StartsWith(gameData, StringComparison.OrdinalIgnoreCase))
			{
				return fullPath.IndexOf("StreamingAssets", StringComparison.OrdinalIgnoreCase) < 0;
			}
			return false;
		}

		private static void Register(AssetBundle bundle)
		{
			if (!((Object)(object)bundle == (Object)null))
			{
				int instanceID = ((Object)bundle).GetInstanceID();
				if (mods.Add(instanceID))
				{
					names[instanceID] = ((Object)bundle).name;
					Plugin.Log.LogDebug((object)$"Tracking mod bundle {((Object)bundle).name} #{instanceID}");
				}
			}
		}

		private static void BundleLoaded(AssetBundle __result)
		{
			try
			{
				Register(__result);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void FileBundleLoaded(object[] __args, AssetBundle __result)
		{
			try
			{
				if ((Object)(object)__result != (Object)null && IsModFile(__args))
				{
					Register(__result);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void BundleRequested(AssetBundleCreateRequest __result)
		{
			try
			{
				if (__result != null)
				{
					Remember(creating, __result, 0);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void FileBundleRequested(object[] __args, AssetBundleCreateRequest __result)
		{
			try
			{
				if (__result != null && IsModFile(__args))
				{
					Remember(creating, __result, 0);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void BundleReady(AssetBundleCreateRequest __instance, AssetBundle __result)
		{
			try
			{
				if ((Object)(object)__result != (Object)null && creating.TryGetValue(__instance, out var _))
				{
					Register(__result);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void AssetLoaded(AssetBundle __instance, Object __result)
		{
			try
			{
				if (IsMod(__instance, out var id))
				{
					Mark(__result, id);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void AssetsLoaded(AssetBundle __instance, Object[] __result)
		{
			try
			{
				if (IsMod(__instance, out var id))
				{
					Mark(__result, id);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void AssetRequested(AssetBundle __instance, AssetBundleRequest __result)
		{
			try
			{
				if (__result != null && IsMod(__instance, out var id))
				{
					Remember(loading, __result, id);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void AssetReady(AssetBundleRequest __instance, Object __result)
		{
			try
			{
				if (__instance != null && loading.TryGetValue(__instance, out var value))
				{
					Mark(__result, value.Bundle);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void AssetsReady(AssetBundleRequest __instance, Object[] __result)
		{
			try
			{
				if (__instance != null && loading.TryGetValue(__instance, out var value))
				{
					Mark(__result, value.Bundle);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static bool IsMod(AssetBundle bundle, out int id)
		{
			id = ((!((Object)(object)bundle == (Object)null)) ? ((Object)bundle).GetInstanceID() : 0);
			if (id != 0)
			{
				return mods.Contains(id);
			}
			return false;
		}

		private static void Remember<T>(ConditionalWeakTable<T, Id> table, T key, int bundle) where T : class
		{
			table.Remove(key);
			table.Add(key, new Id
			{
				Bundle = bundle
			});
		}

		private static void Mark(object asset, int bundle)
		{
			if (asset == null)
			{
				return;
			}
			Object val = (Object)((asset is Object) ? asset : null);
			if (val != null && val == (Object)null)
			{
				return;
			}
			if (!(asset is Object[] array))
			{
				GameObject val2 = (GameObject)((asset is GameObject) ? asset : null);
				if (val2 == null)
				{
					Component val3 = (Component)((asset is Component) ? asset : null);
					if (val3 == null)
					{
						Material val4 = (Material)((asset is Material) ? asset : null);
						if (val4 == null)
						{
							Shader val5 = (Shader)((asset is Shader) ? asset : null);
							if (val5 != null)
							{
								Attribute(val5, bundle);
							}
						}
						else
						{
							MarkMaterial(val4, bundle);
						}
					}
					else
					{
						Mark(val3.gameObject, bundle);
					}
				}
				else
				{
					if (!walked.Add(((Object)val2).GetInstanceID()))
					{
						return;
					}
					Renderer[] componentsInChildren = val2.GetComponentsInChildren<Renderer>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
						for (int j = 0; j < sharedMaterials.Length; j++)
						{
							MarkMaterial(sharedMaterials[j], bundle);
						}
					}
				}
			}
			else
			{
				Object[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					Mark(array2[i], bundle);
				}
			}
		}

		private static void MarkMaterial(Material m, int bundle)
		{
			if (!((Object)(object)m == (Object)null) && seen.Add(((Object)m).GetInstanceID()))
			{
				NewAssets = true;
				Attribute(m.shader, bundle);
			}
		}

		private static void Attribute(Shader s, int bundle)
		{
			if (!((Object)(object)s == (Object)null))
			{
				int instanceID = ((Object)s).GetInstanceID();
				if (!shaderBundle.ContainsKey(instanceID) && !(((Object)s).name == "Hidden/InternalErrorShader"))
				{
					shaderBundle[instanceID] = bundle;
					shaderCount.TryGetValue(bundle, out var value);
					shaderCount[bundle] = value + 1;
					NewAssets = true;
				}
			}
		}
	}
	internal static class Fallback
	{
		private class StandIn
		{
			public string Original;

			public bool Twin;

			public Shader Used;
		}

		private enum AlphaMode
		{
			None,
			Cutout,
			Blend
		}

		private class Setup
		{
			public string Shader;

			public string[] Keywords;

			public int Queue;

			public Color? Color;

			public Color? Tint;

			public bool HadBlendProps;

			public int Src = 1;

			public int Dst;

			public AlphaMode Mode;
		}

		private class Slice
		{
			private readonly Stopwatch watch = Stopwatch.StartNew();

			private int draws = Probes + AlphaProbes;

			internal bool Full => watch.Elapsed.TotalMilliseconds > ((Probes + AlphaProbes != draws) ? 10.0 : 1.0);

			internal void Next()
			{
				watch.Restart();
				draws = Probes + AlphaProbes;
			}
		}

		private enum Result
		{
			Nothing,
			Moved,
			Missing
		}

		private static readonly Dictionary<int, bool> works = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> errorShaders = new Dictionary<int, bool>();

		private static Dictionary<string, List<Shader>> byName;

		private static float lastRefresh = -100f;

		private static readonly Dictionary<Material, StandIn> standIns = new Dictionary<Material, StandIn>();

		private static readonly HashSet<Material> touched = new HashSet<Material>();

		private static readonly Dictionary<Material, Setup> originals = new Dictionary<Material, Setup>();

		private static Dictionary<string, Material> named = new Dictionary<string, Material>();

		private static readonly Dictionary<string, bool> alphaTests = new Dictionary<string, bool>();

		private static RenderTexture probeTarget;

		private static RenderTexture alphaTarget;

		private static Texture2D probePixels;

		private static Texture2D alphaPixels;

		private static Texture2D clearTexture;

		private static Mesh quad;

		private static Color? backdrop;

		internal static int Probes;

		internal static int AlphaProbes;

		internal static readonly Stopwatch ProbeTime = new Stopwatch();

		internal static readonly Stopwatch AlphaTime = new Stopwatch();

		private const string Additive = "Legacy Shaders/Particles/Additive";

		private const string AlphaBlended = "Legacy Shaders/Particles/Alpha Blended";

		private static readonly HashSet<string> selfTested = new HashSet<string> { "Standard", "Autodesk Interactive", "Particles/Standard Unlit" };

		internal static int LastMoved;

		private const double SliceMs = 1.0;

		private const double TestSliceMs = 10.0;

		private static int namedFrom = -1;

		internal static IEnumerator Sweep()
		{
			int count = 0;
			Slice slice = new Slice();
			Material[] materials = Resources.FindObjectsOfTypeAll<Material>();
			List<Material> missing = null;
			Material[] array = materials;
			foreach (Material mat in array)
			{
				if (slice.Full)
				{
					yield return null;
					slice.Next();
				}
				switch (Check(mat))
				{
				case Result.Moved:
					count++;
					break;
				case Result.Missing:
				{
					List<Material> list = missing;
					if (list == null)
					{
						List<Material> list2;
						missing = (list2 = new List<Material>());
						list = list2;
					}
					list.Add(mat);
					break;
				}
				}
			}
			if ((missing != null || standIns.Values.Any((StandIn v) => v.Twin)) && materials.Length != namedFrom)
			{
				namedFrom = materials.Length;
				Dictionary<string, Material> map = new Dictionary<string, Material>();
				array = materials;
				foreach (Material mat in array)
				{
					if (slice.Full)
					{
						yield return null;
						slice.Next();
					}
					AddNamed(map, mat);
				}
				named = map;
			}
			if (missing != null)
			{
				foreach (Material mat in missing)
				{
					if (slice.Full)
					{
						yield return null;
						slice.Next();
					}
					try
					{
						if ((Object)(object)mat != (Object)null && (Object)(object)mat.shader != (Object)null && IsMissing(mat.shader) && FixMissing(mat))
						{
							count++;
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)ex);
					}
				}
			}
			foreach (KeyValuePair<Material, StandIn> pair in standIns.ToList())
			{
				if (slice.Full)
				{
					yield return null;
					slice.Next();
				}
				try
				{
					if (Upgrade(pair.Key, pair.Value))
					{
						count++;
					}
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogError((object)ex2);
				}
			}
			LastMoved = count;
		}

		private static Result Check(Material mat)
		{
			try
			{
				if ((Object)(object)mat == (Object)null)
				{
					return Result.Nothing;
				}
				Shader shader = mat.shader;
				if ((Object)(object)shader == (Object)null)
				{
					return Result.Nothing;
				}
				if (IsMissing(shader))
				{
					return (!string.IsNullOrEmpty(((Object)mat).name)) ? Result.Missing : Result.Nothing;
				}
				return FixMaterial(mat, shader) ? Result.Moved : Result.Nothing;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
				return Result.Nothing;
			}
		}

		private static void AddNamed(Dictionary<string, Material> map, Material mat)
		{
			try
			{
				if (!((Object)(object)mat == (Object)null) && !((Object)(object)mat.shader == (Object)null) && !touched.Contains(mat) && !Bundles.IsModMaterial(mat) && Works(mat.shader))
				{
					string name = ((Object)mat).name;
					if (!map.ContainsKey(name))
					{
						map[name] = mat;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static bool FixMaterial(Material mat, Shader shader)
		{
			if (Works(shader))
			{
				return false;
			}
			string name = ((Object)shader).name;
			Shader val = Find(name);
			bool flag = (Object)(object)val == (Object)null;
			if (flag)
			{
				if (!Bundles.IsModMaterial(mat) && Proven(shader))
				{
					return false;
				}
				val = FindAny(Substitutes(name, OriginalOf(mat)));
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Shader val2 = Apply(mat, val);
			if (flag)
			{
				standIns[mat] = new StandIn
				{
					Original = name,
					Used = val2
				};
			}
			Plugin.Detail("Material " + ((Object)mat).name + ": " + name + " -> " + ((Object)val2).name + (flag ? " (until the game loads its own copy)" : ""));
			return true;
		}

		private static bool FixMissing(Material mat)
		{
			if (string.IsNullOrEmpty(((Object)mat).name))
			{
				return false;
			}
			if (named.TryGetValue(((Object)mat).name, out var value) && (Object)(object)value != (Object)null)
			{
				OriginalOf(mat);
				CopyTwin(mat, value);
				Plugin.Detail("Material " + ((Object)mat).name + ": shader missing -> copied the game's material (" + ((Object)value.shader).name + ")");
				return true;
			}
			Shader val = FindAny(Guess(((Object)mat).name), "Standard");
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Shader val2 = Apply(mat, val);
			standIns[mat] = new StandIn
			{
				Original = ((Object)mat).name,
				Twin = true,
				Used = val2
			};
			Plugin.Detail("Material " + ((Object)mat).name + ": shader missing -> " + ((Object)val2).name + " (until the game loads its own copy)");
			return true;
		}

		private static bool Upgrade(Material mat, StandIn info)
		{
			if ((Object)(object)mat == (Object)null || (Object)(object)mat.shader != (Object)(object)info.Used)
			{
				standIns.Remove(mat);
				return false;
			}
			if (info.Twin)
			{
				if (!named.TryGetValue(info.Original, out var value) || (Object)(object)value == (Object)null)
				{
					return false;
				}
				CopyTwin(mat, value);
			}
			else
			{
				Shader val = Find(info.Original);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				Apply(mat, val);
			}
			standIns.Remove(mat);
			Plugin.Detail("Material " + ((Object)mat).name + ": now using the game's " + ((Object)mat.shader).name);
			return true;
		}

		private static void CopyTwin(Material mat, Material twin)
		{
			mat.shader = twin.shader;
			mat.CopyPropertiesFromMaterial(twin);
			touched.Add(mat);
		}

		private static Setup OriginalOf(Material mat)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (originals.TryGetValue(mat, out var value))
			{
				return value;
			}
			string name = ((Object)mat.shader).name;
			string text = name.ToLowerInvariant();
			value = new Setup
			{
				Shader = name,
				Keywords = (mat.shaderKeywords ?? new string[0]),
				Queue = mat.renderQueue,
				Color = (mat.HasProperty("_Color") ? new Color?(mat.color) : ((Color?)null)),
				Tint = (mat.HasProperty("_TintColor") ? new Color?(mat.GetColor("_TintColor")) : ((Color?)null))
			};
			if (mat.HasProperty("_SrcBlend") && mat.HasProperty("_DstBlend"))
			{
				value.HadBlendProps = true;
				value.Src = Mathf.RoundToInt(mat.GetFloat("_SrcBlend"));
				value.Dst = Mathf.RoundToInt(mat.GetFloat("_DstBlend"));
			}
			else if (text.Contains("transparent") && !text.Contains("cutout"))
			{
				value.Src = 5;
				value.Dst = 10;
			}
			bool flag = value.Keywords.Contains("_ALPHATEST_ON") || Has(text, "cutout", "groundcover", "grass", "foliage", "leaf", "leaves", "vegetation") || Has(((Object)mat).name, "leaf", "leaves", "grass", "foliage");
			value.Mode = ((value.Dst != 0) ? AlphaMode.Blend : (flag ? AlphaMode.Cutout : AlphaMode.None));
			originals[mat] = value;
			return value;
		}

		internal static string OriginalShader(Material mat)
		{
			if (!((Object)(object)mat != (Object)null) || !originals.TryGetValue(mat, out var value))
			{
				return null;
			}
			return value.Shader;
		}

		private static Shader Apply(Material mat, Shader target)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			Setup setup = OriginalOf(mat);
			List<KeyValuePair<string, Texture>> source = (from n in mat.GetTexturePropertyNames()
				select new KeyValuePair<string, Texture>(n, mat.GetTexture(n)) into p
				where (Object)(object)p.Value != (Object)null
				select p).ToList();
			Color val = (Color)(((??)setup.Color) ?? Color.white);
			target = ForBlending(mat, target, setup);
			mat.shader = target;
			mat.shaderKeywords = setup.Keywords;
			Configure(mat, setup);
			if (setup.Queue > 2500 && mat.renderQueue <= 2500)
			{
				mat.renderQueue = setup.Queue;
			}
			if (mat.HasProperty("_MainTex") && (Object)(object)mat.GetTexture("_MainTex") == (Object)null)
			{
				KeyValuePair<string, Texture> keyValuePair = source.FirstOrDefault((KeyValuePair<string, Texture> p) => Has(p.Key, "albedo", "diffuse", "basecolor", "base", "main", "color", "tex"));
				if ((Object)(object)keyValuePair.Value != (Object)null)
				{
					mat.SetTexture("_MainTex", keyValuePair.Value);
				}
			}
			if (mat.HasProperty("_BumpMap") && (Object)(object)mat.GetTexture("_BumpMap") == (Object)null)
			{
				KeyValuePair<string, Texture> keyValuePair2 = source.FirstOrDefault((KeyValuePair<string, Texture> p) => Has(p.Key, "normal", "bump"));
				if ((Object)(object)keyValuePair2.Value != (Object)null)
				{
					mat.SetTexture("_BumpMap", keyValuePair2.Value);
					mat.EnableKeyword("_NORMALMAP");
				}
			}
			if (mat.HasProperty("_Color"))
			{
				mat.color = val;
			}
			if (mat.HasProperty("_TintColor"))
			{
				if (((Object)target).name != setup.Shader && Has(setup.Shader, "distort"))
				{
					mat.SetColor("_TintColor", Color.clear);
				}
				else
				{
					mat.SetColor("_TintColor", (Color)(((??)setup.Tint) ?? (val * 0.5f)));
				}
			}
			touched.Add(mat);
			return target;
		}

		private static void Configure(Material mat, Setup original)
		{
			if (!original.HadBlendProps && original.Mode != AlphaMode.None && IsStandardLike(mat.shader))
			{
				bool flag = original.Mode == AlphaMode.Cutout;
				SetFloat(mat, "_Mode", flag ? 1 : ((original.Src == 1) ? 3 : 2));
				SetFloat(mat, "_SrcBlend", flag ? 1 : original.Src);
				SetFloat(mat, "_DstBlend", (!flag) ? original.Dst : 0);
				SetFloat(mat, "_ZWrite", flag ? 1 : 0);
				mat.EnableKeyword(flag ? "_ALPHATEST_ON" : ((original.Src == 1) ? "_ALPHAPREMULTIPLY_ON" : "_ALPHABLEND_ON"));
				mat.renderQueue = (flag ? 2450 : 3000);
			}
		}

		private static void SetFloat(Material mat, string name, float value)
		{
			if (mat.HasProperty(name))
			{
				mat.SetFloat(name, value);
			}
		}

		private static Shader ForBlending(Material mat, Shader target, Setup original)
		{
			if (original.Mode == AlphaMode.None || !IsStandardLike(target))
			{
				return target;
			}
			if (HandlesAlpha(target, mat, original))
			{
				return target;
			}
			Shader val = Find("Standard");
			if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)target && HandlesAlpha(val, mat, original))
			{
				return val;
			}
			if (original.Mode == AlphaMode.Cutout)
			{
				return target;
			}
			return FindAny(LegacyParticles(original)) ?? target;
		}

		private static string[] LegacyParticles(Setup original)
		{
			return new string[1] { (original.Dst == 1) ? "Legacy Shaders/Particles/Additive" : "Legacy Shaders/Particles/Alpha Blended" };
		}

		private static bool IsStandardLike(Shader s)
		{
			if (!((Object)s).name.StartsWith("Standard") && !(((Object)s).name == "Autodesk Interactive"))
			{
				return ((Object)s).name.StartsWith("Particles/Standard");
			}
			return true;
		}

		private static bool Has(string s, params string[] words)
		{
			s = s.ToLowerInvariant();
			return words.Any((string w) => s.Contains(w));
		}

		private static string[] Substitutes(string name, Setup original)
		{
			string text = name.ToLowerInvariant();
			if (text.Contains("distort"))
			{
				return new string[1] { "Legacy Shaders/Particles/Additive" };
			}
			if (text.StartsWith("particles/standard surface"))
			{
				return new string[1] { "Particles/Standard Surface2" }.Concat(ParticlesFor(text, original)).ToArray();
			}
			if (text.StartsWith("particles/standard unlit"))
			{
				return new string[1] { "Particles/Standard Unlit2" }.Concat(ParticlesFor(text, original)).ToArray();
			}
			if (text.Contains("particle") || text.Contains("hovl"))
			{
				return ParticlesFor(text, original);
			}
			if (text.Contains("unlit") && Has(text, "add", "glow", "blend", "transparent"))
			{
				return ParticlesFor(text, original);
			}
			if (!text.Contains("double") && !text.Contains("2-sided") && !text.Contains("twosided") && !text.Contains("two sided"))
			{
				if (text.StartsWith("legacy shaders/") && !text.Contains("transparent"))
				{
					return new string[2] { "Legacy Shaders/Diffuse", "Standard" };
				}
				return new string[1] { "Standard" };
			}
			return new string[2] { "Standard TwoSided", "Standard" };
		}

		private static string[] ParticlesFor(string n, Setup original)
		{
			if (original.HadBlendProps && original.Mode == AlphaMode.Blend)
			{
				return LegacyParticles(original);
			}
			if (!n.Contains("blend"))
			{
				return new string[1] { (n.Contains("add") || n.Contains("glow")) ? "Legacy Shaders/Particles/Additive" : "Legacy Shaders/Particles/Alpha Blended" };
			}
			return new string[1] { "Legacy Shaders/Particles/Alpha Blended" };
		}

		private static string Guess(string name)
		{
			if (Has(name, "spark", "fire", "flame", "glow", "ember", "flare", "light", "magic", "beam", "trail"))
			{
				return "Legacy Shaders/Particles/Additive";
			}
			if (Has(name, "smoke", "fog", "dust", "blob", "mist", "steam", "splash", "particle", "cloud"))
			{
				return "Legacy Shaders/Particles/Alpha Blended";
			}
			return "Standard";
		}

		internal static bool Works(Shader s)
		{
			if (!Bundles.FromNoMetalBundle(s))
			{
				return Proven(s);
			}
			return false;
		}

		private static bool IsMissing(Shader s)
		{
			int instanceID = ((Object)s).GetInstanceID();
			if (!errorShaders.TryGetValue(instanceID, out var value))
			{
				value = (errorShaders[instanceID] = ((Object)s).name == "Hidden/InternalErrorShader");
			}
			return value;
		}

		private static bool Proven(Shader s)
		{
			int instanceID = ((Object)s).GetInstanceID();
			if (works.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			string name = ((Object)s).name;
			value = s.isSupported && !IsMissing(s);
			if (value && !name.StartsWith("Hidden/"))
			{
				if (DrawsPink(s))
				{
					value = false;
					Plugin.Log.LogInfo((object)("Shader " + name + " says it runs on Metal but draws pink"));
				}
				else if (selfTested.Contains(name) && Bundles.FromMod(s) && SelfTest(s) == false)
				{
					value = false;
					Plugin.Log.LogInfo((object)$"Shader {name} #{instanceID} shows its fallback shader instead of itself");
				}
			}
			works[instanceID] = value;
			if (!value && !IsMissing(s))
			{
				Bundles.Broken(s);
			}
			return value;
		}

		private static bool DrawsPink(Shader s)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0068: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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)
			if ((Object)(object)probeTarget == (Object)null)
			{
				probeTarget = new RenderTexture(4, 4, 24, (RenderTextureFormat)0);
				probePixels = new Texture2D(4, 4, (TextureFormat)4, false);
			}
			ProbeTime.Start();
			Probes++;
			Material val = new Material(s);
			RenderTexture active = RenderTexture.active;
			try
			{
				if (val.passCount == 0)
				{
					return false;
				}
				RenderTexture.active = probeTarget;
				GL.Clear(true, true, Color.clear);
				Graphics.Blit((Texture)(object)Texture2D.whiteTexture, probeTarget, val, 0);
				RenderTexture.active = probeTarget;
				probePixels.ReadPixels(new Rect(0f, 0f, 4f, 4f), 0, 0, false);
				Color pixel = probePixels.GetPixel(2, 2);
				return pixel.r > 0.9f && pixel.g < 0.1f && pixel.b > 0.9f;
			}
			finally
			{
				RenderTexture.active = active;
				Object.Destroy((Object)(object)val);
				ProbeTime.Stop();
			}
		}

		private static bool? SelfTest(Shader s)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Material val = new Material(s);
			try
			{
				val.EnableKeyword("_ALPHABLEND_ON");
				SetFloat(val, "_Mode", 2f);
				SetFloat(val, "_SrcBlend", 5f);
				SetFloat(val, "_DstBlend", 10f);
				SetFloat(val, "_ZWrite", 0f);
				return SeesThrough(val);
			}
			finally
			{
				Object.Destroy((Object)(object)val);
			}
		}

		private static bool HandlesAlpha(Shader shader, Material mat, Setup original)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			string key = string.Format("{0}|{1}|{2}|{3}|{4}|{5}", ((Object)shader).GetInstanceID(), original.Mode, original.Src, original.Dst, original.HadBlendProps, string.Join(" ", from k in Declared(shader, original.Keywords)
				orderby k
				select k));
			if (alphaTests.TryGetValue(key, out var value))
			{
				return value;
			}
			Material val = new Material(mat);
			try
			{
				val.shader = shader;
				string[] keywords = original.Keywords;
				foreach (string text in keywords)
				{
					val.EnableKeyword(text);
				}
				Configure(val, original);
				value = SeesThrough(val) ?? true;
			}
			finally
			{
				Object.Destroy((Object)(object)val);
			}
			alphaTests[key] = value;
			if (!value)
			{
				Plugin.Detail("Shader " + ((Object)shader).name + " has no see-through variant for " + ((Object)mat).name);
			}
			return value;
		}

		private static string[] Declared(Shader shader, string[] keywords)
		{
			//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)
			try
			{
				LocalKeywordSpace space = shader.keywordSpace;
				return keywords.Where(delegate(string k)
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					LocalKeyword val = ((LocalKeywordSpace)(ref space)).FindKeyword(k);
					return ((LocalKeyword)(ref val)).isValid;
				}).ToArray();
			}
			catch (Exception)
			{
				return keywords;
			}
		}

		private static bool? SeesThrough(Material mat)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			AlphaTime.Start();
			AlphaProbes++;
			try
			{
				int num = MainPass(mat);
				if (num < 0)
				{
					return null;
				}
				if ((Object)(object)alphaTarget == (Object)null)
				{
					alphaTarget = new RenderTexture(8, 8, 24, (RenderTextureFormat)0);
					alphaPixels = new Texture2D(8, 8, (TextureFormat)4, false);
					clearTexture = new Texture2D(4, 4, (TextureFormat)4, false);
					clearTexture.SetPixels(Enumerable.Repeat<Color>(new Color(1f, 1f, 1f, 0f), 16).ToArray());
					clearTexture.Apply();
				}
				if (!backdrop.HasValue)
				{
					backdrop = DrawAndRead(null, 0);
				}
				if (!backdrop.HasValue)
				{
					return null;
				}
				Prepare(mat, transparent: true);
				Color? val = DrawAndRead(mat, num);
				Prepare(mat, transparent: false);
				Color? val2 = DrawAndRead(mat, num);
				if (!val.HasValue || !val2.HasValue || Near(val2.Value, backdrop.Value))
				{
					return null;
				}
				return Near(val.Value, backdrop.Value);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("See-through test failed: " + ex.Message));
				return null;
			}
			finally
			{
				AlphaTime.Stop();
			}
		}

		private static void Prepare(Material mat, bool transparent)
		{
			//IL_001e: 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)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0090: 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)
			Color val = (Color)(transparent ? new Color(1f, 1f, 1f, 0f) : Color.white);
			if (mat.HasProperty("_MainTex"))
			{
				mat.SetTexture("_MainTex", (Texture)(object)(transparent ? clearTexture : Texture2D.whiteTexture));
			}
			if (mat.HasProperty("_Color"))
			{
				mat.SetColor("_Color", val);
			}
			if (mat.HasProperty("_TintColor"))
			{
				mat.SetColor("_TintColor", val);
			}
			if (mat.HasProperty("_EmissionColor"))
			{
				mat.SetColor("_EmissionColor", Color.black);
			}
			if (mat.HasProperty("_SpecColor"))
			{
				mat.SetColor("_SpecColor", Color.black);
			}
			SetFloat(mat, "_Glossiness", 0f);
			SetFloat(mat, "_GlossMapScale", 0f);
			SetFloat(mat, "_Metallic", 0f);
		}

		private static Color? DrawAndRead(Material mat, int pass)
		{
			//IL_0026: 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_009c: 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)
			RenderTexture active = RenderTexture.active;
			try
			{
				RenderTexture.active = alphaTarget;
				GL.Clear(true, true, new Color(0.25f, 0.75f, 0.25f, 1f));
				if ((Object)(object)mat != (Object)null)
				{
					GL.PushMatrix();
					GL.LoadOrtho();
					bool num = mat.SetPass(pass);
					if (num)
					{
						Graphics.DrawMeshNow(Quad(), Matrix4x4.identity);
					}
					GL.PopMatrix();
					if (!num)
					{
						return null;
					}
				}
				alphaPixels.ReadPixels(new Rect(0f, 0f, 8f, 8f), 0, 0, false);
				return alphaPixels.GetPixel(4, 4);
			}
			finally
			{
				RenderTexture.active = active;
			}
		}

		private static bool Near(Color a, Color b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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)
			return Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g) + Mathf.Abs(a.b - b.b) < 0.08f;
		}

		private static Mesh Quad()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//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)
			//IL_004c: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_00a4: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: 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_012f: 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)
			if ((Object)(object)quad != (Object)null)
			{
				return quad;
			}
			Mesh val = new Mesh();
			val.vertices = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(0f, 0f, 0f),
				new Vector3(0f, 1f, 0f),
				new Vector3(1f, 1f, 0f),
				new Vector3(1f, 0f, 0f)
			};
			val.uv = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(0f, 0f),
				new Vector2(0f, 1f),
				new Vector2(1f, 1f),
				new Vector2(1f, 0f)
			};
			val.normals = Enumerable.Repeat<Vector3>(new Vector3(0f, 0f, -1f), 4).ToArray();
			val.tangents = Enumerable.Repeat<Vector4>(new Vector4(1f, 0f, 0f, 1f), 4).ToArray();
			val.colors = Enumerable.Repeat<Color>(Color.white, 4).ToArray();
			val.triangles = new int[12]
			{
				0, 1, 2, 0, 2, 3, 0, 2, 1, 0,
				3, 2
			};
			quad = val;
			return quad;
		}

		private static int MainPass(Material mat)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			int result = -1;
			for (int i = 0; i < mat.passCount; i++)
			{
				string text = (mat.GetPassName(i) ?? "").ToUpperInvariant();
				if (!new string[8] { "SHADOW", "META", "SCENE", "PICKING", "DEFERRED", "DELTA", "MOTION", "DEPTH" }.Any(text.Contains))
				{
					string text2;
					try
					{
						ShaderTagId val = mat.shader.FindPassTagValue(i, new ShaderTagId("LightMode"));
						text2 = (((ShaderTagId)(ref val)).name ?? "").ToUpperInvariant();
					}
					catch (Exception)
					{
						text2 = "";
					}
					switch (text2)
					{
					case "FORWARDBASE":
						return i;
					case "":
					case "ALWAYS":
					case "SRPDEFAULTUNLIT":
						result = i;
						break;
					}
				}
			}
			return result;
		}

		private static Shader Find(string name)
		{
			Shader val = Pick(name);
			if ((Object)(object)val == (Object)null && Time.realtimeSinceStartup - lastRefresh > 5f)
			{
				Refresh();
				val = Pick(name);
			}
			return val;
		}

		private static Shader FindAny(params string[] names)
		{
			for (int i = 0; i < names.Length; i++)
			{
				Shader val = Find(names[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Shader Pick(string name)
		{
			if (byName == null || !byName.TryGetValue(name, out var value))
			{
				return null;
			}
			return ((IEnumerable<Shader>)value).FirstOrDefault((Func<Shader, bool>)((Shader s) => (Object)(object)s != (Object)null && !Bundles.FromMod(s) && Works(s))) ?? ((IEnumerable<Shader>)value).FirstOrDefault((Func<Shader, bool>)((Shader s) => (Object)(object)s != (Object)null && Works(s)));
		}

		private static void Refresh()
		{
			lastRefresh = Time.realtimeSinceStartup;
			byName = new Dictionary<string, List<Shader>>();
			Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
			foreach (Shader val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (!byName.TryGetValue(((Object)val).name, out var value))
					{
						value = (byName[((Object)val).name] = new List<Shader>());
					}
					value.Add(val);
				}
			}
		}

		internal static void Audit(string path)
		{
			Dictionary<Shader, List<string>> dictionary = new Dictionary<Shader, List<string>>();
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null))
				{
					if (!dictionary.TryGetValue(val.shader, out var value))
					{
						value = (dictionary[val.shader] = new List<string>());
					}
					value.Add(((Object)val).name);
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine(Bundles.Describe());
			foreach (KeyValuePair<Shader, List<string>> item in dictionary.OrderBy((KeyValuePair<Shader, List<string>> p) => ((Object)p.Key).name))
			{
				Shader key = item.Key;
				string text = (Bundles.FromMod(key) ? "mod" : "game");
				stringBuilder.AppendLine(string.Format("{0}\t#{1}\t{2}\tisSupported={3}\t{4}\t{5} materials: {6}", ((Object)key).name, ((Object)key).GetInstanceID(), text, key.isSupported, State(key), item.Value.Count, string.Join(", ", item.Value.Take(6))));
			}
			stringBuilder.AppendLine();
			Renderer[] array2 = Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0);
			foreach (Renderer val2 in array2)
			{
				array = val2.sharedMaterials;
				foreach (Material val3 in array)
				{
					if ((Object)(object)val3 != (Object)null && (Object)(object)val3.shader != (Object)null && State(val3.shader) != "works")
					{
						stringBuilder.AppendLine("ON SCREEN\t" + PathOf(((Component)val2).transform) + "\t" + ((object)val2).GetType().Name + "\t" + ((Object)val3).name + "\t" + ((Object)val3.shader).name + "\t" + State(val3.shader));
					}
				}
			}
			File.WriteAllText(path, stringBuilder.ToString());
		}

		internal static string State(Shader s)
		{
			if (IsMissing(s))
			{
				return "MISSING";
			}
			if (!works.TryGetValue(((Object)s).GetInstanceID(), out var value))
			{
				return "untested";
			}
			if (value)
			{
				if (!Bundles.FromNoMetalBundle(s))
				{
					return "works";
				}
				return "BROKEN (mod copy, no Metal)";
			}
			return "BROKEN";
		}

		private static string PathOf(Transform t)
		{
			string text = ((Object)t).name;
			while ((Object)(object)(t = t.parent) != (Object)null)
			{
				text = ((Object)t).name + "/" + text;
			}
			return text;
		}
	}
	[BepInPlugin("Monstrum-Mac", "Monstrum Mac", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static string CacheDir;

		private static bool logEachMaterial;

		private float nextSweep = 5f;

		private float interval = 10f;

		private Coroutine sweep;

		private int lastStep;

		private bool sweeping;

		private bool sceneLoaded;

		private void Awake()
		{
			//IL_0092: 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)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Invalid comparison between Unknown and I4
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			CacheDir = Path.Combine(Environment.GetEnvironmentVariable("HOME") ?? "", "Library/Caches/Monstrum-Mac");
			bool value = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the plugin off without uninstalling it").Value;
			logEachMaterial = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogEachMaterial", false, "Write a line to the BepInEx log for every material moved to another shader").Value;
			KeyCode value2 = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Diagnostics", "SnapshotKey", (KeyCode)289, "Writes snapshot.txt and audit.txt in ~/Library/Caches/Monstrum-Mac, listing what is on screen and the shader each material really uses").Value;
			GraphicsDeviceType graphicsDeviceType = SystemInfo.graphicsDeviceType;
			if (!value || (int)graphicsDeviceType != 16)
			{
				Log.LogInfo((object)(value ? $"Graphics API is {graphicsDeviceType}, not Metal, so mod shaders already work here: staying idle" : "Turned off in the config"));
				((Behaviour)this).enabled = false;
				return;
			}
			try
			{
				Directory.CreateDirectory(CacheDir);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not create the cache folder: " + ex.Message));
			}
			Harmony harmony = new Harmony("Monstrum-Mac");
			if (HasConvertedBundles())
			{
				Patch(harmony, "LoadFromStreamInternal", "StreamPrefix");
				Patch(harmony, "LoadFromStreamAsyncInternal", "StreamPrefix");
				Patch(harmony, "LoadFromMemory_Internal", "MemoryPrefix");
				Patch(harmony, "LoadFromMemoryAsync_Internal", "MemoryPrefix");
			}
			else
			{
				Log.LogDebug((object)"No converted bundles in the cache folder, so mod bundles load unchanged");
			}
			Bundles.Patch(harmony);
			Snapshot.Init(value2);
			Log.LogDebug((object)("Cache folder: " + CacheDir));
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		internal static void Detail(string message)
		{
			if (logEachMaterial)
			{
				Log.LogInfo((object)message);
			}
			else
			{
				Log.LogDebug((object)message);
			}
		}

		private static bool HasConvertedBundles()
		{
			try
			{
				return Directory.Exists(CacheDir) && Directory.GetFiles(CacheDir, "*.bundle").Length != 0;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not look for converted bundles: " + ex.Message));
				return true;
			}
		}

		private void Update()
		{
			Snapshot.Update();
			if (sweeping)
			{
				if (Time.frameCount - lastStep < 600)
				{
					return;
				}
				if (sweep != null)
				{
					((MonoBehaviour)this).StopCoroutine(sweep);
				}
				sweeping = false;
			}
			if (sceneLoaded || Bundles.NewAssets)
			{
				nextSweep = Mathf.Min(nextSweep, Time.realtimeSinceStartup + (sceneLoaded ? 2f : 3f));
				interval = 10f;
				sceneLoaded = (Bundles.NewAssets = false);
			}
			if (!(Time.realtimeSinceStartup < nextSweep))
			{
				sweep = ((MonoBehaviour)this).StartCoroutine(RunSweep());
			}
		}

		private IEnumerator RunSweep()
		{
			sweeping = true;
			lastStep = Time.frameCount;
			try
			{
				Stopwatch busy = new Stopwatch();
				int frames = 0;
				int probes = Fallback.Probes;
				int alphaProbes = Fallback.AlphaProbes;
				long probeMs = Fallback.ProbeTime.ElapsedMilliseconds;
				long alphaMs = Fallback.AlphaTime.ElapsedMilliseconds;
				IEnumerator steps = Fallback.Sweep();
				while (true)
				{
					busy.Start();
					bool flag;
					try
					{
						flag = steps.MoveNext();
					}
					catch (Exception ex)
					{
						Log.LogError((object)ex);
						flag = false;
					}
					busy.Stop();
					frames++;
					lastStep = Time.frameCount;
					if (!flag)
					{
						break;
					}
					yield return null;
				}
				int lastMoved = Fallback.LastMoved;
				interval = ((lastMoved > 0) ? 10f : Mathf.Min(interval * 2f, 60f));
				nextSweep = Time.realtimeSinceStartup + interval;
				if (lastMoved > 0)
				{
					Log.LogInfo((object)$"Moved {lastMoved} materials to a working shader ({busy.ElapsedMilliseconds} ms of work over {frames} frames)");
				}
				else
				{
					Log.LogDebug((object)$"Sweep: nothing to move ({busy.ElapsedMilliseconds} ms of work over {frames} frames)");
				}
				if (Fallback.Probes > probes || Fallback.AlphaProbes > alphaProbes)
				{
					Log.LogInfo((object)$"Test-drew {Fallback.Probes - probes} shaders ({Fallback.ProbeTime.ElapsedMilliseconds - probeMs} ms) and {Fallback.AlphaProbes - alphaProbes} see-through setups ({Fallback.AlphaTime.ElapsedMilliseconds - alphaMs} ms)");
				}
			}
			finally
			{
				sweeping = false;
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			sceneLoaded = true;
		}

		private void OnApplicationQuit()
		{
			if (!((Behaviour)this).enabled)
			{
				return;
			}
			try
			{
				Fallback.Audit(Path.Combine(CacheDir, "audit.txt"));
			}
			catch (Exception ex)
			{
				Log.LogError((object)ex);
			}
		}

		private static void Patch(Harmony harmony, string target, string prefix)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(AssetBundle), target, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)("Not found: AssetBundle." + target));
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogDebug((object)("Hooked AssetBundle." + target));
		}

		internal static byte[] Lookup(byte[] original)
		{
			string text;
			using (SHA256 sHA = SHA256.Create())
			{
				text = BitConverter.ToString(sHA.ComputeHash(original)).Replace("-", "").ToLowerInvariant();
			}
			string path = Path.Combine(CacheDir, text + ".bundle");
			if (!File.Exists(path))
			{
				Log.LogDebug((object)("No converted copy for bundle " + text.Substring(0, 12)));
				return null;
			}
			Log.LogInfo((object)("Using converted bundle " + text.Substring(0, 12)));
			return File.ReadAllBytes(path);
		}

		private static void StreamPrefix(ref Stream stream)
		{
			try
			{
				if (stream != null && stream.CanRead && stream.CanSeek)
				{
					long position = stream.Position;
					MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					stream.Position = position;
					byte[] array = Lookup(memoryStream.ToArray());
					if (array != null)
					{
						stream = new MemoryStream(array, writable: false);
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)ex);
			}
		}

		private static void MemoryPrefix(ref byte[] binary)
		{
			try
			{
				if (binary != null)
				{
					byte[] array = Lookup(binary);
					if (array != null)
					{
						binary = array;
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)ex);
			}
		}
	}
	internal static class Snapshot
	{
		private const float Radius = 40f;

		private const int MaxRenderers = 3000;

		private static KeyCode key = (KeyCode)289;

		private static Func<bool> pressed;

		private static float nextFileCheck;

		internal static void Init(KeyCode snapshotKey)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			key = snapshotKey;
			pressed = null;
		}

		internal static void Update()
		{
			try
			{
				bool flag = KeyDown();
				if (!flag && Time.realtimeSinceStartup >= nextFileCheck)
				{
					nextFileCheck = Time.realtimeSinceStartup + 2f;
					string path = Path.Combine(Plugin.CacheDir, "snapshot");
					if (File.Exists(path))
					{
						File.Delete(path);
						flag = true;
					}
				}
				if (flag)
				{
					Write();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static bool KeyDown()
		{
			//IL_0000: 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)
			if ((int)key == 0)
			{
				return false;
			}
			if (pressed == null)
			{
				pressed = KeyCheck();
			}
			try
			{
				return pressed();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)$"Snapshot key {key} disabled: {ex.Message}");
				pressed = () => false;
				return false;
			}
		}

		private static Func<bool> KeyCheck()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Input.GetKeyDown(key);
				return () => Input.GetKeyDown(key);
			}
			catch (Exception)
			{
			}
			try
			{
				Type type = Type.GetType("UnityEngine.InputSystem.Keyboard, Unity.InputSystem", throwOnError: true);
				PropertyInfo current = type.GetProperty("current", BindingFlags.Static | BindingFlags.Public);
				string text = ((object)Unsafe.As<KeyCode, KeyCode>(ref key)/*cast due to .constrained prefix*/).ToString();
				PropertyInfo control = type.GetProperty(char.ToLowerInvariant(text[0]) + text.Substring(1) + "Key", BindingFlags.Instance | BindingFlags.Public);
				PropertyInfo down = control.PropertyType.GetProperty("wasPressedThisFrame", BindingFlags.Instance | BindingFlags.Public);
				if (current == null || down == null)
				{
					throw new MissingMemberException("Keyboard." + text);
				}
				return delegate
				{
					object value = current.GetValue(null, null);
					return value != null && (bool)down.GetValue(control.GetValue(value, null), null);
				};
			}
			catch (Exception)
			{
			}
			Plugin.Log.LogWarning((object)$"Snapshot key {key} is not available; create a file named \"snapshot\" in {Plugin.CacheDir} instead");
			return () => false;
		}

		private static void Write()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.Combine(Plugin.CacheDir, "snapshot.txt");
			Camera cam = Camera.main;
			Vector3 origin = (((Object)(object)cam != (Object)null) ? ((Component)cam).transform.position : Vector3.zero);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"Monstrum Mac {Version()} snapshot at {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
			stringBuilder.AppendLine(((Object)(object)cam != (Object)null) ? $"Camera {((Object)cam).name} at {origin}" : "No main camera, listing visible renderers only");
			stringBuilder.AppendLine(Bundles.Describe());
			List<KeyValuePair<Renderer, float>> list = (from p in (from r in Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0)
					where (Object)(object)r != (Object)null
					select r).Select(delegate(Renderer r)
				{
					//IL_0002: 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_0010: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val = origin;
					Bounds bounds = r.bounds;
					return new KeyValuePair<Renderer, float>(r, Vector3.Distance(val, ((Bounds)(ref bounds)).center));
				})
				where p.Key.isVisible || ((Object)(object)cam != (Object)null && p.Value <= 40f)
				orderby p.Value
				select p).ToList();
			int num = Math.Min(list.Count, 3000);
			stringBuilder.AppendLine(string.Format("{0} renderers visible or within {1} m{2}", list.Count, 40f, (list.Count > num) ? $", nearest {num} listed" : ""));
			stringBuilder.AppendLine();
			foreach (KeyValuePair<Renderer, float> item in list.Take(num))
			{
				try
				{
					Append(stringBuilder, item.Key, item.Value);
				}
				catch (Exception ex)
				{
					stringBuilder.AppendLine("    (could not read this renderer: " + ex.Message + ")");
				}
			}
			File.WriteAllText(text, stringBuilder.ToString());
			Plugin.Log.LogInfo((object)$"Saved a snapshot of {num} renderers to {text}");
			try
			{
				Fallback.Audit(Path.Combine(Plugin.CacheDir, "audit.txt"));
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)ex2);
			}
			Show($"Monstrum Mac: snapshot saved ({num} objects)");
		}

		private static void Append(StringBuilder sb, Renderer r, float distance)
		{
			string text = $"{distance,7:0.0} m  {PathOf(((Component)r).transform)}  {((object)r).GetType().Name}  enabled={r.enabled}";
			if (r is ParticleSystemRenderer)
			{
				ParticleSystem component = ((Component)r).GetComponent<ParticleSystem>();
				if ((Object)(object)component != (Object)null)
				{
					text += $"  playing={component.isPlaying} particles={component.particleCount}";
				}
			}
			sb.AppendLine(text);
			Material[] sharedMaterials = r.sharedMaterials;
			foreach (Material mat in sharedMaterials)
			{
				sb.AppendLine("    " + Describe(mat));
			}
		}

		private static string Describe(Material mat)
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mat == (Object)null)
			{
				return "(no material)";
			}
			StringBuilder stringBuilder = new StringBuilder(((Object)mat).name);
			Shader shader = mat.shader;
			if ((Object)(object)shader == (Object)null)
			{
				return stringBuilder.Append("  shader=(none)").ToString();
			}
			stringBuilder.Append($"  shader={((Object)shader).name} #{((Object)shader).GetInstanceID()} {Fallback.State(shader)}");
			if (Bundles.FromMod(shader))
			{
				stringBuilder.Append(" mod copy");
			}
			string text = Fallback.OriginalShader(mat);
			if (text != null)
			{
				stringBuilder.Append("  was " + text);
			}
			stringBuilder.Append($"  queue={mat.renderQueue}");
			string[] shaderKeywords = mat.shaderKeywords;
			if (shaderKeywords != null && shaderKeywords.Length != 0)
			{
				stringBuilder.Append("  kw=" + string.Join(" ", shaderKeywords));
			}
			if (mat.HasProperty("_SrcBlend") && mat.HasProperty("_DstBlend"))
			{
				stringBuilder.Append(string.Format("  blend={0}/{1}", mat.GetFloat("_SrcBlend"), mat.GetFloat("_DstBlend")));
			}
			if (mat.HasProperty("_Color"))
			{
				stringBuilder.Append(string.Format("  color={0}", mat.GetColor("_Color")));
			}
			if (mat.HasProperty("_TintColor"))
			{
				stringBuilder.Append(string.Format("  tint={0}", mat.GetColor("_TintColor")));
			}
			if (mat.HasProperty("_MainTex"))
			{
				Texture texture = mat.GetTexture("_MainTex");
				stringBuilder.Append("  tex=" + (((Object)(object)texture != (Object)null) ? ((Object)texture).name : "none"));
			}
			return stringBuilder.ToString();
		}

		private static string PathOf(Transform t)
		{
			string text = ((Object)t).name;
			while ((Object)(object)(t = t.parent) != (Object)null)
			{
				text = ((Object)t).name + "/" + text;
			}
			return text;
		}

		private static string Version()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			BepInPlugin val = (BepInPlugin)Attribute.GetCustomAttribute(typeof(Plugin), typeof(BepInPlugin));
			if (val == null)
			{
				return "?";
			}
			return val.Version.ToString();
		}

		private static void Show(string text)
		{
			try
			{
				Type type = (from a in AppDomain.CurrentDomain.GetAssemblies()
					where a.GetName().Name == "assembly_valheim"
					select a.GetType("MessageHud")).FirstOrDefault((Type t) => t != null);
				if (type == null)
				{
					return;
				}
				object obj = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null) ?? type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
				if (obj == null)
				{
					return;
				}
				MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo m)
				{
					ParameterInfo[] parameters2 = m.GetParameters();
					return m.Name == "ShowMessage" && parameters2.Length >= 2 && parameters2[0].ParameterType.IsEnum && parameters2[1].ParameterType == typeof(string);
				});
				if (methodInfo == null)
				{
					return;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				object[] array = new object[parameters.Length];
				array[0] = Enum.Parse(parameters[0].ParameterType, "TopLeft");
				array[1] = text;
				for (int num = 2; num < parameters.Length; num++)
				{
					Type parameterType = parameters[num].ParameterType;
					object obj2 = (parameters[num].HasDefaultValue ? parameters[num].DefaultValue : null);
					if (obj2 == null && parameterType.IsValueType)
					{
						obj2 = Activator.CreateInstance(parameterType);
					}
					else if (obj2 != null && parameterType.IsEnum && !parameterType.IsInstanceOfType(obj2))
					{
						obj2 = Enum.ToObject(parameterType, obj2);
					}
					array[num] = obj2;
				}
				methodInfo.Invoke(obj, array);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Could not show the snapshot message: " + ex.Message));
			}
		}
	}
}