asecli 0.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. asecli/__init__.py +3 -0
  2. asecli/bridge/__init__.py +40 -0
  3. asecli/bridge/_editor_port_contract.py +131 -0
  4. asecli/bridge/_editor_primitives.py +205 -0
  5. asecli/bridge/_editor_spec_grammar.py +245 -0
  6. asecli/bridge/_editor_spec_io.py +26 -0
  7. asecli/bridge/_editor_spec_validation.py +218 -0
  8. asecli/bridge/_gui_handoff_store.py +78 -0
  9. asecli/bridge/_gui_project.py +35 -0
  10. asecli/bridge/_gui_resource_store.py +159 -0
  11. asecli/bridge/_gui_resource_upgrade.py +122 -0
  12. asecli/bridge/editor_create.py +173 -0
  13. asecli/bridge/editor_spec.py +250 -0
  14. asecli/bridge/graph_geometry.py +249 -0
  15. asecli/bridge/graph_geometry_parser.py +57 -0
  16. asecli/bridge/graph_inspect.py +136 -0
  17. asecli/bridge/gui_handoff.py +100 -0
  18. asecli/bridge/gui_presentation.py +70 -0
  19. asecli/bridge/gui_provider_detection.py +197 -0
  20. asecli/bridge/gui_runtime_probe.py +25 -0
  21. asecli/bridge/gui_support.py +240 -0
  22. asecli/bridge/gui_support_resource.py +58 -0
  23. asecli/bridge/mcp_client.py +231 -0
  24. asecli/bridge/recompile.py +107 -0
  25. asecli/bridge/resource_text.py +12 -0
  26. asecli/bridge/resources/asecli_material_gui.authoring.part00.cs.txt +238 -0
  27. asecli/bridge/resources/asecli_material_gui.authoring.part01.cs.txt +235 -0
  28. asecli/bridge/resources/asecli_material_gui.authoring.store.cs.txt +125 -0
  29. asecli/bridge/resources/asecli_material_gui.condition.cs.txt +99 -0
  30. asecli/bridge/resources/asecli_material_gui.hydration.cs.txt +105 -0
  31. asecli/bridge/resources/asecli_material_gui.part00.cs.txt +300 -0
  32. asecli/bridge/resources/asecli_material_gui.part01.cs.txt +290 -0
  33. asecli/bridge/resources/asecli_material_gui.reconciliation.cs.txt +174 -0
  34. asecli/bridge/resources/asecli_material_gui.transaction.cs.txt +108 -0
  35. asecli/bridge/resources/editor_create.part00.cs.txt +176 -0
  36. asecli/bridge/resources/editor_create.part01.cs.txt +145 -0
  37. asecli/bridge/resources/editor_create.part02.cs.txt +139 -0
  38. asecli/bridge/resources/editor_create.part03.cs.txt +161 -0
  39. asecli/bridge/resources/editor_create.part04.cs.txt +114 -0
  40. asecli/bridge/resources/wire_route.transaction.cs.txt +140 -0
  41. asecli/bridge/wire_route.py +127 -0
  42. asecli/checks/__init__.py +10 -0
  43. asecli/checks/checksum.py +53 -0
  44. asecli/checks/local_vars.py +135 -0
  45. asecli/checks/usage.py +141 -0
  46. asecli/checks/validate.py +129 -0
  47. asecli/cli/__init__.py +1 -0
  48. asecli/cli/commands.py +237 -0
  49. asecli/cli/commentary_command.py +158 -0
  50. asecli/cli/create_command.py +250 -0
  51. asecli/cli/custom_gui_command.py +170 -0
  52. asecli/cli/gui_provider.py +30 -0
  53. asecli/cli/gui_support_command.py +35 -0
  54. asecli/cli/io.py +214 -0
  55. asecli/cli/layout_command.py +213 -0
  56. asecli/cli/main.py +245 -0
  57. asecli/cli/recompile_metadata.py +53 -0
  58. asecli/cli/skill_command.py +121 -0
  59. asecli/cli/usage_command.py +42 -0
  60. asecli/core/__init__.py +84 -0
  61. asecli/core/comment_bounds.py +166 -0
  62. asecli/core/comment_layout.py +167 -0
  63. asecli/core/comment_metrics.py +132 -0
  64. asecli/core/comment_purpose.py +140 -0
  65. asecli/core/commentary.py +225 -0
  66. asecli/core/compiled_metadata.py +135 -0
  67. asecli/core/compiled_properties.py +190 -0
  68. asecli/core/custom_gui.py +250 -0
  69. asecli/core/custom_gui_versions.py +35 -0
  70. asecli/core/fishbone_placement.py +159 -0
  71. asecli/core/fishbone_topology.py +186 -0
  72. asecli/core/graph_ops.py +122 -0
  73. asecli/core/layout.py +174 -0
  74. asecli/core/layout_audit.py +237 -0
  75. asecli/core/layout_audit_geometry.py +247 -0
  76. asecli/core/layout_audit_repeated.py +44 -0
  77. asecli/core/layout_graph.py +113 -0
  78. asecli/core/local_vars.py +54 -0
  79. asecli/core/material_gui_condition.py +68 -0
  80. asecli/core/material_gui_protocol.py +35 -0
  81. asecli/core/material_gui_spec.py +191 -0
  82. asecli/core/meticulous_layout.py +114 -0
  83. asecli/core/model.py +237 -0
  84. asecli/core/property_presentation.py +233 -0
  85. asecli/core/wire_geometry.py +83 -0
  86. asecli/core/wire_router.py +248 -0
  87. asecli/schema/__init__.py +49 -0
  88. asecli/schema/data/observed.json +16 -0
  89. asecli/schema/data/schemas.json +14106 -0
  90. asecli/skills/asecli/SKILL.md +275 -0
  91. asecli/skills/asecli/references/layout-standard.md +99 -0
  92. asecli/skills/asecli/references/master-output-settings-standard.md +51 -0
  93. asecli/skills/asecli/references/material-property-standard.md +108 -0
  94. asecli-0.6.2.dist-info/METADATA +594 -0
  95. asecli-0.6.2.dist-info/RECORD +98 -0
  96. asecli-0.6.2.dist-info/WHEEL +4 -0
  97. asecli-0.6.2.dist-info/entry_points.txt +2 -0
  98. asecli-0.6.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,238 @@
1
+
2
+ #if UNITY_EDITOR
3
+ namespace ASECLI.MaterialGUI.Authoring
4
+ {
5
+ using System;
6
+ using System.Collections;
7
+ using System.Collections.Generic;
8
+ using System.Globalization;
9
+ using System.Reflection;
10
+ using System.Text;
11
+ using System.Text.RegularExpressions;
12
+ using UnityEditor;
13
+ using UnityEngine;
14
+
15
+ // Clean-room ASE integration. It uses the public Custom Attributes storage
16
+ // through a capability-checked reflection adapter and never patches ASE source.
17
+ public sealed class ASECLIGUIAttributesWindow : EditorWindow
18
+ {
19
+ private UnityEngine.Object selectedNode;
20
+ private string nodeLabel = String.Empty;
21
+ private string aseVersion = "unknown";
22
+ private string adapterError = String.Empty;
23
+ private bool useFoldout;
24
+ private bool useTooltip;
25
+ private bool useHelpBox;
26
+ private bool useEnabledIf;
27
+ private bool assignFallbackEditor = true;
28
+ private string foldout = String.Empty;
29
+ private string tooltip = String.Empty;
30
+ private string helpBox = String.Empty;
31
+ private string enabledIfProperty = String.Empty;
32
+ private int enabledIfOperatorIndex = 2;
33
+ private float enabledIfValue = 1f;
34
+ private Vector2 scroll;
35
+ [MenuItem("Window/Amplify Shader Editor/MZGUI Attributes (ASECLI)")]
36
+ public static void Open()
37
+ {
38
+ ASECLIGUIAttributesWindow window = GetWindow<ASECLIGUIAttributesWindow>();
39
+ window.titleContent = new GUIContent("MZGUI Attributes");
40
+ window.minSize = new Vector2(340f, 360f);
41
+ window.Show();
42
+ }
43
+
44
+ private void OnEnable() { EditorApplication.update += TrackSelection; }
45
+ private void OnDisable() { EditorApplication.update -= TrackSelection; }
46
+
47
+ private void TrackSelection()
48
+ {
49
+ UnityEngine.Object node;
50
+ string label;
51
+ string version;
52
+ string error;
53
+ if (!ASEReflection.TrySelectedProperty(out node, out label, out version, out error))
54
+ {
55
+ if (selectedNode != null || adapterError != error)
56
+ {
57
+ selectedNode = null;
58
+ adapterError = error;
59
+ aseVersion = version;
60
+ Repaint();
61
+ }
62
+ return;
63
+ }
64
+ if (node == selectedNode)
65
+ return;
66
+ selectedNode = node;
67
+ nodeLabel = label;
68
+ aseVersion = version;
69
+ adapterError = String.Empty;
70
+ LoadValues();
71
+ Repaint();
72
+ }
73
+
74
+ private void OnGUI()
75
+ {
76
+ EditorGUILayout.LabelField("ASE GUI 属性", EditorStyles.boldLabel);
77
+ EditorGUILayout.LabelField("ASE 版本", aseVersion);
78
+ GUILayout.Label(
79
+ "选择一个 ASE Property 节点后,在这里直接编辑兼容 MZGUI 的 Foldout、Tooltip 和 HelpBox。",
80
+ EditorStyles.wordWrappedLabel);
81
+ if (selectedNode == null)
82
+ {
83
+ GUILayout.Label(
84
+ String.IsNullOrEmpty(adapterError) ? "请打开 ASE 并选择一个 Property 节点。" : adapterError,
85
+ EditorStyles.wordWrappedLabel);
86
+ if (GUILayout.Button("重新检测")) TrackSelection();
87
+ return;
88
+ }
89
+
90
+ EditorGUILayout.LabelField("当前属性", nodeLabel);
91
+ scroll = EditorGUILayout.BeginScrollView(scroll);
92
+ useFoldout = EditorGUILayout.ToggleLeft("Foldout", useFoldout);
93
+ using (new EditorGUI.DisabledScope(!useFoldout))
94
+ foldout = EditorGUILayout.TextField("分组标题", foldout);
95
+ EditorGUILayout.Space(8f);
96
+ useTooltip = EditorGUILayout.ToggleLeft("Tooltip", useTooltip);
97
+ using (new EditorGUI.DisabledScope(!useTooltip))
98
+ tooltip = EditorGUILayout.TextArea(tooltip, GUILayout.MinHeight(64f));
99
+ EditorGUILayout.Space(8f);
100
+ useHelpBox = EditorGUILayout.ToggleLeft("HelpBox", useHelpBox);
101
+ using (new EditorGUI.DisabledScope(!useHelpBox))
102
+ helpBox = EditorGUILayout.TextArea(helpBox, GUILayout.MinHeight(80f));
103
+ EditorGUILayout.Space(8f);
104
+ useEnabledIf = EditorGUILayout.ToggleLeft(
105
+ "条件启用(不满足时置灰)", useEnabledIf);
106
+ using (new EditorGUI.DisabledScope(!useEnabledIf))
107
+ {
108
+ enabledIfProperty = EditorGUILayout.TextField("控制属性", enabledIfProperty);
109
+ enabledIfOperatorIndex = EditorGUILayout.Popup(
110
+ "比较", enabledIfOperatorIndex, ASEAttributeStore.EnabledIfOperators);
111
+ enabledIfValue = EditorGUILayout.FloatField("比较值", enabledIfValue);
112
+ }
113
+ EditorGUILayout.Space(8f);
114
+ assignFallbackEditor = EditorGUILayout.ToggleLeft(
115
+ "使用 MZGUI.MZGUI 材质面板", assignFallbackEditor);
116
+ EditorGUILayout.EndScrollView();
117
+
118
+ EditorGUILayout.BeginHorizontal();
119
+ if (GUILayout.Button("应用到节点")) Apply(false);
120
+ if (GUILayout.Button("应用并保存 Shader")) Apply(true);
121
+ EditorGUILayout.EndHorizontal();
122
+ }
123
+
124
+ private void LoadValues()
125
+ {
126
+ Dictionary<string, string> values;
127
+ string error;
128
+ if (!ASEAttributeStore.TryRead(selectedNode, out values, out error))
129
+ {
130
+ adapterError = error;
131
+ selectedNode = null;
132
+ return;
133
+ }
134
+ useFoldout = values.TryGetValue("FoldoutMzgui", out foldout);
135
+ useTooltip = values.TryGetValue("TooltipMzgui", out tooltip);
136
+ useHelpBox = values.TryGetValue("HelpBoxMzgui", out helpBox);
137
+ string condition;
138
+ useEnabledIf = values.TryGetValue("EnableIfMzgui", out condition);
139
+ if (useEnabledIf && !ASEAttributeStore.TryParseCondition(
140
+ condition, out enabledIfProperty, out enabledIfOperatorIndex,
141
+ out enabledIfValue, out error))
142
+ {
143
+ adapterError = error;
144
+ selectedNode = null;
145
+ }
146
+ }
147
+
148
+ private void Apply(bool save)
149
+ {
150
+ string error;
151
+ if (!ASEApplyTransaction.TryApply(
152
+ selectedNode, useFoldout ? foldout : null,
153
+ useTooltip ? tooltip : null, useHelpBox ? helpBox : null,
154
+ useEnabledIf ? ASEAttributeStore.FormatCondition(
155
+ enabledIfProperty, enabledIfOperatorIndex, enabledIfValue) : null,
156
+ assignFallbackEditor, save, out error))
157
+ { ShowNotification(new GUIContent(error)); return; }
158
+ ShowNotification(new GUIContent(save ? "已应用并保存" : "已应用"));
159
+ }
160
+ }
161
+
162
+ internal static class ASEAttributeStore
163
+ {
164
+ internal static readonly string[] EnabledIfOperators = {
165
+ "Less", "LessEqual", "Equal", "NotEqual", "GreaterEqual", "Greater"
166
+ };
167
+ private static readonly Regex Managed = new Regex(
168
+ @"^\[?(FoldoutMzgui|TooltipMzgui|HelpBoxMzgui|EnableIfMzgui|ASECLIFoldout|ASECLITooltip|ASECLIHelpBox|ASECLIEnableIf)(?:\((.*)\))?\]?$",
169
+ RegexOptions.CultureInvariant);
170
+
171
+ internal static bool TryRead(
172
+ UnityEngine.Object node, out Dictionary<string, string> values, out string error)
173
+ {
174
+ values = new Dictionary<string, string>(StringComparer.Ordinal);
175
+ IList attributes;
176
+ FieldInfo listField;
177
+ FieldInfo countField;
178
+ if (!TryStorage(node, out attributes, out listField, out countField, out error))
179
+ return false;
180
+ foreach (object item in attributes)
181
+ {
182
+ Match match = Managed.Match(item as string ?? String.Empty);
183
+ if (!match.Success) continue;
184
+ string encoded = match.Groups[2].Success ? match.Groups[2].Value : String.Empty;
185
+ string canonical = Canonical(match.Groups[1].Value);
186
+ string decoded = Decode(encoded);
187
+ string existing;
188
+ if (values.TryGetValue(canonical, out existing) && existing != decoded &&
189
+ match.Groups[1].Value == canonical)
190
+ {
191
+ error = "同一 MZGUI 属性存在冲突的 canonical 值;已停止写入";
192
+ return false;
193
+ }
194
+ if (!values.ContainsKey(canonical) || match.Groups[1].Value == canonical)
195
+ values[canonical] = decoded;
196
+ }
197
+ return true;
198
+ }
199
+
200
+ internal static bool TryWrite(
201
+ UnityEngine.Object node, string foldout, string tooltip, string help,
202
+ string enabledIf, out string error)
203
+ {
204
+ IList attributes;
205
+ FieldInfo listField;
206
+ FieldInfo countField;
207
+ if (!TryStorage(node, out attributes, out listField, out countField, out error))
208
+ return false;
209
+ if (foldout != null && foldout.IndexOfAny(";\r\n\\><'\"[]{}=+`~/?!@#$%^&*:".ToCharArray()) >= 0)
210
+ {
211
+ error = "Foldout 标题包含 ASE 不接受的字符";
212
+ return false;
213
+ }
214
+ if (enabledIf != null)
215
+ {
216
+ string propertyName; int operatorIndex; float value;
217
+ if (!TryParseCondition(
218
+ enabledIf, out propertyName, out operatorIndex, out value, out error))
219
+ return false;
220
+ }
221
+ Undo.RecordObject(node, "Edit ASE GUI attributes");
222
+ List<string> kept = new List<string>();
223
+ foreach (object item in attributes)
224
+ {
225
+ string raw = item as string ?? String.Empty;
226
+ if (!Managed.IsMatch(raw)) kept.Add(raw);
227
+ }
228
+ if (foldout != null) kept.Add("FoldoutMzgui(" + EncodeFoldout(foldout) + ")");
229
+ if (tooltip != null) kept.Add("TooltipMzgui(" + EncodeUnicode(tooltip) + ")");
230
+ if (help != null) kept.Add("HelpBoxMzgui(" + EncodeUnicode(help) + ")");
231
+ if (enabledIf != null) kept.Add("EnableIfMzgui(" + enabledIf + ")");
232
+ attributes.Clear();
233
+ foreach (string raw in kept) attributes.Add(raw);
234
+ countField.SetValue(node, attributes.Count);
235
+ EditorUtility.SetDirty(node);
236
+ error = String.Empty;
237
+ return true;
238
+ }
@@ -0,0 +1,235 @@
1
+ internal static class ASEReflection
2
+ {
3
+ private const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic |
4
+ BindingFlags.Instance | BindingFlags.Static;
5
+ private static EditorWindow currentWindow;
6
+ private static object currentGraph;
7
+
8
+ internal static bool TryGraph(out EditorWindow window, out object graph)
9
+ {
10
+ window = FindWindow();
11
+ graph = window != null
12
+ ? Member(window, "CurrentGraph", "OutsideGraph", "m_mainGraphInstance") : null;
13
+ return window != null && graph != null;
14
+ }
15
+
16
+ internal static IList PropertyNodes(object graph)
17
+ {
18
+ IList nodes = graph != null ? Member(graph, "Nodes", "m_nodes") as IList : null;
19
+ if (nodes == null) return null;
20
+ ArrayList properties = new ArrayList();
21
+ foreach (object node in nodes)
22
+ if (IsPropertyNode(node)) properties.Add(node);
23
+ return properties;
24
+ }
25
+
26
+ internal static object Value(object target, params string[] names)
27
+ {
28
+ return target != null ? Member(target, names) : null;
29
+ }
30
+
31
+ internal static bool TrySelectedProperty(
32
+ out UnityEngine.Object node, out string label, out string version, out string error)
33
+ {
34
+ node = null;
35
+ label = String.Empty;
36
+ version = Version();
37
+ if (!TryGraph(out currentWindow, out currentGraph))
38
+ {
39
+ error = "未检测到打开的 Amplify Shader Editor 窗口";
40
+ return false;
41
+ }
42
+ IList selected = currentGraph != null
43
+ ? Member(currentGraph, "SelectedNodes", "m_selectedNodes") as IList : null;
44
+ if (selected == null)
45
+ {
46
+ error = "当前 ASE 版本无法读取节点选择;已停止写入";
47
+ return false;
48
+ }
49
+ foreach (object candidate in selected)
50
+ {
51
+ if (!IsPropertyNode(candidate)) continue;
52
+ if (node != null)
53
+ {
54
+ node = null;
55
+ error = "请只选择一个 Property 节点";
56
+ return false;
57
+ }
58
+ node = candidate as UnityEngine.Object;
59
+ }
60
+ if (node == null)
61
+ {
62
+ error = "请在 ASE 中选择一个 Property 节点";
63
+ return false;
64
+ }
65
+ object name = Member(node, "PropertyInspectorName", "PropertyName", "m_propertyName");
66
+ label = name as string ?? node.name;
67
+ error = String.Empty;
68
+ return true;
69
+ }
70
+
71
+ internal static bool TryAssignFallbackEditor(out string error)
72
+ {
73
+ object master = currentGraph != null
74
+ ? Member(currentGraph, "CurrentMasterNode", "CurrentOutputNode", "m_currentMasterNode") : null;
75
+ FieldInfo field = master != null ? Field(master.GetType(), "m_customInspectorName") : null;
76
+ if (field == null)
77
+ {
78
+ error = "当前 ASE 版本无法设置 Custom Editor;属性已保留,请在 Master 节点手动选择 ASECLI GUI";
79
+ return false;
80
+ }
81
+ UnityEngine.Object target = master as UnityEngine.Object;
82
+ if (target != null) Undo.RecordObject(target, "Select ASECLI material GUI");
83
+ field.SetValue(master, "MZGUI.MZGUI");
84
+ if (target != null) EditorUtility.SetDirty(target);
85
+ error = String.Empty;
86
+ return true;
87
+ }
88
+
89
+ internal static bool TryMasterSnapshot(
90
+ bool required, out object master, out FieldInfo field, out string value, out string error)
91
+ {
92
+ master = currentGraph != null
93
+ ? Member(currentGraph, "CurrentMasterNode", "CurrentOutputNode", "m_currentMasterNode") : null;
94
+ field = master != null ? Field(master.GetType(), "m_customInspectorName") : null;
95
+ value = field != null ? field.GetValue(master) as string : null;
96
+ if (!required || field != null)
97
+ {
98
+ error = String.Empty;
99
+ return true;
100
+ }
101
+ error = "当前 ASE 版本无法设置 Custom Editor;事务未开始";
102
+ return false;
103
+ }
104
+
105
+ internal static bool TryRestoreMaster(object master, FieldInfo field, string value, out string error)
106
+ {
107
+ try
108
+ {
109
+ if (field != null) field.SetValue(master, value);
110
+ UnityEngine.Object target = master as UnityEngine.Object;
111
+ if (target != null) EditorUtility.SetDirty(target);
112
+ error = String.Empty;
113
+ return true;
114
+ }
115
+ catch (Exception exception)
116
+ {
117
+ error = exception.GetBaseException().Message;
118
+ return false;
119
+ }
120
+ }
121
+
122
+ internal static bool CanSave(bool required, out string error)
123
+ {
124
+ if (!required || SaveMethod() != null)
125
+ {
126
+ error = String.Empty;
127
+ return true;
128
+ }
129
+ error = "当前 ASE 版本无法调用保存;事务未开始";
130
+ return false;
131
+ }
132
+
133
+ internal static bool TryDiskSnapshot(
134
+ bool required, out string path, out byte[] content, out string error)
135
+ {
136
+ path = null;
137
+ content = null;
138
+ if (!required) { error = String.Empty; return true; }
139
+ Shader shader = currentGraph != null ? Member(currentGraph, "CurrentShader") as Shader : null;
140
+ path = shader != null ? AssetDatabase.GetAssetPath(shader) : null;
141
+ string projectRoot = System.IO.Path.GetDirectoryName(Application.dataPath);
142
+ string fullPath = !String.IsNullOrEmpty(path)
143
+ ? System.IO.Path.GetFullPath(System.IO.Path.Combine(projectRoot, path)) : null;
144
+ if (String.IsNullOrEmpty(fullPath) || !System.IO.File.Exists(fullPath))
145
+ { error = "无法建立 Shader 磁盘快照;事务未开始"; return false; }
146
+ content = System.IO.File.ReadAllBytes(fullPath);
147
+ path = fullPath;
148
+ error = String.Empty;
149
+ return true;
150
+ }
151
+
152
+ internal static bool TryRestoreDisk(string path, byte[] content, out string error)
153
+ {
154
+ try
155
+ {
156
+ if (path != null && content != null) System.IO.File.WriteAllBytes(path, content);
157
+ AssetDatabase.Refresh();
158
+ error = String.Empty;
159
+ return true;
160
+ }
161
+ catch (Exception exception)
162
+ { error = exception.GetBaseException().Message; return false; }
163
+ }
164
+
165
+ internal static bool TrySave(out string error)
166
+ {
167
+ MethodInfo save = SaveMethod();
168
+ if (save == null)
169
+ {
170
+ error = "当前 ASE 版本无法调用保存;属性已应用,请使用 ASE 的 Save 按钮";
171
+ return false;
172
+ }
173
+ save.Invoke(currentWindow, new object[] { false });
174
+ error = String.Empty;
175
+ return true;
176
+ }
177
+
178
+ private static MethodInfo SaveMethod()
179
+ {
180
+ return currentWindow != null
181
+ ? currentWindow.GetType().GetMethod("SaveToDisk", All, null, new[] { typeof(bool) }, null) : null;
182
+ }
183
+
184
+ internal static FieldInfo Field(Type type, params string[] names)
185
+ {
186
+ for (Type current = type; current != null; current = current.BaseType)
187
+ foreach (string name in names)
188
+ {
189
+ FieldInfo field = current.GetField(name, All);
190
+ if (field != null) return field;
191
+ }
192
+ return null;
193
+ }
194
+
195
+ private static object Member(object target, params string[] names)
196
+ {
197
+ Type type = target.GetType();
198
+ foreach (string name in names)
199
+ {
200
+ PropertyInfo property = type.GetProperty(name, All);
201
+ if (property != null) return property.GetValue(target, null);
202
+ FieldInfo field = Field(type, name);
203
+ if (field != null) return field.GetValue(target);
204
+ }
205
+ return null;
206
+ }
207
+
208
+ private static EditorWindow FindWindow()
209
+ {
210
+ foreach (EditorWindow window in Resources.FindObjectsOfTypeAll<EditorWindow>())
211
+ if (window != null && window.GetType().FullName == "AmplifyShaderEditor.AmplifyShaderEditorWindow")
212
+ return window;
213
+ return null;
214
+ }
215
+
216
+ private static bool IsPropertyNode(object node)
217
+ {
218
+ for (Type type = node != null ? node.GetType() : null; type != null; type = type.BaseType)
219
+ if (type.FullName == "AmplifyShaderEditor.PropertyNode") return true;
220
+ return false;
221
+ }
222
+
223
+ private static string Version()
224
+ {
225
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
226
+ {
227
+ Type type = assembly.GetType("AmplifyShaderEditor.VersionInfo", false);
228
+ MethodInfo method = type != null ? type.GetMethod("StaticToString", All) : null;
229
+ if (method != null) return method.Invoke(null, null) as string ?? "unknown";
230
+ }
231
+ return "not detected";
232
+ }
233
+ }
234
+ }
235
+ #endif
@@ -0,0 +1,125 @@
1
+ internal static bool TryMergeRaw(
2
+ UnityEngine.Object node, IEnumerable<string> source, out bool changed, out string error)
3
+ {
4
+ changed = false;
5
+ bool handled;
6
+ if (!ASENativeMzguiReconciler.TryMerge(
7
+ node, source, out handled, out changed, out error))
8
+ return false;
9
+ IList attributes;
10
+ FieldInfo listField;
11
+ FieldInfo countField;
12
+ if (!TryStorage(node, out attributes, out listField, out countField, out error))
13
+ return false;
14
+ HashSet<string> existing = new HashSet<string>(StringComparer.Ordinal);
15
+ foreach (object item in attributes)
16
+ {
17
+ Match match = Managed.Match(item as string ?? String.Empty);
18
+ if (match.Success) existing.Add(Canonical(match.Groups[1].Value));
19
+ }
20
+ foreach (string item in source)
21
+ {
22
+ string raw = (item ?? String.Empty).Trim();
23
+ if (raw.StartsWith("[", StringComparison.Ordinal) && raw.EndsWith("]", StringComparison.Ordinal))
24
+ raw = raw.Substring(1, raw.Length - 2);
25
+ Match match = Managed.Match(raw);
26
+ if (!match.Success || existing.Contains(Canonical(match.Groups[1].Value))) continue;
27
+ if (handled && Canonical(match.Groups[1].Value) != "EnableIfMzgui") continue;
28
+ raw = Canonical(match.Groups[1].Value) +
29
+ (match.Groups[2].Success ? "(" + match.Groups[2].Value + ")" : String.Empty);
30
+ if (!changed) Undo.RecordObject(node, "Restore ASE GUI attributes");
31
+ attributes.Add(raw);
32
+ existing.Add(Canonical(match.Groups[1].Value));
33
+ changed = true;
34
+ }
35
+ if (changed)
36
+ {
37
+ countField.SetValue(node, attributes.Count);
38
+ EditorUtility.SetDirty(node);
39
+ }
40
+ error = String.Empty;
41
+ return true;
42
+ }
43
+
44
+ internal static bool TryStorage(
45
+ object node, out IList values, out FieldInfo listField, out FieldInfo countField, out string error)
46
+ {
47
+ listField = ASEReflection.Field(node.GetType(), "m_customAttr", "m_customAttributes");
48
+ countField = ASEReflection.Field(node.GetType(), "m_customAttrCount", "m_customAttributesCount");
49
+ values = listField != null ? listField.GetValue(node) as IList : null;
50
+ if (values != null && countField != null)
51
+ {
52
+ error = String.Empty;
53
+ return true;
54
+ }
55
+ error = "当前 ASE 版本未暴露兼容的 Custom Attributes 存储;已停止写入";
56
+ return false;
57
+ }
58
+
59
+ private static string EncodeFoldout(string value)
60
+ {
61
+ StringBuilder output = new StringBuilder();
62
+ foreach (char c in value) output.Append(c <= 127 ? c.ToString() : "#" + ((int)c).ToString("x4"));
63
+ return output.ToString();
64
+ }
65
+
66
+ private static string EncodeUnicode(string value)
67
+ {
68
+ StringBuilder output = new StringBuilder();
69
+ foreach (char c in value) output.Append("#" + ((int)c).ToString("X4"));
70
+ return output.ToString();
71
+ }
72
+
73
+ private static string Decode(string value)
74
+ {
75
+ return Regex.Replace(value, @"#([0-9A-Fa-f]{4})", delegate(Match match)
76
+ {
77
+ return ((char)Convert.ToInt32(match.Groups[1].Value, 16)).ToString();
78
+ });
79
+ }
80
+
81
+ private static string Canonical(string value)
82
+ {
83
+ if (value == "ASECLIFoldout") return "FoldoutMzgui";
84
+ if (value == "ASECLITooltip") return "TooltipMzgui";
85
+ if (value == "ASECLIHelpBox") return "HelpBoxMzgui";
86
+ if (value == "ASECLIEnableIf") return "EnableIfMzgui";
87
+ return value;
88
+ }
89
+
90
+ internal static string FormatCondition(
91
+ string propertyName, int operatorIndex, float value)
92
+ {
93
+ string comparison = operatorIndex >= 0 && operatorIndex < EnabledIfOperators.Length
94
+ ? EnabledIfOperators[operatorIndex] : "Equal";
95
+ return propertyName + "," + comparison + "," +
96
+ value.ToString("R", CultureInfo.InvariantCulture);
97
+ }
98
+
99
+ internal static bool TryParseCondition(
100
+ string raw, out string propertyName, out int operatorIndex,
101
+ out float value, out string error)
102
+ {
103
+ propertyName = String.Empty;
104
+ operatorIndex = 2;
105
+ value = 0f;
106
+ string[] parts = (raw ?? String.Empty).Split(',');
107
+ if (parts.Length != 3 ||
108
+ !Regex.IsMatch(parts[0].Trim(), @"^_[A-Za-z][A-Za-z0-9_]{0,127}$"))
109
+ {
110
+ error = "条件启用需要有效的控制属性、比较方式和数值";
111
+ return false;
112
+ }
113
+ propertyName = parts[0].Trim();
114
+ operatorIndex = Array.IndexOf(EnabledIfOperators, parts[1].Trim());
115
+ if (operatorIndex < 0 || !Single.TryParse(
116
+ parts[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture,
117
+ out value) || Single.IsNaN(value) || Single.IsInfinity(value))
118
+ {
119
+ error = "条件启用的比较方式或数值无效";
120
+ return false;
121
+ }
122
+ error = String.Empty;
123
+ return true;
124
+ }
125
+ }