Decompiled source of TetoCommandoSkin v1.0.1

plugins/TetoCommandoSkin/TetoCommandoSkin.dll

Decompiled 8 hours 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.Text;
using BepInEx;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[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("TetoCommandoSkin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("TetoCommandoSkin")]
[assembly: AssemblyTitle("TetoCommandoSkin")]
[assembly: AssemblyVersion("1.0.1.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 TetoCommandoSkin
{
	[BepInPlugin("meko.TetoCommandoSkin", "TetoCommandoSkin", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "meko.TetoCommandoSkin";

		public const string Name = "TetoCommandoSkin";

		public const string Version = "1.0.1";

		private const string BodyAddress = "RoR2/Base/Commando/CommandoBody.prefab";

		private const string NameToken = "MEKO_SKIN_COMMANDO_TETO_NAME";

		private ConfigEntry<float> scaleMultiplier;

		private ConfigEntry<string> boneOverrides;

		private ConfigEntry<bool> exportDebugObj;

		private ConfigEntry<float> shoulderLowerPercent;

		private ConfigEntry<float> headRaisePercent;

		private string dir;

		private GameObject body;

		private Object bodySkin;

		private Object displaySkin;

		private Material material;

		private Sprite icon;

		private static readonly Dictionary<string, Type> typeCache = new Dictionary<string, Type>();

		private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private void Awake()
		{
			dir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			scaleMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "ScaleMultiplier", 1f, "Multiplies the model's overall size after it is fitted to Commando's height.");
			boneOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Fitting", "BoneOverrides", "", "Only if the log says a bone was matched wrongly: role=ExactBoneName pairs separated by ';', e.g. hand.l=hand.l;neck=neck. Roles: " + string.Join(", ", Retargeter.Roles));
			shoulderLowerPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "ShoulderLowerPercent", 0f, "Lowers the shoulders by this percent of the character's height (negative raises them).");
			headRaisePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "HeadRaisePercent", 0f, "Raises the neck and head by this percent of the character's height (negative lowers them).");
			exportDebugObj = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ExportDebugObj", false, "Writes TetoCommandoSkin_debug.obj (the fitted mesh in Commando's bind pose) next to the plugin so it can be inspected.");
			try
			{
				Build();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Teto skin was not added: " + ex));
			}
		}

		private void Build()
		{
			body = LoadAddressable<GameObject>("RoR2/Base/Commando/CommandoBody.prefab") ?? throw new Exception("could not load RoR2/Base/Commando/CommandoBody.prefab");
			Component model = body.GetComponentInChildren(FindType("RoR2.CharacterModel"), true) ?? throw new Exception("CommandoBody has no CharacterModel");
			SkinnedMeshRenderer smr = MainRenderer(model);
			Mesh mesh = FitMesh(model, smr, "body", exportDebugObj.Value);
			material = MakeMaterial(model, smr);
			icon = LoadIcon();
			FindType("R2API.LanguageAPI").GetMethod("Add", new Type[2]
			{
				typeof(string),
				typeof(string)
			}).Invoke(null, new object[2] { "MEKO_SKIN_COMMANDO_TETO_NAME", "Kasane Teto" });
			bodySkin = CreateSkinDef(model, smr, mesh, "skinCommandoTeto");
			bool flag = (bool)FindType("R2API.Skins").GetMethod("AddSkinToCharacter", new Type[2]
			{
				typeof(GameObject),
				FindType("RoR2.SkinDef")
			}).Invoke(null, new object[2] { body, bodySkin });
			((BaseUnityPlugin)this).Logger.LogInfo((object)(flag ? "Kasane Teto skin added to Commando." : "R2API refused the skin (see R2API log above)."));
			if (flag)
			{
				Subscribe("RoR2.UI.MainMenu.MainMenuController", "OnMainMenuInitialised", EnsureDisplaySkin);
				SceneManager.sceneLoaded += delegate
				{
					EnsureDisplaySkin();
				};
			}
		}

		private void EnsureDisplaySkin()
		{
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			try
			{
				GameObject val = FindDisplayPrefab();
				if (!Object.op_Implicit((Object)(object)val))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Commando display prefab not found; preview not patched.");
					return;
				}
				Component componentInChildren = val.GetComponentInChildren(FindType("RoR2.CharacterModel"), true);
				if (!Object.op_Implicit((Object)(object)componentInChildren))
				{
					return;
				}
				Type type = FindType("RoR2.ModelSkinController");
				Type elementType = FindType("RoR2.SkinDef");
				Array array = (Array)Get(body.GetComponentInChildren(type, true), "skins");
				int num = Array.IndexOf(array, bodySkin);
				if (num < 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Our skin is no longer in Commando's skin list.");
					return;
				}
				Component o = componentInChildren.GetComponent(type) ?? componentInChildren.gameObject.AddComponent(type);
				Array array2 = (Get(o, "skins") as Array) ?? Array.CreateInstance(elementType, 0);
				if (!Object.op_Implicit(displaySkin))
				{
					SkinnedMeshRenderer smr = MainRenderer(componentInChildren);
					displaySkin = CreateSkinDef(componentInChildren, smr, FitMesh(componentInChildren, smr, "display", writeDebug: false), "skinCommandoTetoDisplay");
				}
				try
				{
					Set(displaySkin, "skinIndex", Get(bodySkin, "skinIndex"));
				}
				catch
				{
				}
				if (num < array2.Length && array2.GetValue(num) == displaySkin)
				{
					return;
				}
				if (array2.Length <= num)
				{
					Array array3 = Array.CreateInstance(elementType, num + 1);
					Array.Copy(array2, array3, array2.Length);
					Object val2 = (Object)((array2.Length > 0) ? /*isinst with value type is only supported in some contexts*/: null);
					for (int i = array2.Length; i < num; i++)
					{
						Object val3 = Object.Instantiate((Object)array.GetValue(i));
						try
						{
							Set(val3, "skinIndex", Get(array.GetValue(i), "skinIndex"));
						}
						catch
						{
						}
						if (Object.op_Implicit(val2))
						{
							Array array4 = Array.CreateInstance(elementType, 1);
							array4.SetValue(val2, 0);
							Set(val3, "baseSkins", array4);
							Set(val3, "rootObject", Get(val2, "rootObject") ?? componentInChildren.gameObject);
						}
						array3.SetValue(val3, i);
					}
					array2 = array3;
				}
				array2.SetValue(displaySkin, num);
				Set(o, "skins", array2);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Character-select preview: Teto placed in display slot {num} of {array2.Length}.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not set up the character-select preview: " + ex));
			}
		}

		private GameObject FindDisplayPrefab()
		{
			IEnumerable enumerable = (FindType("RoR2.SurvivorCatalog")?.GetProperty("survivorDefs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as IEnumerable) ?? (FindType("RoR2.SurvivorCatalog")?.GetField("survivorDefs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as IEnumerable);
			if (enumerable != null)
			{
				foreach (object item in enumerable)
				{
					Object val = (Object)((item is Object) ? item : null);
					if (val != null && Object.op_Implicit(val) && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)(object)body)
					{
						object obj = Get(item, "displayPrefab");
						return (GameObject)((obj is GameObject) ? obj : null);
					}
				}
			}
			return TryLoad<GameObject>("RoR2/Base/Commando/CommandoDisplay.prefab");
		}

		private static SkinnedMeshRenderer MainRenderer(Component model)
		{
			object obj = Get(model, "mainSkinnedMeshRenderer");
			return (SkinnedMeshRenderer)(((obj is SkinnedMeshRenderer) ? obj : null) ?? (from r in model.GetComponentsInChildren<SkinnedMeshRenderer>(true)
				orderby r.bones.Length descending
				select r).First());
		}

		private Object CreateSkinDef(Component model, SkinnedMeshRenderer smr, Mesh mesh, string name)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			Array array = (Array)Get(model, "baseRendererInfos");
			Array array2 = Array.CreateInstance(array.GetType().GetElementType(), array.Length);
			for (int i = 0; i < array.Length; i++)
			{
				object value = array.GetValue(i);
				if ((Object)Get(value, "renderer") == (Object)(object)smr)
				{
					Set(value, "defaultMaterial", material);
				}
				array2.SetValue(value, i);
			}
			Type type = FindType("RoR2.SkinDefParams+MeshReplacement");
			object obj = Activator.CreateInstance(type);
			Set(obj, "renderer", smr);
			Set(obj, "mesh", mesh);
			Array array3 = Array.CreateInstance(type, 1);
			array3.SetValue(obj, 0);
			Type elementType = FindType("RoR2.SkinDef");
			Component component = model.GetComponent(FindType("RoR2.ModelSkinController"));
			Array array4 = (Object.op_Implicit((Object)(object)component) ? (Get(component, "skins") as Array) : null);
			Array array5 = Array.CreateInstance(elementType, (array4 != null && array4.Length > 0) ? 1 : 0);
			if (array5.Length > 0)
			{
				array5.SetValue(array4.GetValue(0), 0);
			}
			Type type2 = FindType("R2API.SkinDefParamsInfo");
			object obj2 = Activator.CreateInstance(type2);
			Set(obj2, "Name", name);
			Set(obj2, "NameToken", "MEKO_SKIN_COMMANDO_TETO_NAME");
			Set(obj2, "Icon", icon);
			Set(obj2, "RootObject", model.gameObject);
			Set(obj2, "BaseSkins", array5);
			Set(obj2, "RendererInfos", array2);
			Set(obj2, "MeshReplacements", array3);
			return (Object)FindType("R2API.Skins").GetMethod("CreateNewSkinDef", new Type[1] { type2 }).Invoke(null, new object[1] { obj2 });
		}

		private Material MakeMaterial(Component model, SkinnedMeshRenderer smr)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			Array obj = (Array)Get(model, "baseRendererInfos");
			Material val = null;
			foreach (object item in obj)
			{
				if ((Object)Get(item, "renderer") == (Object)(object)smr)
				{
					object obj2 = Get(item, "defaultMaterial");
					val = (Material)((obj2 is Material) ? obj2 : null);
				}
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = ((Renderer)smr).sharedMaterial;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = TryLoad<Material>("RoR2/Base/Commando/matCommandoDualies.mat");
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = new Material(TryLoad<Shader>("RoR2/Base/Shaders/HGStandard.shader") ?? Shader.Find("Hopoo Games/Deferred/Standard") ?? Shader.Find("Standard"));
			}
			Material val2 = new Material(val)
			{
				name = "matTetoCommando"
			};
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Base material: " + ((Object)val).name + " / shader " + ((Object)val.shader).name));
			val2.mainTexture = (Texture)(object)LoadTexture("TetoAtlas.png", compress: true);
			SetTex(val2, "_MainTex", val2.mainTexture);
			SetTex(val2, "_NormalTex", null);
			SetTex(val2, "_EmTex", null);
			SetColor(val2, "_Color", Color.white);
			SetColor(val2, "_EmColor", Color.black);
			SetFloat(val2, "_SpecularStrength", 0.05f);
			SetFloat(val2, "_Smoothness", 0.1f);
			return val2;
		}

		private Mesh FitMesh(Component model, SkinnedMeshRenderer smr, string label, bool writeDebug)
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: 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)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Expected O, but got Unknown
			//IL_06fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b6: Unknown result type (might be due to invalid IL or missing references)
			Transform[] bones = smr.bones;
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("[{0}] renderer: {1}, {2} bones, mesh {3}", label, ((Object)smr).name, bones.Length, Object.op_Implicit((Object)(object)smr.sharedMesh) ? ((Object)smr.sharedMesh).name : "(none)"));
			Matrix4x4[] array = ReadBindposes(smr);
			List<Transform> list = model.GetComponentsInChildren<Transform>(true).ToList();
			Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>();
			for (int i = 0; i < list.Count; i++)
			{
				dictionary[list[i]] = i;
			}
			Dictionary<int, int> boneSlot = new Dictionary<int, int>();
			for (int j = 0; j < bones.Length; j++)
			{
				if (Object.op_Implicit((Object)(object)bones[j]) && dictionary.TryGetValue(bones[j], out var value) && !boneSlot.ContainsKey(value))
				{
					boneSlot[value] = j;
				}
			}
			Matrix4x4 worldToLocalMatrix = ((Component)smr).transform.worldToLocalMatrix;
			List<TargetBone> tgt = new List<TargetBone>();
			for (int k = 0; k < list.Count; k++)
			{
				Transform val = list[k];
				Vector3 val2;
				if (!boneSlot.TryGetValue(k, out var value2))
				{
					val2 = ((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(val.position);
				}
				else
				{
					Matrix4x4 inverse = ((Matrix4x4)(ref array[value2])).inverse;
					val2 = Vector4.op_Implicit(((Matrix4x4)(ref inverse)).GetColumn(3));
				}
				Vector3 val3 = val2;
				tgt.Add(new TargetBone
				{
					Name = ((Object)val).name,
					Parent = ((Object.op_Implicit((Object)(object)val.parent) && dictionary.TryGetValue(val.parent, out var value3)) ? value3 : (-1)),
					Pos = new V3(val3.x, val3.y, val3.z),
					Skinned = boneSlot.ContainsKey(k)
				});
			}
			if (label == "body")
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Commando skinned bones (name <- parent @ bind position):\n" + string.Join("\n", from b in tgt
					where b.Skinned
					select string.Format("  {0} <- {1} @ {2}", b.Name, (b.Parent >= 0) ? tgt[b.Parent].Name : "-", b.Pos))));
			}
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			Type type = FindType("ChildLocator");
			Component val4 = ((type != null) ? model.GetComponent(type) : null);
			if (Object.op_Implicit((Object)(object)val4))
			{
				MethodInfo method = type.GetMethod("FindChild", new Type[1] { typeof(string) });
				foreach (KeyValuePair<string, string> item in new Dictionary<string, string>
				{
					["hand.l"] = "HandL",
					["hand.r"] = "HandR",
					["head"] = "Head",
					["chest"] = "Chest",
					["pelvis"] = "Pelvis",
					["foot.l"] = "FootL",
					["foot.r"] = "FootR",
					["thigh.l"] = "ThighL",
					["thigh.r"] = "ThighR",
					["calf.l"] = "CalfL",
					["calf.r"] = "CalfR",
					["upperarm.l"] = "UpperArmL",
					["upperarm.r"] = "UpperArmR",
					["forearm.l"] = "LowerArmL",
					["forearm.r"] = "LowerArmR",
					["spine"] = "Stomach"
				})
				{
					object? obj = method?.Invoke(val4, new object[1] { item.Value });
					Transform val5 = (Transform)((obj is Transform) ? obj : null);
					if (Object.op_Implicit((Object)(object)val5) && dictionary.TryGetValue(val5, out var value4))
					{
						dictionary2[item.Key] = value4;
					}
				}
			}
			Dictionary<string, string> dictionary3 = new Dictionary<string, string>();
			string[] array2 = (boneOverrides.Value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int num = 0; num < array2.Length; num++)
			{
				string[] array3 = array2[num].Split('=');
				if (array3.Length == 2)
				{
					dictionary3[array3[0].Trim()] = array3[1].Trim();
				}
			}
			List<string> list2 = new List<string>();
			Dictionary<string, int> roles = Retargeter.FindRoles(tgt, dictionary2, dictionary3, list2);
			SourceModel src;
			using (FileStream s = File.OpenRead(Path.Combine(dir, "TetoSkin.rskn")))
			{
				src = SourceModel.Read(s);
			}
			Retargeter.Options opt = new Retargeter.Options
			{
				ScaleMultiplier = scaleMultiplier.Value,
				ShoulderLower = (double)shoulderLowerPercent.Value / 100.0,
				HeadRaise = (double)headRaisePercent.Value / 100.0
			};
			RetargetResult res = Retargeter.Fit(src, tgt, roles, opt);
			bool flag = Retargeter.NeedsWindingFlip(src, tgt, roles, res);
			list2.AddRange(res.Log);
			list2.Add("flip triangle winding: " + flag);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[" + label + "] fitting:\n  " + string.Join("\n  ", list2)));
			int num2 = res.Positions.Length;
			Mesh val6 = new Mesh
			{
				name = "TetoCommandoMesh_" + label
			};
			val6.vertices = ((IEnumerable<V3>)res.Positions).Select((Func<V3, Vector3>)((V3 p) => new Vector3((float)p.x, (float)p.y, (float)p.z))).ToArray();
			val6.normals = ((IEnumerable<V3>)res.Normals).Select((Func<V3, Vector3>)((V3 p) => new Vector3((float)p.x, (float)p.y, (float)p.z))).ToArray();
			val6.uv = Enumerable.Range(0, num2).Select((Func<int, Vector2>)((int num6) => new Vector2(src.U[num6], src.V[num6]))).ToArray();
			int[] array4 = (int[])src.Indices.Clone();
			if (flag)
			{
				for (int num3 = 0; num3 + 2 < array4.Length; num3 += 3)
				{
					int num4 = array4[num3 + 1];
					array4[num3 + 1] = array4[num3 + 2];
					array4[num3 + 2] = num4;
				}
			}
			val6.triangles = array4;
			BoneWeight[] array5 = (BoneWeight[])(object)new BoneWeight[num2];
			int v;
			for (v = 0; v < num2; v++)
			{
				int num5 = v;
				BoneWeight val7 = default(BoneWeight);
				((BoneWeight)(ref val7)).boneIndex0 = S(0);
				((BoneWeight)(ref val7)).weight0 = res.Weights[v][0];
				((BoneWeight)(ref val7)).boneIndex1 = S(1);
				((BoneWeight)(ref val7)).weight1 = res.Weights[v][1];
				((BoneWeight)(ref val7)).boneIndex2 = S(2);
				((BoneWeight)(ref val7)).weight2 = res.Weights[v][2];
				((BoneWeight)(ref val7)).boneIndex3 = S(3);
				((BoneWeight)(ref val7)).weight3 = res.Weights[v][3];
				array5[num5] = val7;
			}
			val6.boneWeights = array5;
			val6.bindposes = array;
			val6.RecalculateBounds();
			val6.RecalculateTangents();
			if (writeDebug)
			{
				WriteObj(Path.Combine(dir, "TetoCommandoSkin_debug.obj"), val6, tgt, roles);
			}
			return val6;
			int S(int num6)
			{
				if (!(res.Weights[v][num6] > 0f))
				{
					return 0;
				}
				return boneSlot[res.BoneIndices[v][num6]];
			}
		}

		private static void Subscribe(string typeName, string member, Action handler)
		{
			Type type = FindType(typeName);
			EventInfo eventInfo = type?.GetEvent(member, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (eventInfo != null)
			{
				eventInfo.AddEventHandler(null, Delegate.CreateDelegate(eventInfo.EventHandlerType, handler.Target, handler.Method));
				return;
			}
			FieldInfo fieldInfo = type?.GetField(member, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (fieldInfo != null && fieldInfo.FieldType == typeof(Action))
			{
				fieldInfo.SetValue(null, Delegate.Combine((Action)fieldInfo.GetValue(null), handler));
			}
		}

		private Matrix4x4[] ReadBindposes(SkinnedMeshRenderer smr)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			Transform[] bones = smr.bones;
			Matrix4x4[] array = null;
			try
			{
				if (Object.op_Implicit((Object)(object)smr.sharedMesh))
				{
					array = smr.sharedMesh.bindposes;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read Commando's bindposes (" + ex.Message + "); using the prefab pose."));
			}
			Matrix4x4[] array2 = bones.Select((Transform b) => (!Object.op_Implicit((Object)(object)b)) ? Matrix4x4.identity : (b.worldToLocalMatrix * ((Component)smr).transform.localToWorldMatrix)).ToArray();
			if (array != null && array.Length == bones.Length)
			{
				double num = 0.0;
				for (int num2 = 0; num2 < bones.Length; num2++)
				{
					double val = num;
					Matrix4x4 inverse = ((Matrix4x4)(ref array[num2])).inverse;
					Vector4 column = ((Matrix4x4)(ref inverse)).GetColumn(3);
					inverse = ((Matrix4x4)(ref array2[num2])).inverse;
					Vector4 val2 = column - ((Matrix4x4)(ref inverse)).GetColumn(3);
					num = Math.Max(val, ((Vector4)(ref val2)).magnitude);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Using mesh bindposes (prefab pose differs by up to {num:0.####}).");
				return array;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Using bindposes computed from the prefab pose.");
			return array2;
		}

		private Texture2D LoadTexture(string file, bool compress)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
			{
				name = Path.GetFileNameWithoutExtension(file)
			};
			ImageConversion.LoadImage(val, File.ReadAllBytes(Path.Combine(dir, file)), false);
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).anisoLevel = 4;
			if (compress)
			{
				val.Compress(true);
			}
			val.Apply(true, true);
			return val;
		}

		private Sprite LoadIcon()
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			ImageConversion.LoadImage(val, File.ReadAllBytes(Path.Combine(dir, "SkinIcon.png")));
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}

		private static void SetTex(Material m, string p, Texture t)
		{
			if (m.HasProperty(p))
			{
				m.SetTexture(p, t);
			}
		}

		private static void SetColor(Material m, string p, Color c)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (m.HasProperty(p))
			{
				m.SetColor(p, c);
			}
		}

		private static void SetFloat(Material m, string p, float f)
		{
			if (m.HasProperty(p))
			{
				m.SetFloat(p, f);
			}
		}

		private void WriteObj(string path, Mesh mesh, List<TargetBone> tgt, Dictionary<string, int> roles)
		{
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: 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_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			using (StreamWriter streamWriter = new StreamWriter(path))
			{
				streamWriter.WriteLine("# Teto fitted to Commando bind pose (Unity mesh space). Roles:");
				foreach (KeyValuePair<string, int> role in roles)
				{
					streamWriter.WriteLine($"# role {role.Key} {tgt[role.Value].Name} {tgt[role.Value].Pos}");
				}
				foreach (TargetBone item in tgt.Where((TargetBone b) => b.Skinned))
				{
					streamWriter.WriteLine("# bone " + item.Name + " " + ((item.Parent >= 0) ? tgt[item.Parent].Name : "-") + " " + item.Pos.x.ToString(invariantCulture) + " " + item.Pos.y.ToString(invariantCulture) + " " + item.Pos.z.ToString(invariantCulture));
				}
				Vector3[] vertices = mesh.vertices;
				foreach (Vector3 val in vertices)
				{
					streamWriter.WriteLine(string.Format(invariantCulture, "v {0} {1} {2}", val.x, val.y, val.z));
				}
				Vector2[] uv = mesh.uv;
				foreach (Vector2 val2 in uv)
				{
					streamWriter.WriteLine(string.Format(invariantCulture, "vt {0} {1}", val2.x, val2.y));
				}
				int[] triangles = mesh.triangles;
				for (int num2 = 0; num2 < triangles.Length; num2 += 3)
				{
					streamWriter.WriteLine($"f {triangles[num2] + 1} {triangles[num2 + 1] + 1} {triangles[num2 + 2] + 1}");
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Wrote " + path));
		}

		private static Type FindType(string fullName)
		{
			if (typeCache.TryGetValue(fullName, out var value))
			{
				return value;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					value = assembly.GetType(fullName, throwOnError: false);
				}
				catch
				{
					value = null;
				}
				if (value != null)
				{
					break;
				}
			}
			return typeCache[fullName] = value;
		}

		private static object Get(object o, string member)
		{
			Type type = o.GetType();
			FieldInfo field = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(o);
			}
			PropertyInfo property = type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				return property.GetValue(o);
			}
			throw new MissingMemberException(type.FullName, member);
		}

		private static void Set(object o, string member, object value)
		{
			Type type = o.GetType();
			FieldInfo field = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(o, value);
				return;
			}
			PropertyInfo property = type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				property.SetValue(o, value);
				return;
			}
			throw new MissingMemberException(type.FullName, member);
		}

		private T TryLoad<T>(string key) where T : Object
		{
			try
			{
				return LoadAddressable<T>(key);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load " + key + ": " + ex.GetBaseException().Message));
				return default(T);
			}
		}

		private static T LoadAddressable<T>(string key) where T : Object
		{
			object obj = (FindType("UnityEngine.AddressableAssets.Addressables") ?? throw new Exception("Addressables not found")).GetMethods(BindingFlags.Static | BindingFlags.Public).First((MethodInfo m) => m.Name == "LoadAssetAsync" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(object)).MakeGenericMethod(typeof(T))
				.Invoke(null, new object[1] { key });
			object? obj2 = obj.GetType().GetMethod("WaitForCompletion").Invoke(obj, null);
			return (T)((obj2 is T) ? obj2 : null);
		}
	}
	public struct V3
	{
		public double x;

		public double y;

		public double z;

		public double Length => Math.Sqrt(Dot(this, this));

		public V3 Normalized
		{
			get
			{
				double length = Length;
				if (!(length > 1E-12))
				{
					return new V3(0.0, 0.0, 0.0);
				}
				return this / length;
			}
		}

		public V3(double x, double y, double z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}

		public static V3 operator +(V3 a, V3 b)
		{
			return new V3(a.x + b.x, a.y + b.y, a.z + b.z);
		}

		public static V3 operator -(V3 a, V3 b)
		{
			return new V3(a.x - b.x, a.y - b.y, a.z - b.z);
		}

		public static V3 operator -(V3 a)
		{
			return new V3(0.0 - a.x, 0.0 - a.y, 0.0 - a.z);
		}

		public static V3 operator *(V3 a, double s)
		{
			return new V3(a.x * s, a.y * s, a.z * s);
		}

		public static V3 operator /(V3 a, double s)
		{
			return new V3(a.x / s, a.y / s, a.z / s);
		}

		public static double Dot(V3 a, V3 b)
		{
			return a.x * b.x + a.y * b.y + a.z * b.z;
		}

		public static V3 Cross(V3 a, V3 b)
		{
			return new V3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
		}

		public override string ToString()
		{
			return $"({x:0.###}, {y:0.###}, {z:0.###})";
		}
	}
	public struct M3
	{
		public double[] m;

		public static M3 Identity => FromRows(new V3(1.0, 0.0, 0.0), new V3(0.0, 1.0, 0.0), new V3(0.0, 0.0, 1.0));

		public M3 Transposed => new M3
		{
			m = new double[9]
			{
				m[0],
				m[3],
				m[6],
				m[1],
				m[4],
				m[7],
				m[2],
				m[5],
				m[8]
			}
		};

		public static M3 FromRows(V3 a, V3 b, V3 c)
		{
			return new M3
			{
				m = new double[9] { a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z }
			};
		}

		public static M3 FromCols(V3 a, V3 b, V3 c)
		{
			return new M3
			{
				m = new double[9] { a.x, b.x, c.x, a.y, b.y, c.y, a.z, b.z, c.z }
			};
		}

		public static V3 operator *(M3 a, V3 v)
		{
			return new V3(a.m[0] * v.x + a.m[1] * v.y + a.m[2] * v.z, a.m[3] * v.x + a.m[4] * v.y + a.m[5] * v.z, a.m[6] * v.x + a.m[7] * v.y + a.m[8] * v.z);
		}

		public static M3 operator *(M3 a, M3 b)
		{
			double[] array = new double[9];
			for (int i = 0; i < 3; i++)
			{
				for (int j = 0; j < 3; j++)
				{
					array[i * 3 + j] = a.m[i * 3] * b.m[j] + a.m[i * 3 + 1] * b.m[3 + j] + a.m[i * 3 + 2] * b.m[6 + j];
				}
			}
			return new M3
			{
				m = array
			};
		}

		public static M3 operator *(M3 a, double s)
		{
			return new M3
			{
				m = a.m.Select((double x) => x * s).ToArray()
			};
		}

		public static M3 FromTo(V3 a, V3 b)
		{
			a = a.Normalized;
			b = b.Normalized;
			V3 v = V3.Cross(a, b);
			double num = V3.Dot(a, b);
			if (num < -0.999999)
			{
				return AxisAngle(V3.Cross(a, (Math.Abs(a.x) < 0.9) ? new V3(1.0, 0.0, 0.0) : new V3(0.0, 1.0, 0.0)).Normalized, Math.PI);
			}
			double num2 = 1.0 / (1.0 + num);
			return new M3
			{
				m = new double[9]
				{
					v.x * v.x * num2 + num,
					v.x * v.y * num2 - v.z,
					v.x * v.z * num2 + v.y,
					v.y * v.x * num2 + v.z,
					v.y * v.y * num2 + num,
					v.y * v.z * num2 - v.x,
					v.z * v.x * num2 - v.y,
					v.z * v.y * num2 + v.x,
					v.z * v.z * num2 + num
				}
			};
		}

		public static M3 AxisAngle(V3 axis, double ang)
		{
			axis = axis.Normalized;
			double num = Math.Cos(ang);
			double num2 = Math.Sin(ang);
			double num3 = 1.0 - num;
			double x = axis.x;
			double y = axis.y;
			double z = axis.z;
			return new M3
			{
				m = new double[9]
				{
					num3 * x * x + num,
					num3 * x * y - num2 * z,
					num3 * x * z + num2 * y,
					num3 * x * y + num2 * z,
					num3 * y * y + num,
					num3 * y * z - num2 * x,
					num3 * x * z - num2 * y,
					num3 * y * z + num2 * x,
					num3 * z * z + num
				}
			};
		}

		public static M3 AxisScale(V3 d, double s)
		{
			d = d.Normalized;
			double num = s - 1.0;
			return new M3
			{
				m = new double[9]
				{
					1.0 + num * d.x * d.x,
					num * d.x * d.y,
					num * d.x * d.z,
					num * d.y * d.x,
					1.0 + num * d.y * d.y,
					num * d.y * d.z,
					num * d.z * d.x,
					num * d.z * d.y,
					1.0 + num * d.z * d.z
				}
			};
		}
	}
	public struct Affine
	{
		public M3 L;

		public V3 t;

		public static Affine Identity => new Affine
		{
			L = M3.Identity,
			t = new V3(0.0, 0.0, 0.0)
		};

		public V3 Point(V3 p)
		{
			return L * p + t;
		}
	}
	public class SourceBone
	{
		public string Name;

		public int Parent;

		public V3 Head;

		public string Role;
	}
	public class SourceModel
	{
		public List<SourceBone> Bones = new List<SourceBone>();

		public V3[] Positions;

		public V3[] Normals;

		public float[] U;

		public float[] V;

		public int[][] WeightBones;

		public float[][] Weights;

		public int[] Indices;

		public static SourceModel Read(Stream s)
		{
			BinaryReader r = new BinaryReader(s, Encoding.UTF8);
			if (new string(r.ReadChars(4)) != "RSKN")
			{
				throw new InvalidDataException("not an RSKN file");
			}
			r.ReadInt32();
			SourceModel sourceModel = new SourceModel();
			int num = r.ReadInt32();
			for (int i = 0; i < num; i++)
			{
				sourceModel.Bones.Add(new SourceBone
				{
					Name = Str(),
					Parent = r.ReadInt32(),
					Head = Vec(),
					Role = Str()
				});
			}
			int num2 = r.ReadInt32();
			sourceModel.Positions = new V3[num2];
			sourceModel.Normals = new V3[num2];
			sourceModel.U = new float[num2];
			sourceModel.V = new float[num2];
			sourceModel.WeightBones = new int[num2][];
			sourceModel.Weights = new float[num2][];
			for (int j = 0; j < num2; j++)
			{
				sourceModel.Positions[j] = Vec();
				sourceModel.Normals[j] = Vec();
				sourceModel.U[j] = r.ReadSingle();
				sourceModel.V[j] = r.ReadSingle();
				sourceModel.WeightBones[j] = new int[4];
				sourceModel.Weights[j] = new float[4];
				for (int k = 0; k < 4; k++)
				{
					sourceModel.WeightBones[j][k] = r.ReadInt32();
					sourceModel.Weights[j][k] = r.ReadSingle();
				}
			}
			int num3 = r.ReadInt32();
			sourceModel.Indices = new int[num3];
			for (int l = 0; l < num3; l++)
			{
				sourceModel.Indices[l] = r.ReadInt32();
			}
			return sourceModel;
			string Str()
			{
				return Encoding.UTF8.GetString(r.ReadBytes(r.ReadInt32()));
			}
			V3 Vec()
			{
				return new V3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
			}
		}
	}
	public class TargetBone
	{
		public string Name;

		public int Parent;

		public V3 Pos;

		public bool Skinned;
	}
	public class RetargetResult
	{
		public V3[] Positions;

		public V3[] Normals;

		public int[][] BoneIndices;

		public float[][] Weights;

		public Dictionary<string, int> RoleToTarget;

		public List<string> Log = new List<string>();
	}
	public static class Retargeter
	{
		public class Options
		{
			public double ScaleMultiplier = 1.0;

			public double MinAxisScale = 0.5;

			public double MaxAxisScale = 2.0;

			public HashSet<string> Pinned = new HashSet<string>
			{
				"pelvis", "spine", "chest", "forearm.l", "hand.l", "forearm.r", "hand.r", "thigh.l", "calf.l", "foot.l",
				"toe.l", "thigh.r", "calf.r", "foot.r", "toe.r"
			};

			public double ShoulderLower;

			public double HeadRaise;
		}

		public static readonly string[] Roles = new string[21]
		{
			"pelvis", "spine", "chest", "neck", "head", "clavicle.l", "upperarm.l", "forearm.l", "hand.l", "clavicle.r",
			"upperarm.r", "forearm.r", "hand.r", "thigh.l", "calf.l", "foot.l", "toe.l", "thigh.r", "calf.r", "foot.r",
			"toe.r"
		};

		private static readonly Dictionary<string, string[]> Next = new Dictionary<string, string[]>
		{
			["pelvis"] = new string[2] { "spine", "chest" },
			["spine"] = new string[2] { "chest", "neck" },
			["chest"] = new string[2] { "neck", "head" },
			["neck"] = new string[1] { "head" },
			["clavicle.l"] = new string[1] { "upperarm.l" },
			["upperarm.l"] = new string[1] { "forearm.l" },
			["forearm.l"] = new string[1] { "hand.l" },
			["clavicle.r"] = new string[1] { "upperarm.r" },
			["upperarm.r"] = new string[1] { "forearm.r" },
			["forearm.r"] = new string[1] { "hand.r" },
			["thigh.l"] = new string[1] { "calf.l" },
			["calf.l"] = new string[1] { "foot.l" },
			["foot.l"] = new string[1] { "toe.l" },
			["thigh.r"] = new string[1] { "calf.r" },
			["calf.r"] = new string[1] { "foot.r" },
			["foot.r"] = new string[1] { "toe.r" }
		};

		private static readonly string[] Excluded = new string[12]
		{
			"ik", "target", "pole", "twist", "end", "nub", "muzzle", "gun", "item", "jiggle",
			"socket", "attach"
		};

		private static string Norm(string n)
		{
			return new string(n.ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray());
		}

		public static char? Side(string name)
		{
			string text = name.ToLowerInvariant();
			if (text.Contains("left"))
			{
				return 'l';
			}
			if (text.Contains("right"))
			{
				return 'r';
			}
			string[] source = text.Split(new char[6] { '.', '_', ' ', '-', '|', ':' }, StringSplitOptions.RemoveEmptyEntries);
			if (source.Contains("l"))
			{
				return 'l';
			}
			if (source.Contains("r"))
			{
				return 'r';
			}
			if ((name.Length > 1 && char.IsLower(name[name.Length - 2])) || (name.Length > 1 && char.IsDigit(name[name.Length - 2])))
			{
				if (name.EndsWith("L"))
				{
					return 'l';
				}
				if (name.EndsWith("R"))
				{
					return 'r';
				}
			}
			return null;
		}

		private static bool NameIs(string name, params string[] keys)
		{
			string n = Norm(name);
			if (name.ToLowerInvariant().Split(new char[6] { '.', '_', ' ', '-', '|', ':' }, StringSplitOptions.RemoveEmptyEntries).Any((string t) => Excluded.Contains(t)) || Excluded.Any((string e) => e.Length > 2 && n.Contains(e)))
			{
				return false;
			}
			return keys.Any((string k) => n.Contains(k));
		}

		private static bool IsAncestor(List<TargetBone> b, int anc, int i)
		{
			for (int parent = b[i].Parent; parent >= 0; parent = b[parent].Parent)
			{
				if (parent == anc)
				{
					return true;
				}
			}
			return false;
		}

		private static int SkinnedSelfOrAncestor(List<TargetBone> b, int i)
		{
			while (i >= 0 && !b[i].Skinned)
			{
				i = b[i].Parent;
			}
			return i;
		}

		private static int SkinnedParent(List<TargetBone> b, int i)
		{
			if (i >= 0)
			{
				return SkinnedSelfOrAncestor(b, b[i].Parent);
			}
			return -1;
		}

		private static int Lca(List<TargetBone> b, int i, int j)
		{
			HashSet<int> hashSet = new HashSet<int>();
			for (int num = i; num >= 0; num = b[num].Parent)
			{
				hashSet.Add(num);
			}
			for (int num2 = j; num2 >= 0; num2 = b[num2].Parent)
			{
				if (hashSet.Contains(num2))
				{
					return num2;
				}
			}
			return -1;
		}

		private static int FindByName(List<TargetBone> b, char? side, params string[] keys)
		{
			List<int> list = (from i in Enumerable.Range(0, b.Count)
				where b[i].Skinned && NameIs(b[i].Name, keys) && (!side.HasValue || Side(b[i].Name) == side)
				select i).ToList();
			if (list.Count == 0)
			{
				return -1;
			}
			return (from i in list
				orderby Depth(b, i), b[i].Name.Length
				select i).First();
		}

		private static int Depth(List<TargetBone> b, int i)
		{
			int num = 0;
			for (int parent = b[i].Parent; parent >= 0; parent = b[parent].Parent)
			{
				num++;
			}
			return num;
		}

		public static Dictionary<string, int> FindRoles(List<TargetBone> b, Dictionary<string, int> hints, Dictionary<string, string> overrides, List<string> log)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			char[] array = new char[2] { 'l', 'r' };
			for (int i = 0; i < array.Length; i++)
			{
				char value = array[i];
				int num = Pick("hand." + value, value, new string[2] { "hand", "wrist" });
				int foot = Pick("foot." + value, value, new string[2] { "foot", "ankle" });
				if (num >= 0)
				{
					dictionary["hand." + value] = num;
					int num2 = Pick("forearm." + value, value, new string[3] { "forearm", "lowerarm", "elbow" });
					if (num2 < 0 || !IsAncestor(b, num2, num))
					{
						num2 = SkinnedParent(b, num);
					}
					int num3 = Pick("upperarm." + value, value, new string[3] { "upperarm", "uparm", "humerus" });
					if (num3 < 0 || !IsAncestor(b, num3, num2))
					{
						num3 = SkinnedParent(b, num2);
					}
					if (num2 >= 0)
					{
						dictionary["forearm." + value] = num2;
					}
					if (num3 >= 0)
					{
						dictionary["upperarm." + value] = num3;
					}
				}
				if (foot >= 0)
				{
					dictionary["foot." + value] = foot;
					int num4 = Pick("calf." + value, value, new string[4] { "calf", "shin", "lowerleg", "knee" });
					if (num4 < 0 || !IsAncestor(b, num4, foot))
					{
						num4 = SkinnedParent(b, foot);
					}
					int num5 = Pick("thigh." + value, value, new string[4] { "thigh", "upperleg", "upleg", "femur" });
					if (num5 < 0 || !IsAncestor(b, num5, num4))
					{
						num5 = SkinnedParent(b, num4);
					}
					if (num4 >= 0)
					{
						dictionary["calf." + value] = num4;
					}
					if (num5 >= 0)
					{
						dictionary["thigh." + value] = num5;
					}
					int num6 = (from index in Enumerable.Range(0, b.Count)
						where b[index].Skinned && SkinnedParent(b, index) == foot
						orderby NameIs(b[index].Name, "toe", "ball") ? 1 : 0 descending
						select index).DefaultIfEmpty(-1).First();
					if (num6 >= 0)
					{
						dictionary["toe." + value] = num6;
					}
				}
			}
			if (dictionary.TryGetValue("upperarm.l", out var value2) && dictionary.TryGetValue("upperarm.r", out var value3))
			{
				int num7 = SkinnedParent(b, value2);
				int num8 = SkinnedParent(b, value3);
				int num9;
				if (num7 >= 0 && num7 == num8)
				{
					num9 = num7;
				}
				else
				{
					num9 = SkinnedSelfOrAncestor(b, Lca(b, value2, value3));
					if (num7 >= 0 && num7 != num9)
					{
						dictionary["clavicle.l"] = num7;
					}
					if (num8 >= 0 && num8 != num9)
					{
						dictionary["clavicle.r"] = num8;
					}
				}
				if (num9 >= 0)
				{
					dictionary["chest"] = num9;
				}
			}
			else
			{
				int num10 = Pick("chest", null, new string[5] { "chest", "spine2", "spine3", "upperchest", "thorax" });
				if (num10 >= 0)
				{
					dictionary["chest"] = num10;
				}
			}
			if (dictionary.TryGetValue("thigh.l", out var value4) && dictionary.TryGetValue("thigh.r", out var value5))
			{
				int num11 = SkinnedParent(b, value4);
				int num12 = SkinnedParent(b, value5);
				dictionary["pelvis"] = ((num11 >= 0 && num11 == num12) ? num11 : SkinnedSelfOrAncestor(b, Lca(b, value4, value5)));
			}
			else
			{
				int num13 = Pick("pelvis", null, new string[3] { "pelvis", "hip", "hips" });
				if (num13 >= 0)
				{
					dictionary["pelvis"] = num13;
				}
			}
			int num14 = Pick("head", null, new string[1] { "head" });
			if (num14 >= 0)
			{
				dictionary["head"] = num14;
				int num15 = SkinnedParent(b, num14);
				if (num15 >= 0 && (!dictionary.TryGetValue("chest", out var value6) || num15 != value6))
				{
					dictionary["neck"] = num15;
				}
			}
			if (dictionary.TryGetValue("chest", out var value7) && dictionary.TryGetValue("pelvis", out var value8) && IsAncestor(b, value8, value7))
			{
				List<int> list = new List<int>();
				int num16 = SkinnedParent(b, value7);
				while (num16 >= 0 && num16 != value8)
				{
					list.Add(num16);
					num16 = SkinnedParent(b, num16);
				}
				if (list.Count > 0)
				{
					dictionary["spine"] = list[list.Count - 1];
				}
			}
			if (overrides != null)
			{
				foreach (KeyValuePair<string, string> kv in overrides)
				{
					int num17 = b.FindIndex((TargetBone x) => x.Name == kv.Value);
					if (num17 >= 0)
					{
						dictionary[kv.Key] = SkinnedSelfOrAncestor(b, num17);
						log.Add("override " + kv.Key + " -> " + kv.Value);
						continue;
					}
					log.Add("override " + kv.Key + " -> " + kv.Value + ": no bone with that name");
				}
			}
			string[] roles = Roles;
			foreach (string text in roles)
			{
				log.Add(dictionary.TryGetValue(text, out var value9) ? $"role {text,-12} = {b[value9].Name} {b[value9].Pos}" : $"role {text,-12} = (not found)");
			}
			return dictionary;
			int Hint(string role)
			{
				if (hints != null && hints.TryGetValue(role, out var value10) && value10 >= 0)
				{
					return SkinnedSelfOrAncestor(b, value10);
				}
				return -1;
			}
			int Pick(string role, char? side, string[] keys)
			{
				int num18 = FindByName(b, side, keys);
				if (num18 < 0)
				{
					return Hint(role);
				}
				return num18;
			}
		}

		private static string Group(string role)
		{
			if (role != null)
			{
				if (!role.StartsWith("clavicle") && !role.StartsWith("upperarm"))
				{
					if (!(role == "neck") && !(role == "head"))
					{
						return null;
					}
					return "head";
				}
				return "shoulder" + role.Substring(role.Length - 1);
			}
			return null;
		}

		public static RetargetResult Fit(SourceModel src, List<TargetBone> tgt, Dictionary<string, int> roles, Options opt)
		{
			RetargetResult retargetResult = new RetargetResult
			{
				RoleToTarget = roles
			};
			List<string> log = retargetResult.Log;
			List<SourceBone> sb = src.Bones;
			int count = sb.Count;
			Dictionary<string, int> srcRole = new Dictionary<string, int>();
			for (int i = 0; i < count; i++)
			{
				if (!string.IsNullOrEmpty(sb[i].Role) && !srcRole.ContainsKey(sb[i].Role))
				{
					srcRole[sb[i].Role] = i;
				}
			}
			string[] array = new string[4] { "pelvis", "head", "upperarm.l", "upperarm.r" };
			foreach (string text in array)
			{
				if (!Has(text))
				{
					throw new Exception("Could not identify the '" + text + "' bone on the target skeleton. See the log for the bone list and set BoneOverrides in the config.");
				}
			}
			bool flag = Has("foot.l") && Has("foot.r");
			V3 normalized = (S("head") - S("pelvis")).Normalized;
			V3 normalized2 = (T("head") - T("pelvis")).Normalized;
			V3 v = S("upperarm.l") - S("upperarm.r");
			V3 v2 = T("upperarm.l") - T("upperarm.r");
			v = (v - normalized * V3.Dot(v, normalized)).Normalized;
			v2 = (v2 - normalized2 * V3.Dot(v2, normalized2)).Normalized;
			V3 v3 = ((!Has("toe.l") || !Has("foot.l")) ? V3.Cross(v, normalized) : (S("toe.l") - S("foot.l")));
			v3 = (v3 - normalized * V3.Dot(v3, normalized) - v * V3.Dot(v3, v)).Normalized;
			V3 v4 = V3.Cross(normalized2, v2).Normalized;
			if (Has("toe.l") && Has("foot.l"))
			{
				V3 a = T("toe.l") - T("foot.l");
				a -= normalized2 * V3.Dot(a, normalized2);
				if (a.Length > 1E-06 && V3.Dot(a, v4) < 0.0)
				{
					log.Add("toe direction disagrees with Cross(up,left); trusting toes");
					v4 = -v4;
				}
			}
			double num = V3.Dot(S("head") - FootMid(S, flag), normalized);
			double num2 = V3.Dot(T("head") - FootMid(T, flag && roles.ContainsKey("foot.l") && roles.ContainsKey("foot.r")), normalized2);
			double num3 = num2 / num * opt.ScaleMultiplier;
			log.Add($"source height {num:0.###}, target height {num2:0.###}, scale {num3:0.#####}");
			M3 m = M3.FromRows(v, normalized, v3);
			M3 m2 = M3.FromCols(v2, normalized2, v4);
			Affine G = new Affine
			{
				L = m2 * m * num3
			};
			G.t = T("pelvis") - G.L * S("pelvis");
			V3[] array2 = sb.Select((SourceBone sourceBone) => G.Point(sourceBone.Head)).ToArray();
			M3[] array3 = new M3[count];
			Affine[] array4 = new Affine[count];
			foreach (int item in Enumerable.Range(0, count).OrderBy(delegate(int index)
			{
				int num14 = 0;
				for (int parent2 = sb[index].Parent; parent2 >= 0; parent2 = sb[parent2].Parent)
				{
					num14++;
				}
				return num14;
			}))
			{
				int parent = sb[item].Parent;
				M3 m3 = ((parent >= 0) ? array3[parent] : M3.Identity);
				Affine affine = ((parent >= 0) ? array4[parent] : Affine.Identity);
				string role = sb[item].Role;
				if (string.IsNullOrEmpty(role) || !Has(role))
				{
					array3[item] = m3;
					array4[item] = affine;
					continue;
				}
				bool flag2 = opt.Pinned.Contains(role);
				V3 v5 = (flag2 ? T(role) : affine.Point(array2[item]));
				string text2 = Group(role);
				string role2 = ((parent >= 0 && Has(sb[parent].Role ?? "")) ? sb[parent].Role : null);
				if (text2 != null && Group(role2) != text2)
				{
					if (text2.StartsWith("shoulder"))
					{
						v5 -= normalized2 * (opt.ShoulderLower * num2);
					}
					else
					{
						v5 += normalized2 * (opt.HeadRaise * num2);
					}
				}
				M3 m4 = m3;
				M3 m5 = M3.Identity;
				string[] value;
				string text3 = (Next.TryGetValue(role, out value) ? value.FirstOrDefault(Has) : null);
				if (text3 != null && opt.Pinned.Contains(text3))
				{
					V3 v6 = G.L * (S(text3) - S(role)) / num3;
					V3 b = T(text3) - v5;
					if (v6.Length > 1E-09 && b.Length > 1E-09)
					{
						m4 = M3.FromTo(m3 * v6, b) * m3;
						double val = b.Length / (v6.Length * num3);
						val = Math.Max(opt.MinAxisScale, Math.Min(opt.MaxAxisScale, val));
						m5 = M3.AxisScale(v6, val);
					}
				}
				else if (text3 != null && flag2 && role != "chest")
				{
					V3 v7 = G.L * (S(text3) - S(role)) / num3;
					V3 b2 = T(text3) - v5;
					if (v7.Length > 1E-09 && b2.Length > 1E-09)
					{
						m4 = M3.FromTo(m3 * v7, b2) * m3;
					}
				}
				array3[item] = m4;
				M3 m6 = m4 * m5;
				array4[item] = new Affine
				{
					L = m6,
					t = v5 - m6 * array2[item]
				};
			}
			int[] array5 = new int[count];
			for (int num4 = 0; num4 < count; num4++)
			{
				int num5 = num4;
				array5[num4] = -1;
				while (num5 >= 0)
				{
					if (Has(sb[num5].Role ?? ""))
					{
						array5[num4] = roles[sb[num5].Role];
						break;
					}
					num5 = sb[num5].Parent;
				}
				if (array5[num4] < 0)
				{
					array5[num4] = roles["pelvis"];
				}
			}
			int num6 = src.Positions.Length;
			retargetResult.Positions = new V3[num6];
			retargetResult.Normals = new V3[num6];
			retargetResult.BoneIndices = new int[num6][];
			retargetResult.Weights = new float[num6][];
			for (int num7 = 0; num7 < num6; num7++)
			{
				V3 p = G.Point(src.Positions[num7]);
				V3 normalized3 = (G.L * src.Normals[num7]).Normalized;
				V3 v8 = default(V3);
				V3 v9 = default(V3);
				Dictionary<int, double> dictionary = new Dictionary<int, double>();
				double num8 = 0.0;
				for (int num9 = 0; num9 < 4; num9++)
				{
					double num10 = src.Weights[num7][num9];
					if (!(num10 <= 0.0))
					{
						int num11 = src.WeightBones[num7][num9];
						v8 += array4[num11].Point(p) * num10;
						v9 += array3[num11] * normalized3 * num10;
						num8 += num10;
						int key = array5[num11];
						dictionary[key] = (dictionary.TryGetValue(key, out var value2) ? value2 : 0.0) + num10;
					}
				}
				if (num8 <= 0.0)
				{
					v8 = array4[0].Point(p);
					v9 = array3[0] * normalized3;
					dictionary[roles["pelvis"]] = 1.0;
					num8 = 1.0;
				}
				else
				{
					v8 /= num8;
				}
				retargetResult.Positions[num7] = v8;
				retargetResult.Normals[num7] = v9.Normalized;
				List<KeyValuePair<int, double>> list = dictionary.OrderByDescending((KeyValuePair<int, double> kv) => kv.Value).Take(4).ToList();
				double num12 = list.Sum((KeyValuePair<int, double> kv) => kv.Value);
				retargetResult.BoneIndices[num7] = new int[4];
				retargetResult.Weights[num7] = new float[4];
				for (int num13 = 0; num13 < list.Count; num13++)
				{
					retargetResult.BoneIndices[num7][num13] = list[num13].Key;
					retargetResult.Weights[num7][num13] = (float)(list[num13].Value / num12);
				}
			}
			return retargetResult;
			static V3 FootMid(Func<string, V3> P, bool has)
			{
				if (!has)
				{
					return P("pelvis");
				}
				return (P("foot.l") + P("foot.r")) / 2.0;
			}
			bool Has(string key2)
			{
				if (roles.ContainsKey(key2))
				{
					return srcRole.ContainsKey(key2);
				}
				return false;
			}
			V3 S(string key2)
			{
				return sb[srcRole[key2]].Head;
			}
			V3 T(string key2)
			{
				return tgt[roles[key2]].Pos;
			}
		}

		public static bool NeedsWindingFlip(SourceModel src, List<TargetBone> tgt, Dictionary<string, int> roles, RetargetResult res)
		{
			int num = 0;
			int num2 = 0;
			for (int i = 0; i + 2 < src.Indices.Length; i += 3)
			{
				int num3 = src.Indices[i];
				int num4 = src.Indices[i + 1];
				int num5 = src.Indices[i + 2];
				V3 a = V3.Cross(res.Positions[num4] - res.Positions[num3], res.Positions[num5] - res.Positions[num3]);
				V3 b = res.Normals[num3] + res.Normals[num4] + res.Normals[num5];
				double num6 = V3.Dot(a, b);
				if (num6 > 0.0)
				{
					num++;
				}
				else if (num6 < 0.0)
				{
					num2++;
				}
			}
			return num2 > num;
		}
	}
}