benmed00/coh-ucs-tools
GitHub: benmed00/coh-ucs-tools
一款用于《英雄连1》.ucs 本地化文件的解析、对比、验证与合并工具,提供 CLI 和 FastAPI Web 应用两种使用方式。
Stars: 0 | Forks: 0
# CoH UCS 工具包




用于《英雄连》(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 贡献者。
《英雄连》及其本地化文件归其各自所有者所有;本存储库不分发任何游戏内容。
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>
标签:AV绕过, FastAPI, Python, 文件解析, 文本校验, 无后门, 本地化, 游戏, 网络测绘