359 lines
13 KiB
C#
359 lines
13 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using Byway.Thrift.Data;
|
||
using Thrift.Protocol;
|
||
using Thrift.Transport.Client;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
namespace FontTools
|
||
{
|
||
/// <summary>
|
||
/// 语言字符集生成器
|
||
/// 从 AllConfigs 读取 LanguageData 表,提取三种语言的字符集并保存为txt
|
||
/// </summary>
|
||
public class LanguageCharsetGenerator : EditorWindow
|
||
{
|
||
private const string DEFAULT_OUTPUT_PATH = "Assets/Design_SubModule/Scripts/Editor/ThriftPipelineTool/CharacterSets";
|
||
|
||
private string outputPath = DEFAULT_OUTPUT_PATH;
|
||
private TextAsset allConfigsFile;
|
||
private Vector2 scrollPosition;
|
||
private string statusLog = "";
|
||
private bool isProcessing = false;
|
||
|
||
private Dictionary<string, int> charCounts = new Dictionary<string, int>();
|
||
|
||
[MenuItem("配置表pipeline/字体/生成字符集")]
|
||
public static void ShowWindow()
|
||
{
|
||
var window = GetWindow<LanguageCharsetGenerator>("字符集生成器");
|
||
window.minSize = new Vector2(600, 500);
|
||
window.Show();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
// 自动查找 AllConfigs.bytes
|
||
AutoFindAllConfigs();
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
EditorGUILayout.Space(10);
|
||
EditorGUILayout.LabelField("语言字符集生成器", EditorStyles.boldLabel);
|
||
EditorGUILayout.HelpBox(
|
||
"从 AllConfigs.bytes 读取 LanguageData 表\n" +
|
||
"提取 En_US、Zh_CN、Pt_BR 三个语言列的所有字符(去重)\n" +
|
||
"生成三个字符集txt文件",
|
||
MessageType.Info
|
||
);
|
||
|
||
EditorGUILayout.Space(10);
|
||
|
||
// AllConfigs 文件选择
|
||
EditorGUILayout.BeginVertical("box");
|
||
EditorGUILayout.LabelField("1. 选择 AllConfigs.bytes 文件:", EditorStyles.boldLabel);
|
||
EditorGUILayout.BeginHorizontal();
|
||
allConfigsFile = EditorGUILayout.ObjectField("AllConfigs:", allConfigsFile, typeof(TextAsset), false) as TextAsset;
|
||
if (GUILayout.Button("自动查找", GUILayout.Width(80)))
|
||
{
|
||
AutoFindAllConfigs();
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.EndVertical();
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// 输出路径选择
|
||
EditorGUILayout.BeginVertical("box");
|
||
EditorGUILayout.LabelField("2. 选择输出目录:", EditorStyles.boldLabel);
|
||
EditorGUILayout.BeginHorizontal();
|
||
EditorGUILayout.TextField(outputPath);
|
||
if (GUILayout.Button("浏览", GUILayout.Width(60)))
|
||
{
|
||
string selected = EditorUtility.OpenFolderPanel("选择输出目录", outputPath, "");
|
||
if (!string.IsNullOrEmpty(selected))
|
||
{
|
||
// 转换为相对路径
|
||
if (selected.StartsWith(Application.dataPath))
|
||
{
|
||
outputPath = "Assets" + selected.Substring(Application.dataPath.Length);
|
||
}
|
||
else
|
||
{
|
||
outputPath = selected;
|
||
}
|
||
}
|
||
}
|
||
if (GUILayout.Button("重置", GUILayout.Width(60)))
|
||
{
|
||
outputPath = DEFAULT_OUTPUT_PATH;
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.EndVertical();
|
||
|
||
EditorGUILayout.Space(10);
|
||
|
||
// 生成按钮
|
||
GUI.enabled = !isProcessing && allConfigsFile != null;
|
||
if (GUILayout.Button("生成字符集", GUILayout.Height(40)))
|
||
{
|
||
GenerateCharsets();
|
||
}
|
||
GUI.enabled = true;
|
||
|
||
EditorGUILayout.Space(10);
|
||
|
||
// 统计信息
|
||
if (charCounts.Count > 0)
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
EditorGUILayout.LabelField("字符统计:", EditorStyles.boldLabel);
|
||
foreach (var kvp in charCounts.OrderBy(x => x.Key))
|
||
{
|
||
EditorGUILayout.LabelField($" {kvp.Key}: {kvp.Value} 个字符");
|
||
}
|
||
EditorGUILayout.EndVertical();
|
||
EditorGUILayout.Space(5);
|
||
}
|
||
|
||
// 日志区域
|
||
EditorGUILayout.LabelField("执行日志:", EditorStyles.boldLabel);
|
||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, "box", GUILayout.Height(200));
|
||
EditorGUILayout.TextArea(statusLog, EditorStyles.wordWrappedLabel);
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自动查找 AllConfigs.bytes
|
||
/// </summary>
|
||
private void AutoFindAllConfigs()
|
||
{
|
||
string[] guids = AssetDatabase.FindAssets("AllConfigs t:TextAsset");
|
||
foreach (string guid in guids)
|
||
{
|
||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||
if (path.EndsWith(".bytes", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
allConfigsFile = AssetDatabase.LoadAssetAtPath<TextAsset>(path);
|
||
if (allConfigsFile != null)
|
||
{
|
||
Log($"✓ 自动找到 AllConfigs: {path}");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
Log("⚠ 未找到 AllConfigs.bytes,请手动选择");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成字符集
|
||
/// </summary>
|
||
private void GenerateCharsets()
|
||
{
|
||
isProcessing = true;
|
||
statusLog = "";
|
||
charCounts.Clear();
|
||
|
||
try
|
||
{
|
||
Log("========================================");
|
||
Log("开始生成字符集");
|
||
Log("========================================\n");
|
||
|
||
// 步骤1: 反序列化 AllConfigs
|
||
Log("[步骤1] 读取 AllConfigs.bytes...");
|
||
var allConfigs = DeserializeAllConfigs(allConfigsFile.bytes);
|
||
if (allConfigs == null)
|
||
{
|
||
LogError("✗ 反序列化 AllConfigs 失败!");
|
||
return;
|
||
}
|
||
Log("✓ AllConfigs 读取成功\n");
|
||
|
||
// 步骤2: 获取 LanguageData
|
||
Log("[步骤2] 提取 LanguageData...");
|
||
var languageData = allConfigs.LanguageData;
|
||
if (languageData == null || languageData.Languagedatas == null)
|
||
{
|
||
LogError("✗ LanguageData 不存在或为空!");
|
||
return;
|
||
}
|
||
Log($"✓ 找到 {languageData.Languagedatas.Count} 条语言数据\n");
|
||
|
||
// 步骤3: 提取字符集
|
||
Log("[步骤3] 提取字符集...");
|
||
var charsets = ExtractCharsets(languageData);
|
||
|
||
// 步骤4: 保存txt文件
|
||
Log("\n[步骤4] 保存txt文件...");
|
||
SaveCharsets(charsets);
|
||
|
||
Log("\n========================================");
|
||
Log("✓ 字符集生成完成!");
|
||
Log($"输出目录: {outputPath}");
|
||
Log("========================================");
|
||
|
||
EditorUtility.DisplayDialog("完成",
|
||
$"字符集生成成功!\n\n" +
|
||
$"En_US: {charCounts["En_US"]} 个字符\n" +
|
||
$"Zh_CN: {charCounts["Zh_CN"]} 个字符\n" +
|
||
$"Pt_BR: {charCounts["Pt_BR"]} 个字符\n\n" +
|
||
$"文件保存在: {outputPath}",
|
||
"确定");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogError($"✗ 生成失败: {ex.Message}\n{ex.StackTrace}");
|
||
EditorUtility.DisplayDialog("错误", $"生成失败:\n{ex.Message}", "确定");
|
||
}
|
||
finally
|
||
{
|
||
isProcessing = false;
|
||
Repaint();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 反序列化 AllConfigs
|
||
/// </summary>
|
||
private AllConfigs DeserializeAllConfigs(byte[] bytes)
|
||
{
|
||
try
|
||
{
|
||
using (var transport = new TMemoryBufferTransport(bytes, new Thrift.TConfiguration()))
|
||
using (var protocol = new TBinaryProtocol(transport))
|
||
{
|
||
var allConfigs = new AllConfigs();
|
||
allConfigs.ReadAsync(protocol, System.Threading.CancellationToken.None).Wait();
|
||
return allConfigs;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"反序列化失败: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从 LanguageData 提取字符集
|
||
/// </summary>
|
||
private Dictionary<string, HashSet<char>> ExtractCharsets(LanguageData languageData)
|
||
{
|
||
var charsets = new Dictionary<string, HashSet<char>>
|
||
{
|
||
{ "En_US", new HashSet<char>() },
|
||
{ "Zh_CN", new HashSet<char>() },
|
||
{ "Pt_BR", new HashSet<char>() }
|
||
};
|
||
|
||
int processedCount = 0;
|
||
int totalCount = languageData.Languagedatas.Count;
|
||
|
||
// 遍历所有行
|
||
foreach (var kvp in languageData.Languagedatas)
|
||
{
|
||
var item = kvp.Value;
|
||
|
||
// 提取每个语言列的字符
|
||
AddChars(charsets["En_US"], item.En_US);
|
||
AddChars(charsets["Zh_CN"], item.Zh_CN);
|
||
AddChars(charsets["Pt_BR"], item.Pt_BR);
|
||
|
||
processedCount++;
|
||
if (processedCount % 100 == 0)
|
||
{
|
||
Log($" 处理进度: {processedCount}/{totalCount}");
|
||
}
|
||
}
|
||
|
||
// 确保所有字符集都包含下划线和省略号
|
||
foreach (var charset in charsets.Values)
|
||
{
|
||
charset.Add('_'); // 下划线
|
||
charset.Add('…'); // 省略号
|
||
}
|
||
|
||
// 英文字符集额外添加美元符号
|
||
charsets["En_US"].Add('$');
|
||
|
||
// 统计
|
||
charCounts["En_US"] = charsets["En_US"].Count;
|
||
charCounts["Zh_CN"] = charsets["Zh_CN"].Count;
|
||
charCounts["Pt_BR"] = charsets["Pt_BR"].Count;
|
||
|
||
Log($"✓ En_US: {charCounts["En_US"]} 个字符");
|
||
Log($"✓ Zh_CN: {charCounts["Zh_CN"]} 个字符");
|
||
Log($"✓ Pt_BR: {charCounts["Pt_BR"]} 个字符");
|
||
|
||
return charsets;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加字符到集合(排除控制字符)
|
||
/// </summary>
|
||
private void AddChars(HashSet<char> charset, string text)
|
||
{
|
||
if (string.IsNullOrEmpty(text))
|
||
return;
|
||
|
||
foreach (char c in text)
|
||
{
|
||
if (!char.IsControl(c)) // 排除换行符、制表符等控制字符
|
||
{
|
||
charset.Add(c);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存字符集到txt文件
|
||
/// </summary>
|
||
private void SaveCharsets(Dictionary<string, HashSet<char>> charsets)
|
||
{
|
||
// 创建输出目录
|
||
if (!Directory.Exists(outputPath))
|
||
{
|
||
Directory.CreateDirectory(outputPath);
|
||
Log($"✓ 创建目录: {outputPath}");
|
||
}
|
||
|
||
foreach (var kvp in charsets)
|
||
{
|
||
string langName = kvp.Key;
|
||
var chars = kvp.Value;
|
||
|
||
// 按 Unicode 排序
|
||
var sortedChars = chars.OrderBy(c => (int)c).ToArray();
|
||
string content = new string(sortedChars);
|
||
|
||
// 保存文件
|
||
string filePath = Path.Combine(outputPath, $"{langName}_charset.txt");
|
||
File.WriteAllText(filePath, content, System.Text.Encoding.UTF8);
|
||
|
||
Log($"✓ {langName}: {filePath} ({chars.Count} 字符)");
|
||
}
|
||
|
||
// 刷新 AssetDatabase
|
||
AssetDatabase.Refresh();
|
||
}
|
||
|
||
private void Log(string message)
|
||
{
|
||
statusLog += message + "\n";
|
||
Repaint();
|
||
}
|
||
|
||
private void LogError(string message)
|
||
{
|
||
statusLog += $"<color=red>{message}</color>\n";
|
||
Debug.LogError(message);
|
||
Repaint();
|
||
}
|
||
}
|
||
}
|