Decompiled source of MoreHeadBridge v3.0.0

MoreHeadBridge.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Photon.Pun;
using Photon.Realtime;
using REPOLib;
using REPOLib.Modules;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Xuaun")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Registers MoreHead .hhh cosmetics into the vanilla REPO cosmetics system via REPOLib.")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0+ce7a067d8229aec8619b63065777d71107459760")]
[assembly: AssemblyProduct("MoreHeadBridge")]
[assembly: AssemblyTitle("MoreHeadBridge")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("3.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 MoreHeadBridge
{
	internal static class AtomicJson
	{
		internal static void Write(string path, string json)
		{
			string text = path + ".tmp";
			File.WriteAllText(text, json, Encoding.UTF8);
			try
			{
				if (File.Exists(path))
				{
					File.Replace(text, path, null);
				}
				else
				{
					File.Move(text, path);
				}
			}
			catch
			{
				try
				{
					if (File.Exists(text))
					{
						File.Delete(text);
					}
				}
				catch
				{
				}
				throw;
			}
		}

		internal static Task QueueWrite(Task lastWrite, string path, string json, string errLabel)
		{
			return lastWrite.ContinueWith(delegate
			{
				try
				{
					Write(path, json);
				}
				catch (Exception ex)
				{
					BceConsole.LogWarning(errLabel + ": " + ex.Message);
				}
			}, TaskScheduler.Default);
		}
	}
	internal enum AvatarKind
	{
		Local,
		Menu,
		RemoteMini,
		Remote
	}
	internal readonly struct AvatarIdentity
	{
		public readonly AvatarKind Kind;

		public readonly int Actor;

		public bool IsRemote
		{
			get
			{
				if (Kind != AvatarKind.RemoteMini)
				{
					return Kind == AvatarKind.Remote;
				}
				return true;
			}
		}

		public AvatarIdentity(AvatarKind kind, int actor)
		{
			Kind = kind;
			Actor = actor;
		}

		public static AvatarIdentity Of(PlayerCosmetics instance)
		{
			if (!SemiFunc.IsMultiplayer())
			{
				return new AvatarIdentity(AvatarKind.Local, -1);
			}
			int num = MiniSemibotSpawner.RemoteMiniActorOf(instance);
			if (num > 0)
			{
				return new AvatarIdentity(AvatarKind.RemoteMini, num);
			}
			if (MiniSemibotSpawner.IsMenuOrPreviewWearer(instance.playerAvatarVisuals))
			{
				return new AvatarIdentity(AvatarKind.Menu, -1);
			}
			PhotonView val = ((Object.op_Implicit((Object)(object)instance.deathHead) && instance.deathHead.setup && Object.op_Implicit((Object)(object)instance.deathHead.playerAvatar)) ? instance.deathHead.playerAvatar.photonView : instance.photonView);
			if ((Object)(object)val == (Object)null || val.IsMine)
			{
				return new AvatarIdentity(AvatarKind.Local, -1);
			}
			Player owner = val.Owner;
			int num2 = ((owner != null) ? owner.ActorNumber : (-1));
			if (num2 <= 0)
			{
				return new AvatarIdentity(AvatarKind.Local, -1);
			}
			return new AvatarIdentity(AvatarKind.Remote, num2);
		}

		internal static bool TryGetRemoteActor(PlayerCosmetics instance, out int actorNumber)
		{
			AvatarIdentity avatarIdentity = Of(instance);
			actorNumber = (avatarIdentity.IsRemote ? avatarIdentity.Actor : (-1));
			return avatarIdentity.IsRemote;
		}

		internal static bool IsLocalOrMenu(PlayerCosmetics pc)
		{
			if ((Object)(object)pc == (Object)null)
			{
				return false;
			}
			PlayerAvatarVisuals playerAvatarVisuals = pc.playerAvatarVisuals;
			if (playerAvatarVisuals != null && playerAvatarVisuals.isMenuAvatar)
			{
				return true;
			}
			if ((Object)(object)pc.photonView == (Object)null)
			{
				return true;
			}
			if (pc.photonView.IsMine)
			{
				return true;
			}
			if ((Object)(object)pc.deathHead != (Object)null && pc.deathHead.setup)
			{
				PlayerAvatar playerAvatar = pc.deathHead.playerAvatar;
				if (playerAvatar == null)
				{
					return false;
				}
				PhotonView photonView = playerAvatar.photonView;
				return ((photonView != null) ? new bool?(photonView.IsMine) : ((bool?)null)) == true;
			}
			return false;
		}

		internal static bool IsRemoteMini(PlayerCosmetics? pc)
		{
			return MiniSemibotSpawner.IsRemoteMiniCosmetics(pc);
		}

		internal static bool IsLocalStyleTarget(PlayerCosmetics pc)
		{
			if (IsLocalOrMenu(pc))
			{
				return !IsRemoteMini(pc);
			}
			return false;
		}
	}
	internal static class BceConsole
	{
		private const string InfoPrefix = "[Info   :  MoreHead Bridge] ";

		private const string WarnPrefix = "[Warning:  MoreHead Bridge] ";

		private const string ErrorPrefix = "[Error  :  MoreHead Bridge] ";

		private const string DebugPrefix = "[Debug  :  MoreHead Bridge] ";

		private static readonly Action<string, ConsoleColor>? _writeLineDelegate;

		private static readonly Action<string, ConsoleColor>? _writeDelegate;

		internal static bool IsAvailable => _writeLineDelegate != null;

		static BceConsole()
		{
			Type type = Type.GetType("BCE.console, BCE");
			if (type == null)
			{
				return;
			}
			try
			{
				MethodInfo method = type.GetMethod("WriteLine", new Type[2]
				{
					typeof(string),
					typeof(ConsoleColor)
				});
				MethodInfo method2 = type.GetMethod("Write", new Type[2]
				{
					typeof(string),
					typeof(ConsoleColor)
				});
				if (method != null)
				{
					_writeLineDelegate = (Action<string, ConsoleColor>)Delegate.CreateDelegate(typeof(Action<string, ConsoleColor>), null, method);
				}
				if (method2 != null)
				{
					_writeDelegate = (Action<string, ConsoleColor>)Delegate.CreateDelegate(typeof(Action<string, ConsoleColor>), null, method2);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("BceConsole: delegate creation failed (" + ex.Message + "). BCE output disabled — falling back to BepInEx logger."));
				}
			}
		}

		internal static void WriteLine(string msg, ConsoleColor color)
		{
			_writeLineDelegate?.Invoke(msg, color);
		}

		internal static void Write(string msg, ConsoleColor color)
		{
			_writeDelegate?.Invoke(msg, color);
		}

		internal static void LogInfo(string msg)
		{
			LogInfo(msg, ConsoleColor.Cyan);
		}

		internal static void LogInfo(string msg, ConsoleColor color)
		{
			if (IsAvailable)
			{
				WriteLine("[Info   :  MoreHead Bridge] " + msg, color);
			}
			else
			{
				Plugin.Logger.LogInfo((object)msg);
			}
		}

		internal static void LogWarning(string msg)
		{
			LogWarning(msg, ConsoleColor.Yellow);
		}

		internal static void LogWarning(string msg, ConsoleColor color)
		{
			if (IsAvailable)
			{
				WriteLine("[Warning:  MoreHead Bridge] " + msg, color);
			}
			else
			{
				Plugin.Logger.LogWarning((object)msg);
			}
		}

		internal static void LogError(string msg)
		{
			if (IsAvailable)
			{
				WriteLine("[Error  :  MoreHead Bridge] " + msg, ConsoleColor.Red);
			}
			else
			{
				Plugin.Logger.LogError((object)msg);
			}
		}

		internal static void LogDebug(string msg)
		{
			if (IsAvailable)
			{
				WriteLine("[Debug  :  MoreHead Bridge] " + msg, ConsoleColor.DarkGray);
			}
			else
			{
				Plugin.Logger.LogDebug((object)msg);
			}
		}
	}
	public enum BlacklistLoadMode
	{
		NotLoadIngame,
		LoadOnHiddenMenu
	}
	internal static class BridgeBlacklist
	{
		private sealed class Entry
		{
			public string AssetId { get; set; } = "";

			public string DisplayName { get; set; } = "";
		}

		private sealed class SaveData
		{
			public List<Entry> Entries { get; set; } = new List<Entry>();

			public List<string> MirroredNames { get; set; } = new List<string>();
		}

		private sealed class MoreHeadBlacklistData
		{
			public List<string> DecorationNames { get; set; } = new List<string>();
		}

		private static readonly Dictionary<string, string> _byAssetId = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly HashSet<string> _mirrored = new HashSet<string>(StringComparer.Ordinal);

		private static readonly string SavePath = BridgePaths.Of("Blacklist.json");

		private static readonly string MoreHeadPath = Path.Combine(Paths.ConfigPath, "MoreHeadBlacklist.json");

		private static bool _loaded;

		internal static bool ExistedAtStartup { get; private set; }

		internal static void EnsureLoaded()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			ExistedAtStartup = File.Exists(SavePath);
			if (!ExistedAtStartup)
			{
				return;
			}
			try
			{
				SaveData saveData = JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(SavePath));
				if (saveData == null)
				{
					return;
				}
				foreach (Entry item in saveData.Entries ?? new List<Entry>())
				{
					if (!string.IsNullOrEmpty(item.AssetId))
					{
						_byAssetId[item.AssetId] = item.DisplayName ?? "";
					}
				}
				foreach (string item2 in saveData.MirroredNames ?? new List<string>())
				{
					_mirrored.Add(item2);
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: load failed: " + ex.Message);
			}
		}

		internal static bool Contains(string assetId)
		{
			return _byAssetId.ContainsKey(assetId);
		}

		internal static void Add(string assetId, string displayName)
		{
			if (!string.IsNullOrEmpty(assetId))
			{
				_byAssetId[assetId] = displayName ?? "";
			}
		}

		internal static void SetBlacklisted(string assetId, string? displayName, bool blacklisted)
		{
			EnsureLoaded();
			if (string.IsNullOrEmpty(assetId))
			{
				return;
			}
			string value;
			if (blacklisted)
			{
				if (!_byAssetId.ContainsKey(assetId))
				{
					_byAssetId[assetId] = displayName ?? "";
					Save();
					if (Plugin.MirrorBlacklistToMoreHead.Value)
					{
						MirrorAdd(displayName);
					}
				}
			}
			else if (_byAssetId.TryGetValue(assetId, out value))
			{
				_byAssetId.Remove(assetId);
				Save();
				if (Plugin.MirrorBlacklistToMoreHead.Value)
				{
					MirrorRemove(value);
				}
			}
		}

		internal static HashSet<string> ReadMoreHeadNames()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			try
			{
				if (!File.Exists(MoreHeadPath))
				{
					return hashSet;
				}
				foreach (string item in JsonConvert.DeserializeObject<MoreHeadBlacklistData>(File.ReadAllText(MoreHeadPath))?.DecorationNames ?? new List<string>())
				{
					hashSet.Add(item);
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: reading MoreHead blacklist failed: " + ex.Message);
			}
			return hashSet;
		}

		internal static void Save()
		{
			try
			{
				Directory.CreateDirectory(BridgePaths.DataDir);
				SaveData saveData = new SaveData
				{
					Entries = _byAssetId.Select<KeyValuePair<string, string>, Entry>((KeyValuePair<string, string> kv) => new Entry
					{
						AssetId = kv.Key,
						DisplayName = kv.Value
					}).ToList(),
					MirroredNames = _mirrored.ToList()
				};
				AtomicJson.Write(SavePath, JsonConvert.SerializeObject((object)saveData, (Formatting)1));
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: save failed: " + ex.Message);
			}
		}

		internal static void MirrorToMoreHead()
		{
			if (!Plugin.MirrorBlacklistToMoreHead.Value)
			{
				return;
			}
			try
			{
				HashSet<string> hashSet = ReadMoreHeadNames();
				bool flag = false;
				foreach (string item in _byAssetId.Values.Where((string n) => !string.IsNullOrEmpty(n)))
				{
					if (hashSet.Add(item))
					{
						_mirrored.Add(item);
						flag = true;
					}
				}
				if (flag)
				{
					WriteMoreHead(hashSet);
					Save();
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: mirror failed: " + ex.Message);
			}
		}

		private static void MirrorAdd(string? name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return;
			}
			try
			{
				HashSet<string> hashSet = ReadMoreHeadNames();
				if (hashSet.Add(name))
				{
					_mirrored.Add(name);
					WriteMoreHead(hashSet);
					Save();
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: mirror add failed: " + ex.Message);
			}
		}

		private static void MirrorRemove(string? name)
		{
			if (string.IsNullOrEmpty(name) || !_mirrored.Contains(name))
			{
				return;
			}
			try
			{
				HashSet<string> hashSet = ReadMoreHeadNames();
				if (hashSet.Remove(name))
				{
					WriteMoreHead(hashSet);
				}
				_mirrored.Remove(name);
				Save();
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeBlacklist: mirror remove failed: " + ex.Message);
			}
		}

		private static void WriteMoreHead(HashSet<string> names)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(MoreHeadPath));
			File.WriteAllText(MoreHeadPath, JsonConvert.SerializeObject((object)new MoreHeadBlacklistData
			{
				DecorationNames = names.ToList()
			}, (Formatting)1));
		}
	}
	internal static class BridgeIds
	{
		internal const string Prefix = "morehead-bridge:";

		private static HashSet<CosmeticAsset>? _registeredSet;

		private static int _registeredCount = -1;

		private static HashSet<CosmeticAsset> RegisteredSet()
		{
			IReadOnlyList<CosmeticAsset> registeredCosmetics = Cosmetics.RegisteredCosmetics;
			if (_registeredSet == null || _registeredCount != registeredCosmetics.Count)
			{
				_registeredSet = new HashSet<CosmeticAsset>(registeredCosmetics);
				_registeredCount = registeredCosmetics.Count;
			}
			return _registeredSet;
		}

		internal static bool IsBridgeAsset(string? assetId)
		{
			if (!string.IsNullOrEmpty(assetId))
			{
				return assetId.StartsWith("morehead-bridge:", StringComparison.Ordinal);
			}
			return false;
		}

		internal static bool IsBridgeAsset(CosmeticAsset? asset)
		{
			if ((Object)(object)asset != (Object)null)
			{
				return IsBridgeAsset(asset.assetId);
			}
			return false;
		}

		internal static bool IsModdedCosmetic(CosmeticAsset? asset)
		{
			if ((Object)(object)asset != (Object)null && !IsBridgeAsset(asset))
			{
				return RegisteredSet().Contains(asset);
			}
			return false;
		}

		internal static bool HasAnyNonBridgeModded()
		{
			return Cosmetics.RegisteredCosmetics.Count > 0;
		}

		internal static bool IsCustomizable(CosmeticAsset? asset)
		{
			if (!IsBridgeAsset(asset))
			{
				if (Plugin.AllowModdedOverrides.Value)
				{
					return IsModdedCosmetic(asset);
				}
				return false;
			}
			return true;
		}
	}
	internal static class BridgeLog
	{
		internal static void UserInfo(string msg)
		{
			BceConsole.LogInfo(msg);
		}

		internal static void UserInfo(string msg, ConsoleColor color)
		{
			BceConsole.LogInfo(msg, color);
		}

		internal static void UserWarning(string msg)
		{
			BceConsole.LogWarning(msg);
		}

		internal static void UserWarning(string msg, ConsoleColor color)
		{
			BceConsole.LogWarning(msg, color);
		}

		internal static void UserError(string msg)
		{
			BceConsole.LogError(msg);
		}

		internal static void Debug(string msg)
		{
			if (Plugin.ShowBridgeDebugLogs.Value)
			{
				BceConsole.LogDebug(msg);
			}
		}

		internal static void Trace(string msg)
		{
			Plugin.Logger.LogDebug((object)msg);
		}
	}
	internal static class BridgePaths
	{
		internal static readonly string DataDir = Path.Combine(Paths.ConfigPath, "MoreHeadBridge");

		private static bool _migrated;

		internal static string Of(string fileName)
		{
			return Path.Combine(DataDir, fileName);
		}

		internal static void Init()
		{
			if (_migrated)
			{
				return;
			}
			_migrated = true;
			try
			{
				Directory.CreateDirectory(DataDir);
				string[] files = Directory.GetFiles(Paths.ConfigPath, "MoreHeadBridge_*.json");
				foreach (string text in files)
				{
					string text2 = Path.Combine(DataDir, Path.GetFileName(text).Substring("MoreHeadBridge_".Length));
					if (!File.Exists(text2))
					{
						File.Move(text, text2);
					}
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgePaths: could not move legacy save files into MoreHeadBridge/ — " + ex.Message);
			}
		}
	}
	internal sealed class BridgeHideCondition : MonoBehaviour
	{
		private sealed class Target
		{
			internal Transform Transform;

			internal Vector3 BaseScale;

			internal Renderer[] Renderers = Array.Empty<Renderer>();
		}

		private const float CheckInterval = 0.1f;

		private const float HideSpeed = 6f;

		private const float ShowSpeed = 4f;

		private Cosmetic _cosmetic;

		private PlayerCosmetics? _pc;

		private CosmeticHideConfig _config;

		private readonly List<Target> _targets = new List<Target>();

		private float _checkTimer;

		private bool _hidden;

		private float _shownFactor = 1f;

		private bool _renderersDisabled;

		internal void Init(Cosmetic cosmetic, CosmeticHideConfig config)
		{
			_cosmetic = cosmetic;
			_pc = cosmetic.playerCosmetics;
			_config = config;
			CosmeticHideCondition[] componentsInChildren = ((Component)cosmetic).GetComponentsInChildren<CosmeticHideCondition>(true);
			foreach (CosmeticHideCondition val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			_targets.Clear();
			List<Transform> meshParents = cosmetic.meshParents;
			if (meshParents != null && meshParents.Count > 0)
			{
				foreach (Transform meshParent in cosmetic.meshParents)
				{
					AddTarget(meshParent);
				}
			}
			if (_targets.Count == 0)
			{
				Renderer[] componentsInChildren2 = ((Component)cosmetic).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren2)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						AddTarget(((Component)val2).transform);
					}
				}
			}
			((Behaviour)this).enabled = _targets.Count > 0 && config.HasAny;
		}

		private void AddTarget(Transform? t)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)t == (Object)null)
			{
				return;
			}
			foreach (Target target in _targets)
			{
				if ((Object)(object)target.Transform == (Object)(object)t)
				{
					return;
				}
			}
			_targets.Add(new Target
			{
				Transform = t,
				BaseScale = t.localScale,
				Renderers = ((Component)t).GetComponentsInChildren<Renderer>(true)
			});
		}

		private void LateUpdate()
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			if (_targets.Count == 0)
			{
				return;
			}
			_checkTimer -= Time.deltaTime;
			if (_checkTimer <= 0f)
			{
				_checkTimer = 0.1f;
				_hidden = ShouldHide();
			}
			float num = (_hidden ? 0f : 1f);
			float num2 = (_hidden ? 6f : 4f);
			_shownFactor = Mathf.MoveTowards(_shownFactor, num, num2 * Time.deltaTime);
			if (_shownFactor >= 0.999f && !_hidden)
			{
				EnsureRenderers(on: true);
				for (int i = 0; i < _targets.Count; i++)
				{
					Target target = _targets[i];
					if ((Object)(object)target.Transform != (Object)null)
					{
						target.BaseScale = target.Transform.localScale;
					}
				}
				return;
			}
			float num3 = Mathf.SmoothStep(0f, 1f, _shownFactor);
			for (int num4 = _targets.Count - 1; num4 >= 0; num4--)
			{
				Target target2 = _targets[num4];
				if ((Object)(object)target2.Transform == (Object)null)
				{
					_targets.RemoveAt(num4);
				}
				else
				{
					target2.Transform.localScale = target2.BaseScale * num3;
				}
			}
			EnsureRenderers(_shownFactor > 0.001f);
		}

		private void OnDestroy()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Target target in _targets)
			{
				if ((Object)(object)target.Transform != (Object)null)
				{
					target.Transform.localScale = target.BaseScale;
				}
				Renderer[] renderers = target.Renderers;
				foreach (Renderer val in renderers)
				{
					if ((Object)(object)val != (Object)null)
					{
						val.enabled = true;
					}
				}
			}
		}

		private void EnsureRenderers(bool on)
		{
			if (_renderersDisabled == !on)
			{
				return;
			}
			_renderersDisabled = !on;
			foreach (Target target in _targets)
			{
				Renderer[] renderers = target.Renderers;
				foreach (Renderer val in renderers)
				{
					if ((Object)(object)val != (Object)null)
					{
						val.enabled = on;
					}
				}
			}
		}

		private bool ShouldHide()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_pc == (Object)null)
			{
				return false;
			}
			List<Type> whenConditions = _config.WhenConditions;
			if (whenConditions != null && whenConditions.Count > 0)
			{
				foreach (Type whenCondition in _config.WhenConditions)
				{
					if (_pc.ConditionCustomCheck(whenCondition))
					{
						return true;
					}
				}
			}
			List<Pose> whenPoses = _config.WhenPoses;
			if (whenPoses != null && whenPoses.Count > 0)
			{
				PlayerAvatarVisuals playerAvatarVisuals = _pc.playerAvatarVisuals;
				if ((Object)(object)playerAvatarVisuals != (Object)null && _config.WhenPoses.Contains(playerAvatarVisuals.currentPose))
				{
					return true;
				}
			}
			List<CosmeticType> whenTypes = _config.WhenTypes;
			bool flag = whenTypes != null && whenTypes.Count > 0;
			List<string> whenCosmetics = _config.WhenCosmetics;
			bool flag2 = whenCosmetics != null && whenCosmetics.Count > 0;
			if (flag || flag2)
			{
				PlayerAvatarVisuals playerAvatarVisuals2 = _pc.playerAvatarVisuals;
				if ((Object)(object)playerAvatarVisuals2 != (Object)null)
				{
					Cosmetic[] componentsInChildren = ((Component)playerAvatarVisuals2).GetComponentsInChildren<Cosmetic>(false);
					foreach (Cosmetic val in componentsInChildren)
					{
						if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_cosmetic))
						{
							if (flag && _config.WhenTypes.Contains(val.type))
							{
								return true;
							}
							if (flag2 && (Object)(object)val.cosmeticAsset != (Object)null && _config.WhenCosmetics.Contains(((Object)val.cosmeticAsset).name))
							{
								return true;
							}
						}
					}
				}
			}
			return false;
		}
	}
	internal sealed class BridgeLiveBlocked : MonoBehaviour
	{
		private const float CheckInterval = 0.1f;

		private const float BlockedCooldown = 0.25f;

		private const float SwitchDebounce = 0.1f;

		private const float ProbeRadiusMin = 0.03f;

		private const float ProbeRadiusMax = 0.1f;

		private const float SpringStiffness = 120f;

		private const float SpringDamping = 14f;

		private const float SpringKickVelocity = 6f;

		private const float MaxBlockedDuration = 6f;

		private const string GrabHandleColliderName = "Health Grab";

		private PlayerCosmetics? _pc;

		private Transform _target;

		private Transform _anchor;

		private DeathHeadFloorPose _pose;

		private MiniSemibotFollow? _miniFollow;

		private Vector3 _anchorLocalCenter;

		private float _localMaxExtent;

		private bool _valid;

		private LayerMask _layerMask;

		private float _springPos;

		private float _springVel;

		private bool _blocked;

		private float _checkTimer;

		private float _cooldownTimer;

		private float _switchTimer;

		private float _blockedDuration;

		private Vector3 _refPos;

		private Vector3 _refEuler;

		private Vector3 _refScale;

		internal void Init(Cosmetic cosmetic, DeathHeadFloorPose pose)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			_pc = cosmetic.playerCosmetics;
			_target = ((Component)cosmetic).transform;
			_anchor = (Transform)(((Object)(object)_target.parent != (Object)null) ? ((object)_target.parent) : ((object)_target));
			_pose = pose;
			_pose.MigrateLegacy();
			_refPos = _target.localPosition;
			_refEuler = _target.localEulerAngles;
			_refScale = _target.localScale;
			Transform val = _target;
			while ((Object)(object)val != (Object)null && (Object)(object)_miniFollow == (Object)null)
			{
				_miniFollow = ((Component)val).GetComponent<MiniSemibotFollow>();
				val = val.parent;
			}
			if (TryGetWorldBounds(out var bounds))
			{
				Vector3 val2 = ((Bounds)(ref bounds)).center + Vector3.up * ((Bounds)(ref bounds)).extents.y;
				_anchorLocalCenter = _anchor.InverseTransformPoint(val2);
				float num = Mathf.Max(new float[3]
				{
					((Bounds)(ref bounds)).extents.x,
					((Bounds)(ref bounds)).extents.y,
					((Bounds)(ref bounds)).extents.z
				});
				_localMaxExtent = num / Mathf.Max(0.0001f, Mathf.Abs(_anchor.lossyScale.y));
				_valid = num > 0.0001f;
			}
			_layerMask = LayerMask.op_Implicit(LayerMask.op_Implicit(SemiFunc.LayerMaskGetPhysGrabObject()) + LayerMask.GetMask(new string[1] { "Default" }) + LayerMask.GetMask(new string[1] { "Enemy" }));
		}

		private bool IsLiveBody()
		{
			if ((Object)(object)_pc == (Object)null)
			{
				return false;
			}
			PlayerAvatarVisuals playerAvatarVisuals = _pc.playerAvatarVisuals;
			if ((Object)(object)playerAvatarVisuals == (Object)null)
			{
				return false;
			}
			if (!playerAvatarVisuals.isMenuAvatar)
			{
				return true;
			}
			if ((Object)(object)_miniFollow != (Object)null && !_miniFollow.ExpressionPreview && (Object)(object)_miniFollow.WearerVisuals != (Object)null)
			{
				return !MiniSemibotSpawner.IsMenuOrPreviewWearer(_miniFollow.WearerVisuals);
			}
			return false;
		}

		private void LateUpdate()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: 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_027b: Unknown result type (might be due to invalid IL or missing references)
			if (!_valid || _pose == null)
			{
				return;
			}
			if (!_pose.ReactWhenAlive || !IsLiveBody())
			{
				if (_springPos > 0.001f || Mathf.Abs(_springVel) > 0.001f)
				{
					_target.localPosition = _refPos;
					_target.localRotation = Quaternion.Euler(_refEuler);
					_target.localScale = _refScale;
				}
				_springPos = 0f;
				_springVel = 0f;
				_blocked = false;
				_blockedDuration = 0f;
				return;
			}
			if (_cooldownTimer > 0f)
			{
				_cooldownTimer -= Time.deltaTime;
			}
			if (_switchTimer > 0f)
			{
				_switchTimer -= Time.deltaTime;
			}
			bool blocked = _blocked;
			if (_switchTimer <= 0f)
			{
				_checkTimer -= Time.deltaTime;
				if (_checkTimer <= 0f)
				{
					_checkTimer = 0.1f;
					if (CheckBlocked())
					{
						_blocked = true;
						_cooldownTimer = 0.25f;
					}
					else if (_cooldownTimer <= 0f)
					{
						_blocked = false;
					}
				}
			}
			if (_blocked)
			{
				_blockedDuration += Time.deltaTime;
				if (_blockedDuration >= 6f)
				{
					_blocked = false;
					_cooldownTimer = 0f;
					_blockedDuration = 0f;
				}
			}
			else
			{
				_blockedDuration = 0f;
			}
			if (_blocked != blocked)
			{
				_switchTimer = 0.1f;
				_springVel += (_blocked ? 6f : (-6f));
			}
			float num = (_blocked ? 1f : 0f);
			float num2 = (num - _springPos) * 120f - _springVel * 14f;
			_springVel += num2 * Time.deltaTime;
			_springPos += _springVel * Time.deltaTime;
			float num3 = Mathf.Clamp01(_springPos);
			if (num3 <= 0f)
			{
				_refPos = _target.localPosition;
				_refEuler = _target.localEulerAngles;
				_refScale = _target.localScale;
				if (!_blocked && Mathf.Abs(_springVel) < 0.05f)
				{
					_springPos = 0f;
					_springVel = 0f;
				}
			}
			else
			{
				_target.localPosition = Vector3.Lerp(_refPos, new Vector3(_pose.PosX, _pose.PosY, _pose.PosZ), num3);
				_target.localRotation = Quaternion.Slerp(Quaternion.Euler(_refEuler), Quaternion.Euler(_pose.RotX, _pose.RotY, _pose.RotZ), num3);
				_target.localScale = Vector3.Lerp(_refScale, new Vector3(_pose.ScaleX, _pose.ScaleY, _pose.ScaleZ), num3);
			}
		}

		private void OnDestroy()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_target == (Object)null) && (_springPos > 0.001f || Mathf.Abs(_springVel) > 0.001f))
			{
				_target.localPosition = _refPos;
				_target.localRotation = Quaternion.Euler(_refEuler);
				_target.localScale = _refScale;
			}
		}

		private bool CheckBlocked()
		{
			//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)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = _anchor.TransformPoint(_anchorLocalCenter);
			float num = Mathf.Max(0.0001f, Mathf.Abs(_anchor.lossyScale.y));
			float num2 = ((num < 1f) ? Mathf.Sqrt(num) : num);
			float num3 = Mathf.Clamp(_localMaxExtent * num2 * 0.25f, 0.03f * num2, 0.1f * num2);
			Collider[] array = Physics.OverlapSphere(val, num3, LayerMask.op_Implicit(_layerMask), (QueryTriggerInteraction)2);
			Collider[] array2 = array;
			foreach (Collider val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null) && !(((Object)val2).name == "Health Grab") && !((Object)(object)((Component)val2).GetComponentInParent<PlayerDeathHead>() != (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent<PlayerTumble>() != (Object)null))
				{
					return true;
				}
			}
			return false;
		}

		private bool TryGetWorldBounds(out Bounds bounds)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			Renderer[] componentsInChildren = ((Component)_target).GetComponentsInChildren<Renderer>(true);
			bool flag = false;
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.enabled && ((Component)val).gameObject.activeInHierarchy)
				{
					if (!flag)
					{
						bounds = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val.bounds);
					}
				}
			}
			if (flag)
			{
				return true;
			}
			Renderer[] array2 = componentsInChildren;
			foreach (Renderer val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					if (!flag)
					{
						bounds = val2.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val2.bounds);
					}
				}
			}
			return flag;
		}
	}
	internal static class CosmeticEquipAnimation
	{
		private static FieldInfo? _equipLerpField;

		private static FieldInfo? _meshParentsScaleField;

		internal static void Finish(GameObject go)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Cosmetic val = (((Object)(object)go != (Object)null) ? go.GetComponentInChildren<Cosmetic>(true) : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				if ((object)_equipLerpField == null)
				{
					_equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp");
				}
				_equipLerpField?.SetValue(val, 1f);
				if ((object)_meshParentsScaleField == null)
				{
					_meshParentsScaleField = AccessTools.Field(typeof(Cosmetic), "meshParentsScale");
				}
				if (!(_meshParentsScaleField?.GetValue(val) is List<Vector3> list))
				{
					return;
				}
				List<Transform> meshParents = val.meshParents;
				for (int i = 0; i < meshParents.Count && i < list.Count; i++)
				{
					if ((Object)(object)meshParents[i] != (Object)null)
					{
						meshParents[i].localScale = list[i];
					}
				}
			}
			catch (Exception ex)
			{
				BridgeLog.Trace("CosmeticEquipAnimation.Finish failed: " + ex.Message);
			}
		}
	}
	internal static class MultiEquipTypeFlags
	{
		private static readonly Dictionary<CosmeticTypeAsset, bool> _originals = new Dictionary<CosmeticTypeAsset, bool>();

		internal static void Sync()
		{
			if (!Plugin.AllowMultipleCosmetics.Value)
			{
				Restore();
			}
			else
			{
				Apply();
			}
		}

		private static void Apply()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			MetaManager instance = MetaManager.instance;
			if (instance?.cosmeticTypeAssets == null)
			{
				return;
			}
			foreach (CosmeticTypeAsset cosmeticTypeAsset in instance.cosmeticTypeAssets)
			{
				if (!((Object)(object)cosmeticTypeAsset == (Object)null) && MultiEquipTypes.All.Contains(cosmeticTypeAsset.type))
				{
					if (!_originals.ContainsKey(cosmeticTypeAsset))
					{
						_originals[cosmeticTypeAsset] = cosmeticTypeAsset.canEquipMultiple;
					}
					cosmeticTypeAsset.canEquipMultiple = true;
				}
			}
		}

		internal static void Restore()
		{
			if (_originals.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<CosmeticTypeAsset, bool> original in _originals)
			{
				if ((Object)(object)original.Key != (Object)null)
				{
					original.Key.canEquipMultiple = original.Value;
				}
			}
			_originals.Clear();
		}
	}
	internal static class MultiEquipTypes
	{
		internal static readonly HashSet<CosmeticType> All = new HashSet<CosmeticType>
		{
			(CosmeticType)0,
			(CosmeticType)30,
			(CosmeticType)31,
			(CosmeticType)32,
			(CosmeticType)18,
			(CosmeticType)17,
			(CosmeticType)20,
			(CosmeticType)21,
			(CosmeticType)1,
			(CosmeticType)2,
			(CosmeticType)3,
			(CosmeticType)19,
			(CosmeticType)4,
			(CosmeticType)22
		};
	}
	[HarmonyPatch(typeof(MetaManager), "Awake")]
	internal static class MultiEquipFlagsApplyPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			MultiEquipTypeFlags.Sync();
		}
	}
	internal static class CustomizerIO
	{
		internal static string ExportFolder => Path.Combine(Paths.ConfigPath, "MoreHeadBridge");

		internal static string ExportFilePath => Path.Combine(ExportFolder, "overrides_export.json");

		internal static void ExportAll()
		{
			Dictionary<string, CosmeticOverrideData> allData = CustomizerStore.GetAllData();
			if (allData.Count == 0)
			{
				BceConsole.LogInfo("CustomizerIO: no overrides to export.");
			}
			else
			{
				MergeWrite(allData);
			}
		}

		internal static void ExportSingle(string assetId)
		{
			if (!CustomizerStore.TryGet(assetId, out CosmeticOverrideData data))
			{
				BceConsole.LogWarning("CustomizerIO: no saved override for '" + assetId + "' — save first.");
				return;
			}
			MergeWrite(new Dictionary<string, CosmeticOverrideData> { [assetId] = data });
		}

		internal static void ImportMerge()
		{
			try
			{
				if (!File.Exists(ExportFilePath))
				{
					BceConsole.LogWarning("CustomizerIO: export file not found — nothing to import.");
					return;
				}
				string text = File.ReadAllText(ExportFilePath);
				Dictionary<string, CosmeticOverrideData> dictionary = JsonConvert.DeserializeObject<Dictionary<string, CosmeticOverrideData>>(text);
				if (dictionary == null || dictionary.Count == 0)
				{
					BceConsole.LogWarning("CustomizerIO: export file is empty — nothing to import.");
					return;
				}
				CustomizerStore.ImportBatch(dictionary);
				BceConsole.LogInfo($"CustomizerIO: imported {dictionary.Count} override(s) from {ExportFilePath}");
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("CustomizerIO: import failed — " + ex.Message);
			}
		}

		private static void MergeWrite(Dictionary<string, CosmeticOverrideData> incoming)
		{
			try
			{
				Directory.CreateDirectory(ExportFolder);
				Dictionary<string, CosmeticOverrideData> dictionary = new Dictionary<string, CosmeticOverrideData>();
				if (File.Exists(ExportFilePath))
				{
					string text = File.ReadAllText(ExportFilePath);
					Dictionary<string, CosmeticOverrideData> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, CosmeticOverrideData>>(text);
					if (dictionary2 != null)
					{
						dictionary = dictionary2;
					}
				}
				foreach (KeyValuePair<string, CosmeticOverrideData> item in incoming)
				{
					dictionary[item.Key] = item.Value;
				}
				string json = JsonConvert.SerializeObject((object)dictionary, (Formatting)1);
				AtomicJson.Write(ExportFilePath, json);
				BceConsole.LogInfo($"CustomizerIO: exported {incoming.Count} override(s) to {ExportFilePath}");
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("CustomizerIO: export failed — " + ex.Message);
			}
		}
	}
	internal static class BridgeFavoritesManager
	{
		private sealed class SaveData
		{
			public List<string> Favorites { get; set; } = new List<string>();

			public List<string> Hidden { get; set; } = new List<string>();
		}

		private static readonly HashSet<string> _favorites = new HashSet<string>();

		private static readonly HashSet<string> _hidden = new HashSet<string>();

		private static bool _loaded;

		private static readonly string SavePath = BridgePaths.Of("Favorites.json");

		private static Task _lastWrite = Task.CompletedTask;

		internal static void EnsureLoaded()
		{
			if (!_loaded)
			{
				_loaded = true;
				Load();
			}
		}

		internal static bool IsFavorite(CosmeticAsset? asset)
		{
			if ((Object)(object)asset != (Object)null)
			{
				return _favorites.Contains(KeyFor(asset));
			}
			return false;
		}

		internal static bool IsHidden(CosmeticAsset? asset)
		{
			if ((Object)(object)asset != (Object)null)
			{
				return _hidden.Contains(KeyFor(asset));
			}
			return false;
		}

		internal static bool HasAnyFavorite()
		{
			return _favorites.Count > 0;
		}

		internal static bool HasAnyHidden()
		{
			return _hidden.Count > 0;
		}

		internal static bool ToggleFavorite(CosmeticAsset asset)
		{
			string item = KeyFor(asset);
			if (_favorites.Remove(item))
			{
				Save();
				return false;
			}
			_hidden.Remove(item);
			_favorites.Add(item);
			Save();
			return true;
		}

		internal static bool ToggleHidden(CosmeticAsset asset)
		{
			string item = KeyFor(asset);
			if (_hidden.Remove(item))
			{
				Save();
				return false;
			}
			_favorites.Remove(item);
			_hidden.Add(item);
			Save();
			return true;
		}

		internal static void EnsureHidden(CosmeticAsset asset)
		{
			EnsureLoaded();
			string item = KeyFor(asset);
			_favorites.Remove(item);
			if (_hidden.Add(item))
			{
				Save();
			}
		}

		private static void Load()
		{
			try
			{
				if (!File.Exists(SavePath))
				{
					return;
				}
				SaveData saveData = JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(SavePath));
				if (saveData == null)
				{
					return;
				}
				_favorites.Clear();
				_hidden.Clear();
				foreach (string item in saveData.Favorites ?? new List<string>())
				{
					_favorites.Add(item);
				}
				foreach (string item2 in saveData.Hidden ?? new List<string>())
				{
					_hidden.Add(item2);
				}
				BceConsole.LogInfo($"BridgeFavoritesManager: loaded {_favorites.Count} favorite(s), {_hidden.Count} hidden.", ConsoleColor.DarkBlue);
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("BridgeFavoritesManager: load failed: " + ex.Message);
			}
		}

		private static void Save()
		{
			string json = JsonConvert.SerializeObject((object)new SaveData
			{
				Favorites = new List<string>(_favorites),
				Hidden = new List<string>(_hidden)
			}, (Formatting)1);
			_lastWrite = AtomicJson.QueueWrite(_lastWrite, SavePath, json, "BridgeFavoritesManager: save failed");
		}

		internal static void FlushPendingWrites()
		{
			try
			{
				_lastWrite.Wait(TimeSpan.FromSeconds(2.0));
			}
			catch
			{
			}
		}

		private static string KeyFor(CosmeticAsset asset)
		{
			if (!string.IsNullOrEmpty(asset.assetId))
			{
				return asset.assetId;
			}
			if (!string.IsNullOrEmpty(asset.assetName))
			{
				return asset.assetName;
			}
			return ((Object)asset).name ?? "";
		}
	}
	internal static class FavHideIcons
	{
		private const string ResourcePrefix = "MoreHeadBridge.Icons.Resources.";

		private static Sprite? _star;

		private static Sprite? _hide;

		internal static Sprite? StarSprite => _star ?? (_star = LoadSprite("star.png"));

		internal static Sprite? HideSprite => _hide ?? (_hide = LoadSprite("hide.png"));

		private static Sprite? LoadSprite(string fileName)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = "MoreHeadBridge.Icons.Resources." + fileName;
				using Stream stream = typeof(FavHideIcons).Assembly.GetManifestResourceStream(text);
				if (stream == null)
				{
					BceConsole.LogWarning("FavHideIcons: embedded resource '" + text + "' not found");
					return null;
				}
				byte[] array = new byte[stream.Length];
				for (int i = 0; i < array.Length; i += stream.Read(array, i, array.Length - i))
				{
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				((Object)val).name = "MoreHeadBridge_" + fileName;
				((Texture)val).filterMode = (FilterMode)1;
				if (!ImageConversion.LoadImage(val, array))
				{
					BceConsole.LogWarning("FavHideIcons: Texture2D.LoadImage failed for '" + text + "'");
					return null;
				}
				MaskWhitePixels(val);
				Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), (float)((Texture)val).width);
				((Object)val2).name = "MoreHeadBridge_" + fileName;
				return val2;
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("FavHideIcons: error loading '" + fileName + "': " + ex.Message);
				return null;
			}
		}

		private static void MaskWhitePixels(Texture2D tex)
		{
			//IL_000d: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Color32[] pixels = tex.GetPixels32();
			for (int i = 0; i < pixels.Length; i++)
			{
				Color32 val = pixels[i];
				if (val.r > 220 && val.g > 220 && val.b > 220)
				{
					pixels[i].a = 0;
				}
			}
			tex.SetPixels32(pixels);
			tex.Apply();
		}
	}
	internal static class FavHideMarkerHelper
	{
		private const string MarkerName = "MHB_FavHideMarker";

		private const float OffsetX = -7f;

		private const float OffsetY = 7f;

		private const float Size = 9f;

		internal static void UpdateMarker(MenuElementCosmeticButton btn)
		{
			if (!((Object)(object)btn == (Object)null) && !((Object)(object)btn.cosmeticAsset == (Object)null))
			{
				bool isFav = BridgeFavoritesManager.IsFavorite(btn.cosmeticAsset);
				bool isHide = BridgeFavoritesManager.IsHidden(btn.cosmeticAsset);
				UpdateMarker(btn, isFav, isHide);
			}
		}

		internal static void UpdateMarker(MenuElementCosmeticButton btn, bool isFav, bool isHide)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)btn == (Object)null || (Object)(object)btn.cosmeticAsset == (Object)null)
			{
				return;
			}
			Transform val = ((Component)btn).transform.Find("MHB_FavHideMarker");
			if (!isFav && !isHide)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
				return;
			}
			Image val4;
			if ((Object)(object)val == (Object)null)
			{
				GameObject val2 = new GameObject("MHB_FavHideMarker");
				RectTransform val3 = val2.AddComponent<RectTransform>();
				val2.transform.SetParent(((Component)btn).transform, false);
				val2.transform.SetAsLastSibling();
				val3.anchorMin = new Vector2(1f, 0f);
				val3.anchorMax = new Vector2(1f, 0f);
				val3.pivot = new Vector2(1f, 0f);
				ApplyRect(val3);
				val4 = val2.AddComponent<Image>();
				((Graphic)val4).raycastTarget = false;
				val4.preserveAspect = true;
			}
			else
			{
				val.SetAsLastSibling();
				ApplyRect(((Component)val).GetComponent<RectTransform>());
				val4 = ((Component)val).GetComponent<Image>();
			}
			if (!((Object)(object)val4 == (Object)null))
			{
				if (isFav)
				{
					val4.sprite = FavHideIcons.StarSprite;
					((Graphic)val4).color = Color.white;
				}
				else
				{
					val4.sprite = FavHideIcons.HideSprite;
					((Graphic)val4).color = Color.white;
				}
			}
		}

		private static void ApplyRect(RectTransform? rt)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rt == (Object)null))
			{
				rt.anchoredPosition = new Vector2(-7f, 7f);
				rt.sizeDelta = new Vector2(9f, 9f);
			}
		}
	}
	[HarmonyPatch(typeof(MenuElementCosmeticButton), "ToggleCosmetic")]
	[HarmonyPriority(400)]
	internal static class FavHideTogglePatch
	{
		private static MethodInfo? _triggerClickAnimations;

		private static bool _triggerLookupDone;

		private static int _lastShiftFrame = int.MinValue;

		[HarmonyPrefix]
		private static bool Prefix(MenuElementCosmeticButton __instance)
		{
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			VariantCell component = ((Component)__instance).GetComponent<VariantCell>();
			if ((Object)(object)component != (Object)null)
			{
				component.OnClick?.Invoke();
				return false;
			}
			bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			bool flag2 = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			bool flag3 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			if (flag3)
			{
				_lastShiftFrame = Time.frameCount;
			}
			bool flag4 = flag3 || (_lastShiftFrame >= 0 && Time.frameCount - _lastShiftFrame <= 3);
			if (!flag && !flag2 && !flag4)
			{
				CosmeticGroupButton group = ((Component)__instance).GetComponent<CosmeticGroupButton>();
				if ((Object)(object)group != (Object)null && group.IsActive && Plugin.MenuLibAvailable)
				{
					PopupUI.AfterMouseRelease((MonoBehaviour)(object)Plugin.Instance, delegate
					{
						CosmeticVariantPopup.Show(__instance, group);
					});
					return false;
				}
			}
			if (!flag && !flag2 && !flag4)
			{
				return true;
			}
			if ((flag || flag2) && !Plugin.EnableMenuEnhancements.Value)
			{
				return true;
			}
			if ((Object)(object)__instance.menuButton != (Object)null && __instance.menuButton.disabled)
			{
				return true;
			}
			CosmeticAsset asset = __instance.cosmeticAsset;
			if ((Object)(object)asset == (Object)null)
			{
				return true;
			}
			if (flag4 && !flag && !flag2)
			{
				if (Plugin.EnableCosmeticCustomizer.Value && Plugin.MenuLibAvailable && BridgeIds.IsCustomizable(asset))
				{
					bool flag5 = __instance.IsEquipped();
					PopupUI.AfterMouseRelease((MonoBehaviour)(object)Plugin.Instance, delegate
					{
						CosmeticOverridePopup.Show(asset);
					});
					return !flag5;
				}
				return true;
			}
			BridgeFavoritesManager.EnsureLoaded();
			if (flag)
			{
				BridgeFavoritesManager.ToggleFavorite(asset);
			}
			else
			{
				BridgeFavoritesManager.ToggleHidden(asset);
			}
			try
			{
				__instance.soundClick.Play(MenuManager.instance.soundPosition, 1f, 1f, 1f, 1f);
			}
			catch (Exception ex)
			{
				BridgeLog.Trace("FavHideTogglePatch: sound skipped — " + ex.Message);
			}
			if (!_triggerLookupDone)
			{
				_triggerClickAnimations = AccessTools.Method(typeof(MenuElementCosmeticButton), "TriggerClickAnimations", (Type[])null, (Type[])null);
				_triggerLookupDone = true;
			}
			try
			{
				_triggerClickAnimations?.Invoke(__instance, null);
			}
			catch (Exception ex2)
			{
				BridgeLog.Trace("FavHideTogglePatch: animation skipped — " + ex2.Message);
			}
			FavHideMarkerHelper.UpdateMarker(__instance);
			return false;
		}
	}
	internal static class HhhCosmeticLoader
	{
		internal static readonly List<string> RegisteredAssetIds = new List<string>();

		internal static readonly HashSet<string> WorldAssetIds = new HashSet<string>();

		private static readonly Dictionary<string, string> _sourcePath = new Dictionary<string, string>(StringComparer.Ordinal);

		internal static readonly Dictionary<string, Texture2D> BridgeIconTextures = new Dictionary<string, Texture2D>();

		private static readonly Dictionary<string, (CosmeticType VanillaType, OverrideCosmeticType OverrideType)> TagMap = new Dictionary<string, (CosmeticType, OverrideCosmeticType)>
		{
			["head"] = ((CosmeticType)0, OverrideCosmeticType.Hat),
			["neck"] = ((CosmeticType)30, OverrideCosmeticType.HeadBottom),
			["body"] = ((CosmeticType)20, OverrideCosmeticType.BodyTop),
			["hip"] = ((CosmeticType)21, OverrideCosmeticType.BodyBottom),
			["rightarm"] = ((CosmeticType)1, OverrideCosmeticType.ArmRight),
			["leftarm"] = ((CosmeticType)2, OverrideCosmeticType.ArmLeft),
			["rightleg"] = ((CosmeticType)3, OverrideCosmeticType.LegRight),
			["leftleg"] = ((CosmeticType)4, OverrideCosmeticType.LegLeft),
			["world"] = ((CosmeticType)0, OverrideCosmeticType.World)
		};

		private static readonly HashSet<string> ValidTags;

		private static readonly HashSet<string> _usedPrefabIds;

		private static readonly HashSet<string> _usedInternalNames;

		private static bool _seeding;

		private static HashSet<string>? _seedNames;

		private static bool _moreHeadFixDone;

		private static Rarity _lastAppliedRarity;

		private static bool? _lastAppliedTinting;

		private static readonly Dictionary<string, OverrideCosmeticType> _originalTypes;

		private static readonly Dictionary<string, bool> _originalTintable;

		internal static bool IsFromFolder(CosmeticAsset? asset, string folderName)
		{
			if ((Object)(object)asset == (Object)null || string.IsNullOrEmpty(folderName))
			{
				return false;
			}
			if (_sourcePath.TryGetValue(asset.assetId, out string value))
			{
				return value.IndexOf(folderName, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		internal static string? SourceModFolder(CosmeticAsset? asset)
		{
			if ((Object)(object)asset == (Object)null)
			{
				return null;
			}
			if (!_sourcePath.TryGetValue(asset.assetId, out string value) || string.IsNullOrEmpty(value))
			{
				return null;
			}
			string[] array = value.Replace('\\', '/').Split('/');
			for (int i = 0; i < array.Length - 1; i++)
			{
				if (string.Equals(array[i], "plugins", StringComparison.OrdinalIgnoreCase))
				{
					if (array[i + 1].Length <= 0)
					{
						return null;
					}
					return array[i + 1];
				}
			}
			return null;
		}

		public static void LoadAll()
		{
			BridgeBlacklist.EnsureLoaded();
			_seeding = !BridgeBlacklist.ExistedAtStartup;
			_seedNames = (_seeding ? BridgeBlacklist.ReadMoreHeadNames() : null);
			string pluginPath = Paths.PluginPath;
			string[] files = Directory.GetFiles(pluginPath, "*.hhh", SearchOption.AllDirectories);
			string text = Plugin.SpecificFolders.Value ?? "";
			if (!string.IsNullOrWhiteSpace(text))
			{
				char[] invalidChars = Path.GetInvalidPathChars();
				string[] array = (from s in (from s in text.Split(',')
						select s.Trim() into s
						where s.Length > 0
						select s).Select(delegate(string s)
					{
						string text2 = new string(s.Where((char c) => !invalidChars.Contains(c)).ToArray());
						if (text2 != s)
						{
							BceConsole.LogWarning("SpecificFolders: '" + s + "' contained invalid path characters — changed to '" + text2 + "'");
						}
						return text2;
					})
					where s.Length > 0
					select s).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
				string[] matched = array.Where((string a) => files.Any((string f) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray();
				string[] array2 = array.Except<string>(matched, StringComparer.OrdinalIgnoreCase).ToArray();
				if (matched.Length == 0)
				{
					BceConsole.LogWarning("SpecificFolders: none of the specified folders were found (" + string.Join(", ", array) + "). Loading all .hhh files instead");
				}
				else
				{
					if (array2.Length != 0)
					{
						BceConsole.LogWarning("SpecificFolders: folder(s) not found and skipped: " + string.Join(", ", array2));
					}
					int num = files.Length;
					files = files.Where((string f) => matched.Any((string a) => f.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0)).ToArray();
					BceConsole.LogInfo(string.Format("SpecificFolders: loaded from {0} — kept {1}/{2} files", string.Join(", ", matched), files.Length, num));
				}
			}
			BceConsole.LogInfo($"Found {files.Length} .hhh file(s). Translating cosmetics from MoreHead to Vanilla REPO...");
			int num2 = 0;
			int num3 = Math.Max(2, Environment.ProcessorCount);
			Queue<(string, Task<byte[]>)> queue = new Queue<(string, Task<byte[]>)>(num3);
			int num4 = 0;
			while (num4 < files.Length && queue.Count < num3)
			{
				string p = files[num4++];
				queue.Enqueue((p, Task.Run(() => ReadBytesOrNull(p))));
			}
			while (queue.Count > 0)
			{
				(string, Task<byte[]>) tuple = queue.Dequeue();
				string item = tuple.Item1;
				Task<byte[]> item2 = tuple.Item2;
				byte[] result = item2.GetAwaiter().GetResult();
				if (TryRegister(item, result))
				{
					num2++;
				}
				if (num4 < files.Length)
				{
					string p2 = files[num4++];
					queue.Enqueue((p2, Task.Run(() => ReadBytesOrNull(p2))));
				}
			}
			int num5 = files.Length;
			int num6 = num5 - num2;
			BceConsole.LogInfo($"Done — {num2}/{num5} registered, {num6} error(s)");
			if (_seeding)
			{
				BridgeBlacklist.Save();
			}
			BridgeBlacklist.MirrorToMoreHead();
		}

		private static byte[]? ReadBytesOrNull(string path)
		{
			try
			{
				FileInfo fileInfo = new FileInfo(path);
				return (fileInfo.Exists && fileInfo.Length >= 1024) ? File.ReadAllBytes(path) : null;
			}
			catch
			{
				return null;
			}
		}

		private static bool TryRegister(string path, byte[]? bytes = null)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			FileInfo fileInfo = new FileInfo(path);
			if (!fileInfo.Exists || fileInfo.Length < 1024)
			{
				BceConsole.LogWarning("Skipped (too small/missing): " + Path.GetFileName(path));
				return false;
			}
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
			ParseFileName(fileNameWithoutExtension, out string name, out string tag);
			if (!TagMap.TryGetValue(tag, out (CosmeticType, OverrideCosmeticType) value))
			{
				return false;
			}
			CosmeticType item = value.Item1;
			AssetBundle val = ((bytes != null) ? AssetBundle.LoadFromMemory(bytes) : AssetBundle.LoadFromFile(path));
			if ((Object)(object)val == (Object)null)
			{
				BceConsole.LogError("Failed to load bundle: " + fileNameWithoutExtension);
				return false;
			}
			GameObject val2 = LoadFirstPrefab(val);
			val.Unload(false);
			if ((Object)(object)val2 == (Object)null)
			{
				BceConsole.LogError("No GameObject in bundle: " + fileNameWithoutExtension);
				return false;
			}
			if (!val2.activeSelf)
			{
				val2.SetActive(true);
			}
			string name2 = ((Object)val2).name;
			((Object)val2).name = EnsureUniqueId(name2, _usedPrefabIds);
			string text = name;
			name = EnsureUniqueId(name, _usedInternalNames);
			bool flag = ((Object)val2).name != name2;
			bool flag2 = name != text;
			if (flag && flag2 && name2 == text)
			{
				if (((Object)val2).name == name)
				{
					BceConsole.LogWarning("Duplicate name '" + name2 + "' — renamed internal and prefab to '" + name + "'.", ConsoleColor.DarkYellow);
				}
				else
				{
					BceConsole.LogWarning("Duplicate name '" + name2 + "' — prefab renamed to '" + ((Object)val2).name + "', internal id to '" + name + "'.", ConsoleColor.DarkYellow);
				}
			}
			else
			{
				if (flag)
				{
					BceConsole.LogWarning("Duplicate prefab name '" + name2 + "' → renamed to '" + ((Object)val2).name + "'", ConsoleColor.DarkYellow);
				}
				if (flag2)
				{
					BceConsole.LogWarning("Duplicate internal id '" + text + "' → renamed to '" + name + "'", ConsoleColor.DarkYellow);
				}
			}
			string text2 = "morehead-bridge:" + name.ToLowerInvariant();
			bool flag3 = _seeding && _seedNames != null && _seedNames.Contains(name2);
			if (flag3)
			{
				BridgeBlacklist.Add(text2, name2);
			}
			bool flag4 = false;
			if (BridgeBlacklist.Contains(text2))
			{
				if (Plugin.BridgeBlacklistMode.Value == BlacklistLoadMode.NotLoadIngame)
				{
					Object.Destroy((Object)(object)val2);
					return false;
				}
				flag4 = true;
			}
			Cosmetic val3 = val2.GetComponent<Cosmetic>();
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = val2.AddComponent<Cosmetic>();
			}
			val3.type = item;
			PrefabRef val4 = NetworkPrefabs.RegisterNetworkPrefab("Cosmetics/" + ((Object)val2).name, val2);
			if (val4 == null)
			{
				BceConsole.LogError("Failed to register network prefab: " + name);
				return false;
			}
			CosmeticAsset val5 = ScriptableObject.CreateInstance<CosmeticAsset>();
			((Object)val5).name = name;
			val5.assetName = name2;
			val5.type = item;
			val5.prefab = val4;
			val5.assetId = text2;
			val5.rarity = Plugin.BridgeDefaultRarity.Value;
			val5.customTypeList = new List<Type>();
			bool flag5 = BridgeTintHelper.DetectTintable(val2);
			_originalTintable[text2] = flag5;
			val5.tintable = Plugin.EnableBridgeTinting.Value && flag5;
			_originalTypes[text2] = value.Item2;
			CustomizerStore.ApplyIfPresent(val5);
			Cosmetics.RegisterCosmetic(val5);
			if (flag4 && flag3)
			{
				BridgeFavoritesManager.EnsureHidden(val5);
			}
			RegisteredAssetIds.Add(text2);
			_sourcePath[text2] = path;
			if (tag == "world")
			{
				WorldAssetIds.Add(text2);
			}
			Texture2D val6 = TryExtractIconTexture(val2);
			if ((Object)(object)val6 != (Object)null)
			{
				BridgeIconTextures[text2] = val6;
			}
			return true;
		}

		private static GameObject? LoadFirstPrefab(AssetBundle bundle)
		{
			string[] allAssetNames = bundle.GetAllAssetNames();
			foreach (string text in allAssetNames)
			{
				GameObject val = bundle.LoadAsset<GameObject>(text);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Texture2D? TryExtractIconTexture(GameObject prefab)
		{
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			string[] array = new string[5] { "_MainTex", "_BaseMap", "_BaseColorMap", "_Albedo", "_AlbedoMap" };
			Renderer[] array2 = componentsInChildren;
			foreach (Renderer val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					string[] array3 = array;
					foreach (string text in array3)
					{
						if (val2.HasProperty(text))
						{
							Texture texture = val2.GetTexture(text);
							Texture2D val3 = (Texture2D)(object)((texture is Texture2D) ? texture : null);
							if (val3 != null && (Object)(object)val3 != (Object)null)
							{
								return val3;
							}
						}
					}
				}
			}
			return null;
		}

		private static void ParseFileName(string fileName, out string name, out string tag)
		{
			int num = fileName.LastIndexOf('_');
			if (num >= 0)
			{
				int num2 = num + 1;
				string text = fileName.Substring(num2, fileName.Length - num2).ToLowerInvariant();
				if (ValidTags.Contains(text))
				{
					name = fileName.Substring(0, num);
					tag = text;
					return;
				}
			}
			name = fileName;
			tag = "head";
		}

		private static string EnsureUniqueId(string baseName, HashSet<string> used)
		{
			string text = baseName;
			int num = 1;
			while (!used.Add(text))
			{
				text = $"{baseName}({num})";
				num++;
			}
			return text;
		}

		internal static bool IsWorldAsset(CosmeticAsset? asset)
		{
			if ((Object)(object)asset != (Object)null && BridgeIds.IsBridgeAsset(asset))
			{
				return WorldAssetIds.Contains(asset.assetId);
			}
			return false;
		}

		internal static bool TryGetOriginalType(string assetId, out OverrideCosmeticType type)
		{
			return _originalTypes.TryGetValue(assetId, out type);
		}

		internal static void RefreshTintableFlags()
		{
			if ((Object)(object)MetaManager.instance == (Object)null)
			{
				return;
			}
			bool value = Plugin.EnableBridgeTinting.Value;
			_lastAppliedTinting = value;
			foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets)
			{
				if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && (!CustomizerStore.TryGet(cosmeticAsset.assetId, out CosmeticOverrideData data) || !data.Tintable.HasValue) && _originalTintable.TryGetValue(cosmeticAsset.assetId, out var value2))
				{
					cosmeticAsset.tintable = value && value2;
				}
			}
		}

		internal static void RefreshDefaultRarity()
		{
			//IL_0013: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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)
			if ((Object)(object)MetaManager.instance == (Object)null)
			{
				return;
			}
			Rarity rarity = (_lastAppliedRarity = Plugin.BridgeDefaultRarity.Value);
			foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets)
			{
				if (!((Object)(object)cosmeticAsset == (Object)null) && BridgeIds.IsBridgeAsset(cosmeticAsset) && (!CustomizerStore.TryGet(cosmeticAsset.assetId, out CosmeticOverrideData data) || !data.Rarity.HasValue))
				{
					cosmeticAsset.rarity = rarity;
				}
			}
		}

		internal static bool TryGetDefaultTintable(CosmeticAsset asset, out bool tintable)
		{
			tintable = false;
			if ((Object)(object)asset == (Object)null || !BridgeIds.IsBridgeAsset(asset))
			{
				return false;
			}
			if (!_originalTintable.TryGetValue(asset.assetId, out var value))
			{
				return false;
			}
			tintable = Plugin.EnableBridgeTinting.Value && value;
			return true;
		}

		internal static void OnMenuOpen(MenuPageCosmetics page)
		{
			//IL_0013: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)MetaManager.instance == (Object)null))
			{
				Rarity value = Plugin.BridgeDefaultRarity.Value;
				if (value != _lastAppliedRarity)
				{
					RefreshDefaultRarity();
				}
				bool value2 = Plugin.EnableBridgeTinting.Value;
				if (_lastAppliedTinting != value2)
				{
					RefreshTintableFlags();
				}
				if (!_moreHeadFixDone && (Plugin.RemoveBridgePhysics.Value || Plugin.LoopBridgeAnimation.Value))
				{
					_moreHeadFixDone = true;
					((MonoBehaviour)page).StartCoroutine(CosmeticPrefabFixer.TryFixMoreHeadPrefabsAsync());
				}
			}
		}

		internal static void ReapplyDefaults(CosmeticAsset asset)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			asset.rarity = Plugin.BridgeDefaultRarity.Value;
			if (_originalTypes.TryGetValue(asset.assetId, out var value))
			{
				(CosmeticType cosmeticType, bool isWorld) tuple = CustomizerStore.MapOverrideToVanilla(value);
				CosmeticType item = tuple.cosmeticType;
				bool item2 = tuple.isWorld;
				asset.type = item;
				if (item2)
				{
					WorldAssetIds.Add(asset.assetId);
				}
				else
				{
					WorldAssetIds.Remove(asset.assetId);
				}
				if (_originalTintable.TryGetValue(asset.assetId, out var value2))
				{
					asset.tintable = Plugin.EnableBridgeTinting.Value && value2;
				}
			}
		}

		static HhhCosmeticLoader()
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>();
			foreach (string key in TagMap.Keys)
			{
				hashSet.Add(key);
			}
			ValidTags = hashSet;
			_usedPrefabIds = new HashSet<string>();
			_usedInternalNames = new HashSet<string>();
			_lastAppliedRarity = (Rarity)(-1);
			_lastAppliedTinting = null;
			_originalTypes = new Dictionary<string, OverrideCosmeticType>();
			_originalTintable = new Dictionary<string, bool>();
		}
	}
	internal static class BatchIconGenerator
	{
		private static bool _isRunning;

		private static bool _didStartOnce;

		private static int _progressDone;

		private static int _progressFailed;

		private static int _progressTotal;

		private static RawImage? _avatarRawImage;

		internal static Action? OnBatchCompleted;

		private static FieldInfo? _menuPageStateField;

		private static FieldInfo? _iconCreationAvatarField;

		private static readonly string[] FaceMeshNames = new string[5] { "mesh_eye_l", "mesh_eye_r", "mesh_pupil_l", "mesh_pupil_r", "mesh_head_top" };

		private static FieldInfo? _equipLerpField;

		internal static bool IsGenerating => _isRunning;

		internal static string ProgressText { get; private set; } = "";

		internal static int ProgressDone => _progressDone;

		internal static int ProgressFailed => _progressFailed;

		internal static int ProgressTotal => _progressTotal;

		internal static void TryStart(MonoBehaviour host)
		{
			if (!_isRunning && Plugin.GenerateAllIcons.Value)
			{
				if (_didStartOnce)
				{
					BceConsole.LogWarning("GenerateAllIcons: previous batch was interrupted. Resuming — only icons still missing will be generated");
				}
				_isRunning = true;
				MenuPage component = ((Component)host).GetComponent<MenuPage>();
				host.StartCoroutine(Run(component));
			}
		}

		internal static void NotifyMenuClosed()
		{
			if (_isRunning)
			{
				_isRunning = false;
				if (Plugin.MenuLibAvailable)
				{
					PopupDestroy();
				}
				if ((Object)(object)_avatarRawImage != (Object)null)
				{
					((Behaviour)_avatarRawImage).enabled = true;
					_avatarRawImage = null;
				}
				if ((Object)(object)MetaManager.instance != (Object)null)
				{
					MetaManager.instance.CosmeticPreviewSet(false);
					MetaManager.instance.CosmeticPlayerUpdateLocal(false, false);
				}
				WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true);
				ProgressText = "";
				int num = _progressTotal - _progressDone - _progressFailed;
				BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue. (Your equipped cosmetics were not modified.)");
			}
		}

		private static IEnumerator Run(MenuPage? cosmeticsMenuPage)
		{
			_didStartOnce = true;
			if ((object)_menuPageStateField == null)
			{
				_menuPageStateField = AccessTools.Field(typeof(MenuPage), "currentPageState");
			}
			if ((Object)(object)cosmeticsMenuPage != (Object)null && _menuPageStateField != null)
			{
				float elapsed = 0f;
				while (elapsed < 3f)
				{
					PageState val = (PageState)(_menuPageStateField.GetValue(cosmeticsMenuPage) ?? ((object)(PageState)1));
					if ((int)val == 1)
					{
						break;
					}
					elapsed += Time.unscaledDeltaTime;
					yield return null;
				}
			}
			else
			{
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			if ((Object)(object)MetaManager.instance == (Object)null)
			{
				_isRunning = false;
				yield break;
			}
			List<CosmeticAsset> work = new List<CosmeticAsset>();
			foreach (CosmeticAsset cosmeticAsset in MetaManager.instance.cosmeticAssets)
			{
				if (!((Object)(object)cosmeticAsset == (Object)null) && cosmeticAsset.assetId != null && BridgeIds.IsBridgeAsset(cosmeticAsset) && !(cosmeticAsset.assetId == MiniSemibotCosmetic.AssetId) && !IconCapture.HasCache(cosmeticAsset))
				{
					work.Add(cosmeticAsset);
				}
			}
			BceConsole.LogInfo($"GenerateAllIcons: {work.Count} icon(s) to generate.", ConsoleColor.DarkGreen);
			if (work.Count == 0)
			{
				_isRunning = false;
				Plugin.GenerateAllIcons.Value = false;
				((BaseUnityPlugin)Plugin.Instance).Config.Save();
				yield break;
			}
			_progressDone = 0;
			_progressFailed = 0;
			_progressTotal = work.Count;
			_avatarRawImage = FindAvatarRawImage();
			if ((Object)(object)_avatarRawImage != (Object)null && Plugin.HideAvatarWhileGenerating.Value)
			{
				((Behaviour)_avatarRawImage).enabled = false;
			}
			if (Plugin.MenuLibAvailable)
			{
				PopupOpen();
			}
			bool interrupted = true;
			try
			{
				foreach (CosmeticAsset asset in work)
				{
					if (!_isRunning || (Object)(object)MetaManager.instance == (Object)null)
					{
						break;
					}
					int num = MetaManager.instance.cosmeticAssets.IndexOf(asset);
					if (num < 0)
					{
						_progressFailed++;
						continue;
					}
					MetaManager.instance.cosmeticEquippedPreview.Clear();
					if (!Plugin.HideClothesWhileGenerating.Value && MetaManager.instance.cosmeticEquipped != null)
					{
						foreach (int item in MetaManager.instance.cosmeticEquipped)
						{
							MetaManager.instance.cosmeticEquippedPreview.Add(item);
						}
					}
					if (!MetaManager.instance.cosmeticEquippedPreview.Contains(num))
					{
						MetaManager.instance.cosmeticEquippedPreview.Add(num);
					}
					if (MetaManager.instance.colorsEquipped != null)
					{
						MetaManager.instance.colorsEquippedPreview = (Plugin.ResetBodyColorWhileGenerating.Value ? new int[MetaManager.instance.colorsEquipped.Length] : ((int[])MetaManager.instance.colorsEquipped.Clone()));
					}
					MetaManager.instance.CosmeticPreviewSet(true);
					MetaManager.instance.CosmeticPlayerUpdateLocal(false, false);
					WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: false);
					if (HhhCosmeticLoader.IsWorldAsset(asset))
					{
						WorldCosmeticsSetupPatch.SetWorldAssetActive(asset, active: true);
					}
					Cosmetic[] snapshot = Object.FindObjectsOfType<Cosmetic>();
					SkipEquipAnimationFor(asset, snapshot);
					yield return null;
					for (int guard = 0; guard < 3; guard++)
					{
						if (IsAnimComplete(asset))
						{
							break;
						}
						snapshot = Object.FindObjectsOfType<Cosmetic>();
						if (IsAnimComplete(asset, snapshot))
						{
							break;
						}
						yield return null;
					}
					PlayerAvatarVisuals iconVisuals = Object.FindObjectOfType<PlayerAvatarMenuHover>()?.playerAvatarMenu?.playerVisuals ?? PlayerAvatarMenu.instance?.playerVisuals;
					PartShrinkerBridge.ResyncFromMountedCosmetics(iconVisuals);
					PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: false);
					MirrorFaceMeshesToAllAvatars(iconVisuals);
					ResetCustomConditionsForCapture();
					yield return null;
					MirrorFaceMeshesToAllAvatars(iconVisuals);
					SnapHideConditions();
					yield return (object)new WaitForEndOfFrame();
					MirrorFaceMeshesToAllAvatars(iconVisuals);
					SnapHideConditions();
					yield return (object)new WaitForEndOfFrame();
					if ((Object)(object)MetaManager.instance == (Object)null)
					{
						PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: true);
						break;
					}
					bool flag = IconCapture.TryCapture(asset);
					PartShrinkerBridge.SetAllHiddenPartsEnabled(enabled: true);
					if (flag)
					{
						_progressDone++;
					}
					else
					{
						_progressFailed++;
					}
					int num2 = _progressDone + _progressFailed;
					int num3 = ((_progressTotal > 0) ? (num2 * 100 / _progressTotal) : 0);
					ProgressText = ((_progressFailed > 0) ? $"Generating icons: {num2}/{_progressTotal} ({num3}%)  |  {_progressFailed} failed" : $"Generating icons: {num2}/{_progressTotal} ({num3}%)");
					if (Plugin.MenuLibAvailable)
					{
						PopupUpdate();
					}
					MetaManager.instance.CosmeticPreviewSet(false);
					MetaManager.instance.CosmeticPlayerUpdateLocal(false, false);
					WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true);
					int num4 = _progressDone + _progressFailed;
					if (num4 % 50 == 0)
					{
						BceConsole.LogInfo($"Batch progress: {num4}/{work.Count} " + $"({_progressDone} ok, {_progressFailed} failed)", ConsoleColor.DarkGreen);
					}
				}
				if (_isRunning)
				{
					interrupted = false;
				}
			}
			finally
			{
				if (Plugin.MenuLibAvailable)
				{
					PopupClose();
				}
				if (_isRunning)
				{
					_isRunning = false;
					if (interrupted)
					{
						int num5 = _progressTotal - _progressDone - _progressFailed;
						BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num5} still to go). " + "Reopen the menu to continue");
					}
				}
				if ((Object)(object)MetaManager.instance != (Object)null)
				{
					MetaManager.instance.CosmeticPreviewSet(false);
					MetaManager.instance.CosmeticPlayerUpdateLocal(false, false);
				}
				WorldCosmeticsSetupPatch.SetAllWorldInstancesActive(active: true);
				if ((Object)(object)_avatarRawImage != (Object)null)
				{
					((Behaviour)_avatarRawImage).enabled = true;
					_avatarRawImage = null;
				}
				ProgressText = "";
			}
			if (!interrupted)
			{
				Plugin.GenerateAllIcons.Value = false;
				((BaseUnityPlugin)Plugin.Instance).Config.Save();
				_didStartOnce = false;
				BceConsole.LogInfo($"GenerateAllIcons done — {_progressDone} captured, " + $"{_progressFailed} failed.", ConsoleColor.DarkGreen);
				Action onBatchCompleted = OnBatchCompleted;
				OnBatchCompleted = null;
				onBatchCompleted?.Invoke();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void PopupOpen()
		{
			BatchIconPopup.Open();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void PopupUpdate()
		{
			BatchIconPopup.UpdateProgress();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void PopupClose()
		{
			BatchIconPopup.Close();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void PopupDestroy()
		{
			BatchIconPopup.Destroy();
		}

		internal static bool OnPopupEscape()
		{
			if ((Object)(object)_avatarRawImage != (Object)null)
			{
				((Behaviour)_avatarRawImage).enabled = true;
				_avatarRawImage = null;
			}
			_isRunning = false;
			int num = _progressTotal - _progressDone - _progressFailed;
			BceConsole.LogWarning("GenerateAllIcons: batch interrupted at " + $"{_progressDone + _progressFailed}/{_progressTotal} " + $"({num} still to go). " + "Reopen the menu to continue. (Your equipped cosmetics were not modified.)");
			return true;
		}

		private static RawImage? FindAvatarRawImage()
		{
			PlayerAvatarMenuHover val = Object.FindObjectOfType<PlayerAvatarMenuHover>();
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponent<RawImage>();
		}

		private static void SkipEquipAnimationFor(CosmeticAsset asset, Cosmetic[]? snapshot = null)
		{
			if ((object)_iconCreationAvatarField == null)
			{
				_iconCreationAvatarField = AccessTools.Field(typeof(Cosmetic), "iconCreationAvatar");
			}
			if (_iconCreationAvatarField == null)
			{
				return;
			}
			Cosmetic[] array = snapshot ?? Object.FindObjectsOfType<Cosmetic>();
			Cosmetic[] array2 = array;
			foreach (Cosmetic val in array2)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset)
				{
					_iconCreationAvatarField.SetValue(val, true);
				}
			}
		}

		private static void ResetCustomConditionsForCapture()
		{
			PlayerCosmetics[] array = Object.FindObjectsOfType<PlayerCosmetics>(true);
			foreach (PlayerCosmetics val in array)
			{
				try
				{
					val.conditionsCustom.Clear();
					val.ConditionUpdateAll();
				}
				catch (Exception ex)
				{
					BridgeLog.Debug("BatchIconGenerator: condition reset failed — " + ex.Message);
				}
			}
		}

		private static void SnapHideConditions()
		{
			CosmeticHideCondition[] array = Object.FindObjectsOfType<CosmeticHideCondition>(true);
			foreach (CosmeticHideCondition val in array)
			{
				try
				{
					val.AnimateInstant();
				}
				catch
				{
				}
			}
		}

		private static void MirrorFaceMeshesToAllAvatars(PlayerAvatarVisuals? reference)
		{
			if ((Object)(object)reference == (Object)null)
			{
				return;
			}
			Dictionary<string, bool> dictionary = new Dictionary<string, bool>();
			MeshRenderer[] componentsInChildren = ((Component)reference).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				string name = ((Object)((Component)val).gameObject).name;
				if (Array.IndexOf(FaceMeshNames, name) >= 0 && !dictionary.ContainsKey(name))
				{
					dictionary[name] = ((Renderer)val).enabled;
				}
			}
			if (dictionary.Count == 0)
			{
				return;
			}
			PlayerAvatarVisuals[] array = Object.FindObjectsOfType<PlayerAvatarVisuals>(true);
			foreach (PlayerAvatarVisuals val2 in array)
			{
				MeshRenderer[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<MeshRenderer>(true);
				foreach (MeshRenderer val3 in componentsInChildren2)
				{
					if (dictionary.TryGetValue(((Object)((Component)val3).gameObject).name, out var value) && ((Renderer)val3).enabled != value)
					{
						((Renderer)val3).enabled = value;
					}
				}
			}
		}

		private static bool IsAnimComplete(CosmeticAsset asset, Cosmetic[]? snapshot = null)
		{
			if ((object)_equipLerpField == null)
			{
				_equipLerpField = AccessTools.Field(typeof(Cosmetic), "equipLerp");
			}
			if (_equipLerpField == null)
			{
				return true;
			}
			Cosmetic[] array = snapshot ?? Object.FindObjectsOfType<Cosmetic>();
			Cosmetic[] array2 = array;
			foreach (Cosmetic val in array2)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.cosmeticAsset == (Object)(object)asset && (float)(_equipLerpField.GetValue(val) ?? ((object)1f)) < 1f)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class BatchIconPopup
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ShouldCloseMenuDelegate <>9__12_1;

			internal bool <Create>b__12_1()
			{
				Clear();
				return BatchIconGenerator.OnPopupEscape();
			}
		}

		private static REPOPopupPage? _page;

		private static TextMeshProUGUI? _progressLabel;

		private static TextMeshProUGUI? _hintLabel;

		private const float GeneratingLabelH = 140f;

		private const float NumbersLabelH = 40f;

		private static string[] _tips = Array.Empty<string>();

		private static int _currentTipIndex;

		internal static void Open()
		{
			_page = Create();
			_page.OpenPage(false);
			UpdateProgress();
		}

		internal static void Close()
		{
			if (!((Object)(object)_page == (Object)null))
			{
				_page.ClosePage(false);
				Clear();
			}
		}

		internal static void Destroy()
		{
			if (!((Object)(object)_page == (Object)null))
			{
				Object.Destroy((Object)(object)((Component)_page).gameObject);
				Clear();
			}
		}

		private static void Clear()
		{
			_page = null;
			_progressLabel = null;
			_hintLabel = null;
		}

		internal static void UpdateProgress()
		{
			if (!((Object)(object)_progressLabel == (Object)null))
			{
				int num = BatchIconGenerator.ProgressDone + BatchIconGenerator.ProgressFailed;
				int progressTotal = BatchIconGenerator.ProgressTotal;
				int num2 = ((progressTotal > 0) ? (num * 100 / progressTotal) : 0);
				string text = $"{num} / {progressTotal}  ({num2}%)";
				if (BatchIconGenerator.ProgressFailed > 0)
				{
					text += $"\n{BatchIconGenerator.ProgressFailed} failed";
				}
				((TMP_Text)_progressLabel).text = text;
			}
		}

		private static REPOPopupPage Create()
		{
			//IL_001c: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			REPOPopupPage val = MenuAPI.CreateREPOPopupPage("Generating Icons", false, true, 0f, (Vector2?)new Vector2(-120f, 0f));
			PopupScrollGuard popupScrollGuard = ((Component)val).gameObject.AddComponent<PopupScrollGuard>();
			popupScrollGuard.Init(((Component)val).transform);
			_tips = BuildTipList();
			_currentTipIndex = 0;
			TextMeshProUGUI capturedLabel = null;
			TextMeshProUGUI capturedHint = null;
			REPOLabel capturedParent = null;
			float maskW = val.maskRectTransform.sizeDelta.x;
			val.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform sv)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
				//IL_010c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0127: Unknown result type (might be due to invalid IL or missing references)
				//IL_013c: Unknown result type (might be due to invalid IL or missing references)
				capturedParent = MenuAPI.CreateREPOLabel("Starting...", sv, default(Vector2));
				capturedLabel = capturedParent.labelTMP;
				if ((Object)(object)capturedLabel != (Object)null)
				{
					((TMP_Text)capturedLabel).fontSize = 16f;
					((TMP_Text)capturedLabel).alignment = (TextAlignmentOptions)514;
					((TMP_Text)capturedLabel).enableWordWrapping = true;
					float num = ((maskW > 0f) ? maskW : 200f) - 10f;
					((Component)capturedParent).GetComponent<RectTransform>().sizeDelta = new Vector2(num, 140f);
					GameObject val3 = Object.Instantiate<GameObject>(((Component)capturedLabel).gameObject, ((TMP_Text)capturedLabel).transform.parent);
					((Object)val3).name = "HintLabel";
					capturedHint = val3.GetComponent<TextMeshProUGUI>();
					((TMP_Text)capturedLabel).rectTransform.sizeDelta = new Vector2(num, 40f);
					((Transform)((TMP_Text)capturedLabel).rectTransform).localPosition = new Vector3(0f, 100f);
					((TMP_Text)capturedHint).rectTransform.sizeDelta = new Vector2(num, 100f);
					((Transform)((TMP_Text)capturedHint).rectTransform).localPosition = Vector3.zero;
				}
				return ((Component)capturedParent).GetComponent<RectTransform>();
			}, 0f, 0f);
			if ((Object)(object)capturedParent != (Object)null)
			{
				REPOScrollViewElement component = ((Component)capturedParent).GetComponent<REPOScrollViewElement>();
				if ((Object)(object)component != (Object)null)
				{
					((MonoBehaviour)val).StartCoroutine(CenterScrollElement(val, component));
				}
			}
			_progressLabel = capturedLabel;
			_hintLabel = capturedHint;
			UpdateHintLabel();
			if (_tips.Length > 1)
			{
				((MonoBehaviour)val).StartCoroutine(CycleTips());
			}
			object obj = <>c.<>9__12_1;
			if (obj == null)
			{
				ShouldCloseMenuDelegate val2 = delegate
				{
					Clear();
					return BatchIconGenerator.OnPopupEscape();
				};
				<>c.<>9__12_1 = val2;
				obj = (object)val2;
			}
			val.onEscapePressed = (ShouldCloseMenuDelegate)obj;
			return val;
		}

		private static void UpdateHintLabel()
		{
			if (!((Object)(object)_hintLabel == (Object)null))
			{
				string text = "Press ESC to stop early";
				if (_tips.Length != 0)
				{
					text = text + "\n\n" + _tips[_currentTipIndex];
				}
				((TMP_Text)_hintLabel).text = text;
			}
		}

		private static IEnumerator CycleTips()
		{
			while (BatchIconGenerator.IsGenerating && (Object)(object)_progressLabel != (Object)null)
			{
				yield return (object)new WaitForSecondsRealtime(10f);
				if (!BatchIconGenerator.IsGenerating || (Object)(object)_progressLabel == (Object)null)
				{
					break;
				}
				_currentTipIndex = (_currentTipIndex + 1) % _tips.Length;
				UpdateHintLabel();
			}
		}

		private static string[] BuildTipList()
		{
			List<string> list = new List<string> { "Tip: close the menu anytime to pause — reopen it to resume where you left off" };
			if (Plugin.AutoCaptureIcons.Value)
			{
				list.Add("Tip: hovering a cosmetic with no icon captures it automatically");
			}
			return list.ToArray();
		}

		private static IEnumerator CenterScrollElement(REPOPopupPage popup, REPOScrollViewElement element)
		{
			yield return null;
			if ((Object)(object)_progressLabel != (Object)null)
			{
				((Transform)((TMP_Text)_progressLabel).rectTransform).localPosition = new Vector3(0f, 100f);
			}
			float y = popup.maskRectTransform.sizeDelta.y;
			Rect rect = element.rectTransform.rect;
			float height = ((Rect)(ref rect)).height;
			if (height > 0f)
			{
				element.topPadding = Mathf.Max(0f, (y - height) / 2f);
			}
		}
	}
	internal static class IconCacheCleaner
	{
		internal static void Run()
		{
			if (!Plugin.DeleteIconCache.Value)
			{
				return;
			}
			try
			{
				string cacheDir = IconCapture.CacheDir;
				if (!Directory.Exists(cacheDir))
				{
					BceConsole.LogInfo("DeleteIconCache: no cache directory, nothing to do");
					ResetFlag();
					return;
				}
				string text = Plugin.DeleteIconsMatching.Value ?? "";
				string[] array = (from s in text.Split(',')
					select s.Trim().ToLowerInvariant() into s
					where s.Length > 0
					select s).ToArray();
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				foreach (string registeredAssetId in HhhCosmeticLoader.RegisteredAssetIds)
				{
					int num = registeredAssetId.IndexOf(':');
					if (num >= 0 && num + 1 < registeredAssetId.Length)
					{
						string text2 = registeredAssetId;
						int num2 = num + 1;
						hashSet.Add(text2.Substring(num2, text2.Length - num2));
					}
				}
				int num3 = 0;
				int num4 = 0;
				string text3 = "MHB_MiniMe".ToLowerInvariant();
				string[] files = Directory.GetFiles(cacheDir, "*.png");
				foreach (string text4 in files)
				{
					string name = Path.GetFileNameWithoutExtension(text4).ToLowerInvariant();
					if (name == text3)
					{
						num4++;
						continue;
					}
					if (!hashSet.Contains(name))
					{
						num4++;
						continue;
					}
					if (array.Length != 0 && !array.Any((string f) => name.Contains(f)))
					{
						num4++;
						continue;
					}
					try
					{
						File.Delete(text4);
						num3++;
					}
					catch (Exception ex)
					{
						BceConsole.LogWarning("Failed to delete '" + text4 + "': " + ex.Message);
					}
				}
				BceConsole.LogInfo($"DeleteIconCache: removed {num3} bridge icon(s), kept {num4}. " + "Filter: " + ((array.Length == 0) ? "(all bridge icons)" : string.Join(",", array)));
				if (num3 > 0)
				{
					IconCapture.InvalidateAll();
				}
			}
			catch (Exception ex2)
			{
				BceConsole.LogError("DeleteIconCache failed: " + ex2.Message);
			}
			finally
			{
				ResetFlag();
			}
		}

		private static void ResetFlag()
		{
			Plugin.DeleteIconCache.Value = false;
			((BaseUnityPlugin)Plugin.Instance).Config.Save();
		}
	}
	internal static class IconCapture
	{
		private const int OutSize = 128;

		private static string? _cacheDir;

		private static HashSet<string>? _knownCached;

		private static FieldInfo? _renderTextureInstanceField;

		private static readonly Rect CropHead = new Rect(0.22f, 0.62f, 0.56f, 0.35f);

		private static readonly Rect CropNeck = new Rect(0.22f, 0.5f, 0.56f, 0.38f);

		private static readonly Rect CropBody = new Rect(0.18f, 0.34f, 0.64f, 0.36f);

		private static readonly Rect CropArmR = new Rect(0.05f, 0.3f, 0.5f, 0.4f);

		private static readonly Rect CropArmL = new Rect(0.45f, 0.3f, 0.5f, 0.4f);

		private static readonly Rect CropLegR = new Rect(0.1f, 0f, 0.45f, 0.45f);

		private static readonly Rect CropLegL = new Rect(0.45f, 0f, 0.45f, 0.45f);

		private static readonly Rect CropFull = new Rect(0f, 0f, 1f, 1f);

		internal static string CacheDir
		{
			get
			{
				if (_cacheDir != null)
				{
					return _cacheDir;
				}
				_cacheDir = Path.Combine(Application.persistentDataPath, "Cache", "Icons", "CosmeticsModded", "MoreHeadBridge_CosmeticsIcons");
				MigrateLegacyCache(_cacheDir);
				return _cacheDir;
			}
		}

		private static void MigrateLegacyCache(string newDir)
		{
			string path = Path.Combine(Application.persistentDataPath, "MoreHeadBridge_Icons");
			if (!Directory.Exists(path))
			{
				return;
			}
			BceConsole.LogInfo("IconCapture: migrating icon cache from legacy location...");
			try
			{
				Directory.CreateDirectory(newDir);
				int num = 0;
				int num2 = 0;
				string[] files = Directory.GetFiles(path, "*.png");
				foreach (string text in files)
				{
					string text2 = Path.Combine(newDir, Path.GetFileName(text));
					try
					{
						if (!File.Exists(text2))
						{
							File.Move(text, text2);
						}
						else
						{
							File.Delete(text);
						}
						num++;
					}
					catch (Exception ex)
					{
						num2++;
						BceConsole.LogWarning("IconCapture: could not migrate '" + Path.GetFileName(text) + "': " + ex.Message);
					}
				}
				try
				{
					if (Directory.GetFiles(path).Length == 0)
					{
						Directory.Delete(path, recursive: false);
					}
				}
				catch
				{
				}
				BceConsole.LogInfo($"IconCapture: cache migration done — {num} moved, {num2} failed");
			}
			catch (Exception ex2)
			{
				BceConsole.LogWarning("IconCapture: cache migration failed: " + ex2.Message);
			}
		}

		internal static string CachePathFor(CosmeticAsset asset)
		{
			string name = ((Object)asset).name.Replace("(Clone)", "").Trim().ToLowerInvariant();
			return Path.Combine(CacheDir, MakeSafeFileName(name) + ".png");
		}

		private static string MakeSafeFileName(string name)
		{
			name = name.Replace('/', '_').Replace('\\', '_').Replace("..", "__");
			if (name.Length != 0)
			{
				return name;
			}
			return "_unnamed";
		}

		private static void EnsureCacheSeeded()
		{
			if (_knownCached != null)
			{
				return;
			}
			_knownCached = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			try
			{
				if (Directory.Exists(CacheDir))
				{
					string[] files = Directory.GetFiles(CacheDir, "*.png");
					foreach (string item in files)
					{
						_knownCached.Add(item);
					}
				}
			}
			catch
			{
			}
		}

		internal static void MarkCached(string path)
		{
			EnsureCacheSeeded();
			_knownCached.Add(path);
		}

		internal static bool HasCache(CosmeticAsset asset)
		{
			EnsureCacheSeeded();
			return _knownCached.Contains(CachePathFor(asset));
		}

		internal static void MarkCached(CosmeticAsset asset)
		{
			EnsureCacheSeeded();
			_knownCached.Add(CachePathFor(asset));
		}

		internal static void DeleteCache(CosmeticAsset asset)
		{
			EnsureCacheSeeded();
			string text = CachePathFor(asset);
			_knownCached.Remove(text);
			try
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
			catch (Exception ex)
			{
				BceConsole.LogWarning("IconCapture: could not delete '" + Path.GetFileName(text) + "': " + ex.Message);
			}
			if ((Object)(object)asset.icon != (Object)null)
			{
				Object.Destroy((Object)(object)asset.icon);
				asset.icon = null;
			}
			CosmeticHoverPatch.Invalidate(asset);
			CosmeticsMenuStartPatch.RefreshToolsButtons?.Invoke();
			BridgeLog.Trace("IconCapture: deleted cached icon for '" + ((Object)asset).name + "'");
		}

		private static RenderTexture? FindActiveAvatarRT()
		{
			PlayerAvatarMenuHover val = Object.FindObjectOfType<PlayerAvatarMenuHover>();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if ((object)_renderTextureInstanceField == null)
			{
				_renderTextureInstanceField = AccessTools.Field(typeof(PlayerAvatarMenuHover), "renderTextureInstance");
			}
			if (_renderTextureInstanceField == null)
			{
				BceConsole.LogWarning("IconCapture: PlayerAvatarMenuHover.renderTextureInstance not found — update MoreHeadBridge");
			}
			else
			{
				object? value = _renderTextureInstanceField.GetValue(val);
				RenderTexture val2 = (RenderTexture)((value is RenderTexture) ? value : null);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			RawImage component = ((Component)val).GetComponent<RawImage>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			Texture texture = component.texture;
			return (RenderTexture?)(object)((texture is RenderTexture) ? texture : null);
		}

		internal static bool TryCapture(CosmeticAsset asset)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return TryCapture(asset, (CosmeticType)(HhhCosmeticLoader.IsWorldAsset(asset) ? (-1) : ((asset != null) ? ((int)asset.type) : 0)));
		}

		internal static bool TryCapture(CosmeticAsset asset, CosmeticType type)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			if ((Object)(object)asset == (Object)null)
			{
				return false;
			}
			if (HasCache(asset))
			{
				return false;
			}
			if (BridgeIds.IsBridgeAsset(asset) && asset.assetId != MiniSemibotCosmet