Decompiled source of Gersemi v0.1.4

plugins/Gersemi.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GBV.Skeid;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[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("Guys Being Vikings")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.4.0")]
[assembly: AssemblyInformationalVersion("0.1.4+2087dfdd650271e6d7e91d296caf46c449767c69")]
[assembly: AssemblyProduct("Gersemi")]
[assembly: AssemblyTitle("Gersemi")]
[assembly: AssemblyVersion("0.1.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GBV.Skeid
{
	internal sealed class ObjModel
	{
		internal sealed class Part
		{
			public string Name;

			public string Material;

			public float[] Vertices;

			public float[] Normals;

			public float[] Uvs;

			public int[] Triangles;

			public float MinX;

			public float MinY;

			public float MinZ;

			public float MaxX;

			public float MaxY;

			public float MaxZ;

			public int VertexCount
			{
				get
				{
					if (Vertices == null)
					{
						return 0;
					}
					return Vertices.Length / 3;
				}
			}

			public int TriangleCount
			{
				get
				{
					if (Triangles == null)
					{
						return 0;
					}
					return Triangles.Length / 3;
				}
			}

			public float CentreX => (MinX + MaxX) * 0.5f;

			public float CentreY => (MinY + MaxY) * 0.5f;

			public float CentreZ => (MinZ + MaxZ) * 0.5f;

			public float SizeX => MaxX - MinX;

			public float SizeY => MaxY - MinY;

			public float SizeZ => MaxZ - MinZ;

			public static void Tangent(float nx, float ny, float nz, float[] into, int at)
			{
				float num = 0f;
				float num2 = 0f;
				float num3 = 0f;
				float num4 = Math.Abs(nx);
				float num5 = Math.Abs(ny);
				float num6 = Math.Abs(nz);
				if (num4 <= num5 && num4 <= num6)
				{
					num = 1f;
				}
				else if (num5 <= num6)
				{
					num2 = 1f;
				}
				else
				{
					num3 = 1f;
				}
				float num7 = ny * num3 - nz * num2;
				float num8 = nz * num - nx * num3;
				float num9 = nx * num2 - ny * num;
				float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9);
				if (num10 < 1E-06f || float.IsNaN(num10) || float.IsInfinity(num10))
				{
					num7 = 1f;
					num8 = 0f;
					num9 = 0f;
					num10 = 1f;
				}
				into[at] = num7 / num10;
				into[at + 1] = num8 / num10;
				into[at + 2] = num9 / num10;
				into[at + 3] = 1f;
			}

			public bool LooksLikeAWheel()
			{
				if (!IsWheelWord(Name))
				{
					return IsWheelWord(Material);
				}
				return true;
			}

			public Part SideOf(bool negative)
			{
				if (Triangles == null || Vertices == null)
				{
					return null;
				}
				Dictionary<int, int> remap = new Dictionary<int, int>();
				List<float> list = new List<float>();
				List<float> list2 = ((Normals != null) ? new List<float>() : null);
				List<float> list3 = ((Uvs != null) ? new List<float>() : null);
				List<int> list4 = new List<int>();
				for (int i = 0; i + 2 < Triangles.Length; i += 3)
				{
					int num = Triangles[i];
					int num2 = Triangles[i + 1];
					int num3 = Triangles[i + 2];
					if ((Vertices[num * 3] + Vertices[num2 * 3] + Vertices[num3 * 3]) / 3f < 0f == negative)
					{
						list4.Add(Copy(num, remap, list, list2, list3));
						list4.Add(Copy(num2, remap, list, list2, list3));
						list4.Add(Copy(num3, remap, list, list2, list3));
					}
				}
				if (list4.Count == 0)
				{
					return null;
				}
				Part part = new Part();
				part.Name = Name + (negative ? "_L" : "_R");
				part.Material = Material;
				part.Vertices = list.ToArray();
				part.Normals = list2?.ToArray();
				part.Uvs = list3?.ToArray();
				part.Triangles = list4.ToArray();
				part.Recalculate();
				return part;
			}

			private int Copy(int index, Dictionary<int, int> remap, List<float> vs, List<float> ns, List<float> uvs)
			{
				if (remap.TryGetValue(index, out var value))
				{
					return value;
				}
				int result = (remap[index] = vs.Count / 3);
				vs.Add(Vertices[index * 3]);
				vs.Add(Vertices[index * 3 + 1]);
				vs.Add(Vertices[index * 3 + 2]);
				if (ns != null)
				{
					ns.Add(Normals[index * 3]);
					ns.Add(Normals[index * 3 + 1]);
					ns.Add(Normals[index * 3 + 2]);
				}
				if (uvs != null)
				{
					uvs.Add(Uvs[index * 2]);
					uvs.Add(Uvs[index * 2 + 1]);
				}
				return result;
			}

			internal void Recalculate()
			{
				MinX = (MinY = (MinZ = float.MaxValue));
				MaxX = (MaxY = (MaxZ = float.MinValue));
				for (int i = 0; i + 2 < Vertices.Length; i += 3)
				{
					float num = Vertices[i];
					float num2 = Vertices[i + 1];
					float num3 = Vertices[i + 2];
					if (num < MinX)
					{
						MinX = num;
					}
					if (num2 < MinY)
					{
						MinY = num2;
					}
					if (num3 < MinZ)
					{
						MinZ = num3;
					}
					if (num > MaxX)
					{
						MaxX = num;
					}
					if (num2 > MaxY)
					{
						MaxY = num2;
					}
					if (num3 > MaxZ)
					{
						MaxZ = num3;
					}
				}
			}

			private static bool IsWheelWord(string s)
			{
				if (string.IsNullOrEmpty(s))
				{
					return false;
				}
				string text = s.ToLowerInvariant();
				if (text.Contains("steer") || text.Contains("ster"))
				{
					return false;
				}
				if (!text.Contains("wheel") && !text.Contains("tyre") && !text.Contains("tire") && !text.Contains("rim"))
				{
					return text.Contains("hubcap");
				}
				return true;
			}
		}

		private sealed class Builder
		{
			public readonly string Name;

			public string Material;

			public readonly List<int> Triangles = new List<int>();

			private readonly Dictionary<long, int> _map = new Dictionary<long, int>();

			private readonly List<float> _vs = new List<float>();

			private readonly List<float> _vns = new List<float>();

			private readonly List<float> _vts = new List<float>();

			private bool _anyNormals;

			private bool _anyUvs;

			public Builder(string name)
			{
				Name = name;
			}

			public int Resolve(string text, int from, int stop, List<float> vs, List<float> vns, List<float> vts, int lineNumber)
			{
				int num = 0;
				int num2 = 0;
				int num3 = 0;
				int num4 = 0;
				int num5 = from;
				while (num5 <= stop)
				{
					int i;
					for (i = num5; i < stop && text[i] != '/'; i++)
					{
					}
					if (i > num5)
					{
						if (!int.TryParse(text.Substring(num5, i - num5), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
						{
							throw new ObjException("line " + lineNumber + " has a face corner that is not a number");
						}
						switch (num4)
						{
						case 0:
							num = result;
							break;
						case 1:
							num2 = result;
							break;
						case 2:
							num3 = result;
							break;
						}
					}
					num4++;
					if (i >= stop)
					{
						break;
					}
					num5 = i + 1;
				}
				int num6 = vs.Count / 3;
				int num7 = vts.Count / 2;
				int num8 = vns.Count / 3;
				num = ((num < 0) ? (num6 + num) : (num - 1));
				num2 = ((num2 < 0) ? (num7 + num2) : (num2 - 1));
				num3 = ((num3 < 0) ? (num8 + num3) : (num3 - 1));
				if (num < 0 || num >= num6)
				{
					throw new ObjException("line " + lineNumber + " refers to vertex " + (num + 1) + ", and the file only has " + num6);
				}
				bool flag = num2 >= 0 && num2 < num7;
				bool flag2 = num3 >= 0 && num3 < num8;
				long key = ((long)(num & 0x1FFFFF) << 42) | ((long)((flag ? num2 : 2097151) & 0x1FFFFF) << 21) | ((flag2 ? num3 : 2097151) & 0x1FFFFF);
				if (_map.TryGetValue(key, out var value))
				{
					return value;
				}
				value = _vs.Count / 3;
				_map[key] = value;
				_vs.Add(vs[num * 3]);
				_vs.Add(vs[num * 3 + 1]);
				_vs.Add(vs[num * 3 + 2]);
				if (flag2)
				{
					_anyNormals = true;
					_vns.Add(vns[num3 * 3]);
					_vns.Add(vns[num3 * 3 + 1]);
					_vns.Add(vns[num3 * 3 + 2]);
				}
				else
				{
					_vns.Add(0f);
					_vns.Add(1f);
					_vns.Add(0f);
				}
				if (flag)
				{
					_anyUvs = true;
					_vts.Add(vts[num2 * 2]);
					_vts.Add(vts[num2 * 2 + 1]);
				}
				else
				{
					_vts.Add(0f);
					_vts.Add(0f);
				}
				return value;
			}

			public Part Build()
			{
				Part part = new Part();
				part.Name = Name;
				part.Material = Material;
				part.Vertices = _vs.ToArray();
				part.Triangles = Triangles.ToArray();
				part.Normals = (_anyNormals ? _vns.ToArray() : null);
				part.Uvs = (_anyUvs ? _vts.ToArray() : null);
				part.Recalculate();
				return part;
			}
		}

		public float MinX;

		public float MinY;

		public float MinZ;

		public float MaxX;

		public float MaxY;

		public float MaxZ;

		internal const int MaxTriangles = 200000;

		private const int MaxCorners = 64;

		private static readonly int[] FaceBuffer = new int[64];

		internal List<Part> Parts { get; } = new List<Part>();

		public float SizeX => MaxX - MinX;

		public float SizeY => MaxY - MinY;

		public float SizeZ => MaxZ - MinZ;

		public int VertexCount
		{
			get
			{
				int num = 0;
				for (int i = 0; i < Parts.Count; i++)
				{
					num += Parts[i].VertexCount;
				}
				return num;
			}
		}

		public int TriangleCount
		{
			get
			{
				int num = 0;
				for (int i = 0; i < Parts.Count; i++)
				{
					num += Parts[i].TriangleCount;
				}
				return num;
			}
		}

		internal List<Part> Wheels()
		{
			List<Part> list = new List<Part>();
			for (int i = 0; i < Parts.Count; i++)
			{
				if (Parts[i].LooksLikeAWheel())
				{
					list.Add(Parts[i]);
				}
			}
			return list;
		}

		internal List<Part> Body()
		{
			List<Part> list = new List<Part>();
			for (int i = 0; i < Parts.Count; i++)
			{
				if (!Parts[i].LooksLikeAWheel())
				{
					list.Add(Parts[i]);
				}
			}
			return list;
		}

		internal static ObjModel Parse(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				throw new ObjException("the file is empty");
			}
			ObjModel objModel = new ObjModel();
			List<float> vs = new List<float>();
			List<float> vns = new List<float>();
			List<float> vts = new List<float>();
			Builder current = null;
			string pendingMaterial = null;
			int num = 0;
			int totalTriangles = 0;
			int num2 = 0;
			while (num2 <= text.Length)
			{
				int num3 = text.IndexOf('\n', num2);
				if (num3 < 0)
				{
					num3 = text.Length;
				}
				int num4 = num3;
				if (num4 > num2 && text[num4 - 1] == '\r')
				{
					num4--;
				}
				num++;
				ReadLine(text, num2, num4, vs, vns, vts, ref current, ref pendingMaterial, ref totalTriangles, objModel, num);
				if (num3 >= text.Length)
				{
					break;
				}
				num2 = num3 + 1;
			}
			Flush(current, objModel);
			if (objModel.Parts.Count == 0 || objModel.TriangleCount == 0)
			{
				throw new ObjException("it has no triangles. Faces are the f lines in an OBJ; a file with only v lines is a point cloud, not a model");
			}
			objModel.Recalculate();
			return objModel;
		}

		private static void ReadLine(string text, int start, int stop, List<float> vs, List<float> vns, List<float> vts, ref Builder current, ref string pendingMaterial, ref int totalTriangles, ObjModel model, int lineNumber)
		{
			while (start < stop && (text[start] == ' ' || text[start] == '\t'))
			{
				start++;
			}
			if (start >= stop || text[start] == '#')
			{
				return;
			}
			int i;
			for (i = start; i < stop && text[i] != ' ' && text[i] != '\t'; i++)
			{
			}
			int num = i - start;
			if (num == 1 && text[start] == 'v')
			{
				ReadFloats(text, i, stop, vs, 3, lineNumber, "v", negateThird: true);
			}
			else if (num == 2 && text[start] == 'v' && text[start + 1] == 'n')
			{
				ReadFloats(text, i, stop, vns, 3, lineNumber, "vn", negateThird: true);
			}
			else if (num == 2 && text[start] == 'v' && text[start + 1] == 't')
			{
				ReadFloats(text, i, stop, vts, 2, lineNumber, "vt", negateThird: false);
			}
			else if (num == 1 && text[start] == 'f')
			{
				if (current == null)
				{
					current = new Builder("car");
					current.Material = pendingMaterial;
				}
				AddFace(text, i, stop, vs, vns, vts, current, ref totalTriangles, lineNumber);
			}
			else if (num == 1 && (text[start] == 'o' || text[start] == 'g'))
			{
				string text2 = Trimmed(text, i, stop);
				if (text2.Length != 0)
				{
					Flush(current, model);
					current = new Builder(text2)
					{
						Material = pendingMaterial
					};
				}
			}
			else
			{
				if (num != 6 || string.CompareOrdinal(text, start, "usemtl", 0, 6) != 0)
				{
					return;
				}
				string text3 = Trimmed(text, i, stop);
				if (!(text3 == pendingMaterial))
				{
					pendingMaterial = text3;
					if (current != null && current.Triangles.Count > 0)
					{
						Flush(current, model);
						current = new Builder(current.Name)
						{
							Material = text3
						};
					}
					else if (current != null)
					{
						current.Material = text3;
					}
				}
			}
		}

		private static void ReadFloats(string text, int from, int stop, List<float> into, int count, int lineNumber, string what, bool negateThird)
		{
			int num = 0;
			int i = from;
			while (num < count)
			{
				for (; i < stop && (text[i] == ' ' || text[i] == '\t'); i++)
				{
				}
				if (i >= stop)
				{
					break;
				}
				int j;
				for (j = i; j < stop && text[j] != ' ' && text[j] != '\t'; j++)
				{
				}
				if (!float.TryParse(text.Substring(i, j - i), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					throw new ObjException("line " + lineNumber + " is a " + what + " with something that is not a number in it");
				}
				if (float.IsNaN(result))
				{
					result = 0f;
				}
				into.Add(result);
				num++;
				i = j;
			}
			if (num < count)
			{
				throw new ObjException("line " + lineNumber + " is a " + what + " with only " + num + " numbers; " + count + " are needed");
			}
			if (negateThird && count == 3)
			{
				into[into.Count - 1] = 0f - into[into.Count - 1];
			}
			if (count == 2)
			{
				into[into.Count - 1] = 1f - into[into.Count - 1];
			}
		}

		private static void AddFace(string text, int from, int stop, List<float> vs, List<float> vns, List<float> vts, Builder part, ref int totalTriangles, int lineNumber)
		{
			int[] faceBuffer = FaceBuffer;
			int num = 0;
			int i = from;
			while (i < stop && num < 64)
			{
				for (; i < stop && (text[i] == ' ' || text[i] == '\t'); i++)
				{
				}
				if (i >= stop)
				{
					break;
				}
				int j;
				for (j = i; j < stop && text[j] != ' ' && text[j] != '\t'; j++)
				{
				}
				faceBuffer[num++] = part.Resolve(text, i, j, vs, vns, vts, lineNumber);
				i = j;
			}
			if (num < 3)
			{
				return;
			}
			for (int k = 1; k + 1 < num; k++)
			{
				if (++totalTriangles > 200000)
				{
					throw new ObjException("it has more than " + 200000 + " triangles, which is far more than a car needs and more than this loads");
				}
				part.Triangles.Add(faceBuffer[0]);
				part.Triangles.Add(faceBuffer[k + 1]);
				part.Triangles.Add(faceBuffer[k]);
			}
		}

		private static void Flush(Builder builder, ObjModel model)
		{
			if (builder != null && builder.Triangles.Count != 0)
			{
				model.Parts.Add(builder.Build());
			}
		}

		private static string Trimmed(string text, int from, int stop)
		{
			while (from < stop && (text[from] == ' ' || text[from] == '\t'))
			{
				from++;
			}
			while (stop > from && (text[stop - 1] == ' ' || text[stop - 1] == '\t'))
			{
				stop--;
			}
			if (from >= stop)
			{
				return string.Empty;
			}
			return text.Substring(from, stop - from);
		}

		internal void Recalculate()
		{
			MinX = (MinY = (MinZ = float.MaxValue));
			MaxX = (MaxY = (MaxZ = float.MinValue));
			for (int i = 0; i < Parts.Count; i++)
			{
				Part part = Parts[i];
				if (part.MinX < MinX)
				{
					MinX = part.MinX;
				}
				if (part.MinY < MinY)
				{
					MinY = part.MinY;
				}
				if (part.MinZ < MinZ)
				{
					MinZ = part.MinZ;
				}
				if (part.MaxX > MaxX)
				{
					MaxX = part.MaxX;
				}
				if (part.MaxY > MaxY)
				{
					MaxY = part.MaxY;
				}
				if (part.MaxZ > MaxZ)
				{
					MaxZ = part.MaxZ;
				}
			}
		}
	}
	internal sealed class ObjException : Exception
	{
		internal ObjException(string message)
			: base(message)
		{
		}
	}
	internal static class GlbModel
	{
		internal struct Shade
		{
			public float R;

			public float G;

			public float B;

			public float A;

			public bool Blend;

			public float Metallic;

			public float Roughness;

			public float Smoothness => 1f - Roughness;
		}

		private struct Span
		{
			public byte[] Data;

			public int Offset;

			public int Stride;
		}

		private const uint Magic = 1179937895u;

		private const uint ChunkJson = 1313821514u;

		private const uint ChunkBinary = 5130562u;

		private const int Byte = 5120;

		private const int UnsignedByte = 5121;

		private const int Short = 5122;

		private const int UnsignedShort = 5123;

		private const int UnsignedInt = 5125;

		private const int Float = 5126;

		internal static ObjModel Parse(byte[] bytes, out Dictionary<string, byte[]> textures, out Dictionary<string, Shade> shades)
		{
			shades = new Dictionary<string, Shade>(StringComparer.Ordinal);
			ObjModel result = Parse(bytes, out textures);
			try
			{
				Split(bytes, out var json, out var _);
				if (json == null)
				{
					return result;
				}
				Json json2 = Json.Parse(json);
				for (int i = 0; i < json2["materials"].Count; i++)
				{
					Json json3 = json2["materials"][i];
					Json json4 = json3["pbrMetallicRoughness"]["baseColorFactor"];
					string asText = json3["alphaMode"].AsText;
					Json json5 = json3["pbrMetallicRoughness"];
					Json json6 = json5["metallicFactor"];
					Json json7 = json5["roughnessFactor"];
					Shade value = new Shade
					{
						R = ((json4.Count > 0) ? json4[0].AsFloat : 1f),
						G = ((json4.Count > 1) ? json4[1].AsFloat : 1f),
						B = ((json4.Count > 2) ? json4[2].AsFloat : 1f),
						A = ((json4.Count > 3) ? json4[3].AsFloat : 1f),
						Metallic = ((json6.Type == Json.Kind.Number) ? json6.AsFloat : 1f),
						Roughness = ((json7.Type == Json.Kind.Number) ? json7.AsFloat : 1f),
						Blend = string.Equals(asText, "BLEND", StringComparison.OrdinalIgnoreCase)
					};
					shades[MaterialName(json2, i)] = value;
				}
			}
			catch
			{
			}
			return result;
		}

		internal static ObjModel Parse(byte[] bytes, out Dictionary<string, byte[]> textures)
		{
			textures = new Dictionary<string, byte[]>(StringComparer.Ordinal);
			ObjModel result = Parse(bytes);
			try
			{
				Split(bytes, out var json, out var binary);
				if (json == null)
				{
					return result;
				}
				Json json2 = Json.Parse(json);
				for (int i = 0; i < json2["materials"].Count; i++)
				{
					int num = json2["materials"][i]["pbrMetallicRoughness"]["baseColorTexture"]["index"].AsInt();
					if (num < 0)
					{
						continue;
					}
					int num2 = json2["textures"][num]["source"].AsInt();
					if (num2 < 0)
					{
						continue;
					}
					int num3 = json2["images"][num2]["bufferView"].AsInt();
					if (num3 >= 0 && binary != null)
					{
						int num4 = json2["bufferViews"][num3]["byteOffset"].AsInt(0);
						int num5 = json2["bufferViews"][num3]["byteLength"].AsInt(0);
						if (num5 > 0 && num4 + num5 <= binary.Length)
						{
							byte[] array = new byte[num5];
							Array.Copy(binary, num4, array, 0, num5);
							textures[MaterialName(json2, i)] = array;
						}
					}
				}
			}
			catch
			{
			}
			return result;
		}

		internal static ObjModel Parse(byte[] bytes)
		{
			if (bytes == null || bytes.Length < 12)
			{
				throw new ObjException("the model file is empty or far too small to be a GLB.");
			}
			if (ReadUInt(bytes, 0) != 1179937895)
			{
				throw new ObjException("this is not a binary glTF file - it does not start with 'glTF'. A .gltf file is JSON and needs its .bin alongside it; use the .glb instead.");
			}
			Split(bytes, out var json, out var binary);
			if (string.IsNullOrEmpty(json))
			{
				throw new ObjException("the GLB has no JSON chunk, so there is nothing to read.");
			}
			Json json2 = Json.Parse(json);
			if (json2["skins"].Count > 0)
			{
				throw new ObjException("this model is skinned to a rig, and the reader only handles plain meshes. Its vertices are in bind pose and would load in the wrong places.");
			}
			return Build(json2, binary);
		}

		private static void Split(byte[] bytes, out string json, out byte[] binary)
		{
			json = null;
			binary = null;
			int num = 12;
			while (num + 8 <= bytes.Length)
			{
				uint num2 = ReadUInt(bytes, num);
				uint num3 = ReadUInt(bytes, num + 4);
				int num4 = num + 8;
				if (num4 + num2 <= bytes.Length)
				{
					switch (num3)
					{
					case 1313821514u:
						json = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false).GetString(bytes, num4, (int)num2);
						break;
					case 5130562u:
						binary = new byte[num2];
						Array.Copy(bytes, num4, binary, 0, (int)num2);
						break;
					}
					num = num4 + (int)num2;
					continue;
				}
				break;
			}
		}

		private static ObjModel Build(Json root, byte[] binary)
		{
			ObjModel objModel = new ObjModel();
			Json json = root["nodes"];
			Json json2 = root["meshes"];
			Dictionary<int, float[]> dictionary = new Dictionary<int, float[]>();
			List<int> list = new List<int>();
			Json json3 = root["scenes"][root["scene"].AsInt(0)]["nodes"];
			for (int i = 0; i < json3.Count; i++)
			{
				list.Add(json3[i].AsInt());
			}
			if (list.Count == 0)
			{
				for (int j = 0; j < json.Count; j++)
				{
					list.Add(j);
				}
			}
			foreach (int item in list)
			{
				Walk(json, item, Identity(), dictionary);
			}
			foreach (KeyValuePair<int, float[]> item2 in dictionary)
			{
				Json json4 = json[item2.Key];
				int num = json4["mesh"].AsInt();
				if (num < 0)
				{
					continue;
				}
				string name = Clean(json4["name"].AsText) ?? ("node" + item2.Key);
				Json json5 = json2[num]["primitives"];
				for (int k = 0; k < json5.Count; k++)
				{
					ObjModel.Part part = Primitive(root, binary, json5[k], name, item2.Value);
					if (part != null)
					{
						objModel.Parts.Add(part);
					}
				}
			}
			Bounds(objModel);
			return objModel;
		}

		private static ObjModel.Part Primitive(Json root, byte[] binary, Json primitive, string name, float[] transform)
		{
			if (primitive["mode"].AsInt(4) != 4)
			{
				return null;
			}
			float[] array = ReadFloats(root, binary, primitive["attributes"]["POSITION"], 3);
			if (array == null || array.Length == 0)
			{
				return null;
			}
			float[] array2 = ReadFloats(root, binary, primitive["attributes"]["NORMAL"], 3);
			float[] uvs = ReadFloats(root, binary, primitive["attributes"]["TEXCOORD_0"], 2);
			int[] triangles = ReadIndices(root, binary, primitive["indices"], array.Length / 3);
			ObjModel.Part part = new ObjModel.Part
			{
				Name = name,
				Material = MaterialName(root, primitive["material"].AsInt()),
				Vertices = new float[array.Length],
				Normals = ((array2 != null && array2.Length == array.Length) ? new float[array2.Length] : null),
				Uvs = uvs,
				Triangles = triangles
			};
			for (int i = 0; i + 2 < array.Length; i += 3)
			{
				float num = array[i];
				float num2 = array[i + 1];
				float num3 = array[i + 2];
				part.Vertices[i] = transform[0] * num + transform[4] * num2 + transform[8] * num3 + transform[12];
				part.Vertices[i + 1] = transform[1] * num + transform[5] * num2 + transform[9] * num3 + transform[13];
				part.Vertices[i + 2] = transform[2] * num + transform[6] * num2 + transform[10] * num3 + transform[14];
				part.Vertices[i + 2] = 0f - part.Vertices[i + 2];
			}
			if (part.Normals != null)
			{
				for (int j = 0; j + 2 < array2.Length; j += 3)
				{
					float num4 = array2[j];
					float num5 = array2[j + 1];
					float num6 = array2[j + 2];
					float num7 = transform[0] * num4 + transform[4] * num5 + transform[8] * num6;
					float num8 = transform[1] * num4 + transform[5] * num5 + transform[9] * num6;
					float num9 = transform[2] * num4 + transform[6] * num5 + transform[10] * num6;
					float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9);
					if (num10 > 1E-09f)
					{
						num7 /= num10;
						num8 /= num10;
						num9 /= num10;
					}
					part.Normals[j] = num7;
					part.Normals[j + 1] = num8;
					part.Normals[j + 2] = 0f - num9;
				}
			}
			if (part.Uvs != null)
			{
				for (int k = 1; k < part.Uvs.Length; k += 2)
				{
					part.Uvs[k] = 1f - part.Uvs[k];
				}
			}
			for (int l = 0; l + 2 < part.Triangles.Length; l += 3)
			{
				int num11 = part.Triangles[l + 1];
				part.Triangles[l + 1] = part.Triangles[l + 2];
				part.Triangles[l + 2] = num11;
			}
			part.Recalculate();
			return part;
		}

		private static float[] ReadFloats(Json root, byte[] binary, Json accessorIndex, int size)
		{
			int num = accessorIndex.AsInt();
			if (num < 0)
			{
				return null;
			}
			Json json = root["accessors"][num];
			int num2 = json["count"].AsInt(0);
			int num3 = json["componentType"].AsInt(5126);
			if (num2 <= 0)
			{
				return null;
			}
			float[] array = new float[num2 * size];
			Span span = View(root, binary, json);
			if (span.Data == null)
			{
				return null;
			}
			int num4 = ((span.Stride > 0) ? span.Stride : (size * SizeOf(num3)));
			for (int i = 0; i < num2; i++)
			{
				int num5 = span.Offset + i * num4;
				for (int j = 0; j < size; j++)
				{
					int num6 = num5 + j * SizeOf(num3);
					if (num6 + SizeOf(num3) > span.Data.Length)
					{
						return array;
					}
					array[i * size + j] = ((num3 == 5126) ? BitConverter.ToSingle(span.Data, num6) : Normalised(span.Data, num6, num3));
				}
			}
			return array;
		}

		private static int[] ReadIndices(Json root, byte[] binary, Json accessorIndex, int vertices)
		{
			int num = accessorIndex.AsInt();
			if (num < 0)
			{
				int[] array = new int[vertices];
				for (int i = 0; i < vertices; i++)
				{
					array[i] = i;
				}
				return array;
			}
			Json json = root["accessors"][num];
			int num2 = json["count"].AsInt(0);
			int num3 = json["componentType"].AsInt(5123);
			int[] array2 = new int[num2];
			Span span = View(root, binary, json);
			if (span.Data == null)
			{
				return array2;
			}
			int num4 = SizeOf(num3);
			int num5 = ((span.Stride > 0) ? span.Stride : num4);
			for (int j = 0; j < num2; j++)
			{
				int num6 = span.Offset + j * num5;
				if (num6 + num4 > span.Data.Length)
				{
					break;
				}
				switch (num3)
				{
				case 5125:
					array2[j] = (int)BitConverter.ToUInt32(span.Data, num6);
					break;
				case 5123:
					array2[j] = BitConverter.ToUInt16(span.Data, num6);
					break;
				case 5121:
					array2[j] = span.Data[num6];
					break;
				default:
					array2[j] = BitConverter.ToUInt16(span.Data, num6);
					break;
				}
			}
			return array2;
		}

		private static Span View(Json root, byte[] binary, Json accessor)
		{
			Span result = default(Span);
			int num = accessor["bufferView"].AsInt();
			if (num < 0)
			{
				return result;
			}
			Json json = root["bufferViews"][num];
			result.Data = binary;
			result.Offset = json["byteOffset"].AsInt(0) + accessor["byteOffset"].AsInt(0);
			result.Stride = json["byteStride"].AsInt(0);
			return result;
		}

		private static int SizeOf(int component)
		{
			switch (component)
			{
			case 5120:
			case 5121:
				return 1;
			case 5122:
			case 5123:
				return 2;
			default:
				return 4;
			}
		}

		private static float Normalised(byte[] data, int at, int component)
		{
			return component switch
			{
				5120 => Math.Max((float)(sbyte)data[at] / 127f, -1f), 
				5121 => (float)(int)data[at] / 255f, 
				5122 => Math.Max((float)BitConverter.ToInt16(data, at) / 32767f, -1f), 
				5123 => (float)(int)BitConverter.ToUInt16(data, at) / 65535f, 
				_ => BitConverter.ToSingle(data, at), 
			};
		}

		private static void Walk(Json nodes, int index, float[] parent, Dictionary<int, float[]> into)
		{
			if (index >= 0 && index < nodes.Count && !into.ContainsKey(index))
			{
				Json json = nodes[index];
				float[] parent2 = (into[index] = Multiply(parent, Local(json)));
				Json json2 = json["children"];
				for (int i = 0; i < json2.Count; i++)
				{
					Walk(nodes, json2[i].AsInt(), parent2, into);
				}
			}
		}

		private static float[] Local(Json node)
		{
			Json json = node["matrix"];
			if (json.Count == 16)
			{
				float[] array = new float[16];
				for (int i = 0; i < 16; i++)
				{
					array[i] = json[i].AsFloat;
				}
				return array;
			}
			float asFloat = node["translation"][0].AsFloat;
			float asFloat2 = node["translation"][1].AsFloat;
			float asFloat3 = node["translation"][2].AsFloat;
			float asFloat4 = node["rotation"][0].AsFloat;
			float asFloat5 = node["rotation"][1].AsFloat;
			float asFloat6 = node["rotation"][2].AsFloat;
			float num = (node.Has("rotation") ? node["rotation"][3].AsFloat : 1f);
			float num2 = (node.Has("scale") ? node["scale"][0].AsFloat : 1f);
			float num3 = (node.Has("scale") ? node["scale"][1].AsFloat : 1f);
			float num4 = (node.Has("scale") ? node["scale"][2].AsFloat : 1f);
			return new float[16]
			{
				(1f - 2f * (asFloat5 * asFloat5 + asFloat6 * asFloat6)) * num2,
				2f * (asFloat4 * asFloat5 + asFloat6 * num) * num2,
				2f * (asFloat4 * asFloat6 - asFloat5 * num) * num2,
				0f,
				2f * (asFloat4 * asFloat5 - asFloat6 * num) * num3,
				(1f - 2f * (asFloat4 * asFloat4 + asFloat6 * asFloat6)) * num3,
				2f * (asFloat5 * asFloat6 + asFloat4 * num) * num3,
				0f,
				2f * (asFloat4 * asFloat6 + asFloat5 * num) * num4,
				2f * (asFloat5 * asFloat6 - asFloat4 * num) * num4,
				(1f - 2f * (asFloat4 * asFloat4 + asFloat5 * asFloat5)) * num4,
				0f,
				asFloat,
				asFloat2,
				asFloat3,
				1f
			};
		}

		private static float[] Identity()
		{
			float[] array = new float[16];
			array[0] = (array[5] = (array[10] = (array[15] = 1f)));
			return array;
		}

		private static float[] Multiply(float[] a, float[] b)
		{
			float[] array = new float[16];
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					float num = 0f;
					for (int k = 0; k < 4; k++)
					{
						num += a[k * 4 + j] * b[i * 4 + k];
					}
					array[i * 4 + j] = num;
				}
			}
			return array;
		}

		private static string MaterialName(Json root, int index)
		{
			if (index < 0)
			{
				return null;
			}
			string text = Clean(root["materials"][index]["name"].AsText);
			if (!string.IsNullOrEmpty(text))
			{
				return text + "#" + index;
			}
			return "material_" + index;
		}

		private static string Clean(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			foreach (char c in text)
			{
				if (c != '\ufffd' && !char.IsControl(c))
				{
					stringBuilder.Append(c);
				}
			}
			string text2 = stringBuilder.ToString().Trim().TrimStart('.', '_', '-')
				.Trim();
			if (text2.Length != 0)
			{
				return text2;
			}
			return null;
		}

		private static void Bounds(ObjModel model)
		{
			model.MinX = (model.MinY = (model.MinZ = float.MaxValue));
			model.MaxX = (model.MaxY = (model.MaxZ = float.MinValue));
			foreach (ObjModel.Part part in model.Parts)
			{
				if (part.MinX < model.MinX)
				{
					model.MinX = part.MinX;
				}
				if (part.MinY < model.MinY)
				{
					model.MinY = part.MinY;
				}
				if (part.MinZ < model.MinZ)
				{
					model.MinZ = part.MinZ;
				}
				if (part.MaxX > model.MaxX)
				{
					model.MaxX = part.MaxX;
				}
				if (part.MaxY > model.MaxY)
				{
					model.MaxY = part.MaxY;
				}
				if (part.MaxZ > model.MaxZ)
				{
					model.MaxZ = part.MaxZ;
				}
			}
			if (model.Parts.Count == 0)
			{
				model.MinX = (model.MinY = (model.MinZ = 0f));
				model.MaxX = (model.MaxY = (model.MaxZ = 0f));
			}
		}

		private static uint ReadUInt(byte[] bytes, int at)
		{
			return BitConverter.ToUInt32(bytes, at);
		}
	}
	internal sealed class Json
	{
		internal enum Kind
		{
			Null,
			Bool,
			Number,
			Text,
			Array,
			Object
		}

		private bool _bool;

		private double _number;

		private string _text;

		private List<Json> _array;

		private Dictionary<string, Json> _object;

		internal static readonly Json Nothing = new Json
		{
			Type = Kind.Null
		};

		internal Kind Type { get; private set; }

		internal bool AsBool
		{
			get
			{
				if (Type == Kind.Bool)
				{
					return _bool;
				}
				return false;
			}
		}

		internal double AsNumber
		{
			get
			{
				if (Type != Kind.Number)
				{
					return 0.0;
				}
				return _number;
			}
		}

		internal float AsFloat => (float)AsNumber;

		internal string AsText
		{
			get
			{
				if (Type != Kind.Text)
				{
					return null;
				}
				return _text;
			}
		}

		internal int Count
		{
			get
			{
				if (Type != Kind.Array)
				{
					if (Type != Kind.Object)
					{
						return 0;
					}
					return _object.Count;
				}
				return _array.Count;
			}
		}

		internal Json this[string key]
		{
			get
			{
				if (Type != Kind.Object)
				{
					return Nothing;
				}
				if (!_object.TryGetValue(key, out var value))
				{
					return Nothing;
				}
				return value;
			}
		}

		internal Json this[int index]
		{
			get
			{
				if (Type != Kind.Array || index < 0 || index >= _array.Count)
				{
					return Nothing;
				}
				return _array[index];
			}
		}

		internal int AsInt(int fallback = -1)
		{
			if (Type != Kind.Number)
			{
				return fallback;
			}
			return (int)Math.Round(_number);
		}

		internal bool Has(string key)
		{
			if (Type == Kind.Object)
			{
				return _object.ContainsKey(key);
			}
			return false;
		}

		internal static Json Parse(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return Nothing;
			}
			int at = 0;
			return Read(text, ref at) ?? Nothing;
		}

		private static Json Read(string s, ref int at)
		{
			SkipSpace(s, ref at);
			if (at >= s.Length)
			{
				return Nothing;
			}
			switch (s[at])
			{
			case '{':
				return ReadObject(s, ref at);
			case '[':
				return ReadArray(s, ref at);
			case '"':
				return new Json
				{
					Type = Kind.Text,
					_text = ReadString(s, ref at)
				};
			default:
				if (Matches(s, at, "true"))
				{
					at += 4;
					return new Json
					{
						Type = Kind.Bool,
						_bool = true
					};
				}
				if (Matches(s, at, "false"))
				{
					at += 5;
					return new Json
					{
						Type = Kind.Bool,
						_bool = false
					};
				}
				if (Matches(s, at, "null"))
				{
					at += 4;
					return Nothing;
				}
				return ReadNumber(s, ref at);
			}
		}

		private static Json ReadObject(string s, ref int at)
		{
			Json json = new Json
			{
				Type = Kind.Object,
				_object = new Dictionary<string, Json>(StringComparer.Ordinal)
			};
			at++;
			while (at < s.Length)
			{
				SkipSpace(s, ref at);
				if (at >= s.Length)
				{
					break;
				}
				if (s[at] == '}')
				{
					at++;
					break;
				}
				if (s[at] == ',')
				{
					at++;
					continue;
				}
				if (s[at] != '"')
				{
					at++;
					continue;
				}
				string key = ReadString(s, ref at);
				SkipSpace(s, ref at);
				if (at < s.Length && s[at] == ':')
				{
					at++;
				}
				json._object[key] = Read(s, ref at);
			}
			return json;
		}

		private static Json ReadArray(string s, ref int at)
		{
			Json json = new Json
			{
				Type = Kind.Array,
				_array = new List<Json>()
			};
			at++;
			while (at < s.Length)
			{
				SkipSpace(s, ref at);
				if (at >= s.Length)
				{
					break;
				}
				if (s[at] == ']')
				{
					at++;
					break;
				}
				if (s[at] == ',')
				{
					at++;
				}
				else
				{
					json._array.Add(Read(s, ref at));
				}
			}
			return json;
		}

		private static string ReadString(string s, ref int at)
		{
			StringBuilder stringBuilder = new StringBuilder();
			at++;
			while (at < s.Length)
			{
				char c = s[at++];
				switch (c)
				{
				default:
					stringBuilder.Append(c);
					continue;
				case '\\':
				{
					if (at >= s.Length)
					{
						break;
					}
					char c2 = s[at++];
					switch (c2)
					{
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '"':
						stringBuilder.Append('"');
						break;
					case 'u':
					{
						if (at + 4 <= s.Length && int.TryParse(s.Substring(at, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
						{
							stringBuilder.Append((char)result);
							at += 4;
						}
						break;
					}
					default:
						stringBuilder.Append(c2);
						break;
					}
					continue;
				}
				case '"':
					break;
				}
				break;
			}
			return stringBuilder.ToString();
		}

		private static Json ReadNumber(string s, ref int at)
		{
			int num = at;
			while (at < s.Length)
			{
				char c = s[at];
				if ((c < '0' || c > '9') && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E')
				{
					break;
				}
				at++;
			}
			if (!double.TryParse(s.Substring(num, at - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return Nothing;
			}
			return new Json
			{
				Type = Kind.Number,
				_number = result
			};
		}

		private static void SkipSpace(string s, ref int at)
		{
			while (at < s.Length && char.IsWhiteSpace(s[at]))
			{
				at++;
			}
		}

		private static bool Matches(string s, int at, string word)
		{
			if (at + word.Length <= s.Length)
			{
				return string.CompareOrdinal(s, at, word, 0, word.Length) == 0;
			}
			return false;
		}
	}
}
namespace GBV.Gersemi
{
	internal static class Assets
	{
		internal static string Override => Path.Combine(Paths.ConfigPath, "Gersemi");

		internal static string PluginFolder
		{
			get
			{
				try
				{
					return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				}
				catch
				{
					return null;
				}
			}
		}

		internal static string Find(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			try
			{
				if (Path.IsPathRooted(name))
				{
					return File.Exists(name) ? name : null;
				}
				string text = Path.Combine(Override, name);
				if (File.Exists(text))
				{
					return text;
				}
				string pluginFolder = PluginFolder;
				if (!string.IsNullOrEmpty(pluginFolder))
				{
					string text2 = Path.Combine(pluginFolder, name);
					if (File.Exists(text2))
					{
						return text2;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		internal static void EnsureOverride()
		{
			try
			{
				if (!Directory.Exists(Override))
				{
					Directory.CreateDirectory(Override);
				}
			}
			catch (Exception ex)
			{
				Safety.Once("Assets.EnsureOverride", "could not create " + Override + " (" + ex.Message + "). The shipped gear still works; you just cannot override the art from there.");
			}
		}
	}
	internal static class Commands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__1_0;

			internal void <Register>b__1_0(ConsoleEventArgs args)
			{
				Run(args);
			}
		}

		private static bool _registered;

		internal static void Register()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			_registered = true;
			try
			{
				object obj = <>c.<>9__1_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs args)
					{
						Run(args);
					};
					<>c.<>9__1_0 = val;
					obj = (object)val;
				}
				new ConsoleCommand("gersemi", "Gersemi: probe, fit, shader, give, list. Run it with no argument for help.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
			catch (Exception ex)
			{
				Safety.Failed("Commands.Register", ex);
			}
		}

		private static void Run(ConsoleEventArgs args)
		{
			try
			{
				switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "")
				{
				case "probe":
					Probe(args);
					break;
				case "fit":
					Fit(args);
					break;
				case "shader":
					ShaderVerb(args);
					break;
				case "fx":
					Fx(args);
					break;
				case "color":
				case "colour":
					Colour(args);
					break;
				case "give":
					Give(args);
					break;
				case "list":
					List(args);
					break;
				case "rebuild":
					Rebuild(args);
					break;
				default:
					Say(args, "gersemi probe            what was cloned, what it was painted with, and where the embers are\ngersemi list            what Gersemi adds, and whether it built\ngersemi give [name]     put one in your pack\ngersemi fit ...         yaw, pitch, roll, scale, x, y, z - nudge the helm on the head\ngersemi shader <n>      repaint a worn helm from a different donor\ngersemi fx [name]       borrow the fire off one of the game's own weapons; no name lists them\ngersemi colour <name>   Ember, Muspel, Wyrd or Draugr\ngersemi rebuild         build the prefabs again from the current settings");
					break;
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("Commands.Run", ex);
				Say(args, "that threw; see the log.");
			}
		}

		private static void Probe(ConsoleEventArgs args)
		{
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: 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_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: 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_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Gersemi ").Append("0.1.4").Append('\n');
			stringBuilder.Append("shader: ").Append(HelmMaterial.ShaderUsed).Append("  from ")
				.Append(HelmMaterial.DonorUsed);
			if (HelmMaterial.ShaderUsed == "Custom/Piece")
			{
				stringBuilder.Append("   <<< THIS IS THE FLICKERING FAMILY. It samples noise by world position, and a helmet on a head moves every frame.");
			}
			stringBuilder.Append('\n');
			foreach (GearPrefab.Built item in GearPrefab.Everything)
			{
				if (item == null)
				{
					continue;
				}
				stringBuilder.Append(item.Def.PrefabName).Append(": cloned from ").Append(item.ClonedFrom ?? "nothing")
					.Append(", ")
					.Append(item.Triangles)
					.Append(" triangles")
					.Append(", fitted scale ")
					.Append(F(item.Scale))
					.Append(item.Painted ? ", painted" : ", NOT PAINTED - wearing the donor helmet's own textures");
				if (item.UsedOverrideModel)
				{
					stringBuilder.Append(", USING THE OVERRIDE MODEL");
				}
				stringBuilder.Append('\n');
				GameObject prefab = item.Prefab;
				if ((Object)(object)prefab == (Object)null)
				{
					stringBuilder.Append("  (not built)\n");
					continue;
				}
				int num = 0;
				Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if ((Object)(object)componentsInChildren[i] != (Object)null)
					{
						num++;
					}
				}
				bool flag = (Object)(object)prefab.GetComponentInChildren<Rigidbody>(true) != (Object)null;
				stringBuilder.Append("  physics: ").Append(num).Append(" collider(s), ")
					.Append(flag ? "a rigidbody" : "NO RIGIDBODY");
				if (num == 0)
				{
					stringBuilder.Append("   <<< IT WILL FALL THROUGH THE WORLD WHEN DROPPED");
				}
				stringBuilder.Append('\n');
				MeshFilter[] componentsInChildren2 = prefab.GetComponentsInChildren<MeshFilter>(true);
				Bounds val2;
				foreach (MeshFilter val in componentsInChildren2)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null))
					{
						StringBuilder stringBuilder2 = stringBuilder.Append("  mesh '").Append(((Object)((Component)val).gameObject).name).Append("' under '")
							.Append(((Object)(object)((Component)val).transform.parent != (Object)null) ? ((Object)((Component)val).transform.parent).name : "?")
							.Append("' size ");
						val2 = val.sharedMesh.bounds;
						StringBuilder stringBuilder3 = stringBuilder2.Append(V(((Bounds)(ref val2)).size)).Append(" centred ");
						val2 = val.sharedMesh.bounds;
						stringBuilder3.Append(V(((Bounds)(ref val2)).center)).Append(" xform ").Append(V(((Component)val).transform.localPosition))
							.Append(" scale ")
							.Append(F(((Component)val).transform.localScale.x))
							.Append('\n');
					}
				}
				foreach (KeyValuePair<string, Bounds> donorBound in item.DonorBounds)
				{
					StringBuilder stringBuilder4 = stringBuilder.Append("  donor '").Append(donorBound.Key).Append("' size ");
					val2 = donorBound.Value;
					StringBuilder stringBuilder5 = stringBuilder4.Append(V(((Bounds)(ref val2)).size)).Append(" centred ");
					val2 = donorBound.Value;
					stringBuilder5.Append(V(((Bounds)(ref val2)).center)).Append('\n');
				}
				Transform val3 = prefab.transform.Find("attach");
				stringBuilder.Append("  attach: ").Append(((Object)(object)val3 == (Object)null) ? "MISSING" : "present").Append('\n');
				if (!((Object)(object)val3 != (Object)null))
				{
					continue;
				}
				Transform val4 = val3.Find("equiped");
				stringBuilder.Append("  embers: ").Append(((Object)(object)val4 == (Object)null) ? "MISSING - the child must be called 'equiped', with one p, or the game never switches it on" : (((Component)val4).GetComponentsInChildren<ParticleSystem>(true).Length + " systems, " + ((Component)val4).GetComponentsInChildren<Light>(true).Length + " light")).Append('\n');
				Renderer[] componentsInChildren3 = ((Component)val3).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val5 in componentsInChildren3)
				{
					if ((Object)(object)val5 == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials = val5.sharedMaterials;
					foreach (Material val6 in sharedMaterials)
					{
						if (!((Object)(object)val6 == (Object)null))
						{
							stringBuilder.Append("  material ").Append(((Object)val6).name).Append(" shader ")
								.Append(((Object)(object)val6.shader != (Object)null) ? ((Object)val6.shader).name : "none")
								.Append(" keywords ")
								.Append(HelmMaterial.Keywords(val6))
								.Append(" maps ")
								.Append(HelmMaterial.Maps(val6));
							if (val6.IsKeywordEnabled("_VALUENOISEVERTEX_ON"))
							{
								stringBuilder.Append("   <<< WORLD-SPACE VERTEX NOISE, this flickers when worn");
							}
							stringBuilder.Append('\n');
						}
					}
				}
			}
			HelmMesh.Built built = HelmBuild.Geometry();
			stringBuilder.Append("horn tips: right ").Append(V(built.RightHornTip)).Append("  left ")
				.Append(V(built.LeftHornTip))
				.Append('\n');
			stringBuilder.Append("fit: yaw ").Append(F(GersemiPlugin.FitYaw.Value)).Append(" pitch ")
				.Append(F(GersemiPlugin.FitPitch.Value))
				.Append(" roll ")
				.Append(F(GersemiPlugin.FitRoll.Value))
				.Append(" scale ")
				.Append(F(GersemiPlugin.FitScale.Value))
				.Append(" offset ")
				.Append(F(GersemiPlugin.FitOffsetX.Value))
				.Append(',')
				.Append(F(GersemiPlugin.FitOffsetY.Value))
				.Append(',')
				.Append(F(GersemiPlugin.FitOffsetZ.Value))
				.Append(" flipped ")
				.Append(GersemiPlugin.FlipWinding.Value);
			if (GersemiPlugin.FitYaw.Value != 0f || GersemiPlugin.FitPitch.Value != 0f || GersemiPlugin.FitRoll.Value != 0f || GersemiPlugin.FitScale.Value != 1f || GersemiPlugin.FitOffsetX.Value != 0f || GersemiPlugin.FitOffsetY.Value != 0f || GersemiPlugin.FitOffsetZ.Value != 0f || GersemiPlugin.FlipWinding.Value)
			{
				stringBuilder.Append("   <<< NOT THE DEFAULTS. If the helm is missing or in the wrong place, gersemi fit reset");
			}
			stringBuilder.Append('\n');
			stringBuilder.Append("fire: ").Append(string.IsNullOrEmpty(GersemiPlugin.EmberDonor.Value) ? ("Gersemi's own, palette " + EmberPlan.Chosen) : ("borrowed from " + Kindling.Borrowed)).Append(", drawn with ")
				.Append(EmberCrown.ShaderUsed);
			if (EmberCrown.ShaderUsed.IndexOf("additive", StringComparison.OrdinalIgnoreCase) < 0)
			{
				stringBuilder.Append("   <<< NOT ADDITIVE, so the fire will look flat rather than hot");
			}
			stringBuilder.Append('\n');
			stringBuilder.Append("donors available: ");
			foreach (string donorName in HelmMaterial.DonorNames)
			{
				stringBuilder.Append(donorName).Append(' ');
			}
			Say(args, stringBuilder.ToString());
		}

		private static void Fit(ConsoleEventArgs args)
		{
			string text = ((args.Length > 2) ? args[2].ToLowerInvariant() : "");
			if (text.Length == 0 || (text != "reset" && text != "flip" && args.Length < 4))
			{
				Say(args, "gersemi fit <yaw|pitch|roll|scale|x|y|z> <number>\ngersemi fit flip      turn the surfaces inside out\ngersemi fit reset     back to the fitted defaults\nfor example: gersemi fit yaw 90");
				return;
			}
			if (text == "reset")
			{
				GersemiPlugin.FitYaw.Value = 0f;
				GersemiPlugin.FitPitch.Value = 0f;
				GersemiPlugin.FitRoll.Value = 0f;
				GersemiPlugin.FitScale.Value = 1f;
				GersemiPlugin.FitOffsetX.Value = 0f;
				GersemiPlugin.FitOffsetY.Value = 0f;
				GersemiPlugin.FitOffsetZ.Value = 0f;
				GersemiPlugin.FlipWinding.Value = false;
				Rebuild(args);
				Say(args, "fit reset.\n" + FitLine());
				return;
			}
			if (text == "flip")
			{
				GersemiPlugin.FlipWinding.Value = !GersemiPlugin.FlipWinding.Value;
				Rebuild(args);
				Say(args, "winding flipped to " + GersemiPlugin.FlipWinding.Value + ".\n" + FitLine());
				return;
			}
			if (!float.TryParse(args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				Say(args, "'" + args[3] + "' is not a number.");
				return;
			}
			switch (text)
			{
			case "yaw":
				GersemiPlugin.FitYaw.Value = result;
				break;
			case "pitch":
				GersemiPlugin.FitPitch.Value = result;
				break;
			case "roll":
				GersemiPlugin.FitRoll.Value = result;
				break;
			case "scale":
				GersemiPlugin.FitScale.Value = result;
				break;
			case "x":
				GersemiPlugin.FitOffsetX.Value = result;
				break;
			case "y":
				GersemiPlugin.FitOffsetY.Value = result;
				break;
			case "z":
				GersemiPlugin.FitOffsetZ.Value = result;
				break;
			default:
				Say(args, "nudge one of: yaw, pitch, roll, scale, x, y, z, flip, reset.");
				return;
			}
			Rebuild(args);
			Say(args, FitLine());
		}

		private static string FitLine()
		{
			return "paste into BepInEx\\config\\gbv.valheim.gersemi.cfg under [Fit]:\nYaw = " + F(GersemiPlugin.FitYaw.Value) + "   Pitch = " + F(GersemiPlugin.FitPitch.Value) + "   Roll = " + F(GersemiPlugin.FitRoll.Value) + "   Scale = " + F(GersemiPlugin.FitScale.Value) + "\nOffsetX = " + F(GersemiPlugin.FitOffsetX.Value) + "   OffsetY = " + F(GersemiPlugin.FitOffsetY.Value) + "   OffsetZ = " + F(GersemiPlugin.FitOffsetZ.Value) + "   FlipWinding = " + GersemiPlugin.FlipWinding.Value;
		}

		private static void ShaderVerb(ConsoleEventArgs args)
		{
			int result;
			string said;
			if (args.Length < 3)
			{
				StringBuilder stringBuilder = new StringBuilder("gersemi shader <n>, where n is:\n");
				int num = 0;
				foreach (string donorName in HelmMaterial.DonorNames)
				{
					stringBuilder.Append("  ").Append(num++).Append(' ')
						.Append(donorName)
						.Append('\n');
				}
				stringBuilder.Append("currently ").Append(HelmMaterial.ShaderUsed).Append(" from ")
					.Append(HelmMaterial.DonorUsed);
				Say(args, stringBuilder.ToString());
			}
			else if (!int.TryParse(args[2], out result))
			{
				Say(args, "'" + args[2] + "' is not a number.");
			}
			else if (!HelmMaterial.Rebind(result, out said))
			{
				Say(args, said);
			}
			else
			{
				Rebuild(args);
				Say(args, said + "\nRe-equip the helm to see it.");
			}
		}

		private static void Colour(ConsoleEventArgs args)
		{
			if (args.Length < 3)
			{
				StringBuilder stringBuilder = new StringBuilder("gersemi colour <name>. Currently ").Append(EmberPlan.Chosen).Append(".\n");
				EmberPlan.Palette[] array = (EmberPlan.Palette[])Enum.GetValues(typeof(EmberPlan.Palette));
				foreach (EmberPlan.Palette palette in array)
				{
					stringBuilder.Append("  ").Append(palette).Append('\n');
				}
				stringBuilder.Append("Recolour is ").Append(GersemiPlugin.Recolour.Value ? "on" : "OFF").Append(", so a borrowed effect ")
					.Append(GersemiPlugin.Recolour.Value ? "follows this too." : "keeps the donor's own colours.");
				Say(args, stringBuilder.ToString());
			}
			else
			{
				EmberPlan.Palette chosen = EmberPlan.ParsePalette(args[2]);
				GersemiPlugin.EmberColour.Value = chosen.ToString();
				EmberPlan.Chosen = chosen;
				EmberCrown.Reset();
				Kindling.Reset();
				Rebuild(args);
				Say(args, "fire is now " + chosen.ToString() + ".");
			}
		}

		private static void Fx(ConsoleEventArgs args)
		{
			if (args.Length < 3)
			{
				List<string> list = Kindling.Candidates();
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("currently burning with: ").Append(string.IsNullOrEmpty(GersemiPlugin.EmberDonor.Value) ? "Gersemi's own fire" : ("borrowed from " + Kindling.Borrowed)).Append('\n');
				stringBuilder.Append("gersemi fx <name>        borrow that weapon's effect\n");
				stringBuilder.Append("gersemi fx any           let Gersemi pick the best one here\n");
				stringBuilder.Append("gersemi fx off           go back to Gersemi's own fire\n");
				stringBuilder.Append("gersemi fx dump <name>   read out how that effect is built\n");
				stringBuilder.Append("gersemi fx drop <layer>  leave one layer of it out\n");
				stringBuilder.Append("gersemi fx keep <layer>  put that layer back\n\n");
				stringBuilder.Append(list.Count).Append(" weapons in this world have an effect to lend:\n");
				foreach (string item in list)
				{
					stringBuilder.Append("  ").Append(item).Append('\n');
				}
				Say(args, stringBuilder.ToString());
				return;
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			for (int i = 2; i < args.Length; i++)
			{
				if (stringBuilder2.Length > 0)
				{
					stringBuilder2.Append(' ');
				}
				stringBuilder2.Append(args[i]);
			}
			string text = stringBuilder2.ToString();
			if (text.StartsWith("drop", StringComparison.OrdinalIgnoreCase) || text.StartsWith("keep", StringComparison.OrdinalIgnoreCase))
			{
				bool flag = text.StartsWith("drop", StringComparison.OrdinalIgnoreCase);
				string text2 = ((text.Length > 4) ? text.Substring(4).Trim() : "");
				if (text2.Length == 0)
				{
					StringBuilder stringBuilder3 = new StringBuilder();
					stringBuilder3.Append("gersemi fx drop <layer>   leave that layer out\n");
					stringBuilder3.Append("gersemi fx keep <layer>   put it back\n");
					stringBuilder3.Append("currently skipping: ").Append(string.IsNullOrEmpty(GersemiPlugin.SkipLayers.Value) ? "(nothing)" : GersemiPlugin.SkipLayers.Value).Append('\n');
					foreach (string item2 in Kindling.Layers(GersemiPlugin.EmberDonor.Value))
					{
						stringBuilder3.Append("  ").Append(item2).Append('\n');
					}
					Say(args, stringBuilder3.ToString());
					return;
				}
				List<string> list2 = new List<string>();
				string[] array = GersemiPlugin.SkipLayers.Value.Split(',');
				for (int j = 0; j < array.Length; j++)
				{
					string text3 = array[j].Trim();
					if (text3.Length != 0 && !string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase))
					{
						list2.Add(text3);
					}
				}
				if (flag)
				{
					list2.Add(text2);
				}
				GersemiPlugin.SkipLayers.Value = string.Join(", ", list2.ToArray());
				Kindling.Reset();
				Rebuild(args);
				Say(args, (flag ? "dropped '" : "kept '") + text2 + "'.\nSkipLayers = " + ((GersemiPlugin.SkipLayers.Value.Length == 0) ? "(nothing)" : GersemiPlugin.SkipLayers.Value));
			}
			else if (text.StartsWith("dump", StringComparison.OrdinalIgnoreCase))
			{
				string text4 = ((text.Length > 4) ? text.Substring(4).Trim() : "");
				if (text4.Length == 0)
				{
					Say(args, "gersemi fx dump <name>, for example: gersemi fx dump Frostfire Dagger");
				}
				else
				{
					Say(args, Kindling.Describe(text4));
				}
			}
			else
			{
				if (string.Equals(text, "off", StringComparison.OrdinalIgnoreCase))
				{
					text = "";
				}
				GersemiPlugin.EmberDonor.Value = text;
				Kindling.Reset();
				Rebuild(args);
				Say(args, ((text.Length == 0) ? "back to Gersemi's own fire." : ("burning with '" + text + "'.")) + "\nRe-equip the helm to see it. Written to BorrowFrom in the config.");
			}
		}

		private static void Give(ConsoleEventArgs args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Say(args, "you have to be in a world.");
				return;
			}
			string text = ((args.Length > 2) ? args[2] : "GS_Muspelhelm");
			GearDef gearDef = Gear.Find(text);
			if (gearDef == null)
			{
				Say(args, "Gersemi has nothing called '" + text + "'. Try gersemi list.");
				return;
			}
			Registry.Sync();
			GameObject val = GearPrefab.PrefabFor(gearDef.PrefabName);
			if ((Object)(object)val == (Object)null)
			{
				Say(args, gearDef.PrefabName + " has not been built. Run gersemi probe.");
				return;
			}
			if (!((Humanoid)localPlayer).GetInventory().AddItem(val, 1))
			{
				Say(args, "no room in your pack.");
				return;
			}
			localPlayer.AddKnownItem(val.GetComponent<ItemDrop>().m_itemData);
			Say(args, "gave you a " + gearDef.DisplayName + ".");
		}

		private static void List(ConsoleEventArgs args)
		{
			StringBuilder stringBuilder = new StringBuilder();
			GearDef[] all = Gear.All;
			foreach (GearDef gearDef in all)
			{
				GearPrefab.Built built = GearPrefab.Of(gearDef.PrefabName);
				stringBuilder.Append(gearDef.PrefabName).Append("  \"").Append(gearDef.DisplayName)
					.Append("\"  ")
					.Append(((Object)(object)built?.Prefab != (Object)null) ? ("built from " + built.ClonedFrom) : "NOT BUILT")
					.Append('\n');
			}
			Say(args, stringBuilder.ToString());
		}

		private static void Rebuild(ConsoleEventArgs args)
		{
			Registry.Reapply();
		}

		private static void Say(ConsoleEventArgs args, string message)
		{
			Terminal context = args.Context;
			if (context != null)
			{
				context.AddString(message);
			}
			ManualLogSource log = GersemiPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Gersemi: " + message));
			}
		}

		private static string F(float f)
		{
			return f.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static string V(float[] v)
		{
			if (v != null)
			{
				return "(" + F(v[0]) + ", " + F(v[1]) + ", " + F(v[2]) + ")";
			}
			return "none";
		}

		private static string V(Vector3 v)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			return "(" + F(v.x) + ", " + F(v.y) + ", " + F(v.z) + ")";
		}
	}
	internal static class EmberCrown
	{
		internal const string EquippedChild = "equiped";

		internal const int FlipbookCols = 4;

		internal const int FlipbookRows = 4;

		private static Material _flameMaterial;

		private static Material _sparkMaterial;

		private static Shader _particleShader;

		internal static string ShaderUsed
		{
			get
			{
				if (!((Object)(object)_particleShader != (Object)null))
				{
					return "(none yet)";
				}
				return ((Object)_particleShader).name;
			}
		}

		internal static GameObject Build(Transform attach, HelmMesh.Built geometry, float scale, Vector3 seat)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)attach == (Object)null || geometry == null)
			{
				return null;
			}
			if (Icons.Headless())
			{
				return null;
			}
			if (GersemiPlugin.Embers != null && !GersemiPlugin.Embers.Value)
			{
				return null;
			}
			try
			{
				Transform val = attach.Find("equiped");
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
				}
				GameObject val2 = new GameObject("equiped");
				val2.transform.SetParent(attach, false);
				val2.transform.localPosition = seat;
				val2.transform.localRotation = Quaternion.identity;
				val2.transform.localScale = Vector3.one;
				EmberPlan.Chosen = EmberPlan.ParsePalette((GersemiPlugin.EmberColour != null) ? GersemiPlugin.EmberColour.Value : null);
				Mesh val3 = HornMesh(geometry, scale);
				if ((Object)(object)Kindling.Build(val2.transform, val3, scale) == (Object)null)
				{
					Emitter(val2.transform, "Flame", val3, EmberPlan.Plan.Flame());
					Emitter(val2.transform, "Sparks", val3, EmberPlan.Plan.Sparks());
				}
				BrowLight(val2.transform, geometry.BrowFront, scale);
				val2.AddComponent<BrowGlimmer>();
				val2.SetActive(false);
				return val2;
			}
			catch (Exception ex)
			{
				Safety.Failed("EmberCrown.Build", ex);
				return null;
			}
		}

		private static Mesh HornMesh(HelmMesh.Built geometry, float scale)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			float[] hornSurface = geometry.HornSurface;
			if (hornSurface == null || hornSurface.Length < 9)
			{
				return null;
			}
			float num = (geometry.Model.MinX + geometry.Model.MaxX) * 0.5f;
			float num2 = (geometry.Model.MinZ + geometry.Model.MaxZ) * 0.5f;
			int num3 = hornSurface.Length / 3;
			Vector3[] array = (Vector3[])(object)new Vector3[num3];
			int[] array2 = new int[num3];
			for (int i = 0; i < num3; i++)
			{
				array[i] = new Vector3((hornSurface[i * 3] - num) * scale, hornSurface[i * 3 + 1] * scale, (hornSurface[i * 3 + 2] - num2) * scale);
				array2[i] = i;
			}
			Mesh val = new Mesh
			{
				name = "GersemiHornEmitter",
				hideFlags = (HideFlags)61
			};
			val.SetVertices(array);
			val.SetTriangles(array2, 0);
			val.RecalculateNormals();
			val.RecalculateBounds();
			return val;
		}

		private static void Emitter(Transform parent, string name, Mesh shapeMesh, EmberPlan.Plan plan)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_011e: 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_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)shapeMesh == (Object)null))
			{
				GameObject val = new GameObject(name);
				val.transform.SetParent(parent, false);
				val.transform.localPosition = HelmBuild.FitOffset();
				val.transform.localRotation = HelmBuild.FitRotation();
				float num = 1f;
				ParticleSystem val2 = val.AddComponent<ParticleSystem>();
				val2.Stop(true, (ParticleSystemStopBehavior)0);
				MainModule main = val2.main;
				((MainModule)(ref main)).loop = true;
				((MainModule)(ref main)).playOnAwake = true;
				((MainModule)(ref main)).startLifetime = new MinMaxCurve(plan.LifetimeMin, plan.LifetimeMax);
				((MainModule)(ref main)).startSpeed = new MinMaxCurve(plan.RiseMin * num, plan.RiseMax * num);
				((MainModule)(ref main)).startSize = new MinMaxCurve(plan.SizeMin * num, plan.SizeMax * num);
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(Color.white);
				((MainModule)(ref main)).gravityModifier = MinMaxCurve.op_Implicit((0f - plan.Gravity) * 0.1f);
				((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)0;
				((MainModule)(ref main)).maxParticles = 48;
				((MainModule)(ref main)).startRotation = new MinMaxCurve(0f, 6.283f);
				EmissionModule emission = val2.emission;
				((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(plan.Rate * Rate());
				ShapeModule shape = val2.shape;
				((ShapeModule)(ref shape)).enabled = true;
				((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)6;
				((ShapeModule)(ref shape)).mesh = shapeMesh;
				((ShapeModule)(ref shape)).meshShapeType = (ParticleSystemMeshShapeType)2;
				((ShapeModule)(ref shape)).useMeshMaterialIndex = false;
				((ShapeModule)(ref shape)).radius = plan.EmitterRadius * num;
				LimitVelocityOverLifetimeModule limitVelocityOverLifetime = val2.limitVelocityOverLifetime;
				((LimitVelocityOverLifetimeModule)(ref limitVelocityOverLifetime)).enabled = true;
				((LimitVelocityOverLifetimeModule)(ref limitVelocityOverLifetime)).dampen = Mathf.Clamp01(plan.Drag * 0.1f);
				VelocityOverLifetimeModule velocityOverLifetime = val2.velocityOverLifetime;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = true;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).space = (ParticleSystemSimulationSpace)0;
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).z = new MinMaxCurve((0f - plan.Drift) * num);
				((VelocityOverLifetimeModule)(ref velocityOverLifetime)).x = new MinMaxCurve((0f - plan.Spread) * num, plan.Spread * num);
				NoiseModule noise = val2.noise;
				((NoiseModule)(ref noise)).enabled = true;
				((NoiseModule)(ref noise)).strength = MinMaxCurve.op_Implicit(plan.Wander * num * 8f);
				((NoiseModule)(ref noise)).frequency = plan.WanderRate;
				((NoiseModule)(ref noise)).damping = true;
				ColourOverLife(val2);
				SizeOverLife(val2);
				Renderer(val2, plan);
				if (plan.Stretch > 0.5f)
				{
					TextureSheetAnimationModule textureSheetAnimation = val2.textureSheetAnimation;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).enabled = true;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).numTilesX = 4;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).numTilesY = 4;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).animation = (ParticleSystemAnimationType)0;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).timeMode = (ParticleSystemAnimationTimeMode)0;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).cycleCount = 1;
					((TextureSheetAnimationModule)(ref textureSheetAnimation)).startFrame = new MinMaxCurve(0f, 16f);
				}
				val2.Play();
			}
		}

		private static void ColourOverLife(ParticleSystem system)
		{
			//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_0016: Unknown result type (might be due to invalid IL or missing references)
			ColorOverLifetimeModule colorOverLifetime = system.colorOverLifetime;
			((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true;
			((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(PaletteGradient());
		}

		internal static Gradient PaletteGradient()
		{
			//IL_0031: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			GradientColorKey[] array = (GradientColorKey[])(object)new GradientColorKey[8];
			GradientAlphaKey[] array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[8];
			for (int i = 0; i < 8; i++)
			{
				float num = (float)i / 7f;
				EmberPlan.ColourAt(num, out var r, out var g, out var b, out var a);
				array[i] = new GradientColorKey(new Color(r, g, b), num);
				array2[i] = new GradientAlphaKey(a, num);
			}
			Gradient val = new Gradient();
			val.SetKeys(array, array2);
			return val;
		}

		private static void SizeOverLife(ParticleSystem system)
		{
			//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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			SizeOverLifetimeModule sizeOverLifetime = system.sizeOverLifetime;
			((SizeOverLifetimeModule)(ref sizeOverLifetime)).enabled = true;
			AnimationCurve val = new AnimationCurve();
			for (int i = 0; i <= 10; i++)
			{
				float num = (float)i / 10f;
				val.AddKey(num, EmberPlan.SizeAt(num));
			}
			((SizeOverLifetimeModule)(ref sizeOverLifetime)).size = new MinMaxCurve(1f, val);
		}

		private static void Renderer(ParticleSystem system, EmberPlan.Plan plan)
		{
			ParticleSystemRenderer component = ((Component)system).GetComponent<ParticleSystemRenderer>();
			if (!((Object)(object)component == (Object)null))
			{
				if (plan.Stretch > 0.5f)
				{
					component.renderMode = (ParticleSystemRenderMode)1;
					component.lengthScale = 1f + plan.Stretch;
					component.velocityScale = 0f;
				}
				else
				{
					component.renderMode = (ParticleSystemRenderMode)0;
					component.alignment = (ParticleSystemRenderSpace)0;
				}
				((Renderer)component).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)component).receiveShadows = false;
				Material val = Additive(plan.Stretch > 0.5f);
				if ((Object)(object)val != (Object)null)
				{
					((Renderer)component).material = val;
				}
			}
		}

		internal static Shader ParticleShader()
		{
			if ((Object)(object)_particleShader != (Object)null)
			{
				return _particleShader;
			}
			string[] array = new string[4] { "Torch", "TorchMist", "FireWorkRocket", "Firestaff" };
			foreach (string text in array)
			{
				GameObject val = Kindling.Find(text);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ParticleSystemRenderer[] componentsInChildren = val.GetComponentsInChildren<ParticleSystemRenderer>(true);
				foreach (ParticleSystemRenderer val2 in componentsInChildren)
				{
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Renderer)val2).sharedMaterial == (Object)null) && !((Object)(object)((Renderer)val2).sharedMaterial.shader == (Object)null))
					{
						string name = ((Object)((Renderer)val2).sharedMaterial.shader).name;
						if (name.IndexOf("additive", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							_particleShader = ((Renderer)val2).sharedMaterial.shader;
							GersemiPlugin.Verbose("fire drawn with " + name + ", taken off " + text + ".");
							return _particleShader;
						}
					}
				}
			}
			array = new string[4] { "Legacy Shaders/Particles/Additive", "Particles/Additive", "Legacy Shaders/Particles/Alpha Blended Premultiply", "Sprites/Default" };
			foreach (string text2 in array)
			{
				Shader val3 = Shader.Find(text2);
				if (!((Object)(object)val3 == (Object)null))
				{
					_particleShader = val3;
					GersemiPlugin.Verbose("fire drawn with " + text2 + " (from Shader.Find).");
					return _particleShader;
				}
			}
			return null;
		}

		private static Material Additive(bool flipbook)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			if (flipbook && (Object)(object)_flameMaterial != (Object)null)
			{
				return _flameMaterial;
			}
			if (!flipbook && (Object)(object)_sparkMaterial != (Object)null)
			{
				return _sparkMaterial;
			}
			Shader val = ParticleShader();
			if ((Object)(object)val == (Object)null)
			{
				Safety.Once("EmberCrown.NoShader", "no particle shader could be found, so the embers will be drawn with whatever Unity defaults to. Run gersemi probe.");
				return null;
			}
			Material val2 = new Material(val)
			{
				name = (flipbook ? "GersemiFlame" : "GersemiSpark"),
				hideFlags = (HideFlags)61
			};
			Texture2D val3 = (flipbook ? Sheet() : Sprite(EmberSprite.Glyph.Ember));
			if ((Object)(object)val3 != (Object)null && val2.HasProperty("_MainTex"))
			{
				val2.SetTexture("_MainTex", (Texture)(object)val3);
			}
			if (flipbook)
			{
				_flameMaterial = val2;
			}
			else
			{
				_sparkMaterial = val2;
			}
			return val2;
		}

		private static Texture2D Sheet()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				int num = 256;
				int num2 = 256;
				float[] array = new float[num * num2 * 4];
				EmberSprite.RenderSheet(64, 4, 4, array);
				Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false)
				{
					name = "GersemiFlameSheet",
					filterMode = (FilterMode)1,
					wrapMode = (TextureWrapMode)1,
					hideFlags = (HideFlags)61
				};
				Color[] array2 = (Color[])(object)new Color[num * num2];
				for (int i = 0; i < num2; i++)
				{
					int num3 = (num2 - 1 - i) * num;
					for (int j = 0; j < num; j++)
					{
						int num4 = (num3 + j) * 4;
						array2[i * num + j] = new Color(array[num4], array[num4 + 1], array[num4 + 2], array[num4 + 3]);
					}
				}
				val.SetPixels(array2);
				val.Apply(false);
				return val;
			}
			catch (Exception ex)
			{
				Safety.Failed("EmberCrown.Sheet", ex);
				return null;
			}
		}

		private static Texture2D Sprite(EmberSprite.Glyph glyph)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float[] array = new float[16384];
				EmberSprite.Render(64, glyph, array);
				Texture2D val = new Texture2D(64, 64, (TextureFormat)4, true)
				{
					name = "GersemiEmber_" + glyph,
					filterMode = (FilterMode)1,
					wrapMode = (TextureWrapMode)1,
					hideFlags = (HideFlags)61
				};
				Color[] array2 = (Color[])(object)new Color[4096];
				for (int i = 0; i < 64; i++)
				{
					int num = (63 - i) * 64;
					for (int j = 0; j < 64; j++)
					{
						int num2 = (num + j) * 4;
						array2[i * 64 + j] = new Color(array[num2], array[num2 + 1], array[num2 + 2], array[num2 + 3]);
					}
				}
				val.SetPixels(array2);
				val.Apply(true);
				return val;
			}
			catch (Exception ex)
			{
				Safety.Failed("EmberCrown.Sprite", ex);
				return null;
			}
		}

		internal static void Reset()
		{
			_particleShader = null;
			_flameMaterial = null;
			_sparkMaterial = null;
		}

		private static float Rate()
		{
			return Mathf.Clamp((GersemiPlugin.EmberRate != null) ? GersemiPlugin.EmberRate.Value : 1f, 0f, 4f);
		}

		private static void BrowLight(Transform parent, float[] at, float scale)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			if (at != null && (GersemiPlugin.BrowLight == null || GersemiPlugin.BrowLight.Value))
			{
				GameObject val = new GameObject("BrowLight");
				val.transform.SetParent(parent, false);
				val.transform.localPosition = new Vector3(at[0], at[1], at[2]) * scale;
				Light obj = val.AddComponent<Light>();
				obj.type = (LightType)2;
				obj.color = new Color(1f, 0.62f, 0.26f);
				obj.range = Mathf.Clamp((GersemiPlugin.BrowLightRange != null) ? GersemiPlugin.BrowLightRange.Value : 1.6f, 0.2f, 6f);
				obj.intensity = 0.85f;
				obj.shadows = (LightShadows)0;
				obj.renderMode = (LightRenderMode)2;
			}
		}
	}
	internal sealed class BrowGlimmer : MonoBehaviour
	{
		private Light _light;

		private Material _trim;

		private Color _base;

		private bool _looked;

		private float _offset;

		private void Update()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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)
			try
			{
				if (!_looked)
				{
					Look();
				}
				float num = EmberPlan.Glimmer(Time.time + _offset);
				if ((Object)(object)_trim != (Object)null && _trim.HasProperty("_EmissionColor"))
				{
					_trim.SetColor("_EmissionColor", _base * Depth() * (0.15f + 0.85f * num));
				}
				if ((Object)(object)_light != (Object)null)
				{
					_light.intensity = 0.55f + 0.5f * num;
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("BrowGlimmer.Update", ex);
				((Behaviour)this).enabled = false;
			}
		}

		private static float Depth()
		{
			return Mathf.Clamp((GersemiPlugin.BrowGlow != null) ? GersemiPlugin.BrowGlow.Value : 0.14f, 0f, 1f);
		}

		private void Look()
		{
			//IL_00b6: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			_looked = true;
			_offset = Random.value * 10f;
			_light = ((Component)this).GetComponentInChildren<Light>(true);
			Transform parent = ((Component)this).transform.parent;
			if ((Object)(object)parent == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material[] materials = val.materials;
				foreach (Material val2 in materials)
				{
					if (!((Object)(object)val2 == (Object)null) && ((Object)val2).name != null && ((Object)val2).name.IndexOf("GersemiHotTrim", StringComparison.Ordinal) >= 0)
					{
						_trim = val2;
						_base = (val2.HasProperty("_EmissionColor") ? HelmMaterial.ColourFor("GersemiHotTrim") : Color.white);
						return;
					}
				}
			}
		}
	}
	internal static class EmberPlan
	{
		internal enum Palette
		{
			Ember,
			Muspel,
			Wyrd,
			Draugr
		}

		internal sealed class Plan
		{
			public string Name = "layer";

			public float Stretch;

			public float Intensity = 1f;

			public float Rate = 16f;

			public float LifetimeMin = 0.45f;

			public float LifetimeMax = 0.9f;

			public float SizeMin = 0.007f;

			public float SizeMax = 0.017f;

			public float RiseMin = 0.13f;

			public float RiseMax = 0.29f;

			public float Spread = 0.075f;

			public float Drift = 0.08f;

			public float Drag = 2.4f;

			public float Gravity = -0.26f;

			public float EmitterRadius = 0.01f;

			public float Wander = 0.03f;

			public float WanderRate = 1.7f;

			internal static Plan Flame()
			{
				return new Plan
				{
					Name = "flame",
					Rate = 210f,
					LifetimeMin = 0.2f,
					LifetimeMax = 0.44f,
					SizeMin = 0.016f,
					SizeMax = 0.036f,
					RiseMin = 0.26f,
					RiseMax = 0.58f,
					Spread = 0.05f,
					Drift = 0.04f,
					Drag = 4.4f,
					Gravity = -0.04f,
					EmitterRadius = 0.006f,
					Wander = 0.014f,
					WanderRate = 3.4f,
					Stretch = 0.85f,
					Intensity = 1.3f
				};
			}

			internal static Plan Sparks()
			{
				return new Plan
				{
					Name = "sparks",
					Rate = 70f,
					LifetimeMin = 0.55f,
					LifetimeMax = 1.25f,
					SizeMin = 0.007f,
					SizeMax = 0.019f,
					RiseMin = 0.34f,
					RiseMax = 0.78f,
					Spread = 0.1f,
					Drift = 0.09f,
					Drag = 2.1f,
					Gravity = -0.3f,
					EmitterRadius = 0.013f,
					Wander = 0.042f,
					WanderRate = 2.1f,
					Stretch = 0.25f,
					Intensity = 1f
				};
			}
		}

		internal sealed class Surface
		{
			private readonly float[] _tris;

			private readonly float[] _cumulative;

			internal float TotalArea { get; }

			internal int TriangleCount => _tris.Length / 9;

			internal Surface(float[] triangles)
			{
				_tris = triangles ?? new float[0];
				int num = _tris.Length / 9;
				_cumulative = new float[num];
				float num2 = 0f;
				for (int i = 0; i < num; i++)
				{
					num2 += Area(i);
					_cumulative[i] = num2;
				}
				TotalArea = num2;
			}

			private float Area(int tri)
			{
				int num = tri * 9;
				float num2 = _tris[num + 3] - _tris[num];
				float num3 = _tris[num + 4] - _tris[num + 1];
				float num4 = _tris[num + 5] - _tris[num + 2];
				float num5 = _tris[num + 6] - _tris[num];
				float num6 = _tris[num + 7] - _tris[num + 1];
				float num7 = _tris[num + 8] - _tris[num + 2];
				float num8 = num3 * num7 - num4 * num6;
				float num9 = num4 * num5 - num2 * num7;
				float num10 = num2 * num6 - num3 * num5;
				return 0.5f * (float)Math.Sqrt(num8 * num8 + num9 * num9 + num10 * num10);
			}

			internal void PointAt(float pick, float u, float v, out float x, out float y, out float z)
			{
				x = (y = (z = 0f));
				if (_cumulative.Length == 0 || TotalArea <= 0f)
				{
					return;
				}
				float num = pick * TotalArea;
				int num2 = 0;
				int num3 = _cumulative.Length - 1;
				while (num2 < num3)
				{
					int num4 = (num2 + num3) / 2;
					if (_cumulative[num4] < num)
					{
						num2 = num4 + 1;
					}
					else
					{
						num3 = num4;
					}
				}
				if (u + v > 1f)
				{
					u = 1f - u;
					v = 1f - v;
				}
				int num5 = num2 * 9;
				float num6 = 1f - u - v;
				x = _tris[num5] * num6 + _tris[num5 + 3] * u + _tris[num5 + 6] * v;
				y = _tris[num5 + 1] * num6 + _tris[num5 + 4] * u + _tris[num5 + 7] * v;
				z = _tris[num5 + 2] * num6 + _tris[num5 + 5] * u + _tris[num5 + 8] * v;
			}
		}

		internal struct Particle
		{
			public float X;

			public float Y;

			public float Z;

			public float Size;

			public float Age;

			public float R;

			public float G;

			public float B;

			public float A;

			public float Stretch;

			public float VX;

			public float VY;

			public float VZ;
		}

		internal static Palette Chosen;

		internal static void ColourAt(float age, out float r, out float g, out float b, out float a)
		{
			ColourAt(age, Chosen, out r, out g, out b, out a);
		}

		internal static void ColourAt(float age, Palette palette, out float r, out float g, out float b, out float a)
		{
			if (age < 0f)
			{
				age = 0f;
			}
			if (age > 1f)
			{
				age = 1f;
			}
			float[] array = Stops(palette);
			if (age < 0.18f)
			{
				float t = age / 0.18f;
				r = Lerp(array[0], array[3], t);
				g = Lerp(array[1], array[4], t);
				b = Lerp(array[2], array[5], t);
			}
			else if (age < 0.52f)
			{
				float t2 = (age - 0.18f) / 0.34f;
				r = Lerp(array[3], array[6], t2);
				g = Lerp(array[4], array[7], t2);
				b = Lerp(array[5], array[8], t2);
			}
			else
			{
				float t3 = (age - 0.52f) / 0.48f;
				r = Lerp(array[6], array[9], t3);
				g = Lerp(array[7], array[10], t3);
				b = Lerp(array[8], array[11], t3);
			}
			float num = ((age < 0.04f) ? (age / 0.04f) : 1f);
			float num2 = ((age > 0.62f) ? (1f - (age - 0.62f) / 0.38f) : 1f);
			a = num * num2;
			if (a < 0f)
			{
				a = 0f;
			}
		}

		private static float[] Stops(Palette palette)
		{
			return palette switch
			{
				Palette.Muspel => new float[12]
				{
					1f, 1f, 1f, 0.72f, 0.9f, 1f, 0.26f, 0.48f, 1f, 0.1f,
					0.06f, 0.42f
				}, 
				Palette.Wyrd => new float[12]
				{
					1f, 1f, 0.92f, 0.72f, 1f, 0.58f, 0.2f, 0.88f, 0.26f, 0.02f,
					0.24f, 0.06f
				}, 
				Palette.Draugr => new float[12]
				{
					1f, 0.96f, 1f, 0.86f, 0.62f, 1f, 0.54f, 0.16f, 0.92f, 0.14f,
					0.02f, 0.28f
				}, 
				_ => new float[12]
				{
					1f, 0.85f, 0.45f, 1f, 0.52f, 0.11f, 0.94f, 0.24f, 0.03f, 0.32f,
					0.04f, 0.01f
				}, 
			};
		}

		internal static Palette ParsePalette(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return Palette.Ember;
			}
			Palette[] array = (Palette[])Enum.GetValues(typeof(Palette));
			for (int i = 0; i < array.Length; i++)
			{
				Palette result = array[i];
				if (string.Equals(result.ToString(), name.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					return result;
				}
			}
			return Palette.Ember;
		}

		internal static float SizeAt(float age)
		{
			if (age < 0f)
			{
				age = 0f;
			}
			if (age > 1f)
			{
				age = 1f;
			}
			if (age < 0.12f)
			{
				return Lerp(0.55f, 1f, age / 0.12f);
			}
			float num = (age - 0.12f) / 0.88f;
			return Lerp(1f, 0.12f, num * num);
		}

		internal static List<Particle> Simulate(float time, Surface surface, int seed, Plan plan, float rateScale = 1f)
		{
			List<Particle> result = new List<Particle>();
			if (plan == null || surface == null || surface.TriangleCount == 0 || time <= 0f)
			{
				return result;
			}
			return Simulate(time, null, surface, seed, plan, rateScale);
		}

		internal static List<Particle> Simulate(float time, float[] emitter, int seed, Plan plan, float rateScale = 1f)
		{
			return Simulate(time, emitter, null, seed, plan, rateScale);
		}

		private static List<Particle> Simulate(float time, float[] emitter, Surface surface, int seed, Plan plan, float rateScale)
		{
			List<Particle> list = new List<Particle>();
			if (plan == null || time <= 0f)
			{
				return list;
			}
			if (emitter == null && surface == null)
			{
				return list;
			}
			float num = Math.Max(0.01f, plan.Rate * Math.Max(0f, rateScale));
			float num2 = 1f / num;
			int num3 = (int)Math.Floor(time / num2);
			int num4 = (int)Math.Floor((time - plan.LifetimeMax) / num2) - 1;
			if (num4 < 0)
			{
				num4 = 0;
			}
			for (int i = num4; i <= num3; i++)
			{
				float num5 = (float)i * num2;
				float num6 = Lerp(plan.LifetimeMin, plan.LifetimeMax, Rand(i, seed, 1));
				float num7 = time - num5;
				if (!(num7 < 0f) && !(num7 > num6))
				{
					float num8 = num7 / num6;
					float x;
					float y;
					float z;
					if (surface != null)
					{
						surface.PointAt(Rand(i, seed, 10), Rand(i, seed, 11), Rand(i, seed, 12), out x, out y, out z);
					}
					else
					{
						x = emitter[0];
						y = emitter[1];
						z = emitter[2];
					}
					float num9 = (Rand(i, seed, 2) - 0.5f) * 2f * plan.EmitterRadius;
					float num10 = (Rand(i, seed, 3) - 0.5f) * 2f * plan.EmitterRadius;
					float num11 = (Rand(i, seed, 4) - 0.5f) * 2f * plan.EmitterRadius;
					float num12 = (Rand(i, seed, 5) - 0.5f) * 2f * plan.Spread;
					float num13 = Lerp(plan.RiseMin, plan.RiseMax, Rand(i, seed, 6));
					float num14 = (Rand(i, seed, 7) - 0.5f) * 2f * plan.Spread - plan.Drift;
					float num15 = ((plan.Drag > 0.0001f) ? ((1f - (float)Math.Exp((0f - plan.Drag) * num7)) / plan.Drag) : num7);
					float num16 = x + num9 + num12 * num15;
					float y2 = y + num10 + num13 * num15 + 0.5f * plan.Gravity * num7 * num7;
					float num17 = z + num11 + num14 * num15;
					float num18 = Rand(i, seed, 8) * (MathF.PI * 2f);
					num16 += (float)Math.Sin(num18 + num7 * plan.WanderRate * (MathF.PI * 2f)) * plan.Wander * num8;
					num17 += (float)Math.Cos(num18 * 1.7f + num7 * plan.WanderRate * 5.1f) * plan.Wander * num8;
					ColourAt(num8, out var r, out var g, out var b, out var a);
					float num19 = (float)Math.Exp((0f - plan.Drag) * num7);
					list.Add(new Particle
					{
						X = num16,
						Y = y2,
						Z = num17,
						Size = Lerp(plan.SizeMin, plan.SizeMax, Rand(i, seed, 9)) * SizeAt(num8),
						Age = num8,
						R = r * plan.Intensity,
						G = g * plan.Intensity,
						B = b * plan.Intensity,
						A = a,
						Stretch = plan.Stretch,
						VX = num12 * num19,
						VY = num13 * num19 + plan.Gravity * num7,
						VZ = num14 * num19
					});
				}
			}
			return list;
		}

		internal static float Glimmer(float time)
		{
			float num = (float)Math.Sin(time * 1.55f);
			float num2 = (float)Math.Sin(time * 2.63f + 1.1f);
			return 0.5f + 0.34f * num + 0.16f * num2;
		}

		private static float Lerp(float a, float b, float t)
		{
			return a + (b - a) * t;
		}

		private static float Rand(int index, int seed, int channel)
		{
			int num = (index * 73856093) ^ (seed * 19349663) ^ (channel * 83492791);
			int num2 = (num ^ (num >>> 16)) * 2146121005;
			int num3 = (num2 ^ (num2 >>> 15)) * -2073254261;
			return (float)(uint)((num3 ^ (num3 >>> 16)) & 0xFFFFFF) / 16777216f;
		}
	}
	internal static class EmberSprite
	{
		internal enum Glyph
		{
			Ember,
			Spark,
			Flame
		}

		private const float BellyRadius = 0.3f;

		private const float BellyY = -0.36f;

		private const float ApexY = 0.66f;

		private const float Bloom = 0.26f;

		internal static void Render(int size, Glyph glyph, float[] rgba)
		{
			if (size <= 0)
			{
				throw new ArgumentOutOfRangeException("size");
			}
			if (rgba == null || rgba.Length < size * size * 4)
			{
				throw new ArgumentException("rgba must hold size * size * 4 floats", "rgba");
			}
			for (int i = 0; i < size; i++)
			{
				float y = 1f - 2f * (((float)i + 0.5f) / (float)size);
				for (int j = 0; j < size; j++)
				{
					float x = 2f * (((float)j + 0.5f) / (float)size) - 1f;
					float num = ((glyph == Glyph.Ember) ? EmberAt(x, y) : SparkAt(x, y));
					if (num < 0f)
					{
						num = 0f;
					}
					if (num > 1f)
					{
						num = 1f;
					}
					int num2 = (i * size + j) * 4;
					rgba[num2] = 1f;
					rgba[num2 + 1] = 1f;
					rgba[num2 + 2] = 1f;
					rgba[num2 + 3] = num;
				}
			}
		}

		internal static void RenderSheet(int size, int cols, int rows, float[] rgba)
		{
			if (size <= 0 || cols <= 0 || rows <= 0)
			{
				throw new ArgumentOutOfRangeException();
			}
			int num = size * cols;
			int num2 = size * rows;
			if (rgba == null || rgba.Length < num * num2 * 4)
			{
				throw new ArgumentException("rgba must hold cols*rows tiles", "rgba");
			}
			int num3 = cols * rows;
			for (int i = 0; i < num3; i++)
			{
				int num4 = i % cols * size;
				int num5 = i / cols * size;
				float phase = (float)i / (float)num3;
				for (int j = 0; j < size; j++)
				{
					float y = 1f - 2f * (((float)j + 0.5f) / (float)size);
					for (int k = 0; k < size; k++)
					{
						float num6 = FlameAt(2f * (((float)k + 0.5f) / (float)size) - 1f, y, phase);
						if (num6 < 0f)
						{
							num6 = 0f;
						}
						if (num6 > 1f)
						{
							num6 = 1f;
						}
						int num7 = ((num5 + j) * num + (num4 + k)) * 4;
						rgba[num7] = 1f;
						rgba[num7 + 1] = 1f;
						rgba[num7 + 2] = 1f;
						rgba[num7 + 3] = num6;
					}
				}
			}
		}

		private static float EmberAt(float x, float y)
		{
			float num = RoundCone(Math.Abs(x), y - -0.36f, 0.3f, 0f, 1.02f);
			if (num <= 0f)
			{
				float num2 = (0f - num) / 0.3f;
				if (num2 > 1f)
				{
					num2 = 1f;
				}
				return 0.34f + 0.66f * (float)Math.Pow(num2, 0.55);
			}
			return 0.4f * (float)Math.Exp((0f - num) / 0.078f);
		}

		private static float FlameAt(float x, float y, float phase)
		{
			float num = 0.09f * (float)Math.Sin(MathF.PI * 2f * phase);
			float h = 0.42f * (1f + 0.16f * (float)Math.Sin(MathF.PI * 2f * phase + 0.9f));
			float num2 = RoundCone(Math.Abs(x - num * 0.35f), y - -0.36f, 0.3f, 0f, h);
			for (int i = 0; i < 3; i++)
			{
				float num3 = MathF.PI * 2f * (phase + (float)i * 0.29f);
				float num4 = (float)(i - 1) * 0.145f + 0.105f * (float)Math.Sin(num3);
				float num5 = -0.18f + 0.09f * (float)Math.Sin(num3 * 1.3f + 0.4f);
				float h2 = 0.22f + 0.48f * (0.5f + 0.5f * (float)Math.Sin(num3));
				float num6 = RoundCone(Math.Abs(x - num4), y - num5, 0.17f - 0.025f * (float)i, 0f, h2);
				if (num6 < num2)
				{
					num2 = num6;
				}
			}
			if (num2 <= 0f)
			{
				float num7 = (0f - num2) / 0.3f;
				if (num7 > 1f)
				{
					num7 = 1f;
				}
				float num8 = 1f - 0.3f * Clamp01((y - -0.36f) / Math.Max(0.01f, 1.02f));
				return Clamp01((0.3f + 0.7f * (float)Math.Pow(num7, 0.5)) * num8);
			}
			return 0.36f * (float)Math.Exp((0f - num2) / 0.078f);
		}

		private static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}

		private static float SparkAt(float x, float y)
		{
			float num = (float)Math.Sqrt(x * x + y * y) / 0.62f;
			if (num >= 1f)
			{
				return 0f;
			}
			return (float)Math.Pow(1f - num, 1.65);
		}

		private static float RoundCone(float x, float y, float r1, float r2, float h)
		{
			if (h <= 1E-06f)
			{
				return (float)Math.Sqrt(x * x + y * y) - Math.Max(r1, r2);
			}
			float num = (r1 - r2) / h;
			float num2 = 1f - num * num;
			if (num2 <= 0f)
			{
				return (float)Math.Sqrt(x * x + y * y) - Math.Max(r1, r2);
			}
			float num3 = (float)Math.Sqrt(num2);
			float num4 = x * (0f - num) + y * num3;
			if (num4 < 0f)
			{
				return (float)Math.Sqrt(x * x + y * y) - r1;
			}
			if (num4 > num3 * h)
			{
				return (float)Math.Sqrt(x * x + (y - h) * (y - h)) - r2;
			}
			return x * num3 + y * num - r1;
		}
	}
	internal sealed class GearDef
	{
		public string PrefabName;

		public string DisplayName;

		public string Description;

		public string Icon;

		public string[] CloneFrom;

		public int Armor;

		public float Weight;

		public bool HidesHair;
	}
	internal static class Gear
	{
		internal const string MuspelhelmPrefab = "GS_Muspelhelm";

		internal static readonly GearDef[] All = new GearDef[1]
		{
			new GearDef
			{
				PrefabName = "GS_Muspelhelm",
				DisplayName = "Muspelhelm",
				Description = "Forged for the first landing of the Guys Being Vikings, in the year 2026.\nThe horns still carry sparks from the fire the world was lit with.\n\nWorth nothing in a fight. Worth something to whoever was there.",
				Icon = "gersemi_muspelhelm.png",
				CloneFrom = new string[4] { "HelmetDrake", "HelmetBronze", "HelmetIron", "HelmetPadded" },
				Armor = 1,
				Weight = 2f,
				HidesHair = true
			}
		};

		internal static GearDef Find(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return null;
			}
			GearDef[] all = All;
			foreach (GearDef gearDef in all)
			{
				if (string.Equals(gearDef.PrefabName, token, StringComparison.OrdinalIgnoreCase))
				{
					return gearDef;
				}
				if (string.Equals(gearDef.DisplayName, token, StringComparison.OrdinalIgnoreCase))
				{
					return gearDef;
				}
			}
			return null;
		}
	}
	internal static class GearPrefab
	{
		internal sealed class Built
		{
			public GearDef Def;

			public GameObject Prefab;

			public string ClonedFrom;

			public float Scale = 1f;

			public Vector3 Seat;

			public int Triangles;

			public bool UsedOverrideModel;

			public GameObject Source;

			public bool Painted;

			public HelmBuild.Result Mesh;

			public readonly Dictionary<string, Bounds> DonorBounds = new Dictionary<string, Bounds>(StringComparer.Ordinal);
		}

		internal const string ModelChild = "GersemiModel";

		private static readonly Dictionary<string, Built> Made = new Dictionary<string, Built>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Mesh> Baked = new Dictionary<string, Mesh>(StringComparer.Ordinal);

		internal static IEnumerable<Built> Everything => Made.Values;

		internal static Built Of(string prefabName)
		{
			if (!Made.TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		internal static GameObject PrefabFor(string prefabName)
		{
			return Of(prefabName)?.Prefab;
		}

		internal static void EnsureBuilt()
		{
			GearDef[] all = Gear.All;
			foreach (GearDef gearDef in all)
			{
				if (!Made.TryGetValue(gearDef.PrefabName, out var value) || !((Object)(object)value.Prefab != (Object)null))
				{
					Built built = Build(gearDef);
					if (built != null)
					{
						Made[gearDef.PrefabName] = built;
					}
				}
			}
		}

		internal static void Repaint()
		{
			foreach (Built value in Made.Values)
			{
				if (value == null || value.Painted || (Object)(object)value.Prefab == (Object)null || value.Mesh == null)
				{
					continue;
				}
				Material[] array = HelmMaterial.For(value.Mesh.Materials, value.Source);
				i