Decompiled source of VRM Fixes v1.1.4

plugins/VRMFixes.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using LocalValheimFixes.Packages;
using Microsoft.CodeAnalysis;
using UniGLTF;
using UniGLTF.Utils;
using UniVRM10;
using UnityEngine;

[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("VRMFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.4.0")]
[assembly: AssemblyInformationalVersion("1.1.4+ad276bd4679d2af06e242ff621684e865f1348ee")]
[assembly: AssemblyProduct("VRMFixes")]
[assembly: AssemblyTitle("VRMFixes")]
[assembly: AssemblyVersion("1.1.4.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 LocalValheimFixes
{
	[BepInPlugin("local.valheim.vrm1springclonefix", "VRM Fixes", "1.1.4")]
	[BepInDependency("com.rawrtastic.plugins.enhancedvalheimvrm", "1.0.0")]
	public sealed class SpringCloneFixPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static FieldInfo CloneField;

		private void Awake()
		{
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PluginInfo val = Chainloader.PluginInfos["com.rawrtastic.plugins.enhancedvalheimvrm"];
			Version version = val.Metadata.Version;
			if (!EnhancedVrmCompatibility.AllowsVersion(version))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Inactive: EnhancedValheimVRM " + version?.ToString() + " is outside the supported 1.x version family."));
				return;
			}
			Type type = ((object)val.Instance).GetType().Assembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false);
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "CreateVrmGo", Type.EmptyTypes, (Type[])null));
			CloneField = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGo"));
			if (methodInfo == null || methodInfo.IsStatic || methodInfo.ContainsGenericParameters || methodInfo.ReturnType != typeof(void) || CloneField == null || CloneField.IsStatic || CloneField.FieldType != typeof(GameObject))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Inactive: the expected clone staging hook was not found.");
				return;
			}
			try
			{
				new Harmony("local.valheim.vrm1springclonefix").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SpringCloneFixPlugin), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Inactive: clone staging hook could not be installed: " + ex));
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("VRM1 spring clone repair enabled for EnhancedValheimVRM " + version?.ToString() + "."));
			try
			{
				PackagedAvatarHooks.Install(type.Assembly, ((BaseUnityPlugin)this).Logger);
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Packaged avatar support inactive: " + ex2));
			}
			try
			{
				BearPawsFix.Install(type.Assembly, version, ((BaseUnityPlugin)this).Logger);
			}
			catch (Exception ex3)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Nordic bear paws support inactive: " + ex3));
			}
		}

		private static void BeforeCreate(object __instance)
		{
			try
			{
				object? value = CloneField.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				int num = SpringGraphRepair.RepairFromCachedTemplate(val);
				if (num > 0)
				{
					Log.LogInfo((object)("Restored VRM1 spring graph: " + num + " joint references on " + ((Object)val).name + "."));
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("VRM1 spring clone repair failed: " + ex));
			}
		}
	}
	public static class SpringGraphRepair
	{
		private static readonly FieldInfo RuntimeField = AccessTools.DeclaredField(typeof(Vrm10Instance), "m_runtime");

		public static int RepairFromCachedTemplate(GameObject cloneRoot)
		{
			if ((Object)(object)cloneRoot == (Object)null)
			{
				return 0;
			}
			Vrm10Instance clone = cloneRoot.GetComponent<Vrm10Instance>();
			if (!NeedsRepair(clone))
			{
				return 0;
			}
			Vrm10Instance[] array = (from v in Resources.FindObjectsOfTypeAll<Vrm10Instance>()
				where (Object)(object)v != (Object)(object)clone && (Object)(object)v.Vrm == (Object)(object)clone.Vrm && (Object)(object)((Component)v).transform.parent == (Object)null && !((Component)v).gameObject.activeInHierarchy && v.SpringBone != null && v.SpringBone.Springs.Count > 0
				select v).ToArray();
			if (array.Length != 1)
			{
				throw new InvalidOperationException("Expected one inactive imported template, found " + array.Length);
			}
			return Repair(((Component)array[0]).gameObject, cloneRoot);
		}

		private static bool NeedsRepair(Vrm10Instance clone)
		{
			if ((Object)(object)clone != (Object)null && (clone.SpringBone == null || clone.SpringBone.Springs.Count == 0))
			{
				return ((Component)clone).GetComponentsInChildren<VRM10SpringBoneJoint>(true).Length > 1;
			}
			return false;
		}

		public static int Repair(GameObject sourceRoot, GameObject cloneRoot)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sourceRoot == (Object)null || (Object)(object)cloneRoot == (Object)null || (Object)(object)sourceRoot == (Object)(object)cloneRoot)
			{
				return 0;
			}
			Vrm10Instance component = sourceRoot.GetComponent<Vrm10Instance>();
			Vrm10Instance component2 = cloneRoot.GetComponent<Vrm10Instance>();
			if (!NeedsRepair(component2) || (Object)(object)component == (Object)null || component.SpringBone == null || component.SpringBone.Springs.Count == 0)
			{
				return 0;
			}
			Dictionary<Transform, Transform> transforms = new Dictionary<Transform, Transform>();
			MapHierarchy(sourceRoot.transform, cloneRoot.transform, transforms);
			Vrm10InstanceSpringBone val = new Vrm10InstanceSpringBone();
			Dictionary<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>> dictionary = new Dictionary<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>>();
			VRM10SpringBoneColliderGroup[] componentsInChildren = ((Component)component).GetComponentsInChildren<VRM10SpringBoneColliderGroup>(true);
			foreach (VRM10SpringBoneColliderGroup val2 in componentsInChildren)
			{
				VRM10SpringBoneColliderGroup key = MapComponent<VRM10SpringBoneColliderGroup>(val2, transforms);
				dictionary[key] = val2.Colliders.Select((VRM10SpringBoneCollider c) => MapComponent<VRM10SpringBoneCollider>(c, transforms)).ToList();
			}
			val.ColliderGroups = component.SpringBone.ColliderGroups.Select((VRM10SpringBoneColliderGroup g) => MapComponent<VRM10SpringBoneColliderGroup>(g, transforms)).ToList();
			int num = 0;
			foreach (Spring spring in component.SpringBone.Springs)
			{
				Spring val3 = new Spring(spring.Name);
				val3.Center = (((Object)(object)spring.Center == (Object)null) ? null : transforms[spring.Center]);
				val3.Joints = spring.Joints.Select((VRM10SpringBoneJoint j) => MapComponent<VRM10SpringBoneJoint>(j, transforms)).ToList();
				val3.ColliderGroups = spring.ColliderGroups.Select((VRM10SpringBoneColliderGroup g) => MapComponent<VRM10SpringBoneColliderGroup>(g, transforms)).ToList();
				if (val3.Joints.Count((VRM10SpringBoneJoint j) => (Object)(object)j != (Object)null) < 2)
				{
					throw new InvalidOperationException("Imported spring has fewer than two usable joints.");
				}
				num += val3.Joints.Count;
				val.Springs.Add(val3);
			}
			RuntimeGltfInstance component3 = sourceRoot.GetComponent<RuntimeGltfInstance>();
			RuntimeGltfInstance component4 = cloneRoot.GetComponent<RuntimeGltfInstance>();
			IDictionary<Transform, TransformState> dictionary2 = (((Object)(object)component4 == (Object)null) ? null : (component4.InitialTransformStates as IDictionary<Transform, TransformState>));
			if ((Object)(object)component3 == (Object)null || dictionary2 == null)
			{
				throw new InvalidOperationException("Initial transform state storage unavailable.");
			}
			Dictionary<Transform, TransformState> dictionary3 = new Dictionary<Transform, TransformState>();
			foreach (KeyValuePair<Transform, TransformState> initialTransformState in component3.InitialTransformStates)
			{
				dictionary3.Add(transforms[initialTransformState.Key], initialTransformState.Value);
			}
			foreach (KeyValuePair<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>> item in dictionary)
			{
				item.Key.Colliders = item.Value;
			}
			VRM10SpringBoneJoint[] componentsInChildren2 = ((Component)component).GetComponentsInChildren<VRM10SpringBoneJoint>(true);
			foreach (VRM10SpringBoneJoint val4 in componentsInChildren2)
			{
				VRM10SpringBoneJoint obj = MapComponent<VRM10SpringBoneJoint>(val4, transforms);
				obj.m_stiffnessForce = val4.m_stiffnessForce;
				obj.m_gravityPower = val4.m_gravityPower;
				obj.m_gravityDir = val4.m_gravityDir;
				obj.m_dragForce = val4.m_dragForce;
				obj.m_jointRadius = val4.m_jointRadius;
			}
			VRM10SpringBoneCollider[] componentsInChildren3 = ((Component)component).GetComponentsInChildren<VRM10SpringBoneCollider>(true);
			foreach (VRM10SpringBoneCollider val5 in componentsInChildren3)
			{
				VRM10SpringBoneCollider obj2 = MapComponent<VRM10SpringBoneCollider>(val5, transforms);
				obj2.ColliderType = val5.ColliderType;
				obj2.Offset = val5.Offset;
				obj2.Tail = val5.Tail;
				obj2.Radius = val5.Radius;
			}
			foreach (KeyValuePair<Transform, TransformState> item2 in dictionary3)
			{
				dictionary2[item2.Key] = item2.Value;
			}
			component2.SpringBone = val;
			if (RuntimeField != null && RuntimeField.GetValue(component2) != null)
			{
				component2.Runtime.ReconstructSpringBone();
			}
			return num;
		}

		private static void MapHierarchy(Transform source, Transform clone, Dictionary<Transform, Transform> map)
		{
			if (source.childCount > clone.childCount)
			{
				throw new InvalidOperationException("Clone hierarchy is shorter than template.");
			}
			map.Add(source, clone);
			for (int i = 0; i < source.childCount; i++)
			{
				Transform child = source.GetChild(i);
				Transform child2 = clone.GetChild(i);
				if (((Object)child).name != ((Object)child2).name)
				{
					throw new InvalidOperationException("Clone hierarchy differs at child " + i + " of " + ((Object)source).name);
				}
				MapHierarchy(child, child2, map);
			}
		}

		private static T MapComponent<T>(T source, Dictionary<Transform, Transform> map) where T : Component
		{
			if ((Object)(object)source == (Object)null)
			{
				return default(T);
			}
			int num = Array.IndexOf(((Component)source).GetComponents<T>(), source);
			T[] components = ((Component)map[((Component)source).transform]).GetComponents<T>();
			if (num < 0 || num >= components.Length)
			{
				throw new InvalidOperationException("Missing cloned component " + typeof(T).Name);
			}
			return components[num];
		}
	}
	internal static class EnhancedVrmCompatibility
	{
		internal static bool AllowsVersion(Version version)
		{
			if (version != null)
			{
				return version.Major == 1;
			}
			return false;
		}
	}
	internal static class PackagedAvatarHooks
	{
		private static AvatarPackIndex _index;

		private static FieldInfo _outfitPath;

		internal static void Install(Assembly enhancedAssembly, ManualLogSource log)
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(enhancedAssembly.GetType("EnhancedValheimVRM.Constants+Vrm", throwOnError: true), "Find", new Type[1] { typeof(string) }, (Type[])null);
			Type? type = enhancedAssembly.GetType("EnhancedValheimVRM.OutfitController", throwOnError: true);
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(type, "Generate", Type.EmptyTypes, (Type[])null);
			_outfitPath = AccessTools.DeclaredField(type, "_path");
			if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ContainsGenericParameters || methodInfo.ReturnType != typeof(string) || methodInfo2 == null || methodInfo2.IsStatic || methodInfo2.ContainsGenericParameters || methodInfo2.ReturnType != typeof(string) || _outfitPath == null || _outfitPath.IsStatic || _outfitPath.FieldType != typeof(string))
			{
				throw new MissingMethodException("Expected EnhancedValheimVRM avatar lookup/write hooks were not found.");
			}
			_index = AvatarPackIndex.Build(Paths.PluginPath);
			foreach (string diagnostic in _index.Diagnostics)
			{
				log.LogWarning((object)diagnostic);
			}
			Harmony val = new Harmony("local.valheim.vrm1springclonefix.packages");
			try
			{
				val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(PackagedAvatarHooks), "BeforeGenerate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PackagedAvatarHooks), "AfterFind", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
				val.UnpatchSelf();
				throw;
			}
			log.LogInfo((object)("Packaged avatars indexed: " + _index.Bindings.Count + ". Explicit package characters override manual files; restart after package changes."));
			foreach (AvatarBinding binding in _index.Bindings)
			{
				log.LogInfo((object)("Packaged avatar " + binding.Character + " from " + binding.PackId + ": " + binding.ModelPath));
			}
		}

		private static void AfterFind(string fileName, ref string __result)
		{
			if (_index.TryResolve(fileName, out var path))
			{
				__result = path;
			}
		}

		private static bool BeforeGenerate(object __instance, ref string __result)
		{
			string path = _outfitPath.GetValue(__instance) as string;
			if (!_index.IsProtectedPath(path))
			{
				return true;
			}
			__result = "Packaged avatars use read-only outfit defaults; outfit generation is disabled for this avatar.";
			return false;
		}
	}
	public static class BearPawsFix
	{
		public sealed class AttachmentPlan
		{
			private readonly SkinnedMeshRenderer renderer;

			private readonly Transform avatarRoot;

			private readonly Transform[] palette;

			private readonly List<Attachment> attachments;

			internal AttachmentPlan(SkinnedMeshRenderer renderer, Transform avatarRoot, Transform[] palette, List<Attachment> attachments)
			{
				this.renderer = renderer;
				this.avatarRoot = avatarRoot;
				this.palette = palette;
				this.attachments = attachments;
			}

			public void Apply(List<Transform> moved)
			{
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: 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)
				foreach (Attachment attachment in attachments)
				{
					attachment.Source.SetParent(attachment.Target, false);
					attachment.Source.localPosition = Vector3.zero;
					attachment.Source.localRotation = attachment.Rotation;
					attachment.Source.localScale = attachment.Scale;
					if (!moved.Contains(attachment.Source))
					{
						moved.Add(attachment.Source);
					}
				}
				renderer.bones = palette;
				renderer.rootBone = avatarRoot;
				renderer.updateWhenOffscreen = true;
			}
		}

		internal sealed class Attachment
		{
			internal Transform Source;

			internal Transform Target;

			internal Quaternion Rotation;

			internal Vector3 Scale;
		}

		private static FieldInfo AvatarRoot;

		private static FieldInfo AvatarAnimator;

		private static FieldInfo Settings;

		private static FieldInfo ItemName;

		private static FieldInfo Equipment;

		private static FieldInfo Moved;

		private static FieldInfo PiecesAvatar;

		private static Type PiecesType;

		private static ManualLogSource Log;

		private static readonly string[] Names = new string[6] { "LeftArm", "LeftForeArm", "LeftHand", "RightArm", "RightForeArm", "RightHand" };

		private static readonly HumanBodyBones[] Targets;

		internal static void Install(Assembly assembly, Version version, ManualLogSource log)
		{
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Expected O, but got Unknown
			Log = log;
			if (version.Major != 1 || version.Minor != 3 || version.Build != 1)
			{
				log.LogInfo((object)"Nordic bear paws fix applies to EnhancedValheimVRM 1.3.1 only; spring and package support retain the 1.x policy.");
				return;
			}
			Type type = assembly.GetType("EnhancedValheimVRM.VrmAnimator", throwOnError: false);
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "MoveRigOntoAvatar", new Type[1] { typeof(GameObject) }, (Type[])null));
			PiecesType = assembly.GetType("EnhancedValheimVRM.RiggedItemPieces", throwOnError: false);
			AvatarRoot = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGo"));
			AvatarAnimator = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGoAnimator"));
			Settings = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmSettings"));
			ItemName = ((type == null) ? null : AccessTools.DeclaredField(type, "_rigItemName"));
			Equipment = ((type == null) ? null : AccessTools.DeclaredField(type, "_visEquipment"));
			Moved = ((PiecesType == null) ? null : AccessTools.DeclaredField(PiecesType, "Moved"));
			PiecesAvatar = ((PiecesType == null) ? null : AccessTools.DeclaredField(PiecesType, "Avatar"));
			if (methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(void) || AvatarRoot?.FieldType != typeof(GameObject) || AvatarAnimator?.FieldType != typeof(Animator) || ItemName?.FieldType != typeof(string) || Settings == null || Equipment == null || PiecesType == null || !typeof(Component).IsAssignableFrom(PiecesType) || Moved?.FieldType != typeof(List<Transform>) || PiecesAvatar?.FieldType != typeof(Animator))
			{
				log.LogWarning((object)"Nordic bear paws fix inactive: expected equipment hooks unavailable. Other fixes remain active.");
				return;
			}
			new Harmony("local.valheim.vrm1springclonefix.darkurgepaws").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BearPawsFix), "BeforeMove", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			log.LogInfo((object)"Paws of the Bear attachment fix enabled for Dark Urge and Sigrid.");
		}

		private static bool BeforeMove(object __instance, GameObject rig)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			if (!string.Equals(ItemName.GetValue(__instance) as string, "FistBjornClaw", StringComparison.Ordinal))
			{
				return true;
			}
			object? value = AvatarRoot.GetValue(__instance);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if (!IsSupportedAvatar(val))
			{
				return true;
			}
			try
			{
				object? value2 = AvatarAnimator.GetValue(__instance);
				Animator val2 = (Animator)((value2 is Animator) ? value2 : null);
				Component val3 = (((Object)(object)rig == (Object)null) ? null : rig.GetComponent(PiecesType));
				if ((Object)(object)val3 != (Object)null && (Object)(Animator)PiecesAvatar.GetValue(val3) == (Object)(object)val2)
				{
					return false;
				}
				object value3 = Settings.GetValue(__instance);
				object value4 = Equipment.GetValue(__instance);
				if (value3 == null || value4 == null)
				{
					return true;
				}
				FieldInfo fieldInfo = AccessTools.Field(value3.GetType(), "PlayerVrmScale");
				PropertyInfo propertyInfo = AccessTools.Property(value3.GetType(), "PlayerVrmScale");
				object obj = ((fieldInfo != null) ? fieldInfo.GetValue(value3) : propertyInfo?.GetValue(value3, null));
				object? obj2 = AccessTools.Field(value4.GetType(), "m_bodyModel")?.GetValue(value4);
				SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)((obj2 is SkinnedMeshRenderer) ? obj2 : null);
				if (!(obj is float scale) || (Object)(object)val4 == (Object)null)
				{
					return true;
				}
				AttachmentPlan attachmentPlan = Prepare(val, val2, rig, val4.bones, scale);
				if (attachmentPlan == null)
				{
					Log.LogWarning((object)"Nordic bear paws fix skipped: item or avatar rig differs from the validated layout.");
					return true;
				}
				val3 = val3 ?? rig.AddComponent(PiecesType);
				if (!(Moved.GetValue(val3) is List<Transform> moved))
				{
					return true;
				}
				attachmentPlan.Apply(moved);
				PiecesAvatar.SetValue(val3, val2);
				Log.LogInfo((object)("Attached bear paws for " + val.GetComponent<Vrm10Instance>().Vrm.Meta.Name + " with calibrated wrist axes and both upper-arm weights."));
				return false;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Nordic bear paws attachment could not be prepared: " + ex));
				return true;
			}
		}

		public static bool IsSupportedAvatar(GameObject root)
		{
			Vrm10Instance val = (((Object)(object)root == (Object)null) ? null : root.GetComponent<Vrm10Instance>());
			if ((Object)(object)val != (Object)null && (Object)(object)val.Vrm != (Object)null && val.Vrm.Meta != null)
			{
				if (!string.Equals(val.Vrm.Meta.Name, "Dark Urge", StringComparison.Ordinal))
				{
					return string.Equals(val.Vrm.Meta.Name, "Sigrid", StringComparison.Ordinal);
				}
				return true;
			}
			return false;
		}

		public static AttachmentPlan Prepare(GameObject root, Animator animator, GameObject rig, Transform[] bodyBones, float scale)
		{
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			if (!IsSupportedAvatar(root) || (Object)(object)animator == (Object)null || (Object)(object)animator.avatar == (Object)null || !animator.isHuman || (Object)(object)rig == (Object)null || bodyBones == null || !Finite(scale) || scale <= 0f)
			{
				return null;
			}
			RuntimeGltfInstance component = root.GetComponent<RuntimeGltfInstance>();
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.Ordinal);
			Transform[] componentsInChildren = rig.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (dictionary.ContainsKey(((Object)val).name))
				{
					return null;
				}
				dictionary.Add(((Object)val).name, val);
			}
			SkinnedMeshRenderer[] componentsInChildren2 = rig.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			if (componentsInChildren2.Length != 1)
			{
				return null;
			}
			SkinnedMeshRenderer val2 = componentsInChildren2[0];
			Mesh sharedMesh = val2.sharedMesh;
			if ((Object)(object)sharedMesh == (Object)null || ((Object)sharedMesh).name != "BerserkerClaws" || sharedMesh.vertexCount != 631 || bodyBones.Length != 53 || val2.bones.Length != bodyBones.Length || sharedMesh.bindposes.Length != bodyBones.Length)
			{
				return null;
			}
			Transform[] array = (Transform[])(object)new Transform[bodyBones.Length];
			for (int j = 0; j < array.Length; j++)
			{
				if ((Object)(object)bodyBones[j] == (Object)null || !dictionary.TryGetValue(((Object)bodyBones[j]).name, out array[j]))
				{
					return null;
				}
			}
			Transform[] array2 = (Transform[])(object)new Transform[Names.Length];
			for (int k = 0; k < array2.Length; k++)
			{
				if (!dictionary.TryGetValue(Names[k], out array2[k]))
				{
					return null;
				}
			}
			List<Attachment> list = new List<Attachment>();
			for (int l = 0; l < array2.Length; l++)
			{
				int num = Array.IndexOf(array, array2[l]);
				Transform boneTransform = animator.GetBoneTransform(Targets[l]);
				if (num < 0 || (Object)(object)boneTransform == (Object)null || !RestRotation(component, root.transform, boneTransform, out var rotation))
				{
					return null;
				}
				Matrix4x4 val3 = rig.transform.worldToLocalMatrix * array2[l].localToWorldMatrix;
				Vector3 lossyScale = ((Matrix4x4)(ref val3)).lossyScale;
				Vector3 lossyScale2 = boneTransform.lossyScale;
				if (!PositiveUniform(lossyScale) || !PositiveUniform(lossyScale2) || ((Matrix4x4)(ref val3)).determinant <= 0f)
				{
					return null;
				}
				Quaternion val4 = Quaternion.Inverse(rotation) * ((Matrix4x4)(ref val3)).rotation;
				if (!Finite(val4.x) || !Finite(val4.y) || !Finite(val4.z) || !Finite(val4.w))
				{
					return null;
				}
				list.Add(new Attachment
				{
					Source = array2[l],
					Target = boneTransform,
					Rotation = val4,
					Scale = new Vector3(lossyScale.x * scale / lossyScale2.x, lossyScale.y * scale / lossyScale2.y, lossyScale.z * scale / lossyScale2.z)
				});
			}
			return new AttachmentPlan(val2, root.transform, array, list);
		}

		private static bool RestRotation(RuntimeGltfInstance gltf, Transform root, Transform bone, out Quaternion rotation)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_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_003d: Unknown result type (might be due to invalid IL or missing references)
			rotation = Quaternion.identity;
			Transform val = bone;
			while ((Object)(object)val != (Object)(object)root)
			{
				if ((Object)(object)val == (Object)null || !gltf.InitialTransformStates.TryGetValue(val, out var value))
				{
					return false;
				}
				rotation = ((TransformState)(ref value)).LocalRotation * rotation;
				val = val.parent;
			}
			return true;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static bool PositiveUniform(Vector3 scale)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(scale.x) && Finite(scale.y) && Finite(scale.z) && scale.x > 1E-06f && scale.y > 1E-06f && scale.z > 1E-06f && Mathf.Abs(scale.x / scale.y - 1f) < 0.002f)
			{
				return Mathf.Abs(scale.x / scale.z - 1f) < 0.002f;
			}
			return false;
		}

		static BearPawsFix()
		{
			HumanBodyBones[] array = new HumanBodyBones[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Targets = (HumanBodyBones[])(object)array;
		}
	}
}
namespace LocalValheimFixes.Packages
{
	[DataContract]
	internal sealed class AvatarPackManifest
	{
		[DataMember(Name = "schemaVersion", IsRequired = true)]
		public int SchemaVersion { get; set; }

		[DataMember(Name = "packId", IsRequired = true)]
		public string PackId { get; set; }

		[DataMember(Name = "avatars", IsRequired = true)]
		public AvatarPackEntry[] Avatars { get; set; }
	}
	[DataContract]
	internal sealed class AvatarPackEntry
	{
		[DataMember(Name = "character", IsRequired = true)]
		public string Character { get; set; }

		[DataMember(Name = "model", IsRequired = true)]
		public string Model { get; set; }

		[DataMember(Name = "settings", IsRequired = true)]
		public string Settings { get; set; }

		[DataMember(Name = "outfits", EmitDefaultValue = false)]
		public string Outfits { get; set; }
	}
	public sealed class AvatarBinding
	{
		public string Character { get; }

		public string PackId { get; }

		public string PackDirectory { get; }

		public string ModelPath { get; }

		public string SettingsPath { get; }

		public string OutfitPath { get; }

		internal AvatarBinding(string character, string packId, string directory, string model, string settings, string outfits)
		{
			Character = character;
			PackId = packId;
			PackDirectory = directory;
			ModelPath = model;
			SettingsPath = settings;
			OutfitPath = outfits;
		}
	}
	public sealed class AvatarPackIndex
	{
		private const string Marker = "avatar-pack.json";

		private const string AbsentOutfitDirectory = "__vrm_fixes_no_outfits__";

		private readonly IReadOnlyDictionary<string, string> _files;

		private readonly string[] _protectedDirectories;

		public IReadOnlyList<AvatarBinding> Bindings { get; }

		public IReadOnlyList<string> Diagnostics { get; }

		private AvatarPackIndex(IEnumerable<AvatarBinding> bindings, IEnumerable<string> diagnostics)
		{
			AvatarBinding[] array = bindings.OrderBy<AvatarBinding, string>((AvatarBinding x) => x.Character, StringComparer.OrdinalIgnoreCase).ToArray();
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			AvatarBinding[] array2 = array;
			foreach (AvatarBinding avatarBinding in array2)
			{
				dictionary.Add(avatarBinding.Character + ".vrm", avatarBinding.ModelPath);
				dictionary.Add("settings_" + avatarBinding.Character + ".txt", avatarBinding.SettingsPath);
				dictionary.Add("outfits_" + avatarBinding.Character + ".txt", avatarBinding.OutfitPath);
			}
			_files = new ReadOnlyDictionary<string, string>(dictionary);
			_protectedDirectories = array.Select((AvatarBinding x) => WithSeparator(x.PackDirectory)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
			Bindings = Array.AsReadOnly(array);
			Diagnostics = Array.AsReadOnly(diagnostics.ToArray());
		}

		public static AvatarPackIndex Build(string pluginDirectory)
		{
			List<string> list = new List<string>();
			List<AvatarBinding[]> list2 = new List<AvatarBinding[]>();
			if (!Directory.Exists(pluginDirectory))
			{
				return new AvatarPackIndex(Array.Empty<AvatarBinding>(), list);
			}
			foreach (string item in FindMarkers(Path.GetFullPath(pluginDirectory), list))
			{
				try
				{
					list2.Add(ReadPack(item));
				}
				catch (Exception ex) when (ex is InvalidDataException || ex is IOException || ex is UnauthorizedAccessException || ex is SerializationException || ex is ArgumentException)
				{
					list.Add("Ignored avatar pack " + item + ": " + ex.Message);
				}
			}
			HashSet<string> duplicateIds = new HashSet<string>(from x in list2.GroupBy<AvatarBinding[], string>((AvatarBinding[] x) => x[0].PackId, StringComparer.OrdinalIgnoreCase)
				where x.Count() > 1
				select x.Key, StringComparer.OrdinalIgnoreCase);
			foreach (string item2 in duplicateIds.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase))
			{
				list.Add("Duplicate avatar pack ID " + item2 + "; all copies ignored.");
			}
			AvatarBinding[] source = list2.Where((AvatarBinding[] x) => !duplicateIds.Contains(x[0].PackId)).SelectMany((AvatarBinding[] x) => x).ToArray();
			List<AvatarBinding> list3 = new List<AvatarBinding>();
			foreach (IGrouping<string, AvatarBinding> item3 in source.GroupBy<AvatarBinding, string>((AvatarBinding x) => x.Character, StringComparer.OrdinalIgnoreCase).OrderBy<IGrouping<string, AvatarBinding>, string>((IGrouping<string, AvatarBinding> x) => x.Key, StringComparer.OrdinalIgnoreCase))
			{
				if (item3.Count() == 1)
				{
					list3.Add(item3.Single());
					continue;
				}
				list.Add("Conflicting packaged character " + item3.Key + " in " + string.Join(", ", item3.Select((AvatarBinding x) => x.PackId).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)) + "; all package mappings for this character ignored. Manual lookup remains available.");
			}
			return new AvatarPackIndex(list3, list);
		}

		public bool TryResolve(string fileName, out string path)
		{
			path = null;
			if (!string.IsNullOrEmpty(fileName))
			{
				return _files.TryGetValue(fileName, out path);
			}
			return false;
		}

		public bool IsProtectedPath(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return false;
			}
			try
			{
				string fullPath = Path.GetFullPath(path);
				return _protectedDirectories.Any((string root) => fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase));
			}
			catch (Exception ex) when (ex is ArgumentException || ex is IOException || ex is NotSupportedException)
			{
				return false;
			}
		}

		private static IEnumerable<string> FindMarkers(string directory, List<string> diagnostics)
		{
			string[] source;
			string[] array;
			try
			{
				if (DisabledName(Path.GetFileName(directory)) || IsReparsePoint(directory))
				{
					yield break;
				}
				source = Directory.GetFiles(directory).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string x) => x, StringComparer.Ordinal)
					.ToArray();
				array = Directory.GetDirectories(directory).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string x) => x, StringComparer.Ordinal)
					.ToArray();
			}
			catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
			{
				diagnostics.Add("Cannot inspect avatar package directory " + directory + ": " + ex.Message);
				yield break;
			}
			string[] array2 = source.Where((string x) => string.Equals(Path.GetFileName(x), "avatar-pack.json", StringComparison.OrdinalIgnoreCase)).ToArray();
			if (array2.Length != 0)
			{
				bool flag = false;
				try
				{
					flag = array2.Length == 1 && !IsReparsePoint(array2[0]);
				}
				catch (Exception ex2) when (ex2 is IOException || ex2 is UnauthorizedAccessException)
				{
					diagnostics.Add("Cannot inspect avatar-pack.json in " + directory + ": " + ex2.Message);
				}
				if (flag)
				{
					yield return array2[0];
				}
				else
				{
					diagnostics.Add("Ambiguous, inaccessible or linked avatar-pack.json in " + directory + "; directory ignored.");
				}
				yield break;
			}
			string[] array3 = array;
			foreach (string directory2 in array3)
			{
				foreach (string item in FindMarkers(directory2, diagnostics))
				{
					yield return item;
				}
			}
		}

		private static AvatarBinding[] ReadPack(string marker)
		{
			if (new FileInfo(marker).Length > 65536)
			{
				throw new InvalidDataException("Manifest exceeds 64 KiB.");
			}
			AvatarPackManifest avatarPackManifest;
			using (FileStream stream = File.OpenRead(marker))
			{
				avatarPackManifest = (AvatarPackManifest)new DataContractJsonSerializer(typeof(AvatarPackManifest)).ReadObject(stream);
			}
			if (avatarPackManifest == null || avatarPackManifest.SchemaVersion != 1)
			{
				throw new InvalidDataException("Only schemaVersion 1 is supported.");
			}
			if (string.IsNullOrEmpty(avatarPackManifest.PackId) || avatarPackManifest.PackId.Length > 128 || avatarPackManifest.PackId.Any((char c) => !char.IsLetterOrDigit(c) && c != '-' && c != '_' && c != '.'))
			{
				throw new InvalidDataException("packId must contain letters, digits, dots, hyphens or underscores.");
			}
			if (avatarPackManifest.Avatars == null || avatarPackManifest.Avatars.Length == 0 || avatarPackManifest.Avatars.Length > 256)
			{
				throw new InvalidDataException("avatars must contain 1 to 256 entries.");
			}
			string directoryName = Path.GetDirectoryName(marker);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			List<AvatarBinding> list = new List<AvatarBinding>();
			AvatarPackEntry[] avatars = avatarPackManifest.Avatars;
			foreach (AvatarPackEntry avatarPackEntry in avatars)
			{
				if (avatarPackEntry == null || !ValidCharacter(avatarPackEntry.Character) || !hashSet.Add(avatarPackEntry.Character))
				{
					throw new InvalidDataException("Invalid, reserved or duplicate character name.");
				}
				string model = ResolveAsset(directoryName, avatarPackEntry.Model, avatarPackEntry.Character + ".vrm");
				string settings = ResolveAsset(directoryName, avatarPackEntry.Settings, "settings_" + avatarPackEntry.Character + ".txt");
				string outfits;
				if (avatarPackEntry.Outfits != null)
				{
					outfits = ResolveAsset(directoryName, avatarPackEntry.Outfits, "outfits_" + avatarPackEntry.Character + ".txt");
				}
				else
				{
					string text = Path.Combine(directoryName, "__vrm_fixes_no_outfits__");
					if (Directory.Exists(text) || File.Exists(text))
					{
						throw new InvalidDataException("Reserved absent-outfit path exists in the package.");
					}
					outfits = Path.Combine(text, "outfits_" + avatarPackEntry.Character + ".txt");
				}
				list.Add(new AvatarBinding(avatarPackEntry.Character, avatarPackManifest.PackId, directoryName, model, settings, outfits));
			}
			return list.ToArray();
		}

		private static bool ValidCharacter(string name)
		{
			if (!string.IsNullOrWhiteSpace(name) && name == name.Trim() && name.Length <= 128 && name != "." && name != ".." && !name.Equals("___Default", StringComparison.OrdinalIgnoreCase))
			{
				return !name.Any((char c) => char.IsControl(c) || "<>:\"/\\|?*".Contains(c));
			}
			return false;
		}

		private static string ResolveAsset(string directory, string relativePath, string expectedName)
		{
			if (string.IsNullOrWhiteSpace(relativePath) || Path.IsPathRooted(relativePath) || relativePath.Contains(':') || relativePath.Any(char.IsControl))
			{
				throw new InvalidDataException("Asset path must be a relative path inside its pack.");
			}
			string[] array = relativePath.Replace('\\', '/').Split('/');
			if (array.Any((string x) => x.Length == 0 || x == "." || x == ".." || DisabledName(x)))
			{
				throw new InvalidDataException("Asset path contains an invalid or disabled segment.");
			}
			if (!array.Last().Equals(expectedName, StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidDataException("Expected payload basename " + expectedName + ".");
			}
			string fullPath = Path.GetFullPath(Path.Combine(directory, Path.Combine(array)));
			if (!fullPath.StartsWith(WithSeparator(directory), StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidDataException("Asset path escapes its package.");
			}
			string text = directory;
			string[] array2 = array;
			foreach (string path in array2)
			{
				text = Path.Combine(text, path);
				if (!File.Exists(text) && !Directory.Exists(text))
				{
					throw new InvalidDataException("Required active payload is missing: " + relativePath);
				}
				if (IsReparsePoint(text))
				{
					throw new InvalidDataException("Linked asset paths are not supported.");
				}
			}
			if (!File.Exists(fullPath))
			{
				throw new InvalidDataException("Asset is not a regular file: " + relativePath);
			}
			return fullPath;
		}

		private static bool DisabledName(string name)
		{
			return name.EndsWith(".old", StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsReparsePoint(string path)
		{
			return (File.GetAttributes(path) & FileAttributes.ReparsePoint) != 0;
		}

		private static string WithSeparator(string path)
		{
			return Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
		}
	}
}