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;
}
}
}