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;
}
}
}