Decompiled source of PeakSkin v1.1.0

PeakSkin/PeakSkin.dll

Decompiled 3 weeks ago
using System;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.Rendering;
using ZstdSharp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace PeakSkin;

internal static class BonePairs
{
	public static readonly string[,] Pairs = new string[20, 2]
	{
		{ "Hip", "hips" },
		{ "Mid", "spine" },
		{ "AimJoint", "chest" },
		{ "Head", "head" },
		{ "S_Shoulder_L", "shoulder.L" },
		{ "Arm_L", "upper_arm.L" },
		{ "Elbow_L", "lower_arm.L" },
		{ "Hand_L", "hand.L" },
		{ "S_Shoulder_R", "shoulder.R" },
		{ "Arm_R", "upper_arm.R" },
		{ "Elbow_R", "lower_arm.R" },
		{ "Hand_R", "hand.R" },
		{ "Leg_L", "upper_leg.L" },
		{ "Knee_L", "lower_leg.L" },
		{ "Foot_L", "foot.L" },
		{ "S_Toe_1_L", "toes.L" },
		{ "Leg_R", "upper_leg.R" },
		{ "Knee_R", "lower_leg.R" },
		{ "Foot_R", "foot.R" },
		{ "S_Toe_1_R", "toes.R" }
	};
}
internal sealed class AvatarManager : MonoBehaviour
{
	public static AvatarManager Instance;

	public VrmModel Model;

	private const string LocalId = "local";

	private readonly List<VrmAvatar> _avatars = new List<VrmAvatar>();

	private readonly Dictionary<string, VrmModel> _peer = new Dictionary<string, VrmModel>(4, StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<string> _failed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<string> _warned = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private const int MaxPeerModels = 6;

	private float _next;

	private int _lastCount = -1;

	private string _lastLocal = "<none>";

	private float _nextDiag;

	private void Awake()
	{
		Instance = this;
	}

	private void Update()
	{
		if (Model != Plugin.CurrentModel)
		{
			SwapModel();
		}
		if (ModConfig.PanelEnabled && Input.GetKeyDown((KeyCode)107) && (Object)(object)SkinPanel.Instance != (Object)null)
		{
			SkinPanel.Instance.ToggleVisible();
		}
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime < _next)
		{
			return;
		}
		_next = unscaledTime + 0.5f;
		try
		{
			NetSync.Tick();
		}
		catch (Exception ex)
		{
			Plugin.Log("[net] tick failed: " + ex.Message);
		}
		try
		{
			Scan();
		}
		catch (Exception ex2)
		{
			Plugin.Err("scan failed: " + ex2);
		}
	}

	private void SwapModel()
	{
		Plugin.Log((Plugin.CurrentModel == null) ? "[model] 原版装扮 — tearing down every PeakSkin avatar" : ("[model] rebuild avatars for " + (Plugin.CurrentModelPath ?? "<null>")));
		for (int num = _avatars.Count - 1; num >= 0; num--)
		{
			DestroyAvatar(_avatars[num]);
		}
		_avatars.Clear();
		Model = Plugin.CurrentModel;
		_lastCount = -1;
		_lastLocal = "<none>";
	}

	private void DestroyAvatar(VrmAvatar av)
	{
		if ((Object)(object)av == (Object)null)
		{
			return;
		}
		try
		{
			if (av.Hidden != null)
			{
				for (int i = 0; i < av.Hidden.Length; i++)
				{
					if ((Object)(object)av.Hidden[i] != (Object)null)
					{
						av.Hidden[i].forceRenderingOff = false;
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			((Component)av).gameObject.SetActive(false);
			Object.Destroy((Object)(object)((Component)av).gameObject);
		}
		catch
		{
		}
	}

	private void DropAvatar(Character c)
	{
		VrmAvatar vrmAvatar = FindAvatar(c);
		if (!((Object)(object)vrmAvatar == (Object)null))
		{
			_avatars.Remove(vrmAvatar);
			DestroyAvatar(vrmAvatar);
		}
	}

	private bool PickModel(Character c, bool local, out VrmModel want, out string modelId)
	{
		want = null;
		modelId = null;
		if (local)
		{
			if (Model == null)
			{
				return false;
			}
			want = Model;
			modelId = "local";
			return true;
		}
		int num = ActorOf(c);
		string text = (ModConfig.ShowOtherModels ? NetSync.IdOfActor(num) : null);
		if (string.Equals(text, "none", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		if (!string.IsNullOrEmpty(text))
		{
			VrmModel vrmModel = PeerModel(text);
			if (vrmModel != null)
			{
				want = vrmModel;
				modelId = text;
				return true;
			}
			WarnMissing(num, text);
			return false;
		}
		if (ModConfig.ReplaceOthers && Model != null)
		{
			want = Model;
			modelId = "local";
			return true;
		}
		return false;
	}

	private static Tune PickTune(VrmAvatar av)
	{
		if (ModConfig.ShowOtherModels && NetSync.TryGetTuneOfActor(av.ActorNumber, out var tune))
		{
			return tune;
		}
		if (!(av.ModelId == "local"))
		{
			return Tune.Identity;
		}
		return Tune.FromConfig();
	}

	private VrmModel PeerModel(string id)
	{
		if (string.IsNullOrEmpty(id))
		{
			return null;
		}
		if (Model != null && string.Equals(NetSync.OwnId, id, StringComparison.OrdinalIgnoreCase))
		{
			return Model;
		}
		if (_peer.TryGetValue(id, out var value) && value != null && (Object)(object)value.Root != (Object)null)
		{
			return value;
		}
		string text = ModelCatalog.PathOf(id);
		if (text == null)
		{
			return null;
		}
		if (_failed.Contains(id))
		{
			return null;
		}
		if (_peer.Count >= 6)
		{
			Plugin.Log("[net] peer model limit reached (" + 6 + "), ignoring " + Path.GetFileName(text));
			_failed.Add(id);
			return null;
		}
		Plugin.Log("[net] loading peer model " + Path.GetFileName(text) + " (" + id + ")");
		try
		{
			value = Plugin.LoadModel(text);
		}
		catch (Exception ex)
		{
			Plugin.Log("[net] peer model load failed: " + ex.Message);
			value = null;
		}
		if (value == null)
		{
			_failed.Add(id);
			Plugin.Log("[net] giving up on " + Path.GetFileName(text) + " — it will stay vanilla this session");
			return null;
		}
		_peer[id] = value;
		return value;
	}

	private void WarnMissing(int actor, string id)
	{
		if (_warned.Add(id))
		{
			string text = ModelCatalog.PathOf(id);
			Plugin.Log("[net] actor " + actor + " wears " + id + ((text == null) ? " — not on this machine; put the same model in the skin folder to see them" : " — that file failed to load"));
		}
	}

	private static int ActorOf(Character c)
	{
		try
		{
			PhotonView photonView = ((MonoBehaviourPun)c).photonView;
			if ((Object)(object)photonView == (Object)null)
			{
				return -1;
			}
			if (photonView.Owner != null)
			{
				return photonView.Owner.ActorNumber;
			}
			return photonView.OwnerActorNr;
		}
		catch
		{
			return -1;
		}
	}

	private void Scan()
	{
		if (Model == null && !ModConfig.ShowOtherModels)
		{
			Diag("model is null");
			return;
		}
		Character[] array = Object.FindObjectsOfType<Character>();
		Character val = null;
		try
		{
			val = Character.localCharacter;
		}
		catch
		{
		}
		string text = (((Object)(object)val == (Object)null) ? "<none>" : ((Object)((Component)val).gameObject).name);
		if (array.Length != _lastCount || text != _lastLocal || Time.unscaledTime >= _nextDiag)
		{
			_nextDiag = Time.unscaledTime + 5f;
			if (array.Length != _lastCount || text != _lastLocal)
			{
				_lastCount = array.Length;
				_lastLocal = text;
				StringBuilder stringBuilder = new StringBuilder();
				for (int i = 0; i < array.Length && i < 8; i++)
				{
					stringBuilder.Append(' ').Append(((Object)(object)array[i] == (Object)null) ? "<null>" : ((Object)((Component)array[i]).gameObject).name);
				}
				Diag("characters=" + array.Length + " local=" + text + " names:" + stringBuilder);
			}
		}
		foreach (Character val2 in array)
		{
			if ((Object)(object)val2 == (Object)null)
			{
				continue;
			}
			bool flag = IsLocal(val2);
			if (!flag && !ModConfig.ReplaceOthers && !ModConfig.ShowOtherModels)
			{
				continue;
			}
			if (!PickModel(val2, flag, out var want, out var modelId))
			{
				DropAvatar(val2);
				continue;
			}
			VrmAvatar vrmAvatar = FindAvatar(val2);
			if ((Object)(object)vrmAvatar != (Object)null)
			{
				if (vrmAvatar.SourceModel == want && vrmAvatar.ModelId == modelId)
				{
					continue;
				}
				Plugin.Log("[net] " + ((Object)((Component)val2).gameObject).name + " switched to " + (modelId ?? "?") + " — rebuilding avatar");
				_avatars.Remove(vrmAvatar);
				DestroyAvatar(vrmAvatar);
			}
			try
			{
				Attach(val2, want, modelId);
			}
			catch (Exception ex)
			{
				Plugin.Err("attach failed on " + ((Object)val2).name + ": " + ex);
			}
		}
		for (int num = _avatars.Count - 1; num >= 0; num--)
		{
			VrmAvatar vrmAvatar2 = _avatars[num];
			if ((Object)(object)vrmAvatar2 == (Object)null || (Object)(object)vrmAvatar2.Character == (Object)null)
			{
				if ((Object)(object)vrmAvatar2 != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)vrmAvatar2).gameObject);
				}
				_avatars.RemoveAt(num);
			}
			else
			{
				vrmAvatar2.ReapplyHiding();
				if (!vrmAvatar2.IsLocalPlayer)
				{
					Tune live = PickTune(vrmAvatar2);
					if (!live.Same(vrmAvatar2.Live))
					{
						Plugin.Log("[net] " + ((Object)((Component)vrmAvatar2.Character).gameObject).name + " tune " + live.Describe());
						vrmAvatar2.Live = live;
					}
				}
				if (!vrmAvatar2.DiagDone && Time.unscaledTime > vrmAvatar2.DiagAt)
				{
					vrmAvatar2.DiagDone = true;
					Diag(vrmAvatar2.Diagnose());
				}
			}
		}
	}

	private VrmAvatar FindAvatar(Character c)
	{
		for (int i = 0; i < _avatars.Count; i++)
		{
			VrmAvatar vrmAvatar = _avatars[i];
			if ((Object)(object)vrmAvatar != (Object)null && (Object)(object)vrmAvatar.Character == (Object)(object)c)
			{
				return vrmAvatar;
			}
		}
		return null;
	}

	private static bool IsLocal(Character c)
	{
		try
		{
			return (Object)(object)Character.localCharacter == (Object)(object)c;
		}
		catch
		{
			return false;
		}
	}

	private void Attach(Character c, VrmModel model, string modelId)
	{
		//IL_1273: Unknown result type (might be due to invalid IL or missing references)
		//IL_1274: Unknown result type (might be due to invalid IL or missing references)
		//IL_14c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_14c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: 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)
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_1361: Unknown result type (might be due to invalid IL or missing references)
		//IL_1366: Unknown result type (might be due to invalid IL or missing references)
		//IL_136a: Unknown result type (might be due to invalid IL or missing references)
		//IL_1384: Unknown result type (might be due to invalid IL or missing references)
		//IL_13a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_13b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_13cc: 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)
		//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0fa4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0faa: Invalid comparison between Unknown and I4
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_1077: Unknown result type (might be due to invalid IL or missing references)
		//IL_107c: Unknown result type (might be due to invalid IL or missing references)
		//IL_1631: Unknown result type (might be due to invalid IL or missing references)
		//IL_1636: Unknown result type (might be due to invalid IL or missing references)
		//IL_10b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_10c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_10d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_10de: Unknown result type (might be due to invalid IL or missing references)
		//IL_10eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_10f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_1102: Unknown result type (might be due to invalid IL or missing references)
		//IL_1148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0372: 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_037e: Unknown result type (might be due to invalid IL or missing references)
		//IL_173e: Unknown result type (might be due to invalid IL or missing references)
		//IL_174c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0632: Unknown result type (might be due to invalid IL or missing references)
		//IL_0637: Unknown result type (might be due to invalid IL or missing references)
		//IL_063e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0643: Unknown result type (might be due to invalid IL or missing references)
		//IL_0648: Unknown result type (might be due to invalid IL or missing references)
		//IL_064a: Unknown result type (might be due to invalid IL or missing references)
		//IL_064f: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_0506: Unknown result type (might be due to invalid IL or missing references)
		//IL_050b: Unknown result type (might be due to invalid IL or missing references)
		//IL_050f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0514: Unknown result type (might be due to invalid IL or missing references)
		//IL_0525: Unknown result type (might be due to invalid IL or missing references)
		//IL_0530: Unknown result type (might be due to invalid IL or missing references)
		//IL_0547: Unknown result type (might be due to invalid IL or missing references)
		//IL_054c: Unknown result type (might be due to invalid IL or missing references)
		//IL_039b: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0690: Unknown result type (might be due to invalid IL or missing references)
		//IL_0692: Unknown result type (might be due to invalid IL or missing references)
		//IL_0694: Unknown result type (might be due to invalid IL or missing references)
		//IL_0699: Unknown result type (might be due to invalid IL or missing references)
		//IL_069e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0702: Unknown result type (might be due to invalid IL or missing references)
		//IL_0704: Unknown result type (might be due to invalid IL or missing references)
		//IL_070a: Unknown result type (might be due to invalid IL or missing references)
		//IL_070c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0712: Unknown result type (might be due to invalid IL or missing references)
		//IL_0714: Unknown result type (might be due to invalid IL or missing references)
		//IL_071c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0721: Unknown result type (might be due to invalid IL or missing references)
		//IL_0729: Unknown result type (might be due to invalid IL or missing references)
		//IL_072e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0906: Unknown result type (might be due to invalid IL or missing references)
		//IL_0912: Unknown result type (might be due to invalid IL or missing references)
		//IL_0917: Unknown result type (might be due to invalid IL or missing references)
		//IL_091c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0753: Unknown result type (might be due to invalid IL or missing references)
		//IL_0745: Unknown result type (might be due to invalid IL or missing references)
		//IL_0974: Unknown result type (might be due to invalid IL or missing references)
		//IL_0985: Unknown result type (might be due to invalid IL or missing references)
		//IL_098a: Unknown result type (might be due to invalid IL or missing references)
		//IL_098f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0938: Unknown result type (might be due to invalid IL or missing references)
		//IL_093d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0942: Unknown result type (might be due to invalid IL or missing references)
		//IL_0944: Unknown result type (might be due to invalid IL or missing references)
		//IL_0949: Unknown result type (might be due to invalid IL or missing references)
		//IL_094d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0952: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cda: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cdf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ce3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0758: Unknown result type (might be due to invalid IL or missing references)
		//IL_09a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_09ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_09b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_09bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_09c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_07b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_07ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0802: Unknown result type (might be due to invalid IL or missing references)
		//IL_0815: Unknown result type (might be due to invalid IL or missing references)
		if (model == null || (Object)(object)model.Root == (Object)null)
		{
			Diag("attach skip: no model for " + ((Object)((Component)c).gameObject).name);
			return;
		}
		CharacterRefs refs = c.refs;
		if (refs == null)
		{
			Diag("attach skip: refs null on " + ((Object)((Component)c).gameObject).name);
			return;
		}
		SkinnedMeshRenderer mainRenderer = refs.mainRenderer;
		if ((Object)(object)mainRenderer == (Object)null || mainRenderer.bones == null || mainRenderer.bones.Length == 0)
		{
			Diag("attach skip: mainRenderer/bones missing on " + ((Object)((Component)c).gameObject).name);
			return;
		}
		if ((Object)(object)refs.customization == (Object)null)
		{
			Diag("attach skip: customization null on " + ((Object)((Component)c).gameObject).name);
			return;
		}
		CustomizationRefs refs2 = refs.customization.refs;
		if ((Object)(object)refs2 == (Object)null)
		{
			Diag("attach skip: CustomizationRefs null on " + ((Object)((Component)c).gameObject).name);
			return;
		}
		Quaternion align = Quaternion.Euler(0f, ModConfig.YawOffset + model.SpecYaw, 0f);
		Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(128);
		Transform[] bones = mainRenderer.bones;
		for (int i = 0; i < bones.Length; i++)
		{
			AddBone(dictionary, bones[i]);
		}
		Transform[] componentsInChildren = ((Component)c).GetComponentsInChildren<Transform>(true);
		for (int j = 0; j < componentsInChildren.Length; j++)
		{
			AddBone(dictionary, componentsInChildren[j]);
		}
		GameObject val = Object.Instantiate<GameObject>(model.Root);
		((Object)val).name = "PhoebeVrm_" + ((Object)((Component)c).gameObject).name;
		val.transform.SetParent(((Component)c).transform, false);
		val.SetActive(true);
		try
		{
			Object.DontDestroyOnLoad((Object)(object)val);
		}
		catch
		{
		}
		SkinnedMeshRenderer[] componentsInChildren2 = val.GetComponentsInChildren<SkinnedMeshRenderer>(true);
		Renderer[] componentsInChildren3 = val.GetComponentsInChildren<Renderer>(true);
		List<Renderer> list = new List<Renderer>(componentsInChildren3.Length);
		foreach (Renderer val2 in componentsInChildren3)
		{
			if (!((Object)(object)val2 == (Object)null) && !(val2 is ParticleSystemRenderer) && !(val2 is LineRenderer) && !(val2 is TrailRenderer))
			{
				list.Add(val2);
			}
		}
		componentsInChildren3 = list.ToArray();
		Dictionary<string, Transform> dictionary2 = new Dictionary<string, Transform>(128);
		foreach (SkinnedMeshRenderer val3 in componentsInChildren2)
		{
			if ((Object)(object)val3 == (Object)null)
			{
				continue;
			}
			Transform[] bones2 = val3.bones;
			if (bones2 == null)
			{
				continue;
			}
			foreach (Transform val4 in bones2)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					string key = Norm(((Object)val4).name);
					if (!dictionary2.ContainsKey(key))
					{
						dictionary2[key] = val4;
					}
				}
			}
		}
		Vector3 modelHipLocal = Vector3.zero;
		bool flag = false;
		if (dictionary2.TryGetValue(Norm("hips"), out var value) && (Object)(object)value != (Object)null)
		{
			flag = true;
			modelHipLocal = val.transform.InverseTransformPoint(value.position);
		}
		Quaternion val5 = Quaternion.Inverse(ModConfig.GameMeshRest);
		Dictionary<string, Quaternion> dictionary3 = new Dictionary<string, Quaternion>(128);
		Dictionary<string, Quaternion> dictionary4 = new Dictionary<string, Quaternion>(128);
		if ((Object)(object)mainRenderer.sharedMesh != (Object)null)
		{
			Matrix4x4[] bindposes = mainRenderer.sharedMesh.bindposes;
			if (bindposes != null)
			{
				int num = Mathf.Min(bindposes.Length, bones.Length);
				for (int n = 0; n < num; n++)
				{
					Transform val6 = bones[n];
					if (!((Object)(object)val6 == (Object)null))
					{
						string key2 = Norm(((Object)val6).name);
						if (!dictionary3.ContainsKey(key2))
						{
							dictionary3[key2] = Glb.ExtractRotation(bindposes[n]) * val5;
						}
						if (!dictionary4.ContainsKey(key2))
						{
							dictionary4[key2] = Glb.ExtractRotation(bindposes[n]);
						}
					}
				}
			}
		}
		Vector3 val8;
		if ((Object)(object)mainRenderer.sharedMesh != (Object)null)
		{
			Matrix4x4[] bindposes2 = mainRenderer.sharedMesh.bindposes;
			if (bindposes2 != null)
			{
				int num2 = Mathf.Min(bindposes2.Length, bones.Length);
				for (int num3 = 0; num3 < num2; num3++)
				{
					if (!((Object)(object)bones[num3] == (Object)null))
					{
						string text = Norm(((Object)bones[num3]).name);
						if (!(text != "hip") || !(text != "head") || !(text != "handl") || !(text != "handr") || !(text != "arml") || !(text != "legl") || !(text != "footl") || !(text != "toe1l") || !(text != "kneel") || !(text != "torso"))
						{
							Quaternion val7 = Glb.ExtractRotation(bindposes2[num3]);
							object[] obj2 = new object[6]
							{
								((Object)bones[num3]).name,
								((Matrix4x4)(ref bindposes2[num3])).determinant,
								null,
								null,
								null,
								null
							};
							val8 = ((Quaternion)(ref val7)).eulerAngles;
							obj2[2] = ((Vector3)(ref val8)).ToString("F1");
							Quaternion rotation = ((Matrix4x4)(ref bindposes2[num3])).rotation;
							val8 = ((Quaternion)(ref rotation)).eulerAngles;
							obj2[3] = ((Vector3)(ref val8)).ToString("F1");
							obj2[4] = Quaternion.Angle(val7, ((Matrix4x4)(ref bindposes2[num3])).rotation);
							val8 = bones[num3].lossyScale;
							obj2[5] = ((Vector3)(ref val8)).ToString("F2");
							Plugin.Log(string.Format("[bp] {0} det={1:F3} mine={2} unity={3} angErr={4:F1} boneLossy={5}", obj2));
						}
					}
				}
			}
		}
		List<BoneBinding> list2 = new List<BoneBinding>(32);
		StringBuilder stringBuilder = new StringBuilder();
		for (int num4 = 0; num4 < BonePairs.Pairs.GetLength(0); num4++)
		{
			string text2 = BonePairs.Pairs[num4, 0];
			string text3 = BonePairs.Pairs[num4, 1];
			if (!dictionary.TryGetValue(Norm(text2), out var value2))
			{
				stringBuilder.Append(" game:").Append(text2);
				continue;
			}
			if (!dictionary2.TryGetValue(Norm(text3), out var value3))
			{
				stringBuilder.Append(" vrm:").Append(text3);
				continue;
			}
			if (!dictionary3.TryGetValue(Norm(text2), out var value4))
			{
				stringBuilder.Append(" bind:").Append(text2);
				continue;
			}
			Quaternion val9 = Quaternion.Inverse(val.transform.rotation) * value3.rotation;
			Quaternion lG = Quaternion.identity;
			bool flag2 = false;
			if (dictionary4.TryGetValue(Norm(text2), out var value5))
			{
				Transform parent = value2.parent;
				if ((Object)(object)parent != (Object)null && dictionary4.TryGetValue(Norm(((Object)parent).name), out var value6))
				{
					lG = value6 * Quaternion.Inverse(value5);
					flag2 = true;
				}
			}
			bool flag3 = Norm(text2) == "head" || Norm(text3) == "head";
			if (flag3 && !flag2)
			{
				Plugin.Log("[head] no bind partner for " + text2 + " -> the head stays on aim");
			}
			list2.Add(new BoneBinding
			{
				Game = value2,
				Our = value3,
				K = value4,
				W0 = val9,
				LG = lG,
				LO = value3.localRotation,
				GRest = value2.rotation,
				GPRest = (((Object)(object)value2.parent != (Object)null) ? value2.parent.rotation : value2.rotation),
				IsHead = flag3,
				Local = (!flag3 && flag2 && ModConfig.Retarget == "local")
			});
			if (num4 == 2 || num4 == 6 || num4 == 8)
			{
				Quaternion value7;
				string text4 = (dictionary4.TryGetValue(Norm(text2), out value7) ? VrmAvatar.Q(value7) : "?");
				Plugin.Log("[rb] " + text2 + " bp=" + text4 + " worldO=" + VrmAvatar.Q(value3.rotation) + " w0=" + VrmAvatar.Q(val9) + " bpGForMeshRest=" + VrmAvatar.Q(ModConfig.GameMeshRest));
			}
		}
		for (int num5 = 0; num5 < list2.Count; num5++)
		{
			BoneBinding boneBinding = list2[num5];
			if ((Object)(object)boneBinding.Game == (Object)null || (Object)(object)boneBinding.Our == (Object)null)
			{
				continue;
			}
			for (int num6 = 0; num6 < list2.Count; num6++)
			{
				BoneBinding boneBinding2 = list2[num6];
				if ((Object)(object)boneBinding2.Game != (Object)null && (Object)(object)boneBinding2.Our != (Object)null && (Object)(object)boneBinding2.Game.parent == (Object)(object)boneBinding.Game)
				{
					boneBinding.GameChild = boneBinding2.Game;
					boneBinding.OurChild = boneBinding2.Our;
					break;
				}
			}
			if ((Object)(object)boneBinding.OurChild != (Object)null)
			{
				Vector3 val10 = boneBinding.OurChild.position - boneBinding.Our.position;
				if (((Vector3)(ref val10)).sqrMagnitude > 1E-08f)
				{
					val8 = Quaternion.Inverse(boneBinding.Our.rotation) * val10;
					boneBinding.RestDirLocal = ((Vector3)(ref val8)).normalized;
				}
			}
			else if ((Object)(object)boneBinding.Our.parent != (Object)null)
			{
				Vector3 val11 = boneBinding.Our.position - boneBinding.Our.parent.position;
				if (((Vector3)(ref val11)).sqrMagnitude > 1E-08f)
				{
					val8 = Quaternion.Inverse(boneBinding.Our.rotation) * val11;
					boneBinding.RestDirLocal = ((Vector3)(ref val8)).normalized;
					boneBinding.AimSelf = true;
				}
			}
			string text5 = (((Object)(object)boneBinding.Our != (Object)null) ? ((Object)boneBinding.Our).name.ToLowerInvariant() : "");
			boneBinding.ArmsDown = ModConfig.ArmsDown && text5.Contains("upper_arm");
			boneBinding.ArmsDownSide = (text5.EndsWith(".l") ? (-1f) : 1f);
			boneBinding.NoAim = text5.StartsWith("foot.") || text5.StartsWith("toes.");
			boneBinding.ReachArm = text5 == "upper_arm.r" || text5 == "lower_arm.r";
			boneBinding.Aim = ModConfig.Retarget == "aim" && ((Object)(object)boneBinding.GameChild != (Object)null || boneBinding.AimSelf) && ((Vector3)(ref boneBinding.RestDirLocal)).sqrMagnitude > 0.5f && text5 != "head";
		}
		StringBuilder stringBuilder2 = new StringBuilder("[aim]");
		int num7 = 0;
		int num8 = 0;
		int num9 = 0;
		int num10 = 0;
		for (int num11 = 0; num11 < list2.Count; num11++)
		{
			BoneBinding boneBinding3 = list2[num11];
			if ((Object)(object)boneBinding3.Our == (Object)null)
			{
				continue;
			}
			if (boneBinding3.Aim)
			{
				if (boneBinding3.AimSelf)
				{
					num8++;
				}
				else
				{
					num7++;
				}
			}
			else if (boneBinding3.Local)
			{
				num10++;
			}
			else
			{
				num9++;
			}
		}
		stringBuilder2.Append(" child=").Append(num7).Append(" self=")
			.Append(num8)
			.Append(" delta=")
			.Append(num10)
			.Append(" world=")
			.Append(num9);
		stringBuilder2.Append(" armsDown=").Append(ModConfig.ArmsDown ? "on" : "off");
		stringBuilder2.Append(" fpRadius=").Append(ModConfig.FirstPersonRadius.ToString("F2"));
		stringBuilder2.Append(" |");
		for (int num12 = 0; num12 < list2.Count; num12++)
		{
			BoneBinding boneBinding4 = list2[num12];
			if (!((Object)(object)boneBinding4.Our == (Object)null))
			{
				stringBuilder2.Append(' ').Append(((Object)boneBinding4.Our).name).Append('=')
					.Append(boneBinding4.IsHead ? "head-euler" : ((!boneBinding4.Aim) ? (boneBinding4.Local ? "delta" : "world") : (boneBinding4.AimSelf ? "self" : "aim")));
			}
		}
		Plugin.Log(stringBuilder2.ToString());
		LogRestArm(dictionary2, "upper_arm.L", "lower_arm.L", "L");
		LogRestArm(dictionary2, "upper_arm.R", "lower_arm.R", "R");
		Bounds bounds = ((Renderer)mainRenderer).bounds;
		float y = ((Bounds)(ref bounds)).size.y;
		float height = model.Height;
		float num13 = ((ModConfig.AutoFit && y > 0.2f && height > 0.2f) ? (y / height) : 1f);
		float num14 = ModConfig.Scale * num13;
		List<Renderer> list3 = new List<Renderer>(32);
		AddR(list3, (Renderer)(object)refs2.mainRenderer);
		AddR(list3, (Renderer)(object)refs2.mainRendererShadow);
		AddR(list3, refs2.mouthRenderer);
		AddR(list3, refs2.accessoryRenderer);
		AddR(list3, refs2.shorts);
		AddR(list3, refs2.skirt);
		AddR(list3, refs2.skirtShadow);
		AddR(list3, refs2.shortsShadow);
		AddR(list3, refs2.sashRenderer);
		AddR(list3, refs2.medalRenderer);
		AddR(list3, refs2.blindRenderer);
		AddR(list3, refs2.chickenRenderer);
		AddR(list3, refs2.headShadow);
		AddR(list3, refs2.skeletonRenderer);
		AddRs(list3, refs2.PlayerRenderers);
		AddRs(list3, refs2.EyeRenderers);
		AddRs(list3, refs2.playerHats);
		AddRs(list3, refs2.stoneRenderers);
		AddRs(list3, refs2.AllRenderers);
		if ((Object)(object)refs2.thirdEye != (Object)null)
		{
			AddRs(list3, refs2.thirdEye.GetComponentsInChildren<Renderer>(true));
		}
		if (ModConfig.HideAllRenderers)
		{
			Renderer[] componentsInChildren4 = ((Component)c).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val12 in componentsInChildren4)
			{
				if (!((Object)(object)val12 == (Object)null) && !(val12 is ParticleSystemRenderer) && (!((Object)(object)val != (Object)null) || !((Component)val12).transform.IsChildOf(val.transform)))
				{
					AddR(list3, val12);
				}
			}
		}
		int num16 = 0;
		try
		{
			HideTheBody hideTheBody = refs.hideTheBody;
			if ((Object)(object)hideTheBody != (Object)null)
			{
				if ((Object)(object)hideTheBody.shadowCaster != (Object)null)
				{
					AddRs(list3, hideTheBody.shadowCaster.GetComponentsInChildren<Renderer>(true));
					num16++;
				}
				if ((Object)(object)hideTheBody.shadowCasterHat != (Object)null)
				{
					AddRs(list3, hideTheBody.shadowCasterHat.GetComponentsInChildren<Renderer>(true));
					num16++;
				}
			}
			if ((Object)(object)refs2.hatShadowMeshFilter != (Object)null)
			{
				AddR(list3, ((Component)refs2.hatShadowMeshFilter).GetComponent<Renderer>());
			}
		}
		catch (Exception ex)
		{
			Diag("[shadow] hideTheBody: " + ex.Message);
		}
		try
		{
			Renderer[] componentsInChildren5 = ((Component)c).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val13 in componentsInChildren5)
			{
				if (!((Object)(object)val13 == (Object)null) && !(val13 is ParticleSystemRenderer) && (!((Object)(object)val != (Object)null) || !((Component)val13).transform.IsChildOf(val.transform)))
				{
					Material sharedMaterial = val13.sharedMaterial;
					if ((int)val13.shadowCastingMode == 3 || ((Object)(object)sharedMaterial != (Object)null && ((Object)sharedMaterial).name.StartsWith("Shadow", StringComparison.OrdinalIgnoreCase)))
					{
						AddR(list3, val13);
					}
				}
			}
		}
		catch (Exception ex2)
		{
			Diag("[shadow] sweep: " + ex2.Message);
		}
		int num18 = 0;
		try
		{
			foreach (Renderer val14 in componentsInChildren3)
			{
				if (!((Object)(object)val14 == (Object)null))
				{
					val14.shadowCastingMode = (ShadowCastingMode)0;
					SkinnedMeshRenderer val15 = (SkinnedMeshRenderer)(object)((val14 is SkinnedMeshRenderer) ? val14 : null);
					if (!((Object)(object)val15 == (Object)null) && !((Object)(object)val15.sharedMesh == (Object)null) && !IsTransparent(((Renderer)val15).sharedMaterial))
					{
						GameObject val16 = new GameObject(((Object)val15).name + "_shadow")
						{
							layer = ((Component)val15).gameObject.layer
						};
						Transform val17 = (((Object)(object)((Component)val15).transform.parent != (Object)null) ? ((Component)val15).transform.parent : val.transform);
						val16.transform.SetParent(val17, false);
						val16.transform.localPosition = ((Component)val15).transform.localPosition;
						val16.transform.localRotation = ((Component)val15).transform.localRotation;
						val16.transform.localScale = ((Component)val15).transform.localScale;
						SkinnedMeshRenderer obj3 = val16.AddComponent<SkinnedMeshRenderer>();
						obj3.sharedMesh = val15.sharedMesh;
						obj3.bones = val15.bones;
						obj3.rootBone = val15.rootBone;
						((Renderer)obj3).sharedMaterial = ((Renderer)val15).sharedMaterial;
						((Renderer)obj3).localBounds = ((Renderer)val15).localBounds;
						obj3.updateWhenOffscreen = true;
						((Renderer)obj3).receiveShadows = false;
						((Renderer)obj3).shadowCastingMode = (ShadowCastingMode)3;
						num18++;
					}
				}
			}
		}
		catch (Exception ex3)
		{
			Diag("[shadow] proxy: " + ex3.Message);
		}
		Plugin.Log("[shadow] fake casters hidden=" + num16 + " (hide list " + list3.Count + ") | ours " + componentsInChildren3.Length + ": cast=Off, shadowsOnly=" + num18);
		VrmAvatar vrmAvatar = val.AddComponent<VrmAvatar>();
		vrmAvatar.ModelRoot = val.transform;
		vrmAvatar.GameRef = ((Component)mainRenderer).transform;
		vrmAvatar.GameRoot = ((Component)c).transform;
		vrmAvatar.GameSignal = (Renderer)(object)mainRenderer;
		vrmAvatar.HideWithGame = ModConfig.HideWithGameBody;
		vrmAvatar.OurRenderers = componentsInChildren3;
		vrmAvatar.Hidden = list3.ToArray();
		vrmAvatar.Bindings = list2.ToArray();
		vrmAvatar.CaptureRest();
		vrmAvatar.Align = align;
		vrmAvatar.AutoFitScale = num13;
		vrmAvatar.Scale = num14;
		vrmAvatar.YOffset = ModConfig.YOffset;
		vrmAvatar.AnchorToHip = ModConfig.AnchorToHip;
		vrmAvatar.OffsetForward = ModConfig.OffsetForward;
		vrmAvatar.OffsetRight = ModConfig.OffsetRight;
		vrmAvatar.IsLocalPlayer = IsLocal(c);
		vrmAvatar.ActorNumber = ActorOf(c);
		vrmAvatar.Character = c;
		vrmAvatar.SourceModel = model;
		vrmAvatar.ModelId = modelId;
		vrmAvatar.Live = (vrmAvatar.IsLocalPlayer ? Tune.FromConfig() : PickTune(vrmAvatar));
		if (dictionary2.TryGetValue(Norm("head"), out var value8))
		{
			vrmAvatar.HeadBone = value8;
		}
		if (dictionary2.TryGetValue(Norm("hips"), out var value9))
		{
			vrmAvatar.HipBone = value9;
		}
		vrmAvatar.DiagAt = Time.unscaledTime + 2f;
		float num20 = 0f;
		try
		{
			Bounds bounds2 = ((Renderer)mainRenderer).bounds;
			num20 = ((Bounds)(ref bounds2)).min.y - model.MinY * num14 - ((Component)c).transform.position.y;
			Plugin.Log($"[anchor] gameBounds y={((Bounds)(ref bounds2)).min.y:F3}..{((Bounds)(ref bounds2)).max.y:F3} rootY={((Component)c).transform.position.y:F3} ourMinY={model.MinY:F3} anchorY={num20:F3}");
		}
		catch (Exception ex4)
		{
			Plugin.Log("[anchor] bounds failed: " + ex4.Message);
		}
		vrmAvatar.AnchorY = num20;
		Transform val18 = null;
		try
		{
			if ((Object)(object)refs.hip != (Object)null)
			{
				val18 = refs.hip.transform;
			}
		}
		catch
		{
		}
		if ((Object)(object)val18 == (Object)null && dictionary.TryGetValue("hip", out var value10))
		{
			val18 = value10;
		}
		vrmAvatar.GameHip = val18;
		try
		{
			if ((Object)(object)refs.head != (Object)null)
			{
				vrmAvatar.GameHead = refs.head.transform;
			}
		}
		catch
		{
		}
		if ((Object)(object)vrmAvatar.GameHead == (Object)null && dictionary.TryGetValue("head", out var value11))
		{
			vrmAvatar.GameHead = value11;
		}
		vrmAvatar.HasModelHip = flag;
		vrmAvatar.ModelHipLocal = modelHipLocal;
		try
		{
			if ((Object)(object)refs.rigCreator != (Object)null)
			{
				vrmAvatar.GameRig = ((Component)refs.rigCreator).transform;
			}
		}
		catch
		{
		}
		if ((Object)(object)vrmAvatar.GameRig == (Object)null && (Object)(object)vrmAvatar.GameHip != (Object)null)
		{
			Transform val19 = vrmAvatar.GameHip;
			for (int num21 = 0; num21 < 16; num21++)
			{
				if (!((Object)(object)val19.parent != (Object)null))
				{
					break;
				}
				if (!((Object)(object)val19.parent != (Object)(object)((Component)c).transform))
				{
					break;
				}
				val19 = val19.parent;
			}
			if ((Object)(object)val19 != (Object)null && (Object)(object)val19.parent == (Object)(object)((Component)c).transform)
			{
				vrmAvatar.GameRig = val19;
			}
		}
		Plugin.Log("[body] rig=" + (((Object)(object)vrmAvatar.GameRig == (Object)null) ? "NULL" : ((Object)vrmAvatar.GameRig).name) + " yaw=" + vrmAvatar.BodyYaw().ToString("F0") + " (absolute heading; 0 = world +Z, the face follows this)");
		object[] obj7 = new object[4]
		{
			((Object)(object)val18 == (Object)null) ? "NULL" : ((Object)val18).name,
			flag ? "yes" : "no",
			((Vector3)(ref modelHipLocal)).ToString("F3"),
			null
		};
		val8 = ((Component)c).transform.position;
		obj7[3] = ((Vector3)(ref val8)).ToString("F2");
		Plugin.Log(string.Format("[anchor] hip={0} ourHips={1} local=({2}) charRoot=({3})", obj7));
		vrmAvatar.Sort();
		vrmAvatar.ReapplyHiding();
		_avatars.Add(vrmAvatar);
		Plugin.Log(string.Format("[attach] {0} local={1} bones={2}/{3} scale={4:F3} (gameH={5:F3} ourH={6:F3}) hidden={7} ourRenderers={8}{9}", ((Object)((Component)c).gameObject).name, IsLocal(c), list2.Count, BonePairs.Pairs.GetLength(0), num14, y, height, list3.Count, componentsInChildren2.Length, (stringBuilder.Length > 0) ? (" missing:" + stringBuilder) : ""));
		float num22 = 0f;
		string arg = "-";
		StringBuilder stringBuilder3 = new StringBuilder();
		for (int num23 = 0; num23 < list2.Count; num23++)
		{
			float num24 = Quaternion.Angle(Quaternion.identity, list2[num23].W0);
			if (num24 > num22)
			{
				num22 = num24;
				arg = ((Object)list2[num23].Our).name;
			}
			if (num24 >= 1f)
			{
				stringBuilder3.AppendFormat("{0}={1:F0} ", ((Object)list2[num23].Our).name, num24);
			}
		}
		Plugin.Log(string.Format("[rest] W0 max={0:F1}@ {1} affected={2}", num22, arg, (stringBuilder3.Length == 0) ? "none (identity-rest model, W0 is a no-op)" : stringBuilder3.ToString()));
	}

	private static void Diag(string msg)
	{
		Plugin.Log("[scan] " + msg);
	}

	private static void AddBone(Dictionary<string, Transform> dict, Transform t)
	{
		if (!((Object)(object)t == (Object)null))
		{
			string key = Norm(((Object)t).name);
			if (!dict.ContainsKey(key))
			{
				dict[key] = t;
			}
		}
	}

	private static void AddR(List<Renderer> list, Renderer r)
	{
		if ((Object)(object)r == (Object)null)
		{
			return;
		}
		for (int i = 0; i < list.Count; i++)
		{
			if ((Object)(object)list[i] == (Object)(object)r)
			{
				return;
			}
		}
		list.Add(r);
	}

	private static void AddRs(List<Renderer> list, Renderer[] rs)
	{
		if (rs != null)
		{
			for (int i = 0; i < rs.Length; i++)
			{
				AddR(list, rs[i]);
			}
		}
	}

	private static bool IsTransparent(Material m)
	{
		if ((Object)(object)m == (Object)null)
		{
			return true;
		}
		if (m.renderQueue >= 3000)
		{
			return true;
		}
		if (m.HasProperty("_Surface") && m.GetFloat("_Surface") > 0.5f)
		{
			return true;
		}
		return false;
	}

	public static string Norm(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return "";
		}
		StringBuilder stringBuilder = new StringBuilder(s.Length);
		for (int i = 0; i < s.Length; i++)
		{
			char c = s[i];
			if (c >= 'A' && c <= 'Z')
			{
				c = (char)(c + 32);
			}
			if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
			{
				stringBuilder.Append(c);
			}
		}
		return stringBuilder.ToString();
	}

	private static void LogRestArm(Dictionary<string, Transform> bones, string a, string b, string side)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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)
		if (bones.TryGetValue(Norm(a), out var value) && bones.TryGetValue(Norm(b), out var value2) && !((Object)(object)value == (Object)null) && !((Object)(object)value2 == (Object)null))
		{
			Vector3 val = value2.position - value.position;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (!(magnitude < 1E-06f))
			{
				Plugin.Log($"[rest] {side} {a}->{b}: elev={Mathf.Asin(Mathf.Clamp(val.y / magnitude, -1f, 1f)) * 57.29578f:F1} deg len={magnitude:F3}");
			}
		}
	}
}
internal static class CameraVisibility
{
	private static readonly List<VrmAvatar> Avatars = new List<VrmAvatar>();

	private static bool _hooked;

	private static int _applyCount;

	public static void Register(VrmAvatar a)
	{
		if (!((Object)(object)a == (Object)null))
		{
			EnsureHook();
			if (!Avatars.Contains(a))
			{
				Avatars.Add(a);
			}
		}
	}

	public static void Unregister(VrmAvatar a)
	{
		if (!((Object)(object)a == (Object)null))
		{
			Avatars.Remove(a);
		}
	}

	private static void EnsureHook()
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		if (_hooked)
		{
			return;
		}
		_hooked = true;
		try
		{
			RenderPipelineManager.beginCameraRendering += OnBeginCamera;
			Plugin.Log("[mirror] per-camera visibility hook installed (" + (((Object)(object)GraphicsSettings.currentRenderPipeline != (Object)null) ? "SRP" : "built-in") + ")");
		}
		catch (Exception ex)
		{
			Plugin.Log("[mirror] beginCameraRendering hook failed: " + ex.Message);
		}
		try
		{
			Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnPreCull));
		}
		catch (Exception ex2)
		{
			Plugin.Log("[mirror] onPreCull hook failed: " + ex2.Message);
		}
	}

	private static void OnBeginCamera(ScriptableRenderContext context, Camera cam)
	{
		Apply(cam);
	}

	private static void OnPreCull(Camera cam)
	{
		if ((Object)(object)GraphicsSettings.currentRenderPipeline == (Object)null)
		{
			Apply(cam);
		}
	}

	public static string Audit(Renderer[] ours, Renderer gameSignal)
	{
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		StringBuilder stringBuilder = new StringBuilder();
		int num = -1;
		if (ours != null)
		{
			for (int i = 0; i < ours.Length; i++)
			{
				if ((Object)(object)ours[i] != (Object)null)
				{
					num = ((Component)ours[i]).gameObject.layer;
					break;
				}
			}
		}
		int num2 = (((Object)(object)gameSignal != (Object)null) ? ((Component)gameSignal).gameObject.layer : (-1));
		Camera main = Camera.main;
		stringBuilder.AppendFormat("[mirror] ourLayer={0}({1}) gameBodyLayer={2}({3}) mainCam={4} mainMask=0x{5:X8}\n", num, LayerMask.LayerToName(num), num2, LayerMask.LayerToName(num2), ((Object)(object)main == (Object)null) ? "null" : ((Object)main).name, (!((Object)(object)main == (Object)null)) ? main.cullingMask : 0);
		Camera[] array = Resources.FindObjectsOfTypeAll<Camera>();
		foreach (Camera val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				int cullingMask = val.cullingMask;
				stringBuilder.AppendFormat("[mirror] cam '{0}' enabled={1} active={2} depth={3:F2} type={4} mask=0x{5:X8} seesOurs={6} seesBody={7} rt={8}\n", ((Object)val).name, ((Behaviour)val).enabled, ((Behaviour)val).isActiveAndEnabled, val.depth, val.cameraType, cullingMask, (num < 0) ? "?" : ((cullingMask & (1 << num)) != 0).ToString(), (num2 < 0) ? "?" : ((cullingMask & (1 << num2)) != 0).ToString(), ((Object)(object)val.targetTexture == (Object)null) ? "-" : ((Object)val.targetTexture).name);
			}
		}
		return stringBuilder.ToString();
	}

	public static string AlignLayer(Renderer[] ours, Renderer gameSignal)
	{
		if (ours == null || (Object)(object)gameSignal == (Object)null)
		{
			return "[mirror] align skipped: no avatar or no game body";
		}
		int num = -1;
		for (int i = 0; i < ours.Length; i++)
		{
			if ((Object)(object)ours[i] != (Object)null)
			{
				num = ((Component)ours[i]).gameObject.layer;
				break;
			}
		}
		if (num < 0)
		{
			return "[mirror] align skipped: no renderer";
		}
		int layer = ((Component)gameSignal).gameObject.layer;
		if (num == layer)
		{
			return "[mirror] align ok: already on body layer " + layer;
		}
		Camera main = Camera.main;
		if ((Object)(object)main != (Object)null && (main.cullingMask & (1 << layer)) == 0)
		{
			return "[mirror] align refused: layer " + layer + " not in main camera mask";
		}
		int num2 = 0;
		for (int j = 0; j < ours.Length; j++)
		{
			if (!((Object)(object)ours[j] == (Object)null))
			{
				GameObject gameObject = ((Component)ours[j]).gameObject;
				if ((Object)(object)gameObject != (Object)null && gameObject.layer != layer)
				{
					gameObject.layer = layer;
					num2++;
				}
			}
		}
		return "[mirror] align layer " + num + " -> " + layer + " on " + num2 + " renderer(s)";
	}

	private static void Apply(Camera cam)
	{
		for (int num = Avatars.Count - 1; num >= 0; num--)
		{
			VrmAvatar vrmAvatar = Avatars[num];
			if ((Object)(object)vrmAvatar == (Object)null)
			{
				Avatars.RemoveAt(num);
			}
			else
			{
				try
				{
					vrmAvatar.ApplyVisibilityFor(cam);
				}
				catch (Exception ex)
				{
					if (_applyCount++ < 3)
					{
						Plugin.Log("[mirror] apply failed: " + ex.Message);
					}
				}
			}
		}
	}
}
internal static class FreeMouse
{
	private static float _nextTick;

	private static bool _wasActive;

	public static bool IsActive()
	{
		SkinPanel instance = SkinPanel.Instance;
		if ((Object)(object)instance != (Object)null && instance.Visible)
		{
			return true;
		}
		ModelEditor instance2 = ModelEditor.Instance;
		if ((Object)(object)instance2 != (Object)null)
		{
			return instance2.Visible;
		}
		return false;
	}

	internal static void Telemetry()
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		bool flag = IsActive();
		if (flag != _wasActive)
		{
			_wasActive = flag;
			Plugin.Log("[free] " + (flag ? "cursor freed" : "cursor restored") + " lock=" + ((object)Cursor.lockState/*cast due to .constrained prefix*/).ToString() + " visible=" + Cursor.visible);
		}
		if (flag && Time.unscaledTime > _nextTick)
		{
			_nextTick = Time.unscaledTime + 5f;
			Vector3 mousePosition = Input.mousePosition;
			Plugin.Log("[free] holding: lock=" + ((object)Cursor.lockState/*cast due to .constrained prefix*/).ToString() + " visible=" + Cursor.visible + " mouse=" + (int)mousePosition.x + "," + (int)mousePosition.y + " screen=" + Screen.width + "x" + Screen.height);
		}
	}
}
internal sealed class FreeMouseProbe : MonoBehaviour
{
	private void Update()
	{
		FreeMouse.Telemetry();
	}
}
[HarmonyPatch(typeof(CursorHandler), "Update")]
internal static class PatchCursorHandlerUpdate
{
	private static void Postfix()
	{
		if (FreeMouse.IsActive())
		{
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
		}
	}
}
[HarmonyPatch(typeof(CharacterMovement), "CameraLook")]
internal static class PatchCameraLook
{
	private static bool Prefix()
	{
		return !FreeMouse.IsActive();
	}
}
[HarmonyPatch(typeof(CharacterInput), "Sample")]
internal static class PatchCharacterInputSample
{
	private static void Postfix(CharacterInput __instance)
	{
		//IL_0009: 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)
		if (FreeMouse.IsActive())
		{
			__instance.lookInput = Vector2.zero;
			__instance.scrollInput = 0f;
			__instance.pauseWasPressed = false;
			__instance.jumpWasPressed = false;
			__instance.jumpIsPressed = false;
			__instance.interactWasPressed = false;
			__instance.interactWasReleased = false;
			__instance.dropWasPressed = false;
			__instance.dropIsPressed = false;
			__instance.dropWasReleased = false;
			__instance.usePrimaryWasPressed = false;
			__instance.usePrimaryIsPressed = false;
			__instance.usePrimaryWasReleased = false;
			__instance.useSecondaryWasPressed = false;
			__instance.useSecondaryIsPressed = false;
			__instance.useSecondaryWasReleased = false;
			__instance.pingWasPressed = false;
			__instance.selectBackpackWasPressed = false;
			__instance.unselectSlotWasPressed = false;
			__instance.selectSlotForwardWasPressed = false;
			__instance.selectSlotBackwardWasPressed = false;
			__instance.spectateLeftWasPressed = false;
			__instance.spectateRightWasPressed = false;
		}
	}
}
[HarmonyPatch(typeof(CharacterInput), "HotbarKeyWasPressed")]
internal static class PatchHotbarKeyWasPressed
{
	private static bool Prefix(ref bool __result)
	{
		if (!FreeMouse.IsActive())
		{
			return true;
		}
		__result = false;
		return false;
	}
}
[HarmonyPatch(typeof(CharacterInput), "HotbarKeyIsPressed")]
internal static class PatchHotbarKeyIsPressed
{
	private static bool Prefix(ref bool __result)
	{
		if (!FreeMouse.IsActive())
		{
			return true;
		}
		__result = false;
		return false;
	}
}
internal sealed class GlbAccessor
{
	public int BufferView = -1;

	public int ByteOffset;

	public int ComponentType;

	public int Count;

	public int NumComp;

	public int Stride;

	public bool Normalized;

	public string Type;
}
internal sealed class GlbView
{
	public int ByteOffset;

	public int ByteLength;

	public int Stride;
}
internal sealed class Glb
{
	public JNode Root;

	public byte[] Bin;

	public readonly List<GlbAccessor> Accessors = new List<GlbAccessor>();

	public readonly List<GlbView> Views = new List<GlbView>();

	public int BufferBase;

	public bool IsVrm1
	{
		get
		{
			try
			{
				JNode jNode = Root["extensions"];
				if (jNode.Has("VRMC_vrm") || jNode.Has("VRMCVrm"))
				{
					return true;
				}
				JNode jNode2 = Root["extensionsUsed"];
				if (jNode2.IsArray)
				{
					for (int i = 0; i < jNode2.Count; i++)
					{
						string str = jNode2[i].Str;
						if (str != null && str.StartsWith("VRMC_vrm"))
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}
	}

	public static Glb Load(byte[] bytes, Action<string> log)
	{
		if (bytes == null || bytes.Length < 20)
		{
			throw new Exception("glb too small: " + ((bytes != null) ? bytes.Length : 0));
		}
		uint num = BitConverter.ToUInt32(bytes, 0);
		if (num != 1179937895)
		{
			throw new Exception("not a GLB: bad magic 0x" + num.ToString("x8"));
		}
		uint num2 = BitConverter.ToUInt32(bytes, 4);
		int num3 = 12;
		string text = null;
		byte[] array = null;
		while (num3 + 8 <= bytes.Length)
		{
			uint num4 = BitConverter.ToUInt32(bytes, num3);
			uint num5 = BitConverter.ToUInt32(bytes, num3 + 4);
			int num6 = num3 + 8;
			int num7 = bytes.Length - num6;
			int num8 = (int)Math.Min(num4, num7);
			if (num8 < 0)
			{
				num8 = 0;
			}
			switch (num5)
			{
			case 1313821514u:
				text = Encoding.UTF8.GetString(bytes, num6, num8);
				break;
			case 5130562u:
				array = new byte[num8];
				Buffer.BlockCopy(bytes, num6, array, 0, num8);
				break;
			}
			num3 = num6 + num8;
		}
		if (text == null)
		{
			throw new Exception("glb: no JSON chunk (version " + num2 + ")");
		}
		Glb glb = new Glb();
		glb.Root = Json.Parse(text);
		glb.Bin = array ?? new byte[0];
		JNode jNode = glb.Root["buffers"];
		if (jNode.Count > 0)
		{
			glb.BufferBase = jNode[0].IntOr("byteOffset", 0);
		}
		JNode jNode2 = glb.Root["bufferViews"];
		for (int i = 0; i < jNode2.Count; i++)
		{
			JNode jNode3 = jNode2[i];
			glb.Views.Add(new GlbView
			{
				ByteOffset = jNode3.IntOr("byteOffset", 0),
				ByteLength = jNode3.IntOr("byteLength", 0),
				Stride = jNode3.IntOr("byteStride", 0)
			});
		}
		JNode jNode4 = glb.Root["accessors"];
		for (int j = 0; j < jNode4.Count; j++)
		{
			JNode jNode5 = jNode4[j];
			GlbAccessor glbAccessor = new GlbAccessor
			{
				BufferView = jNode5.IntOr("bufferView", -1),
				ByteOffset = jNode5.IntOr("byteOffset", 0),
				ComponentType = jNode5.IntOr("componentType", 5126),
				Count = jNode5.IntOr("count", 0),
				Type = jNode5.StringOr("type", "SCALAR"),
				Normalized = jNode5.BoolOr("normalized", dflt: false)
			};
			glbAccessor.NumComp = CompCount(glbAccessor.Type);
			if (glbAccessor.BufferView >= 0 && glbAccessor.BufferView < glb.Views.Count)
			{
				glbAccessor.Stride = glb.Views[glbAccessor.BufferView].Stride;
			}
			glb.Accessors.Add(glbAccessor);
		}
		return glb;
	}

	private static int CompCount(string type)
	{
		return type switch
		{
			"SCALAR" => 1, 
			"VEC2" => 2, 
			"VEC3" => 3, 
			"VEC4" => 4, 
			"MAT2" => 4, 
			"MAT3" => 9, 
			"MAT4" => 16, 
			_ => 1, 
		};
	}

	private static int CompSize(int ct)
	{
		return ct switch
		{
			5120 => 1, 
			5121 => 1, 
			5122 => 2, 
			5123 => 2, 
			5125 => 4, 
			5126 => 4, 
			_ => 4, 
		};
	}

	private int OffsetOf(GlbAccessor a, int elem, int comp)
	{
		int num = CompSize(a.ComponentType);
		int num2 = num * a.NumComp;
		int num3 = ((a.Stride > 0) ? a.Stride : num2);
		GlbView glbView = Views[a.BufferView];
		return BufferBase + glbView.ByteOffset + a.ByteOffset + elem * num3 + comp * num;
	}

	public int CompI(int acc, int elem, int comp)
	{
		GlbAccessor glbAccessor = Accessors[acc];
		int num = OffsetOf(glbAccessor, elem, comp);
		return glbAccessor.ComponentType switch
		{
			5120 => (sbyte)Bin[num], 
			5121 => Bin[num], 
			5122 => BitConverter.ToInt16(Bin, num), 
			5123 => BitConverter.ToUInt16(Bin, num), 
			5125 => (int)BitConverter.ToUInt32(Bin, num), 
			5126 => (int)BitConverter.ToSingle(Bin, num), 
			_ => 0, 
		};
	}

	public float CompF(int acc, int elem, int comp)
	{
		GlbAccessor glbAccessor = Accessors[acc];
		int num = OffsetOf(glbAccessor, elem, comp);
		switch (glbAccessor.ComponentType)
		{
		case 5120:
		{
			sbyte b2 = (sbyte)Bin[num];
			if (!glbAccessor.Normalized)
			{
				return b2;
			}
			return Mathf.Max((float)b2 / 127f, -1f);
		}
		case 5121:
		{
			byte b = Bin[num];
			if (!glbAccessor.Normalized)
			{
				return (int)b;
			}
			return (float)(int)b / 255f;
		}
		case 5122:
		{
			short num3 = BitConverter.ToInt16(Bin, num);
			if (!glbAccessor.Normalized)
			{
				return num3;
			}
			return Mathf.Max((float)num3 / 32767f, -1f);
		}
		case 5123:
		{
			ushort num2 = BitConverter.ToUInt16(Bin, num);
			if (!glbAccessor.Normalized)
			{
				return (int)num2;
			}
			return (float)(int)num2 / 65535f;
		}
		case 5125:
			return BitConverter.ToUInt32(Bin, num);
		case 5126:
			return BitConverter.ToSingle(Bin, num);
		default:
			return 0f;
		}
	}

	public int Count(int acc)
	{
		if (acc < 0 || acc >= Accessors.Count)
		{
			return 0;
		}
		return Accessors[acc].Count;
	}

	public int NumComp(int acc)
	{
		if (acc < 0 || acc >= Accessors.Count)
		{
			return 0;
		}
		return Accessors[acc].NumComp;
	}

	public static Vector3 ToUnityPos(float x, float y, float z)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(x, y, 0f - z);
	}

	public static Vector3 ToUnityDir(float x, float y, float z)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(x, y, 0f - z);
	}

	public static Quaternion ToUnityQuat(float x, float y, float z, float w)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return new Quaternion(0f - x, 0f - y, z, w);
	}

	public static Matrix4x4 FlipZ(Matrix4x4 m)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < 4; i++)
		{
			((Matrix4x4)(ref m))[2, i] = 0f - ((Matrix4x4)(ref m))[2, i];
		}
		for (int j = 0; j < 4; j++)
		{
			((Matrix4x4)(ref m))[j, 2] = 0f - ((Matrix4x4)(ref m))[j, 2];
		}
		return m;
	}

	public Matrix4x4 ReadMatrix(int acc, int elem)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		_ = Accessors[acc];
		Matrix4x4 m = default(Matrix4x4);
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				((Matrix4x4)(ref m))[j, i] = CompF(acc, elem, i * 4 + j);
			}
		}
		return FlipZ(m);
	}

	public Vector3 Vec3At(int acc, int elem)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		return ToUnityPos(CompF(acc, elem, 0), CompF(acc, elem, 1), CompF(acc, elem, 2));
	}

	public static Quaternion ExtractRotation(Matrix4x4 m)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: 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_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: 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_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: 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_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: 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_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(m.m00, m.m10, m.m20);
		Vector3 val2 = default(Vector3);
		((Vector3)(ref val2))..ctor(m.m01, m.m11, m.m21);
		Vector3 val3 = default(Vector3);
		((Vector3)(ref val3))..ctor(m.m02, m.m12, m.m22);
		if (((Vector3)(ref val)).sqrMagnitude < 1E-12f)
		{
			return Quaternion.identity;
		}
		((Vector3)(ref val)).Normalize();
		val2 -= Vector3.Dot(val2, val) * val;
		if (((Vector3)(ref val2)).sqrMagnitude < 1E-10f)
		{
			Vector3 val4 = ((Mathf.Abs(val.y) < 0.9f) ? Vector3.up : Vector3.right);
			val2 = val4 - Vector3.Dot(val4, val) * val;
		}
		((Vector3)(ref val2)).Normalize();
		val3 = Vector3.Cross(val, val2);
		Vector3 val5 = default(Vector3);
		((Vector3)(ref val5))..ctor(m.m02, m.m12, m.m22);
		if (Vector3.Dot(val3, val5) < 0f)
		{
			val3 = -val3;
		}
		Matrix4x4 identity = Matrix4x4.identity;
		((Matrix4x4)(ref identity)).SetColumn(0, Vector4.op_Implicit(val));
		((Matrix4x4)(ref identity)).SetColumn(1, Vector4.op_Implicit(val2));
		((Matrix4x4)(ref identity)).SetColumn(2, Vector4.op_Implicit(val3));
		return ((Matrix4x4)(ref identity)).rotation;
	}
}
internal enum JKind
{
	Null,
	Bool,
	Number,
	String,
	Array,
	Object
}
internal sealed class JNode
{
	public JKind Kind;

	public bool Bool;

	public double Num;

	public string Str;

	public List<JNode> Arr;

	public Dictionary<string, JNode> Obj;

	public static readonly JNode Nil = new JNode();

	public bool IsNull => Kind == JKind.Null;

	public bool IsArray => Kind == JKind.Array;

	public bool IsObject => Kind == JKind.Object;

	public bool IsString => Kind == JKind.String;

	public int Count
	{
		get
		{
			if (Arr == null)
			{
				return 0;
			}
			return Arr.Count;
		}
	}

	public JNode this[string key]
	{
		get
		{
			if (Obj != null && key != null && Obj.TryGetValue(key, out var value) && value != null)
			{
				return value;
			}
			return Nil;
		}
	}

	public JNode this[int index]
	{
		get
		{
			if (Arr != null && index >= 0 && index < Arr.Count && Arr[index] != null)
			{
				return Arr[index];
			}
			return Nil;
		}
	}

	public int I => (int)Num;

	public float F => (float)Num;

	public bool Has(string key)
	{
		if (Obj != null && key != null)
		{
			return Obj.ContainsKey(key);
		}
		return false;
	}

	public int IAt(int i)
	{
		return (int)this[i].Num;
	}

	public float FAt(int i)
	{
		return (float)this[i].Num;
	}

	public int IntOr(string key, int dflt)
	{
		JNode jNode = this[key];
		if (!jNode.IsNull)
		{
			return (int)jNode.Num;
		}
		return dflt;
	}

	public float FloatOr(string key, float dflt)
	{
		JNode jNode = this[key];
		if (!jNode.IsNull)
		{
			return (float)jNode.Num;
		}
		return dflt;
	}

	public bool BoolOr(string key, bool dflt)
	{
		JNode jNode = this[key];
		if (!jNode.IsNull)
		{
			return jNode.Bool;
		}
		return dflt;
	}

	public string StringOr(string key, string dflt)
	{
		JNode jNode = this[key];
		if (jNode.IsNull || jNode.Str == null)
		{
			return dflt;
		}
		return jNode.Str;
	}
}
internal static class Json
{
	public static JNode Parse(string text)
	{
		int i = 0;
		return ParseValue(text, ref i) ?? JNode.Nil;
	}

	private static void SkipWs(string s, ref int i)
	{
		while (i < s.Length)
		{
			char c = s[i];
			if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
			{
				i++;
				continue;
			}
			break;
		}
	}

	private static JNode ParseValue(string s, ref int i)
	{
		SkipWs(s, ref i);
		if (i >= s.Length)
		{
			return JNode.Nil;
		}
		switch (s[i])
		{
		case '{':
			return ParseObject(s, ref i);
		case '[':
			return ParseArray(s, ref i);
		case '"':
			return new JNode
			{
				Kind = JKind.String,
				Str = ParseString(s, ref i)
			};
		case 't':
			i += 4;
			return new JNode
			{
				Kind = JKind.Bool,
				Bool = true
			};
		case 'f':
			i += 5;
			return new JNode
			{
				Kind = JKind.Bool,
				Bool = false
			};
		case 'n':
			i += 4;
			return JNode.Nil;
		default:
			return ParseNumber(s, ref i);
		}
	}

	private static JNode ParseObject(string s, ref int i)
	{
		JNode jNode = new JNode
		{
			Kind = JKind.Object,
			Obj = new Dictionary<string, JNode>(32)
		};
		i++;
		SkipWs(s, ref i);
		if (i < s.Length && s[i] == '}')
		{
			i++;
			return jNode;
		}
		while (i < s.Length)
		{
			SkipWs(s, ref i);
			if (i >= s.Length || s[i] != '"')
			{
				break;
			}
			string key = ParseString(s, ref i);
			SkipWs(s, ref i);
			if (i < s.Length && s[i] == ':')
			{
				i++;
			}
			jNode.Obj[key] = ParseValue(s, ref i);
			SkipWs(s, ref i);
			if (i < s.Length && s[i] == ',')
			{
				i++;
				continue;
			}
			if (i < s.Length && s[i] == '}')
			{
				i++;
			}
			break;
		}
		return jNode;
	}

	private static JNode ParseArray(string s, ref int i)
	{
		JNode jNode = new JNode
		{
			Kind = JKind.Array,
			Arr = new List<JNode>(16)
		};
		i++;
		SkipWs(s, ref i);
		if (i < s.Length && s[i] == ']')
		{
			i++;
			return jNode;
		}
		while (i < s.Length)
		{
			jNode.Arr.Add(ParseValue(s, ref i));
			SkipWs(s, ref i);
			if (i < s.Length && s[i] == ',')
			{
				i++;
				continue;
			}
			if (i < s.Length && s[i] == ']')
			{
				i++;
			}
			break;
		}
		return jNode;
	}

	private static string ParseString(string s, ref int i)
	{
		i++;
		StringBuilder stringBuilder = new StringBuilder(32);
		while (i < s.Length)
		{
			char c = s[i++];
			switch (c)
			{
			default:
				stringBuilder.Append(c);
				continue;
			case '\\':
			{
				if (i >= s.Length)
				{
					break;
				}
				char c2 = s[i++];
				switch (c2)
				{
				case '"':
					stringBuilder.Append('"');
					break;
				case '\\':
					stringBuilder.Append('\\');
					break;
				case '/':
					stringBuilder.Append('/');
					break;
				case 'b':
					stringBuilder.Append('\b');
					break;
				case 'f':
					stringBuilder.Append('\f');
					break;
				case 'n':
					stringBuilder.Append('\n');
					break;
				case 'r':
					stringBuilder.Append('\r');
					break;
				case 't':
					stringBuilder.Append('\t');
					break;
				case 'u':
					if (i + 4 <= s.Length)
					{
						int num = 0;
						for (int j = 0; j < 4; j++)
						{
							num = num * 16 + HexVal(s[i + j]);
						}
						stringBuilder.Append((char)num);
						i += 4;
					}
					break;
				default:
					stringBuilder.Append(c2);
					break;
				}
				continue;
			}
			case '"':
				break;
			}
			break;
		}
		return stringBuilder.ToString();
	}

	private static int HexVal(char c)
	{
		if (c >= '0' && c <= '9')
		{
			return c - 48;
		}
		if (c >= 'a' && c <= 'f')
		{
			return c - 97 + 10;
		}
		if (c >= 'A' && c <= 'F')
		{
			return c - 65 + 10;
		}
		return 0;
	}

	private static JNode ParseNumber(string s, ref int i)
	{
		int num = i;
		while (i < s.Length)
		{
			char c = s[i];
			if ((c < '0' || c > '9') && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E')
			{
				break;
			}
			i++;
		}
		double result = 0.0;
		if (i > num)
		{
			double.TryParse(s.Substring(num, i - num), NumberStyles.Float, CultureInfo.InvariantCulture, out result);
		}
		return new JNode
		{
			Kind = JKind.Number,
			Num = result
		};
	}
}
public static class ModConfig
{
	public static bool Enabled = true;

	public static string VrmPath = "";

	public static bool ReplaceOthers = false;

	public static bool SyncOwnModel = true;

	public static bool ShowOtherModels = true;

	public static bool PanelEnabled = true;

	public static bool Vanilla = false;

	public static bool AutoFit = true;

	public static float Scale = 1f;

	public static float ScaleWidth = 1f;

	public static float ScaleHeight = 1f;

	public static float RotX = 0f;

	public static float RotY = 0f;

	public static float RotZ = 0f;

	public static float YOffset = 0f;

	public static float YawOffset = 0f;

	public static bool AnchorToHip = true;

	public static float OffsetForward = 0f;

	public static float OffsetRight = 0f;

	public static bool GroundFeet = true;

	public static bool FlipV = true;

	public static string ShaderName = "";

	public static bool DumpShaders = false;

	public static bool HideAllRenderers = false;

	public static bool HideWithGameBody = true;

	public static bool FirstPersonBody = true;

	public static float FirstPersonNearClip = 0.05f;

	private const string FpKeepDefault = "lower_arm,hand,thumb,wrist,finger,lower_leg,foot,toe,toes,ankle";

	private const string FpKeepLegacy = "hand,thumb,foot,toe,ankle,wrist,finger";

	public static string FirstPersonKeepBones = "lower_arm,hand,thumb,wrist,finger,lower_leg,foot,toe,toes,ankle";

	public static float FirstPersonRadius = 0.18f;

	public static bool ArmsDown = true;

	public static string GameMeshRestEuler = "-90,0,0";

	public static string Retarget = "world";

	public static Quaternion GameMeshRest = Quaternion.Euler(-90f, 0f, 0f);

	private static ConfigFile _cfg;

	private static ConfigEntry<string> _vrmEntry;

	private static ConfigEntry<bool> _vanillaEntry;

	private static ConfigEntry<float> _scaleEntry;

	private static ConfigEntry<float> _scaleWidthEntry;

	private static ConfigEntry<float> _scaleHeightEntry;

	private static ConfigEntry<float> _rotXEntry;

	private static ConfigEntry<float> _rotYEntry;

	private static ConfigEntry<float> _rotZEntry;

	private static ConfigEntry<float> _yEntry;

	private static ConfigEntry<float> _fwdEntry;

	private static ConfigEntry<float> _rightEntry;

	public static void Bind(ConfigFile cfg)
	{
		//IL_04d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_051b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0525: Expected O, but got Unknown
		//IL_0537: Unknown result type (might be due to invalid IL or missing references)
		//IL_0541: Expected O, but got Unknown
		_cfg = cfg;
		Enabled = cfg.Bind<bool>("1.General", "Enabled", true, "启用换模").Value;
		_vrmEntry = cfg.Bind<string>("1.General", "VrmPath", "", "当前模型文件路径;按 K 在面板里切换会自动写回这里");
		VrmPath = _vrmEntry.Value;
		_vanillaEntry = cfg.Bind<bool>("1.General", "Vanilla", false, "穿 PEAK 原版装扮,不替换自己的模型。面板里点「原版 PEAK 装扮」也会改这里;点任意一个模型文件就自动关掉");
		Vanilla = _vanillaEntry.Value;
		ReplaceOthers = cfg.Bind<bool>("1.General", "ReplaceOtherPlayers", false, "替换其他玩家:对方也装了 PeakSkin 且本地有同一份模型时,显示他自己选的模型;否则退回显示你当前这个模型。关掉就完全不碰别人").Value;
		SyncOwnModel = cfg.Bind<bool>("1.General", "SyncOwnModel", true, "联机时告诉同房间的 PeakSkin 用户你穿的哪个模型(只发一个文件指纹,不发模型文件本体)").Value;
		ShowOtherModels = cfg.Bind<bool>("1.General", "ShowOtherModels", true, "用对方自己选的模型显示别人。你本地没有那份模型时,对方的模型会保持原版(不会套错模型)").Value;
		PanelEnabled = cfg.Bind<bool>("1.General", "PanelKeyEnabled", true, "允许用 K 键打开换模面板").Value;
		AutoFit = cfg.Bind<bool>("2.Transform", "AutoFit", true, "按游戏角色身高自动缩放(推荐)").Value;
		_scaleEntry = cfg.Bind<float>("2.Transform", "Scale", 1f, "额外缩放倍率(AutoFit 生效时是相对值)");
		Scale = _scaleEntry.Value;
		_scaleWidthEntry = cfg.Bind<float>("2.Transform", "ScaleWidth", 1f, "横向(X/Z)缩放倍率,和 Scale 一起决定宽高比。面板里的模型编辑器会实时改这里");
		ScaleWidth = _scaleWidthEntry.Value;
		_scaleHeightEntry = cfg.Bind<float>("2.Transform", "ScaleHeight", 1f, "竖向(Y)缩放倍率,和 Scale 一起决定宽高比。面板里的模型编辑器会实时改这里");
		ScaleHeight = _scaleHeightEntry.Value;
		_rotXEntry = cfg.Bind<float>("2.Transform", "RotX", 0f, "整体俯仰(度,正=前倾)。模型编辑器实时改");
		RotX = _rotXEntry.Value;
		_rotYEntry = cfg.Bind<float>("2.Transform", "RotY", 0f, "整体朝向(度)。和 YawOffset 的区别:这个不用重进游戏,滑块即时生效");
		RotY = _rotYEntry.Value;
		_rotZEntry = cfg.Bind<float>("2.Transform", "RotZ", 0f, "整体侧倾(度,正=向右倒)。模型编辑器实时改");
		RotZ = _rotZEntry.Value;
		_yEntry = cfg.Bind<float>("2.Transform", "YOffset", 0f, "上下偏移(米)");
		YOffset = _yEntry.Value;
		YawOffset = cfg.Bind<float>("2.Transform", "YawOffset", 0f, "朝向偏移(度,180=掉头)").Value;
		AnchorToHip = cfg.Bind<bool>("2.Transform", "AnchorToHip", true, "钉在角色髋骨上(默认开)。PEAK 的角色是物理布娃娃,Character.transform 根本不移动,只有髋骨跟着人跑;关掉会退回旧锚点,模型就会停在原地不跟随").Value;
		_fwdEntry = cfg.Bind<float>("2.Transform", "OffsetForward", 0f, "前后微调(米,相对模型自身朝向;模型比实际位置偏前/偏后时用它)");
		OffsetForward = _fwdEntry.Value;
		_rightEntry = cfg.Bind<float>("2.Transform", "OffsetRight", 0f, "左右微调(米,相对模型自身朝向)");
		OffsetRight = _rightEntry.Value;
		GroundFeet = cfg.Bind<bool>("2.Transform", "GroundFeet", true, "脚落地(默认开):髋部仍跟着游戏髋骨,但高度改成让模型自己的最低点(脚底)贴在游戏角色的最低点上。PEAK 的腿占身高 47%,这些模型只占 10-19%,只钉髋骨的话脚会悬空 0.3-0.9 米。第一人称不生效(那个视角的高度是另外框定的)。关掉=退回旧行为,模型会比地面高出一截").Value;
		FlipV = cfg.Bind<bool>("3.Render", "FlipV", true, "贴图 V 轴翻转(贴图上下颠倒时改这里)").Value;
		ShaderName = cfg.Bind<string>("3.Render", "Shader", "", "强制指定着色器名,例如 Universal Render Pipeline/Lit;留空自动挑").Value;
		DumpShaders = cfg.Bind<bool>("3.Render", "DumpShaderNames", false, "启动时把所有可用着色器名写进日志(排查用)").Value;
		HideAllRenderers = cfg.Bind<bool>("3.Render", "HideAllCharacterRenderers", false, "隐藏角色下所有渲染器(含手持物品,慎用)").Value;
		HideWithGameBody = cfg.Bind<bool>("3.Render", "HideWithGameBody", true, "第一人称相机进入模型内部时自动隐藏(关掉会看到模型内部,一般不要关)").Value;
		FirstPersonBody = cfg.Bind<bool>("3.Render", "FirstPersonBody", true, "第一人称保留身体:只裁掉头部,手/脚/躯干照常显示(关掉=旧行为,相机进入模型内部就整只隐藏)").Value;
		FirstPersonNearClip = cfg.Bind<float>("3.Render", "FirstPersonNearClip", 0.05f, "第一人称保留身体时的近裁剪距离(米)。相机钻进头部空腔后,比这个距离更近的东西会被裁掉").Value;
		ConfigEntry<string> val = cfg.Bind<string>("3.Render", "FirstPersonKeepBones", "lower_arm,hand,thumb,wrist,finger,lower_leg,foot,toe,toes,ankle", "第一人称保留哪些部位(逗号分隔,按骨骼名包含匹配)。默认留手腕以下加小腿,因为模型的掌心权重常落在 lower_arm、整条小腿(含脚)落在 lower_leg,只留 hand/foot 会缺一块");
		if (string.Equals(val.Value.Trim(), "hand,thumb,foot,toe,ankle,wrist,finger", StringComparison.OrdinalIgnoreCase))
		{
			val.Value = "lower_arm,hand,thumb,wrist,finger,lower_leg,foot,toe,toes,ankle";
			try
			{
				cfg.Save();
			}
			catch
			{
			}
		}
		FirstPersonKeepBones = val.Value;
		FirstPersonRadius = cfg.Bind<float>("3.Render", "FirstPersonRadius", 0.18f, "第一人称保留的顶点还必须离自己那根骨头这么近(米)。Tripo 系模型(蓝色小肥鱼)把身体/裙摆/鱼尾的一大块权重刷到了手脚骨上(权重>0.5、最远 0.8 米),只按骨骼名筛会把它们拖到眼前。0=不限距离").Value;
		ArmsDown = cfg.Bind<bool>("3.Render", "ArmsDown", true, "待机双手垂在身侧:游戏上臂朝上或水平时,把模型上臂改成朝下(游戏本来就在下垂时不干预)").Value;
		GameMeshRestEuler = cfg.Bind<string>("3.Render", "GameMeshRestEuler", "-90,0,0", "游戏网格节点 bind 时的世界欧拉角(PEAK 骨架是 Rx(-90),一般不用动)").Value;
		GameMeshRest = Quaternion.Euler(ParseEuler(GameMeshRestEuler, new Vector3(-90f, 0f, 0f)));
		Retarget = cfg.Bind<string>("3.Render", "Retarget", "aim", "绑骨方式:aim=让我们的骨头指向游戏同一节骨头的方向(推荐,不受模型 rest 姿势差异影响);world=世界旋转直抄(旧版;VRM1.0/Blender 导出的模型手臂会举起来);local=按关节角度差回放(对这些模型会变成手朝后);right/conj=两种备选对齐摆法").Value;
		try
		{
			cfg.Remove(new ConfigDefinition("4.Mouse", "FreeMouseKey"));
		}
		catch
		{
		}
		try
		{
			cfg.Remove(new ConfigDefinition("4.Mouse", "FreeMouseWithPanel"));
		}
		catch
		{
		}
		if (true)
		{
			try
			{
				cfg.Save();
			}
			catch
			{
			}
		}
	}

	public static void SetVrmPath(string path)
	{
		VrmPath = path;
		if (_vrmEntry == null)
		{
			return;
		}
		try
		{
			_vrmEntry.Value = path;
			if (_cfg != null)
			{
				_cfg.Save();
			}
		}
		catch
		{
		}
	}

	public static void SetVanilla(bool on)
	{
		Vanilla = on;
		if (_vanillaEntry == null)
		{
			return;
		}
		try
		{
			_vanillaEntry.Value = on;
			if (_cfg != null)
			{
				_cfg.Save();
			}
		}
		catch
		{
		}
	}

	public static void SaveTransform()
	{
		try
		{
			if (_scaleEntry != null)
			{
				_scaleEntry.Value = Scale;
			}
			if (_scaleWidthEntry != null)
			{
				_scaleWidthEntry.Value = ScaleWidth;
			}
			if (_scaleHeightEntry != null)
			{
				_scaleHeightEntry.Value = ScaleHeight;
			}
			if (_rotXEntry != null)
			{
				_rotXEntry.Value = RotX;
			}
			if (_rotYEntry != null)
			{
				_rotYEntry.Value = RotY;
			}
			if (_rotZEntry != null)
			{
				_rotZEntry.Value = RotZ;
			}
			if (_yEntry != null)
			{
				_yEntry.Value = YOffset;
			}
			if (_fwdEntry != null)
			{
				_fwdEntry.Value = OffsetForward;
			}
			if (_rightEntry != null)
			{
				_rightEntry.Value = OffsetRight;
			}
			if (_cfg != null)
			{
				_cfg.Save();
			}
		}
		catch
		{
		}
	}

	public static void Save()
	{
		try
		{
			if (_cfg != null)
			{
				_cfg.Save();
			}
		}
		catch
		{
		}
	}

	public static Vector3 ParseEuler(string s, Vector3 fallback)
	{
		//IL_0008: 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_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(s))
		{
			return fallback;
		}
		string[] array = s.Split(',');
		if (array.Length != 3)
		{
			return fallback;
		}
		if (!float.TryParse(array[0].Trim(), out var result))
		{
			return fallback;
		}
		if (!float.TryParse(array[1].Trim(), out var result2))
		{
			return fallback;
		}
		if (!float.TryParse(array[2].Trim(), out var result3))
		{
			return fallback;
		}
		return new Vector3(result, result2, result3);
	}
}
internal static class ModelCatalog
{
	private const int Chunk = 65536;

	private const ulong FnvOffset = 14695981039346656037uL;

	private const ulong FnvPrime = 1099511628211uL;

	private static readonly Dictionary<string, string> Id2Path = new Dictionary<string, string>(8, StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, string> Path2Id = new Dictionary<string, string>(8, StringComparer.OrdinalIgnoreCase);

	public static int Count => Id2Path.Count;

	public static void Rebuild(List<string> files)
	{
		Id2Path.Clear();
		Path2Id.Clear();
		if (files == null)
		{
			return;
		}
		for (int i = 0; i < files.Count; i++)
		{
			string text = files[i];
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = IdOf(text);
				if (text2 != null)
				{
					Id2Path[text2] = text;
				}
			}
		}
		Plugin.Log("[net] catalog indexed " + Id2Path.Count + " model(s)");
	}

	public static string IdOf(string path)
	{
		if (string.IsNullOrEmpty(path))
		{
			return null;
		}
		string text = Safe(path);
		if (Path2Id.TryGetValue(text, out var value))
		{
			return value;
		}
		string text2 = null;
		try
		{
			if (File.Exists(text))
			{
				text2 = Compute(text);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log("[net] id failed for " + path + ": " + ex.Message);
		}
		if (text2 != null)
		{
			Path2Id[text] = text2;
			Id2Path[text2] = text;
		}
		return text2;
	}

	public static string PathOf(string id)
	{
		if (string.IsNullOrEmpty(id))
		{
			return null;
		}
		if (!Id2Path.TryGetValue(id, out var value))
		{
			return null;
		}
		return value;
	}

	public static bool Has(string id)
	{
		return PathOf(id) != null;
	}

	private static string Safe(string p)
	{
		try
		{
			return Path.GetFullPath(p);
		}
		catch
		{
			return p;
		}
	}

	private static string Compute(string path)
	{
		long length = new FileInfo(path).Length;
		ulong num = 14695981039346656037uL;
		byte[] array = new byte[65536];
		using (FileStream fileStream = File.OpenRead(path))
		{
			int num2 = fileStream.Read(array, 0, 65536);
			for (int i = 0; i < num2; i++)
			{
				num ^= array[i];
				num *= 1099511628211L;
			}
			if (length > 131072)
			{
				fileStream.Seek(-65536L, SeekOrigin.End);
				num2 = fileStream.Read(array, 0, 65536);
				for (int j = 0; j < num2; j++)
				{
					num ^= array[j];
					num *= 1099511628211L;
				}
			}
		}
		StringBuilder stringBuilder = new StringBuilder(24);
		stringBuilder.Append(length.ToString("x"));
		stringBuilder.Append('-').Append(num.ToString("x16"));
		return stringBuilder.ToString();
	}
}
internal sealed class ModelEditor : MonoBehaviour
{
	private sealed class Param
	{
		public string Name;

		public float Min;

		public float Max;

		public Func<float> Get;

		public Action<float> Set;

		public string Fmt;
	}

	public static ModelEditor Instance;

	public bool Visible;

	private const float BoxX = 458f;

	private const float PadX = 12f;

	private const float PadY = 10f;

	private const float BtnH = 30f;

	private const float SliderMinW = 170f;

	private GUIStyle _label;

	private GUIStyle _val;

	private GUIStyle _btn;

	private Param[] _params;

	private float _labelW;

	private float _valW;

	private float _rowStep;

	private float _boxW;

	private void Awake()
	{
		Instance = this;
	}

	public void Show()
	{
		Visible = true;
		Plugin.Log("[editor] open");
	}

	public void Hide()
	{
		if (Visible)
		{
			ModConfig.SaveTransform();
			Visible = false;
			Plugin.Log("[editor] closed");
		}
	}

	private static Param Row(string name, float min, float max, Func<float> get, Action<float> set, string fmt)
	{
		return new Param
		{
			Name = name,
			Min = min,
			Max = max,
			Get = get,
			Set = set,
			Fmt = fmt
		};
	}

	private void EnsureParams()
	{
		if (_params == null)
		{
			_params = new Param[9]
			{
				Row("大小", 0.1f, 2.5f, () => ModConfig.Scale, delegate(float v)
				{
					ModConfig.Scale = v;
				}, "F2"),
				Row("宽 XZ", 0.3f, 2f, () => ModConfig.ScaleWidth, delegate(float v)
				{
					ModConfig.ScaleWidth = v;
				}, "F2"),
				Row("高 Y", 0.3f, 2f, () => ModConfig.ScaleHeight, delegate(float v)
				{
					ModConfig.ScaleHeight = v;
				}, "F2"),
				Row("旋转X", -180f, 180f, () => ModConfig.RotX, delegate(float v)
				{
					ModConfig.RotX = v;
				}, "F0"),
				Row("旋转Y", -180f, 180f, () => ModConfig.RotY, delegate(float v)
				{
					ModConfig.RotY = v;
				}, "F0"),
				Row("旋转Z", -180f, 180f, () => ModConfig.RotZ, delegate(float v)
				{
					ModConfig.RotZ = v;
				}, "F0"),
				Row("上下", -0.8f, 0.8f, () => ModConfig.YOffset, delegate(float v)
				{
					ModConfig.YOffset = v;
				}, "F2"),
				Row("前后", -0.8f, 0.8f, () => ModConfig.OffsetForward, delegate(float v)
				{
					ModConfig.OffsetForward = v;
				}, "F2"),
				Row("左右", -0.8f, 0.8f, () => ModConfig.OffsetRight, delegate(float v)
				{
					ModConfig.OffsetRight = v;
				}, "F2")
			};
		}
	}

	private void BuildStyles()
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Expected O, but got Unknown
		if (_label == null)
		{
			Font val = null;
			try
			{
				val = Font.CreateDynamicFontFromOSFont(new string[4] { "Microsoft YaHei", "SimHei", "Arial Unicode MS", "Arial" }, 14);
			}
			catch
			{
			}
			_label = MakeStyle(GUI.skin.label, (TextAnchor)3);
			_val = MakeStyle(GUI.skin.label, (TextAnchor)5);
			_btn = MakeStyle(GUI.skin.button, (TextAnchor)4);
			_btn.padding = new RectOffset(4, 4, 4, 4);
			if ((Object)(object)val != (Object)null)
			{
				_label.font = val;
				_val.font = val;
				_btn.font = val;
			}
		}
	}

	private static GUIStyle MakeStyle(GUIStyle src, TextAnchor anchor)
	{
		//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_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: 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)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: 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_0032: Expected O, but got Unknown
		//IL_0032: 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_0041: Expected O, but got Unknown
		//IL_0042: Expected O, but got Unknown
		return new GUIStyle(src)
		{
			fontSize = 14,
			alignment = anchor,
			wordWrap = false,
			clipping = (TextClipping)0,
			padding = new RectOffset(0, 0, 0, 0),
			margin = new RectOffset(0, 0, 0, 0)
		};
	}

	private void Measure()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		//IL_001e: 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_007b: Expected O, but got Unknown
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Expected O, but got Unknown
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Expected O, but got Unknown
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		if (!(_rowStep > 0f))
		{
			float num = _label.CalcSize(new GUIContent("国")).y;
			if (num <= 0f)
			{
				num = 19f;
			}
			_rowStep = Mathf.Ceil(Mathf.Max(24f, num + 8f));
			float num2 = 0f;
			for (int i = 0; i < _params.Length; i++)
			{
				num2 = Mathf.Max(num2, _label.CalcSize(new GUIContent(_params[i].Name)).x);
			}
			_labelW = Mathf.Ceil(Mathf.Max(num2, 40f)) + 4f;
			float num3 = Mathf.Max(_val.CalcSize(new GUIContent("-180")).x, _val.CalcSize(new GUIContent("0.00")).x);
			_valW = Mathf.Ceil(Mathf.Max(num3, 26f)) + 4f;
			_boxW = Mathf.Ceil(Mathf.Max(340f, 24f + _labelW + 8f + 170f + 8f + _valW));
		}
	}

	private void ResetAll()
	{
		ModConfig.Scale = 1f;
		ModConfig.ScaleWidth = 1f;
		ModConfig.ScaleHeight = 1f;
		ModConfig.RotX = (ModConfig.RotY = (ModConfig.RotZ = 0f));
		ModConfig.YOffset = (ModConfig.OffsetForward = (ModConfig.OffsetRight = 0f));
		ModConfig.SaveTransform();
		Plugin.Log("[editor] 已重置为默认");
	}

	private void OnGUI()
	{
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: 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_0319: Unknown result type (might be due to invalid IL or missing references)
		if (!Visible)
		{
			return;
		}
		BuildStyles();
		EnsureParams();
		Measure();
		int num = _params.Length;
		float num2 = 20f + (float)num * _rowStep + 8f + 30f;
		float num3 = Mathf.Clamp(458f, 8f, Mathf.Max(8f, (float)Screen.width - _boxW - 8f));
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(num3, 16f, _boxW, num2);
		GUI.color = new Color(0f, 0f, 0f, 0.86f);
		GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture);
		GUI.color = Color.white;
		GUI.Box(val, GUIContent.none);
		float num4 = ((Rect)(ref val)).x + 12f;
		float num5 = _boxW - 24f;
		float num6 = num4 + _labelW + 8f;
		float num7 = Mathf.Max(60f, num5 - _labelW - 8f - _valW - 8f);
		float num8 = num6 + num7 + 8f;
		float num9 = ((Rect)(ref val)).y + 10f;
		for (int i = 0; i < num; i++)
		{
			Param param = _params[i];
			GUI.Label(new Rect(num4, num9, _labelW, _rowStep), param.Name, _label);
			float num10 = GUI.HorizontalSlider(new Rect(num6, num9, num7, _rowStep), param.Get(), param.Min, param.Max);
			num10 = Mathf.Round(num10 * 100f) / 100f;
			if (!Mathf.Approximately(num10, param.Get()))
			{
				param.Set(num10);
			}
			GUI.Label(new Rect(num8, num9, _valW, _rowStep), param.Get().ToString(param.Fmt), _val);
			num9 += _rowStep;
		}
		num9 += 8f;
		float num11 = (num5 - 12f) / 3f;
		if (GUI.Button(new Rect(num4, num9, num11, 30f), "重置", _btn))
		{
			ResetAll();
		}
		if (GUI.Button(new Rect(num4 + num11 + 6f, num9, num11, 30f), "保存", _btn))
		{
			ModConfig.SaveTransform();
			Plugin.Log($"[editor] 已保存 scale={ModConfig.Scale:F2} wxz={ModConfig.ScaleWidth:F2} hy={ModConfig.ScaleHeight:F2} rot=({ModConfig.RotX:F0},{ModConfig.RotY:F0},{ModConfig.RotZ:F0})");
		}
		if (GUI.Button(new Rect(num4 + (num11 + 6f) * 2f, num9, num11, 30f), "关闭", _btn))
		{
			Hide();
		}
	}

	private void OnDisable()
	{
		Visible = false;
	}
}
internal static class BlendReader
{
	private sealed class ObjInfo
	{
		public string Name = "object";

		public int Type;

		public Block Block;

		public long BlockAddr;

		public int DataOff;

		public Block Data;

		public long ParentAddr;

		public Matrix4x4 LocalB = Ident();

		public Matrix4x4 WorldB = Ident();

		public Matrix4x4 WorldU = Ident();

		public Matrix4x4 ParentInv = Ident();

		public bool Resolved;

		public bool Resolving;
	}

	private sealed class BoneInfo
	{
		public string Name = "bone";

		public long Addr;

		public long ParentAddr;

		public BoneInfo Parent;

		public Matrix4x4 WorldU = Ident();
	}

	private sealed class Geo
	{
		public int VCount;

		public int PCount;

		public int LCount;

		public Vector3[] Pos;

		public int[] Corner;

		public int[] PolyStart;

		public int[] PolyMat;

		public Vector2[] Uv;

		public int[] DStart;

		public int[] DBone;

		public float[] DWeight;
	}

	private sealed class CDLayer
	{
		public int Type;

		public string Name = "";

		public long Data;
	}

	private sealed class Attr
	{
		public string Name = "";

		public int Type;

		public int Domain;

		public long Data;
	}

	private struct GroupStat
	{
		public float Weight;

		public Vector3 Sum;
	}

	private struct VKey : IEquatable<VKey>
	{
		public int Vert;

		public int Ux;

		public int Uy;

		public bool Equals(VKey o)
		{
			if (o.Vert == Vert && o.Ux == Ux)
			{
				return o.Uy == Uy;
			}
			return false;
		}

		public override bool Equals(object o)
		{
			if (o is VKey)
			{
				return Equals((VKey)o);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (Vert * 73856093) ^ (Ux * 19349663) ^ (Uy * 83492791);
		}
	}

	private sealed class Block
	{
		public string Code = "?";

		public long Old;

		public int Data;

		public int Len;

		public int Nr;
	}

	private sealed class Field
	{
		public string Name = "";

		public string Type = "";

		public int Offset;

		public int Count = 1;

		public bool Ptr;
	}

	private sealed class FieldMap
	{
		public int Size;

		private readonly Dictionary<string, Field> byName = new Dictionary<string, Field>();

		public Field Get(string n)
		{
			if (!byName.TryGetValue(n, out var value))
			{
				return null;
			}
			return value;
		}

		public static FieldMap Of(Blend f, DnaStruct s)
		{
			if (s == null)
			{
				return null;
			}
			if (f.fieldCache.TryGetValue(s, out var value))
			{
				return value;
			}
			value = new FieldMap();
			value.Size = s.Size;
			for (int i = 0; i < s.Fields.Length; i++)
			{
				Field field = s.Fields[i];
				if (!value.byName.ContainsKey(field.Name))
				{
					value.byName[field.Name] = field;
				}
			}
			f.fieldCache[s] = value;
			return value;
		}
	}

	private sealed class DnaStruct
	{
		public string Name = "";

		public int Size;

		public Field[] Fields = new Field[0];
	}

	private sealed class Dna
	{
		public string[] Names = new string[0];

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

		public int[] Lens = new int[0];

		public List<DnaStruct> Structs = new List<DnaStruct>();

		public Dictionary<string, DnaStruct> Map = new Dictionary<string, DnaStruct>();

		public string Layout = "?";

		public DnaStruct Find(string name)
		{
			if (!Map.TryGetValue(name, out var value))
			{
				return null;
			}
			return value;
		}

		public static Dna Parse(byte[] b, int start, int len, bool be, int ptr)
		{
			int end = start + len;
			int num = start;
			if (!Tag(b, num, "SDNA"))
			{
				throw new FormatException("no SDNA tag");
			}
			num += 4;
			if (!Tag(b, num, "NAME"))
			{
				throw new FormatException("no NAME tag");
			}
			num += 4;
			int num2 = I32(b, num, be);
			num += 4;
			if (num2 < 0 || num2 > 400000)
			{
				throw new FormatException("bad name count " + num2);
			}
			string[] array = new string[num2];
			for (int i = 0; i < num2; i++)
			{
				int num3 = Nul(b, num, end);
				if (num3 < 0)
				{
					throw new FormatException("name table truncated");
				}
				array[i] = Ascii(b, num, num3 - num);
				num = num3 + 1;
			}
			num = Align4(num, start);
			if (!Tag(b, num, "TYPE"))
			{
				throw new FormatException("no TYPE tag");
			}
			num += 4;
			int num4 = I32(b, num, be);
			num += 4;
			if (num4 < 0 || num4 > 400000)
			{
				throw new FormatException("bad type count " + num4);
			}
			string[] array2 = new string[num4];
			for (int j = 0; j < num4; j++)
			{
				int num5 = Nul(b, num, end);
				if (num5 < 0)
				{
					throw new FormatException("type table truncated");
				}
				array2[j] = Ascii(b, num, num5 - num);
				num = num5 + 1;
			}
			num = Align4(num, start);
			if (!Tag(b, num, "TLEN"))
			{
				throw new FormatException("no TLEN tag");
			}
			num += 4;
			int[] array3 = new int[num4];
			for (int k = 0; k < num4; k++)
			{
				array3[k] = I16(b, num, be);
				num += 2;
			}
			num = Align4(num, start);
			if (!Tag(b, num, "STRC"))
			{
				throw new FormatException("no STRC tag");
			}
			num += 4;
			Dna dna = new Dna();
			dna.Names = array;
			dna.Types = array2;
			dna.Lens = array3;
			List<DnaStruct> list = TryModern(b, num, end, ptr, be, array2, array3, array);
			if (list != null)
			{
				dna.Layout = "modern";
				dna.Structs = list;
			}
			else
			{
				dna.Layout = "classic";
				dna.Structs = ParseClassic(b, num, end, ptr, be, array2, array3, array);
			}
			for (int l = 0; l < dna.Structs.Count; l++)
			{
				DnaStruct dnaStruct = dna.Structs[l];
				if (!dna.Map.ContainsKey(dnaStruct.Name))
				{
					dna.Map[dnaStruct.Name] = dnaStruct;
				}
			}
			return dna;
		}

		private static List<DnaStruct> TryModern(byte[] b, int p, int end, int ptr, bool be, string[] types, int[] lens, string[] names)
		{
			try
			{
				int num = p;
				int num2 = I32(b, num, be);
				num += 4;
				if (num2 <= 0 || num2 > 200000)
				{
					return null;
				}
				List<DnaStruct> list = new List<DnaStruct>(num2);
				for (int i = 0; i < num2; i++)
				{
					if (num + 4 > end)
					{
						return null;
					}
					int num3 = I16(b, num, be);
					int num4 = I16(b, num + 2, be);
					num += 4;
					if (num3 < 0 || num3 >= types.Length || num4 < 0 || num4 > 8192)
					{
						return null;
					}
					DnaStruct dnaStruct = new DnaStruct();
					dnaStruct.Name = types[num3];
					dnaStruct.Size = lens[num3];
					dnaStruct.Fields = ReadFields(b, ref num, end, num4, ptr, be, types, lens, names);
					if (dnaStruct.Fields == null)
					{
						return null;
					}
					list.Add(dnaStruct);
				}
				return (num == end) ? list : null;
			}
			catch
			{
				return null;
			}
		}

		private static List<DnaStruct> ParseClassic(byte[] b, int p, int end, int ptr, bool be, string[] types, int[] lens, string[] names)
		{
			int num = p;
			if (num + 2 > end)
			{
				throw new FormatException("struct table truncated");
			}
			int num2 = I16(b, num, be);
			num += 2;
			if (num2 <= 0 || num2 > 200000)
			{
				throw new FormatException("bad struct count " + num2);
			}
			if (num + num2 * 4 > end)
			{
				throw new FormatException("struct index table truncated");
			}
			int[] array = new int[num2];
			for (int i = 0; i < num2; i++)
			{
				array[i] = I16(b, num + i * 2, be);
			}
			num += num2 * 2;
			int[] array2 = new int[num2];
			for (int j = 0; j < num2; j++)
			{
				array2[j] = I16(b, num + j * 2, be);
			}
			num += num2 * 2;
			List<DnaStruct> list = new List<DnaStruct>(num2);
			for (int k = 0; k < num2; k++)
			{
				if (num + 2 > end)
				{
					throw new FormatException("field table truncated");
				}
				int nf = I16(b, num, be);
				num += 2;
				DnaStruct dnaStruct = new DnaStruct();
				dnaStruct.Name = ((array2[k] >= 0 && array2[k] < types.Length) ? types[array2[k]] : "?");
				dnaStruct.Size = array[k];
				dnaStruct.Fields = ReadFields(b, ref num, end, nf, ptr, be, types, lens, names);
				if (dnaStruct.Fields == null)
				{
					throw new FormatException("field table truncated");
				}
				list.Add(dnaStruct);
			}
			return list;
		}

		private static Field[] ReadFields(byte[] b, ref int q, int end, int nf, int ptr, bool be, string[] types, int[] lens, string[] names)
		{
			Field[] array = new Field[nf];
			int num = 0;
			for (int i = 0; i < nf; i++)
			{
				if (q + 4 > end)
				{
					return null;
				}
				int num2 = I16(b, q, be);
				int num3 = I16(b, q + 2, be);
				q += 4;
				if (num2 < 0 || num2 >= types.Length |