benmed00/coh-ucs-tools

GitHub: benmed00/coh-ucs-tools

一款用于《英雄连1》.ucs 本地化文件的解析、对比、验证与合并工具,提供 CLI 和 FastAPI Web 应用两种使用方式。

Stars: 0 | Forks: 0

# CoH UCS 工具包 ![Python 3.12](https://img.shields.io/badge/python-3.12-blue) ![License: MIT](https://img.shields.io/badge/license-MIT-green) ![Tests](https://static.pigsec.cn/wp-content/uploads/repos/cas/09/097271ca091990be630ef6043309cc48240faa054413384202036fa2efedb2d2.svg) ![Tests local](https://img.shields.io/badge/tests-64%20passing-brightgreen) 用于《英雄连》(Company of Heroes) 的 `.ucs` 本地化文件(`RelicCOH.Russian.ucs` / `RelicCOH.English.ucs`)的对比、验证、搜索和迁移工具—— 外加一个将相同工具包作为 REST API 暴露的 FastAPI Web 服务。 纯 Python 3.12 标准库核心。`chardet` 是用于没有 BOM 的文件的*可选*后备方案。在 Windows 上运行。 源自一个真实问题:完整版安装后在《勇气传说》(Tales of Valor) 菜单中显示 `$559200 No Key`。完整的故事——逆向工程、官方英文本地化的恢复及其验证——位于 [`docs/PROJECT_REPORT.md`](docs/PROJECT_REPORT.md)。 ## 目录 - [快速开始](#quick-start) - [架构](#architecture) - [UCS 文件结构(逆向工程)](#ucs-file-structure-reverse-engineered) - [模块](#modules) - [CLI 用法](#cli-usage) - [Web 应用程序](#web-application) - [恢复的官方英文文本](#recovered-official-english-text) - [法语本地化](#french-localization) - [机器翻译交叉比对](#machine-translation-cross-check-translatepy) - [阿拉伯语本地化(非官方)](#arabic-localization-unofficial) - [报告](#report) - [测试](#tests) - [文档](#documentation) - [局限性](#limitations) - [贡献与许可](#contributing--license) ## 快速开始 ``` git clone coh-ucs-tools cd coh-ucs-tools # 核心工具包除了 stdlib 外不需要任何其他依赖;这里添加了可选的 # chardet fallback 和 web app stack pip install -r requirements.txt # 交互式菜单(默认使用 cli.py 中内置的两个游戏路径) python cli.py # 或一次性命令 python cli.py compare --russian "C:\path\RelicCOH.Russian.ucs" --english "C:\path\RelicCOH.English.ucs" # 或者带有 Swagger docs 的 web app,地址为 http://127.0.0.1:8000/docs python -m uvicorn webapp.main:app --reload ``` ## 架构 ``` flowchart TD subgraph core ["Core (stdlib only)"] parser["parser.py
UcsDocument, parse/write,
encoding & BOM detection"] validator["validator.py
duplicate/invalid/bad-char/
empty/missing checks"] stats["statistics.py
Comparison, coverage,
range compression, report/"] merge["merge.py
placeholder & fill-from-source
merge, safe writing"] end cli["cli.py
interactive menu +
argparse subcommands"] translate["translate.py
MT cross-check (QA only)"] webapp["webapp/
FastAPI REST API + frontend
Swagger at /docs"] tests["tests/
29 unittest cases"] parser --> validator parser --> stats parser --> merge cli --> parser & validator & stats & merge translate --> parser & stats webapp --> parser & validator & stats & merge tests -.-> core ``` CLI、Web 应用程序和 MT 交叉比对都委托给相同的四个核心模块——没有重复的逻辑。 ## UCS 文件结构(逆向工程) 针对随游戏发布的两个文件逐字节验证的结果: | 属性 | 值 | |--------------------|--------------------------------------------------------------| | 编码 | UTF-16 小端序 | | BOM | 文件开头的 `FF FE`(2 字节) | | 换行符 | CRLF (`\r\n`);不存在单独的 `\n` 或 `\r` | | 条目语法 | `<数字 ID><文本>` | | 分隔符 | **第一个**制表符;值可以包含额外的制表符 | | 注释 | 无 —— 该格式没有注释语法 | | 空值 | 合法(`` 后紧跟行尾);使用了约 30-40 次 | | 重复项 | 发布的文件中没有;解析器保留**最后**一个 | | 键值范围 | 1 … 11 005 454 (俄语), 1 … 809 001 (英语) | 格式错误的行(没有制表符,键值非数字)永远不会被静默丢弃:解析器会记录它们的行号和原因,并显示在 `report/invalid_lines.txt` 和验证结果中。 ## 模块 | 文件 | 用途 | |-----------------|--------------------------------------------------------------------| | `parser.py` | 编码/BOM 检测,解析为 `UcsDocument`,UCS 写入器 | | `validator.py` | 重复 ID、缺失 ID、空值、UTF-16/损坏检查 | | `statistics.py` | 对比、覆盖率统计、范围压缩、`report/` 生成器 | | `merge.py` | 合并算法和合并文件的安全写入 | | `translate.py` | 机器翻译(仅用于对比/QA)对比恢复的官方文本 | | `cli.py` | 交互式菜单 + argparse 子命令 | | `webapp/` | FastAPI REST API + 静态前端(参见 [Web 应用程序](#web-application)) | | `tests/` | `unittest` 套件(解析器、写入器、验证器、合并、排序) | 包含签名和示例的完整函数级文档: [`docs/API.md`](docs/API.md)。 ### 解析器 `parse_file(path)` 返回一个 `UcsDocument` dataclass,包含: * `entries: dict[int, str]` —— `Dictionary` 视图(最后出现的 重复项胜出), * `all_entries` —— 文件顺序中的每个有效行(用于诊断), * `duplicates` —— 键 → 行号列表, * `invalid_lines` —— 行号、原始文本和原因, * 检测到的 `encoding`、`has_bom`、`newline`、`trailing_newline`。 编码检测顺序:UTF-16 BOM (LE/BE) → 严格 UTF-16-LE 探测 → `chardet`(如果已安装)→ UTF-8 后备。 ### 编辑器 / 写入器 `parser.write_file()` 使用从输入文档中捕获的约定(UTF-16-LE、BOM、CRLF、末尾换行符)将 `(id, value)` 对序列化回磁盘。**除非明确告知,否则它拒绝覆盖现有文件**,因此原始文件永远不会被破坏。 ### 验证 `validator.validate(doc, reference=None)` 报告: * `invalid-line`(错误)—— 结构损坏的行, * `duplicate-id`(错误)—— 同一 ID 被定义了多次, * `bad-character`(错误)—— 单独的 UTF-16 代理项或除制表符以外的控制字符 (行损坏指示器);每个值也通过严格的 UTF-16-LE 编码进行往返测试, * `empty-value`(警告)—— 没有文本的 ``, * `missing-id`(警告)—— 参考文档中存在但此处缺失的 ID。 ### 合并算法 1. 解析目标(英文)和源(俄文)文件。 2. 从所有目标条目的副本开始——**现有的英文文本永远不会被修改**。 3. 对于仅存在于源文件中的每个 ID,添加 `ID`。 使用 `--fill-from-source`(菜单选项 4,模式 2)时,将**逐字**复制源文件的 原始文本,而不是使用占位符。 **在任何模式下都不会生成翻译**。 4. 按数字对所有 ID 进行排序。 5. 使用目标文件的确切编码约定(UTF-16-LE + BOM、CRLF、末尾换行符)写入 `RelicCOH.English.merged.ucs`。 6. 拒绝覆盖任何一个输入文件(抛出 `ValueError`),因此永远不会触及原始文件。 ### 缺失 ID 范围检测 连续缺失的 ID 在导出的文件中被压缩为范围, 例如 `[559200, 559201, …, 559650]` 变为 `559200-559650`。 ## CLI 用法 ``` # 交互式菜单(默认使用 cli.py 中内置的两个游戏路径) python cli.py python merge.py # same menu # 自定义路径 python cli.py --russian "C:\path\RelicCOH.Russian.ucs" --english "C:\path\RelicCOH.English.ucs" # 非交互式 subcommands python cli.py compare # writes the report/ directory python cli.py statistics # prints statistics.json content python cli.py export-missing # missing IDs as ranges python cli.py merge # writes RelicCOH.English.merged.ucs (cwd) python cli.py merge --fill-from-source # fill gaps with verbatim source text # instead of placeholders python cli.py validate python cli.py search-id 559200 python cli.py search-text "Panzer" python cli.py search-text "Pz\.? ?IV" --regex ``` 交互式菜单: ``` 1 Compare 2 Statistics 3 Export missing IDs 4 Merge 5 Validate 6 Search ID 7 Search Text 8 Exit ``` 在菜单选项 7 中,在查询前加上 `re:` 前缀以使用正则表达式搜索;否则将使用不区分大小写的子字符串搜索。 ## Web 应用程序 `webapp/` 包将工具包作为 REST API 提供服务,外加一个静态的单页前端: ``` pip install -r requirements.txt python -m uvicorn webapp.main:app --reload ``` 然后打开 —— 二战指挥控制台主题,带有**浅色主题切换**(设置)、原生 JS ES 模块(无需构建步骤)、Three.js 语言环境地球仪(点击图钉 → 语言中心)、Chart.js 覆盖率圆环图。 **SPA 部分:**仪表板 · 上传与分析 · 对比 · **差异** · **范围热力图** · **验证器** · **语言中心** · **合并向导**(预览) · 安装检测 · **MT 实验室** · **词汇表** · **时间线** · **仓库与来源** · **全局搜索**(模糊匹配/正则表达式) · **书签** · **补丁构建器** · **SGA 浏览器** · 设置 · 工具 * 交互式 Swagger/OpenAPI: * 内置 CoH1 UCS 版本注册表(启动时只读副本) * 状态:`uploads/`(被 gitignored)+ `webapp/storage/`(词汇表、书签、审计、MT 状态) * 可选的 `UCS_API_KEY` 环境变量用于 API 密钥中间件;启动时进行 24 小时上传清理 ### REST API 接口 核心接口以及扩展的分析/本地化/搜索——完整的表格请参见 [`docs/API.md`](docs/API.md)(diff、指纹、批量对比、补丁构建、交叉引用等)。 接口参考:[`docs/API.md`](docs/API.md#rest-api)。该 API 委托给与 CLI 完全相同的核心模块。 ## 恢复的官方英文文本 旧的 THQ 零售版 `RelicCOH.English.ucs`(8,578 个键)早于《抵抗前线》/《勇气传说》。来自 Steam 新版本(New Steam Version,22,523 个键,无占位符)的完整官方英文本地化可以从 [Steam 论坛](https://steamcommunity.com/app/228200/discussions/0/353915847943232144/) 上引用的社区共享副本中恢复(`downloads/RelicCOH.English.NSV.ucs`)。它涵盖了 100% 的俄语完整版 ID。`RelicCOH.English.complete.ucs` 是该文件加上 157 个仅限旧版 THQ 键的并集。 恢复/生成的游戏文件包含受版权保护的文本,**不属于此存储库的一部分**(参见 `.gitignore`)。完整的恢复故事和验证结果在 [`docs/PROJECT_REPORT.md`](docs/PROJECT_REPORT.md) 中。 ## 法语本地化 《英雄连 1》**已正式发布法语版**。旧版 / Steam 新版本法文仓库(SteamDB 仓库 **4565**,app 4560)附带了完整的散落文件 `CoH/Engine/Locale/French/RelicCoH.French.ucs`(约 2.35 MiB)——这是恢复的 NSV 英文文件的法语对应文件。 在这台机器上,完整版安装**没有** `French`(法语)语言文件夹(在 `CoH\Engine\Locale\` 下只有英语和俄语),THQ 零售版没有法文 UCS,也没有找到社区共享的法文下载(不像来自 Steam 论坛的 [NSV 英文 Dropbox](https://www.dropbox.com/s/mzsmb0w42ua1z15/RelicCOH.English.ucs?dl=0))。**DepotDownloader** 已安装;匿名的 Steam 登录可以工作,但在没有拥有旧版的账号的情况下无法拉取仓库 4565。 ### 构建 `RelicCOH.French.complete.ucs` 将恢复的官方法语文件放在 `downloads/RelicCOH.French.NSV.ucs`(通过 DepotDownloader 从你的 Steam 法语仓库获取,或者来自朋友的安装,或者是语言包副本),然后: ``` python build_french.py python build_french.py --search-only # local search + report only python build_french.py --nsv "D:\path\RelicCOH.French.ucs" ``` 构建方式与英文完整版相同:**官方法语 NSV** 作为基础,合并任何**仅限旧版 THQ 零售版法语**的键(如果存在),然后对于没有法语来源的俄语完整版 ID 仅使用 ``。**没有任何机器翻译**被写入游戏文件。 输出(当包含游戏文本时会被 gitignored): | 文件 | 用途 | |---|---| | `RelicCOH.French.complete.ucs` | 联合构建(仅官方法语 + 占位符) | | `downloads/RelicCOH.French.NSV.ucs` | 恢复的官方法语源(由你提供) | | `report/french/` | `french_keys.txt`、`missing_in_french.txt`、`statistics.json`、搜索/恢复说明 | ### 在游戏中安装法语 1. 如果缺失,请创建 `CoH\Engine\Locale\French\`(完整版通常在发布时没有它)。 2. 复制 `RelicCOH.French.complete.ucs` → `CoH\Engine\Locale\French\RelicCOH.French.ucs`(安装时重命名)。 3. 通过 `CoH\locale.ini` 将游戏指向法语(将当前激活的语言 文件夹设置为 `French`)。某些版本还使用顶层的 `Locale.ini`。 4. 备份原文件;**在没有 `.bak` 备份的情况下,切勿覆盖**原始文件。 THQ 零售版路径:`Engine\Locale\French\RelicCOH.French.ucs`。完整版 路径`CoH\Engine\Locale\French\RelicCOH.French.ucs`。 ## 机器翻译交叉比对 (`translate.py`) `translate.py` 对旧英文文件中缺失的每个 ID,将俄语源字符串进行机器翻译(公共 Google 翻译接口,无需 API 密钥),然后将 MT(机器翻译)输出与恢复的官方英文文本进行比对: ``` python translate.py --limit 200 # pilot run python translate.py # everything (resumes from downloads/mt_cache.json) python translate.py --compare-only # rebuild the report from cached MT ``` 相似度是经过 token 标准化的 `difflib` 比率(去除了 `%1%` 等格式 token,忽略大小写/标点符号)。输出: * `report/translation_comparison.tsv` —— 每个 ID 一行,按差异最大的 优先排序(ID、相似度、MT、官方、俄语源), * `report/translation_summary.json` —— 分组的相似度分布。 MT 输出**仅用于验证**恢复的官方文本——它永远不会被写入游戏文件。 ## 阿拉伯语本地化(非官方) 《英雄连 1》**从未正式发布过阿拉伯语版本**。Steam 仓库或 THQ 零售版安装中没有 `RelicCOH.Arabic.ucs`,也没有找到广泛流传的社区阿拉伯语 UCS 补丁(本地完整版安装和网络搜索记录在 `report/arabic/search_results.json` 中)。 交付成果是一个清晰标注的**机器翻译产物**: | 文件 | 用途 | |---|---| | `RelicCOH.Arabic.MT.ucs` | 由 `RelicCOH.English.complete.ucs` 制作的粉丝 MT (英→阿) | | `downloads/mt_ar_cache.json` | 可恢复的 MT 检查点(已 gitignored) | | `report/arabic/` | 统计数据、验证、50 个键的英阿对比样本 TSV | ``` python build_arabic.py --limit 200 # pilot run python build_arabic.py # full build (resumes from cache) python build_arabic.py --report-only # rebuild report from cache + existing UCS ``` **重要注意事项:** * 这是**非官方的粉丝机器翻译**(Google 翻译公共端点),**不是** Relic/THQ 官方文本。请勿将其作为官方本地化展示。 * UCS 格式 token(`%1%`、`%1NAME%` 等)在 MT 之前被剥离为占位符,并在之后恢复,以保持格式字符串完整。 * CoH1 引擎可能**无法在菜单和 UI 中正确渲染从右向左(RTL)的阿拉伯语**(如 LTR 布局、连写及拉丁/阿拉伯混合字符串)。即使 UCS 文件是有效的 UTF-16-LE,也应预料到会出现显示问题。 * 生成的 `.ucs` / MT 缓存文件包含受版权保护的源文本,并且已被 **gitignored** —— 切勿提交它们。 ### 在游戏中安装阿拉伯语 MT 1. 创建语言环境文件夹(游戏未附带): `CoH\Engine\Locale\Arabic\` 2. 复制 `RelicCOH.Arabic.MT.ucs` → `CoH\Engine\Locale\Arabic\RelicCOH.Arabic.ucs` (安装时重命名;`.MT` 后缀标记为非官方构建产物)。 3. 将游戏指向阿拉伯语 —— 通常通过编辑 `CoH\locale.ini`(或 模组的 `Locale.ini`),将激活的语言文件夹设置为 `Arabic`。具体步骤因版本/模组而异;请先备份原始文件。 4. **切勿覆盖**原生的 `English` 或 `Russian` UCS 文件。 与其他进行中的本地化工作(例如法语)没有冲突:阿拉伯语输出使用独立的文件名(`RelicCOH.Arabic.MT.ucs`)。 ## 报告 `python cli.py compare` 生成: ``` report/ russian_keys.txt all Russian IDs, numerically sorted english_keys.txt all English IDs, numerically sorted missing_in_english.txt IDs missing from English, as ranges missing_in_russian.txt IDs missing from Russian, as ranges duplicate_keys.txt duplicated IDs with line numbers invalid_lines.txt malformed lines with reasons statistics.json totals, duplicates, invalid, empty, coverage % ``` 覆盖率是根据两个键集的并集计算的。 ## 测试 ``` python -m unittest discover -s tests -v ``` 45 项测试:36 项涵盖了核心工具包和 `build_french.py` —— 解析(BOM、编码、值中的制表符、重复项、无效行)、写入(往返测试、覆盖保护)、验证(所有问题代码)、范围压缩、对比统计、合并行为(仅占位符、数字排序、原始文件保护)、数字键排序,以及 MT token 保留(`protect_tokens` / `restore_tokens`)。另外 9 项(`tests/test_webapp.py`,FastAPI TestClient)涵盖了 REST API 的正常执行路径(上传 → 分析 → 对比 → 合并 → 下载)及其错误情况。 ## 文档 | 文档 | 内容 | |---|---| | [`docs/PROJECT_REPORT.md`](docs/PROJECT_REPORT.md) | 完整项目报告:格式逆向工程、对比发现、NSV 恢复、MT 交叉比对、验证 | | [`docs/API.md`](docs/API.md) | Python API(签名 + 示例)和 REST API 接口参考 | | [`docs/BACKLOG.md`](docs/BACKLOG.md) | 优先级路线图 (P1/P2/P3) | | [`CONTRIBUTING.md`](CONTRIBUTING.md) | 开发设置、测试、代码风格、添加游戏变体 | | [`LICENSE`](LICENSE) | MIT | ## 局限性 * 该工具包仅对比和合并**现有**条目。它从不进行翻译,也从不捏造本地化字符串;间隙会变成 `` 占位符,必须由人工翻译人员填补。 * 如果按原样安装合并后的文件,`` 占位符将被游戏逐字显示。 * 重复处理(“后出现的覆盖先出现的”)是基于引擎常规行为的假设;自带的文件中没有重复项可供验证。 * 写入器将输出标准化为每行一个条目,并按数字排序 —— 原始的*行顺序*在合并输出中不会被保留(自带的文件已经过排序,因此在实践中这是一个空操作)。 * 没有 BOM 的编码检测是启发式的(严格的 UTF-16-LE 探测,然后是可选的 `chardet`)。 * 非常大的文件会被完全加载到内存中(约 2 MB 的游戏文件是瞬间完成的;这不是流式解析器)。 ## 贡献与许可 欢迎贡献 —— 有关开发设置、测试说明、代码风格(仅标准库核心、dataclass 和完整类型提示)以及如何添加对其他 Relic 游戏支持的详细信息,请参见 [`CONTRIBUTING.md`](CONTRIBUTING.md)。 采用 [MIT License](LICENSE) 授权 © 2026 coh-ucs-tools 贡献者。 《英雄连》及其本地化文件归其各自所有者所有;本存储库不分发任何游戏内容。
标签:AV绕过, FastAPI, Python, 文件解析, 文本校验, 无后门, 本地化, 游戏, 网络测绘