Decompiled source of VRM Fixes v1.1.5

plugins/NordicGlutePhysics.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LocalValheimFixes;
using Microsoft.CodeAnalysis;
using UniGLTF;
using UniGLTF.Utils;
using UniVRM10.FastSpringBones.System;
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("NordicGlutePhysics")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+3356aed1f6508684669959e6a326f48d5ef50221")]
[assembly: AssemblyProduct("NordicGlutePhysics")]
[assembly: AssemblyTitle("NordicGlutePhysics")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace NordicGlutePhysics
{
	public struct GluteFrame
	{
		public D3 P;

		public M3 R;

		public double S;

		public GluteFrame(D3 p, M3 r, double s)
		{
			P = p;
			R = r;
			S = s;
		}
	}
	public sealed class GluteParams
	{
		public double[] Freq;

		public double[] Zeta;

		public double[] LimPos;

		public double[] LimNeg;

		public double Inertia = 1.0;

		public double Twist = 2.0;

		public double HpHz = 0.8;

		public double Steady = 0.2;

		public double EnvK = 1.5;

		public double EnvAttack = 0.25;

		public double EnvRelease = 1.5;

		public double DriveHz = 3.0;

		public double Gravity = 0.1;

		public double Amount = 1.0;

		public double ThighDrive = 0.3;

		public double PivotUp = 0.06;

		public double PivotIn = 0.08;

		public double RotShare = 1.0;

		public double MaxTurnDeg = 25.0;

		public double FilterHz = 12.0;

		public double MaxAccel = 60.0;

		public double MaxSpeed = 1.2;

		public double Substep = 1.0 / 240.0;

		public double SoftStart = 0.7;

		public double SoftFreq = 5.0;

		public double LimZeta = 0.9;

		public double HardAt = 1.3;

		public GluteParams Clone()
		{
			GluteParams obj = (GluteParams)MemberwiseClone();
			obj.Freq = (double[])Freq.Clone();
			obj.Zeta = (double[])Zeta.Clone();
			obj.LimPos = (double[])LimPos.Clone();
			obj.LimNeg = (double[])LimNeg.Clone();
			return obj;
		}
	}
	public static class GlutePresets
	{
		public static readonly string[] Names = new string[3] { "light", "visible", "strong" };

		public static GluteParams For(string name)
		{
			string text = (name ?? "").ToLowerInvariant();
			if (!(text == "light"))
			{
				if (text == "strong")
				{
					return Make(new double[3] { 2.0, 1.5, 2.1 }, new double[3] { 0.8, 0.8, 0.8 }, new double[6] { 0.026, 0.03, 0.03, 0.022, 0.036, 0.022 }, 1.1, 0.3);
				}
				return Make(new double[3] { 2.2, 1.6, 2.3 }, new double[3] { 0.85, 0.85, 0.85 }, new double[6] { 0.022, 0.026, 0.026, 0.018, 0.03, 0.018 }, 0.9, 0.3);
			}
			return Make(new double[3] { 2.4, 1.8, 2.5 }, new double[3] { 0.9, 0.9, 0.9 }, new double[6] { 0.018, 0.02, 0.02, 0.014, 0.024, 0.014 }, 0.65, 0.3);
		}

		private static GluteParams Make(double[] freq, double[] zeta, double[] lim, double inertia, double steady)
		{
			GluteParams gluteParams = new GluteParams();
			gluteParams.Freq = freq;
			gluteParams.Zeta = zeta;
			gluteParams.LimPos = new double[3]
			{
				lim[0],
				lim[1],
				lim[2]
			};
			gluteParams.LimNeg = new double[3]
			{
				lim[3],
				lim[4],
				lim[5]
			};
			gluteParams.Inertia = inertia;
			gluteParams.Steady = steady;
			return gluteParams;
		}
	}
	public sealed class GluteSide
	{
		private struct Prev
		{
			public D3 A;

			public D3 P;

			public D3 V;

			public D3 W;

			public M3 M;

			public int N;

			public double Dt;
		}

		public const double Gravity = 9.81;

		public readonly D3 HeadHip;

		public readonly D3 HeadThigh;

		public readonly M3 Basis;

		public readonly double Hand;

		public GluteParams Params;

		public D3 X;

		public D3 V;

		public D3 Lp;

		public D3 Env;

		public D3 Drive;

		public D3 F1;

		public D3 F2;

		private Prev prev;

		private bool hasPrev;

		public GluteSide(D3 headHip, D3 headThigh, M3 hipRot, D3 headWorld, D3 midWorld, GluteParams p)
		{
			HeadHip = headHip;
			HeadThigh = headThigh;
			D3 d = MulMtV(hipRot, new D3(0.0, 1.0, 0.0));
			D3 d2 = MulMtV(hipRot, new D3(0.0, 0.0, -1.0));
			d2 = Norm(d2 - d * Dot(d2, d));
			D3 d3 = Cross(d, d2);
			if (Dot(MulMV(hipRot, d3), headWorld - midWorld) < 0.0)
			{
				d3 *= -1.0;
			}
			Basis = new M3(d3, d, d2);
			Hand = ((!(Dot(Cross(d3, d), d2) < 0.0)) ? 1 : (-1));
			Params = p;
			Reset();
		}

		public void Reset()
		{
			X = default(D3);
			V = default(D3);
			Lp = default(D3);
			Env = default(D3);
			Drive = default(D3);
			F1 = default(D3);
			F2 = default(D3);
			hasPrev = false;
		}

		private D3 Anchor(GluteFrame hip, GluteFrame thigh)
		{
			D3 d = hip.P + MulMV(hip.R, HeadHip) * hip.S;
			double thighDrive = Params.ThighDrive;
			if (thighDrive == 0.0)
			{
				return d;
			}
			D3 d2 = thigh.P + MulMV(thigh.R, HeadThigh) * thigh.S;
			return d + (d2 - d) * thighDrive;
		}

		public void Step(double dt, GluteFrame hip, GluteFrame thigh)
		{
			GluteParams gluteParams = Params;
			double s = hip.S;
			D3 d = Anchor(hip, thigh);
			M3 m = MulMM(hip.R, Basis);
			if (!(dt > 0.0))
			{
				return;
			}
			double num = 0.0;
			if (hasPrev)
			{
				double num2 = 0.0;
				for (int i = 0; i < 3; i++)
				{
					num2 += Dot(prev.M.Col(i), m.Col(i));
				}
				num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0)));
			}
			if (!hasPrev || dt > 0.1 || Len(d - prev.A) > 1.0 || num > Math.PI / 2.0)
			{
				prev = new Prev
				{
					A = d,
					M = m,
					P = hip.P,
					V = default(D3),
					W = default(D3),
					N = 0,
					Dt = dt
				};
				hasPrev = true;
				return;
			}
			D3 d2 = new D3(0.0, 0.0, 0.0);
			for (int j = 0; j < 3; j++)
			{
				d2 += Cross(prev.M.Col(j), m.Col(j));
			}
			double num3 = Len(d2);
			d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : new D3(0.0, 0.0, 0.0));
			D3 d3 = (d - prev.A) * (1.0 / dt);
			double t = ((gluteParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.FilterHz * dt)) : 1.0);
			bool num4 = prev.N == 0;
			D3 d4 = (num4 ? d3 : Lerp(prev.V, d3, t));
			D3 d5 = (num4 ? d2 : Lerp(prev.W, d2, t));
			double num5 = (prev.Dt + dt) / 2.0;
			D3 d6 = (num4 ? new D3(0.0, 0.0, 0.0) : ((d4 - prev.V) * (1.0 / num5)));
			D3 a = (num4 ? new D3(0.0, 0.0, 0.0) : ((d5 - prev.W) * (1.0 / num5)));
			d6 += Cross(a, prev.A - prev.P) * (gluteParams.Twist - 1.0);
			d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (gluteParams.MaxAccel * gluteParams.MaxAccel));
			D3 d7 = MulMtV(prev.M, d6);
			double t2 = ((gluteParams.HpHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.HpHz * dt)) : 0.0);
			Lp = Lerp(Lp, d7, t2);
			D3 d8 = d7 - Lp;
			D3 d9 = new D3(0.0, 0.0, 0.0);
			double num6 = 1.0 - Math.Exp((0.0 - dt) / gluteParams.EnvAttack);
			double num7 = 1.0 - Math.Exp((0.0 - dt) / gluteParams.EnvRelease);
			for (int k = 0; k < 3; k++)
			{
				double num8 = Math.Abs(d8[k]);
				double num9 = gluteParams.EnvK * Env[k];
				double num10 = Math.Max(0.0, num8 - num9);
				double num11 = Math.Min(num8, num9);
				d9[k] = (double)Math.Sign(d8[k]) * (num10 + gluteParams.Steady * num11);
				Env[k] += (num8 - Env[k]) * ((num8 > Env[k]) ? num6 : num7);
			}
			D3 f = F2;
			if (gluteParams.DriveHz > 0.0)
			{
				double t3 = 1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.DriveHz * dt);
				F1 = Lerp(F1, d9, t3);
				F2 = Lerp(F2, F1, t3);
			}
			else
			{
				F1 = d9;
				F2 = d9;
			}
			D3 b = (Drive = F2);
			D3 a2 = MulMtV(prev.M, new D3(0.0, -9.81, 0.0));
			D3 b2 = MulMtV(m, new D3(0.0, -9.81, 0.0));
			int num12 = Math.Max(1, (int)Math.Ceiling(dt / gluteParams.Substep - 1E-09));
			double num13 = dt / (double)num12;
			for (int l = 1; l <= num12; l++)
			{
				D3 d10 = Lerp(a2, b2, (double)l / (double)num12);
				D3 d11 = Lerp(f, b, (double)l / (double)num12);
				D3 a3 = new D3(0.0, 0.0, 0.0);
				for (int n = 0; n < 3; n++)
				{
					double num14 = (0.0 - gluteParams.Inertia) * d11[n] + gluteParams.Gravity * (d10[n] - ((n == 1) ? (-9.81) : 0.0));
					double num15 = Math.PI * 2.0 * gluteParams.Freq[n];
					a3[n] = num14 - num15 * num15 * X[n] - 2.0 * gluteParams.Zeta[n] * num15 * V[n];
				}
				SoftLimit(ref a3, X, V, gluteParams, s);
				for (int num16 = 0; num16 < 3; num16++)
				{
					V[num16] += a3[num16] * num13;
				}
				double num17 = Len(V);
				double num18 = gluteParams.MaxSpeed * s;
				if (num17 > num18)
				{
					V *= num18 / num17;
				}
				for (int num19 = 0; num19 < 3; num19++)
				{
					X[num19] += V[num19] * num13;
				}
				HardLimit(ref X, ref V, gluteParams, s);
			}
			prev = new Prev
			{
				A = d,
				M = m,
				P = hip.P,
				V = d4,
				W = d5,
				N = prev.N + 1,
				Dt = dt
			};
		}

		public void Motion(double hipScale, out Q4 Q, out D3 d)
		{
			GluteParams gluteParams = Params;
			M3 basis = Basis;
			d = MulMV(basis, X) * (gluteParams.Amount / hipScale);
			D3 d2 = (basis.C1 * (0.0 - gluteParams.PivotUp) + basis.C2 * gluteParams.PivotIn) * (1.0 / hipScale);
			double num = Len(d2);
			D3 d3 = d2 * (1.0 / num);
			D3 d4 = d * gluteParams.RotShare;
			d4 -= d3 * Dot(d4, d3);
			double num2 = num * Math.Tan(gluteParams.MaxTurnDeg * Math.PI / 180.0);
			double num3 = Len(d4);
			if (num3 > 1E-12)
			{
				d4 *= num2 * Math.Tanh(num3 / num2) / num3;
			}
			Q = QFromTo(d3, Norm(d2 + d4));
		}

		public void Pose(GluteFrame hip, GluteFrame thigh, out D3 hipPos, out Q4 hipRot, out D3 thighPos, out Q4 thighRot)
		{
			Motion(hip.S, out var Q, out var d);
			hipPos = HeadHip + d;
			hipRot = Q;
			D3 d2 = thigh.P + MulMV(thigh.R, HeadThigh) * thigh.S;
			D3 d3 = MulMtV(hip.R, d2 - hip.P) * (1.0 / hip.S);
			D3 v = QRotate(Q, d3 - HeadHip) + HeadHip + d;
			D3 d4 = hip.P + MulMV(hip.R, v) * hip.S;
			thighPos = MulMtV(thigh.R, d4 - thigh.P) * (1.0 / thigh.S);
			Q4 q = QuatFromMat(hip.R);
			Q4 q2 = QuatFromMat(thigh.R);
			thighRot = QMul(QMul(QConj(q2), QMul(QMul(q, Q), QConj(q))), q2);
		}

		private static void Ellipsoid(D3 x, GluteParams p, double s, out double r, out D3 L)
		{
			double num = 0.0;
			L = new D3(0.0, 0.0, 0.0);
			for (int i = 0; i < 3; i++)
			{
				L[i] = ((x[i] >= 0.0) ? p.LimPos[i] : p.LimNeg[i]) * s;
				num += x[i] / L[i] * (x[i] / L[i]);
			}
			r = Math.Sqrt(num);
		}

		private static void SoftLimit(ref D3 a, D3 x, D3 v, GluteParams p, double s)
		{
			Ellipsoid(x, p, s, out var r, out var L);
			if (!(r <= p.SoftStart))
			{
				double num = (r - p.SoftStart) / (1.0 - p.SoftStart);
				double num2 = Math.PI * 2.0 * p.SoftFreq;
				double num3 = num2 * num2 * num * num;
				D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
				double num4 = Dot(x, b);
				double num5 = 2.0 * p.LimZeta * num2 * num;
				double num6 = Dot(v, b);
				for (int i = 0; i < 3; i++)
				{
					a[i] -= (num3 * num4 + num5 * num6) * b[i];
				}
			}
		}

		private static void HardLimit(ref D3 x, ref D3 v, GluteParams p, double s)
		{
			Ellipsoid(x, p, s, out var r, out var L);
			if (r <= p.HardAt)
			{
				return;
			}
			for (int i = 0; i < 3; i++)
			{
				x[i] *= p.HardAt / r;
			}
			D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
			double num = Dot(v, b);
			if (num > 0.0)
			{
				for (int j = 0; j < 3; j++)
				{
					v[j] -= num * b[j];
				}
			}
		}

		private static double Dot(D3 a, D3 b)
		{
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}

		private static D3 Cross(D3 a, D3 b)
		{
			return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
		}

		private static double Len(D3 a)
		{
			return Math.Sqrt(Dot(a, a));
		}

		private static D3 Norm(D3 a)
		{
			double num = Len(a);
			if (!(num > 1E-12))
			{
				return new D3(0.0, 0.0, 0.0);
			}
			return a * (1.0 / num);
		}

		private static D3 Lerp(D3 a, D3 b, double t)
		{
			return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t);
		}

		private static D3 MulMV(M3 m, D3 v)
		{
			return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z);
		}

		private static D3 MulMtV(M3 m, D3 v)
		{
			return new D3(m.C0.X * v.X + m.C0.Y * v.Y + m.C0.Z * v.Z, m.C1.X * v.X + m.C1.Y * v.Y + m.C1.Z * v.Z, m.C2.X * v.X + m.C2.Y * v.Y + m.C2.Z * v.Z);
		}

		private static M3 MulMM(M3 a, M3 b)
		{
			return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2));
		}

		private static Q4 QMul(Q4 q, Q4 r)
		{
			return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z);
		}

		private static Q4 QConj(Q4 q)
		{
			return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W);
		}

		private static D3 QRotate(Q4 q, D3 v)
		{
			D3 a = new D3(q.X, q.Y, q.Z);
			D3 d = Cross(a, v) * 2.0;
			return v + d * q.W + Cross(a, d);
		}

		private static Q4 QFromTo(D3 a, D3 b)
		{
			D3 d = Cross(a, b);
			double num = 1.0 + Dot(a, b);
			if (num < 1E-08)
			{
				return new Q4(0.0, 0.0, 0.0, 1.0);
			}
			double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num);
			return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2);
		}

		public static Q4 QuatFromMat(M3 m)
		{
			double x = m.C0.X;
			double y = m.C0.Y;
			double z = m.C0.Z;
			double x2 = m.C1.X;
			double y2 = m.C1.Y;
			double z2 = m.C1.Z;
			double x3 = m.C2.X;
			double y3 = m.C2.Y;
			double z3 = m.C2.Z;
			double num = x + y2 + z3;
			Q4 q;
			if (num > 0.0)
			{
				double num2 = Math.Sqrt(num + 1.0) * 2.0;
				q = new Q4((z2 - y3) / num2, (x3 - z) / num2, (y - x2) / num2, 0.25 * num2);
			}
			else if (x > y2 && x > z3)
			{
				double num3 = Math.Sqrt(1.0 + x - y2 - z3) * 2.0;
				q = new Q4(0.25 * num3, (x2 + y) / num3, (x3 + z) / num3, (z2 - y3) / num3);
			}
			else if (y2 > z3)
			{
				double num4 = Math.Sqrt(1.0 + y2 - x - z3) * 2.0;
				q = new Q4((x2 + y) / num4, 0.25 * num4, (y3 + z2) / num4, (x3 - z) / num4);
			}
			else
			{
				double num5 = Math.Sqrt(1.0 + z3 - x - y2) * 2.0;
				q = new Q4((x3 + z) / num5, (y3 + z2) / num5, 0.25 * num5, (y - x2) / num5);
			}
			double num6 = Math.Sqrt(q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W);
			return new Q4(q.X / num6, q.Y / num6, q.Z / num6, q.W / num6);
		}
	}
	[BepInPlugin("local.valheim.nordicglutephysics", "Nordic Glute Physics (prototype)", "0.3.0")]
	[BepInDependency("com.rawrtastic.plugins.enhancedvalheimvrm", "1.0.0")]
	public sealed class GlutePhysicsPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<string> Preset;

		internal static ConfigEntry<float> Amount;

		private static FieldInfo cloneField;

		internal static readonly List<GluteJiggleDriver> Drivers = new List<GluteJiggleDriver>();

		internal static int ServiceFrame = -1;

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("GlutePhysics", "Enabled", true, "Glute physics for avatars that have the Glute_* bones (applies to avatars loaded after the change).");
			Preset = ((BaseUnityPlugin)this).Config.Bind<string>("GlutePhysics", "Preset", "visible", new ConfigDescription("How much the glutes show their motion: light, visible or strong (all toned). Read when an avatar is loaded: restart the game (or reload the avatar) after changing it.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GlutePresets.Names), Array.Empty<object>()));
			Amount = ((BaseUnityPlugin)this).Config.Bind<float>("GlutePhysics", "Amount", 1f, new ConfigDescription("Strength of the shown motion: 0 = still, 1 = the preset; above 1 goes past the preset's travel limits (tested with clothing at 1 only). Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.5f), Array.Empty<object>()));
			Type type = ((object)Chainloader.PluginInfos["com.rawrtastic.plugins.enhancedvalheimvrm"].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 || cloneField == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Inactive: EnhancedValheimVRM clone staging hook not found.");
				return;
			}
			Harmony val = new Harmony("local.valheim.nordicglutephysics");
			val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GlutePhysicsPlugin), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(FastSpringBoneService), "LateUpdate", Type.EmptyTypes, (Type[])null);
			try
			{
				if (methodInfo2 == null)
				{
					throw new MissingMethodException("FastSpringBoneService.LateUpdate");
				}
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(GlutePhysicsPlugin), "AfterSprings", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Glute physics: spring service hook unavailable, avatars step in their own LateUpdate: " + ex.Message));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Glute physics prototype enabled (preset {Preset.Value}, Amount {Amount.Value:0.##}).");
		}

		private static void AfterSprings()
		{
			ServiceFrame = Time.frameCount;
			for (int num = Drivers.Count - 1; num >= 0; num--)
			{
				GluteJiggleDriver gluteJiggleDriver = Drivers[num];
				if ((Object)(object)gluteJiggleDriver == (Object)null)
				{
					Drivers.RemoveAt(num);
				}
				else
				{
					try
					{
						gluteJiggleDriver.Tick();
					}
					catch (Exception ex)
					{
						Log.LogError((object)("Glute physics step failed, driver stopped: " + ex));
						((Behaviour)gluteJiggleDriver).enabled = false;
					}
				}
			}
		}

		private static void BeforeCreate(object __instance)
		{
			if (!Enabled.Value)
			{
				return;
			}
			try
			{
				object? value = cloneField.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<GluteJiggleDriver>() != (Object)null)
				{
					return;
				}
				Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>();
				Transform[] componentsInChildren = val.GetComponentsInChildren<Transform>(true);
				foreach (Transform val2 in componentsInChildren)
				{
					if (((Object)val2).name.StartsWith("Glute_", StringComparison.Ordinal) || ((Object)val2).name == "CC_Base_Hip")
					{
						dictionary[((Object)val2).name] = val2;
					}
				}
				if (dictionary.ContainsKey("Glute_L") || dictionary.ContainsKey("Glute_R"))
				{
					GluteParams gluteParams = GlutePresets.For(Preset.Value);
					GluteJiggleDriver gluteJiggleDriver = val.AddComponent<GluteJiggleDriver>();
					if (!gluteJiggleDriver.Setup(dictionary, gluteParams, ((Object)val).name))
					{
						Object.Destroy((Object)(object)gluteJiggleDriver);
						Log.LogWarning((object)("Glute physics not attached to " + ((Object)val).name + ": glute bones not as expected."));
						return;
					}
					Log.LogInfo((object)("Glute physics attached to " + ((Object)val).name + ": preset " + Preset.Value + ", freq out/up/back " + FormattableString.Invariant($"{gluteParams.Freq[0]:0.##}/{gluteParams.Freq[1]:0.##}/{gluteParams.Freq[2]:0.##} Hz, damping {gluteParams.Zeta[1]:0.##}, inertia {gluteParams.Inertia:0.##}, steady-run share {gluteParams.Steady:0.##}.")));
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Glute physics setup failed: " + ex));
			}
		}
	}
	[DefaultExecutionOrder(11030)]
	public sealed class GluteJiggleDriver : MonoBehaviour
	{
		private sealed class Side
		{
			public Transform HipBone;

			public Transform ThighBone;

			public Transform Hip;

			public Transform Thigh;

			public Vector3 RestHip;

			public Vector3 RestThigh;

			public GluteSide Solver;
		}

		private readonly List<Side> sides = new List<Side>();

		private GluteParams preset;

		private int steppedFrame = -1;

		private bool loggedStep;

		private string who;

		internal bool Setup(Dictionary<string, Transform> bones, GluteParams p, string label)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			who = label;
			preset = p;
			RuntimeGltfInstance component = ((Component)this).GetComponent<RuntimeGltfInstance>();
			IReadOnlyDictionary<Transform, TransformState> states = ((component != null) ? component.InitialTransformStates : null);
			if (!bones.TryGetValue("CC_Base_Hip", out var value))
			{
				return false;
			}
			string[] array = new string[2] { "L", "R" };
			foreach (string text in array)
			{
				if (bones.TryGetValue("Glute_" + text, out var value2) && bones.TryGetValue("Glute_" + text + "_Thigh", out var value3))
				{
					if ((Object)(object)value2.parent != (Object)(object)value || (Object)(object)value3.parent == (Object)null || ((Object)value3.parent).name != "CC_Base_" + text + "_ThighTwist01" || value2.childCount != 0 || value3.childCount != 0)
					{
						return false;
					}
					if (Quaternion.Angle(Local(value2, states, out var pos), Quaternion.identity) > 0.01f || Quaternion.Angle(Local(value3, states, out pos), Quaternion.identity) > 0.01f)
					{
						return false;
					}
					RestTRS(value, states, out var pos2, out var rot);
					RestTRS(value2, states, out var pos3, out var _);
					Local(value2, states, out var pos4);
					Local(value3, states, out var pos5);
					GluteSide solver = new GluteSide(D(pos4), D(pos5), M(rot), D(pos3), D(pos2), p.Clone());
					sides.Add(new Side
					{
						HipBone = value2,
						ThighBone = value3,
						Hip = value,
						Thigh = value3.parent,
						RestHip = pos4,
						RestThigh = pos5,
						Solver = solver
					});
				}
			}
			return sides.Count > 0;
		}

		private void OnEnable()
		{
			foreach (Side side in sides)
			{
				side.Solver.Reset();
			}
			if (!GlutePhysicsPlugin.Drivers.Contains(this))
			{
				GlutePhysicsPlugin.Drivers.Add(this);
			}
		}

		private void OnDisable()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			GlutePhysicsPlugin.Drivers.Remove(this);
			foreach (Side side in sides)
			{
				if ((Object)(object)side.HipBone != (Object)null)
				{
					side.HipBone.localPosition = side.RestHip;
					side.HipBone.localRotation = Quaternion.identity;
				}
				if ((Object)(object)side.ThighBone != (Object)null)
				{
					side.ThighBone.localPosition = side.RestThigh;
					side.ThighBone.localRotation = Quaternion.identity;
				}
			}
		}

		private void LateUpdate()
		{
			if (GlutePhysicsPlugin.ServiceFrame >= Time.frameCount - 1)
			{
				return;
			}
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				GlutePhysicsPlugin.Log.LogError((object)("Glute physics step failed, driver stopped: " + ex));
				((Behaviour)this).enabled = false;
			}
		}

		internal void Tick()
		{
			//IL_01d0: 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_024e: 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)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			if (steppedFrame == Time.frameCount || !((Behaviour)this).isActiveAndEnabled)
			{
				return;
			}
			steppedFrame = Time.frameCount;
			float deltaTime = Time.deltaTime;
			if (!loggedStep)
			{
				loggedStep = true;
				GlutePhysicsPlugin.Log.LogInfo((object)($"Glute physics {who}: first step at frame {Time.frameCount}, dt {deltaTime:0.0000}, " + ((GlutePhysicsPlugin.ServiceFrame == Time.frameCount) ? "right after the spring service." : "in its own LateUpdate.")));
			}
			double amount = GlutePhysicsPlugin.Amount?.Value ?? 1f;
			Vector3 val = default(Vector3);
			Vector3 val2 = default(Vector3);
			Quaternion val3 = default(Quaternion);
			Quaternion val4 = default(Quaternion);
			foreach (Side side in sides)
			{
				if (!((Object)(object)side.HipBone == (Object)null) && !((Object)(object)side.ThighBone == (Object)null) && !((Object)(object)side.Hip == (Object)null) && !((Object)(object)side.Thigh == (Object)null))
				{
					side.Solver.Params.Amount = amount;
					GluteFrame hip = Frame(side.Hip);
					GluteFrame thigh = Frame(side.Thigh);
					side.Solver.Step(deltaTime, hip, thigh);
					side.Solver.Pose(hip, thigh, out var hipPos, out var hipRot, out var thighPos, out var thighRot);
					((Vector3)(ref val))..ctor((float)hipPos.X, (float)hipPos.Y, (float)hipPos.Z);
					((Vector3)(ref val2))..ctor((float)thighPos.X, (float)thighPos.Y, (float)thighPos.Z);
					((Quaternion)(ref val3))..ctor((float)hipRot.X, (float)hipRot.Y, (float)hipRot.Z, (float)hipRot.W);
					((Quaternion)(ref val4))..ctor((float)thighRot.X, (float)thighRot.Y, (float)thighRot.Z, (float)thighRot.W);
					if (!Finite(val) || !Finite(val2) || !Finite(new Vector3(val3.x + val4.x, val3.y + val4.y, val3.z + val4.z + val3.w + val4.w)))
					{
						side.Solver.Reset();
						side.HipBone.localPosition = side.RestHip;
						side.ThighBone.localPosition = side.RestThigh;
						side.HipBone.localRotation = Quaternion.identity;
						side.ThighBone.localRotation = Quaternion.identity;
					}
					else
					{
						side.HipBone.localPosition = val;
						side.ThighBone.localPosition = val2;
						side.HipBone.localRotation = val3;
						side.ThighBone.localRotation = val4;
					}
				}
			}
		}

		private static GluteFrame Frame(Transform t)
		{
			//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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			Vector3 lossyScale = t.lossyScale;
			return new GluteFrame(D(t.position), M(t.rotation), (double)(Mathf.Abs(lossyScale.x) + Mathf.Abs(lossyScale.y) + Mathf.Abs(lossyScale.z)) / 3.0);
		}

		private static Quaternion Local(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos)
		{
			//IL_0025: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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)
			if (states != null && states.TryGetValue(t, out var value))
			{
				pos = ((TransformState)(ref value)).LocalPosition;
				return ((TransformState)(ref value)).LocalRotation;
			}
			pos = t.localPosition;
			return t.localRotation;
		}

		private void RestTRS(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos, out Quaternion rot)
		{
			//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_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_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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			pos = Vector3.zero;
			rot = Quaternion.identity;
			List<Transform> list = new List<Transform>();
			Transform val = t;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)((Component)this).transform)
			{
				list.Add(val);
				val = val.parent;
			}
			for (int num = list.Count - 1; num >= 0; num--)
			{
				Vector3 pos2;
				Quaternion val2 = Local(list[num], states, out pos2);
				pos += rot * pos2;
				rot *= val2;
			}
		}

		private static D3 D(Vector3 v)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			return new D3(v.x, v.y, v.z);
		}

		private static M3 M(Quaternion q)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			return new M3(D(q * Vector3.right), D(q * Vector3.up), D(q * Vector3.forward));
		}

		private static bool Finite(Vector3 v)
		{
			//IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(v.x + v.y + v.z))
			{
				return !float.IsInfinity(v.x + v.y + v.z);
			}
			return false;
		}
	}
}
namespace LocalValheimFixes
{
	public struct D3
	{
		public double X;

		public double Y;

		public double Z;

		public double this[int k]
		{
			get
			{
				return k switch
				{
					1 => Y, 
					0 => X, 
					_ => Z, 
				};
			}
			set
			{
				switch (k)
				{
				case 0:
					X = value;
					break;
				case 1:
					Y = value;
					break;
				default:
					Z = value;
					break;
				}
			}
		}

		public D3(double x, double y, double z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public static D3 operator +(D3 a, D3 b)
		{
			return new D3(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}

		public static D3 operator -(D3 a, D3 b)
		{
			return new D3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		public static D3 operator *(D3 a, double s)
		{
			return new D3(a.X * s, a.Y * s, a.Z * s);
		}
	}
	public struct M3
	{
		public D3 C0;

		public D3 C1;

		public D3 C2;

		public M3(D3 c0, D3 c1, D3 c2)
		{
			C0 = c0;
			C1 = c1;
			C2 = c2;
		}

		public D3 Col(int k)
		{
			return k switch
			{
				1 => C1, 
				0 => C0, 
				_ => C2, 
			};
		}
	}
	public struct Q4
	{
		public double X;

		public double Y;

		public double Z;

		public double W;

		public Q4(double x, double y, double z, double w)
		{
			X = x;
			Y = y;
			Z = z;
			W = w;
		}
	}
	public sealed class JiggleStage
	{
		public bool On = true;

		public double[] Freq;

		public double[] Zeta;

		public double[] LimPos;

		public double[] LimNeg;

		public JiggleStage Clone()
		{
			return new JiggleStage
			{
				On = On,
				Freq = (double[])Freq.Clone(),
				Zeta = (double[])Zeta.Clone(),
				LimPos = (double[])LimPos.Clone(),
				LimNeg = (double[])LimNeg.Clone()
			};
		}
	}
	public sealed class JiggleSupport
	{
		public bool On = true;

		public double FreqMul = 1.25;

		public double ZetaMul = 1.35;

		public double LimMul = 0.6;

		public double SideLimMul = 0.48;

		public double InertiaMul = 0.6;

		public double SwingMul = 0.5;

		public JiggleSupport Clone()
		{
			return (JiggleSupport)MemberwiseClone();
		}
	}
	public sealed class JiggleSwing
	{
		public bool On = true;

		public double Amount = 1.0;

		public double RateLo = 300.0;

		public double RateHi = 800.0;

		public double Attack = 0.06;

		public double Release = 0.45;

		public double FreqMul = 0.6;

		public double ZetaMul = 1.2;

		public double LimMul = 1.7;

		public JiggleSwing Clone()
		{
			return (JiggleSwing)MemberwiseClone();
		}
	}
	public sealed class JiggleParams
	{
		public JiggleStage Base = new JiggleStage
		{
			Freq = new double[3] { 2.5, 3.2, 5.5 },
			Zeta = new double[3] { 0.24, 0.22, 0.4 },
			LimPos = new double[3] { 0.025, 0.025, 0.012 },
			LimNeg = new double[3] { 0.025, 0.03, 0.008 }
		};

		public JiggleStage Tip = new JiggleStage
		{
			On = false,
			Freq = new double[3] { 5.0, 4.5, 6.0 },
			Zeta = new double[3] { 0.2, 0.18, 0.3 },
			LimPos = new double[3] { 0.008, 0.01, 0.005 },
			LimNeg = new double[3] { 0.008, 0.01, 0.004 }
		};

		public double Inertia = 0.45;

		public double Gravity = 0.7;

		public double Amount = 1.0;

		public double RotShare = 0.75;

		public double MaxTurnDeg = 32.0;

		public double Squash;

		public double Radius = 0.045;

		public double MarkerFrac = 0.66;

		public double SkinWeight = 0.85;

		public double FilterHz = 12.0;

		public double MaxAccel = 40.0;

		public double MaxSpeed = 1.5;

		public double Substep = 1.0 / 240.0;

		public double SoftStart = 0.8;

		public double SoftFreq = 8.0;

		public double HardAt = 1.25;

		public double Bounce;

		public JiggleSupport Support = new JiggleSupport();

		public JiggleSwing Swing = new JiggleSwing();

		public const double SupportBlendSeconds = 0.3;

		public JiggleParams Clone()
		{
			JiggleParams obj = (JiggleParams)MemberwiseClone();
			obj.Base = Base.Clone();
			obj.Tip = Tip.Clone();
			obj.Support = Support.Clone();
			obj.Swing = Swing.Clone();
			return obj;
		}

		public JiggleParams Supported()
		{
			if (!Support.On)
			{
				return this;
			}
			JiggleParams jiggleParams = Clone();
			jiggleParams.Inertia = Inertia * Support.InertiaMul;
			jiggleParams.Swing.Amount = Swing.Amount * Support.SwingMul;
			JiggleStage[] array = new JiggleStage[2] { jiggleParams.Base, jiggleParams.Tip };
			foreach (JiggleStage jiggleStage in array)
			{
				for (int j = 0; j < 3; j++)
				{
					double num = ((j == 0) ? Support.SideLimMul : Support.LimMul);
					jiggleStage.Freq[j] *= Support.FreqMul;
					jiggleStage.Zeta[j] = Math.Min(1.0, jiggleStage.Zeta[j] * Support.ZetaMul);
					jiggleStage.LimPos[j] *= num;
					jiggleStage.LimNeg[j] *= num;
				}
			}
			return jiggleParams;
		}

		public static void Blend(JiggleParams bare, JiggleParams clothed, double t, JiggleParams into)
		{
			JiggleParams jiggleParams = ((t <= 0.0) ? bare : ((t >= 1.0) ? clothed : null));
			into.Inertia = jiggleParams?.Inertia ?? (bare.Inertia + (clothed.Inertia - bare.Inertia) * t);
			into.Swing.Amount = jiggleParams?.Swing.Amount ?? (bare.Swing.Amount + (clothed.Swing.Amount - bare.Swing.Amount) * t);
			BlendStage(bare.Base, clothed.Base, t, jiggleParams?.Base, into.Base);
			BlendStage(bare.Tip, clothed.Tip, t, jiggleParams?.Tip, into.Tip);
		}

		private static void BlendStage(JiggleStage a, JiggleStage b, double t, JiggleStage src, JiggleStage into)
		{
			for (int i = 0; i < 3; i++)
			{
				into.Freq[i] = ((src != null) ? src.Freq[i] : (a.Freq[i] + (b.Freq[i] - a.Freq[i]) * t));
				into.Zeta[i] = ((src != null) ? src.Zeta[i] : (a.Zeta[i] + (b.Zeta[i] - a.Zeta[i]) * t));
				into.LimPos[i] = ((src != null) ? src.LimPos[i] : (a.LimPos[i] + (b.LimPos[i] - a.LimPos[i]) * t));
				into.LimNeg[i] = ((src != null) ? src.LimNeg[i] : (a.LimNeg[i] + (b.LimNeg[i] - a.LimNeg[i]) * t));
			}
		}
	}
	public sealed class JiggleRest
	{
		public D3 Head;

		public D3 Tail;

		public Q4 Q0;

		public M3 Basis;

		public double Hand = 1.0;
	}
	public struct JigglePose
	{
		public D3 Position;

		public D3 Scale;

		public Q4 Quaternion;
	}
	public sealed class BreastJiggle
	{
		public const double GRAVITY = 9.81;

		public readonly JiggleRest Rest;

		public JiggleParams Params;

		public D3 X;

		public D3 V;

		public D3 Xt;

		public D3 Vt;

		public D3 Accel;

		public double Swing;

		public double TwistRate;

		private readonly JiggleStage swingStage = new JiggleStage
		{
			Freq = new double[3],
			Zeta = new double[3],
			LimPos = new double[3],
			LimNeg = new double[3]
		};

		private bool hasPrev;

		private D3 prevA;

		private D3 prevV;

		private D3 prevW;

		private M3 prevM;

		private int prevN;

		private double prevDt;

		public BreastJiggle(JiggleRest rest, JiggleParams p)
		{
			Rest = rest;
			Params = p;
			Reset();
		}

		public void Reset()
		{
			X = default(D3);
			V = default(D3);
			Xt = default(D3);
			Vt = default(D3);
			hasPrev = false;
			Accel = default(D3);
			Swing = 0.0;
			TwistRate = 0.0;
		}

		public static JiggleRest MakeRest(D3 head, Q4 q0, D3 tailLocal, M3 parentRot, D3 headWorld, D3 midWorld)
		{
			D3 d = head + QRotate(q0, tailLocal);
			D3 d2 = MulMtV(parentRot, new D3(0.0, 1.0, 0.0));
			D3 d3 = d - head;
			d3 = Norm(d3 - d2 * Dot(d3, d2));
			D3 d4 = Cross(d2, d3);
			if (Dot(MulMV(parentRot, d4), headWorld - midWorld) < 0.0)
			{
				d4 *= -1.0;
			}
			double hand = ((!(Dot(Cross(d4, d2), d3) < 0.0)) ? 1 : (-1));
			return new JiggleRest
			{
				Head = head,
				Q0 = q0,
				Tail = d,
				Basis = new M3(d4, d2, d3),
				Hand = hand
			};
		}

		private D3 MarkerLocal()
		{
			return Rest.Head + (Rest.Tail - Rest.Head) * Math.Max(0.05, Params.MarkerFrac);
		}

		public void Step(double dt, D3 parentPos, M3 parentRot, double parentScale = 1.0)
		{
			JiggleParams jiggleParams = Params;
			JiggleRest rest = Rest;
			D3 d = parentPos + MulMV(parentRot, MarkerLocal()) * parentScale;
			M3 m = MulMM(parentRot, rest.Basis);
			if (!(dt > 0.0))
			{
				return;
			}
			double num = 0.0;
			if (hasPrev)
			{
				double num2 = 0.0;
				for (int i = 0; i < 3; i++)
				{
					num2 += Dot(prevM.Col(i), m.Col(i));
				}
				num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0)));
			}
			if (!hasPrev || dt > 0.1 || Len(d - prevA) > 1.0 || num > Math.PI / 2.0)
			{
				hasPrev = true;
				prevA = d;
				prevM = m;
				prevV = default(D3);
				prevW = default(D3);
				prevN = 0;
				prevDt = dt;
				return;
			}
			D3 d2 = default(D3);
			for (int j = 0; j < 3; j++)
			{
				d2 += Cross(prevM.Col(j), m.Col(j));
			}
			double num3 = Len(d2);
			d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : default(D3));
			D3 d3 = (d - prevA) * (1.0 / dt);
			double t = ((jiggleParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * jiggleParams.FilterHz * dt)) : 1.0);
			bool num4 = prevN == 0;
			D3 d4 = (num4 ? d3 : Lerp(prevV, d3, t));
			D3 d5 = (num4 ? d2 : Lerp(prevW, d2, t));
			double num5 = (prevDt + dt) / 2.0;
			D3 d6 = (num4 ? default(D3) : ((d4 - prevV) * (1.0 / num5)));
			D3 v = (num4 ? default(D3) : ((d5 - prevW) * (1.0 / num5)));
			d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (jiggleParams.MaxAccel * jiggleParams.MaxAccel));
			D3 accel = MulMtV(prevM, d6);
			D3 a = MulMtV(m, d5) * rest.Hand;
			D3 a2 = MulMtV(m, v) * rest.Hand;
			D3 a3 = MulMtV(prevM, new D3(0.0, -9.81, 0.0));
			D3 b = MulMtV(m, new D3(0.0, -9.81, 0.0));
			Accel = accel;
			JiggleStage jiggleStage = jiggleParams.Base;
			JiggleSwing swing = jiggleParams.Swing;
			TwistRate = a.Y;
			if (swing != null && swing.On)
			{
				double num6 = Math.Min(1.0, Math.Max(0.0, (Math.Abs(a.Y) * 180.0 / Math.PI - swing.RateLo) / (swing.RateHi - swing.RateLo)));
				double num7 = num6 * num6 * (3.0 - 2.0 * num6);
				Swing += (num7 - Swing) * (1.0 - Math.Exp((0.0 - dt) / ((num7 > Swing) ? swing.Attack : swing.Release)));
				double num8 = Swing * swing.Amount;
				if (num8 > 0.0)
				{
					JiggleStage jiggleStage2 = jiggleParams.Base;
					jiggleStage = swingStage;
					double num9 = 1.0 + (swing.FreqMul - 1.0) * num8;
					double num10 = 1.0 + (swing.ZetaMul - 1.0) * num8;
					double num11 = 1.0 + (swing.LimMul - 1.0) * num8;
					for (int k = 0; k < 3; k++)
					{
						jiggleStage.Freq[k] = jiggleStage2.Freq[k];
						jiggleStage.Zeta[k] = jiggleStage2.Zeta[k];
						jiggleStage.LimPos[k] = jiggleStage2.LimPos[k];
						jiggleStage.LimNeg[k] = jiggleStage2.LimNeg[k];
					}
					jiggleStage.Freq[0] = jiggleStage2.Freq[0] * num9;
					jiggleStage.Zeta[0] = Math.Min(1.0, jiggleStage2.Zeta[0] * num10);
					jiggleStage.LimPos[0] = jiggleStage2.LimPos[0] * num11;
					jiggleStage.LimNeg[0] = jiggleStage2.LimNeg[0] * num11;
				}
			}
			int num12 = Math.Max(1, (int)Math.Ceiling(dt / jiggleParams.Substep - 1E-09));
			double num13 = dt / (double)num12;
			for (int l = 1; l <= num12; l++)
			{
				D3 d7 = Lerp(a3, b, (double)l / (double)num12);
				D3 d8 = Cross(a, V);
				D3 d9 = Cross(a2, X);
				D3 d10 = Cross(a, Cross(a, X));
				D3 d11 = default(D3);
				D3 a4 = default(D3);
				for (int n = 0; n < 3; n++)
				{
					d11[n] = jiggleParams.Inertia * (0.0 - accel[n] - 2.0 * d8[n] - d9[n] - d10[n]) + jiggleParams.Gravity * (d7[n] - ((n == 1) ? (-9.81) : 0.0));
					double num14 = Math.PI * 2.0 * jiggleStage.Freq[n];
					a4[n] = d11[n] - num14 * num14 * X[n] - 2.0 * jiggleStage.Zeta[n] * num14 * V[n];
				}
				SoftLimit(ref a4, X, jiggleStage, parentScale, jiggleParams);
				for (int num15 = 0; num15 < 3; num15++)
				{
					V[num15] += a4[num15] * num13;
				}
				double num16 = Len(V);
				double num17 = jiggleParams.MaxSpeed * parentScale;
				if (num16 > num17)
				{
					V *= num17 / num16;
				}
				for (int num18 = 0; num18 < 3; num18++)
				{
					X[num18] += V[num18] * num13;
				}
				HardLimit(ref X, ref V, jiggleStage, parentScale, jiggleParams.HardAt, jiggleParams.Bounce);
				if (jiggleParams.Tip.On)
				{
					for (int num19 = 0; num19 < 3; num19++)
					{
						double num20 = Math.PI * 2.0 * jiggleParams.Tip.Freq[num19];
						double num21 = 0.0 - (a4[num19] - d11[num19]) - num20 * num20 * Xt[num19] - 2.0 * jiggleParams.Tip.Zeta[num19] * num20 * Vt[num19];
						Vt[num19] += num21 * num13;
						Xt[num19] += Vt[num19] * num13;
					}
					HardLimit(ref Xt, ref Vt, jiggleParams.Tip, parentScale, jiggleParams.HardAt, jiggleParams.Bounce);
				}
			}
			if (!jiggleParams.Tip.On)
			{
				Xt = default(D3);
				Vt = default(D3);
			}
			prevA = d;
			prevM = m;
			prevV = d4;
			prevW = d5;
			prevN++;
			prevDt = dt;
		}

		public D3 Displacement()
		{
			return X + Xt;
		}

		public JigglePose Pose(double parentScale = 1.0)
		{
			JiggleParams jiggleParams = Params;
			JiggleRest rest = Rest;
			M3 basis = rest.Basis;
			double num = jiggleParams.Amount / (parentScale * jiggleParams.SkinWeight);
			D3 d = MulMV(basis, X) * num;
			D3 d2 = MulMV(basis, Xt) * num;
			D3 d3 = d + d2;
			D3 d4 = MarkerLocal() - rest.Head;
			double num2 = Len(d4);
			D3 d5 = d4 * (1.0 / num2);
			D3 d6 = d * jiggleParams.RotShare + d2;
			d6 -= d5 * Dot(d6, d5);
			double num3 = num2 * Math.Tan(jiggleParams.MaxTurnDeg * Math.PI / 180.0);
			double num4 = Len(d6);
			if (num4 > num3)
			{
				d6 *= num3 / num4;
			}
			Q4 q = QFromTo(d5, Norm(d4 + d6));
			double num5 = Math.Max(-0.1, Math.Min(0.1, jiggleParams.Amount * jiggleParams.Squash * X.Y / (jiggleParams.Radius * parentScale)));
			double num6 = 1.0 - num5;
			double num7 = 1.0 / Math.Sqrt(num6);
			D3 d7 = QRotate(QConj(rest.Q0), basis.C1);
			D3 scale = ((Math.Abs(d7.X) >= Math.Abs(d7.Z)) ? new D3(num6, num7, num7) : new D3(num7, num7, num6));
			D3 d8 = QRotate(QConj(rest.Q0), d4);
			D3 d9 = QRotate(QMul(q, rest.Q0), new D3(d8.X * scale.X, d8.Y * scale.Y, d8.Z * scale.Z));
			D3 position = rest.Head + (d3 - (d9 - d4));
			return new JigglePose
			{
				Position = position,
				Quaternion = QMul(q, rest.Q0),
				Scale = scale
			};
		}

		private static double Ellipsoid(D3 x, JiggleStage lim, double s, out D3 L)
		{
			double num = 0.0;
			L = default(D3);
			for (int i = 0; i < 3; i++)
			{
				L[i] = ((x[i] >= 0.0) ? lim.LimPos[i] : lim.LimNeg[i]) * s;
				num += x[i] / L[i] * (x[i] / L[i]);
			}
			return Math.Sqrt(num);
		}

		private static void SoftLimit(ref D3 a, D3 x, JiggleStage lim, double s, JiggleParams p)
		{
			D3 L;
			double num = Ellipsoid(x, lim, s, out L);
			if (!(num <= p.SoftStart))
			{
				double num2 = (num - p.SoftStart) / (1.0 - p.SoftStart);
				double num3 = Math.PI * 2.0 * p.SoftFreq;
				double num4 = num3 * num3 * num2 * num2;
				D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
				double num5 = Dot(x, b);
				for (int i = 0; i < 3; i++)
				{
					a[i] -= num4 * num5 * b[i];
				}
			}
		}

		private static bool HardLimit(ref D3 x, ref D3 v, JiggleStage lim, double s, double hardAt, double bounce)
		{
			D3 L;
			double num = Ellipsoid(x, lim, s, out L);
			if (num <= hardAt)
			{
				return false;
			}
			for (int i = 0; i < 3; i++)
			{
				x[i] *= hardAt / num;
			}
			D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
			double num2 = Dot(v, b);
			if (num2 > 0.0)
			{
				for (int j = 0; j < 3; j++)
				{
					v[j] -= (1.0 + bounce) * num2 * b[j];
				}
			}
			return true;
		}

		private static double Dot(D3 a, D3 b)
		{
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}

		private static D3 Cross(D3 a, D3 b)
		{
			return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
		}

		private static double Len(D3 a)
		{
			return Math.Sqrt(Dot(a, a));
		}

		private static D3 Lerp(D3 a, D3 b, double t)
		{
			return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t);
		}

		private static D3 Norm(D3 a)
		{
			double num = Len(a);
			if (!(num > 1E-12))
			{
				return default(D3);
			}
			return a * (1.0 / num);
		}

		private static D3 MulMV(M3 m, D3 v)
		{
			return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z);
		}

		private static D3 MulMtV(M3 m, D3 v)
		{
			return new D3(Dot(m.C0, v), Dot(m.C1, v), Dot(m.C2, v));
		}

		private static M3 MulMM(M3 a, M3 b)
		{
			return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2));
		}

		private static Q4 QMul(Q4 q, Q4 r)
		{
			return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z);
		}

		private static Q4 QConj(Q4 q)
		{
			return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W);
		}

		private static Q4 QFromTo(D3 a, D3 b)
		{
			D3 d = Cross(a, b);
			double num = 1.0 + Dot(a, b);
			if (num < 1E-08)
			{
				return new Q4(0.0, 0.0, 0.0, 1.0);
			}
			double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num);
			return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2);
		}

		public static D3 QRotate(Q4 q, D3 v)
		{
			D3 a = new D3(q.X, q.Y, q.Z);
			D3 d = Cross(a, v) * 2.0;
			return v + d * q.W + Cross(a, d);
		}
	}
}

plugins/VRMFixes.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
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 System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LocalValheimFixes.Packages;
using Microsoft.CodeAnalysis;
using UniGLTF;
using UniGLTF.Utils;
using UniVRM10;
using UniVRM10.FastSpringBones.System;
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.5.0")]
[assembly: AssemblyInformationalVersion("1.1.5+3356aed1f6508684669959e6a326f48d5ef50221")]
[assembly: AssemblyProduct("VRMFixes")]
[assembly: AssemblyTitle("VRMFixes")]
[assembly: AssemblyVersion("1.1.5.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.5")]
	[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));
			}
			try
			{
				Wardrobe.Install(type.Assembly, ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			}
			catch (Exception ex4)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Avatar wardrobe inactive: " + ex4));
			}
			try
			{
				BreastPhysics.Install(type.Assembly, ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			}
			catch (Exception ex5)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Breast physics inactive: " + ex5));
			}
		}

		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));
			}
			try
			{
				object? value2 = CloneField.GetValue(__instance);
				BreastPhysics.Attach(__instance, (GameObject)((value2 is GameObject) ? value2 : null));
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Breast physics setup failed, VRM springs kept: " + ex2));
			}
		}
	}
	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;
		}
	}
	internal static class Wardrobe
	{
		internal sealed class Garment
		{
			internal GameObject Node;

			internal Renderer Renderer;

			internal string Item;

			internal int Level;

			internal readonly List<KeyValuePair<SkinnedMeshRenderer, int>> Masks = new List<KeyValuePair<SkinnedMeshRenderer, int>>();
		}

		private sealed class State
		{
			internal Player Player;

			internal bool NoPlayer;

			internal WardrobeView View;

			internal int NextLookup;

			internal int Helmet;

			internal int Chest;

			internal int Leg;

			internal int HelmetQuality;

			internal int ChestQuality;

			internal int LegQuality;

			internal bool Applied;
		}

		private const string Prefix = "Garment_";

		private const string MaskPrefix = "Mask_";

		private const string UnderwearPrefix = "Underwear_";

		private const string GivenKey = "NordicTestGarmentsGivenItems";

		private const int RetryFrames = 30;

		private static readonly int HelmetQualityKey = StringExtensionMethods.GetStableHashCode("NordicHelmetQuality");

		private static readonly int ChestQualityKey = StringExtensionMethods.GetStableHashCode("NordicChestQuality");

		private static readonly int LegQualityKey = StringExtensionMethods.GetStableHashCode("NordicLegQuality");

		private static readonly ConditionalWeakTable<VisEquipment, State> States = new ConditionalWeakTable<VisEquipment, State>();

		private static ManualLogSource Log;

		private static ConfigEntry<bool> GiveTestGarments;

		private static MethodInfo FindInstance;

		private static MethodInfo GetGameObject;

		private static FieldInfo Clone;

		private static FieldInfo Owner;

		private static FieldRef<VisEquipment, int> HelmetHash;

		private static FieldRef<VisEquipment, int> ChestHash;

		private static FieldRef<VisEquipment, int> LegHash;

		private static FieldRef<VisEquipment, ZNetView> NView;

		private static FieldRef<Humanoid, ItemData> HelmetItem;

		private static FieldRef<Humanoid, ItemData> ChestItem;

		private static FieldRef<Humanoid, ItemData> LegItem;

		private static bool Failed;

		private static bool WarnedFull;

		internal static void Install(Assembly enhancedAssembly, ConfigFile config, ManualLogSource log)
		{
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Expected O, but got Unknown
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Expected O, but got Unknown
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Expected O, but got Unknown
			Log = log;
			GiveTestGarments = config.Bind<bool>("Wardrobe", "GiveTestGarments", false, "Test only: put one item for every garment level found in the avatar into the inventory, once per level and character, as free slots allow.");
			Type type = enhancedAssembly.GetType("EnhancedValheimVRM.VrmController", throwOnError: false);
			Type type2 = enhancedAssembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false);
			FindInstance = ((type == null) ? null : AccessTools.DeclaredMethod(type, "FindInstance", new Type[1] { typeof(Player) }, (Type[])null));
			GetGameObject = ((type2 == null) ? null : AccessTools.DeclaredMethod(type2, "GetGameObject", Type.EmptyTypes, (Type[])null));
			MethodInfo methodInfo = ((type2 == null) ? null : AccessTools.DeclaredMethod(type2, "CreateVrmGo", Type.EmptyTypes, (Type[])null));
			Clone = ((type2 == null) ? null : AccessTools.DeclaredField(type2, "_vrmGo"));
			Owner = ((type2 == null) ? null : AccessTools.DeclaredField(type2, "_player"));
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(VisEquipment), "UpdateEquipmentVisuals", Type.EmptyTypes, (Type[])null);
			FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentHelmetItemHash");
			FieldInfo fieldInfo2 = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentChestItemHash");
			FieldInfo fieldInfo3 = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentLegItemHash");
			FieldInfo fieldInfo4 = AccessTools.DeclaredField(typeof(VisEquipment), "m_nview");
			FieldInfo fieldInfo5 = AccessTools.DeclaredField(typeof(Humanoid), "m_helmetItem");
			FieldInfo fieldInfo6 = AccessTools.DeclaredField(typeof(Humanoid), "m_chestItem");
			FieldInfo fieldInfo7 = AccessTools.DeclaredField(typeof(Humanoid), "m_legItem");
			if (FindInstance == null || !FindInstance.IsStatic || FindInstance.ReturnType != type2 || GetGameObject == null || GetGameObject.IsStatic || GetGameObject.ReturnType != typeof(GameObject) || Owner?.FieldType != typeof(Player) || methodInfo == null || methodInfo.IsStatic || Clone?.FieldType != typeof(GameObject) || methodInfo2 == null || fieldInfo?.FieldType != typeof(int) || fieldInfo2?.FieldType != typeof(int) || fieldInfo3?.FieldType != typeof(int) || fieldInfo4?.FieldType != typeof(ZNetView) || fieldInfo5?.FieldType != typeof(ItemData) || fieldInfo6?.FieldType != typeof(ItemData) || fieldInfo7?.FieldType != typeof(ItemData))
			{
				throw new MissingMethodException("Expected avatar or equipment hooks were not found.");
			}
			HelmetHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo);
			ChestHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo2);
			LegHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo3);
			NView = AccessTools.FieldRefAccess<VisEquipment, ZNetView>(fieldInfo4);
			HelmetItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo5);
			ChestItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo6);
			LegItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo7);
			Harmony val = new Harmony("local.valheim.vrm1springclonefix.wardrobe");
			try
			{
				val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Wardrobe), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Wardrobe), "AfterEquipment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
				val.UnpatchSelf();
				throw;
			}
			log.LogInfo((object)("Avatar wardrobe enabled: Garment_<item>_Q<level> meshes follow worn helmet, chest and legs items; Underwear_* meshes hide under a legs garment." + (GiveTestGarments.Value ? " Test garments will be given once per level and character." : "")));
		}

		private static void BeforeCreate(object __instance)
		{
			try
			{
				object? value = Clone.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<WardrobeView>() != (Object)null)
				{
					return;
				}
				List<Garment> underwear;
				List<Garment> list = Collect(val, out underwear);
				if (list.Count == 0 && underwear.Count == 0)
				{
					return;
				}
				WardrobeView wardrobeView = val.AddComponent<WardrobeView>();
				wardrobeView.Garments = list;
				wardrobeView.Underwear = underwear;
				object? value2 = Owner.GetValue(__instance);
				Player val2 = (Player)((value2 is Player) ? value2 : null);
				VisEquipment val3 = (((Object)(object)val2 == (Object)null) ? null : ((Component)val2).GetComponent<VisEquipment>());
				if ((Object)(object)val3 != (Object)null)
				{
					State value3 = States.GetValue(val3, (VisEquipment _) => new State());
					value3.View = wardrobeView;
					value3.Applied = false;
					value3.NextLookup = Time.frameCount + 30;
				}
				Log.LogInfo((object)("Avatar garments" + (((Object)(object)val2 == (Object)null) ? "" : (" on " + val2.GetPlayerName())) + ": " + string.Join(", ", from g in list.Concat(underwear)
					select ((Object)g.Node).name + ((g.Masks.Count > 0) ? (" (skin masks: " + g.Masks.Count + ")") : ""))));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Wardrobe could not prepare a new avatar: " + ex));
			}
		}

		private static void AfterEquipment(VisEquipment __instance)
		{
			if (Failed || !__instance.m_isPlayer)
			{
				return;
			}
			try
			{
				State value = States.GetValue(__instance, (VisEquipment _) => new State());
				if (value.NoPlayer)
				{
					return;
				}
				if ((Object)(object)value.Player == (Object)null)
				{
					value.Player = ((Component)__instance).GetComponent<Player>();
					if ((Object)(object)value.Player == (Object)null)
					{
						value.NoPlayer = true;
						return;
					}
				}
				if (Time.frameCount >= value.NextLookup)
				{
					value.NextLookup = Time.frameCount + 30;
					GameObject val = AvatarRoot(value.Player);
					WardrobeView wardrobeView = (((Object)(object)val == (Object)null) ? null : val.GetComponent<WardrobeView>());
					if ((Object)(object)wardrobeView != (Object)(object)value.View)
					{
						value.View = wardrobeView;
						value.Applied = false;
					}
					if ((Object)(object)wardrobeView != (Object)null)
					{
						GiveOnce(value.Player, wardrobeView.Garments);
					}
				}
				if ((Object)(object)value.View == (Object)null || value.View.Frozen || value.View.Garments.Count == 0)
				{
					return;
				}
				ZNetView obj = NView.Invoke(__instance);
				ZDO zdo = ((obj != null) ? obj.GetZDO() : null);
				int num = HelmetHash.Invoke(__instance);
				int num2 = ChestHash.Invoke(__instance);
				int num3 = LegHash.Invoke(__instance);
				int num4 = Quality(value.Player, zdo, HelmetItem, HelmetQualityKey);
				int num5 = Quality(value.Player, zdo, ChestItem, ChestQualityKey);
				int num6 = Quality(value.Player, zdo, LegItem, LegQualityKey);
				if (!value.Applied || num != value.Helmet || num2 != value.Chest || num3 != value.Leg || num4 != value.HelmetQuality || num5 != value.ChestQuality || num6 != value.LegQuality)
				{
					if (((Character)value.Player).IsDead())
					{
						value.View.Frozen = true;
						return;
					}
					value.Helmet = num;
					value.Chest = num2;
					value.Leg = num3;
					value.HelmetQuality = num4;
					value.ChestQuality = num5;
					value.LegQuality = num6;
					value.View.Helmet = Pick(value.View.Garments, ItemName(num), num4);
					value.View.Chest = Pick(value.View.Garments, ItemName(num2), num5);
					value.View.Leg = Pick(value.View.Garments, ItemName(num3), num6);
					value.Applied = true;
				}
			}
			catch (Exception ex)
			{
				Failed = true;
				Log.LogError((object)("Wardrobe disabled after an error: " + ex));
			}
		}

		private static GameObject AvatarRoot(Player player)
		{
			object obj = FindInstance.Invoke(null, new object[1] { player });
			if (obj != null)
			{
				object? obj2 = GetGameObject.Invoke(obj, null);
				return (GameObject)((obj2 is GameObject) ? obj2 : null);
			}
			return null;
		}

		private static int Quality(Player player, ZDO zdo, FieldRef<Humanoid, ItemData> itemField, int key)
		{
			if (zdo == null || zdo.IsOwner())
			{
				int num = itemField.Invoke((Humanoid)(object)player)?.m_quality ?? 0;
				if (zdo != null && zdo.GetInt(key, 0) != num)
				{
					zdo.Set(key, num, false);
				}
				return num;
			}
			return zdo.GetInt(key, 0);
		}

		private static string ItemName(int hash)
		{
			if (hash == 0 || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(hash);
			if (!((Object)(object)itemPrefab == (Object)null))
			{
				return ((Object)itemPrefab).name;
			}
			return null;
		}

		private static Garment Pick(List<Garment> garments, string item, int quality)
		{
			if (item == null)
			{
				return null;
			}
			List<Garment> source = (from g in garments
				where g.Item == item
				orderby g.Level
				select g).ToList();
			return source.LastOrDefault((Garment g) => g.Level <= Math.Max(1, quality)) ?? source.FirstOrDefault();
		}

		private static List<Garment> Collect(GameObject root, out List<Garment> underwear)
		{
			List<Garment> list = new List<Garment>();
			underwear = new List<Garment>();
			Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				string name = ((Object)val).name;
				if (name.StartsWith("Underwear_", StringComparison.Ordinal))
				{
					Renderer component = ((Component)val).GetComponent<Renderer>();
					if ((Object)(object)component != (Object)null)
					{
						underwear.Add(new Garment
						{
							Node = ((Component)val).gameObject,
							Renderer = component
						});
					}
					continue;
				}
				int num = name.LastIndexOf("_Q", StringComparison.Ordinal);
				if (!name.StartsWith("Garment_", StringComparison.Ordinal) || num <= "Garment_".Length)
				{
					continue;
				}
				int j;
				for (j = num + 2; j < name.Length && char.IsDigit(name[j]); j++)
				{
				}
				if (j != num + 2 && int.TryParse(name.Substring(num + 2, j - num - 2), out var result))
				{
					Renderer component2 = ((Component)val).GetComponent<Renderer>();
					if (!((Object)(object)component2 == (Object)null))
					{
						list.Add(new Garment
						{
							Node = ((Component)val).gameObject,
							Renderer = component2,
							Item = name.Substring("Garment_".Length, num - "Garment_".Length),
							Level = result
						});
					}
				}
			}
			if (list.Count == 0 && underwear.Count == 0)
			{
				return list;
			}
			SkinnedMeshRenderer[] componentsInChildren2 = root.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val2 in componentsInChildren2)
			{
				Mesh sharedMesh = val2.sharedMesh;
				if ((Object)(object)sharedMesh == (Object)null)
				{
					continue;
				}
				for (int k = 0; k < sharedMesh.blendShapeCount; k++)
				{
					string blendShapeName = sharedMesh.GetBlendShapeName(k);
					if (blendShapeName.StartsWith("Mask_", StringComparison.Ordinal))
					{
						string target = blendShapeName.Substring("Mask_".Length);
						(list.Find((Garment g) => ((Object)g.Node).name == target) ?? underwear.Find((Garment g) => ((Object)g.Node).name == target))?.Masks.Add(new KeyValuePair<SkinnedMeshRenderer, int>(val2, k));
					}
				}
			}
			return list;
		}

		private static void GiveOnce(Player player, List<Garment> garments)
		{
			if (!GiveTestGarments.Value || garments.Count == 0 || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			player.m_customData.TryGetValue("NordicTestGarmentsGivenItems", out var value);
			HashSet<string> hashSet = new HashSet<string>((value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries));
			List<ItemData> allItems = inventory.GetAllItems();
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			int num = 0;
			foreach (Garment garment in from g in garments
				orderby g.Item, g.Level
				select g)
			{
				string item = garment.Item + ":" + garment.Level;
				if (hashSet.Contains(item))
				{
					continue;
				}
				if (allItems.Any((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == garment.Item && i.m_quality == garment.Level))
				{
					hashSet.Add(item);
					continue;
				}
				if (inventory.GetEmptySlots() == 0)
				{
					num++;
					continue;
				}
				hashSet.Add(item);
				if (inventory.AddItem(garment.Item, 1, garment.Level, 0, player.GetPlayerID(), player.GetPlayerName(), false, false) != null)
				{
					list.Add(garment.Item + " quality " + garment.Level);
				}
				else
				{
					list2.Add(item);
				}
			}
			string text = string.Join(";", hashSet.OrderBy<string, string>((string s) => s, StringComparer.Ordinal).ToArray());
			if (text != (value ?? ""))
			{
				player.m_customData["NordicTestGarmentsGivenItems"] = text;
			}
			if (list.Count > 0)
			{
				Log.LogInfo((object)("Test garments given to " + player.GetPlayerName() + ": " + string.Join(", ", list.ToArray())));
			}
			if (list2.Count > 0)
			{
				Log.LogWarning((object)("Test garments could not be created: " + string.Join(", ", list2.ToArray())));
			}
			if (num > 0 && (!WarnedFull || list.Count > 0))
			{
				Log.LogWarning((object)(num + " test garments wait for free inventory slots."));
			}
			WarnedFull = num > 0;
		}
	}
	internal sealed class WardrobeView : MonoBehaviour
	{
		internal List<Wardrobe.Garment> Garments = new List<Wardrobe.Garment>();

		internal List<Wardrobe.Garment> Underwear = new List<Wardrobe.Garment>();

		internal Wardrobe.Garment Helmet;

		internal Wardrobe.Garment Chest;

		internal Wardrobe.Garment Leg;

		internal bool Frozen;

		private void LateUpdate()
		{
			foreach (Wardrobe.Garment garment in Garments)
			{
				Show(garment, garment == Helmet || garment == Chest || garment == Leg);
			}
			bool show = Leg == null || (Object)(object)Leg.Renderer == (Object)null || Leg.Renderer.forceRenderingOff;
			foreach (Wardrobe.Garment item in Underwear)
			{
				Show(item, show);
			}
		}

		private static void Show(Wardrobe.Garment garment, bool show)
		{
			if ((Object)(object)garment.Renderer != (Object)null && garment.Renderer.enabled != show)
			{
				garment.Renderer.enabled = show;
			}
			float num = ((show && (Object)(object)garment.Renderer != (Object)null && !garment.Renderer.forceRenderingOff) ? 100f : 0f);
			foreach (KeyValuePair<SkinnedMeshRenderer, int> mask in garment.Masks)
			{
				if ((Object)(object)mask.Key != (Object)null && mask.Key.GetBlendShapeWeight(mask.Value) != num)
				{
					mask.Key.SetBlendShapeWeight(mask.Value, num);
				}
			}
		}
	}
	public struct D3
	{
		public double X;

		public double Y;

		public double Z;

		public double this[int k]
		{
			get
			{
				return k switch
				{
					1 => Y, 
					0 => X, 
					_ => Z, 
				};
			}
			set
			{
				switch (k)
				{
				case 0:
					X = value;
					break;
				case 1:
					Y = value;
					break;
				default:
					Z = value;
					break;
				}
			}
		}

		public D3(double x, double y, double z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public static D3 operator +(D3 a, D3 b)
		{
			return new D3(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
		}

		public static D3 operator -(D3 a, D3 b)
		{
			return new D3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
		}

		public static D3 operator *(D3 a, double s)
		{
			return new D3(a.X * s, a.Y * s, a.Z * s);
		}
	}
	public struct M3
	{
		public D3 C0;

		public D3 C1;

		public D3 C2;

		public M3(D3 c0, D3 c1, D3 c2)
		{
			C0 = c0;
			C1 = c1;
			C2 = c2;
		}

		public D3 Col(int k)
		{
			return k switch
			{
				1 => C1, 
				0 => C0, 
				_ => C2, 
			};
		}
	}
	public struct Q4
	{
		public double X;

		public double Y;

		public double Z;

		public double W;

		public Q4(double x, double y, double z, double w)
		{
			X = x;
			Y = y;
			Z = z;
			W = w;
		}
	}
	public sealed class JiggleStage
	{
		public bool On = true;

		public double[] Freq;

		public double[] Zeta;

		public double[] LimPos;

		public double[] LimNeg;

		public JiggleStage Clone()
		{
			return new JiggleStage
			{
				On = On,
				Freq = (double[])Freq.Clone(),
				Zeta = (double[])Zeta.Clone(),
				LimPos = (double[])LimPos.Clone(),
				LimNeg = (double[])LimNeg.Clone()
			};
		}
	}
	public sealed class JiggleSupport
	{
		public bool On = true;

		public double FreqMul = 1.25;

		public double ZetaMul = 1.35;

		public double LimMul = 0.6;

		public double SideLimMul = 0.48;

		public double InertiaMul = 0.6;

		public double SwingMul = 0.5;

		public JiggleSupport Clone()
		{
			return (JiggleSupport)MemberwiseClone();
		}
	}
	public sealed class JiggleSwing
	{
		public bool On = true;

		public double Amount = 1.0;

		public double RateLo = 300.0;

		public double RateHi = 800.0;

		public double Attack = 0.06;

		public double Release = 0.45;

		public double FreqMul = 0.6;

		public double ZetaMul = 1.2;

		public double LimMul = 1.7;

		public JiggleSwing Clone()
		{
			return (JiggleSwing)MemberwiseClone();
		}
	}
	public sealed class JiggleParams
	{
		public JiggleStage Base = new JiggleStage
		{
			Freq = new double[3] { 2.5, 3.2, 5.5 },
			Zeta = new double[3] { 0.24, 0.22, 0.4 },
			LimPos = new double[3] { 0.025, 0.025, 0.012 },
			LimNeg = new double[3] { 0.025, 0.03, 0.008 }
		};

		public JiggleStage Tip = new JiggleStage
		{
			On = false,
			Freq = new double[3] { 5.0, 4.5, 6.0 },
			Zeta = new double[3] { 0.2, 0.18, 0.3 },
			LimPos = new double[3] { 0.008, 0.01, 0.005 },
			LimNeg = new double[3] { 0.008, 0.01, 0.004 }
		};

		public double Inertia = 0.45;

		public double Gravity = 0.7;

		public double Amount = 1.0;

		public double RotShare = 0.75;

		public double MaxTurnDeg = 32.0;

		public double Squash;

		public double Radius = 0.045;

		public double MarkerFrac = 0.66;

		public double SkinWeight = 0.85;

		public double FilterHz = 12.0;

		public double MaxAccel = 40.0;

		public double MaxSpeed = 1.5;

		public double Substep = 1.0 / 240.0;

		public double SoftStart = 0.8;

		public double SoftFreq = 8.0;

		public double HardAt = 1.25;

		public double Bounce;

		public JiggleSupport Support = new JiggleSupport();

		public JiggleSwing Swing = new JiggleSwing();

		public const double SupportBlendSeconds = 0.3;

		public JiggleParams Clone()
		{
			JiggleParams obj = (JiggleParams)MemberwiseClone();
			obj.Base = Base.Clone();
			obj.Tip = Tip.Clone();
			obj.Support = Support.Clone();
			obj.Swing = Swing.Clone();
			return obj;
		}

		public JiggleParams Supported()
		{
			if (!Support.On)
			{
				return this;
			}
			JiggleParams jiggleParams = Clone();
			jiggleParams.Inertia = Inertia * Support.InertiaMul;
			jiggleParams.Swing.Amount = Swing.Amount * Support.SwingMul;
			JiggleStage[] array = new JiggleStage[2] { jiggleParams.Base, jiggleParams.Tip };
			foreach (JiggleStage jiggleStage in array)
			{
				for (int j = 0; j < 3; j++)
				{
					double num = ((j == 0) ? Support.SideLimMul : Support.LimMul);
					jiggleStage.Freq[j] *= Support.FreqMul;
					jiggleStage.Zeta[j] = Math.Min(1.0, jiggleStage.Zeta[j] * Support.ZetaMul);
					jiggleStage.LimPos[j] *= num;
					jiggleStage.LimNeg[j] *= num;
				}
			}
			return jiggleParams;
		}

		public static void Blend(JiggleParams bare, JiggleParams clothed, double t, JiggleParams into)
		{
			JiggleParams jiggleParams = ((t <= 0.0) ? bare : ((t >= 1.0) ? clothed : null));
			into.Inertia = jiggleParams?.Inertia ?? (bare.Inertia + (clothed.Inertia - bare.Inertia) * t);
			into.Swing.Amount = jiggleParams?.Swing.Amount ?? (bare.Swing.Amount + (clothed.Swing.Amount - bare.Swing.Amount) * t);
			BlendStage(bare.Base, clothed.Base, t, jiggleParams?.Base, into.Base);
			BlendStage(bare.Tip, clothed.Tip, t, jiggleParams?.Tip, into.Tip);
		}

		private static void BlendStage(JiggleStage a, JiggleStage b, double t, JiggleStage src, JiggleStage into)
		{
			for (int i = 0; i < 3; i++)
			{
				into.Freq[i] = ((src != null) ? src.Freq[i] : (a.Freq[i] + (b.Freq[i] - a.Freq[i]) * t));
				into.Zeta[i] = ((src != null) ? src.Zeta[i] : (a.Zeta[i] + (b.Zeta[i] - a.Zeta[i]) * t));
				into.LimPos[i] = ((src != null) ? src.LimPos[i] : (a.LimPos[i] + (b.LimPos[i] - a.LimPos[i]) * t));
				into.LimNeg[i] = ((src != null) ? src.LimNeg[i] : (a.LimNeg[i] + (b.LimNeg[i] - a.LimNeg[i]) * t));
			}
		}
	}
	public sealed class JiggleRest
	{
		public D3 Head;

		public D3 Tail;

		public Q4 Q0;

		public M3 Basis;

		public double Hand = 1.0;
	}
	public struct JigglePose
	{
		public D3 Position;

		public D3 Scale;

		public Q4 Quaternion;
	}
	public sealed class BreastJiggle
	{
		public const double GRAVITY = 9.81;

		public readonly JiggleRest Rest;

		public JiggleParams Params;

		public D3 X;

		public D3 V;

		public D3 Xt;

		public D3 Vt;

		public D3 Accel;

		public double Swing;

		public double TwistRate;

		private readonly JiggleStage swingStage = new JiggleStage
		{
			Freq = new double[3],
			Zeta = new double[3],
			LimPos = new double[3],
			LimNeg = new double[3]
		};

		private bool hasPrev;

		private D3 prevA;

		private D3 prevV;

		private D3 prevW;

		private M3 prevM;

		private int prevN;

		private double prevDt;

		public BreastJiggle(JiggleRest rest, JiggleParams p)
		{
			Rest = rest;
			Params = p;
			Reset();
		}

		public void Reset()
		{
			X = default(D3);
			V = default(D3);
			Xt = default(D3);
			Vt = default(D3);
			hasPrev = false;
			Accel = default(D3);
			Swing = 0.0;
			TwistRate = 0.0;
		}

		public static JiggleRest MakeRest(D3 head, Q4 q0, D3 tailLocal, M3 parentRot, D3 headWorld, D3 midWorld)
		{
			D3 d = head + QRotate(q0, tailLocal);
			D3 d2 = MulMtV(parentRot, new D3(0.0, 1.0, 0.0));
			D3 d3 = d - head;
			d3 = Norm(d3 - d2 * Dot(d3, d2));
			D3 d4 = Cross(d2, d3);
			if (Dot(MulMV(parentRot, d4), headWorld - midWorld) < 0.0)
			{
				d4 *= -1.0;
			}
			double hand = ((!(Dot(Cross(d4, d2), d3) < 0.0)) ? 1 : (-1));
			return new JiggleRest
			{
				Head = head,
				Q0 = q0,
				Tail = d,
				Basis = new M3(d4, d2, d3),
				Hand = hand
			};
		}

		private D3 MarkerLocal()
		{
			return Rest.Head + (Rest.Tail - Rest.Head) * Math.Max(0.05, Params.MarkerFrac);
		}

		public void Step(double dt, D3 parentPos, M3 parentRot, double parentScale = 1.0)
		{
			JiggleParams jiggleParams = Params;
			JiggleRest rest = Rest;
			D3 d = parentPos + MulMV(parentRot, MarkerLocal()) * parentScale;
			M3 m = MulMM(parentRot, rest.Basis);
			if (!(dt > 0.0))
			{
				return;
			}
			double num = 0.0;
			if (hasPrev)
			{
				double num2 = 0.0;
				for (int i = 0; i < 3; i++)
				{
					num2 += Dot(prevM.Col(i), m.Col(i));
				}
				num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0)));
			}
			if (!hasPrev || dt > 0.1 || Len(d - prevA) > 1.0 || num > Math.PI / 2.0)
			{
				hasPrev = true;
				prevA = d;
				prevM = m;
				prevV = default(D3);
				prevW = default(D3);
				prevN = 0;
				prevDt = dt;
				return;
			}
			D3 d2 = default(D3);
			for (int j = 0; j < 3; j++)
			{
				d2 += Cross(prevM.Col(j), m.Col(j));
			}
			double num3 = Len(d2);
			d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : default(D3));
			D3 d3 = (d - prevA) * (1.0 / dt);
			double t = ((jiggleParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * jiggleParams.FilterHz * dt)) : 1.0);
			bool num4 = prevN == 0;
			D3 d4 = (num4 ? d3 : Lerp(prevV, d3, t));
			D3 d5 = (num4 ? d2 : Lerp(prevW, d2, t));
			double num5 = (prevDt + dt) / 2.0;
			D3 d6 = (num4 ? default(D3) : ((d4 - prevV) * (1.0 / num5)));
			D3 v = (num4 ? default(D3) : ((d5 - prevW) * (1.0 / num5)));
			d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (jiggleParams.MaxAccel * jiggleParams.MaxAccel));
			D3 accel = MulMtV(prevM, d6);
			D3 a = MulMtV(m, d5) * rest.Hand;
			D3 a2 = MulMtV(m, v) * rest.Hand;
			D3 a3 = MulMtV(prevM, new D3(0.0, -9.81, 0.0));
			D3 b = MulMtV(m, new D3(0.0, -9.81, 0.0));
			Accel = accel;
			JiggleStage jiggleStage = jiggleParams.Base;
			JiggleSwing swing = jiggleParams.Swing;
			TwistRate = a.Y;
			if (swing != null && swing.On)
			{
				double num6 = Math.Min(1.0, Math.Max(0.0, (Math.Abs(a.Y) * 180.0 / Math.PI - swing.RateLo) / (swing.RateHi - swing.RateLo)));
				double num7 = num6 * num6 * (3.0 - 2.0 * num6);
				Swing += (num7 - Swing) * (1.0 - Math.Exp((0.0 - dt) / ((num7 > Swing) ? swing.Attack : swing.Release)));
				double num8 = Swing * swing.Amount;
				if (num8 > 0.0)
				{
					JiggleStage jiggleStage2 = jiggleParams.Base;
					jiggleStage = swingStage;
					double num9 = 1.0 + (swing.FreqMul - 1.0) * num8;
					double num10 = 1.0 + (swing.ZetaMul - 1.0) * num8;
					double num11 = 1.0 + (swing.LimMul - 1.0) * num8;
					for (int k = 0; k < 3; k++)
					{
						jiggleStage.Freq[k] = jiggleStage2.Freq[k];
						jiggleStage.Zeta[k] = jiggleStage2.Zeta[k];
						jiggleStage.LimPos[k] = jiggleStage2.LimPos[k];
						jiggleStage.LimNeg[k] = jiggleStage2.LimNeg[k];
					}
					jiggleStage.Freq[0] = jiggleStage2.Freq[0] * num9;
					jiggleStage.Zeta[0] = Math.Min(1.0, jiggleStage2.Zeta[0] * num10);
					jiggleStage.LimPos[0] = jiggleStage2.LimPos[0] * num11;
					jiggleStage.LimNeg[0] = jiggleStage2.LimNeg[0] * num11;
				}
			}
			int num12 = Math.Max(1, (int)Math.Ceiling(dt / jiggleParams.Substep - 1E-09));
			double num13 = dt / (double)num12;
			for (int l = 1; l <= num12; l++)
			{
				D3 d7 = Lerp(a3, b, (double)l / (double)num12);
				D3 d8 = Cross(a, V);
				D3 d9 = Cross(a2, X);
				D3 d10 = Cross(a, Cross(a, X));
				D3 d11 = default(D3);
				D3 a4 = default(D3);
				for (int n = 0; n < 3; n++)
				{
					d11[n] = jiggleParams.Inertia * (0.0 - accel[n] - 2.0 * d8[n] - d9[n] - d10[n]) + jiggleParams.Gravity * (d7[n] - ((n == 1) ? (-9.81) : 0.0));
					double num14 = Math.PI * 2.0 * jiggleStage.Freq[n];
					a4[n] = d11[n] - num14 * num14 * X[n] - 2.0 * jiggleStage.Zeta[n] * num14 * V[n];
				}
				SoftLimit(ref a4, X, jiggleStage, parentScale, jiggleParams);
				for (int num15 = 0; num15 < 3; num15++)
				{
					V[num15] += a4[num15] * num13;
				}
				double num16 = Len(V);
				double num17 = jiggleParams.MaxSpeed * parentScale;
				if (num16 > num17)
				{
					V *= num17 / num16;
				}
				for (int num18 = 0; num18 < 3; num18++)
				{
					X[num18] += V[num18] * num13;
				}
				HardLimit(ref X, ref V, jiggleStage, parentScale, jiggleParams.HardAt, jiggleParams.Bounce);
				if (jiggleParams.Tip.On)
				{
					for (int num19 = 0; num19 < 3; num19++)
					{
						double num20 = Math.PI * 2.0 * jiggleParams.Tip.Freq[num19];
						double num21 = 0.0 - (a4[num19] - d11[num19]) - num20 * num20 * Xt[num19] - 2.0 * jiggleParams.Tip.Zeta[num19] * num20 * Vt[num19];
						Vt[num19] += num21 * num13;
						Xt[num19] += Vt[num19] * num13;
					}
					HardLimit(ref Xt, ref Vt, jiggleParams.Tip, parentScale, jiggleParams.HardAt, jiggleParams.Bounce);
				}
			}
			if (!jiggleParams.Tip.On)
			{
				Xt = default(D3);
				Vt = default(D3);
			}
			prevA = d;
			prevM = m;
			prevV = d4;
			prevW = d5;
			prevN++;
			prevDt = dt;
		}

		public D3 Displacement()
		{
			return X + Xt;
		}

		public JigglePose Pose(double parentScale = 1.0)
		{
			JiggleParams jiggleParams = Params;
			JiggleRest rest = Rest;
			M3 basis = rest.Basis;
			double num = jiggleParams.Amount / (parentScale * jiggleParams.SkinWeight);
			D3 d = MulMV(basis, X) * num;
			D3 d2 = MulMV(basis, Xt) * num;
			D3 d3 = d + d2;
			D3 d4 = MarkerLocal() - rest.Head;
			double num2 = Len(d4);
			D3 d5 = d4 * (1.0 / num2);
			D3 d6 = d * jiggleParams.RotShare + d2;
			d6 -= d5 * Dot(d6, d5);
			double num3 = num2 * Math.Tan(jiggleParams.MaxTurnDeg * Math.PI / 180.0);
			double num4 = Len(d6);
			if (num4 > num3)
			{
				d6 *= num3 / num4;
			}
			Q4 q = QFromTo(d5, Norm(d4 + d6));
			double num5 = Math.Max(-0.1, Math.Min(0.1, jiggleParams.Amount * jiggleParams.Squash * X.Y / (jiggleParams.Radius * parentScale)));
			double num6 = 1.0 - num5;
			double num7 = 1.0 / Math.Sqrt(num6);
			D3 d7 = QRotate(QConj(rest.Q0), basis.C1);
			D3 scale = ((Math.Abs(d7.X) >= Math.Abs(d7.Z)) ? new D3(num6, num7, num7) : new D3(num7, num7, num6));
			D3 d8 = QRotate(QConj(rest.Q0), d4);
			D3 d9 = QRotate(QMul(q, rest.Q0), new D3(d8.X * scale.X, d8.Y * scale.Y, d8.Z * scale.Z));
			D3 position = rest.Head + (d3 - (d9 - d4));
			return new JigglePose
			{
				Position = position,
				Quaternion = QMul(q, rest.Q0),
				Scale = scale
			};
		}

		private static double Ellipsoid(D3 x, JiggleStage lim, double s, out D3 L)
		{
			double num = 0.0;
			L = default(D3);
			for (int i = 0; i < 3; i++)
			{
				L[i] = ((x[i] >= 0.0) ? lim.LimPos[i] : lim.LimNeg[i]) * s;
				num += x[i] / L[i] * (x[i] / L[i]);
			}
			return Math.Sqrt(num);
		}

		private static void SoftLimit(ref D3 a, D3 x, JiggleStage lim, double s, JiggleParams p)
		{
			D3 L;
			double num = Ellipsoid(x, lim, s, out L);
			if (!(num <= p.SoftStart))
			{
				double num2 = (num - p.SoftStart) / (1.0 - p.SoftStart);
				double num3 = Math.PI * 2.0 * p.SoftFreq;
				double num4 = num3 * num3 * num2 * num2;
				D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
				double num5 = Dot(x, b);
				for (int i = 0; i < 3; i++)
				{
					a[i] -= num4 * num5 * b[i];
				}
			}
		}

		private static bool HardLimit(ref D3 x, ref D3 v, JiggleStage lim, double s, double hardAt, double bounce)
		{
			D3 L;
			double num = Ellipsoid(x, lim, s, out L);
			if (num <= hardAt)
			{
				return false;
			}
			for (int i = 0; i < 3; i++)
			{
				x[i] *= hardAt / num;
			}
			D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z)));
			double num2 = Dot(v, b);
			if (num2 > 0.0)
			{
				for (int j = 0; j < 3; j++)
				{
					v[j] -= (1.0 + bounce) * num2 * b[j];
				}
			}
			return true;
		}

		private static double Dot(D3 a, D3 b)
		{
			return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
		}

		private static D3 Cross(D3 a, D3 b)
		{
			return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
		}

		private static double Len(D3 a)
		{
			return Math.Sqrt(Dot(a, a));
		}

		private static D3 Lerp(D3 a, D3 b, double t)
		{
			return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t);
		}

		private static D3 Norm(D3 a)
		{
			double num = Len(a);
			if (!(num > 1E-12))
			{
				return default(D3);
			}
			return a * (1.0 / num);
		}

		private static D3 MulMV(M3 m, D3 v)
		{
			return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z);
		}

		private static D3 MulMtV(M3 m, D3 v)
		{
			return new D3(Dot(m.C0, v), Dot(m.C1, v), Dot(m.C2, v));
		}

		private static M3 MulMM(M3 a, M3 b)
		{
			return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2));
		}

		private static Q4 QMul(Q4 q, Q4 r)
		{
			return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z);
		}

		private static Q4 QConj(Q4 q)
		{
			return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W);
		}

		private static Q4 QFromTo(D3 a, D3 b)
		{
			D3 d = Cross(a, b);
			double num = 1.0 + Dot(a, b);
			if (num < 1E-08)
			{
				return new Q4(0.0, 0.0, 0.0, 1.0);
			}
			double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num);
			return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2);
		}

		public static D3 QRotate(Q4 q, D3 v)
		{
			D3 a = new D3(q.X, q.Y, q.Z);
			D3 d = Cross(a, v) * 2.0;
			return v + d * q.W + Cross(a, d);
		}
	}
	internal static class BreastPhysics
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> Amount;

		internal static ConfigEntry<float> RecordSeconds;

		private static FieldInfo Owner;

		private static readonly FieldInfo RuntimeField = AccessTools.DeclaredField(typeof(Vrm10Instance), "m_runtime");

		internal static readonly List<BreastJiggleDriver> Drivers = new List<BreastJiggleDriver>();

		internal static int ServiceFrame = -1;

		internal static void Install(Assembly enhancedAssembly, ConfigFile config, ManualLogSource log)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			Log = log;
			Enabled = config.Bind<bool>("BreastPhysics", "Enabled", true, "Soft breast physics for the Nordic avatars (Sigrid, Dark Urge) instead of their VRM breast springs. Applies to avatars loaded after the change: restart the game after switching it.");
			Amount = config.Bind<float>("BreastPhysics", "Amount", 1f, new ConfigDescription("Strength of the breast motion: 0 = still, 1 = default. Read every frame (the config file itself is read at game start).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.2f), Array.Empty<object>()));
			RecordSeconds = config.Bind<float>("BreastPhysics", "RecordSeconds", 0f, "Tuning only: above 0, each avatar writes this many seconds of chest motion and breast offsets to BepInEx/breast-physics-records/*.csv, starting when the avatar appears and again whenever the setting changes in game.");
			Type type = enhancedAssembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false);
			Owner = (((object)type != null) ? AccessTools.DeclaredField(type, "_player") : null);
			if (!Enabled.Value)
			{
				log.LogInfo((object)"Breast physics disabled in the config ([BreastPhysics] Enabled = false).");
				return;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(FastSpringBoneService), "LateUpdate", Type.EmptyTypes, (Type[])null);
			try
			{
				if (methodInfo == null)
				{
					throw new MissingMethodException("FastSpringBoneService.LateUpdate");
				}
				new Harmony("local.valheim.vrm1springclonefix.breastphysics").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BreastPhysics), "AfterSprings", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("Breast physics: spring service hook unavailable, avatars step in their own LateUpdate: " + ex.Message));
			}
			log.LogInfo((object)$"Breast physics enabled for Nordic avatars (Amount {Amount.Value:0.##}).");
		}

		private static void AfterSprings()
		{
			ServiceFrame = Time.frameCount;
			for (int num = Drivers.Count - 1; num >= 0; num--)
			{
				BreastJiggleDriver breastJiggleDriver = Drivers[num];
				if ((Object)(object)breastJiggleDriver == (Object)null)
				{
					Drivers.RemoveAt(num);
				}
				else
				{
					try
					{
						breastJiggleDriver.Tick();
					}
					catch (Exception ex)
					{
						Log.LogError((object)("Breast physics step failed, driver stopped: " + ex));
						((Behaviour)breastJiggleDriver).enabled = false;
					}
				}
			}
		}

		internal static void Attach(object vrmInstance, GameObject root)
		{
			if (Enabled == null || !Enabled.Value || (Object)(object)root == (Object)null)
			{
				return;
			}
			Vrm10Instance component = root.GetComponent<Vrm10Instance>();
			if ((Object)(object)component == (Object)null || component.SpringBone == null || (Object)(object)root.GetComponent<BreastJiggleDriver>() != (Object)null)
			{
				return;
			}
			string text = JigglePresets.AvatarOf(root);
			if (text == null)
			{
				return;
			}
			object? obj = Owner?.GetValue(vrmInstance);
			Player val = (Player)((obj is Player) ? obj : null);
			string text2 = text + (((Object)(object)val == (Object)null) ? "" : (" (" + val.GetPlayerName() + ")"));
			List<Spring> springs = component.SpringBone.Springs;
			List<Spring> list = springs.Where((Spring sp) => sp.Joints.Count > 0 && (Object)(object)sp.Joints[0] != (Object)null && JigglePresets.IsBreastBone(((Object)((Component)sp.Joints[0]).transform).name)).ToList();
			List<(Transform, Transform)> list2 = list.Select((Spring sp) => (bone: ((Component)sp.Joints[0]).transform, end: (sp.Joints.Count > 1 && (Object)(object)sp.Joints[1] != (Object)null) ? ((Component)sp.Joints[1]).transform : null)).ToList();
			List<Transform> list3 = list2.Select<(Transform, Transform), Transform>(((Transform bone, Transform end) x) => x.bone).Distinct().ToList();
			if (list.Count != 2 || list3.Count != 2 || list2.Any<(Transform, Transform)>(((Transform bone, Transform end) x) => (Object)(object)x.end == (Object)null || (Object)(object)x.end.parent != (Object)(object)x.bone || (Object)(object)x.bone.parent == (Object)null || !x.bone.IsChildOf(root.transform)))
			{
				Log.LogWarning((object)$"Breast physics not attached to {text2}: expected two breast springs with end bones, found {list.Count}; VRM springs kept.");
				return;
			}
			JiggleParams jiggleParams = JigglePresets.For(text);
			BreastJiggleDriver breastJiggleDriver = root.AddComponent<BreastJiggleDriver>();
			if (!breastJiggleDriver.Setup(list2, jiggleParams, text2))
			{
				Object.Destroy((Object)(object)breastJiggleDriver);
				Log.LogWarning((object)("Breast physics not attached to " + text2 + ": rest pose unusable; VRM springs kept."));
				return;
			}
			List<Spring> collection = springs.ToList();
			try
			{
				foreach (Spring item in list)
				{
					springs.Remove(item);
				}
				if (RuntimeField != null && RuntimeField.GetValue(component) != null)
				{
					component.Runtime.ReconstructSpringBone();
				}
			}
			catch
			{
				springs.Clear();
				springs.AddRange(collection);
				Object.Destroy((Object)(object)breastJiggleDriver);
				if (RuntimeField != null && RuntimeField.GetValue(component) != null)
				{
					component.Runtime.ReconstructSpringBone();
				}
				throw;
			}
			Log.LogInfo((object)("Breast physics attached to " + text2 + ", clone " + ((Object)root).name + ": bones " + string.Join(", ", list3.Select((Transform b) => ((Object)b).name)) + "; " + $"bare {Describe(jiggleParams)}; clothed {Describe(jiggleParams.Supported())}; Amount {Amount.Value:0.##}. " + "Starts bare; a change to clothed or bare is logged when the worn chest garment changes."));
		}

		internal static string Describe(JiggleParams p)
		{
			return FormattableString.Invariant($"freq out/up/fwd {p.Base.Freq[0]:0.##}/{p.Base.Freq[1]:0.##}/{p.Base.Freq[2]:0.##} Hz, damping {p.Base.Zeta[0]:0.###}/{p.Base.Zeta[1]:0.###}/{p.Base.Zeta[2]:0.###}, ") + FormattableString.Invariant($"travel out/up/down/fwd {p.Base.LimPos[0] * 1000.0:0.#}/{p.Base.LimPos[1] * 1000.0:0.#}/{p.Base.LimNeg[1] * 1000.0:0.#}/{p.Base.LimPos[2] * 1000.0:0.#} mm, inertia {p.Inertia:0.###}") + FormattableString.Invariant($", swing-out {(p.Swing.On ? p.Swing.Amount : 0.0):0.##}");
		}
	}
	internal static class JigglePresets
	{
		internal static bool IsBreastBone(string name)
		{
			if (!(name == "CC_Base_L_Breast"))
			{
				return name == "CC_Base_R_Breast";
			}
			return true;
		}

		internal static string AvatarOf(GameObject root)
		{
			Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name == "Sigrid_Breast_L_End")
				{
					return "Sigrid";
				}
				if (((Object)val).name == "DarkUrge_Breast_L_End")
				{
					return "DarkUrge";
				}
			}
			return null;
		}

		internal static JiggleParams For(string avatar)
		{
			return new JiggleParams();
		}
	}
	[DefaultExecutionOrder(11020)]
	internal sealed class BreastJiggleDriver : MonoBehaviour
	{
		private sealed class Breast
		{
			public Transform Bone;

			public Transform Parent;

			public Vector3 RestPos;

			public Vector3 RestScale;

			public Quaternion RestRot;

			public BreastJiggle Solver;
		}

		private readonly List<Breast> breasts = new List<Breast>();

		private JiggleParams bare;

		private JiggleParams clothed;

		private JiggleParams blended;

		private double support;

		private bool wearing;

		private bool known;

		private bool loggedStep;

		private WardrobeView wardrobe;

		private int steppedFrame = -1;

		private string who;

		private StringBuilder record;

		private float recordLeft;

		private float recordAsked;

		internal bool Setup(List<(Transform bone, Transform end)> pairs, JiggleParams p, string label)
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: 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_0107: 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_0115: 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_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			who = label;
			bare = p;
			clothed = p.Supported();
			blended = p.Clone();
			breasts.Clear();
			RuntimeGltfInstance component = ((Component)this).GetComponent<RuntimeGltfInstance>();
			IReadOnlyDictionary<Transform, TransformState> states = ((component != null) ? component.InitialTransformStates : null);
			Transform val = FindDeep(((Component)this).transform, "CC_Base_Hip");
			foreach (var (val2, val3) in pairs.OrderBy(((Transform bone, Transform end) x) => (!((Object)x.bone).name.Contains("_L_")) ? 1 : 0))
			{
				if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val2.parent == (Object)null))
				{
					RestTRS(val2.parent, states, out var _, out var rot);
					RestTRS(val2, states, out var pos2, out var rot2);
					RestTRS(val ?? ((Component)this).transform, states, out var pos3, out rot2);
					Vector3 pos4;
					Quaternion val4 = Local(val2, states, out pos4);
					Local(val3, states, out var pos5);
					JiggleRest rest = BreastJiggle.MakeRest(D(pos4), Q(val4), D(pos5), M(rot), D(pos2), D(pos3));
					breasts.Add(new Breast
					{
						Bone = val2,
						Parent = val2.parent,
						RestPos = pos4,
						RestRot = val4,
						RestScale = val2.localScale,
						Solver = new BreastJiggle(rest, bare)
					});
				}
			}
			foreach (Breast breast in breasts)
			{
				JiggleRest rest2 = breast.Solver.Rest;
				CultureInfo inv = CultureInfo.InvariantCulture;
				BreastPhysics.Log.LogDebug((object)(string.Format("Breast physics {0}/{1}: hand {2}, out {3}, up {4}, ", who, ((Object)breast.Bone).name, rest2.Hand, V(rest2.Basis.C0, "0.###"), V(rest2.Basis.C1, "0.###")) + "fwd " + V(rest2.Basis.C2, "0.###") + ", head " + V(rest2.Head, "0.####") + ", tail " + V(rest2.Tail, "0.####")));
				string V(D3 v, string f)
				{
					return v.X.ToString(f, inv) + "," + v.Y.ToString(f, inv) + "," + v.Z.ToString(f, inv);
				}
			}
			if (breasts.Count == 2)
			{
				return breasts.All((Breast b) => Finite(b.Solver.Rest.Basis.C0) && Finite(b.Solver.Rest.Basis.C1) && Finite(b.Solver.Rest.Basis.C2));
			}
			return false;
		}

		private void OnEnable()
		{
			foreach (Breast breast in breasts)
			{
				breast.Solver.Reset();
			}
			if (!BreastPhysics.Drivers.Contains(this))
			{
				BreastPhysics.Drivers.Add(this);
			}
		}

		private void OnDisable()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			BreastPhysics.Drivers.Remove(this);
			foreach (Breast breast in breasts)
			{
				if ((Object)(object)breast.Bone != (Object)null)
				{
					breast.Bone.localPosition = breast.RestPos;
					breast.Bone.localRotation = breast.RestRot;
					breast.Bone.localScale = breast.RestScale;
				}
			}
		}

		private void LateUpdate()
		{
			if (BreastPhysics.ServiceFrame >= Time.frameCount - 1)
			{
				return;
			}
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				BreastPhysics.Log.LogError((object)("Breast physics step failed, driver stopped: " + ex));
				((Behaviour)this).enabled = false;
			}
		}

		internal void Tick()
		{
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: 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_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: 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_02c1: 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_02ca: Unknown result type (might be due to invalid IL or missing references)
			if (steppedFrame == Time.frameCount || !((Behaviour)this).isActiveAndEnabled || bare == null || breasts.Count != 2)
			{
				return;
			}
			steppedFrame = Time.frameCount;
			float deltaTime = Time.deltaTime;
			if (!loggedStep)
			{
				loggedStep = true;
				BreastPhysics.Log.LogInfo((object)($"Breast physics {who}: first step at frame {Time.frameCount}, dt {deltaTime:0.0000}, " + ((BreastPhysics.ServiceFrame == Time.frameCount) ? "right after the spring service." : "in its own LateUpdate (spring service not ticking).")));
			}
			UpdateSupport(deltaTime);
			double amount = BreastPhysics.Amount?.Value ?? 1f;
			bare.Amount = amount;
			clothed.Amount = amount;
			blended.Amount = amount;
			float num = BreastPhysics.RecordSeconds?.Value ?? 0f;
			if (num != recordAsked)
			{
				recordAsked = num;
				recordLeft = num;
				record = ((num > 0f) ? new StringBuilder("frame,time,dt,support,L_px,L_py,L_pz,L_qx,L_qy,L_qz,L_qw,L_x,L_y,L_z,R_px,R_py,R_pz,R_qx,R_qy,R_qz,R_qw,R_x,R_y,R_z\n") : null);
			}
			Vector3 val = default(Vector3);
			Quaternion val2 = default(Quaternion);
			Vector3 val3 = default(Vector3);
			foreach (Breast breast in breasts)
			{
				if (!((Object)(object)breast.Bone == (Object)null) && !((Object)(object)breast.Parent == (Object)null))
				{
					Vector3 lossyScale = breast.Parent.lossyScale;
					double parentScale = (double)(Mathf.Abs(lossyScale.x) + Mathf.Abs(lossyScale.y) + Mathf.Abs(lossyScale.z)) / 3.0;
					breast.Solver.Step(deltaTime, D(breast.Parent.position), M(breast.Parent.rotation), parentScale);
					JigglePose jigglePose = breast.Solver.Pose(parentScale);
					((Vector3)(ref val))..ctor((float)jigglePose.Position.X, (float)jigglePose.Position.Y, (float)jigglePose.Position.Z);
					((Quaternion)(ref val2))..ctor((float)jigglePose.Quaternion.X, (float)jigglePose.Quaternion.Y, (float)jigglePose.Quaternion.Z, (float)jigglePose.Quaternion.W);
					((Vector3)(ref val3))..ctor((float)jigglePose.Scale.X, (float)jigglePose.Scale.Y, (float)jigglePose.Scale.Z);
					if (!Finite(val) || !Finite(val3) || !Finite(val2))
					{
						breast.Solver.Reset();
						continue;
					}
					breast.Bone.localPosition = val;
					breast.Bone.localRotation = val2;
					breast.Bone.localScale = Vector3.Scale(val3, breast.RestScale);
				}
			}
			if (record != null)
			{
				Record(deltaTime);
			}
		}

		private void UpdateSupport(float dt)
		{
			if ((Object)(object)wardrobe == (Object)null && (!known || Time.frameCount % 30 == 0))
			{
				wardrobe = ((Component)this).GetComponent<WardrobeView>();
			}
			Wardrobe.Garment garment = (((Object)(object)wardrobe == (Object)null) ? null : wardrobe.Chest);
			bool flag = garment != null && (Object)(object)garment.Renderer != (Object)null && !garment.Renderer.forceRenderingOff;
			if (flag != wearing || !known)
			{
				if (known || flag)
				{
					BreastPhysics.Log.LogInfo((object)("Breast physics " + who + ": " + (flag ? ("clothed (" + ((Object)garment.Node).name + "), supported parameters") : "bare parameters") + "."));
				}
				wearing = flag;
				known = true;
			}
			double num = (wearing ? 1 : 0);
			if (support != num && dt > 0f)
			{
				double num2 = (double)dt / 0.3;
				support = ((num > support) ? Math.Min(num, support + num2) : Math.Max(num, support - num2));
			}
			JiggleParams jiggleParams;
			if (support <= 0.0)
			{
				jiggleParams = bare;
			}
			else if (support >= 1.0)
			{
				jiggleParams = clothed;
			}
			else
			{
				JiggleParams.Blend(bare, clothed, support, blended);
				jiggleParams = blended;
			}
			foreach (Breast breast in breasts)
			{
				breast.Solver.Params = jiggleParams;
			}
		}

		private void Record(float dt)
		{
			//IL_00a5: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			record.Append(Time.frameCount).Append(',').Append(Time.unscaledTime.ToString("R", invariantCulture))
				.Append(',')
				.Append(dt.ToString("R", invariantCulture))
				.Append(',')
				.Append(support.ToString("R", invariantCulture));
			foreach (Breast breast in breasts)
			{
				if ((Object)(object)breast.Parent == (Object)null)
				{
					record = null;
					return;
				}
				Vector3 position = breast.Parent.position;
				Quaternion rotation = breast.Parent.rotation;
				D3 x = breast.Solver.X;
				double[] array = new double[10] { position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, x.X, x.Y, x.Z };
				foreach (double num in array)
				{
					record.Append(',').Append(num.ToString("R", invariantCulture));
				}
			}
			record.Append('\n');
			recordLeft -= dt;
			if (recordLeft > 0f)
			{
				return;
			}
			try
			{
				string text = Path.Combine(Paths.BepInExRootPath, "breast-physics-records");
				Directory.CreateDirectory(text);
				string arg = new string(who.Select((char c) => (!char.IsLetterOrDigit(c)) ? '_' : c).ToArray());
				string text2 = Path.Combine(text, $"{arg}-{((Object)this).GetInstanceID()}-{DateTime.Now:yyyyMMdd-HHmmss}.csv");
				File.WriteAllText(text2, record.ToString());
				BreastPhysics.Log.LogInfo((object)("Breast physics record written: " + text2));
			}
			catch (Exception ex)
			{
				BreastPhysics.Log.LogError((object)("Breast physics record failed: " + ex.Message));
			}
			record = null;
		}

		private static Quaternion Local(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos)
		{
			//IL_0025: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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)
			if (states != null && states.TryGetValue(t, out var value))
			{
				pos = ((TransformState)(ref value)).LocalPosition;
				return ((TransformState)(ref value)).LocalRotation;
			}
			pos = t.localPosition;
			return t.localRotation;
		}

		private void RestTRS(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos, out Quaternion rot)
		{
			//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_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_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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			pos = Vector3.zero;
			rot = Quaternion.identity;
			List<Transform> list = new List<Transform>();
			Transform val = t;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)((Component)this).transform)
			{
				list.Add(val);
				val = val.parent;
			}
			for (int num = list.Count - 1; num >= 0; num--)
			{
				Vector3 pos2;
				Quaternion val2 = Local(list[num], states, out pos2);
				pos += rot * pos2;
				rot *= val2;
			}
		}

		private static Transform FindDeep(Transform t, string name)
		{
			if (((Object)t).name == name)
			{
				return t;
			}
			for (int i = 0; i < t.childCount; i++)
			{
				Transform val = FindDeep(t.GetChild(i), name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static D3 D(Vector3 v)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			return new D3(v.x, v.y, v.z);
		}

		private static Q4 Q(Quaternion q)
		{
			//IL_0000: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return new Q4(q.x, q.y, q.z, q.w);
		}

		private static M3 M(Quaternion q)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			return new M3(D(q * Vector3.right), D(q * Vector3.up), D(q * Vector3.forward));
		}

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

		private static bool Finite(Vector3 v)
		{
			//IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(v.x) && Finite(v.y))
			{
				return Finite(v.z);
			}
			return false;
		}

		private static bool Finite(Quaternion q)
		{
			//IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(q.x) && Finite(q.y) && Finite(q.z))
			{
				return Finite(q.w);
			}
			return false;
		}

		private static bool Finite(D3 v)
		{
			if (!double.IsNaN(v.X + v.Y + v.Z))
			{
				return !double.IsInfinity(v.X + v.Y + v.Z);
			}
			return false;
		}
	}
}
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 underscor