using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorerTreeSnapShooter.Models;
using UnityExplorerTreeSnapShooter.Services;
using UnityExplorerTreeSnapShooter.Services.Implementation;
using UnityExplorerTreeSnapShooter.UI;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Panels;
using UniverseLib.UI.Widgets;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("UnityExplorerTreeSnapshooter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UnityExplorerTreeSnapshooter")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c5450fe0-edcf-483f-b9ea-4b1ef9d36da7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace UnityExplorerTreeSnapShooter
{
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("gymmed.unity_tree_snap_shooter", "Unity Tree Snap Shooter", "0.0.1")]
public class UnityExplorerTreeSnapShooter : BaseUnityPlugin
{
public const string GUID = "gymmed.unity_tree_snap_shooter";
public const string NAME = "Unity Tree Snap Shooter";
public const string VERSION = "0.0.1";
public static readonly string Prefix = "[Unity-Tree-Snap-Shooter]";
public const string EVENTS_LISTENER_GUID = "gymmed.unity_tree_snap_shooter_*";
internal static ManualLogSource Log;
internal void Awake()
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
LogMessage("Hello world from Unity Tree Snap Shooter 0.0.1!");
new Harmony("gymmed.unity_tree_snap_shooter").PatchAll();
}
internal void Update()
{
}
public static void LogMessage(string message)
{
Log.LogMessage((object)(Prefix + " " + message));
}
public static string GetProjectLocation()
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
}
}
namespace UnityExplorerTreeSnapShooter.UI
{
public class SnapShooterPanel : PanelBase
{
public static readonly string UIBaseGuid = "gymmed.unity_tree_snap_shooter.SnapShooterInspector";
private Text _logText;
private ISnapShooterService _snapShooterService;
public static SnapShooterPanel Instance { get; private set; }
public static UIBase UIBaseRegistry { get; set; }
public override string Name => "Tree Snap Shooter";
public override int MinWidth => 420;
public override int MinHeight => 350;
public override Vector2 DefaultAnchorMin => new Vector2(0.5f, 0.5f);
public override Vector2 DefaultAnchorMax => new Vector2(0.5f, 0.5f);
public InputFieldRef SavePath { get; private set; }
public Text SelectionText { get; private set; }
public override bool CanDragAndResize => true;
public SnapShooterPanel(UIBase owner)
: base(owner)
{
Instance = this;
}
protected override void ConstructPanelContent()
{
//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_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
//IL_0273: Unknown result type (might be due to invalid IL or missing references)
if (_snapShooterService == null)
{
FileSnapshotSaver saver = new FileSnapshotSaver(PathsManager.Instance);
_snapShooterService = new TreeSnapShooterService(saver);
}
SelectionText = UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "Header", GetSelectionMessage(), (TextAnchor)4, default(Color), true, 14);
UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "SaveLabel", "Save Path:", (TextAnchor)3, default(Color), true, 14);
SavePath = UIFactory.CreateInputField(((PanelBase)this).ContentRoot, "SavePathField", "Save to path...");
GameObject gameObject = ((Component)SavePath.Component).gameObject;
int? num = 25;
int? num2 = 9999;
UIFactory.SetLayoutElement(gameObject, (int?)null, num, num2, (int?)null, (int?)null, (int?)null, (bool?)null);
SavePath.Text = GetDefaultSavePath();
ButtonRef obj = UIFactory.CreateButton(((PanelBase)this).ContentRoot, "SnapshotBtn", "Snapshot Selection", (Color?)new Color(0.2f, 0.2f, 0.35f));
GameObject gameObject2 = ((Component)obj.Component).gameObject;
num2 = 30;
num = 9999;
UIFactory.SetLayoutElement(gameObject2, (int?)null, num2, num, (int?)null, (int?)null, (int?)null, (bool?)null);
obj.OnClick = (Action)Delegate.Combine(obj.OnClick, new Action(OnSnapShoot));
UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "LogLabel", "Output:", (TextAnchor)3, default(Color), true, 14);
GameObject val = default(GameObject);
AutoSliderScrollbar val2 = default(AutoSliderScrollbar);
GameObject obj2 = UIFactory.CreateScrollView(((PanelBase)this).ContentRoot, "LogScroll", ref val, ref val2, new Color(0.2f, 0.2f, 0.2f));
num = 9999;
num2 = 120;
UIFactory.SetLayoutElement(obj2, (int?)null, num2, (int?)null, num, (int?)null, (int?)null, (bool?)null);
_logText = val.AddComponent<Text>();
_logText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
_logText.fontSize = 12;
((Graphic)_logText).color = Color.white;
_logText.supportRichText = true;
_logText.horizontalOverflow = (HorizontalWrapMode)0;
_logText.verticalOverflow = (VerticalWrapMode)1;
_logText.text = "Ready...";
InspectorManager.OnInspectedTabsChanged += OnInspectorChanged;
}
public string GetDefaultSavePath()
{
string selectionName = GetSelectionName();
string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string path = "TreeSnapShooter_" + selectionName + "_" + text + ".txt";
return Path.Combine(_snapShooterService.GetDefaultDirectory(), path);
}
private void OnInspectorChanged()
{
if (((UIModel)this).Enabled)
{
UpdateSelection();
}
}
public override void SetActive(bool active)
{
((PanelBase)this).SetActive(active);
if (active)
{
UpdateSelection();
}
}
protected override void OnClosePanelClicked()
{
VisibilityManager.Instance.Toggle();
}
public void UpdateSelection()
{
if (!((Object)(object)SelectionText == (Object)null))
{
SelectionText.text = GetSelectionMessage();
InputFieldRef savePath = SavePath;
if ((Object)(object)((savePath != null) ? savePath.Component : null) != (Object)null)
{
SavePath.Text = GetDefaultSavePath();
}
}
}
public string GetSelectionMessage()
{
return "<b>Selection: " + GetSelectionName() + "</b>";
}
public string GetSelectionName()
{
GameObject inspectedGameObject = GetInspectedGameObject();
if ((Object)(object)inspectedGameObject != (Object)null)
{
return ((Object)inspectedGameObject).name;
}
return "None";
}
private void OnSnapShoot()
{
if (SavePath == null || (Object)(object)SavePath.Component == (Object)null)
{
Log("ERROR: SavePath is null!");
return;
}
string text = SavePath.Component.text;
GameObject inspectedGameObject = GetInspectedGameObject();
if ((Object)(object)inspectedGameObject != (Object)null)
{
try
{
string content = _snapShooterService.ExecuteSnapShoot(inspectedGameObject);
string text2 = _snapShooterService.SaveSnapshot(content, ((Object)inspectedGameObject).name, text);
Log("Successfully created snapshot: " + text2);
return;
}
catch (Exception ex)
{
Log("Snapshot failed: " + ex.Message);
return;
}
}
Log("Target is not a GameObject!");
}
private void Log(string msg)
{
if ((Object)(object)_logText != (Object)null)
{
_logText.text = msg + "\n" + _logText.text;
}
}
private GameObject GetInspectedGameObject()
{
InspectorBase activeInspector = InspectorManager.ActiveInspector;
if (activeInspector != null)
{
return TryGetGameObjectFromInspector(activeInspector);
}
if (InspectorManager.Inspectors != null)
{
for (int i = 0; i < InspectorManager.Inspectors.Count; i++)
{
InspectorBase inspector = InspectorManager.Inspectors[i];
GameObject val = TryGetGameObjectFromInspector(inspector);
if ((Object)(object)val != (Object)null)
{
return val;
}
}
}
return null;
}
private GameObject TryGetGameObjectFromInspector(InspectorBase inspector)
{
if (inspector == null)
{
return null;
}
object target = inspector.Target;
if (target == null || target.ToString() == "null")
{
return null;
}
GameObject val = (GameObject)((target is GameObject) ? target : null);
if (val != null)
{
return val;
}
Component val2 = (Component)((target is Component) ? target : null);
if (val2 != null && (Object)(object)val2.gameObject != (Object)null)
{
return val2.gameObject;
}
return null;
}
public override void Destroy()
{
((PanelBase)this).Destroy();
InspectorManager.OnInspectedTabsChanged -= OnInspectorChanged;
}
}
}
namespace UnityExplorerTreeSnapShooter.Services
{
public interface IComponentStateReader
{
List<ComponentSnapshot> ReadComponents(GameObject gameObject, IReferenceTracker tracker);
List<GameObjectSnapshot> ReadAllGameObjects(List<SnapshotNode> hierarchyNodes, int selectedInstanceId, IReferenceTracker tracker);
}
public interface IHierarchyBuilder
{
void BuildHierarchy(Transform root, IReferenceTracker tracker);
List<SnapshotNode> GetHierarchyNodes();
List<SnapshotNode> BuildParentChain(Transform transform, IReferenceTracker tracker);
}
public interface IPathsManager
{
string GetSnapshotsDirectory();
}
public interface IReferenceTracker
{
void AddToSelection(int instanceId);
bool IsInSelection(int instanceId);
bool IsInSelection(Object obj);
void Clear();
}
public interface ISnapShooterService
{
string ExecuteSnapShoot(GameObject root);
string SaveSnapshot(string content, string gameObjectName);
string SaveSnapshot(string content, string gameObjectName, string userProvidedPath);
string GetDefaultDirectory();
}
public interface ISnapshotFormatter
{
string Format(SnapshotResult snapshot);
}
public interface ISnapshotSaver
{
string Save(string content, string gameObjectName);
string Save(string content, string gameObjectName, string userProvidedPath);
string GetDefaultDirectory();
}
public interface IVisibilityManager
{
bool IsVisible { get; }
void Initialize(UIBase uiBase);
void Toggle();
void SetVisible(bool visible);
}
public class TreeSnapShooterService : ISnapShooterService
{
private readonly IReferenceTracker _referenceTracker;
private readonly IHierarchyBuilder _hierarchyBuilder;
private readonly IComponentStateReader _componentReader;
private readonly ISnapshotFormatter _formatter;
private readonly ISnapshotSaver _saver;
public TreeSnapShooterService(ISnapshotSaver saver)
{
_referenceTracker = new ReferenceTracker();
_hierarchyBuilder = new HierarchyBuilder();
_componentReader = new ComponentStateReader(_referenceTracker);
_formatter = new PlainTextSnapshotFormatter();
_saver = saver;
}
public string ExecuteSnapShoot(GameObject root)
{
_referenceTracker.Clear();
SnapshotResult snapshotResult = new SnapshotResult
{
RootName = ((Object)root).name,
RootInstanceId = ((Object)root).GetInstanceID(),
GeneratedAt = DateTime.Now
};
_hierarchyBuilder.BuildHierarchy(root.transform, _referenceTracker);
snapshotResult.Hierarchy = _hierarchyBuilder.GetHierarchyNodes();
snapshotResult.ParentChain = _hierarchyBuilder.BuildParentChain(root.transform, _referenceTracker);
snapshotResult.Components = _componentReader.ReadComponents(root, _referenceTracker);
snapshotResult.GameObjectSnapshots = _componentReader.ReadAllGameObjects(snapshotResult.Hierarchy, ((Object)root).GetInstanceID(), _referenceTracker);
return _formatter.Format(snapshotResult);
}
public string SaveSnapshot(string content, string gameObjectName)
{
return _saver.Save(content, gameObjectName);
}
public string SaveSnapshot(string content, string gameObjectName, string userProvidedPath)
{
return _saver.Save(content, gameObjectName, userProvidedPath);
}
public string GetDefaultDirectory()
{
return _saver.GetDefaultDirectory();
}
}
}
namespace UnityExplorerTreeSnapShooter.Services.Implementation
{
public class ComponentStateReader : IComponentStateReader
{
private readonly IReferenceTracker _tracker;
public ComponentStateReader(IReferenceTracker tracker)
{
_tracker = tracker;
}
public List<ComponentSnapshot> ReadComponents(GameObject gameObject, IReferenceTracker tracker)
{
List<ComponentSnapshot> list = new List<ComponentSnapshot>();
Component[] components = gameObject.GetComponents<Component>();
foreach (Component val in components)
{
if (!((Object)(object)val == (Object)null))
{
ComponentSnapshot item = new ComponentSnapshot
{
TypeName = ((object)val).GetType().Name,
InstanceId = ((Object)val).GetInstanceID(),
Members = ReadMembers(val, tracker)
};
list.Add(item);
}
}
return list;
}
public List<GameObjectSnapshot> ReadAllGameObjects(List<SnapshotNode> hierarchyNodes, int selectedInstanceId, IReferenceTracker tracker)
{
List<GameObjectSnapshot> list = new List<GameObjectSnapshot>();
HashSet<int> hashSet = new HashSet<int>();
foreach (SnapshotNode hierarchyNode in hierarchyNodes)
{
GameObject gameObjectByInstanceId = GetGameObjectByInstanceId(hierarchyNode.InstanceId);
if ((Object)(object)gameObjectByInstanceId == (Object)null)
{
continue;
}
bool isSelected = hierarchyNode.InstanceId == selectedInstanceId;
GameObjectSnapshot gameObjectSnapshot = new GameObjectSnapshot
{
Name = ((Object)gameObjectByInstanceId).name,
InstanceId = hierarchyNode.InstanceId,
IsSelected = isSelected,
Components = new List<ComponentSnapshot>()
};
if (hashSet.Contains(hierarchyNode.InstanceId))
{
gameObjectSnapshot.IsDuplicate = true;
list.Add(gameObjectSnapshot);
continue;
}
hashSet.Add(hierarchyNode.InstanceId);
Component[] components = gameObjectByInstanceId.GetComponents<Component>();
foreach (Component val in components)
{
if (!((Object)(object)val == (Object)null))
{
int instanceID = ((Object)val).GetInstanceID();
ComponentSnapshot item = new ComponentSnapshot
{
TypeName = ((object)val).GetType().Name,
InstanceId = instanceID,
Members = ReadMembers(val, tracker)
};
gameObjectSnapshot.Components.Add(item);
}
}
list.Add(gameObjectSnapshot);
}
return list;
}
private GameObject GetGameObjectByInstanceId(int instanceId)
{
GameObject[] array = Object.FindObjectsOfType<GameObject>();
foreach (GameObject val in array)
{
if (((Object)val).GetInstanceID() == instanceId)
{
return val;
}
}
return null;
}
private List<MemberSnapshot> ReadMembers(Component component, IReferenceTracker tracker)
{
List<MemberSnapshot> list = new List<MemberSnapshot>();
Type type = ((object)component).GetType();
BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
FieldInfo[] fields = type.GetFields(bindingAttr);
foreach (FieldInfo fieldInfo in fields)
{
try
{
object value = fieldInfo.GetValue(component);
MemberSnapshot item = CreateMemberSnapshot("field", fieldInfo.Name, value, fieldInfo.FieldType, tracker);
list.Add(item);
}
catch
{
list.Add(new MemberSnapshot
{
MemberType = "field",
MemberName = fieldInfo.Name,
FormattedValue = "[Error reading value]"
});
}
}
PropertyInfo[] properties = type.GetProperties(bindingAttr);
foreach (PropertyInfo propertyInfo in properties)
{
try
{
if (propertyInfo.GetIndexParameters().Length == 0)
{
object value2 = propertyInfo.GetValue(component, null);
MemberSnapshot item2 = CreateMemberSnapshot("property", propertyInfo.Name, value2, propertyInfo.PropertyType, tracker);
list.Add(item2);
}
}
catch
{
list.Add(new MemberSnapshot
{
MemberType = "property",
MemberName = propertyInfo.Name,
FormattedValue = "[Error reading value]"
});
}
}
return list;
}
private MemberSnapshot CreateMemberSnapshot(string memberType, string memberName, object value, Type valueType, IReferenceTracker tracker)
{
MemberSnapshot memberSnapshot = new MemberSnapshot
{
MemberType = memberType,
MemberName = memberName,
ValueType = (valueType?.Name ?? "null")
};
if (value == null)
{
memberSnapshot.FormattedValue = "null";
return memberSnapshot;
}
Object val = (Object)((value is Object) ? value : null);
if (val != null)
{
if (val == (Object)null)
{
memberSnapshot.FormattedValue = "null";
return memberSnapshot;
}
int instanceID = val.GetInstanceID();
memberSnapshot.ReferencedInstanceId = instanceID;
if (tracker.IsInSelection(instanceID))
{
memberSnapshot.FormattedValue = $"[InstanceID: {instanceID}] {val.name} ({valueType.Name})";
memberSnapshot.IsOutsideReference = false;
}
else
{
memberSnapshot.FormattedValue = $"[OUTSIDE_REFERENCE: InstanceID={instanceID}, Type={valueType.Name}] {val.name}";
memberSnapshot.IsOutsideReference = true;
}
return memberSnapshot;
}
memberSnapshot.FormattedValue = FormatPrimitiveValue(value);
return memberSnapshot;
}
private string FormatPrimitiveValue(object value)
{
if (value is string text)
{
return "\"" + text + "\"";
}
if (value.GetType().IsPrimitive || value.GetType().IsEnum)
{
return value.ToString();
}
if (value is Array array)
{
return $"Array[{array.Length}]";
}
if (value is IEnumerable enumerable && !(value is string))
{
int num = 0;
foreach (object item in enumerable)
{
_ = item;
num++;
}
return $"IEnumerable[{num}]";
}
return $"({value.GetType().Name}) {value}";
}
}
public class FileSnapshotSaver : ISnapshotSaver
{
private readonly string _snapshotsDirectory;
public FileSnapshotSaver(IPathsManager pathsManager)
{
_snapshotsDirectory = pathsManager.GetSnapshotsDirectory();
}
public string Save(string content, string gameObjectName)
{
return Save(content, gameObjectName, null);
}
public string Save(string content, string gameObjectName, string userProvidedPath)
{
string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string text2;
string path;
if (!string.IsNullOrEmpty(userProvidedPath))
{
if (userProvidedPath.EndsWith(".txt") || userProvidedPath.EndsWith(".json"))
{
text2 = Path.GetDirectoryName(userProvidedPath);
path = Path.GetFileName(userProvidedPath);
}
else
{
text2 = userProvidedPath;
path = "TreeSnapShooter_" + gameObjectName + "_" + text + ".txt";
}
}
else
{
text2 = _snapshotsDirectory;
path = "TreeSnapShooter_" + gameObjectName + "_" + text + ".txt";
}
string text3 = Path.Combine(text2, path);
try
{
EnsureDirectoryExists(text2);
File.WriteAllText(text3, content);
return text3;
}
catch (Exception ex)
{
UnityExplorerTreeSnapShooter.LogMessage("Error saving snapshot: " + ex.Message);
throw;
}
}
private void EnsureDirectoryExists(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
public string GetDefaultDirectory()
{
return _snapshotsDirectory;
}
}
public class HierarchyBuilder : IHierarchyBuilder
{
private readonly List<SnapshotNode> _nodes = new List<SnapshotNode>();
public void BuildHierarchy(Transform root, IReferenceTracker tracker)
{
_nodes.Clear();
BuildNode(root, 0, tracker);
}
private void BuildNode(Transform transform, int depth, IReferenceTracker tracker)
{
GameObject gameObject = ((Component)transform).gameObject;
int instanceID = ((Object)gameObject).GetInstanceID();
tracker.AddToSelection(instanceID);
SnapshotNode item = new SnapshotNode
{
Name = ((Object)gameObject).name,
InstanceId = instanceID,
TypeName = typeof(GameObject).Name,
Depth = depth,
IsActive = gameObject.activeInHierarchy
};
_nodes.Add(item);
for (int i = 0; i < transform.childCount; i++)
{
Transform child = transform.GetChild(i);
BuildNode(child, depth + 1, tracker);
}
}
public List<SnapshotNode> GetHierarchyNodes()
{
return new List<SnapshotNode>(_nodes);
}
public List<SnapshotNode> BuildParentChain(Transform transform, IReferenceTracker tracker)
{
List<SnapshotNode> list = new List<SnapshotNode>();
List<Transform> list2 = new List<Transform>();
Transform parent = transform.parent;
while ((Object)(object)parent != (Object)null)
{
list2.Add(parent);
parent = parent.parent;
}
list2.Reverse();
for (int i = 0; i < list2.Count; i++)
{
GameObject gameObject = ((Component)list2[i]).gameObject;
int instanceID = ((Object)gameObject).GetInstanceID();
tracker.AddToSelection(instanceID);
SnapshotNode item = new SnapshotNode
{
Name = ((Object)gameObject).name,
InstanceId = instanceID,
TypeName = typeof(GameObject).Name,
Depth = i,
IsActive = gameObject.activeInHierarchy
};
list.Add(item);
}
return list;
}
}
public class PathsManager : IPathsManager
{
private static PathsManager _instance;
private readonly string _snapshotsDirectory;
public static PathsManager Instance => _instance ?? (_instance = new PathsManager());
private PathsManager()
{
string projectLocation = UnityExplorerTreeSnapShooter.GetProjectLocation();
_snapshotsDirectory = Path.Combine(projectLocation, "Snapshots");
EnsureDirectoryExists(_snapshotsDirectory);
}
private void EnsureDirectoryExists(string path)
{
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
UnityExplorerTreeSnapShooter.LogMessage("Created directory: " + path);
}
}
catch (Exception ex)
{
UnityExplorerTreeSnapShooter.LogMessage("Error creating directory " + path + ": " + ex.Message);
}
}
public string GetSnapshotsDirectory()
{
return _snapshotsDirectory;
}
}
public class PlainTextSnapshotFormatter : ISnapshotFormatter
{
private readonly StringBuilder _output = new StringBuilder();
public string Format(SnapshotResult snapshot)
{
_output.Clear();
AddHeader(snapshot);
AddParentHierarchy(snapshot.ParentChain);
AddHierarchy(snapshot.Hierarchy);
AddComponents(snapshot.Components);
AddGameObjectReflection(snapshot.GameObjectSnapshots);
return _output.ToString();
}
private void AddHeader(SnapshotResult snapshot)
{
AppendLine("========================================");
AppendLine("TREE SNAPSHOT - " + snapshot.RootName);
AppendLine($"Root Instance ID: {snapshot.RootInstanceId}");
AppendLine($"Generated: {snapshot.GeneratedAt:yyyy-MM-dd HH:mm:ss}");
AppendLine("========================================");
AppendLine("");
}
private void AddParentHierarchy(List<SnapshotNode> parentChain)
{
AppendLine("=== PARENT HIERARCHY ===");
AppendLine("");
if (parentChain.Count == 0)
{
AppendLine(" (No parents - already at root)");
}
else
{
foreach (SnapshotNode item in parentChain)
{
string text = new string(' ', item.Depth * 2);
string text2 = (item.IsActive ? "" : " [INACTIVE]");
AppendLine($"{text}{item.Name} (InstanceID: {item.InstanceId}){text2}");
}
}
AppendLine("");
}
private void AddHierarchy(List<SnapshotNode> hierarchy)
{
AppendLine("=== CHILDREN HIERARCHY ===");
AppendLine("");
foreach (SnapshotNode item in hierarchy)
{
string text = new string(' ', item.Depth * 2);
string text2 = (item.IsActive ? "" : " [INACTIVE]");
AppendLine($"{text}[{item.Depth}] {item.Name} (InstanceID: {item.InstanceId}){text2}");
}
}
private void AddComponents(List<ComponentSnapshot> components)
{
AppendLine("");
AppendLine("=== COMPONENTS ===");
AppendLine("");
foreach (ComponentSnapshot component in components)
{
AppendLine($" {component.TypeName} (InstanceID: {component.InstanceId})");
}
AddComponentDetails(components);
}
private void AddComponentDetails(List<ComponentSnapshot> components)
{
AppendLine("");
AppendLine("=== UNITYEXPLORER DISCOVERED STATE (REFLECTION) ===");
AppendLine("");
foreach (ComponentSnapshot component in components)
{
AppendLine($"--- {component.TypeName} (InstanceID: {component.InstanceId}) ---");
foreach (MemberSnapshot member in component.Members)
{
string text = " ";
AppendLine(text + member.MemberType + " " + member.MemberName + ": " + member.FormattedValue);
}
AppendLine("");
}
}
private void AddGameObjectReflection(List<GameObjectSnapshot> gameObjectSnapshots)
{
AppendLine("");
AppendLine("=== GAME OBJECT REFLECTION (ALL CHILDREN) ===");
AppendLine("");
foreach (GameObjectSnapshot gameObjectSnapshot in gameObjectSnapshots)
{
string text = (gameObjectSnapshot.IsSelected ? " [SELECTED]" : "");
string text2 = (gameObjectSnapshot.IsDuplicate ? " [DUPLICATE - skipped]" : "");
AppendLine($"--- {gameObjectSnapshot.Name} (InstanceID: {gameObjectSnapshot.InstanceId}){text}{text2} ---");
if (gameObjectSnapshot.IsDuplicate || gameObjectSnapshot.Components.Count == 0)
{
AppendLine("");
continue;
}
foreach (ComponentSnapshot component in gameObjectSnapshot.Components)
{
AppendLine($" {component.TypeName} (InstanceID: {component.InstanceId}):");
foreach (MemberSnapshot member in component.Members)
{
string text3 = " ";
AppendLine(text3 + member.MemberType + " " + member.MemberName + ": " + member.FormattedValue);
}
AppendLine("");
}
AppendLine("");
}
}
private void AppendLine(string line)
{
_output.AppendLine(line);
}
}
public class ReferenceTracker : IReferenceTracker
{
private readonly HashSet<int> _selectionInstanceIds = new HashSet<int>();
public void AddToSelection(int instanceId)
{
_selectionInstanceIds.Add(instanceId);
}
public bool IsInSelection(int instanceId)
{
return _selectionInstanceIds.Contains(instanceId);
}
public bool IsInSelection(Object obj)
{
if (obj == (Object)null)
{
return false;
}
return _selectionInstanceIds.Contains(obj.GetInstanceID());
}
public void Clear()
{
_selectionInstanceIds.Clear();
}
}
public class VisibilityManager : IVisibilityManager
{
private static VisibilityManager _instance;
private UIBase _uiBase;
public static VisibilityManager Instance => _instance ?? (_instance = new VisibilityManager());
public bool IsVisible { get; private set; }
private VisibilityManager()
{
}
public void Initialize(UIBase uiBase)
{
_uiBase = uiBase;
}
public void Toggle()
{
SetVisible(!IsVisible);
}
public void SetVisible(bool visible)
{
if (IsVisible == visible)
{
return;
}
IsVisible = visible;
if (_uiBase != null)
{
_uiBase.Enabled = visible;
}
if (!visible)
{
EventSystem current = EventSystem.current;
if (current != null)
{
current.SetSelectedGameObject((GameObject)null);
}
}
}
}
}
namespace UnityExplorerTreeSnapShooter.Patches
{
[HarmonyPatch(typeof(InspectorPanel), "ConstructPanelContent")]
public static class Patch_InspectorPanel_ConstructPanelContent
{
private static SnapShooterPanel _snapShooterPanel;
private static void Postfix(InspectorPanel __instance)
{
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
if (_snapShooterPanel == null)
{
if (SnapShooterPanel.UIBaseRegistry == null)
{
SnapShooterPanel.UIBaseRegistry = UniversalUI.RegisterUI(SnapShooterPanel.UIBaseGuid, (Action)null);
}
SnapShooterPanel.UIBaseRegistry.Enabled = false;
VisibilityManager.Instance.Initialize(SnapShooterPanel.UIBaseRegistry);
_snapShooterPanel = new SnapShooterPanel(SnapShooterPanel.UIBaseRegistry);
}
ButtonRef obj = UIFactory.CreateButton(((Component)((PanelBase)__instance).TitleBar.transform.Find("CloseHolder")).gameObject, "SnapshotBtn", "Snapshot", (Color?)new Color(0.2f, 0.2f, 0.35f));
GameObject gameObject = ((Component)obj.Component).gameObject;
int? num = 25;
UIFactory.SetLayoutElement(gameObject, (int?)80, num, (int?)null, (int?)null, (int?)null, (int?)null, (bool?)null);
((Component)obj.Component).transform.SetSiblingIndex(0);
obj.OnClick = (Action)Delegate.Combine(obj.OnClick, (Action)delegate
{
VisibilityManager.Instance.Toggle();
});
UnityExplorerTreeSnapShooter.LogMessage("[TreeSnapShooter] Snapshoot button added to Inspector panel!");
}
}
}
namespace UnityExplorerTreeSnapShooter.Models
{
public class SnapshotNode
{
public string Name { get; set; }
public int InstanceId { get; set; }
public string TypeName { get; set; }
public List<SnapshotNode> Children { get; set; } = new List<SnapshotNode>();
public int Depth { get; set; }
public bool IsActive { get; set; }
public bool IsOutsideReference { get; set; }
}
public class ComponentSnapshot
{
public string TypeName { get; set; }
public int InstanceId { get; set; }
public List<MemberSnapshot> Members { get; set; } = new List<MemberSnapshot>();
}
public class GameObjectSnapshot
{
public string Name { get; set; }
public int InstanceId { get; set; }
public bool IsSelected { get; set; }
public bool IsDuplicate { get; set; }
public List<ComponentSnapshot> Components { get; set; } = new List<ComponentSnapshot>();
}
public class MemberSnapshot
{
public string MemberType { get; set; }
public string MemberName { get; set; }
public string ValueType { get; set; }
public string FormattedValue { get; set; }
public int? ReferencedInstanceId { get; set; }
public bool IsOutsideReference { get; set; }
}
public class SnapshotResult
{
public string RootName { get; set; }
public int RootInstanceId { get; set; }
public DateTime GeneratedAt { get; set; }
public List<SnapshotNode> ParentChain { get; set; } = new List<SnapshotNode>();
public List<SnapshotNode> Hierarchy { get; set; } = new List<SnapshotNode>();
public List<ComponentSnapshot> Components { get; set; } = new List<ComponentSnapshot>();
public List<GameObjectSnapshot> GameObjectSnapshots { get; set; } = new List<GameObjectSnapshot>();
}
}