Decompiled source of Too Many Hats v2.1.3

TooManyHats.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TooManyHats.Helpers;
using TooManyHats.Patches;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("TooManyHats")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3")]
[assembly: AssemblyProduct("TooManyHats")]
[assembly: AssemblyTitle("TooManyHats")]
[assembly: AssemblyVersion("1.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TooManyHats
{
	[BepInPlugin("TeddyBRB.toomanyhats", "Too Many Hats", "2.1.3")]
	public class Plugin : BaseUnityPlugin
	{
		public class HatEntry
		{
			public string Name;

			public GameObject Prefab;

			public Texture2D Icon;

			public HatEntry(string name, GameObject prefab, Texture2D icon)
			{
				Name = name;
				Prefab = prefab;
				Icon = icon;
			}
		}

		public static AssetBundle assetBundle;

		public static List<HatEntry> hats;

		internal static ManualLogSource Logger;

		internal static Harmony _harmony;

		public static int BaseHatCount { get; set; }

		public static int OverrideHatCount { get; set; }

		public void Awake()
		{
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			BaseHatCount = 0;
			OverrideHatCount = 0;
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			string text = Path.Combine(directoryName, "toomanyhats");
			Logger.LogInfo((object)("[TooManyHats] Loading AssetBundle from: " + text));
			if (!File.Exists(text))
			{
				Logger.LogError((object)("[TooManyHats] AssetBundle file not found: " + text));
				return;
			}
			byte[] array = File.ReadAllBytes(text);
			assetBundle = AssetBundle.LoadFromMemory(array);
			if ((Object)(object)assetBundle == (Object)null)
			{
				Logger.LogError((object)"[TooManyHats] Failed to load AssetBundle!");
				return;
			}
			hats = new List<HatEntry>();
			AddHatIfValid("mario");
			AddHatIfValid("luigi");
			AddHatIfValid("luffy");
			AddHatIfValid("kitty");
			AddHatIfValid("kitdy");
			AddHatIfValid("cat");
			AddHatIfValid("bucket");
			AddHatIfValid("construction");
			AddHatIfValid("crown");
			AddHatIfValid("cowboy");
			AddHatIfValid("pirate");
			AddHatIfValid("pilot");
			AddHatIfValid("rat");
			AddHatIfValid("sleepy");
			AddHatIfValid("elephant");
			AddHatIfValid("froggy");
			AddHatIfValid("kirby");
			AddHatIfValid("cinnamon");
			AddHatIfValid("bunny");
			AddHatIfValid("freeze");
			AddHatIfValid("spongebob");
			AddHatIfValid("longboy");
			AddHatIfValid("pikachu");
			AddHatIfValid("link");
			AddHatIfValid("blindfold");
			Logger.LogInfo((object)$"[TooManyHats] Loaded {hats.Count} valid hats.");
			_harmony = new Harmony("TeddyBRB.toomanyhats");
			_harmony.PatchAll(typeof(PassportManagerPatch));
			_harmony.PatchAll(typeof(CharacterCustomizationPatch));
			_harmony.PatchAll(typeof(CharacterCustomizationDataPatch));
			_harmony.PatchAll(typeof(PlayerCustomizationDummyPatch));
			_harmony.PatchAll(typeof(PeakHandlePatch));
			_harmony.PatchAll(typeof(PassportButtonPatch));
			Logger.LogInfo((object)"[TooManyHats] Patches applied.");
		}

		private static void AddHatIfValid(string hatName)
		{
			HatEntry hatEntry = LoadHat(hatName);
			if (!((Object)(object)hatEntry.Prefab == (Object)null))
			{
				hats.Add(hatEntry);
			}
		}

		private static HatEntry LoadHat(string hatName)
		{
			Logger.LogInfo((object)("[TooManyHats] Loading hat '" + hatName + "'"));
			GameObject val = assetBundle.LoadAsset<GameObject>("Assets/" + hatName + ".prefab");
			Texture2D val2 = assetBundle.LoadAsset<Texture2D>("Assets/" + hatName + ".png");
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)("[TooManyHats] Missing prefab for hat '" + hatName + "' (Assets/" + hatName + ".prefab)"));
				return null;
			}
			if ((Object)(object)val2 == (Object)null)
			{
				Logger.LogWarning((object)("[TooManyHats] Missing icon for hat '" + hatName + "' (Assets/" + hatName + ".png). It will still load."));
			}
			return new HatEntry(hatName, val, val2);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "TooManyHats";

		public const string PLUGIN_NAME = "TooManyHats";

		public const string PLUGIN_VERSION = "1.1.3";
	}
}
namespace TooManyHats.Patches
{
	public class CharacterCustomizationDataPatch
	{
		[HarmonyPatch(typeof(CharacterCustomizationData), "CorrectValues")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> CorrectValuesTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> codes = instructions.ToList();
			for (int i = 0; i < codes.Count - 2; i++)
			{
				if (codes[i].opcode == OpCodes.Ldarg_0 && codes[i + 1].opcode == OpCodes.Ldfld && codes[i + 1].operand.ToString().Contains("currentHat") && codes[i + 2].opcode == OpCodes.Call && codes[i + 2].operand.ToString().Contains("get_Instance") && codes[i + 3].opcode == OpCodes.Ldfld && codes[i + 3].operand.ToString().Contains("hats") && codes[i + 4].opcode == OpCodes.Ldlen && codes[i + 5].opcode == OpCodes.Conv_I4)
				{
					yield return codes[i];
					yield return codes[i + 1];
					yield return codes[i + 2];
					yield return codes[i + 3];
					yield return codes[i + 4];
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Property(typeof(Plugin), "OverrideHatCount").GetGetMethod(nonPublic: true));
					yield return new CodeInstruction(OpCodes.Add, (object)null);
					yield return codes[i + 5];
					i += 5;
				}
				else
				{
					yield return codes[i];
				}
			}
			for (int k = codes.Count - 2; k < codes.Count; k++)
			{
				if (k >= 0)
				{
					yield return codes[k];
				}
			}
		}
	}
	[HarmonyPatch]
	public static class CharacterCustomizationPatch
	{
		private static Shader _characterShader;

		private static int GetOverrideHatCount()
		{
			return Plugin.OverrideHatCount;
		}

		[HarmonyPatch(typeof(CharacterCustomization), "Awake")]
		[HarmonyPostfix]
		public static void AwakePostfix(CharacterCustomization __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)_characterShader == (Object)null)
			{
				_characterShader = Shader.Find("W/Character");
			}
			Transform val = null;
			try
			{
				val = ((Component)__instance).transform.GetChild(0).GetChild(0).GetChild(0)
					.GetChild(2)
					.GetChild(0)
					.GetChild(0)
					.GetChild(1)
					.GetChild(1);
			}
			catch
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogError((object)"[TooManyHats] Could not find hats container!");
				return;
			}
			if ((Object)(object)__instance.refs == (Object)null)
			{
				Plugin.Logger.LogError((object)"[TooManyHats] CharacterCustomization.refs is null!");
				return;
			}
			List<Renderer> list = new List<Renderer>((IEnumerable<Renderer>)(((object)__instance.refs.playerHats) ?? ((object)new Renderer[0])));
			if (Plugin.hats == null || Plugin.hats.Count == 0)
			{
				Plugin.Logger.LogWarning((object)"[TooManyHats] No hats to instantiate in CharacterCustomizationPatch.");
				__instance.refs.playerHats = list.ToArray();
				return;
			}
			foreach (Plugin.HatEntry hat in Plugin.hats)
			{
				if (hat == null || (Object)(object)hat.Prefab == (Object)null)
				{
					Plugin.Logger.LogWarning((object)"[TooManyHats] Skipping null hat/prefab.");
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(hat.Prefab, val, false);
				Renderer componentInChildren = val2.GetComponentInChildren<Renderer>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					Plugin.Logger.LogError((object)("[TooManyHats] No Renderer on hat '" + hat.Name + "'"));
					Object.Destroy((Object)(object)val2);
					continue;
				}
				if ((Object)(object)_characterShader != (Object)null)
				{
					Material[] materials = componentInChildren.materials;
					for (int i = 0; i < materials.Length; i++)
					{
						if ((Object)(object)materials[i] != (Object)null)
						{
							materials[i].shader = _characterShader;
						}
					}
					componentInChildren.materials = materials;
				}
				else
				{
					Plugin.Logger.LogWarning((object)"[TooManyHats] Shader 'W/Character' not found.");
				}
				((Component)componentInChildren).gameObject.SetActive(false);
				((Object)componentInChildren).name = hat.Name;
				list.Add(componentInChildren);
				Plugin.Logger.LogDebug((object)("[TooManyHats] Added hat '" + hat.Name + "' to playerHats"));
			}
			__instance.refs.playerHats = list.ToArray();
			Plugin.Logger.LogInfo((object)$"[TooManyHats] CharacterCustomization hats ready. playerHats={__instance.refs.playerHats.Length}");
		}

		[HarmonyPatch(typeof(CharacterCustomization), "SetCharacterHat")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SetCharacterHatTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> codes = instructions.ToList();
			bool patched = false;
			for (int i = 0; i < codes.Count; i++)
			{
				if (i <= codes.Count - 5 && codes[i].opcode == OpCodes.Ldarg_0 && codes[i + 1].opcode == OpCodes.Call && codes[i + 1].operand != null && codes[i + 1].operand.ToString().Contains("get_Instance") && codes[i + 2].opcode == OpCodes.Ldfld && codes[i + 2].operand != null && codes[i + 2].operand.ToString().Contains("hats") && codes[i + 3].opcode == OpCodes.Ldlen && codes[i + 4].opcode == OpCodes.Conv_I4)
				{
					patched = true;
					yield return codes[i];
					yield return codes[i + 1];
					yield return codes[i + 2];
					yield return codes[i + 3];
					yield return codes[i + 4];
					MethodInfo getOverrideHatCount = AccessTools.Method(typeof(CharacterCustomizationPatch), "GetOverrideHatCount", (Type[])null, (Type[])null);
					yield return new CodeInstruction(OpCodes.Call, (object)getOverrideHatCount);
					yield return new CodeInstruction(OpCodes.Add, (object)null);
					i += 4;
				}
				else
				{
					yield return codes[i];
				}
			}
			if (!patched)
			{
				Plugin.Logger.LogWarning((object)"[TooManyHats] SetCharacterHat transpiler did not find hats length pattern.");
			}
		}
	}
	public class PassportButtonPatch
	{
		[HarmonyPatch(typeof(PassportButton), "SetButton")]
		[HarmonyPostfix]
		private static void SetButton(ref int ___currentIndex, CustomizationOption option)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			if (!((Object)(object)option == (Object)null) && (int)option.type == 50 && ___currentIndex >= Plugin.BaseHatCount)
			{
				___currentIndex += Plugin.OverrideHatCount;
			}
		}
	}
	public class PassportManagerPatch
	{
		private static int GetBaseHatCount()
		{
			return Plugin.BaseHatCount;
		}

		private static int GetOverrideHatCount()
		{
			return Plugin.OverrideHatCount;
		}

		private static bool IsStloc(CodeInstruction code)
		{
			return code.opcode == OpCodes.Stloc || code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc_0 || code.opcode == OpCodes.Stloc_1 || code.opcode == OpCodes.Stloc_2 || code.opcode == OpCodes.Stloc_3;
		}

		[HarmonyPatch(typeof(PassportManager), "Awake")]
		[HarmonyPostfix]
		public static void AwakePostfix(PassportManager __instance)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			Customization component = ((Component)__instance).GetComponent<Customization>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Logger.LogError((object)"[TooManyHats] Missing Customization component!");
				return;
			}
			List<CustomizationOption> list = new List<CustomizationOption>((IEnumerable<CustomizationOption>)(((object)component.hats) ?? ((object)new CustomizationOption[0])));
			Plugin.BaseHatCount = list.Count;
			Plugin.OverrideHatCount = 0;
			if (component.fits != null)
			{
				CustomizationOption[] fits = component.fits;
				foreach (CustomizationOption val in fits)
				{
					if ((Object)(object)val != (Object)null && val.overrideHat)
					{
						Plugin.OverrideHatCount++;
					}
				}
			}
			if (Plugin.hats == null || Plugin.hats.Count == 0)
			{
				Plugin.Logger.LogWarning((object)"[TooManyHats] No hats found to inject.");
				component.hats = list.ToArray();
				return;
			}
			foreach (Plugin.HatEntry hat in Plugin.hats)
			{
				if (hat != null)
				{
					CustomizationOption val2 = ScriptableObject.CreateInstance<CustomizationOption>();
					val2.requiredAchievement = (ACHIEVEMENTTYPE)0;
					((Object)val2).name = hat.Name;
					val2.texture = (Texture)(object)hat.Icon;
					val2.type = (Type)50;
					list.Add(val2);
					Plugin.Logger.LogDebug((object)("[TooManyHats] Added hat option '" + hat.Name + "'"));
				}
			}
			component.hats = list.ToArray();
			Plugin.Logger.LogInfo((object)($"[TooManyHats] Awake complete. BaseHatCount={Plugin.BaseHatCount}, " + $"OverrideHatCount={Plugin.OverrideHatCount}, TotalHats={component.hats.Length}"));
		}

		[HarmonyPatch(typeof(PassportManager), "SetActiveButton")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SetActiveButtonTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> codes = instructions.ToList();
			bool patched = false;
			for (int i = 0; i < codes.Count; i++)
			{
				if (i <= codes.Count - 4 && codes[i].opcode == OpCodes.Ldloc_0 && codes[i + 1].opcode == OpCodes.Ldfld && codes[i + 1].operand != null && codes[i + 1].operand.ToString().Contains("customizationData") && codes[i + 2].opcode == OpCodes.Ldfld && codes[i + 2].operand != null && codes[i + 2].operand.ToString().Contains("currentHat") && IsStloc(codes[i + 3]))
				{
					patched = true;
					yield return codes[i];
					yield return codes[i + 1];
					yield return codes[i + 2];
					yield return new CodeInstruction(OpCodes.Dup, (object)null);
					MethodInfo getBaseHatCount = AccessTools.Method(typeof(PassportManagerPatch), "GetBaseHatCount", (Type[])null, (Type[])null);
					yield return new CodeInstruction(OpCodes.Call, (object)getBaseHatCount);
					Label skipLabel = default(Label);
					yield return new CodeInstruction(OpCodes.Ble_S, (object)skipLabel);
					MethodInfo getOverrideHatCount = AccessTools.Method(typeof(PassportManagerPatch), "GetOverrideHatCount", (Type[])null, (Type[])null);
					yield return new CodeInstruction(OpCodes.Call, (object)getOverrideHatCount);
					yield return new CodeInstruction(OpCodes.Sub, (object)null);
					CodeInstruction nop = new CodeInstruction(OpCodes.Nop, (object)null);
					nop.labels.Add(skipLabel);
					yield return nop;
					yield return codes[i + 3];
					i += 3;
				}
				else
				{
					yield return codes[i];
				}
			}
			if (!patched)
			{
				Plugin.Logger.LogWarning((object)"[TooManyHats] SetActiveButton transpiler did not find currentHat pattern.");
			}
		}

		[HarmonyPatch(typeof(PassportManager), "CameraIn")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> CameraInTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand != null && instruction.operand.Equals(1f))
				{
					instruction.operand = 3f;
				}
				yield return instruction;
			}
		}

		[HarmonyPatch(typeof(PassportManager), "CameraOut")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> CameraOutTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand != null && instruction.operand.Equals(1f))
				{
					instruction.operand = 3f;
				}
				yield return instruction;
			}
		}
	}
	public class PeakHandlePatch
	{
		[HarmonyPatch(typeof(PeakHandler), "SetCosmetics")]
		[HarmonyPrefix]
		private static void SetCosmetics(PeakHandler __instance, List<Character> characters)
		{
			if (!CustomizationRefsHelper.SyncCustomHats(__instance.firstCutsceneScout))
			{
				Plugin.Logger.LogError((object)"Something went wrong in PeakHandlePatch [firstCutsceneScout]...");
			}
			for (int i = 0; i < __instance.cutsceneScoutRefs.Count(); i++)
			{
				if (!CustomizationRefsHelper.SyncCustomHats(__instance.cutsceneScoutRefs[i]))
				{
					Plugin.Logger.LogError((object)string.Format("Something went wrong in {0} [cutsceneScoutRefs-{1}]...", "PeakHandlePatch", i));
				}
			}
		}
	}
	public class PlayerCustomizationDummyPatch
	{
		[HarmonyPatch(typeof(PlayerCustomizationDummy), "SetPlayerHat")]
		[HarmonyPrefix]
		private static void SetPlayerHat(PlayerCustomizationDummy __instance, int index)
		{
			if (!CustomizationRefsHelper.SyncCustomHats(__instance.refs))
			{
				Plugin.Logger.LogError((object)"Something went wrong in SetPlayerHat patch...");
			}
		}
	}
}
namespace TooManyHats.Helpers
{
	public class CustomizationRefsHelper
	{
		public const string REF_TO_HATS_PATH = "Armature/Hip/Mid/AimJoint/Torso/Head/Hat";

		public static bool SyncCustomHats(CustomizationRefs dstRefs, CustomizationRefs srcRefs = null)
		{
			if ((Object)(object)srcRefs == (Object)null)
			{
				Character localCharacter = Character.localCharacter;
				CharacterCustomization val = default(CharacterCustomization);
				if (!Object.op_Implicit((Object)(object)localCharacter) || !((Component)localCharacter).TryGetComponent<CharacterCustomization>(ref val))
				{
					Plugin.Logger.LogError((object)"Cannot get [LocalCharacter] or its' [CustomizationRefs] ...");
					return false;
				}
				srcRefs = val.refs;
			}
			Renderer[] playerHats = srcRefs.playerHats;
			Renderer[] playerHats2 = dstRefs.playerHats;
			if (playerHats.Length == playerHats2.Length)
			{
				return true;
			}
			Renderer val2 = playerHats2.FirstOrDefault();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				Plugin.Logger.LogError((object)"Cannot find renders in dstPlayerHats...");
				return false;
			}
			int layer = ((Component)val2).gameObject.layer;
			Transform val3 = ((Component)srcRefs).transform.Find("Armature/Hip/Mid/AimJoint/Torso/Head/Hat");
			Transform val4 = ((Component)dstRefs).transform.Find("Armature/Hip/Mid/AimJoint/Torso/Head/Hat");
			List<Renderer> list = new List<Renderer>(playerHats2);
			for (int i = playerHats2.Length; i < playerHats.Length; i++)
			{
				Transform val5 = ((Component)playerHats[i]).transform;
				while ((Object)(object)val5.parent != (Object)(object)val3)
				{
					val5 = val5.parent;
				}
				GameObject val6 = Object.Instantiate<GameObject>(((Component)val5).gameObject, val4, false);
				((Object)val6).name = ((Object)val5).name;
				Renderer componentInChildren = val6.GetComponentInChildren<Renderer>(true);
				((Component)componentInChildren).gameObject.layer = layer;
				list.Add(componentInChildren);
			}
			dstRefs.playerHats = list.ToArray();
			return true;
		}
	}
}