diff --git a/Packer-Index-Doc.md b/Packer-Index-Doc.md index 77b9152633b3..eb26db82fb8b 100644 --- a/Packer-Index-Doc.md +++ b/Packer-Index-Doc.md @@ -25,4 +25,4 @@ packer-policy.json - `patch` 引用另一位置的文件结构,但在其中的部分文件上额外应用自定义的修改。修改使用[Google Diff-Match-Patch算法](https://github.com/google/diff-match-patch)生成;尽管原则上可以放在任意位置、采用任意后缀名,建议将修改文件放在被修改文件相应的位置,采用`.patch`后缀,以保持统一性。如:[示例文件](./projects/1.19/assets/0-example-patch/patch/packer-policy.json) - `source` string -> 复制的源地址。需要从本仓库的根目录开始计算,使用`./`前缀。 - `patches` object -> 修改文件,以及对应的修改目标。 - - `修改目标的相对路径` string -> 修改文件的源地址。需要从本仓库的根目录开始计算,使用`./`前缀;`修改目标的相对路径`需要为在复制源地址的`/`下方的相对位置,必须使用`\\`作为分隔符,如`lang\\zh_cn.json`。 + - `修改目标的相对路径` string -> 修改文件的源地址。需要从本仓库的根目录开始计算,使用`./`前缀;`修改目标的相对路径`需要为在复制源地址的`/`下方的相对位置,必须使用`/`作为分隔符,如`lang/zh_cn.json`。 diff --git a/projects/1.19/assets/0-example-patch/patch/lang/zh_cn.patch b/projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch similarity index 100% rename from projects/1.19/assets/0-example-patch/patch/lang/zh_cn.patch rename to projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch diff --git a/projects/1.19/assets/0-example-patch/patch/packer-policy.json b/projects/1.19/assets/0-example-patch/patch/packer-policy.json index ab636113bbee..a75e14b615d8 100644 --- a/projects/1.19/assets/0-example-patch/patch/packer-policy.json +++ b/projects/1.19/assets/0-example-patch/patch/packer-policy.json @@ -2,6 +2,6 @@ "type": "patch", "source": "./projects/1.19/assets/0-example-nop/nop", "patches": { - "lang\\zh_cn.json": "./projects/1.19/assets/0-example-patch/patch/lang/zh_cn.patch" + "lang/zh_cn.json": "./projects/1.19/assets/0-example-patch/patch/lang/zh_cn.json.patch" } } \ No newline at end of file diff --git a/src/Packer/Extensions/ArchiveExtension.cs b/src/Packer/Extensions/ArchiveExtension.cs index 7d634a1bad4b..ead7c752da90 100644 --- a/src/Packer/Extensions/ArchiveExtension.cs +++ b/src/Packer/Extensions/ArchiveExtension.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Threading.Tasks; namespace Packer.Extensions @@ -60,19 +61,19 @@ public static void Initialize(this ZipArchive archive, Config config) public static async Task WriteContent(this ZipArchive archive, IEnumerable content) { Log.Information("添加处理后的文件"); - var tasks = new List(); - foreach (var asset in content) - { - Log.Information("正在添加 asset-domain: {0}", asset.domainName); - foreach (var file in asset.contents) - { - tasks.Add(archive.CreateLangFile(Path.Combine("assets", + + + var tasks = content.SelectMany(asset => asset.contents.Select(file => archive.CreateLangFile(Path.Combine("assets", asset.domainName, file.RelativePath), - file.StringifiedContent)); - } - } - await Task.WhenAll(tasks); + file.StringifiedContent))); + + await Task.WhenAll(content.SelectMany( + asset => asset.contents.Select( + file => archive.CreateLangFile(destination: Path.Combine("assets", + asset.domainName, + file.RelativePath), + content: file.StringifiedContent)))); Log.Information("添加完毕"); } @@ -86,9 +87,10 @@ public static void WriteBypassed(this ZipArchive archive, Dictionary FromMixedDirectory(DirectoryInfo assetDirecto ref Dictionary unprocessed, Dictionary parameters) => Utils.MergeFiles(FromImmediateDirectory(assetDirectory, config, ref unprocessed, parameters), - FromIndirectDirectory(assetDirectory, config, ref unprocessed, parameters)); + FromIndirectDirectory(assetDirectory, config, ref unprocessed, parameters)); static IEnumerable FromBackPort(DirectoryInfo assetDirectory, Config config, @@ -93,9 +93,9 @@ static IEnumerable FromPatches(DirectoryInfo assetDirectory, { //Log.Information("{0}", reference.Keys); Log.Information("对文件 {0} 应用 {1} 处的 patch。", patch.Key, patch.Value); - var target = reference[patch.Key]; + reference.Remove(patch.Key, out var target); var patchText = string.Join('\n', File.ReadAllLines(patch.Value)); // 不要问我为什么D-M-P默认换行是LF - target.ApplyPatch(patchText); + reference.Add(patch.Key, target.ApplyPatch(patchText)); } return reference.Values; } @@ -111,13 +111,11 @@ static IEnumerable FromImmediateDirectory(DirectoryInfo assetDir .Select(file => { // 这里开始真正的检索。被跳过的文本用 null 代替 var prefixLength = assetDirectory.FullName.Length; - var relativePath = file.FullName[(prefixLength + 1)..]; // 在asset-domain下的位置,用反斜杠分割 + var relativePath = file.FullName[(prefixLength + 1)..] + .Replace('\\', '/'); // 在asset-domain下的位置,规范为用正斜杠分割 - // 跳过非中文文件 - if (!relativePath.IsTargetLang(config)) - { - return null; - } + // 处理被跳过的文本。处理顺序:policy -> [bypass](font, textures) -> !zh_cn + // 顺序乱了会导致字体文件被丢弃,因为没有带zh_cn // 跳过检索策略文件 if (relativePath == "packer-policy.json") @@ -128,21 +126,30 @@ static IEnumerable FromImmediateDirectory(DirectoryInfo assetDir // 选出不经过处理路径的文件 if (relativePath.NeedBypass(config)) { - Log.Information("跳过了标记为直接加入的命名空间:{0}", relativePath.Split('\\')[0]); - bypassed.Add(file.FullName, - Path.Combine("assets", + var target = Path.Combine("assets", assetDirectory.Name, - relativePath)); + relativePath); + Log.Information("跳过了标记为直接加入的命名空间:{0} -> {1}", + relativePath.Split('/')[0], + target); + bypassed.Add(file.FullName, target); + return null; + } + + // 跳过非中文文件 + if (!relativePath.IsTargetLang(config)) + { return null; } // 处理正常的语言文件 + // TODO:Json5支持 var parsingCategory = file.Extension switch { ".json" => FileCategory.JsonAlike, _ => FileCategory.LangAlike }; - if (relativePath.StartsWith("lang\\")) + if (relativePath.StartsWith("lang/")) { return new LangFile(file.OpenRead(), parsingCategory | FileCategory.LanguageFile, diff --git a/src/Packer/Lib.cs b/src/Packer/Lib.cs index df9e6d4c6ae3..1513f6be9bd7 100644 --- a/src/Packer/Lib.cs +++ b/src/Packer/Lib.cs @@ -1,9 +1,11 @@ using Packer.Extensions; using Packer.Models; using Serilog; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; namespace Packer { @@ -17,6 +19,8 @@ static class Lib /// public static IEnumerable RetrieveContent(Config config, out Dictionary unprocessed) { + // 警告:下面的代码中,有部分变量的名称并不规范!远期可能会调整这一部分。 + // 注:仓库的文件结构如下:(仅考虑主要翻译文件) // projects//assets////path/to/the/file // 其中, 与 config.Version 一致 @@ -67,6 +71,12 @@ public static IEnumerable RetrieveContent(Config config, out Dictionary - /// 语言文本的抽象

- /// 这是基本类 + /// 语言文本的抽象。这是基本类
+ /// 基本上是immutable的 /// public class TranslatedFile { /// /// asset-domain下的位置 /// - public string RelativePath { get; set; } + public string RelativePath { get; init; } + /// /// 该文件的文本,用字符串表示

/// 因此,不能存储非文本文件! ///
- public string StringifiedContent { get; private set; } + public string StringifiedContent { get; } + /// /// 文件类型 /// - public FileCategory Category { get; set; } + public FileCategory Category { get; } + /// /// 从文件流构造内容 /// @@ -79,6 +82,7 @@ public TranslatedFile(Stream stream, FileCategory category, Config config) StringifiedContent = reader.ReadToEnd().Preprocess(category, config); this.Category = category; } + /// /// 从文本构造内容 /// @@ -87,31 +91,29 @@ public TranslatedFile(FileCategory category, string content) this.Category = category; StringifiedContent = content; } + /// /// 伪合并文件 /// - /// - /// virtual public TranslatedFile Combine(TranslatedFile file) { Log.Information("文件不支持合并。取消合并"); return this; } + /// /// 伪适配文件 /// - /// - /// virtual public TranslatedFile Port(TranslatedFile file) { Log.Information("文件不支持适配。直接覆盖"); return file; } + /// /// 对该文件的内容进行Google Diff-Match-Patch算法 /// - /// - public void ApplyPatch(string patch) + public TranslatedFile ApplyPatch(string patch) { // 对应的Patch可以自行生成,或者也可以做一个小工具,虽然不在这里 // 应用Patch时,需要先根据Patch文本生成Patch列表,再应用Patch @@ -119,44 +121,57 @@ public void ApplyPatch(string patch) // patch_apply 返回object[] [0]=string [1]=bool[] var dmp = new diff_match_patch(); var patchList = dmp.patch_fromText(patch); - StringifiedContent = (string)dmp.patch_apply(patchList, StringifiedContent)[0]; + return new TranslatedFile(Category, + (string)dmp.patch_apply(patchList, StringifiedContent)[0]); } } /// - /// 可以按照/lang/文件夹下解析的文件。这是衍生类 + /// 可以按照/lang/文件夹下解析的文件。这是衍生类
+ /// 基本上是immutable的 ///
- class LangFile : TranslatedFile + public class LangFile : TranslatedFile { - bool deserialized = false; // 非必要不解析,免得残废的lang解析炸掉 - public Dictionary deserializedContent; + private bool deserialized = false; // 非必要不解析,免得残废的lang解析炸掉 + private Dictionary _deserializedContent; - // 继承构造函数 + /// + /// 按照基础语言文件的格式解析而成的词条列表。
+ /// 在访问时才会解析。 + ///
+ public Dictionary DeserializedContent + { + get + { + if (!deserialized) + { + deserialized = true; + _deserializedContent = StringifiedContent.DeserializeAsset(Category); + } + return _deserializedContent; + } + } + + /// + /// 从文件流构造内容 + /// public LangFile(Stream stream, FileCategory category, Config config) : base(stream, category, config) { - deserializedContent = null; + _deserializedContent = null; } - // 从kv对顺便构造字符串内容备用 + /// + /// 从映射表构造内容 + /// public LangFile(FileCategory category, Dictionary content) : base(category, content.SerializeAsset(category)) { - deserializedContent = content; - } - - public void Deserialize() - { - if (!deserialized) - { - deserialized = true; - deserializedContent = StringifiedContent.DeserializeAsset(Category); - } + _deserializedContent = content; + deserialized = true; } /// /// 真合并文件 /// - /// - /// public override LangFile Combine(TranslatedFile file) { Log.Information("合并文件:{0}", this.RelativePath); @@ -168,10 +183,8 @@ public override LangFile Combine(TranslatedFile file) return this; } - this.Deserialize(); - castedFile.Deserialize(); - var resultMap = new Dictionary(deserializedContent); - foreach (var pair in castedFile.deserializedContent) + var resultMap = new Dictionary(DeserializedContent); + foreach (var pair in castedFile.DeserializedContent) { if (!resultMap.TryAdd(pair.Key, pair.Value)) { @@ -186,6 +199,9 @@ public override LangFile Combine(TranslatedFile file) }; } + /// + /// 真适配文件 + /// public override TranslatedFile Port(TranslatedFile file) { var castedFile = (LangFile)file; @@ -194,12 +210,11 @@ public override TranslatedFile Port(TranslatedFile file) Log.Information("检测到不支持合并的文件。取消合并"); return file; } - this.Deserialize(); - castedFile.Deserialize(); - var resultMap = deserializedContent; + + var resultMap = DeserializedContent; foreach(var key in resultMap.Keys) { - if(castedFile.deserializedContent.TryGetValue(key, out var value)) + if(castedFile.DeserializedContent.TryGetValue(key, out var value)) { Log.Information("正在替换适配项:<{0}> {1} => {2}", key, resultMap[key], value); resultMap[key] = value; diff --git a/src/Packer/Program.cs b/src/Packer/Program.cs index aebfeaa60b77..e6b53d3e90ac 100644 --- a/src/Packer/Program.cs +++ b/src/Packer/Program.cs @@ -33,7 +33,7 @@ public static async Task Main(string version = null) } // Packer输出的文件名,可以随时更改 - string packName = $".\\Minecraft-Mod-Language-Package-{config.Version}.zip"; + string packName = $"./Minecraft-Mod-Language-Package-{config.Version}.zip"; Log.Information("开始对版本 {0} 的打包", config.Version); @@ -49,7 +49,7 @@ public static async Task Main(string version = null) archive.Dispose(); // 关闭压缩文档,不关闭流 // 临时操作 - File.WriteAllText($".\\{config.Version}.md5", stream.ComputeMD5()); + File.WriteAllText($"./{config.Version}.md5", stream.ComputeMD5()); stream.Dispose(); } } diff --git a/src/Packer/Utils.cs b/src/Packer/Utils.cs index b07ab006da31..dd928d9f4d00 100644 --- a/src/Packer/Utils.cs +++ b/src/Packer/Utils.cs @@ -58,6 +58,12 @@ public static IEnumerable MergeFiles(IEnumerable return mapping.Values; } + /// + /// 基于原位置的文件key,用incoming的词条进行替换。未被替换的保持原文。 + /// + /// 原位置的基准文件 + /// 更新文件 + /// public static IEnumerable PortFiles(IEnumerable baseFile, IEnumerable incoming) { var mapping = new Dictionary(); // asset-domain下的目标位置 -> 文件