From 5548bd38735e90a0853a2a968112586b2da72b2f Mon Sep 17 00:00:00 2001 From: jie65535 Date: Sun, 12 Apr 2026 11:49:10 +0800 Subject: [PATCH] Improve TextMap loading to support multiple files --- .../Forms/FormTextMapBrowser.cs | 32 +++++- .../Game/Data/GameResources.cs | 10 +- Source/GrasscutterTools/Game/TextMapData.cs | 104 +++++++++++++++--- Source/GrasscutterTools/Pages/PageTools.cs | 35 +++--- 4 files changed, 138 insertions(+), 43 deletions(-) diff --git a/Source/GrasscutterTools/Forms/FormTextMapBrowser.cs b/Source/GrasscutterTools/Forms/FormTextMapBrowser.cs index 540df2d..97fe5f4 100644 --- a/Source/GrasscutterTools/Forms/FormTextMapBrowser.cs +++ b/Source/GrasscutterTools/Forms/FormTextMapBrowser.cs @@ -30,6 +30,14 @@ namespace GrasscutterTools.Forms { public partial class FormTextMapBrowser : Form { + private static readonly Dictionary LanguageDisplayNames = new Dictionary + { + ["zh-cn"] = "简体中文", + ["zh-tw"] = "繁體中文", + ["en-us"] = "English", + ["ru-ru"] = "Русский", + }; + public FormTextMapBrowser() { InitializeComponent(); @@ -61,12 +69,21 @@ namespace GrasscutterTools.Forms } CmbLanguage.Items.Clear(); - CmbLanguage.Items.AddRange(data.TextMapFiles); + foreach (var lang in LanguageDisplayNames) + { + CmbLanguage.Items.Add($"{lang.Value} ({lang.Key})"); + } if (!string.IsNullOrEmpty(Settings.Default.TextMapFileName)) { var i = CmbLanguage.Items.IndexOf(Settings.Default.TextMapFileName); if (i != -1) CmbLanguage.SelectedIndex = i; + else + CmbLanguage.SelectedIndex = 0; + } + else + { + CmbLanguage.SelectedIndex = 0; } } catch (Exception ex) @@ -100,10 +117,17 @@ namespace GrasscutterTools.Forms { Cursor = Cursors.WaitCursor; Application.DoEvents(); - data.LoadTextMap(data.TextMapFilePaths[CmbLanguage.SelectedIndex]); - GenLines(); - Settings.Default.TextMapFileName = CmbLanguage.Text; + // 从显示名称中提取语言代码 + var selectedText = CmbLanguage.Text; + var languageCode = LanguageDisplayNames.FirstOrDefault(kv => selectedText.Contains($"{kv.Value} ({kv.Key}")).Key; + + if (!string.IsNullOrEmpty(languageCode)) + { + data.LoadTextMapByLanguage(languageCode); + GenLines(); + Settings.Default.TextMapFileName = selectedText; + } } catch (Exception ex) { diff --git a/Source/GrasscutterTools/Game/Data/GameResources.cs b/Source/GrasscutterTools/Game/Data/GameResources.cs index f665af2..681e555 100644 --- a/Source/GrasscutterTools/Game/Data/GameResources.cs +++ b/Source/GrasscutterTools/Game/Data/GameResources.cs @@ -161,10 +161,10 @@ namespace GrasscutterTools.Game.Data private Dictionary Languages = new Dictionary { - ["zh-cn"] = "TextMapCHS", - ["zh-tw"] = "TextMapCHT", - ["en-us"] = "TextMapEN", - // ["ru-ru"] = "TextMapRU", + ["zh-cn"] = "zh-cn", + ["zh-tw"] = "zh-tw", + ["en-us"] = "en-us", + ["ru-ru"] = "ru-ru", }; public void ConvertResources(string projectResourcesDir) @@ -176,7 +176,7 @@ namespace GrasscutterTools.Game.Data foreach (var language in Languages) { var dir = Path.Combine(projectResourcesDir, language.Key); - TextMapData.LoadTextMap(TextMapData.TextMapFilePaths[Array.IndexOf(TextMapData.TextMapFiles, language.Value)]); + TextMapData.LoadTextMapByLanguage(language.Value); Thread.CurrentThread.CurrentUICulture = new CultureInfo(language.Key); GameData.LoadResources(); diff --git a/Source/GrasscutterTools/Game/TextMapData.cs b/Source/GrasscutterTools/Game/TextMapData.cs index 22099e9..e019f46 100644 --- a/Source/GrasscutterTools/Game/TextMapData.cs +++ b/Source/GrasscutterTools/Game/TextMapData.cs @@ -28,11 +28,24 @@ namespace GrasscutterTools.Game { internal class TextMapData { + // 语言代码到TextMap文件标识符的映射 + // 匹配规则:文件名必须包含标识符,如 TextMapCHS.json, TextMap_MediumCHS.json, TextMapRU_0.json 等 + private static readonly Dictionary LanguageIdentifiers = new Dictionary + { + ["zh-cn"] = new[] { "CHS" }, + ["zh-tw"] = new[] { "CHT" }, + ["en-us"] = new[] { "EN" }, + ["ru-ru"] = new[] { "RU" }, + }; + + private string _textMapDirPath; + public TextMapData(string resourcesDirPath) { LoadManualTextMap(Path.Combine(resourcesDirPath, "ExcelBinOutput", "ManualTextMapConfigData.json")); - LoadTextMaps(Path.Combine(resourcesDirPath, "TextMap")); - LoadTextMap(TextMapFilePaths[Array.IndexOf(TextMapFiles, "TextMapCHS")]); + _textMapDirPath = Path.Combine(resourcesDirPath, "TextMap"); + LoadTextMaps(_textMapDirPath); + LoadTextMapByLanguage("zh-cn"); DefaultTextMap = TextMap; } @@ -84,24 +97,89 @@ namespace GrasscutterTools.Game TextMapFiles = TextMapFilePaths.Select(n => Path.GetFileNameWithoutExtension(n)).ToArray(); } - public void LoadTextMap(string textMapPath) + /// + /// 根据语言代码加载TextMap,自动匹配并合并所有相关文件 + /// 匹配规则:文件名以"TextMap"开头,并包含对应的语言标识符 + /// 例如:TextMapCHS.json, TextMap_MediumCHS.json, TextMapRU_0.json, TextMapRU_1.json + /// + /// 语言代码,如 "zh-cn", "en-us", "ru-ru" + public void LoadTextMapByLanguage(string languageCode) { - using (var fs = File.OpenRead(textMapPath)) - using (var sr = new StreamReader(fs)) - using (var reader = new JsonTextReader(sr)) + TextMap = new Dictionary(); + + if (!LanguageIdentifiers.ContainsKey(languageCode)) { - TextMap = new Dictionary(); - while (reader.Read()) - { - if (reader.TokenType == JsonToken.PropertyName) + Console.WriteLine($"Warning: Language code '{languageCode}' not found in LanguageIdentifiers, falling back to zh-cn"); + languageCode = "zh-cn"; + } + + var identifiers = LanguageIdentifiers[languageCode]; + var loadedFiles = new List(); + + foreach (var identifier in identifiers) + { + // 查找所有包含该标识符的文件 + // 例如:TextMap*CHS*.json 会匹配 TextMapCHS.json, TextMap_MediumCHS.json 等 + var matchingFiles = TextMapFilePaths + .Where(f => { - TextMap.Add((string)reader.Value, reader.ReadAsString()); - } + var fileNameWithoutExt = Path.GetFileNameWithoutExtension(f); + return fileNameWithoutExt.StartsWith("TextMap") && + fileNameWithoutExt.Contains(identifier); + }) + .OrderBy(f => f) // 按文件名排序 + .ToArray(); + + foreach (var file in matchingFiles) + { + if (loadedFiles.Contains(file)) + continue; + + LoadTextMapFile(file, TextMap); + loadedFiles.Add(file); } } + + if (loadedFiles.Count == 0) + { + Console.WriteLine($"Warning: No TextMap files found for language '{languageCode}' with identifiers: {string.Join(", ", identifiers)}"); + } + else + { + Console.WriteLine($"Loaded {loadedFiles.Count} TextMap file(s) for language '{languageCode}': {string.Join(", ", loadedFiles.Select(Path.GetFileName))}"); + } + } + + /// + /// 从单个文件加载TextMap并合并到目标字典 + /// + private void LoadTextMapFile(string filePath, Dictionary targetDict) + { + try + { + using (var fs = File.OpenRead(filePath)) + using (var sr = new StreamReader(fs)) + using (var reader = new JsonTextReader(sr)) + { + while (reader.Read()) + { + if (reader.TokenType == JsonToken.PropertyName) + { + var key = (string)reader.Value; + var value = reader.ReadAsString(); + + // 如果key已存在,新值覆盖旧值(后加载的文件优先) + targetDict[key] = value; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error loading TextMap file '{filePath}': {ex.Message}"); + } } - public bool Contains(string textMapPath) => TextMap.ContainsKey(textMapPath) || DefaultTextMap.ContainsKey(textMapPath); public string GetText(string textMapHash) { diff --git a/Source/GrasscutterTools/Pages/PageTools.cs b/Source/GrasscutterTools/Pages/PageTools.cs index a7dcb8b..07cd70a 100644 --- a/Source/GrasscutterTools/Pages/PageTools.cs +++ b/Source/GrasscutterTools/Pages/PageTools.cs @@ -155,10 +155,10 @@ namespace GrasscutterTools.Pages TextMapData ??= new TextMapData(TxtGcResRoot.Text); - UpdateActivityForLanguage(activityItems, "TextMapCHS", "zh-cn"); - UpdateActivityForLanguage(activityItems, "TextMapCHT", "zh-tw"); - UpdateActivityForLanguage(activityItems, "TextMapEN", "en-us"); - // UpdateActivityForLanguage(activityItems, "TextMapRU", "ru-ru"); + UpdateActivityForLanguage(activityItems, "zh-cn"); + UpdateActivityForLanguage(activityItems, "zh-tw"); + UpdateActivityForLanguage(activityItems, "en-us"); + UpdateActivityForLanguage(activityItems, "ru-ru"); MessageBox.Show("OK", Resources.Tips, MessageBoxButtons.OK); } catch (Exception ex) @@ -167,10 +167,9 @@ namespace GrasscutterTools.Pages } } - private void UpdateActivityForLanguage(IReadOnlyCollection activityItems, string textMap, string language) + private void UpdateActivityForLanguage(IReadOnlyCollection activityItems, string languageCode) { - var i = Array.IndexOf(TextMapData.TextMapFiles, textMap); - TextMapData.LoadTextMap(TextMapData.TextMapFilePaths[i]); + TextMapData.LoadTextMapByLanguage(languageCode); var activityMap = new Dictionary(activityItems.Count); foreach (var item in activityItems) @@ -186,21 +185,15 @@ namespace GrasscutterTools.Pages buffer.AppendLine(activityMap.TryGetValue(id, out var title) ? title : item.Value[id]); } } - var activityFilePath = Path.Combine(TxtProjectResRoot.Text, language, "Activity.txt"); + var activityFilePath = Path.Combine(TxtProjectResRoot.Text, languageCode, "Activity.txt"); File.WriteAllText(activityFilePath, buffer.ToString(), Encoding.UTF8); - - //File.WriteAllLines( - // activityFilePath, - // activityItems.Select(it => $"{it.ActivityId}:{TextMapData.GetText(it.NameTextMapHash)}"), - // Encoding.UTF8); } - private void UpdateGachaResourceForLanguage(string textMap, string language) + private void UpdateGachaResourceForLanguage(string languageCode) { - var i = Array.IndexOf(TextMapData.TextMapFiles, textMap); - TextMapData.LoadTextMap(TextMapData.TextMapFilePaths[i]); + TextMapData.LoadTextMapByLanguage(languageCode); var titleBuffer = new StringBuilder(); const string titlePattern = "UI_GACHA_SHOW_PANEL_([^_]+?)_TITLE"; @@ -216,7 +209,7 @@ namespace GrasscutterTools.Pages .AppendLine(Regex.Replace(text, markPattern, "")); } - var titleFilePath = Path.Combine(TxtProjectResRoot.Text, language, "GachaBannerTitle.txt"); + var titleFilePath = Path.Combine(TxtProjectResRoot.Text, languageCode, "GachaBannerTitle.txt"); File.WriteAllText(titleFilePath, titleBuffer.ToString(), Encoding.UTF8); } @@ -229,10 +222,10 @@ namespace GrasscutterTools.Pages TextMapData ??= new TextMapData(TxtGcResRoot.Text); - UpdateGachaResourceForLanguage("TextMapCHS", "zh-cn"); - UpdateGachaResourceForLanguage("TextMapCHT", "zh-tw"); - UpdateGachaResourceForLanguage("TextMapEN", "en-us"); - // UpdateGachaResourceForLanguage("TextMapRU", "ru-ru"); + UpdateGachaResourceForLanguage("zh-cn"); + UpdateGachaResourceForLanguage("zh-tw"); + UpdateGachaResourceForLanguage("en-us"); + UpdateGachaResourceForLanguage("ru-ru"); MessageBox.Show("OK", Resources.Tips, MessageBoxButtons.OK); } catch (Exception ex)