Decompiled source of Delloom v0.5.6

BepInEx/plugins/Delloom/LoomReskin.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LoomReskin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.6.0")]
[assembly: AssemblyInformationalVersion("0.5.6")]
[assembly: AssemblyProduct("LoomReskin")]
[assembly: AssemblyTitle("LoomReskin")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "Code and development tooling were created with assistance from OpenAI Codex and Anthropic Claude (Sonnet).")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "OpenAI; Anthropic")]
[assembly: AssemblyMetadata("AI_Agents", "OpenAI Codex; Anthropic Claude (Sonnet)")]
[assembly: AssemblyVersion("0.5.6.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LoomReskin
{
	[BepInPlugin("local.lemon.loomreskin", "Lady in Red — Loom", "0.5.6")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		private Harmony patches;

		private JewelryAsset jewelry;

		private bool ready;

		private bool validationRun;

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			GameObject gameObject = ((Component)this).gameObject;
			((Object)gameObject).hideFlags = (HideFlags)(((Object)gameObject).hideFlags | 0x3D);
			Instance = this;
			try
			{
				jewelry = AssetLoader.Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "visuals.json")));
				patches = new Harmony("local.lemon.loomreskin");
				patches.PatchAll(typeof(Plugin).Assembly);
				ready = true;
				SceneManager.sceneLoaded += OnSceneLoaded;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Ready: EnemyShadow visual reskin; original AI, colliders, transforms, and animation controllers retained.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Reskin disabled: " + ex));
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (!ready || validationRun)
			{
				return;
			}
			validationRun = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Validating original Loom hierarchy after scene load: " + ((Scene)(ref scene)).name));
			try
			{
				SelfCheck();
			}
			catch (Exception ex)
			{
				ready = false;
				patches.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("Validation failed; instance patch disabled: " + ex));
			}
		}

		internal void ApplyLive(Transform root)
		{
			if (!ready)
			{
				return;
			}
			try
			{
				int num = Visuals.Apply(root, jewelry, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
				if (num > 0)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied Loom reskin: " + num + " original renderers styled; " + jewelry.parts.Length + " accessory and hair mesh pieces."));
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Loom instance left unchanged because its layout was not supported: " + ex));
			}
		}

		private void SelfCheck()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Resources.Load<GameObject>("enemies/enemy - shadow");
			if ((Object)(object)val == (Object)null)
			{
				throw new Exception("Original Loom prefab unavailable.");
			}
			EnemyShadow componentInChildren = val.GetComponentInChildren<EnemyShadow>(true);
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)SpawnPatch.VisualRoot(componentInChildren) != (Object)(object)val.transform)
			{
				throw new Exception("Live spawn hook does not resolve the original prefab root.");
			}
			GameObject val2 = new GameObject("LoomReskin verification");
			val2.SetActive(false);
			try
			{
				CopyVisualTree(val.transform, val2.transform);
				Transform[] componentsInChildren = val2.GetComponentsInChildren<Transform>(true);
				Vector3[] array = componentsInChildren.Select((Transform t) => t.localPosition).ToArray();
				Quaternion[] array2 = componentsInChildren.Select((Transform t) => t.localRotation).ToArray();
				Vector3[] array3 = componentsInChildren.Select((Transform t) => t.localScale).ToArray();
				MeshRenderer[] componentsInChildren2 = val2.GetComponentsInChildren<MeshRenderer>(true);
				Material[] array4 = componentsInChildren2.Select((MeshRenderer r) => ((Renderer)r).sharedMaterial).ToArray();
				Mesh[] first = (from m in val2.GetComponentsInChildren<MeshFilter>(true)
					select m.sharedMesh).ToArray();
				int num = Visuals.Apply(val2.transform, jewelry, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
				foreach (MeshRenderer item in from r in val2.GetComponentsInChildren<MeshRenderer>(true)
					where ((Object)r).name.StartsWith("LoomJewelry_")
					select r)
				{
					Material sharedMaterial = ((Renderer)item).sharedMaterial;
					if ((Object)(object)sharedMaterial.GetTexture("_EmissionTexture") != (Object)null || sharedMaterial.GetColor("_EmissionColor") != Color.black)
					{
						throw new Exception("Authored material inherited original emission: " + ((Object)item).name);
					}
					if (sharedMaterial.GetFloat("_NormalStrength") != 0f || sharedMaterial.GetFloat("_OffsetX") != 0f || sharedMaterial.GetFloat("_OffsetY") != 0f)
					{
						throw new Exception("Authored material inherited normal strength or UV offset: " + ((Object)item).name);
					}
					if (sharedMaterial.GetFloat("_TilingX") != 1f || sharedMaterial.GetFloat("_TilingY") != 1f)
					{
						throw new Exception("Authored material has unexpected UV tiling: " + ((Object)item).name);
					}
				}
				int num2 = ((jewelry.schemaVersion == 2) ? 30 : 15);
				if (num != num2)
				{
					throw new Exception("Expected " + num2 + " targeted renderers; found " + num + ".");
				}
				for (int num3 = 0; num3 < componentsInChildren.Length; num3++)
				{
					if (componentsInChildren[num3].localPosition != array[num3] || componentsInChildren[num3].localRotation != array2[num3] || componentsInChildren[num3].localScale != array3[num3])
					{
						throw new Exception("Original pivot modified: " + ((Object)componentsInChildren[num3]).name);
					}
				}
				for (int num4 = 0; num4 < componentsInChildren2.Length; num4++)
				{
					if (VisualSelection.Classify(Visuals.PathOf(((Component)componentsInChildren2[num4]).transform, val2.transform)) == VisualKind.Original && (Object)(object)((Renderer)componentsInChildren2[num4]).sharedMaterial != (Object)(object)array4[num4])
					{
						throw new Exception("Untargeted renderer changed: " + ((Object)componentsInChildren2[num4]).name);
					}
				}
				Mesh[] second = (from m in val2.GetComponentsInChildren<MeshFilter>(true)
					where !((Object)m).name.StartsWith("LoomJewelry_")
					select m.sharedMesh).ToArray();
				if (!first.SequenceEqual(second))
				{
					throw new Exception("Original meshes changed.");
				}
				JewelryPart[] parts = jewelry.parts;
				foreach (JewelryPart jewelryPart in parts)
				{
					if (VisualSelection.Classify(jewelryPart.parentPath) == VisualKind.Clothing || jewelryPart.replacesParentRenderer)
					{
						MeshRenderer component = ((Component)val2.transform.Find(jewelryPart.parentPath)).GetComponent<MeshRenderer>();
						OwnedVisuals component2 = val2.GetComponent<OwnedVisuals>();
						int index = component2.renderers.IndexOf(component);
						if (((Renderer)((Component)val2.transform.Find(jewelryPart.parentPath + "/LoomJewelry_" + jewelryPart.name)).GetComponent<MeshRenderer>()).enabled != (jewelryPart.enabled && component2.enabledStates[index]))
						{
							throw new Exception("Original replacement visibility not preserved: " + jewelryPart.name);
						}
						if (jewelryPart.replacesParentRenderer && ((Renderer)component).enabled)
						{
							throw new Exception("Replaced face renderer is still visible: " + jewelryPart.name);
						}
					}
				}
				int num6 = val2.GetComponentsInChildren<Transform>(true).Length;
				if (Visuals.Apply(val2.transform, jewelry, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location)) != 0 || val2.GetComponentsInChildren<Transform>(true).Length != num6)
				{
					throw new Exception("Repeated application duplicated accessories.");
				}
				try
				{
					PreviewCapture.Save(val2, Path.Combine(Paths.BepInExRootPath, "LoomReskinPreview"));
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Saved runtime render previews to BepInEx/LoomReskinPreview.");
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Preview capture unavailable: " + ex.Message));
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("SELF-CHECK PASS: " + num2 + " targeted renderers; original meshes and all " + componentsInChildren.Length + " pivots preserved; authored visual parents resolved; repeated application safe."));
			}
			finally
			{
				Object.Destroy((Object)(object)val2);
			}
		}

		private static void CopyVisualTree(Transform source, Transform dest)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			foreach (Transform item in source)
			{
				Transform val = item;
				GameObject val2 = new GameObject(((Object)val).name);
				val2.transform.SetParent(dest, false);
				val2.transform.localPosition = val.localPosition;
				val2.transform.localRotation = val.localRotation;
				val2.transform.localScale = val.localScale;
				MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
				MeshRenderer component2 = ((Component)val).GetComponent<MeshRenderer>();
				if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null)
				{
					val2.AddComponent<MeshFilter>().sharedMesh = component.sharedMesh;
					MeshRenderer obj = val2.AddComponent<MeshRenderer>();
					((Renderer)obj).sharedMaterials = ((Renderer)component2).sharedMaterials;
					((Renderer)obj).enabled = ((Renderer)component2).enabled;
				}
				CopyVisualTree(val, val2.transform);
				val2.SetActive(((Component)val).gameObject.activeSelf);
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony obj = patches;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	[HarmonyPatch(typeof(EnemyShadow), "Awake")]
	internal static class SpawnPatch
	{
		internal static Transform VisualRoot(EnemyShadow instance)
		{
			EnemyParent componentInParent = ((Component)instance).GetComponentInParent<EnemyParent>(true);
			if (componentInParent == null)
			{
				return null;
			}
			return ((Component)componentInParent).transform;
		}

		[HarmonyPostfix]
		private static void Postfix(EnemyShadow __instance)
		{
			Transform val = VisualRoot(__instance);
			if ((Object)(object)val != (Object)null)
			{
				Plugin.Instance?.ApplyLive(val);
			}
		}
	}
	internal sealed class OwnedVisuals : MonoBehaviour
	{
		internal readonly List<Object> assets = new List<Object>();

		internal readonly List<GameObject> additions = new List<GameObject>();

		internal readonly List<MeshRenderer> renderers = new List<MeshRenderer>();

		internal readonly List<Material> originals = new List<Material>();

		internal readonly List<bool> enabledStates = new List<bool>();

		private void OnDestroy()
		{
			for (int i = 0; i < renderers.Count; i++)
			{
				if ((Object)(object)renderers[i] != (Object)null)
				{
					((Renderer)renderers[i]).sharedMaterial = originals[i];
					((Renderer)renderers[i]).enabled = enabledStates[i];
				}
			}
			foreach (GameObject addition in additions)
			{
				if ((Object)(object)addition != (Object)null)
				{
					Object.Destroy((Object)(object)addition);
				}
			}
			foreach (Object asset in assets)
			{
				if (asset != (Object)null)
				{
					Object.Destroy(asset);
				}
			}
		}
	}
	internal static class Visuals
	{
		internal static string PathOf(Transform t, Transform root)
		{
			List<string> list = new List<string>();
			while ((Object)(object)t != (Object)(object)root && (Object)(object)t != (Object)null)
			{
				list.Add(((Object)t).name);
				t = t.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		internal static int Apply(Transform root, JewelryAsset jewelry, string assetDirectory)
		{
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Expected O, but got Unknown
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0480: Unknown result type (might be due to invalid IL or missing references)
			//IL_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: 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_052c: Expected O, but got Unknown
			//IL_082f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0836: Expected O, but got Unknown
			//IL_0889: Unknown result type (might be due to invalid IL or missing references)
			//IL_088e: Unknown result type (might be due to invalid IL or missing references)
			//IL_089d: Expected O, but got Unknown
			//IL_05c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_066f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0674: Unknown result type (might be due to invalid IL or missing references)
			//IL_0697: Unknown result type (might be due to invalid IL or missing references)
			//IL_0719: Unknown result type (might be due to invalid IL or missing references)
			//IL_0720: Expected O, but got Unknown
			//IL_07ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_07bd: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((Component)root).GetComponent<OwnedVisuals>() != (Object)null)
			{
				return 0;
			}
			var targets = (from r in ((Component)root).GetComponentsInChildren<MeshRenderer>(true)
				select new
				{
					renderer = r,
					kind = VisualSelection.Classify(PathOf(((Component)r).transform, root))
				} into x
				where x.kind != VisualKind.Original && (jewelry.schemaVersion == 2 || x.kind != VisualKind.Skin)
				select x).ToArray();
			if (targets.Count(x => x.kind == VisualKind.Clothing) != 10 || targets.Count(x => x.kind == VisualKind.Hair) != 5 || (jewelry.schemaVersion == 2 && targets.Count(x => x.kind == VisualKind.Skin) != 15))
			{
				throw new InvalidDataException("Loom renderer layout changed.");
			}
			Transform[] parents = jewelry.parts.Select((JewelryPart p) => root.Find(p.parentPath)).ToArray();
			if (parents.Any((Transform p) => (Object)(object)p == (Object)null))
			{
				throw new InvalidDataException("Jewelry attachment point missing.");
			}
			if (jewelry.schemaVersion == 2 && (from x in targets
				where x.kind == VisualKind.Clothing
				select ((Component)x.renderer).transform).ToArray().Any((Transform t) => !parents.Contains(t)))
			{
				throw new InvalidDataException("Replacement clothing mesh missing.");
			}
			HashSet<Transform> hashSet = new HashSet<Transform>(from x in jewelry.parts.Select((JewelryPart p, int num4) => new
				{
					part = p,
					parent = parents[num4]
				})
				where x.part.replacesParentRenderer
				select x.parent);
			if (hashSet.Any((Transform p) => !targets.Any(t => (Object)(object)((Component)t.renderer).transform == (Object)(object)p && t.kind == VisualKind.Skin)))
			{
				throw new InvalidDataException("Face renderer missing.");
			}
			OwnedVisuals ownedVisuals = ((Component)root).gameObject.AddComponent<OwnedVisuals>();
			try
			{
				var array = targets;
				foreach (var anon in array)
				{
					ownedVisuals.renderers.Add(anon.renderer);
					ownedVisuals.originals.Add(((Renderer)anon.renderer).sharedMaterial);
					ownedVisuals.enabledStates.Add(((Renderer)anon.renderer).enabled);
					Material val = new Material(((Renderer)anon.renderer).sharedMaterial);
					((Object)val).name = "LoomReskin_" + anon.kind;
					ownedVisuals.assets.Add((Object)(object)val);
					Color val2 = (Color)((anon.kind == VisualKind.Skin) ? jewelry.skinTint : ((anon.kind == VisualKind.Clothing) ? new Color(2.1f, 0.23f, 0.18f, 0f) : new Color(1.4f, 1.16f, 0.66f, 0f)));
					val2.a = 0f;
					if (val.HasProperty("_AlbedoColor"))
					{
						val.SetColor("_AlbedoColor", val2);
					}
					((Renderer)anon.renderer).sharedMaterial = val;
					if (anon.kind == VisualKind.Hair || (jewelry.schemaVersion == 2 && anon.kind == VisualKind.Clothing) || hashSet.Contains(((Component)anon.renderer).transform))
					{
						((Renderer)anon.renderer).enabled = false;
					}
				}
				VisualMaterial[] obj = ((jewelry.schemaVersion == 2) ? jewelry.materials : new VisualMaterial[4]
				{
					new VisualMaterial
					{
						name = "Silver",
						color = new Color(0.72f, 0.78f, 0.83f, 1f),
						metallic = 0.75f,
						smoothness = 0.55f
					},
					new VisualMaterial
					{
						name = "Hair0",
						color = new Color(0.22f, 0.19f, 0.095f, 1f)
					},
					new VisualMaterial
					{
						name = "Hair1",
						color = new Color(0.29f, 0.255f, 0.14f, 1f)
					},
					new VisualMaterial
					{
						name = "Hair2",
						color = new Color(0.16f, 0.14f, 0.075f, 1f)
					}
				});
				Dictionary<string, Material> dictionary = new Dictionary<string, Material>();
				VisualMaterial[] array2 = obj;
				foreach (VisualMaterial visualMaterial in array2)
				{
					Material val3 = new Material(((Renderer)targets[0].renderer).sharedMaterial);
					ownedVisuals.assets.Add((Object)(object)val3);
					((Object)val3).name = "LoomReskin_" + visualMaterial.name;
					string[] array3 = new string[5] { "_AlbedoTexture", "_NormalTexture", "_MetallicTexture", "_SmoothnessTexture", "_EmissionTexture" };
					foreach (string text in array3)
					{
						if (val3.HasProperty(text))
						{
							val3.SetTexture(text, (Texture)null);
						}
					}
					if (val3.HasProperty("_EmissionColor"))
					{
						val3.SetColor("_EmissionColor", Color.black);
					}
					array3 = new string[3] { "_NormalStrength", "_OffsetX", "_OffsetY" };
					foreach (string text2 in array3)
					{
						if (val3.HasProperty(text2))
						{
							val3.SetFloat(text2, 0f);
						}
					}
					array3 = new string[2] { "_TilingX", "_TilingY" };
					foreach (string text3 in array3)
					{
						if (val3.HasProperty(text3))
						{
							val3.SetFloat(text3, 1f);
						}
					}
					Color color = visualMaterial.color;
					color.a = 0f;
					if (val3.HasProperty("_AlbedoColor"))
					{
						val3.SetColor("_AlbedoColor", color);
					}
					if (val3.HasProperty("_Metallic"))
					{
						val3.SetFloat("_Metallic", visualMaterial.metallic);
					}
					if (val3.HasProperty("_Smoothness"))
					{
						val3.SetFloat("_Smoothness", visualMaterial.smoothness);
					}
					if (visualMaterial.texture != null)
					{
						if (!val3.HasProperty("_AlbedoTexture"))
						{
							throw new InvalidDataException("Game shader has no albedo texture input.");
						}
						bool flag = AssetLoader.IsSmoothFaceTexture(visualMaterial.texture);
						Texture2D val4 = new Texture2D(2, 2, (TextureFormat)4, flag, false);
						ownedVisuals.assets.Add((Object)(object)val4);
						if (!ImageConversion.LoadImage(val4, File.ReadAllBytes(Path.Combine(assetDirectory, visualMaterial.texture)), false))
						{
							throw new InvalidDataException("Invalid authored visual texture.");
						}
						if (flag)
						{
							val4.Apply(true, false);
						}
						((Object)val4).name = "LoomReskin_" + visualMaterial.name;
						((Texture)val4).filterMode = (FilterMode)(flag ? 2 : 0);
						((Texture)val4).wrapMode = (TextureWrapMode)(flag ? 1 : 0);
						val3.SetTexture("_AlbedoTexture", (Texture)(object)val4);
						val3.SetTextureScale("_AlbedoTexture", Vector2.one);
						val3.SetTextureOffset("_AlbedoTexture", Vector2.zero);
					}
					dictionary.Add(visualMaterial.name, val3);
				}
				int i;
				for (i = 0; i < jewelry.parts.Length; i++)
				{
					JewelryPart jewelryPart = jewelry.parts[i];
					GameObject val5 = new GameObject("LoomJewelry_" + jewelryPart.name);
					ownedVisuals.additions.Add(val5);
					val5.layer = ((Component)parents[i]).gameObject.layer;
					val5.transform.SetParent(parents[i], false);
					Mesh val6 = new Mesh
					{
						name = jewelryPart.name
					};
					val6.vertices = jewelryPart.vertices;
					val6.triangles = jewelryPart.triangles;
					if (jewelryPart.normals != null)
					{
						val6.normals = jewelryPart.normals;
					}
					else
					{
						val6.RecalculateNormals();
					}
					if (jewelryPart.uv != null)
					{
						val6.uv = jewelryPart.uv;
					}
					val6.RecalculateBounds();
					ownedVisuals.assets.Add((Object)(object)val6);
					val5.AddComponent<MeshFilter>().sharedMesh = val6;
					MeshRenderer obj2 = val5.AddComponent<MeshRenderer>();
					((Renderer)obj2).sharedMaterial = dictionary[jewelryPart.material ?? "Silver"];
					int num3 = ownedVisuals.renderers.FindIndex((MeshRenderer r) => (Object)(object)((Component)r).transform == (Object)(object)parents[i]);
					((Renderer)obj2).enabled = jewelryPart.enabled && (num3 < 0 || (VisualSelection.Classify(jewelryPart.parentPath) != VisualKind.Clothing && !jewelryPart.replacesParentRenderer) || ownedVisuals.enabledStates[num3]);
				}
				return targets.Length;
			}
			catch
			{
				Object.DestroyImmediate((Object)(object)ownedVisuals);
				throw;
			}
		}
	}
	internal static class PreviewCapture
	{
		internal static void Save(GameObject root, string folder)
		{
			//IL_0015: 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_00a4: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			root.transform.position = new Vector3(10000f, 10000f, 10000f);
			Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Component)componentsInChildren[i]).gameObject.layer = 31;
			}
			MeshRenderer[] componentsInChildren2 = root.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren2)
			{
				if (!Visuals.PathOf(((Component)val).transform, root.transform).StartsWith("Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/"))
				{
					((Renderer)val).enabled = false;
				}
			}
			root.SetActive(true);
			GameObject val2 = new GameObject("Loom verification camera");
			GameObject val3 = new GameObject("Loom verification light");
			RenderTexture val4 = new RenderTexture(720, 960, 24);
			try
			{
				Light obj = val3.AddComponent<Light>();
				obj.type = (LightType)1;
				obj.intensity = 1.3f;
				obj.cullingMask = int.MinValue;
				((Component)obj).transform.rotation = Quaternion.Euler(25f, 155f, 0f);
				Camera obj2 = val2.AddComponent<Camera>();
				((Behaviour)obj2).enabled = false;
				obj2.orthographic = true;
				obj2.orthographicSize = 2.08f;
				obj2.aspect = 0.75f;
				obj2.cullingMask = int.MinValue;
				obj2.useOcclusionCulling = false;
				obj2.clearFlags = (CameraClearFlags)2;
				obj2.backgroundColor = new Color(0.16f, 0.17f, 0.19f, 1f);
				obj2.nearClipPlane = 0.1f;
				obj2.farClipPlane = 30f;
				obj2.targetTexture = val4;
				Directory.CreateDirectory(folder);
				Capture(obj2, root, new Vector3(0f, 1.9f, 8f), val4, Path.Combine(folder, "loom-runtime-front.png"));
				Capture(obj2, root, new Vector3(-4f, 1.9f, 6f), val4, Path.Combine(folder, "loom-runtime-three-quarter.png"));
			}
			finally
			{
				root.SetActive(false);
				Object.Destroy((Object)(object)val2);
				Object.Destroy((Object)(object)val3);
				val4.Release();
				Object.Destroy((Object)(object)val4);
			}
		}

		private static void Capture(Camera camera, GameObject root, Vector3 offset, RenderTexture target, string file)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			((Component)camera).transform.position = root.transform.position + offset;
			((Component)camera).transform.LookAt(root.transform.position + new Vector3(0f, 1.9f, 0f));
			camera.Render();
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = target;
			Texture2D val = new Texture2D(((Texture)target).width, ((Texture)target).height, (TextureFormat)3, false);
			try
			{
				val.ReadPixels(new Rect(0f, 0f, (float)((Texture)target).width, (float)((Texture)target).height), 0, 0);
				val.Apply();
				File.WriteAllBytes(file, ImageConversion.EncodeToPNG(val));
			}
			finally
			{
				RenderTexture.active = active;
				Object.Destroy((Object)(object)val);
			}
		}
	}
	[Serializable]
	public sealed class JewelryAsset
	{
		public int schemaVersion;

		public JewelryPart[] parts;

		public VisualMaterial[] materials;

		public Color skinTint;
	}
	[Serializable]
	public sealed class JewelryPart
	{
		public string name;

		public string material;

		public string parentPath;

		public bool enabled = true;

		public bool replacesParentRenderer;

		public Vector3[] vertices;

		public int[] triangles;

		public Vector3[] normals;

		public Vector2[] uv;
	}
	[Serializable]
	public sealed class VisualMaterial
	{
		public string name;

		public Color color;

		public float metallic;

		public float smoothness;

		public string texture;
	}
	public static class AssetLoader
	{
		internal static bool IsSmoothFaceTexture(string texture)
		{
			if (!(texture == "delloom-face.png"))
			{
				return texture == "rodin-face.png";
			}
			return true;
		}

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

		private static bool Finite(Vector3 v)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(v.x) && Finite(v.y))
			{
				return Finite(v.z);
			}
			return false;
		}

		private static bool Unit(float f)
		{
			if (Finite(f) && f >= 0f)
			{
				return f <= 1f;
			}
			return false;
		}

		private static bool ValidColor(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (Unit(c.r) && Unit(c.g) && Unit(c.b))
			{
				return Unit(c.a);
			}
			return false;
		}

		public static JewelryAsset Parse(string json)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			JewelryAsset jewelryAsset = JsonConvert.DeserializeObject<JewelryAsset>(json);
			if (jewelryAsset == null || (jewelryAsset.schemaVersion != 1 && jewelryAsset.schemaVersion != 2) || jewelryAsset.parts == null || jewelryAsset.parts.Length == 0)
			{
				throw new InvalidDataException("Unsupported or empty visual asset.");
			}
			HashSet<string> hashSet = new HashSet<string>();
			Dictionary<string, VisualMaterial> dictionary = new Dictionary<string, VisualMaterial>();
			if (jewelryAsset.schemaVersion == 2)
			{
				if (jewelryAsset.materials == null || jewelryAsset.materials.Length == 0 || !ValidColor(jewelryAsset.skinTint))
				{
					throw new InvalidDataException("Missing visual palette.");
				}
				VisualMaterial[] materials = jewelryAsset.materials;
				foreach (VisualMaterial visualMaterial in materials)
				{
					if (visualMaterial == null || string.IsNullOrEmpty(visualMaterial.name) || dictionary.ContainsKey(visualMaterial.name) || !ValidColor(visualMaterial.color) || !Unit(visualMaterial.metallic) || !Unit(visualMaterial.smoothness))
					{
						throw new InvalidDataException("Invalid visual material.");
					}
					if (visualMaterial.texture != null && visualMaterial.texture != "crimson-fabric.png" && visualMaterial.texture != "aged-bone.png" && visualMaterial.texture != "ochre-hair.png" && !IsSmoothFaceTexture(visualMaterial.texture))
					{
						throw new InvalidDataException("Unsupported visual texture path.");
					}
					dictionary.Add(visualMaterial.name, visualMaterial);
				}
			}
			JewelryPart[] parts = jewelryAsset.parts;
			foreach (JewelryPart p in parts)
			{
				if (p == null || string.IsNullOrEmpty(p.name) || !hashSet.Add(p.name) || string.IsNullOrEmpty(p.parentPath) || !p.parentPath.StartsWith("Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/", StringComparison.Ordinal))
				{
					throw new InvalidDataException("Invalid visual identity or parent path.");
				}
				if (p.vertices == null || p.vertices.Length < 3 || p.vertices.Length > 65535 || p.vertices.Any((Vector3 v) => !Finite(v)) || p.triangles == null || p.triangles.Length == 0 || p.triangles.Length % 3 != 0 || p.triangles.Any((int num) => num < 0 || num >= p.vertices.Length))
				{
					throw new InvalidDataException("Invalid visual mesh.");
				}
				if (p.replacesParentRenderer && (jewelryAsset.schemaVersion != 2 || !p.enabled || !VisualSelection.CanReplaceFace(p.parentPath)))
				{
					throw new InvalidDataException("Invalid face replacement target.");
				}
				if (jewelryAsset.schemaVersion == 2)
				{
					if (p.material == null || !dictionary.ContainsKey(p.material))
					{
						throw new InvalidDataException("Unknown visual material.");
					}
					if (p.normals == null || p.normals.Length != p.vertices.Length || p.normals.Any((Vector3 n) => !Finite(n) || n.x * n.x + n.y * n.y + n.z * n.z < 0.5f))
					{
						throw new InvalidDataException("Invalid authored normals.");
					}
					if (p.uv != null && (p.uv.Length != p.vertices.Length || p.uv.Any((Vector2 v) => !Finite(v.x) || !Finite(v.y))))
					{
						throw new InvalidDataException("Invalid texture coordinates.");
					}
					if (dictionary[p.material].texture != null && p.uv == null)
					{
						throw new InvalidDataException("Textured mesh has no UVs.");
					}
				}
				else if (p.material != "Silver" && p.material != "Hair0" && p.material != "Hair1" && p.material != "Hair2")
				{
					throw new InvalidDataException("Unknown legacy material.");
				}
			}
			return jewelryAsset;
		}
	}
	public enum VisualKind
	{
		Original,
		Clothing,
		Hair,
		Skin
	}
	public static class VisualSelection
	{
		public const string VisualRoot = "Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/";

		public static bool CanReplaceFace(string path)
		{
			if (Classify(path) == VisualKind.Skin)
			{
				if (!path.EndsWith("/Top head (mesh)", StringComparison.Ordinal))
				{
					return path.EndsWith("/Jaw (mesh)", StringComparison.Ordinal);
				}
				return true;
			}
			return false;
		}

		public static VisualKind Classify(string path)
		{
			if (path == null || !path.StartsWith("Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/", StringComparison.Ordinal))
			{
				return VisualKind.Original;
			}
			string text = path.Substring(path.LastIndexOf('/') + 1);
			switch (text)
			{
			case "Dress (mesh)":
			case "Torso (mesh)":
			case "Upper arm (mesh)":
			case "Lower arm (mesh)":
				return VisualKind.Clothing;
			default:
				if (path.Contains("/Head ANIM ⟺/Hair/"))
				{
					switch (text)
					{
					case "Back Hair":
					case "Side hair 1":
					case "Side Hair 2":
					case "Bangs":
					case "Back hair":
						return VisualKind.Hair;
					}
				}
				if ((path.Contains("/Head ANIM ⟺/") && (text == "Top head (mesh)" || text == "Jaw (mesh)")) || (path.Contains("/Neck ANIM ⟺/") && text == "Neck (mesh)"))
				{
					return VisualKind.Skin;
				}
				if (path.StartsWith("Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/Hand RIGHT/", StringComparison.Ordinal) || path.StartsWith("Enable/[VISUALS]/Whole mesh CODE ✢/Whole Mesh ANIM ⟺/Hand LEFT/Hand LEFT/", StringComparison.Ordinal))
				{
					switch (text)
					{
					case "palm (mesh)":
					case "index finger":
					case "middle finger":
					case "ring finger":
					case "pinkie":
					case "thumb":
						return VisualKind.Skin;
					}
				}
				return VisualKind.Original;
			}
		}
	}
}