using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System.Linq; using Thrift; using Thrift.Protocol; using Thrift.Transport; using Thrift.Transport.Client; using Byway.Thrift.Data; namespace ArtTools { /// /// 美术资源Bytes文件查看器 /// 用于查看ArtResourceConfig.bytes和ArtAtlasConfig.bytes的内容 /// public class ArtBytesViewer : EditorWindow { private enum ViewMode { ArtResourceConfig, AtlasConfig } private ViewMode currentMode = ViewMode.ArtResourceConfig; private Vector2 scrollPosition; private Vector2 tableListScrollPosition; private Vector2 itemListScrollPosition; // ArtResourceConfig相关 private ArtResourceConfig artConfig; private int selectedTableIndex = -1; private string tableSearchText = ""; private string itemSearchText = ""; // AtlasConfig相关 private ArtAtlasConfig atlasConfig; private int selectedAtlasIndex = -1; private string atlasSearchText = ""; // 文件信息 private string artConfigPath = "Assets/Art_SubModule/Art_Bytes/ArtResourceConfig.bytes"; private string atlasConfigPath = "Assets/Art_SubModule/Art_Bytes/ArtAtlasConfig.bytes"; private FileInfo artConfigFileInfo; private FileInfo atlasConfigFileInfo; [MenuItem("美术工具/Thrift/Bytes查看器")] public static void ShowWindow() { var window = GetWindow("美术资源Bytes查看器"); window.minSize = new Vector2(800, 600); window.Show(); } private void OnEnable() { LoadBytesFiles(); } private void OnGUI() { EditorGUILayout.BeginVertical(); // 顶部工具栏 DrawToolbar(); EditorGUILayout.Space(5); // 根据模式显示不同内容 if (currentMode == ViewMode.ArtResourceConfig) { DrawArtResourceConfigView(); } else { DrawAtlasConfigView(); } EditorGUILayout.EndVertical(); } private void DrawToolbar() { EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); // 模式切换 if (GUILayout.Toggle(currentMode == ViewMode.ArtResourceConfig, "美术资源配置", EditorStyles.toolbarButton)) { if (currentMode != ViewMode.ArtResourceConfig) { currentMode = ViewMode.ArtResourceConfig; selectedTableIndex = -1; } } if (GUILayout.Toggle(currentMode == ViewMode.AtlasConfig, "图集配置", EditorStyles.toolbarButton)) { if (currentMode != ViewMode.AtlasConfig) { currentMode = ViewMode.AtlasConfig; selectedAtlasIndex = -1; } } GUILayout.FlexibleSpace(); // 刷新按钮 if (GUILayout.Button("🔄 刷新", EditorStyles.toolbarButton, GUILayout.Width(60))) { LoadBytesFiles(); } EditorGUILayout.EndHorizontal(); } private void LoadBytesFiles() { // 加载ArtResourceConfig try { string fullArtPath = Path.Combine(Application.dataPath, "..", artConfigPath); if (File.Exists(fullArtPath)) { byte[] bytes = File.ReadAllBytes(fullArtPath); artConfigFileInfo = new FileInfo(fullArtPath); using (var transport = new TMemoryBufferTransport(bytes, new TConfiguration())) { using (var protocol = new TBinaryProtocol(transport)) { artConfig = new ArtResourceConfig(); artConfig.ReadAsync(protocol, System.Threading.CancellationToken.None).GetAwaiter().GetResult(); } } Debug.Log($"[ArtBytesViewer] 成功加载 ArtResourceConfig.bytes ({bytes.Length / 1024f:F2} KB, {artConfig.Tables.Count} 个表)"); } else { Debug.LogWarning($"[ArtBytesViewer] 找不到文件: {fullArtPath}"); } } catch (System.Exception ex) { Debug.LogError($"[ArtBytesViewer] 加载ArtResourceConfig失败: {ex.Message}"); } // 加载AtlasConfig try { string fullAtlasPath = Path.Combine(Application.dataPath, "..", atlasConfigPath); if (File.Exists(fullAtlasPath)) { byte[] bytes = File.ReadAllBytes(fullAtlasPath); atlasConfigFileInfo = new FileInfo(fullAtlasPath); using (var transport = new TMemoryBufferTransport(bytes, new TConfiguration())) { using (var protocol = new TBinaryProtocol(transport)) { atlasConfig = new ArtAtlasConfig(); atlasConfig.ReadAsync(protocol, System.Threading.CancellationToken.None).GetAwaiter().GetResult(); } } Debug.Log($"[ArtBytesViewer] 成功加载 ArtAtlasConfig.bytes ({bytes.Length / 1024f:F2} KB, {atlasConfig.Atlases.Count} 个图集)"); } else { Debug.LogWarning($"[ArtBytesViewer] 找不到文件: {fullAtlasPath}"); } } catch (System.Exception ex) { Debug.LogError($"[ArtBytesViewer] 加载AtlasConfig失败: {ex.Message}"); } } private void DrawArtResourceConfigView() { if (artConfig == null) { EditorGUILayout.HelpBox("未加载ArtResourceConfig.bytes\n请确保文件存在: " + artConfigPath, MessageType.Warning); return; } // 文件信息 EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField("文件信息", EditorStyles.boldLabel); EditorGUILayout.LabelField($"路径: {artConfigPath}"); if (artConfigFileInfo != null) { EditorGUILayout.LabelField($"大小: {artConfigFileInfo.Length / 1024f:F2} KB"); EditorGUILayout.LabelField($"修改时间: {artConfigFileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss}"); } EditorGUILayout.LabelField($"表数量: {artConfig.Tables.Count}"); EditorGUILayout.LabelField($"版本: {artConfig.Version}"); if (!string.IsNullOrEmpty(artConfig.GenerateTime)) { EditorGUILayout.LabelField($"生成时间: {artConfig.GenerateTime}"); } if (artConfig.PreloadTableIds != null && artConfig.PreloadTableIds.Count > 0) { EditorGUILayout.LabelField($"预加载表: {string.Join(", ", artConfig.PreloadTableIds)}"); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); // 两栏布局 EditorGUILayout.BeginHorizontal(); // 左侧:表列表 EditorGUILayout.BeginVertical(GUILayout.Width(250)); EditorGUILayout.LabelField("资源表列表", EditorStyles.boldLabel); // 搜索框 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("搜索:", GUILayout.Width(40)); tableSearchText = EditorGUILayout.TextField(tableSearchText); EditorGUILayout.EndHorizontal(); tableListScrollPosition = EditorGUILayout.BeginScrollView(tableListScrollPosition, EditorStyles.helpBox); var filteredTables = artConfig.Tables; if (!string.IsNullOrEmpty(tableSearchText)) { filteredTables = artConfig.Tables.Where(t => t.TableName.ToLower().Contains(tableSearchText.ToLower()) || t.TableId.ToString().Contains(tableSearchText) ).ToList(); } for (int i = 0; i < filteredTables.Count; i++) { var table = filteredTables[i]; bool isSelected = selectedTableIndex == artConfig.Tables.IndexOf(table); var style = new GUIStyle(GUI.skin.button); style.alignment = TextAnchor.MiddleLeft; if (isSelected) { style.normal.background = Texture2D.grayTexture; } if (GUILayout.Button($"[{table.TableId}] {table.TableName} ({table.Items.Count})", style)) { selectedTableIndex = artConfig.Tables.IndexOf(table); } } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); // 右侧:详细信息 EditorGUILayout.BeginVertical(); if (selectedTableIndex >= 0 && selectedTableIndex < artConfig.Tables.Count) { var selectedTable = artConfig.Tables[selectedTableIndex]; EditorGUILayout.LabelField($"表: {selectedTable.TableName} (ID: {selectedTable.TableId})", EditorStyles.boldLabel); EditorGUILayout.LabelField($"资源项数量: {selectedTable.Items.Count}"); EditorGUILayout.Space(5); // 资源项搜索 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("筛选:", GUILayout.Width(40)); itemSearchText = EditorGUILayout.TextField(itemSearchText); EditorGUILayout.EndHorizontal(); // 资源项列表 itemListScrollPosition = EditorGUILayout.BeginScrollView(itemListScrollPosition); var filteredItems = selectedTable.Items; if (!string.IsNullOrEmpty(itemSearchText)) { filteredItems = selectedTable.Items.Where(item => item.Name.ToLower().Contains(itemSearchText.ToLower()) || item.Id.ToString().Contains(itemSearchText) || (!string.IsNullOrEmpty(item.Desc) && item.Desc.ToLower().Contains(itemSearchText.ToLower())) ).ToList(); } // 表头 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); EditorGUILayout.LabelField("ID", EditorStyles.boldLabel, GUILayout.Width(50)); EditorGUILayout.LabelField("名称", EditorStyles.boldLabel, GUILayout.Width(150)); EditorGUILayout.LabelField("描述", EditorStyles.boldLabel, GUILayout.Width(200)); EditorGUILayout.LabelField("Sprite", EditorStyles.boldLabel, GUILayout.Width(80)); EditorGUILayout.LabelField("Spine", EditorStyles.boldLabel, GUILayout.Width(80)); EditorGUILayout.EndHorizontal(); foreach (var item in filteredItems) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(item.Id.ToString(), GUILayout.Width(50)); EditorGUILayout.LabelField(item.Name, GUILayout.Width(150)); EditorGUILayout.LabelField(item.Desc ?? "", GUILayout.Width(200)); EditorGUILayout.LabelField(string.IsNullOrEmpty(item.SpritePath) ? "-" : "✓", GUILayout.Width(80)); EditorGUILayout.LabelField(string.IsNullOrEmpty(item.SpineAssetPath) ? "-" : "✓", GUILayout.Width(80)); EditorGUILayout.EndHorizontal(); // 详细路径信息 if (!string.IsNullOrEmpty(item.SpritePath)) { EditorGUI.indentLevel++; EditorGUILayout.LabelField("Sprite路径:", item.SpritePath, EditorStyles.wordWrappedLabel); EditorGUI.indentLevel--; } if (!string.IsNullOrEmpty(item.SpineAssetPath)) { EditorGUI.indentLevel++; EditorGUILayout.LabelField("Spine路径:", item.SpineAssetPath, EditorStyles.wordWrappedLabel); if (!string.IsNullOrEmpty(item.SpineAnimName)) { EditorGUILayout.LabelField("动画名:", item.SpineAnimName); } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); } EditorGUILayout.EndScrollView(); EditorGUILayout.LabelField($"显示: {filteredItems.Count} / {selectedTable.Items.Count} 项"); } else { EditorGUILayout.HelpBox("请在左侧选择一个资源表", MessageType.Info); } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } private void DrawAtlasConfigView() { if (atlasConfig == null) { EditorGUILayout.HelpBox("未加载ArtAtlasConfig.bytes\n请确保文件存在: " + atlasConfigPath, MessageType.Warning); return; } // 文件信息 EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField("文件信息", EditorStyles.boldLabel); EditorGUILayout.LabelField($"路径: {atlasConfigPath}"); if (atlasConfigFileInfo != null) { EditorGUILayout.LabelField($"大小: {atlasConfigFileInfo.Length / 1024f:F2} KB"); EditorGUILayout.LabelField($"修改时间: {atlasConfigFileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss}"); } EditorGUILayout.LabelField($"图集数量: {atlasConfig.Atlases.Count}"); EditorGUILayout.LabelField($"版本: {atlasConfig.Version}"); if (!string.IsNullOrEmpty(atlasConfig.GenerateTime)) { EditorGUILayout.LabelField($"生成时间: {atlasConfig.GenerateTime}"); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); // 两栏布局 EditorGUILayout.BeginHorizontal(); // 左侧:图集列表 EditorGUILayout.BeginVertical(GUILayout.Width(250)); EditorGUILayout.LabelField("图集列表", EditorStyles.boldLabel); // 搜索框 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("搜索:", GUILayout.Width(40)); atlasSearchText = EditorGUILayout.TextField(atlasSearchText); EditorGUILayout.EndHorizontal(); tableListScrollPosition = EditorGUILayout.BeginScrollView(tableListScrollPosition, EditorStyles.helpBox); var filteredAtlases = atlasConfig.Atlases; if (!string.IsNullOrEmpty(atlasSearchText)) { filteredAtlases = atlasConfig.Atlases.Where(a => a.Name.ToLower().Contains(atlasSearchText.ToLower()) ).ToList(); } for (int i = 0; i < filteredAtlases.Count; i++) { var atlas = filteredAtlases[i]; bool isSelected = selectedAtlasIndex == atlasConfig.Atlases.IndexOf(atlas); var style = new GUIStyle(GUI.skin.button); style.alignment = TextAnchor.MiddleLeft; if (isSelected) { style.normal.background = Texture2D.grayTexture; } if (GUILayout.Button($"{atlas.Name} ({atlas.SpritePaths.Count})", style)) { selectedAtlasIndex = atlasConfig.Atlases.IndexOf(atlas); } } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); // 右侧:详细信息 EditorGUILayout.BeginVertical(); if (selectedAtlasIndex >= 0 && selectedAtlasIndex < atlasConfig.Atlases.Count) { var selectedAtlas = atlasConfig.Atlases[selectedAtlasIndex]; EditorGUILayout.LabelField($"图集: {selectedAtlas.Name}", EditorStyles.boldLabel); EditorGUILayout.LabelField($"Sprite数量: {selectedAtlas.SpritePaths.Count}"); EditorGUILayout.Space(5); // Sprite路径列表 itemListScrollPosition = EditorGUILayout.BeginScrollView(itemListScrollPosition); EditorGUILayout.LabelField("包含的Sprite路径:", EditorStyles.boldLabel); int index = 1; foreach (var spritePath in selectedAtlas.SpritePaths) { EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); EditorGUILayout.LabelField(index.ToString(), GUILayout.Width(30)); EditorGUILayout.LabelField(spritePath, EditorStyles.wordWrappedLabel); EditorGUILayout.EndHorizontal(); index++; } EditorGUILayout.EndScrollView(); } else { EditorGUILayout.HelpBox("请在左侧选择一个图集", MessageType.Info); } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } } }