Decompiled source of PrevampFeedbacker v1.0.0

PrevampFeedbacker.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PrevampFeedbacker")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PrevampFeedbacker")]
[assembly: AssemblyTitle("PrevampFeedbacker")]
[assembly: AssemblyVersion("1.0.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 PrevampFeedbacker
{
	internal class ArmScanner : MonoBehaviour
	{
		private const float ScanInterval = 0.25f;

		private static Transform playerRoot;

		private static float deadline;

		private static bool loggedThisWindow;

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

		private float nextScan;

		public static void NotePlayer(Transform anyPlayerPart)
		{
			if (!((Object)(object)anyPlayerPart == (Object)null))
			{
				playerRoot = anyPlayerPart.root;
				deadline = Time.unscaledTime + Mathf.Max(1f, Plugin.CfgScanSeconds.Value);
				loggedThisWindow = false;
				handled.Clear();
			}
		}

		private void Update()
		{
			if (!Plugin.AssetsReady || !Plugin.CfgEnabled.Value || (!Plugin.CfgWeaponArms.Value && !Plugin.CfgLogArms.Value) || Time.unscaledTime > deadline || Time.unscaledTime < nextScan)
			{
				return;
			}
			nextScan = Time.unscaledTime + 0.25f;
			Renderer[] array = Collect();
			if (array == null || array.Length == 0)
			{
				return;
			}
			if (Plugin.CfgLogArms.Value && !loggedThisWindow)
			{
				loggedThisWindow = true;
				DumpHierarchy(array);
			}
			if (!Plugin.CfgWeaponArms.Value)
			{
				return;
			}
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)val).GetInstanceID();
				if (handled.Contains(instanceID))
				{
					continue;
				}
				ArmAsset armAsset = Plugin.FindArmFor(((Object)((Component)val).gameObject).name);
				if (armAsset == null)
				{
					continue;
				}
				string text = PathOf(((Component)val).transform);
				if (IsExcluded(text))
				{
					handled.Add(instanceID);
					Plugin.Detail("Skipping " + text + ", matched an ExcludePathContains entry");
					continue;
				}
				Plugin.Detail("Matched " + text + " to " + armAsset.PrefabName);
				if (ArmSwapper.Apply(val, armAsset))
				{
					handled.Add(instanceID);
				}
			}
		}

		private Renderer[] Collect()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CfgScanWholeScene.Value && (Object)(object)playerRoot != (Object)null)
			{
				return ((Component)playerRoot).GetComponentsInChildren<Renderer>(true);
			}
			Renderer[] array = Resources.FindObjectsOfTypeAll<Renderer>();
			List<Renderer> list = new List<Renderer>();
			for (int i = 0; i < array.Length; i++)
			{
				if (!((Object)(object)array[i] == (Object)null))
				{
					Scene scene = ((Component)array[i]).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						list.Add(array[i]);
					}
				}
			}
			return list.ToArray();
		}

		private void DumpHierarchy(Renderer[] renderers)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("ARM SCAN, root " + (((Object)(object)playerRoot != (Object)null) ? ((Object)playerRoot).name : "whole scene"));
			stringBuilder.AppendLine("renderers found " + renderers.Length);
			foreach (Renderer val in renderers)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
				Mesh val3 = (((Object)(object)val2 != (Object)null) ? val2.sharedMesh : MeshOf(val));
				stringBuilder.AppendLine(PathOf(((Component)val).transform));
				stringBuilder.AppendLine("    name " + ((Object)((Component)val).gameObject).name + "  type " + (((Object)(object)val2 != (Object)null) ? "skinned" : "static") + "  mesh " + (((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "none") + "  submeshes " + (((Object)(object)val3 != (Object)null) ? val3.subMeshCount : 0) + "  materials " + val.sharedMaterials.Length);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Transform[] bones = val2.bones;
				stringBuilder.Append("    bones " + bones.Length + "  ");
				for (int j = 0; j < bones.Length; j++)
				{
					stringBuilder.Append(((Object)(object)bones[j] != (Object)null) ? ((Object)bones[j]).name : "NULL");
					if (j < bones.Length - 1)
					{
						stringBuilder.Append(", ");
					}
				}
				stringBuilder.AppendLine();
			}
			Plugin.Dump(stringBuilder.ToString());
		}

		private static bool IsExcluded(string path)
		{
			string value = Plugin.CfgExcludePathContains.Value;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string[] array = value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && path.Contains(text))
				{
					return true;
				}
			}
			return false;
		}

		private static Mesh MeshOf(Renderer r)
		{
			MeshFilter component = ((Component)r).GetComponent<MeshFilter>();
			return ((Object)(object)component != (Object)null) ? component.sharedMesh : null;
		}

		private static string PathOf(Transform t)
		{
			StringBuilder stringBuilder = new StringBuilder(((Object)t).name);
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				stringBuilder.Insert(0, ((Object)parent).name + "/");
				parent = parent.parent;
			}
			return stringBuilder.ToString();
		}
	}
	internal static class ArmSwapper
	{
		private static readonly string[] MainTexNames = new string[8] { "_MainTex", "_BaseMap", "_BaseColorMap", "_Albedo", "_AlbedoMap", "_Diffuse", "_DiffuseMap", "_Texture" };

		private static readonly string[] MainColorNames = new string[6] { "_Color", "_BaseColor", "_MainColor", "_Tint", "_TintColor", "_AlbedoColor" };

		private static readonly string[] SecondaryHints = new string[16]
		{
			"bump", "normal", "emis", "metal", "occl", "spec", "gloss", "smooth", "mask", "detail",
			"height", "parallax", "cube", "lightmap", "ramp", "noise"
		};

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

		public static bool Apply(Renderer target, ArmAsset asset)
		{
			if ((Object)(object)target == (Object)null || asset == null)
			{
				return false;
			}
			try
			{
				SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((target is SkinnedMeshRenderer) ? target : null);
				if ((Object)(object)val != (Object)null)
				{
					if (!asset.Skinned)
					{
						Plugin.Fail(asset.PrefabName + " is a plain mesh but " + ((Object)target).name + " is skinned, rig to that arm's skeleton");
						return false;
					}
					return ApplySkinned(val, asset);
				}
				if (asset.Skinned)
				{
					Plugin.Fail(asset.PrefabName + " is skinned but " + ((Object)target).name + " is a plain mesh renderer, export without an armature");
					return false;
				}
				return ApplyStatic(target, asset);
			}
			catch (Exception ex)
			{
				Plugin.Fail("Swap failed on " + ((Object)target).name + ", leaving it alone: " + ex);
				return false;
			}
		}

		private static bool ApplySkinned(SkinnedMeshRenderer smr, ArmAsset asset)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)smr.sharedMesh == (Object)null)
			{
				return false;
			}
			string text = SkinnedSignature(smr);
			if (!asset.Baked.TryGetValue(text, out var value))
			{
				value = new BakedArm();
				value.Mesh = BuildRemappedMesh(smr, asset, value);
				value.Materials = BuildMaterials((Renderer)(object)smr, asset, value.Mesh.subMeshCount);
				asset.Baked[text] = value;
				MaybeDump(smr, asset, text);
			}
			Bounds localBounds = ((Renderer)smr).localBounds;
			smr.sharedMesh = value.Mesh;
			((Renderer)smr).sharedMaterials = value.Materials;
			AttachApplier(smr, asset);
			if (Plugin.CfgClearPropertyBlock.Value && ((Renderer)smr).HasPropertyBlock())
			{
				((Renderer)smr).SetPropertyBlock((MaterialPropertyBlock)null);
			}
			smr.updateWhenOffscreen = Plugin.CfgUpdateWhenOffscreen.Value;
			if (Plugin.CfgPreserveVanillaBounds.Value && !smr.updateWhenOffscreen)
			{
				((Renderer)smr).localBounds = localBounds;
			}
			return true;
		}

		private static bool ApplyStatic(Renderer target, ArmAsset asset)
		{
			MeshFilter component = ((Component)target).GetComponent<MeshFilter>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Fail(((Object)target).name + " has a renderer but no MeshFilter");
				return false;
			}
			string text = StaticSignature(target);
			if (!asset.Baked.TryGetValue(text, out var value))
			{
				value = new BakedArm();
				value.Mesh = asset.SourceMesh;
				value.Materials = BuildMaterials(target, asset, value.Mesh.subMeshCount);
				asset.Baked[text] = value;
				MaybeDump(null, asset, text);
			}
			component.sharedMesh = value.Mesh;
			target.sharedMaterials = value.Materials;
			if (Plugin.CfgClearPropertyBlock.Value && target.HasPropertyBlock())
			{
				target.SetPropertyBlock((MaterialPropertyBlock)null);
			}
			return true;
		}

		private static string SkinnedSignature(SkinnedMeshRenderer smr)
		{
			StringBuilder stringBuilder = new StringBuilder();
			Transform[] bones = smr.bones;
			for (int i = 0; i < bones.Length; i++)
			{
				stringBuilder.Append(((Object)(object)bones[i] != (Object)null) ? ((Object)bones[i]).name : "NULL");
				stringBuilder.Append('|');
			}
			stringBuilder.Append('#');
			stringBuilder.Append(ShaderName(((Renderer)smr).sharedMaterial));
			return stringBuilder.ToString();
		}

		private static string StaticSignature(Renderer r)
		{
			return "static#" + ShaderName(r.sharedMaterial);
		}

		private static Mesh BuildRemappedMesh(SkinnedMeshRenderer target, ArmAsset asset, BakedArm baked)
		{
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			Transform[] bones = target.bones;
			Dictionary<string, int> dictionary = new Dictionary<string, int>(bones.Length);
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>(bones.Length);
			for (int i = 0; i < bones.Length; i++)
			{
				if (!((Object)(object)bones[i] == (Object)null))
				{
					dictionary[((Object)bones[i]).name] = i;
					string key = Loosen(((Object)bones[i]).name);
					if (!dictionary2.ContainsKey(key))
					{
						dictionary2[key] = i;
					}
				}
			}
			Mesh val = Object.Instantiate<Mesh>(asset.SourceMesh);
			((Object)val).name = ((Object)asset.SourceMesh).name + "_Remapped";
			((Object)val).hideFlags = (HideFlags)32;
			FixMissingVertexData(val, asset);
			string[] sourceBoneNames = asset.SourceBoneNames;
			Matrix4x4[] bindposes = val.bindposes;
			BoneWeight[] boneWeights = val.boneWeights;
			if (boneWeights.Length == 0)
			{
				Plugin.Fail(asset.PrefabName + " gave back zero bone weights");
				Plugin.Fail("Read/Write was not enabled in the model");
			}
			int[] array = new int[sourceBoneNames.Length];
			for (int j = 0; j < boneWeights.Length; j++)
			{
				Tally(array, ((BoneWeight)(ref boneWeights[j])).boneIndex0, ((BoneWeight)(ref boneWeights[j])).weight0);
				Tally(array, ((BoneWeight)(ref boneWeights[j])).boneIndex1, ((BoneWeight)(ref boneWeights[j])).weight1);
				Tally(array, ((BoneWeight)(ref boneWeights[j])).boneIndex2, ((BoneWeight)(ref boneWeights[j])).weight2);
				Tally(array, ((BoneWeight)(ref boneWeights[j])).boneIndex3, ((BoneWeight)(ref boneWeights[j])).weight3);
			}
			int[] array2 = new int[sourceBoneNames.Length];
			Matrix4x4[] array3 = (Matrix4x4[])(object)new Matrix4x4[bones.Length];
			for (int k = 0; k < array3.Length; k++)
			{
				array3[k] = Matrix4x4.identity;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			for (int l = 0; l < sourceBoneNames.Length; l++)
			{
				if (dictionary.TryGetValue(sourceBoneNames[l], out var value))
				{
					array2[l] = value;
					if (l < bindposes.Length)
					{
						array3[value] = ((Plugin.CfgTuningMode.Value == "Geometry") ? (BoneOffsets.LocalMatrixFor(asset, l) * bindposes[l]) : bindposes[l]);
					}
					num++;
					continue;
				}
				if (Plugin.CfgFuzzyBoneNames.Value && dictionary2.TryGetValue(Loosen(sourceBoneNames[l]), out value))
				{
					array2[l] = value;
					if (l < bindposes.Length)
					{
						array3[value] = ((Plugin.CfgTuningMode.Value == "Geometry") ? (BoneOffsets.LocalMatrixFor(asset, l) * bindposes[l]) : bindposes[l]);
					}
					num++;
					Plugin.Warn(asset.PrefabName + " bone \"" + sourceBoneNames[l] + "\" matched \"" + ((Object)bones[value]).name + "\" only after ignoring spacing and case");
					continue;
				}
				int num4 = FindAncestorIndex(asset, l, dictionary);
				if (num4 >= 0)
				{
					array2[l] = num4;
					num2++;
					Plugin.Warn(asset.PrefabName + " has no target bone \"" + sourceBoneNames[l] + "\" on " + ((Object)target).name + ", " + array[l] + " verts fell back to \"" + ((Object)bones[num4]).name + "\"");
				}
				else
				{
					array2[l] = 0;
					num3++;
					Plugin.Fail(asset.PrefabName + " has no target bone \"" + sourceBoneNames[l] + "\" on " + ((Object)target).name + " and no usable ancestor either, " + array[l] + " verts went to the root and will look wrong");
				}
			}
			Plugin.Say(asset.PrefabName + " on " + ((Object)target).name + " bone match " + num + " of " + sourceBoneNames.Length + " against " + bones.Length + " target bones, rescued " + num2 + ", orphaned " + num3);
			for (int m = 0; m < boneWeights.Length; m++)
			{
				((BoneWeight)(ref boneWeights[m])).boneIndex0 = Remap(((BoneWeight)(ref boneWeights[m])).boneIndex0, array2, bones.Length);
				((BoneWeight)(ref boneWeights[m])).boneIndex1 = Remap(((BoneWeight)(ref boneWeights[m])).boneIndex1, array2, bones.Length);
				((BoneWeight)(ref boneWeights[m])).boneIndex2 = Remap(((BoneWeight)(ref boneWeights[m])).boneIndex2, array2, bones.Length);
				((BoneWeight)(ref boneWeights[m])).boneIndex3 = Remap(((BoneWeight)(ref boneWeights[m])).boneIndex3, array2, bones.Length);
			}
			val.bindposes = array3;
			val.boneWeights = boneWeights;
			baked.SourceBindposes = bindposes;
			baked.ToGame = array2;
			baked.TargetBoneCount = bones.Length;
			return val;
		}

		private static void AttachApplier(SkinnedMeshRenderer smr, ArmAsset asset)
		{
			BoneOffsetApplier component = ((Component)smr).gameObject.GetComponent<BoneOffsetApplier>();
			if (!((Object)(object)component != (Object)null))
			{
				component = ((Component)smr).gameObject.AddComponent<BoneOffsetApplier>();
				component.Bind(asset.PrefabName, smr.bones);
			}
		}

		public static void RefreshOffsets()
		{
			int num = 0;
			for (int i = 0; i < Plugin.AllArms.Count; i++)
			{
				ArmAsset armAsset = Plugin.AllArms[i];
				foreach (KeyValuePair<string, BakedArm> item in armAsset.Baked)
				{
					BakedArm value = item.Value;
					if (!((Object)(object)value.Mesh == (Object)null) && value.SourceBindposes != null && value.ToGame != null)
					{
						value.Mesh.bindposes = BuildBindposes(armAsset, value, Plugin.CfgTuningMode.Value == "Geometry");
						num++;
					}
				}
			}
			BoneOffsetApplier[] array = Object.FindObjectsOfType<BoneOffsetApplier>();
			for (int j = 0; j < array.Length; j++)
			{
				array[j].RefreshCache();
			}
			if (num > 0)
			{
				Plugin.Detail("Bone offsets refreshed across " + num + " baked arms");
			}
		}

		private static Matrix4x4[] BuildBindposes(ArmAsset asset, BakedArm baked, bool withOffsets)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[baked.TargetBoneCount];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Matrix4x4.identity;
			}
			Matrix4x4[] sourceBindposes = baked.SourceBindposes;
			int[] toGame = baked.ToGame;
			for (int j = 0; j < toGame.Length; j++)
			{
				int num = toGame[j];
				if (num >= 0 && num < array.Length && j < sourceBindposes.Length && !(array[num] != Matrix4x4.identity))
				{
					array[num] = (withOffsets ? (BoneOffsets.LocalMatrixFor(asset, j) * sourceBindposes[j]) : sourceBindposes[j]);
				}
			}
			return array;
		}

		private static void Tally(int[] counts, int index, float weight)
		{
			if (!(weight <= 0f) && index >= 0 && index < counts.Length)
			{
				counts[index]++;
			}
		}

		private static int FindAncestorIndex(ArmAsset asset, int boneIndex, Dictionary<string, int> gameIndexByName)
		{
			if (asset.SourceBoneAncestors == null)
			{
				return -1;
			}
			if (boneIndex < 0 || boneIndex >= asset.SourceBoneAncestors.Length)
			{
				return -1;
			}
			string[] array = asset.SourceBoneAncestors[boneIndex];
			if (array == null)
			{
				return -1;
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (gameIndexByName.TryGetValue(array[i], out var value))
				{
					return value;
				}
			}
			return -1;
		}

		private static string Loosen(string boneName)
		{
			return boneName.Replace(" ", "").Replace("_", "").Replace("-", "")
				.Replace(".", "")
				.ToLowerInvariant();
		}

		private static void FixMissingVertexData(Mesh mesh, ArmAsset asset)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CfgFixVertexData.Value)
			{
				return;
			}
			try
			{
				if (!mesh.HasVertexAttribute((VertexAttribute)1))
				{
					mesh.RecalculateNormals();
					Plugin.Warn(asset.PrefabName + " had no normals so they were recalculated");
					Plugin.Warn("Set Normals to Import on the model importer and rebuild the bundle for authored ones");
				}
				if (!mesh.HasVertexAttribute((VertexAttribute)2))
				{
					mesh.RecalculateTangents();
					Plugin.Detail(asset.PrefabName + " had no tangents so they were recalculated");
				}
				if (!mesh.HasVertexAttribute((VertexAttribute)3))
				{
					Color[] array = (Color[])(object)new Color[mesh.vertexCount];
					for (int i = 0; i < array.Length; i++)
					{
						array[i] = Color.white;
					}
					mesh.colors = array;
					Plugin.Detail(asset.PrefabName + " had no vertex colours so they were filled white");
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("Could not repair vertex data on " + asset.PrefabName + ": " + ex.Message);
			}
		}

		private static int Remap(int index, int[] map, int gameBoneCount)
		{
			if (index < 0 || index >= map.Length)
			{
				return 0;
			}
			int num = map[index];
			if (num < 0 || num >= gameBoneCount)
			{
				return 0;
			}
			return num;
		}

		private static Material[] BuildMaterials(Renderer target, ArmAsset asset, int subMeshCount)
		{
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if (subMeshCount < 1)
			{
				subMeshCount = 1;
			}
			Material[] array = (Material[])(object)new Material[subMeshCount];
			Material[] sharedMaterials = target.sharedMaterials;
			Material val = ((sharedMaterials != null && sharedMaterials.Length != 0) ? sharedMaterials[0] : target.sharedMaterial);
			Material[] sourceMats = asset.SourceMats;
			Plugin.Say(asset.PrefabName + " building " + subMeshCount + " material slots from " + sourceMats.Length + " bundle materials, vanilla had " + ((sharedMaterials != null) ? sharedMaterials.Length : 0));
			if (Plugin.CfgSwapMaterials.Value && sourceMats.Length < subMeshCount)
			{
				Plugin.Warn("Mesh has more submeshes than the bundle has materials, the last one repeats");
			}
			for (int i = 0; i < subMeshCount; i++)
			{
				if (!Plugin.CfgSwapMaterials.Value)
				{
					array[i] = ((sharedMaterials != null && sharedMaterials.Length != 0) ? sharedMaterials[Mathf.Min(i, sharedMaterials.Length - 1)] : val);
					continue;
				}
				Material val2 = ((sourceMats.Length != 0) ? sourceMats[Mathf.Min(i, sourceMats.Length - 1)] : null);
				if (!Plugin.CfgUseGameShader.Value)
				{
					array[i] = (((Object)(object)val2 != (Object)null) ? val2 : val);
					continue;
				}
				Material val3 = (Material)(((Object)(object)val != (Object)null) ? ((object)Object.Instantiate<Material>(val)) : ((object)new Material(Shader.Find("Standard"))));
				((Object)val3).name = asset.PrefabName + "_Mat_" + i;
				((Object)val3).hideFlags = (HideFlags)32;
				BridgeMaterial(val2, val3, asset.PrefabName + " slot " + i);
				array[i] = val3;
			}
			return array;
		}

		private unsafe static void BridgeMaterial(Material src, Material clone, string label)
		{
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: 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_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			Texture val = FindSourceTexture(src);
			string text = null;
			if ((Object)(object)val != (Object)null)
			{
				text = WriteTexture(clone, val);
				if (text == null)
				{
					Plugin.Fail(label + " found no texture property on vanilla shader " + ShaderName(clone));
					Plugin.Fail("Turn on LogMaterials, read the property list, set MainTextureProperty in the config");
				}
				else
				{
					Plugin.Say(label + " wrote texture " + ((Object)val).name + " into " + text);
					if ((Object)(object)src != (Object)null && src.HasProperty(text))
					{
						clone.SetTextureScale(text, src.GetTextureScale(text));
						clone.SetTextureOffset(text, src.GetTextureOffset(text));
					}
				}
			}
			else if (Plugin.CfgBlankTextureWhenUntextured.Value)
			{
				string text2 = WriteTexture(clone, (Texture)(object)Texture2D.whiteTexture);
				Plugin.Detail(label + " has no texture, blanked " + ((text2 != null) ? text2 : "nothing") + " to white for flat colour");
			}
			int num = CopyMatchingTextures(src, clone, text);
			if (num > 0)
			{
				Plugin.Detail(label + " also carried " + num + " secondary maps");
			}
			if (TryFindSourceColor(src, out var color))
			{
				string text3 = WriteColor(clone, color);
				if (text3 != null)
				{
					string[] obj = new string[5] { label, " wrote colour ", null, null, null };
					Color val2 = color;
					obj[2] = ((object)(*(Color*)(&val2))/*cast due to .constrained prefix*/).ToString();
					obj[3] = " into ";
					obj[4] = text3;
					Plugin.Detail(string.Concat(obj));
				}
				else
				{
					Plugin.Warn(label + " found no colour property on vanilla shader " + ShaderName(clone));
				}
			}
			else if (clone.HasProperty("_Color"))
			{
				clone.color = new Color(0.95f, 0.95f, 0.95f);
			}
		}

		private static Texture FindSourceTexture(Material m)
		{
			if ((Object)(object)m == (Object)null)
			{
				return null;
			}
			List<string> list = EnumerateTextureProperties(m);
			if (list.Count == 0)
			{
				return null;
			}
			if (m.HasProperty("_MainTex"))
			{
				Texture texture = m.GetTexture("_MainTex");
				if ((Object)(object)texture != (Object)null)
				{
					return texture;
				}
			}
			foreach (string item in list)
			{
				if (!LooksSecondary(item))
				{
					Texture texture2 = m.GetTexture(item);
					if ((Object)(object)texture2 != (Object)null)
					{
						return texture2;
					}
				}
			}
			foreach (string item2 in list)
			{
				Texture texture3 = m.GetTexture(item2);
				if ((Object)(object)texture3 != (Object)null)
				{
					return texture3;
				}
			}
			return null;
		}

		private static string WriteTexture(Material target, Texture tex)
		{
			string value = Plugin.CfgMainTexProperty.Value;
			if (!string.IsNullOrEmpty(value))
			{
				if (target.HasProperty(value))
				{
					target.SetTexture(value, tex);
					return value;
				}
				Plugin.Warn("MainTextureProperty is \"" + value + "\" but the vanilla shader has nothing by that name, falling back");
			}
			string[] mainTexNames = MainTexNames;
			foreach (string text in mainTexNames)
			{
				if (target.HasProperty(text))
				{
					target.SetTexture(text, tex);
					return text;
				}
			}
			foreach (string item in EnumerateTextureProperties(target))
			{
				if (LooksSecondary(item))
				{
					continue;
				}
				target.SetTexture(item, tex);
				return item;
			}
			return null;
		}

		private static bool TryFindSourceColor(Material m, out Color color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Invalid comparison between Unknown and I4
			//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)
			color = Color.white;
			if ((Object)(object)m == (Object)null || (Object)(object)m.shader == (Object)null)
			{
				return false;
			}
			string[] mainColorNames = MainColorNames;
			foreach (string text in mainColorNames)
			{
				if (m.HasProperty(text))
				{
					color = m.GetColor(text);
					return true;
				}
			}
			Shader shader = m.shader;
			int propertyCount = shader.GetPropertyCount();
			for (int j = 0; j < propertyCount; j++)
			{
				if ((int)shader.GetPropertyType(j) <= 0)
				{
					color = m.GetColor(shader.GetPropertyName(j));
					return true;
				}
			}
			return false;
		}

		private static string WriteColor(Material target, Color color)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			string value = Plugin.CfgMainColorProperty.Value;
			if (!string.IsNullOrEmpty(value))
			{
				if (target.HasProperty(value))
				{
					target.SetColor(value, color);
					return value;
				}
				Plugin.Warn("MainColorProperty is \"" + value + "\" but the vanilla shader has nothing by that name, falling back");
			}
			string[] mainColorNames = MainColorNames;
			foreach (string text in mainColorNames)
			{
				if (target.HasProperty(text))
				{
					target.SetColor(text, color);
					return text;
				}
			}
			return null;
		}

		private static int CopyMatchingTextures(Material src, Material dst, string skip)
		{
			if ((Object)(object)src == (Object)null || (Object)(object)dst == (Object)null)
			{
				return 0;
			}
			int num = 0;
			foreach (string item in EnumerateTextureProperties(src))
			{
				if ((skip == null || !(item == skip)) && dst.HasProperty(item))
				{
					Texture texture = src.GetTexture(item);
					if (!((Object)(object)texture == (Object)null))
					{
						dst.SetTexture(item, texture);
						num++;
					}
				}
			}
			return num;
		}

		private static List<string> EnumerateTextureProperties(Material m)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Invalid comparison between Unknown and I4
			List<string> list = new List<string>();
			if ((Object)(object)m == (Object)null || (Object)(object)m.shader == (Object)null)
			{
				return list;
			}
			Shader shader = m.shader;
			int propertyCount = shader.GetPropertyCount();
			for (int i = 0; i < propertyCount; i++)
			{
				if ((int)shader.GetPropertyType(i) == 4)
				{
					list.Add(shader.GetPropertyName(i));
				}
			}
			return list;
		}

		private static bool LooksSecondary(string propertyName)
		{
			string text = propertyName.ToLowerInvariant();
			for (int i = 0; i < SecondaryHints.Length; i++)
			{
				if (text.Contains(SecondaryHints[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static string ShaderName(Material m)
		{
			if ((Object)(object)m == (Object)null || (Object)(object)m.shader == (Object)null)
			{
				return "none";
			}
			return ((Object)m.shader).name;
		}

		private static void MaybeDump(SkinnedMeshRenderer smr, ArmAsset asset, string sig)
		{
			string item = asset.PrefabName + "#" + sig;
			if (!dumped.Contains(item) && (Plugin.CfgLogBones.Value || Plugin.CfgLogMaterials.Value))
			{
				dumped.Add(item);
				if (Plugin.CfgLogBones.Value && (Object)(object)smr != (Object)null)
				{
					DumpSkeletons(smr, asset);
				}
				if (Plugin.CfgLogMaterials.Value)
				{
					DumpMaterials((Renderer)(object)smr, asset);
				}
			}
		}

		private static void DumpMaterials(Renderer target, ArmAsset asset)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("MATERIALS FOR " + asset.PrefabName);
			if ((Object)(object)target != (Object)null)
			{
				Material[] sharedMaterials = target.sharedMaterials;
				stringBuilder.AppendLine("vanilla slots " + ((sharedMaterials != null) ? sharedMaterials.Length : 0));
				stringBuilder.AppendLine("property block present " + target.HasPropertyBlock());
				if (sharedMaterials != null)
				{
					for (int i = 0; i < sharedMaterials.Length; i++)
					{
						stringBuilder.AppendLine("vanilla " + i + "  " + Describe(sharedMaterials[i]));
					}
				}
			}
			if ((Object)(object)target != (Object)null)
			{
				DescribeRenderer(stringBuilder, target);
				DescribeMeshData(stringBuilder, "VANILLA MESH", VanillaMeshOf(target));
			}
			DescribeMeshData(stringBuilder, "CUSTOM MESH", asset.SourceMesh);
			stringBuilder.AppendLine("custom materials " + asset.SourceMats.Length);
			for (int j = 0; j < asset.SourceMats.Length; j++)
			{
				stringBuilder.AppendLine("custom " + j + "  " + Describe(asset.SourceMats[j]));
			}
			Plugin.Dump(stringBuilder.ToString());
		}

		private static void DescribeRenderer(StringBuilder sb, Renderer r)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			sb.AppendLine("RENDERER SETTINGS");
			sb.AppendLine("    receiveShadows " + r.receiveShadows);
			sb.AppendLine("    shadowCastingMode " + ((object)r.shadowCastingMode/*cast due to .constrained prefix*/).ToString());
			sb.AppendLine("    lightProbeUsage " + ((object)r.lightProbeUsage/*cast due to .constrained prefix*/).ToString());
			sb.AppendLine("    probeAnchor " + (((Object)(object)r.probeAnchor != (Object)null) ? ((Object)r.probeAnchor).name : "none"));
			sb.AppendLine("    reflectionProbeUsage " + ((object)r.reflectionProbeUsage/*cast due to .constrained prefix*/).ToString());
			sb.AppendLine("    lightmapIndex " + r.lightmapIndex);
			Bounds bounds = r.bounds;
			string? text = ((object)((Bounds)(ref bounds)).center/*cast due to .constrained prefix*/).ToString();
			bounds = r.bounds;
			sb.AppendLine("    bounds centre " + text + "  size " + ((object)((Bounds)(ref bounds)).size/*cast due to .constrained prefix*/).ToString());
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null);
			if ((Object)(object)val != (Object)null)
			{
				sb.AppendLine("    updateWhenOffscreen " + val.updateWhenOffscreen);
				sb.AppendLine("    quality " + ((object)val.quality/*cast due to .constrained prefix*/).ToString());
			}
		}

		private static void DescribeMeshData(StringBuilder sb, string label, Mesh mesh)
		{
			sb.AppendLine(label);
			if ((Object)(object)mesh == (Object)null)
			{
				sb.AppendLine("    none");
				return;
			}
			sb.AppendLine("    name " + ((Object)mesh).name + "  verts " + mesh.vertexCount + "  submeshes " + mesh.subMeshCount);
			sb.AppendLine("    readable " + mesh.isReadable);
			try
			{
				sb.AppendLine("    normal " + mesh.HasVertexAttribute((VertexAttribute)1) + "  tangent " + mesh.HasVertexAttribute((VertexAttribute)2) + "  colour " + mesh.HasVertexAttribute((VertexAttribute)3));
				sb.AppendLine("    uv0 " + mesh.HasVertexAttribute((VertexAttribute)4) + "  uv1 " + mesh.HasVertexAttribute((VertexAttribute)5) + "  uv2 " + mesh.HasVertexAttribute((VertexAttribute)6));
			}
			catch (Exception)
			{
				sb.AppendLine("    vertex attribute query not supported on this unity version");
			}
		}

		private static Mesh VanillaMeshOf(Renderer r)
		{
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.sharedMesh;
			}
			MeshFilter component = ((Component)r).GetComponent<MeshFilter>();
			return ((Object)(object)component != (Object)null) ? component.sharedMesh : null;
		}

		private static string Describe(Material m)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Invalid comparison between Unknown and I4
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Invalid comparison between Unknown and I4
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m == (Object)null)
			{
				return "NULL";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine(((Object)m).name + "  shader " + ShaderName(m));
			if ((Object)(object)m.shader == (Object)null)
			{
				return stringBuilder.ToString();
			}
			Shader shader = m.shader;
			int propertyCount = shader.GetPropertyCount();
			for (int i = 0; i < propertyCount; i++)
			{
				ShaderPropertyType propertyType = shader.GetPropertyType(i);
				string propertyName = shader.GetPropertyName(i);
				if ((int)propertyType == 4)
				{
					Texture texture = m.GetTexture(propertyName);
					stringBuilder.AppendLine("    tex  " + propertyName + " = " + (((Object)(object)texture != (Object)null) ? ((Object)texture).name : "none"));
				}
				else if ((int)propertyType == 0)
				{
					stringBuilder.AppendLine("    col  " + propertyName + " = " + ((object)m.GetColor(propertyName)/*cast due to .constrained prefix*/).ToString());
				}
			}
			return stringBuilder.ToString();
		}

		private static void DumpSkeletons(SkinnedMeshRenderer target, ArmAsset asset)
		{
			//IL_0144: 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_0174: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			Transform[] bones = target.bones;
			stringBuilder.AppendLine("SKELETON FOR " + asset.PrefabName + " targeting " + ((Object)target).name);
			stringBuilder.AppendLine("vanilla mesh " + (((Object)(object)target.sharedMesh != (Object)null) ? ((Object)target.sharedMesh).name : "none"));
			stringBuilder.AppendLine("root bone " + (((Object)(object)target.rootBone != (Object)null) ? ((Object)target.rootBone).name : "none"));
			stringBuilder.AppendLine("vanilla bones " + bones.Length);
			for (int i = 0; i < bones.Length; i++)
			{
				Transform val = bones[i];
				if ((Object)(object)val == (Object)null)
				{
					stringBuilder.AppendLine(i + "  NULL");
					continue;
				}
				stringBuilder.AppendLine(i + "  " + ((Object)val).name + "  parent " + (((Object)(object)val.parent != (Object)null) ? ((Object)val.parent).name : "none") + "  pos " + Fmt(val.localPosition) + "  rot " + Fmt(val.localEulerAngles) + "  scale " + Fmt(val.localScale));
			}
			stringBuilder.AppendLine("custom bones " + asset.SourceBoneNames.Length);
			for (int j = 0; j < asset.SourceBoneNames.Length; j++)
			{
				bool flag = false;
				for (int k = 0; k < bones.Length; k++)
				{
					if ((Object)(object)bones[k] != (Object)null && ((Object)bones[k]).name == asset.SourceBoneNames[j])
					{
						flag = true;
						break;
					}
				}
				stringBuilder.AppendLine(j + "  " + asset.SourceBoneNames[j] + (flag ? "  MATCH" : "  NO MATCH"));
			}
			Plugin.Dump(stringBuilder.ToString());
		}

		private static string Fmt(Vector3 v)
		{
			return "(" + v.x.ToString("F4") + ", " + v.y.ToString("F4") + ", " + v.z.ToString("F4") + ")";
		}
	}
	internal static class BakedOffsets
	{
		public static void Apply()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: 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_01da: 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_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			BoneOffsets.Set("Custom_RightArm", "Finger_Thumb_Base", new Vector3(0f, 0f, 0.001f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Index_01", new Vector3(0.001f, -0.004f, 0.0015f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Index_02", new Vector3(0f, -0.001f, 0f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Index_03", new Vector3(0f, -0.001f, 0f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Middle_01", new Vector3(0.0005f, -0.005f, 0.0015f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Middle_02", new Vector3(0f, -0.001f, 0f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Ring_01", new Vector3(-0.0015f, -0.005f, 0.001f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Ring_02", new Vector3(0f, -0.001f, 0f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Pinky_01", new Vector3(-0.0025f, -0.004f, -0.0005f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Pinky_02", new Vector3(0f, -0.005f, 0f), new Vector3(0f, 0f, 0f));
			BoneOffsets.Set("CustomFeedbacker", "Thumb_01", new Vector3(-0.0025f, 0f, 0f), new Vector3(0f, 0f, 0f));
		}
	}
	internal class BoneOffsetApplier : MonoBehaviour
	{
		private string assetName;

		private Transform[] bones;

		private BoneOffset[] offsets;

		private bool[] active;

		private Vector3[] cleanPos;

		private Quaternion[] cleanRot;

		private bool anyActive;

		public void Bind(string asset, Transform[] targetBones)
		{
			assetName = asset;
			bones = targetBones;
			int num = ((bones != null) ? bones.Length : 0);
			offsets = new BoneOffset[num];
			active = new bool[num];
			cleanPos = (Vector3[])(object)new Vector3[num];
			cleanRot = (Quaternion[])(object)new Quaternion[num];
			RefreshCache();
			WarnIfPhysicsAnimator();
		}

		public void RefreshCache()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			anyActive = false;
			if (bones == null)
			{
				return;
			}
			for (int i = 0; i < bones.Length; i++)
			{
				if ((Object)(object)bones[i] == (Object)null)
				{
					active[i] = false;
					continue;
				}
				offsets[i] = BoneOffsets.Get(assetName, ((Object)bones[i]).name);
				active[i] = !offsets[i].IsZero;
				if (active[i])
				{
					anyActive = true;
					cleanPos[i] = bones[i].localPosition;
					cleanRot[i] = bones[i].localRotation;
				}
			}
		}

		private void WarnIfPhysicsAnimator()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			Animator componentInParent = ((Component)this).GetComponentInParent<Animator>();
			if (!((Object)(object)componentInParent == (Object)null) && (int)componentInParent.updateMode == 1)
			{
				Plugin.Log.LogWarning((object)("Animator on " + ((Object)componentInParent).name + " uses AnimatePhysics, bone offsets might lag a frame behind the pose"));
			}
		}

		private void Update()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (!anyActive || !Plugin.CfgEnabled.Value || Plugin.CfgTuningMode.Value != "Skeleton")
			{
				return;
			}
			for (int i = 0; i < bones.Length; i++)
			{
				if (active[i])
				{
					Transform val = bones[i];
					if (!((Object)(object)val == (Object)null))
					{
						val.localPosition = cleanPos[i];
						val.localRotation = cleanRot[i];
					}
				}
			}
		}

		private void LateUpdate()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			if (!anyActive || !Plugin.CfgEnabled.Value || Plugin.CfgTuningMode.Value != "Skeleton")
			{
				return;
			}
			for (int i = 0; i < bones.Length; i++)
			{
				if (!active[i])
				{
					continue;
				}
				Transform val = bones[i];
				if (!((Object)(object)val == (Object)null))
				{
					cleanPos[i] = val.localPosition;
					cleanRot[i] = val.localRotation;
					if (offsets[i].Position != Vector3.zero)
					{
						val.localPosition = cleanPos[i] + offsets[i].Position;
					}
					if (offsets[i].Rotation != Vector3.zero)
					{
						val.localRotation = cleanRot[i] * Quaternion.Euler(offsets[i].Rotation);
					}
				}
			}
		}
	}
	internal struct BoneOffset
	{
		public Vector3 Position;

		public Vector3 Rotation;

		public bool IsZero => Position == Vector3.zero && Rotation == Vector3.zero;

		public Matrix4x4 ToMatrix()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			return Matrix4x4.TRS(Position, Quaternion.Euler(Rotation), Vector3.one);
		}
	}
	internal static class BoneOffsets
	{
		private static readonly Dictionary<string, Dictionary<string, BoneOffset>> store = new Dictionary<string, Dictionary<string, BoneOffset>>();

		public static BoneOffset Get(string assetName, string boneName)
		{
			if (!store.TryGetValue(assetName, out var value))
			{
				return default(BoneOffset);
			}
			if (!value.TryGetValue(boneName, out var value2))
			{
				return default(BoneOffset);
			}
			return value2;
		}

		public static void Set(string assetName, string boneName, Vector3 position, Vector3 rotation)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!store.TryGetValue(assetName, out var value))
			{
				value = new Dictionary<string, BoneOffset>();
				store[assetName] = value;
			}
			BoneOffset value2 = default(BoneOffset);
			value2.Position = position;
			value2.Rotation = rotation;
			value[boneName] = value2;
		}

		public static void SetPosition(string assetName, string boneName, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Set(assetName, boneName, position, Get(assetName, boneName).Rotation);
		}

		public static void SetRotation(string assetName, string boneName, Vector3 rotation)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Set(assetName, boneName, Get(assetName, boneName).Position, rotation);
		}

		public static void ClearAsset(string assetName)
		{
			store.Remove(assetName);
		}

		public static bool AnyFor(string assetName)
		{
			if (!store.TryGetValue(assetName, out var value))
			{
				return false;
			}
			foreach (KeyValuePair<string, BoneOffset> item in value)
			{
				if (!item.Value.IsZero)
				{
					return true;
				}
			}
			return false;
		}

		public static Matrix4x4 LocalMatrixFor(ArmAsset asset, int boneIndex)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (boneIndex < 0 || boneIndex >= asset.SourceBoneNames.Length)
			{
				return Matrix4x4.identity;
			}
			BoneOffset boneOffset = Get(asset.PrefabName, asset.SourceBoneNames[boneIndex]);
			if (boneOffset.IsZero)
			{
				return Matrix4x4.identity;
			}
			return boneOffset.ToMatrix();
		}
	}
	[HarmonyPatch(typeof(Punch), "Awake")]
	internal static class FeedbackerPatch
	{
		private static readonly FieldInfo SmrField = AccessTools.Field(typeof(Punch), "smr");

		private static void Postfix(Punch __instance)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			if (!Plugin.AssetsReady || !Plugin.CfgEnabled.Value)
			{
				return;
			}
			ArmScanner.NotePlayer(((Component)__instance).transform);
			if ((int)__instance.type <= 0 && Plugin.Feedbacker != null)
			{
				SkinnedMeshRenderer val = null;
				if (SmrField != null)
				{
					object? value = SmrField.GetValue(__instance);
					val = (SkinnedMeshRenderer)((value is SkinnedMeshRenderer) ? value : null);
				}
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)__instance).GetComponentInChildren<SkinnedMeshRenderer>(true);
				}
				if ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null)
				{
					Plugin.Warn("Found a Feedbacker but no skinned mesh on it");
				}
				else
				{
					ArmSwapper.Apply((Renderer)(object)val, Plugin.Feedbacker);
				}
			}
		}
	}
	internal class BakedArm
	{
		public Mesh Mesh;

		public Material[] Materials;

		public Matrix4x4[] SourceBindposes;

		public int[] ToGame;

		public int TargetBoneCount;
	}
	internal class ArmAsset
	{
		public string PrefabName;

		public string TargetName;

		public bool Skinned;

		public Mesh SourceMesh;

		public Material[] SourceMats = (Material[])(object)new Material[0];

		public string[] SourceBoneNames = new string[0];

		public string[][] SourceBoneAncestors = new string[0][];

		public readonly Dictionary<string, BakedArm> Baked = new Dictionary<string, BakedArm>();
	}
	[BepInPlugin("com.scoutisdelicious.prevampfeedbacker", "PrevampFeedbacker", "1.0.0")]
	[BepInProcess("ULTRAKILL.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.scoutisdelicious.prevampfeedbacker";

		public const string PluginName = "PrevampFeedbacker";

		public const string PluginVersion = "1.0.0";

		public const string BundleFileName = "prevampfeedbacker.bundle";

		public const string FeedbackerPrefabName = "CustomFeedbacker";

		public const string ArmPrefabPrefix = "Custom_";

		internal static ManualLogSource Log;

		internal const int LevelOff = 0;

		internal const int LevelErrors = 1;

		internal const int LevelNormal = 2;

		internal const int LevelVerbose = 3;

		internal static int Level = 1;

		internal static bool AssetsReady;

		internal static ArmAsset Feedbacker;

		internal static readonly List<ArmAsset> AllArms = new List<ArmAsset>();

		internal static readonly List<ArmAsset> WeaponArms = new List<ArmAsset>();

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<bool> CfgSwapMaterials;

		internal static ConfigEntry<bool> CfgUseGameShader;

		internal static ConfigEntry<string> CfgMainTexProperty;

		internal static ConfigEntry<string> CfgMainColorProperty;

		internal static ConfigEntry<bool> CfgBlankTextureWhenUntextured;

		internal static ConfigEntry<bool> CfgClearPropertyBlock;

		internal static ConfigEntry<bool> CfgUpdateWhenOffscreen;

		internal static ConfigEntry<bool> CfgPreserveVanillaBounds;

		internal static ConfigEntry<bool> CfgFixVertexData;

		internal static ConfigEntry<bool> CfgFuzzyBoneNames;

		internal static ConfigEntry<bool> CfgWeaponArms;

		internal static ConfigEntry<float> CfgScanSeconds;

		internal static ConfigEntry<bool> CfgScanWholeScene;

		internal static ConfigEntry<string> CfgTuningMode;

		internal static ConfigEntry<string> CfgExcludePathContains;

		internal static ConfigEntry<string> CfgLogging;

		internal static ConfigEntry<bool> CfgLogBones;

		internal static ConfigEntry<bool> CfgLogMaterials;

		internal static ConfigEntry<bool> CfgLogArms;

		private AssetBundle bundle;

		internal static void Say(string message)
		{
			if (Level >= 2 && Log != null)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void Detail(string message)
		{
			if (Level >= 3 && Log != null)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void Warn(string message)
		{
			if (Level >= 2 && Log != null)
			{
				Log.LogWarning((object)message);
			}
		}

		internal static void Fail(string message)
		{
			if (Level >= 1 && Log != null)
			{
				Log.LogError((object)message);
			}
		}

		internal static void Dump(string message)
		{
			if (Level > 0 && Log != null)
			{
				Log.LogInfo((object)message);
			}
		}

		private void Awake()
		{
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Replace arm models. Reload the level after changing this.");
			CfgSwapMaterials = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwapMaterials", true, "Also replace the materials. Off gives the custom mesh wearing the vanilla texture.");
			CfgUseGameShader = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseGameShader", true, "Clone the vanilla material and push the texture and colour into it. Off uses custom bundle materials as is.");
			CfgMainTexProperty = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MainTextureProperty", "", "Force which shader property texture gets written into. Leave empty to auto detect.");
			CfgMainColorProperty = ((BaseUnityPlugin)this).Config.Bind<string>("General", "MainColorProperty", "", "Force which shader property material colour gets written into. Leave empty to auto detect.");
			CfgBlankTextureWhenUntextured = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BlankTextureWhenUntextured", true, "When material has no texture, white out the vanilla texture so flat colour reads clean. Off keeps the vanilla panel detail showing through, tinted.");
			CfgClearPropertyBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearPropertyBlock", false, "Wipe any MaterialPropertyBlock on the renderer. Only turn on if LogMaterials says one is present and textures get overridden.");
			CfgUpdateWhenOffscreen = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UpdateWhenOffscreen", true, "Recalculate bounds from the live mesh every frame. Stops the arm getting culled, but the bounds centre is where light probes get sampled, so turning it off can make shading match vanilla more closely.");
			CfgPreserveVanillaBounds = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreserveVanillaBounds", true, "Keep the vanilla renderer bounds after swapping. Viewmodel bounds are deliberately huge so the arm never culls, and the bounds centre is where light probes get sampled.");
			CfgFixVertexData = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FixMissingVertexData", true, "Recalculate normals and tangents and fill white vertex colours when the bundle mesh has none. ULTRAKILL/Master is a lit shader so a mesh with no normals shades badly. Authored normals from Blender still look better, this is a safety net.");
			CfgFuzzyBoneNames = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FuzzyBoneNames", true, "When an exact bone name misses, retry ignoring spaces, underscores and case, so UpperArm still finds Upper Arm.");
			CfgWeaponArms = ((BaseUnityPlugin)this).Config.Bind<bool>("WeaponArms", "Enabled", true, "Hunt for weapon arm renderers by name and swap them. Needs Custom_<name> prefabs in the bundle.");
			CfgScanSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("WeaponArms", "ScanSeconds", 8f, "How long to keep looking for weapon arms after a level loads. Guns spawn a little after the player does.");
			CfgScanWholeScene = ((BaseUnityPlugin)this).Config.Bind<bool>("WeaponArms", "ScanWholeScene", false, "Search everything instead of just the player hierarchy. Slower, only needed if the targeted scan misses.");
			CfgTuningMode = ((BaseUnityPlugin)this).Config.Bind<string>("BoneTuning", "TuningMode", "Skeleton", "Skeleton moves the real bones after the animator so children follow and it holds in every animation. Geometry moves only my mesh relative to each bone and never touches the gun, but children do not follow. The shipped offsets were tuned in Skeleton mode, leave it alone unless you are retuning.");
			CfgExcludePathContains = ((BaseUnityPlugin)this).Config.Bind<string>("WeaponArms", "ExcludePathContains", "", "Comma separated. Any renderer whose full transform path contains one of these is left alone. Use it to skip a same named object you do not want swapped.");
			CfgLogging = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "Logging", "Errors", "How much goes to the console. Off is total silence, Errors only reports real failures, Normal adds warnings and a summary, Verbose adds per swap detail.");
			Level = ParseLevel(CfgLogging.Value);
			CfgLogBones = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogBones", false, "Dump the vanilla skeleton against the bundle skeleton for every arm that gets swapped.");
			CfgLogMaterials = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogMaterials", false, "Dump every shader property on the vanilla material and on the bundle materials.");
			CfgLogArms = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogArms", false, "Dump every renderer in the player hierarchy with its path, mesh, and bone names. Run this to find the real arm object names.");
			if (Level >= 3)
			{
				LogEffectiveConfig();
			}
			if (!LoadBundle())
			{
				Fail("No usable arm assets, mod is sitting this one out");
				return;
			}
			BakedOffsets.Apply();
			new Harmony("com.scoutisdelicious.prevampfeedbacker").PatchAll();
			GameObject val = new GameObject("PrevampFeedbackerScanner");
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			val.AddComponent<ArmScanner>();
			Say("PrevampFeedbacker 1.0.0 loaded with " + ((Feedbacker != null) ? "1" : "0") + " left arm and " + WeaponArms.Count + " weapon arms");
		}

		private static int ParseLevel(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return 1;
			}
			return raw.Trim().ToLowerInvariant() switch
			{
				"off" => 0, 
				"errors" => 1, 
				"normal" => 2, 
				"verbose" => 3, 
				_ => 1, 
			};
		}

		private void LogEffectiveConfig()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Effective config, edit the file not the code defaults");
			stringBuilder.AppendLine("  file " + ((BaseUnityPlugin)this).Config.ConfigFilePath);
			stringBuilder.AppendLine("  Enabled " + CfgEnabled.Value);
			stringBuilder.AppendLine("  SwapMaterials " + CfgSwapMaterials.Value);
			stringBuilder.AppendLine("  UseGameShader " + CfgUseGameShader.Value);
			stringBuilder.AppendLine("  MainTextureProperty \"" + CfgMainTexProperty.Value + "\"");
			stringBuilder.AppendLine("  MainColorProperty \"" + CfgMainColorProperty.Value + "\"");
			stringBuilder.AppendLine("  BlankTextureWhenUntextured " + CfgBlankTextureWhenUntextured.Value);
			stringBuilder.AppendLine("  ClearPropertyBlock " + CfgClearPropertyBlock.Value);
			stringBuilder.AppendLine("  UpdateWhenOffscreen " + CfgUpdateWhenOffscreen.Value);
			stringBuilder.AppendLine("  PreserveVanillaBounds " + CfgPreserveVanillaBounds.Value);
			stringBuilder.AppendLine("  FixMissingVertexData " + CfgFixVertexData.Value);
			stringBuilder.AppendLine("  FuzzyBoneNames " + CfgFuzzyBoneNames.Value);
			stringBuilder.AppendLine("  WeaponArms " + CfgWeaponArms.Value);
			stringBuilder.AppendLine("  ScanSeconds " + CfgScanSeconds.Value);
			stringBuilder.AppendLine("  ScanWholeScene " + CfgScanWholeScene.Value);
			stringBuilder.AppendLine("  TuningMode " + CfgTuningMode.Value);
			stringBuilder.AppendLine("  ExcludePathContains \"" + CfgExcludePathContains.Value + "\"");
			stringBuilder.AppendLine("  Logging " + CfgLogging.Value);
			stringBuilder.AppendLine("  LogBones " + CfgLogBones.Value);
			stringBuilder.AppendLine("  LogMaterials " + CfgLogMaterials.Value);
			stringBuilder.AppendLine("  LogArms " + CfgLogArms.Value);
			Dump(stringBuilder.ToString());
		}

		private bool LoadBundle()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
				string text = Path.Combine(directoryName, "prevampfeedbacker.bundle");
				if (!File.Exists(text))
				{
					Fail("Could not find prevampfeedbacker.bundle at " + text);
					return false;
				}
				bundle = AssetBundle.LoadFromFile(text);
				if ((Object)(object)bundle == (Object)null)
				{
					Fail("Bundle failed to load");
					return false;
				}
				GameObject[] array = bundle.LoadAllAssets<GameObject>();
				GameObject[] array2 = array;
				foreach (GameObject val in array2)
				{
					if (string.Equals(((Object)val).name, "CustomFeedbacker", StringComparison.OrdinalIgnoreCase))
					{
						Feedbacker = ReadArm(val, "");
						if (Feedbacker != null)
						{
							AllArms.Add(Feedbacker);
						}
					}
					else if (((Object)val).name.StartsWith("Custom_", StringComparison.OrdinalIgnoreCase))
					{
						string targetName = ((Object)val).name.Substring("Custom_".Length);
						ArmAsset armAsset = ReadArm(val, targetName);
						if (armAsset != null)
						{
							WeaponArms.Add(armAsset);
							AllArms.Add(armAsset);
						}
					}
					else
					{
						Warn("Ignoring prefab \"" + ((Object)val).name + "\", name it CustomFeedbacker or Custom_<target object name>");
					}
				}
				AssetsReady = Feedbacker != null || WeaponArms.Count > 0;
				if (!AssetsReady)
				{
					Fail("Bundle loaded but nothing in it is named usefully");
					Fail("Assets present: " + string.Join(", ", bundle.GetAllAssetNames()));
				}
				return AssetsReady;
			}
			catch (Exception ex)
			{
				Fail("Blew up while loading the bundle: " + ex);
				if ((Object)(object)bundle != (Object)null)
				{
					bundle.Unload(false);
				}
				return false;
			}
		}

		private ArmAsset ReadArm(GameObject prefab, string targetName)
		{
			ArmAsset armAsset = new ArmAsset();
			armAsset.PrefabName = ((Object)prefab).name;
			armAsset.TargetName = targetName;
			SkinnedMeshRenderer componentInChildren = prefab.GetComponentInChildren<SkinnedMeshRenderer>(true);
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.sharedMesh != (Object)null)
			{
				armAsset.Skinned = true;
				armAsset.SourceMesh = componentInChildren.sharedMesh;
				armAsset.SourceMats = (Material[])(((object)((Renderer)componentInChildren).sharedMaterials) ?? ((object)new Material[0]));
				ReadBones(componentInChildren, prefab, armAsset);
				if (armAsset.SourceBoneNames.Length == 0)
				{
					Fail(((Object)prefab).name + " has a skinned mesh but no bones, the mesh is not weighted");
					return null;
				}
			}
			else
			{
				MeshFilter componentInChildren2 = prefab.GetComponentInChildren<MeshFilter>(true);
				MeshRenderer componentInChildren3 = prefab.GetComponentInChildren<MeshRenderer>(true);
				if ((Object)(object)componentInChildren2 == (Object)null || (Object)(object)componentInChildren2.sharedMesh == (Object)null)
				{
					Fail(((Object)prefab).name + " has no mesh on it at all");
					return null;
				}
				armAsset.Skinned = false;
				armAsset.SourceMesh = componentInChildren2.sharedMesh;
				armAsset.SourceMats = (Material[])(((Object)(object)componentInChildren3 != (Object)null) ? ((Array)((Renderer)componentInChildren3).sharedMaterials) : ((Array)new Material[0]));
			}
			Say("Loaded " + ((Object)prefab).name + ((targetName.Length > 0) ? (" targeting \"" + targetName + "\"") : " for the left arm") + "  skinned " + armAsset.Skinned + "  bones " + armAsset.SourceBoneNames.Length + "  submeshes " + armAsset.SourceMesh.subMeshCount + "  materials " + armAsset.SourceMats.Length);
			return armAsset;
		}

		internal static ArmAsset FindArmFor(string objectName)
		{
			ArmAsset armAsset = null;
			for (int i = 0; i < WeaponArms.Count; i++)
			{
				ArmAsset armAsset2 = WeaponArms[i];
				if (armAsset2.TargetName.Length != 0)
				{
					if (string.Equals(objectName, armAsset2.TargetName, StringComparison.Ordinal))
					{
						return armAsset2;
					}
					if (armAsset == null && objectName.EndsWith(armAsset2.TargetName, StringComparison.Ordinal))
					{
						armAsset = armAsset2;
					}
				}
			}
			return armAsset;
		}

		private void ReadBones(SkinnedMeshRenderer smr, GameObject prefab, ArmAsset arm)
		{
			Transform[] bones = smr.bones;
			if (bones == null || bones.Length == 0)
			{
				GameObject val = Object.Instantiate<GameObject>(prefab);
				val.SetActive(false);
				SkinnedMeshRenderer componentInChildren = val.GetComponentInChildren<SkinnedMeshRenderer>(true);
				bones = (Transform[])(((Object)(object)componentInChildren != (Object)null) ? ((Array)componentInChildren.bones) : ((Array)new Transform[0]));
				FillBoneInfo(bones, arm);
				Object.Destroy((Object)(object)val);
			}
			else
			{
				FillBoneInfo(bones, arm);
			}
		}

		private static void FillBoneInfo(Transform[] bones, ArmAsset arm)
		{
			if (bones == null)
			{
				arm.SourceBoneNames = new string[0];
				arm.SourceBoneAncestors = new string[0][];
				return;
			}
			arm.SourceBoneNames = new string[bones.Length];
			arm.SourceBoneAncestors = new string[bones.Length][];
			for (int i = 0; i < bones.Length; i++)
			{
				arm.SourceBoneNames[i] = (((Object)(object)bones[i] != (Object)null) ? ((Object)bones[i]).name : "");
				arm.SourceBoneAncestors[i] = AncestorsOf(bones[i]);
			}
		}

		private static string[] AncestorsOf(Transform t)
		{
			List<string> list = new List<string>();
			if ((Object)(object)t == (Object)null)
			{
				return list.ToArray();
			}
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				list.Add(((Object)parent).name);
				parent = parent.parent;
			}
			return list.ToArray();
		}
	}
}