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; 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.1.0")] 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.1.0"; 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.1.0!"); 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; private IPathsManager _pathsManager; private IVisibilityManager _visibilityManager; private InputFieldRef _saveLocation; private InputFieldRef _saveFileName; private string _lastInspectedName; 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 Text SelectionText { get; private set; } public override bool CanDragAndResize => true; public SnapShooterPanel(UIBase owner) : base(owner) { Instance = this; } protected override void ConstructPanelContent() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_029d: 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_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) if (_snapShooterService == null) { _pathsManager = PathsManager.Instance; _visibilityManager = VisibilityManager.Instance; FileSnapshotSaver saver = new FileSnapshotSaver(); _snapShooterService = new TreeSnapShooterService(saver); } SelectionText = UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "Header", GetSelectionMessage(), (TextAnchor)4, default(Color), true, 14); UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "LocationLabel", "Save Location:", (TextAnchor)3, default(Color), true, 14); _saveLocation = UIFactory.CreateInputField(((PanelBase)this).ContentRoot, "SaveLocationField", "Save to directory..."); GameObject gameObject = ((Component)_saveLocation.Component).gameObject; int? num = 25; int? num2 = 9999; UIFactory.SetLayoutElement(gameObject, (int?)null, num, num2, (int?)null, (int?)null, (int?)null, (bool?)null); _saveLocation.Component.text = _pathsManager.GetSnapshotsDirectory(); UIFactory.CreateLabel(((PanelBase)this).ContentRoot, "FileNameLabel", "Save File Name:", (TextAnchor)3, default(Color), true, 14); _saveFileName = UIFactory.CreateInputField(((PanelBase)this).ContentRoot, "SaveFileNameField", "File name..."); GameObject gameObject2 = ((Component)_saveFileName.Component).gameObject; num2 = 25; num = 9999; UIFactory.SetLayoutElement(gameObject2, (int?)null, num2, num, (int?)null, (int?)null, (int?)null, (bool?)null); _lastInspectedName = GetSelectionName(); _saveFileName.Component.text = _pathsManager.GenerateDefaultFileName(_lastInspectedName); ButtonRef obj = UIFactory.CreateButton(((PanelBase)this).ContentRoot, "SnapshotBtn", "Snapshot Selection", (Color?)new Color(0.2f, 0.2f, 0.35f)); GameObject gameObject3 = ((Component)obj.Component).gameObject; num = 30; num2 = 9999; UIFactory.SetLayoutElement(gameObject3, (int?)null, num, num2, (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)); num2 = 9999; num = 120; UIFactory.SetLayoutElement(obj2, (int?)null, num, (int?)null, num2, (int?)null, (int?)null, (bool?)null); _logText = val.AddComponent(); _logText.font = Resources.GetBuiltinResource("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; } private void OnInspectorChanged() { if (((UIModel)this).Enabled) { _lastInspectedName = GetSelectionName(); if ((Object)(object)SelectionText != (Object)null) { SelectionText.text = GetSelectionMessage(); } InputFieldRef saveFileName = _saveFileName; if ((Object)(object)((saveFileName != null) ? saveFileName.Component : null) != (Object)null) { _saveFileName.Component.text = _pathsManager.GenerateDefaultFileName(_lastInspectedName); } } } public override void SetActive(bool active) { bool enabled = ((UIModel)this).Enabled; ((PanelBase)this).SetActive(active); if (active && !enabled) { _lastInspectedName = GetSelectionName(); if ((Object)(object)SelectionText != (Object)null) { SelectionText.text = GetSelectionMessage(); } InputFieldRef saveFileName = _saveFileName; if ((Object)(object)((saveFileName != null) ? saveFileName.Component : null) != (Object)null) { _saveFileName.Component.text = _pathsManager.GenerateDefaultFileName(_lastInspectedName); } } } public override void Update() { InputFieldRef saveFileName = _saveFileName; if (!((Object)(object)((saveFileName != null) ? saveFileName.Component : null) == (Object)null)) { string selectionName = GetSelectionName(); if (selectionName != _lastInspectedName) { _lastInspectedName = selectionName; OnInspectorChanged(); } } } protected override void OnClosePanelClicked() { _visibilityManager.Hide(); } public string GetSelectionMessage() { return "Selection: " + GetSelectionName() + ""; } public string GetSelectionName() { GameObject inspectedGameObject = GetInspectedGameObject(); if ((Object)(object)inspectedGameObject != (Object)null) { return ((Object)inspectedGameObject).name; } return "None"; } private void OnSnapShoot() { if (_saveLocation == null || (Object)(object)_saveLocation.Component == (Object)null || _saveFileName == null || (Object)(object)_saveFileName.Component == (Object)null) { Log("ERROR: Save fields not initialized!"); return; } string text = _saveLocation.Component.text; string text2 = _saveFileName.Component.text; string fullPath = Path.Combine(text, text2); GameObject inspectedGameObject = GetInspectedGameObject(); if ((Object)(object)inspectedGameObject != (Object)null) { try { string content = _snapShooterService.ExecuteSnapShoot(inspectedGameObject); string text3 = _snapShooterService.SaveSnapshot(content, fullPath); Log("Successfully created snapshot: " + text3); 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 ReadComponents(GameObject gameObject, IReferenceTracker tracker); List ReadAllGameObjects(List hierarchyNodes, int selectedInstanceId, IReferenceTracker tracker); } public interface IHierarchyBuilder { void BuildHierarchy(Transform root, IReferenceTracker tracker); List GetHierarchyNodes(); List BuildParentChain(Transform transform, IReferenceTracker tracker); } public interface IPathsManager { string GetSnapshotsDirectory(); string GenerateDefaultFileName(string gameObjectName); string GenerateDefaultFullPath(string gameObjectName); } 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 fullPath); } public interface ISnapshotFormatter { string Format(SnapshotResult snapshot); } public interface ISnapshotSaver { string Save(string content, string fullPath); } public interface IVisibilityManager { bool IsVisible { get; } void Initialize(UIBase uiBase); void Toggle(); void Hide(); void OnParentVisibilityChanged(bool parentVisible); void OnMouseInspectStarted(); void OnMouseInspectStopped(); } 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 fullPath) { return _saver.Save(content, fullPath); } } } namespace UnityExplorerTreeSnapShooter.Services.Implementation { public class ComponentStateReader : IComponentStateReader { private readonly IReferenceTracker _tracker; public ComponentStateReader(IReferenceTracker tracker) { _tracker = tracker; } public List ReadComponents(GameObject gameObject, IReferenceTracker tracker) { List list = new List(); Component[] components = gameObject.GetComponents(); 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 ReadAllGameObjects(List hierarchyNodes, int selectedInstanceId, IReferenceTracker tracker) { List list = new List(); HashSet hashSet = new HashSet(); 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() }; if (hashSet.Contains(hierarchyNode.InstanceId)) { gameObjectSnapshot.IsDuplicate = true; list.Add(gameObjectSnapshot); continue; } hashSet.Add(hierarchyNode.InstanceId); Component[] components = gameObjectByInstanceId.GetComponents(); 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(); foreach (GameObject val in array) { if (((Object)val).GetInstanceID() == instanceId) { return val; } } return null; } private List ReadMembers(Component component, IReferenceTracker tracker) { List list = new List(); 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 { public string Save(string content, string fullPath) { string directoryName = Path.GetDirectoryName(fullPath); try { EnsureDirectoryExists(directoryName); File.WriteAllText(fullPath, content); return fullPath; } catch (Exception ex) { UnityExplorerTreeSnapShooter.LogMessage("Error saving snapshot: " + ex.Message); throw; } } private void EnsureDirectoryExists(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } } public class HierarchyBuilder : IHierarchyBuilder { private readonly List _nodes = new List(); 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 GetHierarchyNodes() { return new List(_nodes); } public List BuildParentChain(Transform transform, IReferenceTracker tracker) { List list = new List(); List list2 = new List(); 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 string GenerateDefaultFileName(string gameObjectName) { string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); return "TreeSnapShooter_" + gameObjectName + "_" + text + ".txt"; } public string GenerateDefaultFullPath(string gameObjectName) { return Path.Combine(_snapshotsDirectory, GenerateDefaultFileName(gameObjectName)); } } 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 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 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 components) { AppendLine(""); AppendLine("=== COMPONENTS ==="); AppendLine(""); foreach (ComponentSnapshot component in components) { AppendLine($" {component.TypeName} (InstanceID: {component.InstanceId})"); } AddComponentDetails(components); } private void AddComponentDetails(List 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 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 _selectionInstanceIds = new HashSet(); 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; private bool _userRequested; private bool _parentVisible = true; private bool _mouseInspectOverride; 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() { _userRequested = !_userRequested; ApplyVisibility(); } public void Hide() { _userRequested = false; ApplyVisibility(); } public void OnParentVisibilityChanged(bool parentVisible) { _parentVisible = parentVisible; ApplyVisibility(); } public void OnMouseInspectStarted() { _mouseInspectOverride = true; ApplyVisibility(); } public void OnMouseInspectStopped() { _mouseInspectOverride = false; ApplyVisibility(); } private void ApplyVisibility() { bool visible = _userRequested && _parentVisible && !_mouseInspectOverride; SetVisible(visible); } private 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!"); } } [HarmonyPatch(typeof(MouseInspector), "StartInspect")] public static class Patch_MouseInspector_StartInspect { private static void Postfix() { VisibilityManager.Instance.OnMouseInspectStarted(); } } [HarmonyPatch(typeof(MouseInspector), "StopInspect")] public static class Patch_MouseInspector_StopInspect { private static void Postfix() { VisibilityManager.Instance.OnMouseInspectStopped(); } } [HarmonyPatch(typeof(UIManager), "set_ShowMenu")] public static class Patch_UIManager_ShowMenu { private static void Postfix(bool value) { VisibilityManager.Instance.OnParentVisibilityChanged(value); } } } namespace UnityExplorerTreeSnapShooter.Models { public class ComponentSnapshot { public string TypeName { get; set; } public int InstanceId { get; set; } public List Members { get; set; } = new List(); } 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 Components { get; set; } = new List(); } 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 SnapshotNode { public string Name { get; set; } public int InstanceId { get; set; } public string TypeName { get; set; } public List Children { get; set; } = new List(); public int Depth { get; set; } public bool IsActive { 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 ParentChain { get; set; } = new List(); public List Hierarchy { get; set; } = new List(); public List Components { get; set; } = new List(); public List GameObjectSnapshots { get; set; } = new List(); } }