Decompiled source of SkinCustomizer v1.1.2

plugins/SkinRecolor.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 HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Math;
using Pigeon.Movement;
using Steamworks;
using Steamworks.Data;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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("RedLintu16")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Recolor any Mycopunk skin. Modded players see your exact colors via Steam lobby data; players without the mod see your hue on skins with a Hue modifier.")]
[assembly: AssemblyFileVersion("2.1.0.0")]
[assembly: AssemblyInformationalVersion("2.1.0")]
[assembly: AssemblyProduct("SkinRecolor")]
[assembly: AssemblyTitle("SkinRecolor")]
[assembly: AssemblyVersion("2.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SkinRecolor
{
	internal struct TargetColor
	{
		public bool Enabled;

		public bool Override;

		public float HueDegrees;

		public bool Tint;

		public Color TintColor;

		public float TintStrength;

		public bool ChangesHue
		{
			get
			{
				if (Enabled)
				{
					if (!Override)
					{
						return Mathf.Abs(Mathf.DeltaAngle(0f, HueDegrees)) > 0.005f;
					}
					return true;
				}
				return false;
			}
		}

		public bool TintActive
		{
			get
			{
				if (Enabled && Tint)
				{
					return TintStrength > 0.001f;
				}
				return false;
			}
		}

		public bool IsActive
		{
			get
			{
				if (!ChangesHue)
				{
					return TintActive;
				}
				return true;
			}
		}

		public float FinalHue(float ownHue)
		{
			if (!ChangesHue)
			{
				return ownHue;
			}
			float num = HueDegrees / 360f;
			return Mathf.Repeat(Override ? num : (ownHue + num), 1f);
		}
	}
	internal sealed class SlotFx
	{
		public bool ExactColors = true;

		public bool Accent;

		public Color AccentColor = Color.red;

		public bool AccentShineMatch = true;

		public Color AccentShine = Color.white;

		public float AccentAmount = 1f;

		public bool AccentFlip;

		public bool AccentAltMask;

		public bool Glow;

		public Color GlowColor = Color.white;

		public float GlowIntensity = 1f;

		public bool Shine;

		public Color ShineColor = Color.white;

		public float Smoothness = 0.5f;

		public bool Neon;

		public readonly float[] NeonValues = new float[5];

		public readonly bool[] EffectOn = new bool[11];

		public readonly float[] EffectValue = new float[11];

		public bool AnyOn
		{
			get
			{
				if (Accent || Glow || Shine || Neon)
				{
					return true;
				}
				for (int i = 0; i < EffectOn.Length; i++)
				{
					if (EffectOn[i])
					{
						return true;
					}
				}
				return false;
			}
		}

		public bool SameAs(SlotFx o)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (o == null)
			{
				return false;
			}
			if (ExactColors != o.ExactColors || Accent != o.Accent || AccentColor != o.AccentColor || AccentShineMatch != o.AccentShineMatch || AccentShine != o.AccentShine || AccentAmount != o.AccentAmount || AccentFlip != o.AccentFlip || AccentAltMask != o.AccentAltMask || Glow != o.Glow || GlowColor != o.GlowColor || GlowIntensity != o.GlowIntensity || Shine != o.Shine || ShineColor != o.ShineColor || Smoothness != o.Smoothness || Neon != o.Neon)
			{
				return false;
			}
			for (int i = 0; i < NeonValues.Length; i++)
			{
				if (NeonValues[i] != o.NeonValues[i])
				{
					return false;
				}
			}
			for (int j = 0; j < EffectOn.Length; j++)
			{
				if (EffectOn[j] != o.EffectOn[j] || EffectValue[j] != o.EffectValue[j])
				{
					return false;
				}
			}
			return true;
		}
	}
	internal struct SpoofPair
	{
		public float Rolled;

		public float Exact;
	}
	internal sealed class ColorProfile
	{
		public const byte WireVersion = 3;

		public const int MaxPairsPerSlot = 8;

		public const int MaxWireTargets = 16;

		private const byte FlagEnabled = 1;

		private const byte FlagOverride = 2;

		private const byte FlagTint = 4;

		private const byte FxAccent = 1;

		private const byte FxShineMatch = 2;

		private const byte FxFlip = 4;

		private const byte FxAltMask = 8;

		private const byte FxGlow = 16;

		private const byte FxShine = 32;

		private const byte FxNeon = 64;

		private const byte FxExact = 128;

		private const float PairMatchTolerance = 0.0005f;

		private const int MaxFxSize = 127;

		public const int MaxWireSize = 854;

		public static readonly ColorProfile Empty = new ColorProfile();

		private static readonly SlotFx NoFx = new SlotFx();

		public readonly TargetColor[] Targets = new TargetColor[4];

		public readonly SlotFx[] Fx = new SlotFx[4];

		public readonly SpoofPair[][] Pairs = new SpoofPair[4][];

		public float ReceivedAt;

		public bool AnyActive
		{
			get
			{
				for (int i = 0; i < Targets.Length; i++)
				{
					if (Targets[i].IsActive || (Fx[i] != null && Fx[i].AnyOn))
					{
						return true;
					}
				}
				return false;
			}
		}

		public static int ClampSlot(int slot)
		{
			if (slot < 0)
			{
				return 0;
			}
			if (slot >= 4)
			{
				return 3;
			}
			return slot;
		}

		public TargetColor Get(int slot)
		{
			return Targets[ClampSlot(slot)];
		}

		public SlotFx GetFx(int slot)
		{
			return Fx[ClampSlot(slot)];
		}

		public bool HasPairs(int slot)
		{
			SpoofPair[] array = Pairs[ClampSlot(slot)];
			if (array != null)
			{
				return array.Length != 0;
			}
			return false;
		}

		public bool TryMapSpoof(int slot, float baseHue, out float exact)
		{
			SpoofPair[] array = Pairs[ClampSlot(slot)];
			if (array != null)
			{
				for (int i = 0; i < array.Length; i++)
				{
					if (Mathf.Abs(array[i].Rolled - baseHue) <= 0.0005f)
					{
						exact = array[i].Exact;
						return true;
					}
				}
			}
			exact = 0f;
			return false;
		}

		public ColorProfile CloneTargets()
		{
			ColorProfile colorProfile = new ColorProfile();
			Array.Copy(Targets, colorProfile.Targets, Targets.Length);
			Array.Copy(Fx, colorProfile.Fx, Fx.Length);
			return colorProfile;
		}

		public bool SameAs(ColorProfile other)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (other == null)
			{
				return false;
			}
			if (this == other)
			{
				return true;
			}
			for (int i = 0; i < Targets.Length; i++)
			{
				TargetColor targetColor = Targets[i];
				TargetColor targetColor2 = other.Targets[i];
				if (targetColor.Enabled != targetColor2.Enabled || targetColor.Override != targetColor2.Override || targetColor.Tint != targetColor2.Tint || targetColor.HueDegrees != targetColor2.HueDegrees || targetColor.TintStrength != targetColor2.TintStrength || targetColor.TintColor.r != targetColor2.TintColor.r || targetColor.TintColor.g != targetColor2.TintColor.g || targetColor.TintColor.b != targetColor2.TintColor.b)
				{
					return false;
				}
				SlotFx obj = Fx[i] ?? NoFx;
				SlotFx o = other.Fx[i] ?? NoFx;
				if (!obj.SameAs(o))
				{
					return false;
				}
				SpoofPair[] array = Pairs[i];
				SpoofPair[] array2 = other.Pairs[i];
				int num = ((array != null) ? array.Length : 0);
				int num2 = ((array2 != null) ? array2.Length : 0);
				if (num != num2)
				{
					return false;
				}
				for (int j = 0; j < num; j++)
				{
					if (array[j].Rolled != array2[j].Rolled || array[j].Exact != array2[j].Exact)
					{
						return false;
					}
				}
			}
			return true;
		}

		public byte[] ToBytes()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			byte[] array = new byte[854];
			int pos = 0;
			array[pos++] = 3;
			array[pos++] = (byte)Targets.Length;
			for (int i = 0; i < Targets.Length; i++)
			{
				TargetColor targetColor = Targets[i];
				byte b = 0;
				if (targetColor.Enabled)
				{
					b |= 1;
				}
				if (targetColor.Override)
				{
					b |= 2;
				}
				if (targetColor.Tint)
				{
					b |= 4;
				}
				array[pos++] = b;
				WriteFloat(array, ref pos, targetColor.HueDegrees);
				WriteRgb(array, ref pos, targetColor.TintColor);
				WriteFloat(array, ref pos, targetColor.TintStrength);
				SpoofPair[] array2 = Pairs[i];
				int num = ((array2 != null) ? Math.Min(array2.Length, 8) : 0);
				array[pos++] = (byte)num;
				for (int j = 0; j < num; j++)
				{
					WriteFloat(array, ref pos, array2[j].Rolled);
					WriteFloat(array, ref pos, array2[j].Exact);
				}
				WriteFx(array, ref pos, Fx[i] ?? new SlotFx());
			}
			byte[] array3 = new byte[pos];
			Buffer.BlockCopy(array, 0, array3, 0, pos);
			return array3;
		}

		private static void WriteFx(byte[] buffer, ref int pos, SlotFx fx)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			byte b = 0;
			if (fx.Accent)
			{
				b |= 1;
			}
			if (fx.AccentShineMatch)
			{
				b |= 2;
			}
			if (fx.AccentFlip)
			{
				b |= 4;
			}
			if (fx.AccentAltMask)
			{
				b |= 8;
			}
			if (fx.Glow)
			{
				b |= 0x10;
			}
			if (fx.Shine)
			{
				b |= 0x20;
			}
			if (fx.Neon)
			{
				b |= 0x40;
			}
			if (fx.ExactColors)
			{
				b |= 0x80;
			}
			buffer[pos++] = b;
			if (fx.Accent)
			{
				WriteRgb(buffer, ref pos, fx.AccentColor);
				WriteRgb(buffer, ref pos, fx.AccentShine);
				WriteFloat(buffer, ref pos, fx.AccentAmount);
			}
			if (fx.Glow)
			{
				WriteRgb(buffer, ref pos, fx.GlowColor);
				WriteFloat(buffer, ref pos, fx.GlowIntensity);
			}
			if (fx.Shine)
			{
				WriteRgb(buffer, ref pos, fx.ShineColor);
				WriteFloat(buffer, ref pos, fx.Smoothness);
			}
			if (fx.Neon)
			{
				for (int i = 0; i < 5; i++)
				{
					WriteFloat(buffer, ref pos, fx.NeonValues[i]);
				}
			}
			ushort num = 0;
			for (int j = 0; j < 11; j++)
			{
				if (fx.EffectOn[j])
				{
					num |= (ushort)(1 << j);
				}
			}
			buffer[pos++] = (byte)(num & 0xFF);
			buffer[pos++] = (byte)(num >> 8);
			for (int k = 0; k < 11; k++)
			{
				if (fx.EffectOn[k])
				{
					WriteFloat(buffer, ref pos, fx.EffectValue[k]);
				}
			}
		}

		public static bool TryFromBytes(byte[] data, out ColorProfile profile)
		{
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			profile = null;
			if (data == null || data.Length < 2)
			{
				return false;
			}
			int pos = 0;
			if (data[pos++] != 3)
			{
				return false;
			}
			int num = data[pos++];
			if (num == 0 || num > 16)
			{
				return false;
			}
			ColorProfile colorProfile = new ColorProfile();
			for (int i = 0; i < num; i++)
			{
				if (!Has(data, pos, 22))
				{
					return false;
				}
				byte b = data[pos++];
				float value = ReadFloat(data, ref pos);
				float value2 = ReadFloat(data, ref pos);
				float value3 = ReadFloat(data, ref pos);
				float value4 = ReadFloat(data, ref pos);
				float value5 = ReadFloat(data, ref pos);
				int num2 = data[pos++];
				if (num2 > 8 || !Has(data, pos, num2 * 8))
				{
					return false;
				}
				SpoofPair[] array = ((num2 > 0) ? new SpoofPair[num2] : null);
				for (int j = 0; j < num2; j++)
				{
					float value6 = ReadFloat(data, ref pos);
					float value7 = ReadFloat(data, ref pos);
					array[j] = new SpoofPair
					{
						Rolled = Sanitize(value6, -1000f, 1000f, float.NaN),
						Exact = Mathf.Repeat(Sanitize(value7, -1000f, 1000f, 0f), 1f)
					};
				}
				if (!TryReadFx(data, ref pos, out var fx))
				{
					return false;
				}
				if (i < colorProfile.Targets.Length)
				{
					colorProfile.Targets[i] = new TargetColor
					{
						Enabled = ((b & 1) != 0),
						Override = ((b & 2) != 0),
						Tint = ((b & 4) != 0),
						HueDegrees = Mathf.Repeat(Sanitize(value, -10000f, 10000f, 0f), 360f),
						TintColor = new Color(Sanitize(value2, 0f, 1f, 1f), Sanitize(value3, 0f, 1f, 1f), Sanitize(value4, 0f, 1f, 1f), 1f),
						TintStrength = Sanitize(value5, 0f, 1f, 0f)
					};
					colorProfile.Pairs[i] = array;
					colorProfile.Fx[i] = fx;
				}
			}
			profile = colorProfile;
			return true;
		}

		private static bool TryReadFx(byte[] data, ref int pos, out SlotFx fx)
		{
			//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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			fx = new SlotFx();
			if (!Has(data, pos, 1))
			{
				return false;
			}
			byte b = data[pos++];
			fx.Accent = (b & 1) != 0;
			fx.AccentShineMatch = (b & 2) != 0;
			fx.AccentFlip = (b & 4) != 0;
			fx.AccentAltMask = (b & 8) != 0;
			fx.Glow = (b & 0x10) != 0;
			fx.Shine = (b & 0x20) != 0;
			fx.Neon = (b & 0x40) != 0;
			fx.ExactColors = (b & 0x80) != 0;
			if (fx.Accent)
			{
				if (!Has(data, pos, 28))
				{
					return false;
				}
				fx.AccentColor = ReadRgb(data, ref pos);
				fx.AccentShine = ReadRgb(data, ref pos);
				fx.AccentAmount = Sanitize(ReadFloat(data, ref pos), 0f, 1f, 1f);
			}
			if (fx.Glow)
			{
				if (!Has(data, pos, 16))
				{
					return false;
				}
				fx.GlowColor = ReadRgb(data, ref pos);
				fx.GlowIntensity = Sanitize(ReadFloat(data, ref pos), 0f, 20f, 1f);
			}
			if (fx.Shine)
			{
				if (!Has(data, pos, 16))
				{
					return false;
				}
				fx.ShineColor = ReadRgb(data, ref pos);
				fx.Smoothness = Sanitize(ReadFloat(data, ref pos), 0f, 1f, 0.5f);
			}
			if (fx.Neon)
			{
				if (!Has(data, pos, 20))
				{
					return false;
				}
				for (int i = 0; i < 5; i++)
				{
					fx.NeonValues[i] = Sanitize(ReadFloat(data, ref pos), 0f, 1f, 0f);
				}
			}
			if (!Has(data, pos, 2))
			{
				return false;
			}
			int num = data[pos] | (data[pos + 1] << 8);
			pos += 2;
			for (int j = 0; j < 16; j++)
			{
				if ((num & (1 << j)) != 0)
				{
					if (!Has(data, pos, 4))
					{
						return false;
					}
					float num2 = Sanitize(ReadFloat(data, ref pos), 0f, 1f, 0f);
					if (j < 11)
					{
						fx.EffectOn[j] = true;
						fx.EffectValue[j] = num2;
					}
				}
			}
			return true;
		}

		private static bool Has(byte[] data, int pos, int bytes)
		{
			return pos + bytes <= data.Length;
		}

		private static void WriteRgb(byte[] buffer, ref int pos, Color c)
		{
			//IL_0002: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			WriteFloat(buffer, ref pos, c.r);
			WriteFloat(buffer, ref pos, c.g);
			WriteFloat(buffer, ref pos, c.b);
		}

		private static Color ReadRgb(byte[] data, ref int pos)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			float value = ReadFloat(data, ref pos);
			float value2 = ReadFloat(data, ref pos);
			float value3 = ReadFloat(data, ref pos);
			return new Color(Sanitize(value, 0f, 1f, 1f), Sanitize(value2, 0f, 1f, 1f), Sanitize(value3, 0f, 1f, 1f), 1f);
		}

		private static void WriteFloat(byte[] buffer, ref int pos, float value)
		{
			byte[] bytes = BitConverter.GetBytes(value);
			if (!BitConverter.IsLittleEndian)
			{
				Array.Reverse(bytes);
			}
			Buffer.BlockCopy(bytes, 0, buffer, pos, 4);
			pos += 4;
		}

		private static float ReadFloat(byte[] data, ref int pos)
		{
			float result;
			if (BitConverter.IsLittleEndian)
			{
				result = BitConverter.ToSingle(data, pos);
			}
			else
			{
				byte[] array = new byte[4];
				Buffer.BlockCopy(data, pos, array, 0, 4);
				Array.Reverse(array);
				result = BitConverter.ToSingle(array, 0);
			}
			pos += 4;
			return result;
		}

		private static float Sanitize(float value, float min, float max, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return fallback;
			}
			return Mathf.Clamp(value, min, max);
		}
	}
	internal static class Effects
	{
		public struct Def
		{
			public string Key;

			public string Label;

			public string Property;

			public string Help;
		}

		public static readonly Def[] Sliders = new Def[11]
		{
			new Def
			{
				Key = "Desaturate",
				Label = "Desaturate",
				Property = "_Saturation",
				Help = "0 = normal colors, 100% = grayscale. Full desaturate + an accent color = white skin with colored accents."
			},
			new Def
			{
				Key = "Contrast",
				Label = "Contrast shift",
				Property = "_Contrast",
				Help = "The game's Contrast / Inverted modifier."
			},
			new Def
			{
				Key = "Negative",
				Label = "Negative",
				Property = "_Negative",
				Help = "Photo-negative colors."
			},
			new Def
			{
				Key = "Spectral",
				Label = "Spectral",
				Property = "_Spectral",
				Help = "The game's Spectral modifier."
			},
			new Def
			{
				Key = "Color Cycling",
				Label = "Color cycling",
				Property = "_CycledColors",
				Help = "Animated color cycling."
			},
			new Def
			{
				Key = "Color Tossing",
				Label = "Color tossing",
				Property = "_TossedColors",
				Help = "The game's Color Tossing modifier."
			},
			new Def
			{
				Key = "Pixelated",
				Label = "Pixelated",
				Property = "_Pixeled",
				Help = "The game's Pixelated modifier."
			},
			new Def
			{
				Key = "Coppertone",
				Label = "Coppertone",
				Property = "_Coppertone",
				Help = "The game's Coppertone modifier."
			},
			new Def
			{
				Key = "Bloodmetal",
				Label = "Bloodmetal",
				Property = "_Bloodmetal",
				Help = "The game's Bloodmetal modifier."
			},
			new Def
			{
				Key = "Pool Party",
				Label = "Pool party",
				Property = "_PoolParty",
				Help = "The game's Pool Party modifier."
			},
			new Def
			{
				Key = "Chroma",
				Label = "Chroma",
				Property = "_Colorchroma",
				Help = "The game's Chroma modifier."
			}
		};

		public const int SliderCount = 11;

		public static readonly string[] NeonNames = new string[5] { "Red", "Green", "Blue", "Yellow", "Magenta" };

		public static readonly string[] NeonProperties = new string[5] { "_NeonRed", "_NeonGreen", "_NeonBlue", "_NeonYellow", "_NeonMagenta" };

		public const int NeonCount = 5;

		public const float MaxGlowIntensity = 20f;
	}
	internal static class FxIds
	{
		public static int Trim;

		public static int TrimColor;

		public static int TrimSpecular;

		public static int InvertTrim;

		public static int AlphaTrim;

		public static int EnableEmission;

		public static int EmissionColor;

		public static int SpecularColor;

		public static int Smoothness;

		public static readonly int[] Neon = new int[5];

		public static readonly int[] Sliders = new int[11];

		private static bool ready;

		public static void Ensure()
		{
			if (!ready)
			{
				ready = true;
				Trim = Shader.PropertyToID("_Trim");
				TrimColor = Shader.PropertyToID("_TrimColor");
				TrimSpecular = Shader.PropertyToID("_TrimSpecular");
				InvertTrim = Shader.PropertyToID("_InvertTrim");
				AlphaTrim = Shader.PropertyToID("_AlphaTrim");
				EnableEmission = Shader.PropertyToID("_EnableEmission");
				EmissionColor = Shader.PropertyToID("_EmissionColor");
				SpecularColor = Shader.PropertyToID("_SpecularColor");
				Smoothness = Shader.PropertyToID("_Smoothness");
				for (int i = 0; i < 5; i++)
				{
					Neon[i] = Shader.PropertyToID(Effects.NeonProperties[i]);
				}
				for (int j = 0; j < 11; j++)
				{
					Sliders[j] = Shader.PropertyToID(Effects.Sliders[j].Property);
				}
			}
		}
	}
	internal sealed class LobbySync
	{
		private const string Key = "redlintu16.skinrecolor.v3";

		private const float PublishThrottle = 0.4f;

		private const float PollInterval = 1f;

		private const int MaxValueLength = 4096;

		private readonly SkinRecolorPlugin plugin;

		private readonly Dictionary<ulong, ColorProfile> profiles = new Dictionary<ulong, ColorProfile>();

		private readonly Dictionary<ulong, string> lastRaw = new Dictionary<ulong, string>();

		private readonly HashSet<ulong> seen = new HashSet<ulong>();

		private readonly List<ulong> staleBuffer = new List<ulong>();

		private readonly HashSet<ulong> warnedMembers = new HashSet<ulong>();

		private ulong currentLobby;

		private string lastPublished;

		private float lastPublishTime;

		private bool publishPending = true;

		private float nextPublishTime;

		private float nextPollTime;

		private volatile bool dirty = true;

		private bool eventsHooked;

		private bool disabled;

		private string disabledReason;

		private Action<Lobby, Friend> memberDataHandler;

		private Action<Lobby, Friend> memberJoinedHandler;

		private Action<Lobby, Friend> memberLeftHandler;

		private Action<Lobby> lobbyEnteredHandler;

		public bool IsActive
		{
			get
			{
				if (!disabled)
				{
					return currentLobby != 0;
				}
				return false;
			}
		}

		public int RemoteCount => profiles.Count;

		public string DisabledReason => disabledReason;

		public LobbySync(SkinRecolorPlugin plugin)
		{
			this.plugin = plugin;
		}

		public ColorProfile GetProfile(ulong steamId)
		{
			if (steamId == 0L)
			{
				return null;
			}
			if (!profiles.TryGetValue(steamId, out var value))
			{
				return null;
			}
			return value;
		}

		public void MarkLocalDirty()
		{
			publishPending = true;
		}

		public void Tick()
		{
			if (disabled)
			{
				return;
			}
			try
			{
				TickInner();
			}
			catch (Exception ex) when (ex is TypeLoadException || ex is MissingMethodException || ex is MissingFieldException || ex is DllNotFoundException || ex is EntryPointNotFoundException || ex is FileNotFoundException || ex is FileLoadException)
			{
				Disable("Steam lobby sync unavailable on this game build (" + ex.GetType().Name + ": " + ex.Message + "). Colors still sync through the host if the host runs SkinRecolor.");
			}
			catch (Exception ex2)
			{
				SkinRecolorPlugin.Log.LogDebug((object)("Lobby sync tick failed: " + ex2.Message));
				nextPollTime = Time.unscaledTime + 1f;
			}
		}

		public void Shutdown()
		{
			//IL_0028: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			UnhookEvents();
			try
			{
				if (currentLobby != 0L && !string.IsNullOrEmpty(lastPublished) && SteamClient.IsValid)
				{
					Lobby val = new Lobby(SteamId.op_Implicit(currentLobby));
					((Lobby)(ref val)).SetMemberData("redlintu16.skinrecolor.v3", string.Empty);
				}
			}
			catch
			{
			}
			ResetLobbyState(0uL);
		}

		private void TickInner()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!SteamClient.IsValid)
			{
				if (currentLobby != 0L || profiles.Count > 0)
				{
					ResetLobbyState(0uL);
				}
				return;
			}
			HookEvents();
			ulong lobbyID = Online.LobbyID;
			if (lobbyID != currentLobby)
			{
				ResetLobbyState(lobbyID);
				if (lobbyID != 0L)
				{
					SkinRecolorPlugin.Log.LogInfo((object)"Joined a Steam lobby; syncing colors through lobby data.");
				}
			}
			if (lobbyID != 0L)
			{
				Lobby lobby = default(Lobby);
				((Lobby)(ref lobby))..ctor(SteamId.op_Implicit(lobbyID));
				float unscaledTime = Time.unscaledTime;
				if (publishPending && unscaledTime >= nextPublishTime)
				{
					publishPending = false;
					nextPublishTime = unscaledTime + 0.4f;
					Publish(lobby);
				}
				if (dirty || unscaledTime >= nextPollTime)
				{
					dirty = false;
					nextPollTime = unscaledTime + 1f;
					Poll(lobby, unscaledTime);
				}
			}
		}

		private void Publish(Lobby lobby)
		{
			string text = (plugin.Settings.ShareMyColors.Value ? Convert.ToBase64String(plugin.LocalProfile.ToBytes()) : string.Empty);
			if (!string.Equals(text, lastPublished, StringComparison.Ordinal))
			{
				((Lobby)(ref lobby)).SetMemberData("redlintu16.skinrecolor.v3", text);
				lastPublished = text;
				lastPublishTime = Time.unscaledTime;
			}
		}

		private void Poll(Lobby lobby, float now)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			ulong value = SteamClient.SteamId.Value;
			bool flag = false;
			seen.Clear();
			if (lastPublished != null && now - lastPublishTime > 2f && !string.Equals(((Lobby)(ref lobby)).GetMemberData(new Friend(SteamId.op_Implicit(value)), "redlintu16.skinrecolor.v3") ?? string.Empty, lastPublished, StringComparison.Ordinal))
			{
				lastPublished = null;
				publishPending = true;
			}
			foreach (Friend member in ((Lobby)(ref lobby)).Members)
			{
				ulong value2 = member.Id.Value;
				if (value2 == 0L || value2 == value)
				{
					continue;
				}
				seen.Add(value2);
				string memberData = ((Lobby)(ref lobby)).GetMemberData(member, "redlintu16.skinrecolor.v3");
				if (string.IsNullOrEmpty(memberData))
				{
					if (profiles.Remove(value2))
					{
						flag = true;
					}
					lastRaw.Remove(value2);
				}
				else
				{
					if (lastRaw.TryGetValue(value2, out var value3) && string.Equals(value3, memberData, StringComparison.Ordinal))
					{
						continue;
					}
					lastRaw[value2] = memberData;
					if (memberData.Length > 4096 || !TryDecode(memberData, out var profile))
					{
						if (warnedMembers.Add(value2))
						{
							SkinRecolorPlugin.Log.LogWarning((object)$"Ignoring unreadable SkinRecolor lobby data from Steam user {value2} (different mod version?).");
						}
						if (profiles.Remove(value2))
						{
							flag = true;
						}
					}
					else
					{
						profile.ReceivedAt = now;
						profiles[value2] = profile;
						flag = true;
					}
				}
			}
			staleBuffer.Clear();
			foreach (KeyValuePair<ulong, ColorProfile> profile2 in profiles)
			{
				if (!seen.Contains(profile2.Key))
				{
					staleBuffer.Add(profile2.Key);
				}
			}
			for (int i = 0; i < staleBuffer.Count; i++)
			{
				profiles.Remove(staleBuffer[i]);
				lastRaw.Remove(staleBuffer[i]);
				flag = true;
			}
			if (flag)
			{
				plugin.Engine.RequestScan();
			}
		}

		private static bool TryDecode(string raw, out ColorProfile profile)
		{
			profile = null;
			byte[] data;
			try
			{
				data = Convert.FromBase64String(raw);
			}
			catch (FormatException)
			{
				return false;
			}
			return ColorProfile.TryFromBytes(data, out profile);
		}

		private void ResetLobbyState(ulong lobbyId)
		{
			currentLobby = lobbyId;
			profiles.Clear();
			lastRaw.Clear();
			warnedMembers.Clear();
			lastPublished = null;
			publishPending = true;
			nextPublishTime = 0f;
			nextPollTime = 0f;
			dirty = true;
			plugin.Engine?.RequestScan();
		}

		private void Disable(string reason)
		{
			disabled = true;
			disabledReason = reason;
			UnhookEvents();
			profiles.Clear();
			lastRaw.Clear();
			currentLobby = 0uL;
			SkinRecolorPlugin.Log.LogWarning((object)reason);
		}

		private void HookEvents()
		{
			if (!eventsHooked)
			{
				eventsHooked = true;
				memberDataHandler = delegate
				{
					dirty = true;
				};
				memberJoinedHandler = delegate
				{
					dirty = true;
				};
				memberLeftHandler = delegate
				{
					dirty = true;
				};
				lobbyEnteredHandler = delegate
				{
					lastPublished = null;
					dirty = true;
					publishPending = true;
				};
				SteamMatchmaking.OnLobbyMemberDataChanged += memberDataHandler;
				SteamMatchmaking.OnLobbyMemberJoined += memberJoinedHandler;
				SteamMatchmaking.OnLobbyMemberLeave += memberLeftHandler;
				SteamMatchmaking.OnLobbyMemberDisconnected += memberLeftHandler;
				SteamMatchmaking.OnLobbyEntered += lobbyEnteredHandler;
			}
		}

		private void UnhookEvents()
		{
			if (!eventsHooked)
			{
				return;
			}
			eventsHooked = false;
			try
			{
				SteamMatchmaking.OnLobbyMemberDataChanged -= memberDataHandler;
				SteamMatchmaking.OnLobbyMemberJoined -= memberJoinedHandler;
				SteamMatchmaking.OnLobbyMemberLeave -= memberLeftHandler;
				SteamMatchmaking.OnLobbyMemberDisconnected -= memberLeftHandler;
				SteamMatchmaking.OnLobbyEntered -= lobbyEnteredHandler;
			}
			catch
			{
			}
		}
	}
	internal sealed class NetSync
	{
		private const string Channel = "RedLintu16.SkinRecolor.v3";

		private const byte ProtocolVersion = 3;

		private const byte KindState = 1;

		private const byte KindRemove = 2;

		private const int HeaderSize = 10;

		private const int MaxPayload = 4096;

		private const int MessageCapacity = 884;

		private const float SendThrottle = 0.15f;

		private const float MinClientInterval = 0.1f;

		private readonly SkinRecolorPlugin plugin;

		private readonly Dictionary<ulong, ColorProfile> remote = new Dictionary<ulong, ColorProfile>();

		private readonly HashSet<ulong> moddedClients = new HashSet<ulong>();

		private readonly List<ulong> idBuffer = new List<ulong>();

		private readonly List<ulong> pendingBuffer = new List<ulong>();

		private readonly Dictionary<ulong, float> lastRelayTime = new Dictionary<ulong, float>();

		private readonly HashSet<ulong> pendingRelay = new HashSet<ulong>();

		private readonly HashSet<ulong> warnedSenders = new HashSet<ulong>();

		private NetworkManager hookedManager;

		private CustomMessagingManager boundMessaging;

		private Action<ulong> connectedHandler;

		private Action<ulong> disconnectedHandler;

		private bool helloSent;

		private bool sendPending;

		private float nextSendTime;

		private float helloSentTime;

		private bool warnedSend;

		public bool IsOnline { get; private set; }

		public bool IsHost { get; private set; }

		public int RemoteCount => remote.Count;

		public string StatusText
		{
			get
			{
				NetworkManager val = hookedManager;
				if (!IsOnline || (Object)(object)val == (Object)null)
				{
					return "Offline. Your colors show for you; nothing to sync.";
				}
				if (IsHost)
				{
					int count = moddedClients.Count;
					if (count != 0)
					{
						return string.Format("Hosting. Syncing with {0} other player{1} running SkinRecolor.", count, (count == 1) ? "" : "s");
					}
					return "Hosting. No other players with SkinRecolor yet.";
				}
				if (!val.IsConnectedClient)
				{
					return "Connecting...";
				}
				if (remote.Count > 0)
				{
					return string.Format("Connected. Synced with {0} player{1} running SkinRecolor.", remote.Count, (remote.Count == 1) ? "" : "s");
				}
				if (!helloSent || !(Time.unscaledTime - helloSentTime > 3f))
				{
					return "Connected. Waiting for the host...";
				}
				return "Connected. The host doesn't run SkinRecolor, so colors sync through Steam lobby data instead.";
			}
		}

		private ColorProfile OutgoingProfile
		{
			get
			{
				if (!plugin.Settings.ShareMyColors.Value)
				{
					return ColorProfile.Empty;
				}
				return plugin.LocalProfile;
			}
		}

		public NetSync(SkinRecolorPlugin plugin)
		{
			this.plugin = plugin;
		}

		public ColorProfile GetRemote(ulong clientId)
		{
			if (!remote.TryGetValue(clientId, out var value))
			{
				return null;
			}
			return value;
		}

		public bool HasRemote(ulong clientId)
		{
			return remote.ContainsKey(clientId);
		}

		public void MarkLocalDirty()
		{
			sendPending = true;
		}

		public void Tick()
		{
			NetworkManager val = null;
			try
			{
				val = NetworkManager.Singleton;
			}
			catch
			{
			}
			if (!((Object)(object)val != (Object)null) || !val.IsListening || val.ShutdownInProgress)
			{
				if (boundMessaging != null || remote.Count > 0 || moddedClients.Count > 0 || IsOnline)
				{
					EndSession();
				}
				if ((Object)(object)val == (Object)null)
				{
					UnhookManager();
				}
				return;
			}
			if (val != hookedManager)
			{
				UnhookManager();
				HookManager(val);
			}
			CustomMessagingManager customMessagingManager = val.CustomMessagingManager;
			if (customMessagingManager == null)
			{
				return;
			}
			if (customMessagingManager != boundMessaging)
			{
				EndSession();
				Bind(customMessagingManager);
			}
			IsOnline = true;
			IsHost = val.IsServer;
			float unscaledTime = Time.unscaledTime;
			if (!val.IsServer && val.IsConnectedClient && !helloSent)
			{
				helloSent = true;
				helloSentTime = unscaledTime;
				sendPending = false;
				nextSendTime = unscaledTime + 0.15f;
				SendLocalToServer(val);
				return;
			}
			if (sendPending && unscaledTime >= nextSendTime)
			{
				sendPending = false;
				nextSendTime = unscaledTime + 0.15f;
				FlushLocal(val);
			}
			if (val.IsServer && pendingRelay.Count > 0)
			{
				FlushPendingRelays(unscaledTime);
			}
		}

		private void FlushPendingRelays(float now)
		{
			pendingBuffer.Clear();
			pendingBuffer.AddRange(pendingRelay);
			for (int i = 0; i < pendingBuffer.Count; i++)
			{
				ulong num = pendingBuffer[i];
				if (!lastRelayTime.TryGetValue(num, out var value) || !(now - value < 0.1f))
				{
					pendingRelay.Remove(num);
					if (remote.TryGetValue(num, out var value2))
					{
						lastRelayTime[num] = now;
						Relay(num, value2);
					}
				}
			}
		}

		public void Shutdown()
		{
			EndSession();
			UnhookManager();
		}

		private void HookManager(NetworkManager nm)
		{
			hookedManager = nm;
			connectedHandler = OnClientConnected;
			disconnectedHandler = OnClientDisconnected;
			nm.OnClientConnectedCallback += connectedHandler;
			nm.OnClientDisconnectCallback += disconnectedHandler;
		}

		private void UnhookManager()
		{
			NetworkManager val = hookedManager;
			hookedManager = null;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				if (connectedHandler != null)
				{
					val.OnClientConnectedCallback -= connectedHandler;
				}
				if (disconnectedHandler != null)
				{
					val.OnClientDisconnectCallback -= disconnectedHandler;
				}
			}
			catch
			{
			}
			connectedHandler = null;
			disconnectedHandler = null;
		}

		private void Bind(CustomMessagingManager messaging)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			boundMessaging = messaging;
			helloSent = false;
			sendPending = false;
			try
			{
				messaging.RegisterNamedMessageHandler("RedLintu16.SkinRecolor.v3", new HandleNamedMessageDelegate(OnNamedMessage));
				SkinRecolorPlugin.Log.LogInfo((object)"Multiplayer sync ready.");
			}
			catch (Exception ex)
			{
				SkinRecolorPlugin.Log.LogError((object)("Couldn't register the sync message handler: " + ex.Message));
			}
		}

		private void EndSession()
		{
			if (boundMessaging != null)
			{
				try
				{
					boundMessaging.UnregisterNamedMessageHandler("RedLintu16.SkinRecolor.v3");
				}
				catch
				{
				}
				boundMessaging = null;
			}
			helloSent = false;
			sendPending = false;
			IsOnline = false;
			IsHost = false;
			remote.Clear();
			moddedClients.Clear();
			lastRelayTime.Clear();
			pendingRelay.Clear();
			warnedSenders.Clear();
		}

		private void OnClientConnected(ulong clientId)
		{
		}

		private void OnClientDisconnected(ulong clientId)
		{
			NetworkManager val = hookedManager;
			if ((Object)(object)val == (Object)null || !val.IsServer)
			{
				return;
			}
			bool num = moddedClients.Remove(clientId);
			bool flag = remote.Remove(clientId);
			pendingRelay.Remove(clientId);
			lastRelayTime.Remove(clientId);
			warnedSenders.Remove(clientId);
			if (num || flag)
			{
				idBuffer.Clear();
				idBuffer.AddRange(moddedClients);
				for (int i = 0; i < idBuffer.Count; i++)
				{
					Send(idBuffer[i], 2, clientId, null);
				}
			}
		}

		private void SendLocalToServer(NetworkManager nm)
		{
			Send(0uL, 1, nm.LocalClientId, OutgoingProfile);
		}

		private void FlushLocal(NetworkManager nm)
		{
			if (nm.IsServer)
			{
				ColorProfile outgoingProfile = OutgoingProfile;
				idBuffer.Clear();
				idBuffer.AddRange(moddedClients);
				for (int i = 0; i < idBuffer.Count; i++)
				{
					Send(idBuffer[i], 1, nm.LocalClientId, outgoingProfile);
				}
			}
			else if (helloSent && nm.IsConnectedClient)
			{
				SendLocalToServer(nm);
			}
		}

		private void SendSnapshotTo(NetworkManager nm, ulong newClient)
		{
			Send(newClient, 1, nm.LocalClientId, OutgoingProfile);
			foreach (KeyValuePair<ulong, ColorProfile> item in remote)
			{
				if (item.Key != newClient)
				{
					Send(newClient, 1, item.Key, item.Value);
				}
			}
		}

		private void Relay(ulong origin, ColorProfile profile)
		{
			idBuffer.Clear();
			idBuffer.AddRange(moddedClients);
			for (int i = 0; i < idBuffer.Count; i++)
			{
				if (idBuffer[i] != origin)
				{
					Send(idBuffer[i], 1, origin, profile);
				}
			}
		}

		private void Send(ulong target, byte kind, ulong owner, ColorProfile profile)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			CustomMessagingManager val = boundMessaging;
			if (val == null)
			{
				return;
			}
			FastBufferWriter val2 = default(FastBufferWriter);
			((FastBufferWriter)(ref val2))..ctor(884, (Allocator)2, -1);
			try
			{
				byte b = 3;
				((FastBufferWriter)(ref val2)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				((FastBufferWriter)(ref val2)).WriteValueSafe<byte>(ref kind, default(ForPrimitives));
				((FastBufferWriter)(ref val2)).WriteValueSafe<ulong>(ref owner, default(ForPrimitives));
				if (kind == 1)
				{
					byte[] array = (profile ?? ColorProfile.Empty).ToBytes();
					int num = array.Length;
					((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref num, default(ForPrimitives));
					((FastBufferWriter)(ref val2)).WriteBytesSafe(array, array.Length, 0);
				}
				val.SendNamedMessage("RedLintu16.SkinRecolor.v3", target, val2, (NetworkDelivery)3);
			}
			catch (Exception ex)
			{
				if (!warnedSend)
				{
					warnedSend = true;
					SkinRecolorPlugin.Log.LogWarning((object)$"Sending colors to client {target} failed (shown once): {ex.Message}");
				}
			}
			finally
			{
				((FastBufferWriter)(ref val2)).Dispose();
			}
		}

		private void OnNamedMessage(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager val = hookedManager;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				byte b = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
				if (b != 3)
				{
					return;
				}
				byte b2 = default(byte);
				((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b2, default(ForPrimitives));
				ulong num = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
				if (val.IsServer)
				{
					if (senderClientId == val.LocalClientId || b2 != 1 || !IsConnected(val, senderClientId) || !TryReadProfile(ref reader, out var profile))
					{
						return;
					}
					remote[senderClientId] = profile;
					if (moddedClients.Add(senderClientId))
					{
						SkinRecolorPlugin.Log.LogInfo((object)$"Client {senderClientId} has SkinRecolor; syncing colors.");
						SendSnapshotTo(val, senderClientId);
					}
					float unscaledTime = Time.unscaledTime;
					if (lastRelayTime.TryGetValue(senderClientId, out var value) && unscaledTime - value < 0.1f)
					{
						pendingRelay.Add(senderClientId);
					}
					else
					{
						lastRelayTime[senderClientId] = unscaledTime;
						pendingRelay.Remove(senderClientId);
						Relay(senderClientId, profile);
					}
				}
				else
				{
					if (senderClientId != 0L || num == val.LocalClientId)
					{
						return;
					}
					switch (b2)
					{
					case 1:
					{
						if (TryReadProfile(ref reader, out var profile2))
						{
							remote[num] = profile2;
						}
						break;
					}
					case 2:
						remote.Remove(num);
						break;
					}
				}
				plugin.Engine.RequestScan();
			}
			catch (Exception ex)
			{
				if (warnedSenders.Add(senderClientId))
				{
					SkinRecolorPlugin.Log.LogWarning((object)$"Bad SkinRecolor message from client {senderClientId} (further ones from it not logged): {ex.Message}");
				}
			}
		}

		private static bool TryReadProfile(ref FastBufferReader reader, out ColorProfile profile)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			profile = null;
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num <= 0 || num > 4096)
			{
				return false;
			}
			byte[] data = new byte[num];
			((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0);
			if (!ColorProfile.TryFromBytes(data, out profile))
			{
				return false;
			}
			profile.ReceivedAt = Time.unscaledTime;
			return true;
		}

		private static bool IsConnected(NetworkManager nm, ulong clientId)
		{
			IReadOnlyList<ulong> connectedClientsIds = nm.ConnectedClientsIds;
			if (connectedClientsIds == null)
			{
				return true;
			}
			for (int i = 0; i < connectedClientsIds.Count; i++)
			{
				if (connectedClientsIds[i] == clientId)
				{
					return true;
				}
			}
			return false;
		}
	}
	[BepInPlugin("redlintu16.skinrecolor", "SkinRecolor", "2.1.0")]
	[BepInProcess("Mycopunk.exe")]
	[MycoMod(/*Could not decode attribute arguments.*/)]
	public sealed class SkinRecolorPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "redlintu16.skinrecolor";

		public const string PluginName = "SkinRecolor";

		public const string PluginVersion = "2.1.0";

		internal static ManualLogSource Log;

		private Harmony harmony;

		private float saveAt = -1f;

		private string parsedKeySource;

		private Key toggleKey = (Key)101;

		private bool toggleKeyTypesText;

		private bool renderHooked;

		private bool shutDown;

		private readonly bool[] warnedGuard = new bool[8];

		internal Settings Settings { get; private set; }

		internal RecolorEngine Engine { get; private set; }

		internal NetSync Net { get; private set; }

		internal LobbySync Lobby { get; private set; }

		internal VanillaHue Vanilla { get; private set; }

		internal RecolorWindow Window { get; private set; }

		internal ColorProfile BaseProfile { get; private set; } = ColorProfile.Empty;

		internal ColorProfile LocalProfile { get; private set; } = ColorProfile.Empty;

		private void Awake()
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Settings = new Settings(((BaseUnityPlugin)this).Config);
			BaseProfile = Settings.BuildLocalProfile();
			LocalProfile = BaseProfile;
			Settings.Changed += OnSettingsChanged;
			Engine = new RecolorEngine(this);
			Net = new NetSync(this);
			Window = new RecolorWindow(this);
			try
			{
				Lobby = new LobbySync(this);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Steam lobby sync unavailable (" + ex.Message + "); colors only sync through a modded host."));
				Lobby = null;
			}
			Vanilla = new VanillaHue(this);
			try
			{
				harmony = new Harmony("redlintu16.skinrecolor");
				Vanilla.Install(harmony);
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)("Couldn't set up Harmony (" + ex2.Message + "); players without the mod will see your normal skins."));
			}
			RenderPipelineManager.beginContextRendering += OnBeginContextRendering;
			renderHooked = true;
			SaveConfig();
			Log.LogInfo((object)("SkinRecolor 2.1.0 loaded. Press " + Settings.ToggleKey.Value + " in-game to open the recolor window."));
		}

		private void OnSettingsChanged()
		{
			BaseProfile = Settings.BuildLocalProfile();
			Vanilla?.OnProfileChanged();
			RebuildLocalProfile(forceShare: true);
			saveAt = Time.unscaledTime + 1f;
		}

		internal void RebuildLocalProfile(bool forceShare = false)
		{
			ColorProfile colorProfile = BaseProfile.CloneTargets();
			Vanilla?.FillPairs(colorProfile);
			if (forceShare || !colorProfile.SameAs(LocalProfile))
			{
				LocalProfile = colorProfile;
				Net.MarkLocalDirty();
				Lobby?.MarkLocalDirty();
			}
		}

		internal ColorProfile GetRemoteProfile(ulong steamId, bool hasClientId, ulong clientId)
		{
			ColorProfile colorProfile = Lobby?.GetProfile(steamId);
			ColorProfile colorProfile2 = (hasClientId ? Net.GetRemote(clientId) : null);
			if (colorProfile == null)
			{
				return colorProfile2;
			}
			if (colorProfile2 == null)
			{
				return colorProfile;
			}
			if (!(colorProfile2.ReceivedAt > colorProfile.ReceivedAt))
			{
				return colorProfile;
			}
			return colorProfile2;
		}

		internal void SaveSoon()
		{
			saveAt = Time.unscaledTime + 0.1f;
		}

		private void Update()
		{
			Guard(HandleToggleKey, 0);
			Guard(Window.Update, 1);
			Guard(Engine.Tick, 2);
			Guard(Net.Tick, 3);
			if (Lobby != null)
			{
				Guard(Lobby.Tick, 4);
			}
			if (Vanilla != null)
			{
				Guard(Vanilla.Tick, 5);
			}
			if (saveAt >= 0f && Time.unscaledTime >= saveAt)
			{
				saveAt = -1f;
				SaveConfig();
			}
		}

		private void LateUpdate()
		{
			Guard(Engine.LateUpdateFallback, 6);
			Guard(Window.LateUpdate, 7);
		}

		private void Guard(Action step, int id)
		{
			try
			{
				step();
			}
			catch (Exception arg)
			{
				if (!warnedGuard[id])
				{
					warnedGuard[id] = true;
					Log.LogError((object)$"{step.Method.DeclaringType?.Name}.{step.Method.Name} failed (shown once): {arg}");
				}
			}
		}

		private void OnGUI()
		{
			Window.OnGUI();
		}

		private void OnBeginContextRendering(ScriptableRenderContext context, List<Camera> cameras)
		{
			Engine.OnBeginContextRendering();
		}

		private void HandleToggleKey()
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			string value = Settings.ToggleKey.Value;
			if (!string.Equals(value, parsedKeySource, StringComparison.Ordinal))
			{
				parsedKeySource = value;
				toggleKey = ParseKey(value);
				toggleKeyTypesText = IsTextKey(toggleKey);
			}
			if (Window.IsOpen && toggleKeyTypesText && GUIUtility.keyboardControl != 0)
			{
				return;
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && ((ButtonControl)current[toggleKey]).wasPressedThisFrame)
				{
					Window.Toggle();
				}
			}
			catch (ArgumentOutOfRangeException)
			{
				Log.LogWarning((object)("Toggle Key '" + value + "' can't be read from the keyboard; falling back to F8."));
				toggleKey = (Key)101;
			}
		}

		private static Key ParseKey(string source)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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)
			string text = ((source == null) ? string.Empty : source.Trim());
			if (text.Length > 0 && !char.IsDigit(text[0]) && text[0] != '-' && text.IndexOf(',') < 0 && Enum.TryParse<Key>(text, ignoreCase: true, out Key result) && Enum.IsDefined(typeof(Key), result) && (int)result != 0)
			{
				return result;
			}
			Log.LogWarning((object)("Toggle Key '" + source + "' isn't a valid Input System key name (e.g. F8, F9, Insert, Backquote); using F8."));
			return (Key)101;
		}

		private unsafe static bool IsTextKey(Key key)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			string text = ((object)(*(Key*)(&key))/*cast due to .constrained prefix*/).ToString();
			if (text.Length == 1)
			{
				return true;
			}
			if (text.StartsWith("Digit", StringComparison.Ordinal) || text.StartsWith("Numpad", StringComparison.Ordinal))
			{
				return true;
			}
			if ((int)key == 1 || key - 4 <= 10)
			{
				return true;
			}
			return false;
		}

		private void SaveConfig()
		{
			try
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Saving config failed: " + ex.Message));
			}
		}

		private void OnApplicationQuit()
		{
			Shutdown();
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void Shutdown()
		{
			if (shutDown)
			{
				return;
			}
			shutDown = true;
			if (renderHooked)
			{
				RenderPipelineManager.beginContextRendering -= OnBeginContextRendering;
				renderHooked = false;
			}
			try
			{
				Window?.ForceClose();
				Engine?.RestoreAll();
				Net?.Shutdown();
				Lobby?.Shutdown();
				Vanilla?.Shutdown();
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Shutdown cleanup failed: " + ex.Message));
				}
			}
			SaveConfig();
		}
	}
	internal sealed class RecolorEngine
	{
		private sealed class FloatState
		{
			public bool Wrote;

			public float Last;

			public float Base;

			public bool BaseFromBlock;
		}

		private sealed class Entry
		{
			public readonly Renderer Renderer;

			public FloatState Hue = new FloatState();

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

			public bool MaterialsLogged;

			public Player Owner;

			public bool IsLocal;

			public bool HasOwnerId;

			public ulong OwnerId;

			public ulong SteamId;

			public int Slot;

			public int SeenStamp;

			public bool Touched;

			public bool GameOwnsBlock;

			public bool AnyWrote => Hue.Wrote;

			public Entry(Renderer renderer)
			{
				Renderer = renderer;
			}

			public void ResetWrites()
			{
				Hue = new FloatState();
				Touched = false;
				GameOwnsBlock = false;
			}
		}

		private const float ScanInterval = 0.5f;

		private const float HueUnitsPerTurn = 1f;

		private readonly SkinRecolorPlugin plugin;

		private readonly Dictionary<Renderer, Entry> entries = new Dictionary<Renderer, Entry>();

		private readonly Dictionary<Renderer, Entry> keepers = new Dictionary<Renderer, Entry>();

		private readonly Dictionary<Renderer, int> slotMap = new Dictionary<Renderer, int>();

		private readonly List<Renderer> deadKeys = new List<Renderer>();

		private readonly List<Renderer> rendererBuffer = new List<Renderer>();

		private readonly List<Material> materialBuffer = new List<Material>();

		private readonly MaterialPropertyBlock block = new MaterialPropertyBlock();

		private readonly MaterialPropertyBlock materialBlock = new MaterialPropertyBlock();

		private readonly List<Material> countBuffer = new List<Material>();

		private readonly Dictionary<int, KeyValuePair<int[], ShaderPropertyType[]>> shaderProps = new Dictionary<int, KeyValuePair<int[], ShaderPropertyType[]>>();

		private int materialLogs;

		private readonly HashSet<int> loggedShaders = new HashSet<int>();

		private readonly Dictionary<Type, Func<object, IEnumerable<Renderer>>> extraRendererGetters = new Dictionary<Type, Func<object, IEnumerable<Renderer>>>();

		private Player[] players = (Player[])(object)new Player[0];

		private int hueId;

		private bool hueIdReady;

		private int[] tintIds = new int[0];

		private string[] tintNames = new string[0];

		private string tintSource;

		private int scanStamp;

		private float nextScanTime;

		private bool scanRequested = true;

		private int lastPassFrame = -1;

		private int lastContextFrame = -1000;

		private bool warnedPass;

		private bool warnedGear;

		public IReadOnlyList<Player> Players => players;

		public RecolorEngine(SkinRecolorPlugin plugin)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			this.plugin = plugin;
		}

		public void RequestScan()
		{
			scanRequested = true;
		}

		public void Tick()
		{
			EnsureHueId();
			EnsureTintIds();
			float unscaledTime = Time.unscaledTime;
			if (scanRequested || unscaledTime >= nextScanTime)
			{
				scanRequested = false;
				nextScanTime = unscaledTime + 0.5f;
				Scan();
			}
		}

		public void OnBeginContextRendering()
		{
			lastContextFrame = Time.frameCount;
			Pass();
		}

		public void LateUpdateFallback()
		{
			if (Time.frameCount - lastContextFrame > 2)
			{
				Pass();
			}
		}

		public void RestoreAll()
		{
			foreach (KeyValuePair<Renderer, Entry> entry in entries)
			{
				SafeFinalRestore(entry.Value);
			}
			foreach (KeyValuePair<Renderer, Entry> keeper in keepers)
			{
				SafeFinalRestore(keeper.Value);
			}
			entries.Clear();
			keepers.Clear();
		}

		private void SafeFinalRestore(Entry e)
		{
			try
			{
				if ((Object)(object)e.Renderer == (Object)null)
				{
					return;
				}
				ClearMaterialBlocks(e);
				if (e.Touched && e.AnyWrote)
				{
					if (e.GameOwnsBlock)
					{
						WriteNeutral(e);
					}
					else
					{
						e.Renderer.SetPropertyBlock((MaterialPropertyBlock)null);
					}
				}
			}
			catch (Exception ex)
			{
				SkinRecolorPlugin.Log.LogDebug((object)("Restore failed: " + ex.Message));
			}
		}

		private void EnsureHueId()
		{
			if (hueIdReady)
			{
				return;
			}
			hueIdReady = true;
			hueId = Shader.PropertyToID("_HueShift");
			try
			{
				Type type = FindGameType("SkinUpgrade");
				if (type == null)
				{
					SkinRecolorPlugin.Log.LogWarning((object)"Couldn't find the game's SkinUpgrade type; using shader property '_HueShift'.");
					return;
				}
				object obj = null;
				FieldInfo field = type.GetField("_HueShift", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				if (field != null)
				{
					obj = field.GetValue(null);
				}
				else
				{
					PropertyInfo property = type.GetProperty("_HueShift", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
					if (property != null)
					{
						obj = property.GetValue(null, null);
					}
				}
				if (obj is int num)
				{
					hueId = num;
					SkinRecolorPlugin.Log.LogInfo((object)"Using the game's SkinUpgrade._HueShift shader property.");
				}
				else if (obj is string { Length: >0 } text)
				{
					hueId = Shader.PropertyToID(text);
					SkinRecolorPlugin.Log.LogInfo((object)("Using the game's SkinUpgrade._HueShift shader property ('" + text + "')."));
				}
				else
				{
					SkinRecolorPlugin.Log.LogWarning((object)"SkinUpgrade._HueShift not found; using shader property '_HueShift'.");
				}
			}
			catch (Exception ex)
			{
				SkinRecolorPlugin.Log.LogWarning((object)("Reading SkinUpgrade._HueShift failed (" + ex.Message + "); using shader property '_HueShift'."));
			}
		}

		private void EnsureTintIds()
		{
			string text = plugin.Settings.TintProperties.Value ?? string.Empty;
			if (string.Equals(text, tintSource, StringComparison.Ordinal))
			{
				return;
			}
			bool flag = tintSource != null;
			tintSource = text;
			List<string> list = new List<string>();
			string[] array = text.Split(new char[4] { ',', ';', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length > 0 && !list.Contains(text2))
				{
					list.Add(text2);
				}
			}
			tintNames = list.ToArray();
			tintIds = new int[tintNames.Length];
			for (int j = 0; j < tintNames.Length; j++)
			{
				tintIds[j] = Shader.PropertyToID(tintNames[j]);
			}
			if (!flag)
			{
				return;
			}
			foreach (KeyValuePair<Renderer, Entry> entry in entries)
			{
				if ((Object)(object)entry.Value.Renderer != (Object)null)
				{
					ClearMaterialBlocks(entry.Value);
				}
			}
			loggedShaders.Clear();
			scanRequested = true;
		}

		private static Type FindGameType(string name)
		{
			Type type = Type.GetType(name + ", Assembly-CSharp", throwOnError: false);
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetType(name, throwOnError: false);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private void Scan()
		{
			if (!hueIdReady)
			{
				return;
			}
			scanStamp++;
			Player[] array;
			try
			{
				array = Object.FindObjectsOfType<Player>();
			}
			catch (Exception ex)
			{
				SkinRecolorPlugin.Log.LogDebug((object)("FindObjectsOfType<Player> failed: " + ex.Message));
				array = (Player[])(object)new Player[0];
			}
			players = array;
			Player val = SafeLocalPlayer();
			foreach (Player val2 in array)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				bool isLocal = (Object)(object)val2 == (Object)(object)val;
				ulong ownerId;
				bool hasOwnerId = TryGetOwnerId(val2, out ownerId);
				ulong steamId = SafeSteamId(val2);
				slotMap.Clear();
				CollectGearRenderers(val2);
				rendererBuffer.Clear();
				((Component)val2).GetComponentsInChildren<Renderer>(true, rendererBuffer);
				for (int j = 0; j < rendererBuffer.Count; j++)
				{
					Renderer val3 = rendererBuffer[j];
					if ((Object)(object)val3 != (Object)null && !slotMap.ContainsKey(val3))
					{
						slotMap[val3] = 0;
					}
				}
				foreach (KeyValuePair<Renderer, int> item in slotMap)
				{
					Renderer key = item.Key;
					if ((Object)(object)key == (Object)null)
					{
						continue;
					}
					Material val4 = FindSkinMaterial(key);
					if (!entries.TryGetValue(key, out var value))
					{
						if (keepers.TryGetValue(key, out value))
						{
							keepers.Remove(key);
						}
						else
						{
							if ((Object)(object)val4 == (Object)null)
							{
								continue;
							}
							value = new Entry(key);
						}
						entries.Add(key, value);
					}
					if ((Object)(object)val4 != (Object)null)
					{
						LogShaderOnce(val4.shader);
					}
					value.Owner = val2;
					value.IsLocal = isLocal;
					value.HasOwnerId = hasOwnerId;
					value.OwnerId = ownerId;
					value.SteamId = steamId;
					value.Slot = item.Value;
					value.SeenStamp = scanStamp;
				}
			}
			deadKeys.Clear();
			foreach (KeyValuePair<Renderer, Entry> entry in entries)
			{
				if (entry.Value.SeenStamp != scanStamp)
				{
					deadKeys.Add(entry.Key);
				}
			}
			for (int k = 0; k < deadKeys.Count; k++)
			{
				Renderer val5 = deadKeys[k];
				if (!entries.TryGetValue(val5, out var value2))
				{
					continue;
				}
				entries.Remove(val5);
				if ((Object)(object)val5 != (Object)null && value2.MaterialBlocks.Count > 0)
				{
					ClearMaterialBlocks(value2);
				}
				if ((Object)(object)val5 == (Object)null || !value2.Touched || !value2.AnyWrote)
				{
					continue;
				}
				try
				{
					if (value2.GameOwnsBlock)
					{
						value2.Owner = null;
						WriteNeutral(value2);
						keepers[val5] = value2;
					}
					else
					{
						val5.SetPropertyBlock((MaterialPropertyBlock)null);
					}
				}
				catch (Exception ex2)
				{
					SkinRecolorPlugin.Log.LogDebug((object)("Release on untrack failed: " + ex2.Message));
				}
			}
		}

		private void CollectGearRenderers(Player player)
		{
			try
			{
				IGear[] gear = player.Gear;
				if (gear == null)
				{
					return;
				}
				for (int i = 0; i < gear.Length; i++)
				{
					IGear obj = gear[i];
					Component val = (Component)(object)((obj is Component) ? obj : null);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					int value = i switch
					{
						1 => 2, 
						0 => 1, 
						_ => 3, 
					};
					rendererBuffer.Clear();
					val.GetComponentsInChildren<Renderer>(true, rendererBuffer);
					for (int j = 0; j < rendererBuffer.Count; j++)
					{
						if ((Object)(object)rendererBuffer[j] != (Object)null)
						{
							slotMap[rendererBuffer[j]] = value;
						}
					}
					IEnumerable<Renderer> extraSkinRenderers = GetExtraSkinRenderers(val);
					if (extraSkinRenderers == null)
					{
						continue;
					}
					foreach (Renderer item in extraSkinRenderers)
					{
						if ((Object)(object)item != (Object)null)
						{
							slotMap[item] = value;
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (!warnedGear)
				{
					warnedGear = true;
					SkinRecolorPlugin.Log.LogWarning((object)("Reading a player's gear failed (" + ex.Message + "); their gear will use Character colors."));
				}
			}
		}

		private IEnumerable<Renderer> GetExtraSkinRenderers(Component component)
		{
			Type type = ((object)component).GetType();
			if (!extraRendererGetters.TryGetValue(type, out var value))
			{
				value = BuildExtraRendererGetter(type);
				extraRendererGetters[type] = value;
			}
			if (value == null)
			{
				return null;
			}
			try
			{
				return value(component);
			}
			catch
			{
				return null;
			}
		}

		private static Func<object, IEnumerable<Renderer>> BuildExtraRendererGetter(Type type)
		{
			string[] array = new string[2] { "SkinRenderers", "skinRenderers" };
			Type type2 = type;
			while (type2 != null && type2 != typeof(MonoBehaviour))
			{
				string[] array2 = array;
				foreach (string name in array2)
				{
					FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && typeof(IEnumerable<Renderer>).IsAssignableFrom(field.FieldType))
					{
						return (object o) => field.GetValue(o) as IEnumerable<Renderer>;
					}
					PropertyInfo prop = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (prop != null && prop.GetIndexParameters().Length == 0 && prop.CanRead && typeof(IEnumerable<Renderer>).IsAssignableFrom(prop.PropertyType))
					{
						return (object o) => prop.GetValue(o, null) as IEnumerable<Renderer>;
					}
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private static Player SafeLocalPlayer()
		{
			try
			{
				return Player.LocalPlayer;
			}
			catch
			{
				return null;
			}
		}

		internal static ulong SafeSteamId(Player player)
		{
			try
			{
				return player.UserID;
			}
			catch
			{
				return 0uL;
			}
		}

		internal static bool TryGetOwnerId(Player player, out ulong ownerId)
		{
			try
			{
				ownerId = ((NetworkBehaviour)player).OwnerClientId;
				return true;
			}
			catch
			{
				ownerId = 0uL;
				return false;
			}
		}

		private Material FindSkinMaterial(Renderer r)
		{
			materialBuffer.Clear();
			r.GetSharedMaterials(materialBuffer);
			for (int i = 0; i < materialBuffer.Count; i++)
			{
				Material val = materialBuffer[i];
				if ((Object)(object)val != (Object)null && val.HasProperty(hueId))
				{
					return val;
				}
			}
			return null;
		}

		private void LogShaderOnce(Shader shader)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shader == (Object)null || !plugin.Settings.LogShaderProperties.Value || !loggedShaders.Add(((Object)shader).GetInstanceID()))
			{
				return;
			}
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				List<string> list = new List<string>();
				int propertyCount = shader.GetPropertyCount();
				for (int i = 0; i < propertyCount; i++)
				{
					string propertyName = shader.GetPropertyName(i);
					ShaderPropertyType propertyType = shader.GetPropertyType(i);
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(propertyName).Append(" (").Append(propertyType)
						.Append(')');
					if ((int)propertyType == 0)
					{
						list.Add(propertyName);
					}
				}
				SkinRecolorPlugin.Log.LogInfo((object)$"Skin shader '{((Object)shader).name}' properties: {stringBuilder}");
				List<string> list2 = new List<string>();
				string[] array = tintNames;
				foreach (string text in array)
				{
					if (shader.FindPropertyIndex(text) < 0)
					{
						list2.Add(text);
					}
				}
				if (tintNames.Length != 0 && list2.Count == tintNames.Length)
				{
					SkinRecolorPlugin.Log.LogWarning((object)("None of the configured tint properties (" + string.Join(", ", tintNames) + ") exist on skin shader '" + ((Object)shader).name + "', so Tint won't do anything on it. Its color properties are: " + string.Join(", ", list) + ". Put the ones you want in [Advanced] Tint Color Properties."));
				}
				else if (list2.Count > 0)
				{
					SkinRecolorPlugin.Log.LogInfo((object)("Tint properties not on '" + ((Object)shader).name + "' (skipped there): " + string.Join(", ", list2) + "."));
				}
			}
			catch (Exception ex)
			{
				SkinRecolorPlugin.Log.LogDebug((object)("Shader property dump failed: " + ex.Message));
			}
		}

		private void Pass()
		{
			if (lastPassFrame == Time.frameCount)
			{
				return;
			}
			lastPassFrame = Time.frameCount;
			if (!hueIdReady || (entries.Count == 0 && keepers.Count == 0))
			{
				return;
			}
			FxIds.Ensure();
			bool value = plugin.Settings.ShowOthersColors.Value;
			ColorProfile localProfile = plugin.LocalProfile;
			deadKeys.Clear();
			foreach (KeyValuePair<Renderer, Entry> entry in entries)
			{
				Entry value2 = entry.Value;
				if ((Object)(object)value2.Renderer == (Object)null)
				{
					deadKeys.Add(entry.Key);
					continue;
				}
				try
				{
					if (Resolve(value2, localProfile, value, out var target, out var fx, out var pairs))
					{
						ApplyActive(value2, target, fx, pairs);
					}
					else if (value2.Touched || value2.MaterialBlocks.Count > 0)
					{
						ApplyInactive(value2);
					}
				}
				catch (Exception ex)
				{
					WarnPassOnce(ex);
				}
			}
			for (int i = 0; i < deadKeys.Count; i++)
			{
				entries.Remove(deadKeys[i]);
			}
			if (keepers.Count == 0)
			{
				return;
			}
			deadKeys.Clear();
			foreach (KeyValuePair<Renderer, Entry> keeper in keepers)
			{
				Entry value3 = keeper.Value;
				if ((Object)(object)value3.Renderer == (Object)null)
				{
					deadKeys.Add(keeper.Key);
					continue;
				}
				try
				{
					WriteNeutral(value3);
				}
				catch (Exception ex2)
				{
					WarnPassOnce(ex2);
				}
			}
			for (int j = 0; j < deadKeys.Count; j++)
			{
				keepers.Remove(deadKeys[j]);
			}
		}

		private void WarnPassOnce(Exception ex)
		{
			if (!warnedPass)
			{
				warnedPass = true;
				SkinRecolorPlugin.Log.LogWarning((object)$"Recolor pass error (shown once): {ex}");
			}
		}

		private bool Resolve(Entry e, ColorProfile localProfile, bool showOthers, out TargetColor target, out SlotFx fx, out ColorProfile pairs)
		{
			target = default(TargetColor);
			fx = null;
			pairs = null;
			if ((Object)(object)e.Owner == (Object)null)
			{
				return false;
			}
			ColorProfile colorProfile = (e.IsLocal ? localProfile : ((!showOthers) ? null : plugin.GetRemoteProfile(e.SteamId, e.HasOwnerId, e.OwnerId)));
			if (colorProfile == null)
			{
				return false;
			}
			target = colorProfile.Get(e.Slot);
			fx = colorProfile.GetFx(e.Slot);
			if (!e.IsLocal && colorProfile.HasPairs(e.Slot))
			{
				pairs = colorProfile;
			}
			if (!target.IsActive && (fx == null || !fx.AnyOn))
			{
				return pairs != null;
			}
			return true;
		}

		private void ApplyActive(Entry e, TargetColor t, SlotFx fx, ColorProfile pairs)
		{
			Renderer renderer = e.Renderer;
			Material val = FindSkinMaterial(renderer);
			if ((Object)(object)val == (Object)null)
			{
				if (e.Touched || e.MaterialBlocks.Count > 0)
				{
					ApplyInactive(e);
				}
				return;
			}
			if (!e.Touched)
			{
				e.Touched = true;
				e.GameOwnsBlock = renderer.HasPropertyBlock();
			}
			block.Clear();
			renderer.GetPropertyBlock(block);
			float num = BaseFloat(e, e.Hue, val, hueId);
			float num2;
			if (pairs != null && pairs.TryMapSpoof(e.Slot, num, out var exact))
			{
				num2 = exact * 1f;
			}
			else if (t.ChangesHue)
			{
				float num3 = t.HueDegrees / 360f * 1f;
				num2 = Mathf.Repeat(t.Override ? num3 : (num + num3), 1f);
			}
			else
			{
				num2 = num;
			}
			block.SetFloat(hueId, num2);
			e.Hue.Wrote = true;
			e.Hue.Last = num2;
			renderer.SetPropertyBlock(block);
			bool tintActive = t.TintActive;
			bool flag = fx?.AnyOn ?? false;
			if (tintActive || flag)
			{
				ApplyMaterialBlocks(e, t, tintActive, fx, num2 / 1f);
			}
			else if (e.MaterialBlocks.Count > 0)
			{
				ClearMaterialBlocks(e);
			}
		}

		private void ApplyMaterialBlocks(Entry e, TargetColor t, bool tintOn, SlotFx fx, float hueTurns)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			Renderer renderer = e.Renderer;
			materialBuffer.Clear();
			renderer.GetSharedMaterials(materialBuffer);
			bool flag = fx?.ExactColors ?? true;
			Color val = (tintOn ? Color.Lerp(Color.white, t.TintColor, t.TintStrength) : Color.white);
			Color val4 = default(Color);
			for (int i = 0; i < materialBuffer.Count; i++)
			{
				Material val2 = materialBuffer[i];
				bool flag2 = false;
				if ((Object)(object)val2 != (Object)null && val2.HasProperty(hueId))
				{
					materialBlock.Clear();
					CopyRendererBlock(e, val2.shader);
					if (tintOn)
					{
						for (int j = 0; j < tintIds.Length; j++)
						{
							int num = tintIds[j];
							if (val2.HasProperty(num))
							{
								Color val3 = ((e.GameOwnsBlock && block.HasColor(num)) ? block.GetColor(num) : val2.GetColor(num));
								((Color)(ref val4))..ctor(val3.r * val.r, val3.g * val.g, val3.b * val.b, val3.a);
								materialBlock.SetColor(num, flag ? ColorUtil.CompensateHue(val4, hueTurns) : val4);
								flag2 = true;
							}
						}
					}
					if (fx != null)
					{
						flag2 |= WriteEffects(val2, fx, flag, hueTurns);
					}
				}
				if (flag2)
				{
					renderer.SetPropertyBlock(materialBlock, i);
					if (!e.MaterialBlocks.Contains(i))
					{
						e.MaterialBlocks.Add(i);
					}
				}
				else if (e.MaterialBlocks.Remove(i))
				{
					renderer.SetPropertyBlock((MaterialPropertyBlock)null, i);
				}
			}
			for (int num2 = e.MaterialBlocks.Count - 1; num2 >= 0; num2--)
			{
				if (e.MaterialBlocks[num2] >= materialBuffer.Count)
				{
					e.MaterialBlocks.RemoveAt(num2);
				}
			}
			if (!e.MaterialsLogged)
			{
				LogMaterials(e, tintOn, fx);
			}
		}

		private bool WriteEffects(Material m, SlotFx fx, bool exact, float hueTurns)
		{
			//IL_00ce: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			if (fx.Accent)
			{
				Color val = (exact ? ColorUtil.CompensateHue(fx.AccentColor, hueTurns) : fx.AccentColor);
				Color value = (fx.AccentShineMatch ? val : (exact ? ColorUtil.CompensateHue(fx.AccentShine, hueTurns) : fx.AccentShine));
				flag |= SetFloat(m, FxIds.Trim, fx.AccentAmount);
				flag |= SetColor(m, FxIds.TrimColor, val);
				flag |= SetColor(m, FxIds.TrimSpecular, value);
				flag |= SetFloat(m, FxIds.InvertTrim, fx.AccentFlip ? 1f : 0f);
				flag |= SetFloat(m, FxIds.AlphaTrim, fx.AccentAltMask ? 1f : 0f);
			}
			if (fx.Glow)
			{
				Color val2 = fx.GlowColor * fx.GlowIntensity;
				val2.a = 1f;
				flag |= SetFloat(m, FxIds.EnableEmission, 1f);
				flag |= SetColor(m, FxIds.EmissionColor, exact ? ColorUtil.CompensateHue(val2, hueTurns) : val2);
			}
			if (fx.Shine)
			{
				flag |= SetColor(m, FxIds.SpecularColor, exact ? ColorUtil.CompensateHue(fx.ShineColor, hueTurns) : fx.ShineColor);
				flag |= SetFloat(m, FxIds.Smoothness, fx.Smoothness);
			}
			if (fx.Neon)
			{
				for (int i = 0; i < 5; i++)
				{
					flag |= SetFloat(m, FxIds.Neon[i], fx.NeonValues[i]);
				}
			}
			for (int j = 0; j < 11; j++)
			{
				if (fx.EffectOn[j])
				{
					flag |= SetFloat(m, FxIds.Sliders[j], fx.EffectValue[j]);
				}
			}
			return flag;
		}

		private bool SetFloat(Material m, int id, float value)
		{
			if (!m.HasProperty(id))
			{
				return false;
			}
			materialBlock.SetFloat(id, value);
			return true;
		}

		private bool SetColor(Material m, int id, Color value)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (!m.HasProperty(id))
			{
				return false;
			}
			materialBlock.SetColor(id, value);
			return true;
		}

		private void CopyRendererBlock(Entry e, Shader shader)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected I4, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected I4, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			if (!e.GameOwnsBlock)
			{
				if (block.HasFloat(hueId))
				{
					materialBlock.SetFloat(hueId, block.GetFloat(hueId));
				}
			}
			else
			{
				if ((Object)(object)shader == (Object)null)
				{
					return;
				}
				int instanceID = ((Object)shader).GetInstanceID();
				if (!shaderProps.TryGetValue(instanceID, out var value))
				{
					int propertyCount = shader.GetPropertyCount();
					int[] array = new int[propertyCount];
					ShaderPropertyType[] array2 = (ShaderPropertyType[])(object)new ShaderPropertyType[propertyCount];
					for (int i = 0; i < propertyCount; i++)
					{
						array[i] = shader.GetPropertyNameId(i);
						array2[i] = (ShaderPropertyType)(int)shader.GetPropertyType(i);
					}
					value = new KeyValuePair<int[], ShaderPropertyType[]>(array, array2);
					shaderProps[instanceID] = value;
				}
				int[] key = value.Key;
				ShaderPropertyType[] value2 = value.Value;
				for (int j = 0; j < key.Length; j++)
				{
					int num = key[j];
					ShaderPropertyType val = value2[j];
					switch ((int)val)
					{
					case 0:
						if (block.HasColor(num))
						{
							materialBlock.SetColor(num, block.GetColor(num));
						}
						break;
					case 1:
						if (block.HasVector(num))
						{
							materialBlock.SetVector(num, block.GetVector(num));
						}
						break;
					case 2:
					case 3:
						if (block.HasFloat(num))
						{
							materialBlock.SetFloat(num, block.GetFloat(num));
						}
						break;
					case 4:
						if (block.HasTexture(num))
						{
							Texture texture = block.GetTexture(num);
							if ((Object)(object)texture != (Object)null)
							{
								materialBlock.SetTexture(num, texture);
							}
						}
						break;
					case 5:
						if (block.HasInteger(num))
						{
							materialBlock.SetInteger(num, block.GetInteger(num));
						}
						break;
					}
				}
			}
		}

		private void ClearMaterialBlocks(Entry e)
		{
			Renderer renderer = e.Renderer;
			if ((Object)(object)renderer != (Object)null && e.MaterialBlocks.Count > 0)
			{
				countBuffer.Clear();
				renderer.GetSharedMaterials(countBuffer);
				for (int i = 0; i < e.MaterialBlocks.Count; i++)
				{
					if (e.MaterialBlocks[i] < countBuffer.Count)
					{
						renderer.SetPropertyBlock((MaterialPropertyBlock)null, e.MaterialBlocks[i]);
					}
				}
				countBuffer.Clear();
			}
			e.MaterialBlocks.Clear();
		}

		private void LogMaterials(Entry e, bool tintOn, SlotFx fx)
		{
			e.MaterialsLogged = true;
			if (materialLogs >= 12)
			{
				return;
			}
			materialLogs++;
			List<string> list = new List<string>();
			foreach (Material item in materialBuffer)
			{
				if ((Object)(object)item == (Object)null)
				{
					list.Add("(none)");
					continue;
				}
				string text = (((Object)(object)item.shader != (Object)null) ? ((Object)item.shader).name : "?");
				List<string> list2 = new List<string>();
				for (int i = 0; i < tintNames.Length; i++)
				{
					if (item.HasProperty(tintIds[i]))
					{
						list2.Add(tintNames[i]);
					}
				}
				if (item.HasProperty(FxIds.TrimColor))
				{
					list2.Add("_TrimColor");
				}
				if (item.HasProperty(FxIds.EmissionColor))
				{
					list2.Add("_EmissionColor");
				}
				bool flag = item.HasProperty(hueId);
				list.Add(((Object)item).name + " [" + text + (flag ? ", skin: recolored" : ", not a skin material: left alone") + ((list2.Count > 0) ? (", has " + string.Join("/", list2)) : "") + "]");
			}
			string text2 = ((!tintOn) ? "effects" : ((fx != null && fx.AnyOn) ? "tint + effects" : "tint"));
			SkinRecolorPlugin.Log.LogInfo((object)string.Format("{0} on '{1}' ({2} material(s) written). Materials: {3}", text2, ((Object)e.Renderer).name, e.MaterialBlocks.Count, string.Join("; ", list)));
		}

		private void ApplyInactive(Entry e)
		{
			if (e.MaterialBlocks.Count > 0)
			{
				ClearMaterialBlocks(e);
			}
			if (!e.AnyWrote)
			{
				e.ResetWrites();
			}
			else if (!e.GameOwnsBlock)
			{
				e.Renderer.SetPropertyBlock((MaterialPropertyBlock)null);
				e.ResetWrites();
			}
			else
			{
				WriteNeutral(e);
			}
		}

		private void WriteNeutral(Entry e)
		{
			Renderer renderer = e.Renderer;
			Material val = FindSkinMaterial(renderer);
			block.Clear();
			renderer.GetPropertyBlock(block);
			if (e.Hue.Wrote)
			{
				float num = (((Object)(object)val != (Object)null) ? BaseFloat(e, e.Hue, val, hueId) : e.Hue.Base);
				block.SetFloat(hueId, num);
				e.Hue.Last = num;
			}
			renderer.SetPropertyBlock(block);
		}

		private float BaseFloat(Entry e, FloatState st, Material mat, int id)
		{
			if (block.HasFloat(id))
			{
				float num = block.GetFloat(id);
				if (st.Wrote && num == st.Last)
				{
					if (!st.BaseFromBlock)
					{
						st.Base = mat.GetFloat(id);
					}
					return st.Base;
				}
				st.BaseFromBlock = true;
				e.GameOwnsBlock = true;
				st.Base = num;
				return num;
			}
			st.BaseFromBlock = false;
			st.Base = mat.GetFloat(id);
			return st.Base;
		}
	}
	internal sealed class RecolorWindow
	{
		private sealed class FieldState
		{
			public string Text;

			public string Source;
		}

		private const int WindowId = 86769927;

		private const float WindowWidth = 520f;

		private static readonly string[] ModeLabels = new string[2] { "Shift skin's hue", "Override hue" };

		private static readonly float[] HuePresets = new float[8] { 0f, 45f, 90f, 135f, 180f, 225f, 270f, 315f };

		private static readonly Color[] TintPresets = (Color[])(object)new Color[10]
		{
			new Color(1f, 1f, 1f),
			new Color(0.08f, 0.08f, 0.08f),
			new Color(0.85f, 0.1f, 0.12f),
			new Color(1f, 0.5f, 0.05f),
			new Color(1f, 0.85f, 0.1f),
			new Color(0.15f, 0.8f, 0.25f),
			new Color(0.1f, 0.8f, 0.85f),
			new Color(0.15f, 0.35f, 1f),
			new Color(0.55f, 0.2f, 0.9f),
			new Color(1f, 0.4f, 0.75f)
		};

		private readonly SkinRecolorPlugin plugin;

		private Rect rect = new Rect(80f, 80f, 520f, 10f);

		private float virtualWidth = 1920f;

		private float virtualHeight = 1080f;

		private int tab;

		private Vector2 scroll;

		private float contentHeight;

		private const int SecHue = 0;

		private const int SecTint = 1;

		private const int SecAccent = 2;

		private const int SecGlow = 3;

		private const int SecShine = 4;

		private const int SecNeon = 5;

		private const int SecEffects = 6;

		private const float LabelWidth = 96f;

		private readonly Dictionary<string, FieldState> fields = new Dictionary<string, FieldState>();

		private readonly bool[] sectionOpen = new bool[7] { true, true, false, false, false, false, false };

		private readonly Dictionary<string, bool> layoutFlags = new Dictionary<string, bool>();

		private int shownTab;

		private GUIStyle foldout;

		private Texture2D hueStrip;

		private Texture2D white;

		private GUIStyle header;

		private GUIStyle note;

		private GUIStyle playerLine;

		private readonly List<string> vanillaLines = new List<string>();

		private bool menuEnabledByUs;

		private Player lockedPlayer;

		private bool lookLocked;

		private bool fireLocked;

		public bool IsOpen { get; private set; }

		public RecolorWindow(SkinRecolorPlugin plugin)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			this.plugin = plugin;
		}

		public void Toggle()
		{
			if (IsOpen)
			{
				Close();
			}
			else
			{
				Open();
			}
		}

		public void Open()
		{
			if (!IsOpen)
			{
				IsOpen = true;
				LockGameInput();
			}
		}

		public void Close()
		{
			if (IsOpen)
			{
				IsOpen = false;
				UnlockGameInput();
				plugin.SaveSoon();
			}
		}

		public void ForceClose()
		{
			if (IsOpen)
			{
				IsOpen = false;
				UnlockGameInput();
			}
		}

		public void Update()
		{
			if (IsOpen)
			{
				RefreshLocks();
				FreeCursor();
			}
		}

		public void LateUpdate()
		{
			if (IsOpen)
			{
				FreeCursor();
			}
		}

		public void OnGUI()
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOpen)
			{
				return;
			}
			EnsureResources();
			float value = plugin.Settings.UiScale.Value;
			float num = ((value > 0.01f) ? value : Mathf.Max(1f, (float)Screen.height / 1080f));
			virtualWidth = (float)Screen.width / num;
			virtualHeight = (float)Screen.height / num;
			Matrix4x4 matrix = GUI.matrix;
			GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
			try
			{
				((Rect)(ref rect)).width = 520f;
				((Rect)(ref rect)).height = 0f;
				rect = GUILayout.Window(86769927, rect, new WindowFunction(DrawWindow), "Skin Recolor", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) });
				((Rect)(ref rect)).x = Mathf.Clamp(((Rect)(ref rect)).x, 0f, Mathf.Max(0f, virtualWidth - ((Rect)(ref rect)).width));
				((Rect)(ref rect)).y = Mathf.Clamp(((Rect)(ref rect)).y, 0f, Mathf.Max(0f, virtualHeight - 40f));
			}
			finally
			{
				GUI.matrix = matrix;
			}
		}

		private void DrawWindow(int id)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Invalid comparison between Unknown and I4
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Invalid comparison between Unknown and I4
			//IL_0229: 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)
			Settings settings = plugin.Settings;
			float num = Mathf.Max(200f, virtualHeight - ((Rect)(ref rect)).y - 60f);
			float num2 = ((contentHeight > 1f) ? Mathf.Min(contentHeight + 6f, num) : Mathf.Min(650f, num));
			scroll = GUILayout.BeginScrollView(scroll, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num2) });
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			bool flag = Stable("all", settings.UseCharacterForAll.Value);
			if (flag)
			{
				tab = 0;
				GUILayout.Label("Editing <b>Character</b>. These colors also apply to your weapons and gear.", note, Array.Empty<GUILayoutOption>());
			}
			else
			{
				tab = GUILayout.Toolbar(tab, Settings.TargetNames, Array.Empty<GUILayoutOption>());
			}
			if ((int)Event.current.type == 8)
			{
				shownTab = tab;
			}
			DrawTarget(settings.Targets[shownTab], shownTab);
			GUILayout.Space(6f);
			bool flag2 = GUILayout.Toggle(flag, " Use Character colors for weapons & gear", Array.Empty<GUILayoutOption>());
			if (flag2 != flag)
			{
				settings.UseCharacterForAll.Value = flag2;
			}
			GUILayout.Space(10f);
			DrawMultiplayer(settings);
			GUILayout.Space(8f);
			GUILayout.Label("Shift mode at 0° = the skin's original colors. Drag the hue slider to rotate every color on the skin. Works on every skin for you and other modded players. Press <b>" + settings.ToggleKey.Value + "</b> to close.", note, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			if ((int)Event.current.type == 7)
			{
				Rect lastRect = GUILayoutUtility.GetLastRect();
				float height = ((Rect)(ref lastRect)).height;
				if (height > 1f && Mathf.Abs(height - contentHeight) > 0.5f)
				{
					contentHeight = height;
				}
			}
			GUILayout.EndScrollView();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f));
		}

		private void DrawTarget(TargetConfig tc, int index)
		{
			string text = Settings.TargetNames[index];
			string text2 = index + ".";
			bool flag = GUILayout.Toggle(tc.Enabled.Value, " Recolor " + text, Array.Empty<GUILayoutOption>());
			if (flag != tc.Enabled.Value)
			{
				tc.Enabled.Value = flag;
			}
			bool enabled = GUI.enabled;
			GUI.enabled = enabled && flag;
			bool enabled2 = GUI.enabled;
			if (Section(0, "Hue", tc.Mode.Value == HueMode.Override || Mathf.Abs(Mathf.DeltaAngle(0f, tc.Hue.Value)) > 0.005f))
			{
				DrawHue(tc, text2);
			}
			if (Section(1, "Tint", tc.TintEnabled.Value))
			{
				Toggle(tc.TintEnabled, " Tint (multiplies the skin's base & shadow colors: white becomes this color)");
				GUI.enabled = enabled2 && tc.TintEnabled.Value;
				ColorEditor(text2 + "tint", tc.TintColor);
				SliderRow(text2 + "tintStrength", "Strength", tc.TintStrength, 0f, 1f, 100f, "%", 0.001f);
				GUI.enabled = enabled2;
			}
			if (Section(2, "Accent (trim)", tc.AccentEnabled.Value))
			{
				Toggle(tc.AccentEnabled, " Accent color (the skin's trim layer)");
				GUI.enabled = enabled2 && tc.AccentEnabled.Value;
				ColorEditor(text2 + "accent", tc.AccentColor);
				SliderRow(text2 + "accentAmount", "Amount", tc.AccentAmount, 0f, 1f, 100f, "%", 0.001f);
				Toggle(tc.AccentShineMatch, " Highlights use the accent color");
				if (!Stable(text2 + "shineMatch", tc.AccentShineMatch.Value))
				{
					GUILayout.Label("Highlight color", note, Array.Empty<GUILayoutOption>());
					ColorEditor(text2 + "accentShine", tc.AccentShineColor);
				}
				Toggle(tc.AccentFlip, " Flip which areas are accent");
				Toggle(tc.AccentAltMask, " Alternate accent mask");
				GUI.enabled = enabled2;
			}
			if (Section(3, "Glow", tc.GlowEnabled.Value))
			{
				Toggle(tc.GlowEnabled, " Glow (emissive parts light up in this color)");
				GUI.enabled = enabled2 && tc.GlowEnabled.Value;
				ColorEditor(text2 + "glow", tc.GlowColor);
				SliderRow(text2 + "glowIntensity", "Intensity", tc.GlowIntensity, 0f, 20f, 1f, "x", 0.01f);
				GUI.enabled = enabled2;
			}
			if (Section(4, "Shine", tc.ShineEnabled.Value))
			{
				Toggle(tc.ShineEnabled, " Shine (highlight color and glossiness)");
				GUI.enabled = enabled2 && tc.ShineEnabled.Value;
				ColorEditor(text2 + "shine", tc.ShineColor);
				SliderRow(text2 + "smooth", "Smoothness", tc.ShineSmoothness, 0f, 1f, 100f, "%", 0.001f);
				GUI.enabled = enabled2;
			}
			if (Section(5, "Neon", tc.NeonEnabled.Value))
			{
				Toggle(tc.NeonEnabled, " Neon (the game's Neon effect, per channel)");
				GUI.enabled = enabled2 && tc.NeonEnabled.Value;
				for (int i = 0; i < 5; i++)
				{
					SliderRow(text2 + "neon" + i, Effects.NeonNames[i], tc.Neon[i], 0f, 1f, 100f, "%", 0.001f);
				}
				GUI.enabled = enabled2;
			}
			int num = 0;
			for (int j = 0; j < 11; j++)
			{
				if (tc.EffectOn[j].Value)
				{
					num++;
				}
			}
			if (Section(6, (num > 0) ? $"Effects ({num} on)" : "Effects", num > 0))
			{
				for (int k = 0; k < 11; k++)
				{
					GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
					bool flag2 = GUILayout.Toggle(tc.EffectOn[k].Value, " " + Effects.Sliders[k].Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(116f) });
					if (flag2 != tc.EffectOn[k].Value)
					{
						tc.EffectOn[k].Value = flag2;
					}
					GUI.enabled = enabled2 && flag2;
					SliderControls(text2 + "fx" + k, tc.EffectValue[k], 0f, 1f, 100f, "%", 0.001f);
					GUI.enabled = enabled2;
					GUILayout.EndHorizontal();
				}
				GUILayout.Label(Effects.Sliders[0].Help, note, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(4f);
			Toggle(tc.ExactColors, " Color codes are exact (the hue rotation won't shift tint/accent/glow/shine)");
			GUI.enabled = enabled;
			GUILayout.Space(4f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Reset " + text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
			{
				tc.ResetToDefaults();
				GUIUtility.keyboardControl = 0;
			}
			GUILayout.EndHorizontal();
		}

		private void DrawHue(TargetConfig tc, string p)
		{
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			int value = (int)tc.Mode.Value;
			int num = GUILayout.Toolbar(value, ModeLabels, Array.Empty<GUILayoutOption>());
			if (num != value)
			{
				tc.Mode.Value = (HueMode)num;
			}
			float num2 = tc.Hue.Value;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Rotation", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(96f) });
			string text = EditField(p + "hue", FormatNumber(num2), 70f, 8);
			GUILayout.Label("°  (type any value, e.g. 252.5)", note, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			if (text != null && ColorUtil.TryParseNumber(text, out var value2))
			{
				float num3 = Mathf.Repeat(value2, 360f);
				if (Mathf.Approximately(value2, 360f))
				{
					num3 = 360f;
				}
				SetSource(p + "hue", FormatNumber(num3));
				tc.Hue.Value = num3;
				num2 = num3;
			}
			Rect val = GUILayoutUtility.GetRect(10f, 12f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUI.DrawTexture(val, (Texture)(object)hueStrip);
			float num4 = ((Rect)(ref val)).x + ((Rect)(ref val)).width * Mathf.Clamp01(num2 / 360f);
			Color color = GUI.color;
			GUI.color = Color.black;
			GUI.DrawTexture(new Rect(num4 - 2f, ((Rect)(ref val)).y - 2f, 4f, ((Rect)(ref val)).height + 4f), (Texture)(object)white);
			GUI.color = Color.white;
			GUI.DrawTexture(new Rect(num4 - 1f, ((Rect)(ref val)).y - 1f, 2f, ((Rect)(ref val)).height + 2f), (Texture)(object)white);
			GUI.color = color;
			float num5 = GUILayout.HorizontalSlider(num2, 0f, 360f, Array.Empty<GUILayoutOption>());
			if (Mathf.Abs(num5 - num2) > 0.0001f)
			{
				tc.Hue.Value = Mathf.Clamp(Mathf.Round(num5 * 10f) / 10f, 0f, 360f);
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			HueStep(tc, "-10", -10f);
			HueStep(tc, "-1", -1f);
			HueStep(tc, "-0.1", -0.1f);
			HueStep(tc, "+0.1", 0.1f);
			HueStep(tc, "+1", 1f);
			HueStep(tc, "+10", 10f);
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			float[] huePresets = HuePresets;
			foreach (float num6 in huePresets)
			{
				if (GUILayout.Button($"{num6:0}°", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(20f) }))
				{
					tc.Hue.Value = num6;
				}
			}
			GUILayout.EndHorizontal();
		}

		private static void HueStep(TargetConfig tc, string label, float delta)
		{
			if (GUILayout.Button(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(36f) }))
			{
				tc.Hue.Value = Mathf.Round(Mathf.Repeat(tc.Hue.Value + delta, 360f) * 10f) / 10f;
			}
		}

		private bool Section(int index, string title, bool on)
		{
			string text = (sectionOpen[index] ? "[-]" : "[+]");
			string text2 = (on ? "   <color=#8fe08f>on</color>" : "");
			GUILayout.Space(2f);
			bool enabled = GUI.enabled;
			GUI.enabled = true;
			if (GUILayout.Button(text + "  <b>" + title + "</b>" + text2, foldout, Array.Empty<GUILayoutOption>()))
			{
				sectionOpen[index] = !sectionOpen[index];
			}
			GUI.enabled = enabled;
			return Stable("section" + index, sectionOpen[index]);
		}

		private bool Stable(string key, bool live)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if ((int)Event.current.type == 8 || !layoutFlags.TryGetValue(key, out var value))
			{
				layoutFlags[key] = live;
				return live;
			}
			return value;
		}

		private static void Toggle(ConfigEntry<bool> entry, string label)
		{
			bool flag = GUILayout.Toggle(entry.Value, label, Array.Empty<GUILayoutOption>());
			if (flag != entry.Value)
			{
				entry.Value = flag;
			}
		}

		private void ColorEditor(string id, ConfigEntry<string> entry)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//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_0011: Unknown result type (might be due to invalid IL or missing refere