Decompiled source of InfernoSkinFix v0.1.0

plugins/InfernoSkinFix.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using HG;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using RoR2;
using RoR2.ContentManagement;
using RoR2.UI.MainMenu;
using UnityEngine;
using UnityEngine.AddressableAssets;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InfernoSkinFix
{
	internal sealed class AccessoryRigSpec
	{
		public readonly InfernoSkin Skin;

		public readonly GameObject ChainPrefab;

		public readonly string ParentBone;

		public readonly int InsertAt;

		public readonly int BaseBoneCount;

		public readonly int ChainLength;

		public readonly object DynamicBoneData;

		public readonly HashSet<Mesh> Meshes;

		private AccessoryRigSpec(InfernoSkin skin, GameObject prefab, string parentBone, int insertAt, int baseBoneCount, int chainLength, object dynamicBoneData, HashSet<Mesh> meshes)
		{
			Skin = skin;
			ChainPrefab = prefab;
			ParentBone = parentBone;
			InsertAt = insertAt;
			BaseBoneCount = baseBoneCount;
			ChainLength = chainLength;
			DynamicBoneData = dynamicBoneData;
			Meshes = meshes;
		}

		public static AccessoryRigSpec TryCreate(InfernoSkin skin, SkinDef bodySkin, ModelSkinController body, out string error)
		{
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			object[] modifications = InfernoDynamicSkins.GetModifications(skin.Token);
			if (modifications.Length != 1)
			{
				error = $"expected 1 DynamicSkins modification for {skin.Token}, found {modifications.Length}";
				return null;
			}
			object owner = modifications[0];
			object field = InfernoDynamicSkins.GetField(owner, "prefab");
			GameObject val = (GameObject)((field is GameObject) ? field : null);
			string text = InfernoDynamicSkins.GetField(owner, "parentname") as string;
			object field2 = InfernoDynamicSkins.GetField(owner, "boneIndex");
			object field3 = InfernoDynamicSkins.GetField(owner, "affectsbasemodel");
			if (!Object.op_Implicit((Object)(object)val) || string.IsNullOrEmpty(text) || !(field2 is int num) || !(field3 is bool) || !(bool)field3)
			{
				error = "unexpected DynamicSkins modification data";
				return null;
			}
			SkinnedMeshRenderer[] array = BaseRenderers(((Component)body).transform);
			int boneCount = ((array.Length != 0) ? array[0].bones.Length : 0);
			if (array.Length == 0 || array.Any((SkinnedMeshRenderer r) => r.bones.Length != boneCount) || num < 0 || num > boneCount)
			{
				error = $"body skinned renderers do not share one bone list (count {boneCount}, insert at {num})";
				return null;
			}
			if (!Object.op_Implicit((Object)(object)FindChild(((Component)body).transform, text)))
			{
				error = "parent bone '" + text + "' not found";
				return null;
			}
			int count = Chain(val.transform).Count;
			HashSet<Mesh> hashSet = new HashSet<Mesh>();
			MeshReplacement[] meshReplacements = bodySkin.meshReplacements;
			foreach (MeshReplacement val2 in meshReplacements)
			{
				if (Object.op_Implicit((Object)(object)val2.mesh) && val2.renderer is SkinnedMeshRenderer && !((Object)(object)((Component)val2.renderer).transform.parent != (Object)(object)((Component)body).transform))
				{
					int num3 = val2.mesh.bindposes.Length;
					if (num3 != boneCount + count)
					{
						error = $"mesh '{((Object)val2.mesh).name}' has {num3} bindposes, expected {boneCount} + {count}";
						return null;
					}
					hashSet.Add(val2.mesh);
				}
			}
			if (hashSet.Count == 0)
			{
				error = "no Inferno mesh needs the extra bones";
				return null;
			}
			error = null;
			return new AccessoryRigSpec(skin, val, text, num, boneCount, count, InfernoDynamicSkins.GetField(owner, "dynamicBoneData"), hashSet);
		}

		internal static SkinnedMeshRenderer[] BaseRenderers(Transform model)
		{
			return (from r in ((Component)model).GetComponentsInChildren<SkinnedMeshRenderer>(true)
				where (Object)(object)((Component)r).transform.parent == (Object)(object)model
				select r).ToArray();
		}

		internal static List<Transform> Chain(Transform root)
		{
			List<Transform> list = new List<Transform>();
			Walk(root);
			return list;
			void Walk(Transform t)
			{
				if (!((Object)t).name.EndsWith("_end", StringComparison.Ordinal))
				{
					list.Add(t);
				}
				for (int i = 0; i < t.childCount; i++)
				{
					Walk(t.GetChild(i));
				}
			}
		}

		internal static Transform FindChild(Transform root, string name)
		{
			if (((Object)root).name == name)
			{
				return root;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val = FindChild(root.GetChild(i), name);
				if (Object.op_Implicit((Object)(object)val))
				{
					return val;
				}
			}
			return null;
		}
	}
	internal sealed class AccessoryBoneRig : MonoBehaviour
	{
		internal static Action<string> LogError = delegate
		{
		};

		private AccessoryRigSpec spec;

		private SkinnedMeshRenderer[] renderers;

		private Transform[][] originalBones;

		private Mesh[] seenMesh;

		private bool[] extended;

		private bool initialized;

		private GameObject chainRoot;

		private Transform[] chain;

		private readonly List<Component> addedComponents = new List<Component>();

		public static void Ensure(ModelSkinController controller, AccessoryRigSpec spec)
		{
			AccessoryBoneRig accessoryBoneRig = default(AccessoryBoneRig);
			if (((Component)controller).TryGetComponent<AccessoryBoneRig>(ref accessoryBoneRig))
			{
				accessoryBoneRig.Sync();
				return;
			}
			SkinnedMeshRenderer[] array = AccessoryRigSpec.BaseRenderers(((Component)controller).transform);
			if (array.Length == 0 || array.Any((SkinnedMeshRenderer r) => r.bones.Length != spec.BaseBoneCount))
			{
				LogError($"{spec.Skin.Survivor}: '{((Object)controller).name}' does not have the expected {spec.BaseBoneCount}-bone layout; accessory bones not added.");
				return;
			}
			AccessoryBoneRig accessoryBoneRig2 = ((Component)controller).gameObject.AddComponent<AccessoryBoneRig>();
			accessoryBoneRig2.spec = spec;
			accessoryBoneRig2.renderers = array;
			accessoryBoneRig2.originalBones = array.Select((SkinnedMeshRenderer r) => r.bones).ToArray();
			accessoryBoneRig2.seenMesh = (Mesh[])(object)new Mesh[array.Length];
			accessoryBoneRig2.extended = new bool[array.Length];
			accessoryBoneRig2.Sync();
		}

		private void LateUpdate()
		{
			Sync();
		}

		internal void Sync()
		{
			bool flag = false;
			for (int i = 0; i < renderers.Length; i++)
			{
				SkinnedMeshRenderer val = renderers[i];
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Mesh sharedMesh = val.sharedMesh;
				if (initialized && sharedMesh == seenMesh[i])
				{
					flag |= extended[i];
					continue;
				}
				seenMesh[i] = sharedMesh;
				bool flag2 = Object.op_Implicit((Object)(object)sharedMesh) && spec.Meshes.Contains(sharedMesh);
				if (flag2 != extended[i])
				{
					if (flag2 && !AttachChain())
					{
						flag2 = false;
					}
					val.bones = (flag2 ? Extend(originalBones[i]) : originalBones[i]);
					extended[i] = flag2;
				}
				flag |= extended[i];
			}
			initialized = true;
			if (!flag && Object.op_Implicit((Object)(object)chainRoot))
			{
				DetachChain();
			}
		}

		private Transform[] Extend(Transform[] original)
		{
			List<Transform> list = new List<Transform>(original.Length + chain.Length);
			list.AddRange(original);
			list.InsertRange(spec.InsertAt, chain);
			return list.ToArray();
		}

		private bool AttachChain()
		{
			if (Object.op_Implicit((Object)(object)chainRoot))
			{
				return true;
			}
			try
			{
				Transform val = AccessoryRigSpec.FindChild(((Component)this).transform, spec.ParentBone);
				chainRoot = Object.Instantiate<GameObject>(spec.ChainPrefab, val, false);
				((Object)chainRoot).name = ((Object)spec.ChainPrefab).name;
				chain = AccessoryRigSpec.Chain(chainRoot.transform).ToArray();
				if (spec.DynamicBoneData != null)
				{
					AddDynamicBone();
				}
				return true;
			}
			catch (Exception arg)
			{
				LogError($"{spec.Skin.Survivor}: could not add accessory bones to '{((Object)this).name}': {arg}");
				DetachChain();
				return false;
			}
		}

		private void AddDynamicBone()
		{
			object dynamicBoneData = spec.DynamicBoneData;
			DynamicBone val = chainRoot.AddComponent<DynamicBone>();
			addedComponents.Add((Component)(object)val);
			CopyMatchingFields(dynamicBoneData, val, "m_Root", "m_Colliders", "m_Exclusions");
			val.m_Root = AccessoryRigSpec.FindChild(chainRoot.transform, (InfernoDynamicSkins.GetField(dynamicBoneData, "m_Root") as string) ?? ((Object)chainRoot).name);
			val.m_Exclusions = new List<Transform>();
			if (InfernoDynamicSkins.GetField(dynamicBoneData, "m_Exclusions") is IEnumerable<string> enumerable)
			{
				foreach (string item in enumerable)
				{
					Transform val2 = AccessoryRigSpec.FindChild(val.m_Root, item);
					if (Object.op_Implicit((Object)(object)val2))
					{
						val.m_Exclusions.Add(val2);
					}
				}
			}
			val.m_Colliders = new List<DynamicBoneCollider>();
			if (!(InfernoDynamicSkins.GetField(dynamicBoneData, "m_Colliders") is IEnumerable enumerable2))
			{
				return;
			}
			foreach (object item2 in enumerable2)
			{
				Transform val3 = AccessoryRigSpec.FindChild(((Component)this).transform, (InfernoDynamicSkins.GetField(item2, "m_parent_name") as string) ?? "");
				if (Object.op_Implicit((Object)(object)val3))
				{
					DynamicBoneCollider val4 = ((Component)val3).gameObject.AddComponent<DynamicBoneCollider>();
					CopyMatchingFields(item2, val4, "m_parent_name");
					val.m_Colliders.Add(val4);
					addedComponents.Add((Component)(object)val4);
				}
			}
		}

		private static void CopyMatchingFields(object source, object target, params string[] skip)
		{
			FieldInfo[] fields = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (Array.IndexOf(skip, fieldInfo.Name) < 0)
				{
					FieldInfo field = target.GetType().GetField(fieldInfo.Name, BindingFlags.Instance | BindingFlags.Public);
					if (field != null && field.FieldType == fieldInfo.FieldType)
					{
						field.SetValue(target, fieldInfo.GetValue(source));
					}
				}
			}
		}

		private void DetachChain()
		{
			foreach (Component addedComponent in addedComponents)
			{
				if (Object.op_Implicit((Object)(object)addedComponent))
				{
					Object.Destroy((Object)(object)addedComponent);
				}
			}
			addedComponents.Clear();
			if (Object.op_Implicit((Object)(object)chainRoot))
			{
				Object.Destroy((Object)(object)chainRoot);
			}
			chainRoot = null;
			chain = null;
		}
	}
	[BepInPlugin("Nhiblam.InfernoSkinFix", "InfernoSkinFix", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class InfernoSkinFixPlugin : BaseUnityPlugin
	{
		private delegate IEnumerator ApplySkinAsyncFn(ModelSkinController self, int skinIndex, AsyncReferenceHandleUnloadType unloadType);

		public const string Guid = "Nhiblam.InfernoSkinFix";

		public const string PluginName = "InfernoSkinFix";

		public const string PluginVersion = "0.1.0";

		private const string LobbySkinsFixGuid = "com.KingEnderBrine.LobbySkinsFix";

		private const string R2ApiSkinsGuid = "com.bepis.r2api.skins";

		private const string DynamicSkinsFixGuid = "Miyowi.DynamicSkinsFix";

		private Hook skinCatalogInitHook;

		private Hook applySkinHook;

		private void Awake()
		{
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			SkinTrace.Log = ((BaseUnityPlugin)this).Logger.LogInfo;
			SkinTrace.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "TraceSkinApplication", false, "Troubleshooting: log the renderer, mesh, bone and material state of Inferno-skinned survivors whenever a skin is applied (lobby and run).").Value;
			SkinRepairs.LogInfo = ((BaseUnityPlugin)this).Logger.LogInfo;
			SkinRepairs.LogError = ((BaseUnityPlugin)this).Logger.LogError;
			AccessoryBoneRig.LogError = ((BaseUnityPlugin)this).Logger.LogError;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("InfernoSkinFix 0.1.0 build=" + BuildId() + " sha256=" + OwnHash() + " with Inferno " + InfernoSkins.GetPluginVersion("HIFU.Inferno") + " (InfernoSkinMod " + InfernoSkins.GetPluginVersion("com.Dotflare.InfernoSkinMod") + ")"));
			string pluginVersion = InfernoSkins.GetPluginVersion("Miyowi.DynamicSkinsFix");
			if (pluginVersion != null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("DynamicSkinsFix " + pluginVersion + " detected: Inferno's own Bandit/Railgunner bone hook stays disabled; InfernoSkinFix adds those bones."));
			}
			if (!InfernoDynamicSkins.Bind())
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read InfernoSkinMod's DynamicSkins data (" + InfernoDynamicSkins.BindError + "); Bandit and Railgunner will not be repaired."));
			}
			try
			{
				MethodInfo methodInfo = typeof(SkinCatalog).GetMethod("Init", BindingFlags.Static | BindingFlags.NonPublic, null, Type.EmptyTypes, null) ?? throw new MissingMethodException("RoR2.SkinCatalog", "Init");
				skinCatalogInitHook = new Hook((MethodBase)methodInfo, (Delegate)new Func<Func<IEnumerator>, IEnumerator>(OnSkinCatalogInit));
				MethodInfo method = typeof(ModelSkinController).GetMethod("ApplySkinAsync", new Type[2]
				{
					typeof(int),
					typeof(AsyncReferenceHandleUnloadType)
				});
				applySkinHook = new Hook((MethodBase)method, (Delegate)new Func<ApplySkinAsyncFn, ModelSkinController, int, AsyncReferenceHandleUnloadType, IEnumerator>(OnApplySkinAsync));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Hook installation failed; Inferno skins are not repaired: " + ex));
				Hook obj = skinCatalogInitHook;
				if (obj != null)
				{
					obj.Dispose();
				}
				Hook obj2 = applySkinHook;
				if (obj2 != null)
				{
					obj2.Dispose();
				}
				return;
			}
			((ResourceAvailability)(ref SkinCatalog.availability)).CallWhenAvailable((Action)SkinRepairs.Report);
			if (SkinTrace.Enabled)
			{
				((ResourceAvailability)(ref SkinCatalog.availability)).CallWhenAvailable((Action)ReportBodySkins);
				MainMenuController.OnMainMenuInitialised += ReportDisplaySkins;
			}
		}

		private void OnDestroy()
		{
			Hook obj = skinCatalogInitHook;
			if (obj != null)
			{
				obj.Dispose();
			}
			Hook obj2 = applySkinHook;
			if (obj2 != null)
			{
				obj2.Dispose();
			}
			MainMenuController.OnMainMenuInitialised -= ReportDisplaySkins;
		}

		private static IEnumerator OnSkinCatalogInit(Func<IEnumerator> orig)
		{
			SkinRepairs.PatchSkinDefs();
			return orig();
		}

		private static IEnumerator OnApplySkinAsync(ApplySkinAsyncFn orig, ModelSkinController self, int skinIndex, AsyncReferenceHandleUnloadType unloadType)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			IEnumerator inner = orig(self, skinIndex, unloadType);
			bool flag = false;
			try
			{
				SkinDef[] skins = self.skins;
				AccessoryRigSpec accessoryRigSpec = SkinRepairs.FindRig(InfernoSkins.Identify((skins != null && skins.Length != 0) ? skins[((uint)skinIndex < skins.Length) ? skinIndex : 0] : null));
				if (accessoryRigSpec != null)
				{
					AccessoryBoneRig.Ensure(self, accessoryRigSpec);
				}
				AccessoryBoneRig accessoryBoneRig = default(AccessoryBoneRig);
				flag = ((Component)self).TryGetComponent<AccessoryBoneRig>(ref accessoryBoneRig);
			}
			catch (Exception arg)
			{
				AccessoryBoneRig.LogError(string.Format("Accessory bone setup failed on '{0}': {1}", Object.op_Implicit((Object)(object)self) ? ((Object)self).name : "null", arg));
			}
			if (flag)
			{
				inner = SyncAfter(self, inner);
			}
			return SkinTrace.Wrap(self, skinIndex, inner);
		}

		private static IEnumerator SyncAfter(ModelSkinController controller, IEnumerator inner)
		{
			while (inner.MoveNext())
			{
				yield return inner.Current;
			}
			AccessoryBoneRig accessoryBoneRig = default(AccessoryBoneRig);
			if (Object.op_Implicit((Object)(object)controller) && ((Component)controller).TryGetComponent<AccessoryBoneRig>(ref accessoryBoneRig))
			{
				accessoryBoneRig.Sync();
			}
		}

		private void ReportBodySkins()
		{
			InfernoSkin[] all = InfernoSkins.All;
			foreach (InfernoSkin infernoSkin in all)
			{
				try
				{
					ModelSkinController val = InfernoSkins.FindBodyModelSkinController(infernoSkin.BodyName);
					SkinDef val2 = infernoSkin.FindBodySkin();
					if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2))
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)(SkinTrace.DescribeModel(infernoSkin.Survivor + " body", val) + "\n" + SkinTrace.DescribeLegacyTargets(val2, val) + "\n" + SkinTrace.DescribeSkin(val2, "  ") + "\n" + SkinTrace.DescribeSkin(val.skins[0], "  ")));
					}
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"{infernoSkin.Survivor}: body report failed: {arg}");
				}
			}
		}

		private void ReportDisplaySkins()
		{
			MainMenuController.OnMainMenuInitialised -= ReportDisplaySkins;
			InfernoSkin[] all = InfernoSkins.All;
			foreach (InfernoSkin infernoSkin in all)
			{
				try
				{
					SurvivorDef val = SurvivorCatalog.FindSurvivorDefFromBody(BodyCatalog.FindBodyPrefab(infernoSkin.BodyName));
					ModelSkinController val2 = ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.displayPrefab)) ? val.displayPrefab.GetComponentInChildren<ModelSkinController>() : null);
					if (!Object.op_Implicit((Object)(object)val2))
					{
						continue;
					}
					string text = SkinTrace.DescribeModel(infernoSkin.Survivor + " display", val2);
					SkinDef[] skins = val2.skins;
					foreach (SkinDef skin in skins)
					{
						if (InfernoSkins.Identify(skin) == infernoSkin)
						{
							text = text + "\n" + SkinTrace.DescribeSkin(skin, "  ");
						}
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)text);
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"{infernoSkin.Survivor}: display report failed: {arg}");
				}
			}
		}

		private static string BuildId()
		{
			return typeof(InfernoSkinFixPlugin).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "unknown";
		}

		private string OwnHash()
		{
			try
			{
				using SHA256 sHA = SHA256.Create();
				using FileStream inputStream = File.OpenRead(((BaseUnityPlugin)this).Info.Location);
				return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "");
			}
			catch (Exception ex)
			{
				return "unavailable (" + ex.GetType().Name + ")";
			}
		}
	}
	internal sealed class InfernoSkin
	{
		public readonly string Survivor;

		public readonly string BodyName;

		public readonly string SkinName;

		public readonly string Token;

		public InfernoSkin(string survivor, string bodyName, string skinName, string token)
		{
			Survivor = survivor;
			BodyName = bodyName;
			SkinName = skinName;
			Token = token;
		}

		public SkinDef FindBodySkin()
		{
			ModelSkinController val = InfernoSkins.FindBodyModelSkinController(BodyName);
			if (!Object.op_Implicit((Object)(object)val) || val.skins == null)
			{
				return null;
			}
			SkinDef[] skins = val.skins;
			foreach (SkinDef val2 in skins)
			{
				if (Object.op_Implicit((Object)(object)val2) && ((Object)val2).name == SkinName && val2.nameToken == Token)
				{
					return val2;
				}
			}
			return null;
		}
	}
	internal static class InfernoSkins
	{
		public const string InfernoGuid = "HIFU.Inferno";

		public const string SkinModGuid = "com.Dotflare.InfernoSkinMod";

		public static readonly InfernoSkin Commando = new InfernoSkin("Commando", "CommandoBody", "MMando", "DOTFLARE_SKIN_MMANDO_NAME");

		public static readonly InfernoSkin Bandit = new InfernoSkin("Bandit", "Bandit2Body", "DBandit", "DOTFLARE_SKIN_DBANDIT_NAME");

		public static readonly InfernoSkin Captain = new InfernoSkin("Captain", "CaptainBody", "BCaptain", "DOTFLARE_SKIN_BCAPTAIN_NAME");

		public static readonly InfernoSkin Mercenary = new InfernoSkin("Mercenary", "MercBody", "HMerc", "DOTFLARE_SKIN_HMERC_NAME");

		public static readonly InfernoSkin Artificer = new InfernoSkin("Artificer", "MageBody", "CArti", "DOTFLARE_SKIN_CARTI_NAME");

		public static readonly InfernoSkin Railgunner = new InfernoSkin("Railgunner", "RailgunnerBody", "PGunner", "DOTFLARE_SKIN_PGUNNER_NAME");

		public static readonly InfernoSkin[] All = new InfernoSkin[6] { Commando, Bandit, Captain, Mercenary, Artificer, Railgunner };

		public static InfernoSkin Identify(SkinDef skin)
		{
			if (!Object.op_Implicit((Object)(object)skin))
			{
				return null;
			}
			string name = ((Object)skin).name;
			InfernoSkin[] all = All;
			foreach (InfernoSkin infernoSkin in all)
			{
				if (skin.nameToken == infernoSkin.Token && (name == infernoSkin.SkinName || name.StartsWith(infernoSkin.SkinName + "(", StringComparison.Ordinal)))
				{
					return infernoSkin;
				}
			}
			return null;
		}

		public static InfernoSkin FindOnController(ModelSkinController controller)
		{
			if (!Object.op_Implicit((Object)(object)controller) || controller.skins == null)
			{
				return null;
			}
			SkinDef[] skins = controller.skins;
			for (int i = 0; i < skins.Length; i++)
			{
				InfernoSkin infernoSkin = Identify(skins[i]);
				if (infernoSkin != null)
				{
					return infernoSkin;
				}
			}
			return null;
		}

		public static ModelSkinController FindBodyModelSkinController(string bodyName)
		{
			GameObject val = BodyCatalog.FindBodyPrefab(bodyName);
			ModelLocator val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ModelLocator>() : null);
			if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val2.modelTransform))
			{
				return null;
			}
			return ((Component)val2.modelTransform).GetComponent<ModelSkinController>();
		}

		public static string GetPluginVersion(string guid)
		{
			if (!Chainloader.PluginInfos.TryGetValue(guid, out var value))
			{
				return null;
			}
			return value.Metadata.Version.ToString();
		}
	}
	internal static class InfernoDynamicSkins
	{
		private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		public static object Manager { get; private set; }

		public static string BindError { get; private set; }

		public static int ModifiedObjectCount
		{
			get
			{
				if (!(GetField(Manager, "ModifiedObjects") is ICollection collection))
				{
					return -1;
				}
				return collection.Count;
			}
		}

		public static bool Bind()
		{
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue("com.Dotflare.InfernoSkinMod", out var value) || !Object.op_Implicit((Object)(object)value.Instance))
				{
					return Fail("InfernoSkinMod is not loaded");
				}
				Type type = ((object)value.Instance).GetType();
				Manager = type.GetField("_DynamicSkinManager", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
				return Manager != null || Fail(type.FullName + "._DynamicSkinManager not found");
			}
			catch (Exception ex)
			{
				return Fail(ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static bool Fail(string error)
		{
			BindError = error;
			Manager = null;
			return false;
		}

		public static object GetField(object owner, string name)
		{
			return owner?.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(owner);
		}

		public static object[] GetModifications(string token)
		{
			if (!(GetField(Manager, "ModificationList") is IDictionary dictionary) || !(dictionary[token] is IDictionary dictionary2))
			{
				return Array.Empty<object>();
			}
			object[] array = new object[dictionary2.Count];
			dictionary2.Values.CopyTo(array, 0);
			return array;
		}

		public static void DisableOriginal(string token)
		{
			if (GetField(Manager, "SkinDefs") is IDictionary dictionary)
			{
				dictionary.Remove(token);
			}
		}
	}
	internal sealed class SkinRepair
	{
		public readonly InfernoSkin Skin;

		public readonly string BaseSkinName;

		public readonly string[] ExtraActivations;

		public readonly bool AccessoryBones;

		public SkinRepair(InfernoSkin skin, string baseSkinName, string[] extraActivations = null, bool accessoryBones = false)
		{
			Skin = skin;
			BaseSkinName = baseSkinName;
			ExtraActivations = extraActivations ?? Array.Empty<string>();
			AccessoryBones = accessoryBones;
		}
	}
	internal static class SkinRepairs
	{
		internal static Action<string> LogInfo = delegate
		{
		};

		internal static Action<string> LogError = delegate
		{
		};

		public static readonly SkinRepair[] All = new SkinRepair[3]
		{
			new SkinRepair(InfernoSkins.Captain, "skinCaptainDefault"),
			new SkinRepair(InfernoSkins.Bandit, "skinBandit2Default", new string[1] { "Bandit2AccessoriesMesh" }, accessoryBones: true),
			new SkinRepair(InfernoSkins.Railgunner, "skinRailGunnerDefault", null, accessoryBones: true)
		};

		private static readonly Dictionary<InfernoSkin, AccessoryRigSpec> rigs = new Dictionary<InfernoSkin, AccessoryRigSpec>();

		private static readonly HashSet<SkinDef> patched = new HashSet<SkinDef>();

		private static readonly Dictionary<InfernoSkin, string> failures = new Dictionary<InfernoSkin, string>();

		public static AccessoryRigSpec FindRig(InfernoSkin skin)
		{
			if (skin == null || !rigs.TryGetValue(skin, out var value))
			{
				return null;
			}
			return value;
		}

		public static void PatchSkinDefs()
		{
			SkinRepair[] all = All;
			foreach (SkinRepair skinRepair in all)
			{
				try
				{
					string text = Patch(skinRepair);
					if (text != null)
					{
						failures[skinRepair.Skin] = text;
					}
				}
				catch (Exception ex)
				{
					failures[skinRepair.Skin] = ex.GetType().Name + ": " + ex.Message;
				}
			}
		}

		private static string Patch(SkinRepair repair)
		{
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			InfernoSkin skin = repair.Skin;
			ModelSkinController val = InfernoSkins.FindBodyModelSkinController(skin.BodyName);
			SkinDef val2 = skin.FindBodySkin();
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2))
			{
				return "skin '" + skin.SkinName + "' not found on " + skin.BodyName;
			}
			if (patched.Contains(val2))
			{
				return null;
			}
			if (val2.runtimeSkin != null || Object.op_Implicit((Object)(object)val2.skinDefParams))
			{
				return "skin was already converted or baked before InfernoSkinFix could repair it";
			}
			SkinDef val3 = ((IEnumerable<SkinDef>)val.skins).FirstOrDefault((Func<SkinDef, bool>)((SkinDef d) => Object.op_Implicit((Object)(object)d) && ((Object)d).name == repair.BaseSkinName));
			if (!Object.op_Implicit((Object)(object)val3))
			{
				return "vanilla skin '" + repair.BaseSkinName + "' not found";
			}
			List<Transform> list = new List<Transform>();
			string[] extraActivations = repair.ExtraActivations;
			foreach (string text in extraActivations)
			{
				Transform val4 = ((Component)val).transform.Find(text);
				if (!Object.op_Implicit((Object)(object)val4))
				{
					return "'" + text + "' not found on the body model";
				}
				list.Add(val4);
			}
			AccessoryRigSpec accessoryRigSpec = null;
			if (repair.AccessoryBones)
			{
				accessoryRigSpec = AccessoryRigSpec.TryCreate(skin, val2, val, out var error);
				if (accessoryRigSpec == null)
				{
					return "accessory bones: " + error;
				}
			}
			if (!val2.baseSkins.Contains(val3))
			{
				val2.baseSkins = ((IEnumerable<SkinDef>)(object)new SkinDef[1] { val3 }).Concat(val2.baseSkins.Where((SkinDef b) => Object.op_Implicit((Object)(object)b))).ToArray();
			}
			foreach (Transform t in list)
			{
				if (!val2.gameObjectActivations.Any((GameObjectActivation a) => (Object)(object)a.gameObject == (Object)(object)((Component)t).gameObject))
				{
					val2.gameObjectActivations = val2.gameObjectActivations.Append(new GameObjectActivation
					{
						gameObject = ((Component)t).gameObject,
						shouldActivate = true
					}).ToArray();
				}
			}
			if (accessoryRigSpec != null)
			{
				rigs[skin] = accessoryRigSpec;
				InfernoDynamicSkins.DisableOriginal(skin.Token);
			}
			patched.Add(val2);
			return null;
		}

		public static void Report()
		{
			List<string> list = new List<string>();
			SkinRepair[] all = All;
			foreach (SkinRepair repair in all)
			{
				InfernoSkin skin = repair.Skin;
				if (failures.TryGetValue(skin, out var value))
				{
					LogError(skin.Survivor + " (" + skin.SkinName + ") not repaired: " + value);
					continue;
				}
				SkinDef val = skin.FindBodySkin();
				RuntimeSkin val2 = (Object.op_Implicit((Object)(object)val) ? val.runtimeSkin : null);
				if (val2 == null || !val.baseSkins.Any((SkinDef b) => Object.op_Implicit((Object)(object)b) && ((Object)b).name == repair.BaseSkinName))
				{
					LogError(skin.Survivor + " (" + skin.SkinName + ") not repaired: the skin was not patched before it was baked.");
					continue;
				}
				AccessoryRigSpec accessoryRigSpec = FindRig(skin);
				list.Add($"{skin.Survivor} (base {repair.BaseSkinName}, {val2.gameObjectActivationTemplates.Length} activations" + ((accessoryRigSpec != null) ? string.Format(", +{0} bone{1} at {2} for {3} meshes", accessoryRigSpec.ChainLength, (accessoryRigSpec.ChainLength == 1) ? "" : "s", accessoryRigSpec.InsertAt, accessoryRigSpec.Meshes.Count) : "") + ")");
			}
			if (list.Count > 0)
			{
				LogInfo("Repaired Inferno skins: " + string.Join("; ", list) + ". Commando, Mercenary and Artificer need no repair.");
			}
		}
	}
	internal static class SkinTrace
	{
		internal static Action<string> Log = delegate
		{
		};

		internal static bool Enabled;

		private static int nextId;

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

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

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

		private static bool IsDetailed(InfernoSkin s)
		{
			if (s != InfernoSkins.Captain && s != InfernoSkins.Bandit)
			{
				return s == InfernoSkins.Railgunner;
			}
			return true;
		}

		internal static IEnumerator Wrap(ModelSkinController controller, int requestedIndex, IEnumerator inner)
		{
			if (!Enabled)
			{
				return inner;
			}
			InfernoSkin infernoSkin = InfernoSkins.FindOnController(controller);
			if (infernoSkin != null)
			{
				return Traced(controller, infernoSkin, requestedIndex, inner);
			}
			return inner;
		}

		private static IEnumerator Traced(ModelSkinController controller, InfernoSkin survivor, int requestedIndex, IEnumerator inner)
		{
			int id = ++nextId;
			int key = ((Object)controller).GetInstanceID();
			pendingByController.TryGetValue(key, out var overlapping);
			pendingByController[key] = overlapping + 1;
			bool completed = false;
			try
			{
				Safe(delegate
				{
					Snapshot(id, controller, survivor, requestedIndex, "start", overlapping);
				});
				while (inner.MoveNext())
				{
					yield return inner.Current;
				}
				completed = true;
			}
			finally
			{
				if (pendingByController.TryGetValue(key, out var value))
				{
					pendingByController[key] = value - 1;
				}
				if (!completed)
				{
					Log($"#{id} {survivor.Survivor}: skin application ended without completing (exception or abandoned coroutine).");
				}
			}
			Safe(delegate
			{
				Snapshot(id, controller, survivor, requestedIndex, "done", pendingByController[key]);
			});
			if (Object.op_Implicit((Object)(object)controller))
			{
				((MonoBehaviour)controller).StartCoroutine(Settled(id, controller, survivor, requestedIndex));
			}
		}

		private static IEnumerator Settled(int id, ModelSkinController controller, InfernoSkin survivor, int requestedIndex)
		{
			yield return (object)new WaitForSecondsRealtime(1f);
			if (Object.op_Implicit((Object)(object)controller))
			{
				Safe(delegate
				{
					Snapshot(id, controller, survivor, requestedIndex, "+1s", pendingByController.TryGetValue(((Object)controller).GetInstanceID(), out var value) ? value : 0);
				});
			}
		}

		private static void Safe(Action a)
		{
			try
			{
				a();
			}
			catch (Exception ex)
			{
				Log("trace failed: " + ex);
			}
		}

		private static void Snapshot(int id, ModelSkinController controller, InfernoSkin survivor, int requestedIndex, string phase, int pending)
		{
			CharacterModel component = ((Component)controller).GetComponent<CharacterModel>();
			SkinDef[] array = controller.skins ?? Array.Empty<SkinDef>();
			int num = (((uint)requestedIndex < array.Length) ? requestedIndex : 0);
			SkinDef val = ((array.Length != 0) ? array[num] : null);
			SkinDef s = (((uint)controller.currentSkinIndex < array.Length) ? array[controller.currentSkinIndex] : null);
			string text = ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.body)) ? "body" : "display");
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(string.Format("#{0} {1} {2} {3}: target[{4}]={5}{6} current[{7}]={8} otherPending={9} dynSkinsModified={10}", id, survivor.Survivor, text, phase, requestedIndex, Name(val), (InfernoSkins.Identify(val) != null) ? " (Inferno)" : "", controller.currentSkinIndex, Name(s), pending, InfernoDynamicSkins.ModifiedObjectCount));
			if (Object.op_Implicit((Object)(object)val) && describedSkins.Add(((Object)val).GetInstanceID()) && val.runtimeSkin != null)
			{
				stringBuilder.Append('\n').Append(DescribeSkin(val, "  "));
			}
			HashSet<string> hashSet = MeshTemplatePaths(val);
			Renderer[] componentsInChildren = ((Component)controller).GetComponentsInChildren<Renderer>(true);
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			StringBuilder stringBuilder2 = new StringBuilder();
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Renderer val2 = componentsInChildren[i];
				string text2 = PathFrom(((Component)controller).transform, ((Component)val2).transform);
				bool hasMeshSlot;
				Mesh val3 = MeshOf(val2, out hasMeshSlot);
				bool flag = ((Component)val2).gameObject.activeInHierarchy && val2.enabled;
				List<string> list = new List<string>();
				if (!((Component)val2).gameObject.activeInHierarchy)
				{
					num2++;
				}
				if (!((Component)val2).gameObject.activeInHierarchy && hashSet.Contains(text2))
				{
					list.Add("OFF-BUT-SKINNED");
					num6++;
				}
				if (hasMeshSlot && flag && !Object.op_Implicit((Object)(object)val3))
				{
					list.Add("NO-MESH");
					num3++;
				}
				if (flag && !(val2 is ParticleSystemRenderer) && val2.sharedMaterials.All((Material m) => !Object.op_Implicit((Object)(object)m)))
				{
					list.Add("NO-MATERIAL");
					num4++;
				}
				string text3 = "";
				SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null);
				if (val4 != null)
				{
					int num7 = val4.bones.Length;
					int num8 = (Object.op_Implicit((Object)(object)val3) ? BindposeCount(val3) : (-1));
					text3 = $" bones={num7}" + (Object.op_Implicit((Object)(object)val3) ? (" bindposes=" + ((num8 >= 0) ? num8.ToString() : "?")) : "");
					if (num8 >= 0 && num8 != num7)
					{
						list.Add("BONES-MISMATCH");
						num5++;
					}
				}
				stringBuilder2.Append(string.Format("\n  r[{0}] {1} '{2}' self={3} hier={4} en={5}", i, ((object)val2).GetType().Name.Replace("Renderer", ""), text2, On(((Component)val2).gameObject.activeSelf), On(((Component)val2).gameObject.activeInHierarchy), On(val2.enabled)) + " mesh=" + (hasMeshSlot ? MeshName(val3) : "-") + text3 + " mats=[" + string.Join(",", val2.sharedMaterials.Select((Material m) => (!Object.op_Implicit((Object)(object)m)) ? "null" : ((Object)m).name)) + "]" + ((list.Count > 0) ? ("  !! " + string.Join(" ", list)) : ""));
			}
			stringBuilder.Append($"\n  renderers={componentsInChildren.Length} inactive={num2} offButSkinned={num6} noMesh={num3} noMaterial={num4} boneMismatch={num5}");
			if (IsDetailed(survivor))
			{
				stringBuilder.Append(stringBuilder2);
				if (Object.op_Implicit((Object)(object)component))
				{
					stringBuilder.Append($"\n  baseRendererInfos({component.baseRendererInfos.Length}): " + string.Join(", ", component.baseRendererInfos.Select((RendererInfo ri) => (Object.op_Implicit((Object)(object)ri.renderer) ? PathFrom(((Component)controller).transform, ((Component)ri.renderer).transform) : "null") + "=" + (Object.op_Implicit((Object)(object)ri.defaultMaterial) ? ((Object)ri.defaultMaterial).name : "null"))));
					stringBuilder.Append($"\n  activatedBySkin({component.gameObjectActivationTransforms.Count}): " + string.Join(", ", component.gameObjectActivationTransforms.Select((Transform t) => (!Object.op_Implicit((Object)(object)t)) ? "destroyed" : (PathFrom(((Component)controller).transform, t) + "=" + On(((Component)t).gameObject.activeSelf)))));
				}
			}
			Log(stringBuilder.ToString());
		}

		private static HashSet<string> MeshTemplatePaths(SkinDef skin)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>();
			if (Object.op_Implicit((Object)(object)skin) && skin.runtimeSkin != null)
			{
				Enumerator<MeshReplacementTemplate> enumerator = skin.runtimeSkin.meshReplacementTemplates.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						MeshReplacementTemplate current = enumerator.Current;
						if (Object.op_Implicit((Object)(object)current.meshReference.directRef) || (current.meshReference.address != null && ((AssetReference)current.meshReference.address).RuntimeKeyIsValid()))
						{
							hashSet.Add(current.transformPath);
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			return hashSet;
		}

		internal static string DescribeSkin(SkinDef skin, string indent)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected I4, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: 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)
			//IL_015b: 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_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: 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_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			string text = (Object.op_Implicit((Object)(object)skin.rootObject) ? PathFrom(null, skin.rootObject.transform) : "null");
			string text2 = string.Join(",", (skin.baseSkins ?? Array.Empty<SkinDef>()).Select(Name));
			stringBuilder.Append($"{indent}skin {Name(skin)} token={skin.nameToken} catalogIndex={(int)skin.skinIndex} baseSkins=[{text2}] rootObject={text}" + " unlockable=" + (Object.op_Implicit((Object)(object)skin.unlockableDef) ? skin.unlockableDef.cachedName : "none") + " params=" + (Object.op_Implicit((Object)(object)skin.skinDefParams) ? ((Object)skin.skinDefParams).name : "none"));
			RuntimeSkin runtimeSkin = skin.runtimeSkin;
			if (runtimeSkin == null)
			{
				return stringBuilder.Append(" (not baked)").ToString();
			}
			Enumerator<GameObjectActivationTemplate> enumerator = runtimeSkin.gameObjectActivationTemplates.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					GameObjectActivationTemplate current = enumerator.Current;
					stringBuilder.Append("\n" + indent + "  activation '" + current.transformPath + "'=" + (current.isCustomActivation ? "custom prefab" : On(current.shouldActivate)));
				}
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			Enumerator<MeshReplacementTemplate> enumerator2 = runtimeSkin.meshReplacementTemplates.GetEnumerator();
			try
			{
				while (enumerator2.MoveNext())
				{
					MeshReplacementTemplate current2 = enumerator2.Current;
					stringBuilder.Append("\n" + indent + "  mesh '" + current2.transformPath + "'=" + (Object.op_Implicit((Object)(object)current2.meshReference.directRef) ? MeshName(current2.meshReference.directRef) : ((current2.meshReference.address != null && ((AssetReference)current2.meshReference.address).RuntimeKeyIsValid()) ? ("address:" + ((AssetReference)current2.meshReference.address).RuntimeKey) : "null (hide)")));
				}
			}
			finally
			{
				((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
			}
			Enumerator<RendererInfoTemplate> enumerator3 = runtimeSkin.rendererInfoTemplates.GetEnumerator();
			try
			{
				while (enumerator3.MoveNext())
				{
					RendererInfoTemplate current3 = enumerator3.Current;
					AssetOrDirectReference<Material> materialReference = current3.materialReference;
					string text3 = (Object.op_Implicit((Object)(object)materialReference.directRef) ? ((Object)materialReference.directRef).name : ((materialReference.address != null && ((AssetReference)materialReference.address).RuntimeKeyIsValid()) ? ("address:" + ((AssetReference)materialReference.address).RuntimeKey) : "null"));
					stringBuilder.Append("\n" + indent + "  material '" + current3.transformPath + "'=" + text3);
				}
			}
			finally
			{
				((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
			}
			return stringBuilder.ToString();
		}

		internal static string DescribeModel(string label, ModelSkinController controller)
		{
			StringBuilder stringBuilder = new StringBuilder(label + " model '" + PathFrom(null, ((Component)controller).transform) + "' skins=[" + string.Join(",", controller.skins.Select(Name)) + "]");
			Renderer[] componentsInChildren = ((Component)controller).GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Renderer val = componentsInChildren[i];
				bool hasMeshSlot;
				Mesh mesh = MeshOf(val, out hasMeshSlot);
				SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
				string text = ((val2 != null) ? $" bones={val2.bones.Length}" : "");
				stringBuilder.Append(string.Format("\n  r[{0}] {1} '{2}' self={3} en={4} mesh={5}{6}", i, ((object)val).GetType().Name.Replace("Renderer", ""), PathFrom(((Component)controller).transform, ((Component)val).transform), On(((Component)val).gameObject.activeSelf), On(val.enabled), hasMeshSlot ? MeshName(mesh) : "-", text));
			}
			return stringBuilder.ToString();
		}

		internal static string DescribeLegacyTargets(SkinDef skin, ModelSkinController bodyController)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] renderers = ((Component)bodyController).GetComponentsInChildren<Renderer>(true);
			StringBuilder stringBuilder = new StringBuilder($"  legacy fields of {Name(skin)}: activations={skin.gameObjectActivations.Length}");
			MeshReplacement[] meshReplacements = skin.meshReplacements;
			foreach (MeshReplacement val in meshReplacements)
			{
				stringBuilder.Append("\n    meshReplacement " + Target(val.renderer) + " <- " + MeshName(val.mesh) + (Object.op_Implicit((Object)(object)val.mesh) ? $" bindposes={BindposeCount(val.mesh)}" : ""));
			}
			RendererInfo[] rendererInfos = skin.rendererInfos;
			foreach (RendererInfo val2 in rendererInfos)
			{
				stringBuilder.Append(string.Format("\n    rendererInfo {0} <- {1} shadows={2}", Target(val2.renderer), Object.op_Implicit((Object)(object)val2.defaultMaterial) ? ((Object)val2.defaultMaterial).name : "null", val2.defaultShadowCastingMode));
			}
			return stringBuilder.ToString();
			string Target(Renderer r)
			{
				if (!Object.op_Implicit((Object)(object)r))
				{
					return "null";
				}
				return $"r[{Array.IndexOf(renderers, r)}]'{PathFrom(((Component)bodyController).transform, ((Component)r).transform)}'";
			}
		}

		private static Mesh MeshOf(Renderer r, out bool hasMeshSlot)
		{
			hasMeshSlot = true;
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null);
			if (val != null)
			{
				return val.sharedMesh;
			}
			if (r is MeshRenderer)
			{
				MeshFilter component = ((Component)r).GetComponent<MeshFilter>();
				if (!Object.op_Implicit((Object)(object)component))
				{
					return null;
				}
				return component.sharedMesh;
			}
			hasMeshSlot = false;
			return null;
		}

		private static int BindposeCount(Mesh mesh)
		{
			int instanceID = ((Object)mesh).GetInstanceID();
			if (!bindposeCountByMesh.TryGetValue(instanceID, out var value))
			{
				try
				{
					value = mesh.bindposes.Length;
				}
				catch (Exception)
				{
					value = -1;
				}
				bindposeCountByMesh[instanceID] = value;
			}
			return value;
		}

		private static string MeshName(Mesh mesh)
		{
			if (!Object.op_Implicit((Object)(object)mesh))
			{
				return "null";
			}
			return $"{((Object)mesh).name}(v{mesh.vertexCount})";
		}

		private static string Name(SkinDef s)
		{
			if (!Object.op_Implicit((Object)(object)s))
			{
				return "null";
			}
			return ((Object)s).name;
		}

		private static string On(bool b)
		{
			if (!b)
			{
				return "0";
			}
			return "1";
		}

		internal static string PathFrom(Transform root, Transform t)
		{
			List<string> list = new List<string>();
			Transform val = t;
			while (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)root)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}
	}
}