Decompiled source of AvatarStatsChanger v1.0.1

Mods/AvatarStatsChanger.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using AvatarStatsChanger;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSLZ.VRMK;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Main), "AvatarStatsChanger", "1.0.1", "kamonkus", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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 AvatarStatsChanger
{
	internal static class AvatarEditor
	{
		public const int Count = 11;

		public static readonly string[] Labels = new string[11]
		{
			"Agility", "Strength Upper", "Strength Lower", "Vitality", "Speed", "Intelligence", "Chest Mass", "Pelvis Mass", "Head Mass", "Arm Mass",
			"Leg Mass"
		};

		private static readonly string[] Names = new string[11]
		{
			"agility", "strengthupper", "strengthlower", "vitality", "speed", "intelligence", "masschest", "masspelvis", "masshead", "massarm",
			"massleg"
		};

		private static readonly string[][] Tokens = new string[11][]
		{
			new string[1] { "agility" },
			new string[2] { "strength", "upper" },
			new string[2] { "strength", "lower" },
			new string[1] { "vitality" },
			new string[1] { "speed" },
			new string[1] { "intelligence" },
			new string[2] { "mass", "chest" },
			new string[2] { "mass", "pelvis" },
			new string[2] { "mass", "head" },
			new string[2] { "mass", "arm" },
			new string[2] { "mass", "leg" }
		};

		private static readonly PropertyInfo[] _loaded = new PropertyInfo[11];

		private static readonly PropertyInfo[] _computed = new PropertyInfo[11];

		private static bool _resolved;

		private static PropertyInfo _massTotalProp;

		private static float _nextWatch;

		private static readonly Dictionary<long, float[]> _orig = new Dictionary<long, float[]>();

		private static readonly List<float> _schedule = new List<float>();

		private static readonly HashSet<string> _noted = new HashSet<string>();

		private static float _refreshAt = -1f;

		private static bool _uiDirty;

		private static bool IsFloat(PropertyInfo pi)
		{
			if (pi.PropertyType == typeof(float) && pi.CanRead)
			{
				return pi.GetIndexParameters().Length == 0;
			}
			return false;
		}

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			PropertyInfo[] properties = typeof(Avatar).GetProperties(BindingFlags.Instance | BindingFlags.Public);
			PropertyInfo[] array = properties;
			foreach (PropertyInfo propertyInfo in array)
			{
				if (propertyInfo.CanWrite && IsFloat(propertyInfo) && propertyInfo.Name.ToLowerInvariant() == "_masstotal")
				{
					_massTotalProp = propertyInfo;
					break;
				}
			}
			for (int j = 0; j < 11; j++)
			{
				PropertyInfo propertyInfo2 = null;
				PropertyInfo propertyInfo3 = null;
				array = properties;
				foreach (PropertyInfo propertyInfo4 in array)
				{
					if (!IsFloat(propertyInfo4))
					{
						continue;
					}
					string text = propertyInfo4.Name.ToLowerInvariant();
					bool flag = text.StartsWith("_");
					if (!(text.TrimStart('_') != Names[j]))
					{
						if (flag && propertyInfo4.CanWrite && propertyInfo2 == null)
						{
							propertyInfo2 = propertyInfo4;
						}
						if (!flag && propertyInfo3 == null)
						{
							propertyInfo3 = propertyInfo4;
						}
					}
				}
				if (propertyInfo2 == null)
				{
					int num = int.MaxValue;
					array = properties;
					foreach (PropertyInfo propertyInfo5 in array)
					{
						if (!IsFloat(propertyInfo5) || !propertyInfo5.CanWrite)
						{
							continue;
						}
						string text2 = propertyInfo5.Name.ToLowerInvariant();
						bool flag2 = true;
						string[] array2 = Tokens[j];
						foreach (string value in array2)
						{
							if (!text2.Contains(value))
							{
								flag2 = false;
							}
						}
						if (flag2 && text2.Length < num)
						{
							num = text2.Length;
							propertyInfo2 = propertyInfo5;
						}
					}
				}
				_loaded[j] = propertyInfo2;
				_computed[j] = propertyInfo3;
				if (propertyInfo2 == null)
				{
					MelonLogger.Warning("[AvatarStatsChanger] stat not found on the avatar: " + Labels[j]);
				}
			}
		}

		private static Avatar CurrentAvatar()
		{
			try
			{
				return Player.Avatar;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static float[] OriginalsFor(Avatar av)
		{
			Resolve();
			long key = ((Il2CppObjectBase)av).Pointer.ToInt64();
			if (_orig.TryGetValue(key, out var value))
			{
				return value;
			}
			value = new float[12];
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < 11; i++)
			{
				value[i] = float.NaN;
				if (!(_loaded[i] == null))
				{
					try
					{
						value[i] = (float)_loaded[i].GetValue(av, null);
						stringBuilder.Append(Labels[i]).Append('=').Append(value[i].ToString("0.###"))
							.Append("  ");
					}
					catch (Exception)
					{
					}
				}
			}
			value[11] = float.NaN;
			if (_massTotalProp != null)
			{
				try
				{
					value[11] = (float)_massTotalProp.GetValue(av, null);
				}
				catch (Exception)
				{
				}
			}
			if (_orig.Count > 20)
			{
				_orig.Clear();
			}
			_orig[key] = value;
			return value;
		}

		public static float GetOriginal(int index)
		{
			Avatar val = CurrentAvatar();
			if ((Object)(object)val == (Object)null)
			{
				return float.NaN;
			}
			if (_orig.TryGetValue(((Il2CppObjectBase)val).Pointer.ToInt64(), out var value))
			{
				return value[index];
			}
			try
			{
				return OriginalsFor(val)[index];
			}
			catch (Exception)
			{
				return float.NaN;
			}
		}

		public static void ApplyToCurrent()
		{
			Avatar val = CurrentAvatar();
			if ((Object)(object)val == (Object)null || !Settings.Enabled.Value)
			{
				return;
			}
			try
			{
				if (ApplyTo(val, restore: false))
				{
					_refreshAt = Time.unscaledTime + 0.35f;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[AvatarStatsChanger] apply failed: " + ex);
			}
		}

		public static void RestoreOriginals()
		{
			Avatar val = CurrentAvatar();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				if (ApplyTo(val, restore: true))
				{
					_refreshAt = Time.unscaledTime + 0.35f;
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[AvatarStatsChanger] restore failed: " + ex);
			}
		}

		private static bool AnyTouched()
		{
			for (int i = 0; i < 11; i++)
			{
				if (Settings.Touched[i].Value)
				{
					return true;
				}
			}
			return false;
		}

		private static bool ApplyTo(Avatar av, bool restore)
		{
			float[] array = OriginalsFor(av);
			float[] array2 = new float[11];
			bool result = false;
			for (int i = 0; i < 11; i++)
			{
				array2[i] = array[i];
				if (_loaded[i] == null || float.IsNaN(array[i]))
				{
					continue;
				}
				if (!restore && Settings.Touched[i].Value)
				{
					array2[i] = Settings.Value[i].Value;
				}
				if (i >= 6 && i <= 10 && array2[i] < 0.001f)
				{
					array2[i] = 0.001f;
				}
				try
				{
					if (!Mathf.Approximately((float)_loaded[i].GetValue(av, null), array2[i]))
					{
						_loaded[i].SetValue(av, array2[i], null);
						result = true;
					}
				}
				catch (Exception ex)
				{
					Note("set " + Labels[i], "[AvatarStatsChanger] could not set " + Labels[i] + ": " + ex.Message);
				}
			}
			bool flag = _massTotalProp != null && !float.IsNaN(array[11]);
			for (int j = 6; j <= 10; j++)
			{
				if (float.IsNaN(array[j]))
				{
					flag = false;
				}
			}
			if (flag)
			{
				bool flag2 = false;
				for (int k = 6; k <= 10; k++)
				{
					if (!Mathf.Approximately(array2[k], array[k]))
					{
						flag2 = true;
					}
				}
				float num = array[11];
				if (flag2)
				{
					num = array2[6] + array2[7] + array2[8] + 2f * array2[9] + 2f * array2[10];
				}
				try
				{
					if (!Mathf.Approximately((float)_massTotalProp.GetValue(av, null), num))
					{
						_massTotalProp.SetValue(av, num, null);
						result = true;
					}
				}
				catch (Exception ex2)
				{
					Note("set masstotal", "[AvatarStatsChanger] could not set total mass: " + ex2.Message);
				}
			}
			return result;
		}

		private static void Note(string key, string message)
		{
			if (_noted.Add(key))
			{
				MelonLogger.Warning(message);
			}
		}

		private static bool TryInvoke(object target, string method, params object[] args)
		{
			try
			{
				Type[] array = new Type[args.Length];
				for (int i = 0; i < args.Length; i++)
				{
					array[i] = args[i].GetType();
				}
				MethodInfo methodInfo = null;
				MethodInfo[] methods = target.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (methodInfo2.Name != method)
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo2.GetParameters();
					if (parameters.Length != args.Length)
					{
						continue;
					}
					bool flag = true;
					for (int k = 0; k < parameters.Length; k++)
					{
						if (!parameters[k].ParameterType.IsAssignableFrom(array[k]))
						{
							flag = false;
						}
					}
					if (flag)
					{
						methodInfo = methodInfo2;
						break;
					}
				}
				if (methodInfo == null)
				{
					return false;
				}
				methodInfo.Invoke(target, args);
				return true;
			}
			catch (Exception ex)
			{
				Note("invoke " + method, "[AvatarStatsChanger] " + target.GetType().Name + "." + method + " failed: " + ex.Message);
				return false;
			}
		}

		private static void Refresh(Avatar av)
		{
			if (Settings.RigRefresh.Value)
			{
				object[] array = null;
				try
				{
					array = new object[3]
					{
						Player.PhysicsRig,
						Player.RemapRig,
						Player.ControllerRig
					};
				}
				catch (Exception)
				{
				}
				if (array != null)
				{
					object[] array2 = array;
					foreach (object obj in array2)
					{
						if (obj != null)
						{
							TryInvoke(obj, "SetAvatar", av);
						}
					}
				}
			}
			try
			{
				ApplyTo(av, !Settings.Enabled.Value);
			}
			catch (Exception)
			{
			}
			if (!Settings.RigRefresh.Value)
			{
				return;
			}
			try
			{
				object physicsRig = Player.PhysicsRig;
				if (physicsRig != null)
				{
					TryInvoke(physicsRig, "CalibrateJoints", av);
					TryInvoke(physicsRig, "SetBodyMassDistribution");
					TryInvoke(physicsRig, "UpdateTotalDerivatives");
				}
			}
			catch (Exception)
			{
			}
		}

		public static void OnAvatarSwitched(Avatar newAvatar)
		{
			try
			{
				if (!((Object)(object)newAvatar == (Object)null))
				{
					Avatar val = CurrentAvatar();
					if (!((Object)(object)val != (Object)null) || !(((Il2CppObjectBase)val).Pointer != ((Il2CppObjectBase)newAvatar).Pointer))
					{
						ScheduleApply();
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[AvatarStatsChanger] avatar switch error: " + ex);
			}
		}

		public static void ScheduleApply()
		{
			float unscaledTime = Time.unscaledTime;
			_schedule.Add(unscaledTime + 0.1f);
			_schedule.Add(unscaledTime + 0.8f);
			_schedule.Add(unscaledTime + 2.5f);
			_uiDirty = true;
		}

		public static void Tick()
		{
			float unscaledTime = Time.unscaledTime;
			for (int num = _schedule.Count - 1; num >= 0; num--)
			{
				if (!(_schedule[num] > unscaledTime))
				{
					_schedule.RemoveAt(num);
					if (Settings.AutoApply.Value)
					{
						ApplyToCurrent();
					}
					if (_uiDirty && _schedule.Count == 0)
					{
						_uiDirty = false;
						Settings.RefreshElements();
					}
				}
			}
			if (_refreshAt > 0f && unscaledTime >= _refreshAt)
			{
				_refreshAt = -1f;
				Avatar val = CurrentAvatar();
				if ((Object)(object)val != (Object)null && Settings.Enabled.Value)
				{
					try
					{
						Refresh(val);
					}
					catch (Exception ex)
					{
						MelonLogger.Error("[AvatarStatsChanger] refresh failed: " + ex);
					}
				}
			}
			if (unscaledTime >= _nextWatch)
			{
				_nextWatch = unscaledTime + 1f;
				if (Settings.Enabled.Value && AnyTouched())
				{
					ApplyToCurrent();
				}
			}
		}
	}
	public class Main : MelonMod
	{
		private float _nextErrorLog;

		public override void OnInitializeMelon()
		{
			Settings.Init();
			Hooking.OnSwitchAvatarPostfix += AvatarEditor.OnAvatarSwitched;
			Hooking.OnLevelLoaded += delegate
			{
				AvatarEditor.ScheduleApply();
			};
			Settings.BuildMenu();
			MelonLogger.Msg("AvatarStatsChanger loaded. Open BoneMenu -> AvatarStatsChanger.");
		}

		public override void OnUpdate()
		{
			try
			{
				AvatarEditor.Tick();
				Settings.Tick();
			}
			catch (Exception ex)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (realtimeSinceStartup > _nextErrorLog)
				{
					_nextErrorLog = realtimeSinceStartup + 5f;
					MelonLogger.Error("[AvatarStatsChanger] tick error: " + ex);
				}
			}
		}
	}
	public static class Settings
	{
		public const int SlotCount = 6;

		private static MelonPreferences_Category _cat;

		private static float _saveAt = -1f;

		private static bool _suppress;

		public static MelonPreferences_Entry<bool> Enabled;

		public static MelonPreferences_Entry<bool> AutoApply;

		public static MelonPreferences_Entry<bool> RigRefresh;

		public static MelonPreferences_Entry<float>[] Value = new MelonPreferences_Entry<float>[11];

		public static MelonPreferences_Entry<bool>[] Touched = new MelonPreferences_Entry<bool>[11];

		public static MelonPreferences_Entry<string>[] Slots = new MelonPreferences_Entry<string>[6];

		private static readonly float[] Steps = new float[4] { 1f, 10f, 50f, 100f };

		private static readonly FloatElement[][] _rows = new FloatElement[11][];

		private static readonly FunctionElement[] _slotInfo = (FunctionElement[])(object)new FunctionElement[6];

		private static readonly string[] _slotNameEdit = new string[6];

		private static readonly string[] PrefIds = new string[11]
		{
			"Agility", "StrengthUpper", "StrengthLower", "Vitality", "Speed", "Intelligence", "ChestMass", "PelvisMass", "HeadMass", "ArmMass",
			"LegMass"
		};

		public static void Init()
		{
			_cat = MelonPreferences.CreateCategory("AvatarStatsChanger");
			Enabled = _cat.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			AutoApply = _cat.CreateEntry<bool>("ApplyOnAvatarChange", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			RigRefresh = _cat.CreateEntry<bool>("RefreshRigAfterChange", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			for (int i = 0; i < 11; i++)
			{
				Value[i] = _cat.CreateEntry<float>("Value_" + PrefIds[i], 1f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
				Touched[i] = _cat.CreateEntry<bool>("Override_" + PrefIds[i], false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			}
			for (int j = 0; j < 6; j++)
			{
				Slots[j] = _cat.CreateEntry<string>("PresetV2_" + (j + 1), "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			}
		}

		public static void BuildMenu()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			Page val = Page.Root.CreatePage("AvatarStatsChanger", Color.cyan, 0, true);
			Page parent = val.CreatePage("Avatar Stats", Color.green, 0, true);
			for (int i = 0; i <= 5; i++)
			{
				BuildStatPage(parent, i);
			}
			Page parent2 = val.CreatePage("Avatar Mass", Color.red, 0, true);
			for (int j = 6; j <= 10; j++)
			{
				BuildStatPage(parent2, j);
			}
			Page parent3 = val.CreatePage("Presets", Color.magenta, 0, true);
			for (int k = 0; k < 6; k++)
			{
				BuildPresetPage(parent3, k);
			}
			val.CreateBool("Enabled", Color.white, Enabled.Value, (Action<bool>)delegate(bool v)
			{
				Enabled.Value = v;
				Dirty();
				if (v)
				{
					AvatarEditor.ApplyToCurrent();
				}
				else
				{
					AvatarEditor.RestoreOriginals();
				}
			});
			val.CreateBool("Apply on avatar change", Color.white, AutoApply.Value, (Action<bool>)delegate(bool v)
			{
				AutoApply.Value = v;
				Dirty();
			});
			val.CreateBool("Refresh body after change", Color.white, RigRefresh.Value, (Action<bool>)delegate(bool v)
			{
				RigRefresh.Value = v;
				Dirty();
			});
			val.CreateFunction("Reset everything", Color.yellow, (Action)ResetAll);
			RefreshElements();
		}

		private static void BuildStatPage(Page parent, int index)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			Page val = parent.CreatePage(AvatarEditor.Labels[index], Color.white, 0, true);
			_rows[index] = (FloatElement[])(object)new FloatElement[Steps.Length];
			float num = DisplayValue(index);
			for (int i = 0; i < Steps.Length; i++)
			{
				_rows[index][i] = val.CreateFloat(((int)Steps[i]).ToString(), Color.white, num, Steps[i], -100000f, 100000f, (Action<float>)delegate(float v)
				{
					OnRowChanged(index, v);
				});
			}
			val.CreateFunction("Set to 1.0", Color.white, (Action)delegate
			{
				SetValue(index, 1f);
			});
			val.CreateFunction("Load from avatar's loaded value", Color.white, (Action)delegate
			{
				LoadOriginal(index);
			});
		}

		private static float DisplayValue(int idx)
		{
			if (Touched[idx].Value)
			{
				return Value[idx].Value;
			}
			float original = AvatarEditor.GetOriginal(idx);
			if (!float.IsNaN(original))
			{
				return original;
			}
			return Value[idx].Value;
		}

		private static void SyncRows(int idx, float v)
		{
			if (_rows[idx] == null)
			{
				return;
			}
			bool suppress = _suppress;
			_suppress = true;
			try
			{
				for (int i = 0; i < _rows[idx].Length; i++)
				{
					FloatElement val = _rows[idx][i];
					if (val != null && !Mathf.Approximately(val.Value, v))
					{
						val.Value = v;
					}
				}
			}
			finally
			{
				_suppress = suppress;
			}
		}

		public static void RefreshElements()
		{
			for (int i = 0; i < 11; i++)
			{
				SyncRows(i, DisplayValue(i));
			}
		}

		private static void OnRowChanged(int idx, float v)
		{
			if (!_suppress)
			{
				Value[idx].Value = v;
				Touched[idx].Value = true;
				SyncRows(idx, v);
				Dirty();
				AvatarEditor.ApplyToCurrent();
			}
		}

		private static void SetValue(int idx, float v)
		{
			Value[idx].Value = v;
			Touched[idx].Value = true;
			SyncRows(idx, v);
			Dirty();
			AvatarEditor.ApplyToCurrent();
		}

		private static void LoadOriginal(int idx)
		{
			float original = AvatarEditor.GetOriginal(idx);
			if (!float.IsNaN(original))
			{
				Touched[idx].Value = false;
				Value[idx].Value = original;
				SyncRows(idx, original);
				Dirty();
				AvatarEditor.ApplyToCurrent();
			}
		}

		private static void ResetAll()
		{
			for (int i = 0; i < 11; i++)
			{
				Touched[i].Value = false;
			}
			Dirty();
			AvatarEditor.ApplyToCurrent();
			RefreshElements();
		}

		private static void BuildPresetPage(Page parent, int slot)
		{
			//IL_0028: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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)
			Page val = parent.CreatePage("Preset " + (slot + 1), Color.white, 0, true);
			_slotNameEdit[slot] = SlotName(slot) ?? ("Preset " + (slot + 1));
			_slotInfo[slot] = val.CreateFunction(SlotLabel(slot), Color.gray, (Action)delegate
			{
			});
			val.CreateString("Name", Color.white, _slotNameEdit[slot], (Action<string>)delegate(string v)
			{
				_slotNameEdit[slot] = v;
			});
			val.CreateFunction("Save current values here", Color.green, (Action)delegate
			{
				SavePreset(slot);
			});
			val.CreateFunction("Load this preset", Color.cyan, (Action)delegate
			{
				LoadPreset(slot);
			});
			val.CreateFunction("Clear this slot", Color.red, (Action)delegate
			{
				ClearPreset(slot);
			});
		}

		private static string SlotName(int s)
		{
			string value = Slots[s].Value;
			if (string.IsNullOrEmpty(value))
			{
				return null;
			}
			int num = value.IndexOf('|');
			if (num <= 0)
			{
				return value;
			}
			return value.Substring(0, num);
		}

		private static string SlotLabel(int s)
		{
			string text = SlotName(s);
			return "Slot " + (s + 1) + ": " + ((text != null) ? text : "(empty)");
		}

		private static void SavePreset(int s)
		{
			string text = (string.IsNullOrWhiteSpace(_slotNameEdit[s]) ? ("Preset " + (s + 1)) : _slotNameEdit[s].Replace("|", " ").Trim());
			string[] array = new string[11];
			for (int i = 0; i < 11; i++)
			{
				array[i] = DisplayValue(i).ToString("0.####", CultureInfo.InvariantCulture) + ":" + (Touched[i].Value ? "1" : "0");
			}
			Slots[s].Value = text + "|" + string.Join(";", array);
			Dirty();
			if (_slotInfo[s] != null)
			{
				((Element)_slotInfo[s]).ElementName = SlotLabel(s) + "  (saved)";
			}
		}

		private static void LoadPreset(int s)
		{
			string value = Slots[s].Value;
			if (string.IsNullOrEmpty(value))
			{
				if (_slotInfo[s] != null)
				{
					((Element)_slotInfo[s]).ElementName = SlotLabel(s) + "  (nothing to load)";
				}
				return;
			}
			int num = value.IndexOf('|');
			string[] array = ((num >= 0) ? value.Substring(num + 1) : value).Split(';');
			for (int i = 0; i < 11 && i < array.Length; i++)
			{
				string[] array2 = array[i].Split(':');
				if (!float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					result = 1f;
				}
				bool value2 = array2.Length > 1 && array2[1] == "1";
				Value[i].Value = Mathf.Clamp(result, -100000f, 100000f);
				Touched[i].Value = value2;
			}
			Dirty();
			AvatarEditor.ApplyToCurrent();
			RefreshElements();
			if (_slotInfo[s] != null)
			{
				((Element)_slotInfo[s]).ElementName = SlotLabel(s) + "  (loaded)";
			}
		}

		private static void ClearPreset(int s)
		{
			Slots[s].Value = "";
			Dirty();
			if (_slotInfo[s] != null)
			{
				((Element)_slotInfo[s]).ElementName = SlotLabel(s) + "  (cleared)";
			}
		}

		public static void Tick()
		{
			if (_saveAt > 0f && Time.realtimeSinceStartup >= _saveAt)
			{
				_saveAt = -1f;
				MelonPreferences.Save();
			}
		}

		public static void Dirty()
		{
			_saveAt = Time.realtimeSinceStartup + 1.5f;
		}
	}
}