Decompiled source of SaveSlotNames v1.3.0

Mods\SaveSlotNames.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MelonLoader;
using MelonLoader.Preferences;
using SaveSlotNames;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Plugin), "Save Slot Names", "1.3.0", "you", null)]
[assembly: MelonGame(null, null)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public NullableAttribute(byte b)
		{
		}

		public NullableAttribute(byte[] b)
		{
		}
	}
	[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public NullableContextAttribute(byte b)
		{
		}
	}
}
namespace SaveSlotNames
{
	public class Plugin : MelonMod
	{
		private class Slot
		{
			public Component Comp;

			public RectTransform Rt;

			public string Key;
		}

		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private MelonPreferences_Entry<string> _pinType;

		private MelonPreferences_Entry<string> _stored;

		private MelonPreferences_Entry<string> _avoidType;

		private MelonPreferences_Entry<float> _boxW;

		private MelonPreferences_Entry<float> _boxH;

		private MelonPreferences_Entry<float> _offX;

		private MelonPreferences_Entry<float> _offY;

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

		private readonly List<Slot> _slots = new List<Slot>();

		private readonly List<Slot> _avoid = new List<Slot>();

		private List<Type> _cands;

		private float _scanT;

		private string _lastSig;

		private float _dumpAt = 8f;

		private string _editKey;

		private string _editText = "";

		private Il2CppStructArray<Vector3> _corners;

		private bool _legacyBad;

		private GUIStyle _tf;

		private GUIStyle _hint;

		private static readonly string[] Skip = new string[25]
		{
			"inventory", "hotbar", "equip", "item", "quick", "ability", "weapon", "armor", "armour", "backpack",
			"craft", "skill", "attachment", "gear", "loot", "chest", "container", "ammo", "cargo", "storage",
			"trade", "shop", "socket", "hud", "bar"
		};

		private static readonly string[] Pref = new string[8] { "save", "load", "world", "game", "profile", "file", "continue", "menu" };

		private static readonly string[] KeyFields = new string[23]
		{
			"slotIndex", "SlotIndex", "slotId", "SlotId", "slotNumber", "SlotNumber", "slot", "Slot", "saveName", "SaveName",
			"fileName", "FileName", "saveFile", "SaveFile", "path", "Path", "saveId", "SaveId", "index", "Index",
			"id", "Id", "ID"
		};

		private readonly Dictionary<string, Type> _avoidTypes = new Dictionary<string, Type>();

		public override void OnInitializeMelon()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("SaveSlotNames");
			_pinType = val.CreateEntry<string>("slot_type_name", "FFSaveFileButton", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_avoidType = val.CreateEntry<string>("avoid_type_names", "FFDeleteSaveButton", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_boxW = val.CreateEntry<float>("box_width_v2", 320f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_boxH = val.CreateEntry<float>("box_height_v2", 40f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_offX = val.CreateEntry<float>("offset_x_v2", 20f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_offY = val.CreateEntry<float>("offset_y_v2", 0f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			_stored = val.CreateEntry<string>("names", "", (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			string[] array = _stored.Value.Split('&');
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0)
				{
					try
					{
						_names[Uri.UnescapeDataString(text.Substring(0, num))] = Uri.UnescapeDataString(text.Substring(num + 1));
					}
					catch
					{
					}
				}
			}
			_corners = new Il2CppStructArray<Vector3>(4L);
			((MelonBase)this).LoggerInstance.Msg("Save Slot Names 1.3.0 loaded (" + _names.Count + " saved names).");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			_dumpAt = Time.unscaledTime + 8f;
			((MelonBase)this).LoggerInstance.Msg("Scene loaded: " + sceneName);
		}

		public override void OnApplicationQuit()
		{
			Commit();
		}

		private void Store()
		{
			_stored.Value = string.Join("&", _names.Select((KeyValuePair<string, string> kv) => Uri.EscapeDataString(kv.Key) + "=" + Uri.EscapeDataString(kv.Value)));
			MelonPreferences.Save();
		}

		private void Commit()
		{
			if (_editKey != null)
			{
				string text = (_editText ?? "").Trim();
				if (text.Length == 0)
				{
					_names.Remove(_editKey);
				}
				else
				{
					_names[_editKey] = text;
				}
				_editKey = null;
				Store();
			}
		}

		private string NameOf(string key)
		{
			if (!_names.TryGetValue(key, out var value))
			{
				return "";
			}
			return value;
		}

		private static object Get(object o, string name)
		{
			if (o == null)
			{
				return null;
			}
			try
			{
				Type type = o.GetType();
				PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanRead && property.GetIndexParameters().Length == 0)
				{
					return property.GetValue(o);
				}
				FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field.GetValue(o);
				}
			}
			catch
			{
			}
			return null;
		}

		private static List<Assembly> GameAsms()
		{
			return AppDomain.CurrentDomain.GetAssemblies().Where(delegate(Assembly a)
			{
				string name = a.GetName().Name;
				return !name.StartsWith("System") && !name.StartsWith("Microsoft") && !name.StartsWith("Mono.") && !name.StartsWith("netstandard") && !name.StartsWith("mscorlib") && !name.StartsWith("MelonLoader") && !name.StartsWith("0Harmony") && !name.StartsWith("Il2CppInterop") && !name.StartsWith("Il2Cppmscorlib") && !name.StartsWith("Il2CppSystem");
			}).ToList();
		}

		private List<Type> Candidates()
		{
			if (_cands != null)
			{
				return _cands;
			}
			_cands = new List<Type>();
			List<string> list = (from x in _pinType.Value.Split(',')
				select x.Trim() into x
				where x.Length > 0
				select x).ToList();
			if (list.Count == 0)
			{
				list.Add("FFSaveFileButton");
			}
			foreach (Assembly item in GameAsms())
			{
				Type[] array;
				try
				{
					array = item.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types.Where((Type t) => t != null).ToArray();
				}
				catch
				{
					continue;
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					if (type.Name == null || !typeof(Component).IsAssignableFrom(type))
					{
						continue;
					}
					string l = type.Name.ToLowerInvariant();
					if (list.Count > 0)
					{
						if (list.Contains(type.Name))
						{
							_cands.Add(type);
						}
					}
					else if (l.Contains("slot") && !Skip.Any((string s) => l.Contains(s)))
					{
						_cands.Add(type);
					}
				}
			}
			return _cands;
		}

		private Type AvoidType(string name)
		{
			if (_avoidTypes.TryGetValue(name, out var value))
			{
				return value;
			}
			foreach (Assembly item in GameAsms())
			{
				Type[] types;
				try
				{
					types = item.GetTypes();
				}
				catch
				{
					continue;
				}
				value = types.FirstOrDefault((Type x) => x.Name == name && typeof(Component).IsAssignableFrom(x));
				if (value != null)
				{
					break;
				}
			}
			_avoidTypes[name] = value;
			return value;
		}

		private static List<Component> Live(Type t)
		{
			List<Component> list = new List<Component>();
			try
			{
				foreach (Object item in (Il2CppArrayBase<Object>)(object)Object.FindObjectsOfType(Il2CppType.From(t)))
				{
					if (!(item == (Object)null))
					{
						object? obj = Activator.CreateInstance(t, ((Il2CppObjectBase)item).Pointer);
						Component val = (Component)((obj is Component) ? obj : null);
						if ((Object)(object)val != (Object)null && val.gameObject.activeInHierarchy)
						{
							list.Add(val);
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static string HierPath(Transform t)
		{
			List<string> list = new List<string>();
			int num = 0;
			while ((Object)(object)t != (Object)null && num < 6)
			{
				list.Add(t.GetSiblingIndex().ToString());
				num++;
				t = t.parent;
			}
			return string.Join("/", list);
		}

		private void Rescan()
		{
			List<(Type, List<Component>)> list = new List<(Type, List<Component>)>();
			foreach (Type item2 in Candidates())
			{
				List<Component> list2 = Live(item2);
				if (list2.Count > 0)
				{
					list.Add((item2, list2));
				}
			}
			string text = string.Join(", ", list.Select<(Type, List<Component>), string>(((Type t, List<Component> list) f) => f.t.Name + "x" + f.list.Count));
			if (text != _lastSig)
			{
				_lastSig = text;
				((MelonBase)this).LoggerInstance.Msg("Slot-like UI on screen: " + ((text.Length == 0) ? "(none)" : text));
			}
			IEnumerable<(Type, List<Component>)> enumerable = list;
			if (_pinType.Value.Trim().Length == 0)
			{
				List<(Type, List<Component>)> list3 = list.Where<(Type, List<Component>)>(((Type t, List<Component> list) f) => Pref.Any((string p) => f.t.Name.ToLowerInvariant().Contains(p))).ToList();
				enumerable = ((list3.Count > 0) ? list3 : list.Where<(Type, List<Component>)>(((Type t, List<Component> list) f) => f.list.Count >= 2).ToList());
			}
			List<(Component, string)> list4 = new List<(Component, string)>();
			foreach (var item3 in enumerable)
			{
				foreach (Component item4 in item3.Item2)
				{
					string text2 = null;
					string[] keyFields = KeyFields;
					foreach (string text3 in keyFields)
					{
						object obj = Get(item4, text3);
						if (obj != null && obj.ToString().Length > 0 && obj.ToString().Length < 200 && !(obj is Object))
						{
							text2 = text3 + "=" + obj;
							break;
						}
					}
					list4.Add((item4, item3.Item1.Name + "|" + text2));
				}
			}
			List<Slot> list5 = new List<Slot>();
			foreach (var item5 in list4)
			{
				Component item = item5.Item1;
				string key = item5.Item2;
				RectTransform component = item.GetComponent<RectTransform>();
				if (!((Object)(object)component == (Object)null))
				{
					string key2 = ((list4.Count<(Component, string)>(((Component c, string key) b) => b.key == key) > 1 || key.EndsWith("|")) ? (key + "#" + HierPath(item.transform)) : key);
					list5.Add(new Slot
					{
						Comp = item,
						Rt = component,
						Key = key2
					});
				}
			}
			_slots.Clear();
			_slots.AddRange(list5);
			_avoid.Clear();
			foreach (string item6 in from x in _avoidType.Value.Split(',')
				select x.Trim() into x
				where x.Length > 0
				select x)
			{
				Type type = AvoidType(item6);
				if (type == null)
				{
					continue;
				}
				foreach (Component item7 in Live(type))
				{
					RectTransform component2 = item7.GetComponent<RectTransform>();
					if ((Object)(object)component2 != (Object)null)
					{
						_avoid.Add(new Slot
						{
							Comp = item7,
							Rt = component2,
							Key = ""
						});
					}
				}
			}
		}

		public override void OnUpdate()
		{
			_scanT -= Time.unscaledDeltaTime;
			if (_scanT <= 0f)
			{
				_scanT = 0.5f;
				try
				{
					Rescan();
				}
				catch (Exception ex)
				{
					((MelonBase)this).LoggerInstance.Warning("Scan: " + ex.Message);
				}
			}
			if (_dumpAt > 0f && Time.unscaledTime >= _dumpAt)
			{
				_dumpAt = -1f;
				try
				{
					Dump();
				}
				catch (Exception ex2)
				{
					((MelonBase)this).LoggerInstance.Warning("Dump: " + ex2.Message);
				}
			}
			if (_legacyBad)
			{
				return;
			}
			try
			{
				if (Input.GetKeyDown((KeyCode)289))
				{
					Dump();
				}
			}
			catch
			{
				_legacyBad = true;
			}
		}

		private void Dump()
		{
			((MelonBase)this).LoggerInstance.Msg("---- slot dump ----");
			foreach (Assembly item in GameAsms())
			{
				Type[] types;
				try
				{
					types = item.GetTypes();
				}
				catch
				{
					continue;
				}
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type.Name == null || !typeof(Component).IsAssignableFrom(type))
					{
						continue;
					}
					string text = type.Name.ToLowerInvariant();
					if (!text.Contains("slot") && !text.Contains("save") && !text.Contains("load"))
					{
						continue;
					}
					List<Component> list = Live(type);
					if (list.Count != 0)
					{
						IEnumerable<string> values = (from m in type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
							where m is FieldInfo || m is PropertyInfo
							select m.Name into n
							where !n.StartsWith("NativeFieldInfoPtr") && !n.StartsWith("NativeMethodInfoPtr")
							select n).Take(40);
						((MelonBase)this).LoggerInstance.Msg(type.Name + " x" + list.Count + " [" + string.Join(", ", values) + "]");
					}
				}
			}
			try
			{
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				foreach (Object item2 in (Il2CppArrayBase<Object>)(object)Object.FindObjectsOfType(Il2CppType.Of<MonoBehaviour>()))
				{
					if (!(item2 == (Object)null))
					{
						string text2 = null;
						try
						{
							text2 = Marshal.PtrToStringAnsi(IL2CPP.il2cpp_class_get_name(IL2CPP.il2cpp_object_get_class(((Il2CppObjectBase)item2).Pointer)));
						}
						catch
						{
						}
						if (!string.IsNullOrEmpty(text2))
						{
							dictionary.TryGetValue(text2, out var value);
							dictionary[text2] = value + 1;
						}
					}
				}
				((MelonBase)this).LoggerInstance.Msg("Active behaviours: " + string.Join(", ", from kv in dictionary.OrderBy((KeyValuePair<string, int> kv) => kv.Key).Take(250)
					select kv.Key + "x" + kv.Value));
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("Behaviour list: " + ex.Message);
			}
			((MelonBase)this).LoggerInstance.Msg("-------------------");
		}

		private bool ScreenRect(Slot s, out Rect r)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Invalid comparison between Unknown and I4
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: 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)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			r = default(Rect);
			try
			{
				if ((Object)(object)s.Comp == (Object)null || (Object)(object)s.Rt == (Object)null || !s.Comp.gameObject.activeInHierarchy)
				{
					return false;
				}
				s.Rt.GetWorldCorners(_corners);
				Camera val = null;
				Canvas val2 = ((Component)s.Rt).GetComponentInParent<Canvas>();
				if ((Object)(object)val2 != (Object)null)
				{
					val2 = val2.rootCanvas;
				}
				bool flag = (Object)(object)val2 == (Object)null || (int)val2.renderMode == 0;
				if (!flag)
				{
					val = (((Object)(object)val2.worldCamera != (Object)null) ? val2.worldCamera : Camera.main);
				}
				float num = float.MaxValue;
				float num2 = float.MaxValue;
				float num3 = float.MinValue;
				float num4 = float.MinValue;
				for (int i = 0; i < 4; i++)
				{
					Vector3 val3 = ((Il2CppArrayBase<Vector3>)(object)_corners)[i];
					if (!flag && (Object)(object)val != (Object)null)
					{
						val3 = val.WorldToScreenPoint(val3);
					}
					num = Math.Min(num, val3.x);
					num3 = Math.Max(num3, val3.x);
					num2 = Math.Min(num2, val3.y);
					num4 = Math.Max(num4, val3.y);
				}
				r = new Rect(num, (float)Screen.height - num4, num3 - num, num4 - num2);
				return ((Rect)(ref r)).width > 20f && ((Rect)(ref r)).height > 10f && ((Rect)(ref r)).xMax > 0f && ((Rect)(ref r)).x < (float)Screen.width && ((Rect)(ref r)).yMax > 0f && ((Rect)(ref r)).y < (float)Screen.height;
			}
			catch
			{
				return false;
			}
		}

		public override void OnGUI()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0031: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Invalid comparison between Unknown and I4
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Invalid comparison between Unknown and I4
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Invalid comparison between Unknown and I4
			if (_slots.Count == 0)
			{
				return;
			}
			Color color = GUI.color;
			if (_tf == null)
			{
				_tf = new GUIStyle(GUI.skin.textField);
				_tf.fontSize = 22;
				_tf.alignment = (TextAnchor)3;
				_hint = new GUIStyle(GUI.skin.label);
				_hint.fontSize = 20;
				_hint.alignment = (TextAnchor)3;
			}
			float num = 0f;
			for (int i = 0; i < _slots.Count; i++)
			{
				if (!ScreenRect(_slots[i], out var r))
				{
					continue;
				}
				float xMax = ((Rect)(ref r)).xMax;
				float y = ((Rect)(ref r)).center.y;
				foreach (Slot item in _avoid)
				{
					if (ScreenRect(item, out var r2) && y >= ((Rect)(ref r2)).y - 4f && y <= ((Rect)(ref r2)).yMax + 4f && ((Rect)(ref r2)).xMax > xMax)
					{
						xMax = ((Rect)(ref r2)).xMax;
					}
				}
				num = Math.Max(num, xMax);
			}
			Rect val = default(Rect);
			for (int j = 0; j < _slots.Count; j++)
			{
				Slot slot = _slots[j];
				if (!ScreenRect(slot, out var r3))
				{
					continue;
				}
				float value = _boxW.Value;
				float value2 = _boxH.Value;
				((Rect)(ref val))..ctor(Math.Min(num + _offX.Value, (float)Screen.width - value - 4f), ((Rect)(ref r3)).y + (((Rect)(ref r3)).height - value2) * 0.5f + _offY.Value, value, value2);
				string text = "ssn_" + j;
				bool flag = GUI.GetNameOfFocusedControl() == text;
				string text2 = NameOf(slot.Key);
				Event current2 = Event.current;
				if (flag && _editKey == slot.Key && current2 != null && (int)current2.type == 4 && ((int)current2.keyCode == 13 || (int)current2.keyCode == 271))
				{
					Commit();
					GUI.FocusControl("");
					current2.Use();
					continue;
				}
				GUI.SetNextControlName(text);
				if (flag)
				{
					if (_editKey != slot.Key)
					{
						Commit();
						_editKey = slot.Key;
						_editText = text2;
					}
					_editText = GUI.TextField(val, _editText ?? "", 40, _tf);
					continue;
				}
				if (_editKey == slot.Key)
				{
					Commit();
				}
				GUI.TextField(val, text2, 40, _tf);
				if (text2.Length == 0)
				{
					GUI.color = new Color(1f, 1f, 1f, 0.45f);
					GUI.Label(new Rect(((Rect)(ref val)).x + 6f, ((Rect)(ref val)).y, ((Rect)(ref val)).width, ((Rect)(ref val)).height), "name this slot...", _hint);
					GUI.color = color;
				}
			}
			GUI.color = color;
		}
	}
}