451 lines
15 KiB
C#
Raw Normal View History

2020-08-07 22:19:03 +10:00
using System;
using System.Collections;
2020-08-07 22:19:03 +10:00
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Reflection;
using MelonLoader;
namespace Explorer
{
public class SearchPage : WindowPage
2020-08-07 22:19:03 +10:00
{
public static SearchPage Instance;
public override string Name { get => "Object Search"; }
2020-08-07 22:19:03 +10:00
private string m_searchInput = "";
private string m_typeInput = "";
private Vector2 resultsScroll = Vector2.zero;
2020-08-07 22:19:03 +10:00
public PageHelper Pages = new PageHelper();
private List<CacheObjectBase> m_searchResults = new List<CacheObjectBase>();
2020-08-07 22:19:03 +10:00
public SceneFilter SceneMode = SceneFilter.Any;
public TypeFilter TypeMode = TypeFilter.Object;
public enum SceneFilter
{
Any,
This,
DontDestroy,
None
}
public enum TypeFilter
{
Object,
GameObject,
Component,
Custom
}
public override void Init()
{
Instance = this;
}
public void OnSceneChange()
{
m_searchResults.Clear();
Pages.PageOffset = 0;
2020-08-07 22:19:03 +10:00
}
public override void Update()
{
}
private void CacheResults(IEnumerable results)
{
m_searchResults = new List<CacheObjectBase>();
foreach (var obj in results)
{
var toCache = obj;
if (toCache is Il2CppSystem.Object ilObject)
{
toCache = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Transform>()?.gameObject ?? ilObject;
}
var cache = CacheObjectBase.GetCacheObject(toCache);
m_searchResults.Add(cache);
}
Pages.ItemCount = m_searchResults.Count;
Pages.PageOffset = 0;
}
2020-08-07 22:19:03 +10:00
public override void DrawWindow()
{
try
{
// helpers
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b><color=orange>Helpers</color></b>", new GUILayoutOption[] { GUILayout.Width(70) });
if (GUILayout.Button("Find Static Instances", new GUILayoutOption[] { GUILayout.Width(180) }))
{
//m_searchResults = GetInstanceClassScanner().ToList();
CacheResults(GetInstanceClassScanner());
2020-08-07 22:19:03 +10:00
}
GUILayout.EndHorizontal();
// search box
SearchBox();
// results
GUILayout.BeginVertical(GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Results </color></b>" + " (" + m_searchResults.Count + ")", null);
2020-08-07 22:19:03 +10:00
GUI.skin.label.alignment = TextAnchor.UpperLeft;
int count = m_searchResults.Count;
GUILayout.BeginHorizontal(null);
Pages.DrawLimitInputArea();
if (count > Pages.ItemsPerPage)
{
// prev/next page buttons
if (Pages.ItemCount > Pages.ItemsPerPage)
{
if (GUILayout.Button("< Prev", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Left, ref this.resultsScroll);
}
Pages.CurrentPageLabel();
if (GUILayout.Button("Next >", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Pages.TurnPage(Turn.Right, ref this.resultsScroll);
}
}
}
GUILayout.EndHorizontal();
resultsScroll = GUIUnstrip.BeginScrollView(resultsScroll);
2020-08-07 22:19:03 +10:00
var _temprect = new Rect(MainMenu.MainRect.x, MainMenu.MainRect.y, MainMenu.MainRect.width + 160, MainMenu.MainRect.height);
if (m_searchResults.Count > 0)
{
int offset = Pages.CalculateOffsetIndex();
for (int i = offset; i < offset + Pages.ItemsPerPage && i < count; i++)
2020-08-07 22:19:03 +10:00
{
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
2020-08-07 22:19:03 +10:00
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
}
GUIUnstrip.EndScrollView();
2020-08-07 22:19:03 +10:00
GUILayout.EndVertical();
}
catch
{
m_searchResults.Clear();
}
}
private void SearchBox()
{
GUILayout.BeginVertical(GUI.skin.box, null);
// ----- GameObject Search -----
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label("<b><color=orange>Search</color></b>", null);
GUI.skin.label.alignment = TextAnchor.UpperLeft;
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name Contains:", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, new GUILayoutOption[] { GUILayout.Width(200) });
//GUI.skin.label.alignment = TextAnchor.MiddleRight;
//GUILayout.Label("Results per page:", new GUILayoutOption[] { GUILayout.Width(120) });
//var resultinput = m_limit.ToString();
//resultinput = GUILayout.TextField(resultinput, new GUILayoutOption[] { GUILayout.Width(55) });
//if (int.TryParse(resultinput, out int _i) && _i > 0)
//{
// m_limit = _i;
//}
//GUI.skin.label.alignment = TextAnchor.UpperLeft;
2020-08-07 22:19:03 +10:00
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Class Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
ClassFilterToggle(TypeFilter.Object, "Object");
ClassFilterToggle(TypeFilter.GameObject, "GameObject");
ClassFilterToggle(TypeFilter.Component, "Component");
ClassFilterToggle(TypeFilter.Custom, "Custom");
GUILayout.EndHorizontal();
if (TypeMode == TypeFilter.Custom)
{
GUILayout.BeginHorizontal(null);
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Custom Class:", new GUILayoutOption[] { GUILayout.Width(250) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
m_typeInput = GUILayout.TextField(m_typeInput, new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene Filter:", new GUILayoutOption[] { GUILayout.Width(100) });
SceneFilterToggle(SceneFilter.Any, "Any", 60);
SceneFilterToggle(SceneFilter.This, "This Scene", 100);
SceneFilterToggle(SceneFilter.DontDestroy, "DontDestroyOnLoad", 140);
SceneFilterToggle(SceneFilter.None, "No Scene", 80);
GUILayout.EndHorizontal();
if (GUILayout.Button("<b><color=cyan>Search</color></b>", null))
{
Search();
}
GUILayout.EndVertical();
}
private void ClassFilterToggle(TypeFilter mode, string label)
{
if (TypeMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(100) }))
{
TypeMode = mode;
}
GUI.color = Color.white;
}
private void SceneFilterToggle(SceneFilter mode, string label, float width)
{
if (SceneMode == mode)
{
GUI.color = Color.green;
}
else
{
GUI.color = Color.white;
}
if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width) }))
{
SceneMode = mode;
}
GUI.color = Color.white;
}
// -------------- ACTUAL METHODS (not Gui draw) ----------------- //
2020-08-07 22:19:03 +10:00
// ======= search functions =======
private void Search()
{
Pages.PageOffset = 0;
CacheResults(FindAllObjectsOfType(m_searchInput, m_typeInput));
2020-08-07 22:19:03 +10:00
}
private List<object> FindAllObjectsOfType(string _search, string _type)
{
Il2CppSystem.Type searchType = null;
2020-08-07 22:19:03 +10:00
if (TypeMode == TypeFilter.Custom)
{
try
{
var findType = ReflectionHelpers.GetTypeByName(_type);
searchType = Il2CppSystem.Type.GetType(findType.AssemblyQualifiedName);
//MelonLogger.Log("Search type: " + findType.AssemblyQualifiedName);
2020-08-07 22:19:03 +10:00
}
catch (Exception e)
{
MelonLogger.Log("Exception: " + e.GetType() + ", " + e.Message + "\r\n" + e.StackTrace);
}
}
else if (TypeMode == TypeFilter.Object)
{
searchType = ReflectionHelpers.ObjectType;
2020-08-07 22:19:03 +10:00
}
else if (TypeMode == TypeFilter.GameObject)
{
searchType = ReflectionHelpers.GameObjectType;
2020-08-07 22:19:03 +10:00
}
else if (TypeMode == TypeFilter.Component)
{
searchType = ReflectionHelpers.ComponentType;
2020-08-07 22:19:03 +10:00
}
if (!ReflectionHelpers.ObjectType.IsAssignableFrom(searchType))
2020-08-07 22:19:03 +10:00
{
MelonLogger.LogError("Your Custom Class Type must inherit from UnityEngine.Object!");
2020-08-07 22:19:03 +10:00
return new List<object>();
}
var matches = new List<object>();
var allObjectsOfType = Resources.FindObjectsOfTypeAll(searchType);
//MelonLogger.Log("Found count: " + allObjectsOfType.Length);
int i = 0;
foreach (var obj in allObjectsOfType)
2020-08-07 22:19:03 +10:00
{
if (i >= 2000) break;
2020-08-07 22:19:03 +10:00
if (_search != "" && !obj.name.ToLower().Contains(_search.ToLower()))
{
continue;
}
if (searchType.FullName == ReflectionHelpers.ComponentType.FullName
&& ReflectionHelpers.TransformType.IsAssignableFrom(obj.GetIl2CppType()))
{
// Transforms shouldn't really be counted as Components, skip them.
// They're more akin to GameObjects.
continue;
}
if (SceneMode != SceneFilter.Any && !FilterScene(obj, this.SceneMode))
2020-08-07 22:19:03 +10:00
{
continue;
2020-08-07 22:19:03 +10:00
}
if (!matches.Contains(obj))
{
matches.Add(obj);
}
i++;
2020-08-07 22:19:03 +10:00
}
return matches;
}
public static bool FilterScene(object obj, SceneFilter filter)
2020-08-07 22:19:03 +10:00
{
GameObject go;
if (obj is Il2CppSystem.Object ilObject)
2020-08-07 22:19:03 +10:00
{
go = ilObject.TryCast<GameObject>() ?? ilObject.TryCast<Component>().gameObject;
2020-08-07 22:19:03 +10:00
}
else
2020-08-07 22:19:03 +10:00
{
go = (obj as GameObject) ?? (obj as Component).gameObject;
}
2020-08-07 22:19:03 +10:00
if (!go)
{
// object is not on a GameObject, cannot perform scene filter operation.
return false;
2020-08-07 22:19:03 +10:00
}
if (filter == SceneFilter.None)
{
return string.IsNullOrEmpty(go.scene.name);
}
else if (filter == SceneFilter.This)
{
return go.scene.name == UnityHelpers.ActiveSceneName;
}
else if (filter == SceneFilter.DontDestroy)
{
return go.scene.name == "DontDestroyOnLoad";
}
2020-08-07 22:19:03 +10:00
return false;
}
// ====== other ========
private static bool FilterName(string name)
{
// Don't really want these instances.
return !name.StartsWith("Mono")
&& !name.StartsWith("System")
&& !name.StartsWith("Il2CppSystem")
&& !name.StartsWith("Iced");
}
// credit: ManlyMarco (RuntimeUnityEditor)
public static IEnumerable<object> GetInstanceClassScanner()
2020-08-07 22:19:03 +10:00
{
var query = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(GetTypesSafe)
.Where(t => t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters);
2020-08-07 22:19:03 +10:00
var flags = BindingFlags.Public | BindingFlags.Static;
var flatFlags = flags | BindingFlags.FlattenHierarchy;
foreach (var type in query)
{
object obj = null;
try
2020-08-07 22:19:03 +10:00
{
var pi = type.GetProperty("Instance", flags);
if (pi == null)
{
pi = type.GetProperty("Instance", flatFlags);
}
if (pi != null)
{
obj = pi.GetValue(null);
}
else
{
var fi = type.GetField("Instance", flags);
if (fi == null)
{
fi = type.GetField("Instance", flatFlags);
}
if (fi != null)
{
obj = fi.GetValue(null);
}
}
2020-08-07 22:19:03 +10:00
}
catch { }
if (obj != null)
2020-08-07 22:19:03 +10:00
{
var t = ReflectionHelpers.GetActualType(obj);
if (!FilterName(t.FullName) || ReflectionHelpers.IsArray(t) || ReflectionHelpers.IsList(t))
{
continue;
}
yield return obj;
2020-08-07 22:19:03 +10:00
}
}
}
public static IEnumerable<Type> GetTypesSafe(Assembly asm)
{
try { return asm.GetTypes(); }
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
catch { return Enumerable.Empty<Type>(); }
2020-08-07 22:19:03 +10:00
}
}
}