Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Monstrum Mac v1.0.0
plugins/Monstrum-Mac.dll
Decompiled a day agousing 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)); } } } }