内容
# uex
无头、兼容FModel的虚幻引擎pak导出器和资源管理器,基于[CUE4Parse](https://github.com/FabianFG/CUE4Parse)构建。单个二进制文件通过命名配置文件支持多个游戏。它取代了手动的FModel导出,用于arkive游戏维基管道,并通过CLI、JSON行`serve`模式和`mcp`_stdio服务器为工具(和代理)提供编程化的pak访问。
## 要求
- .NET 10 SDK(CUE4Parse NuGet包仅针对`net10.0`)。
- 游戏的pak文件(本地安装或`Paks`目录的副本)。
- 每个游戏的`.usmap`映射文件和加密游戏的AES密钥。
usmap/AES/路径值直接来自有效的FModel设置 — `%APPDATA%\FModel\AppSettings.json`:
- `GameDirectory` → `paksDir`
- 每个目录的`UeVersion` → `game`(CUE4Parse `EGame`枚举名称)
- `AesKeys.mainKey` → `aesKey`
- `Endpoints`映射`FilePath` → `usmap`
## 设置
```bash
cp profiles.example.json profiles.json
# 编辑profiles.json — 按游戏填写paksDir、usmap、aesKey
```
`profiles.json`是**gitignored**的,因为它包含AES密钥和机器特定的路径。`profiles.example.json`是已提交的模板。
配置解析顺序(第一个匹配项获胜):
1. `--config <path>`全局选项
2. `UEX_PROFILES`环境变量
3. `./profiles.json`(当前目录)
4. `profiles.json`位于可执行文件旁边
### 配置文件字段
| 字段 | 含义 |
|---------------|----------------------------------------------------------------|
| `game` | CUE4Parse `EGame`枚举名称,例如`GAME_Aion2`、`GAME_Palworld`|
| `paksDir` | 包含游戏`.pak`/`.utoc`文件的目录 |
| `usmap` | `.usmap`映射文件的路径(某些游戏可选) |
| `aesKey` | AES密钥作为`0x…`十六进制,或`null`用于未加密游戏 |
| `outputDir` | `export`命令写入FModel样式树结构的根目录 |
| `exportRoots` | `export`/`doctor`遍历的虚拟路径根列表 |
## 命令
除`serve`/`mcp`外,每个命令都需要 `--profile <name>`。
```bash
# 挂载并检查配置文件(探测3个软件包,失败时退出非零)
uex doctor --profile aion2
# 批量导出exportRoots到outputDir(FModel兼容树)
uex export --profile aion2
uex export --profile aion2 --only AION2/Content/UI # 子集按虚拟路径
# 浏览虚拟文件系统
uex list --profile aion2 AION2/Content/UI
uex search --profile aion2 "*.uasset" --regex --limit 200
# 将一个资产转储为FModel样式的JSON到stdout(处理UE软件包和.dat文件)
uex preview --profile aion2 AION2/Content/UI/SomeTexture --max-bytes 200000
# 将单个纹理导出为PNG
uex preview-texture --profile aion2 AION2/Content/UI/SomeTexture --out out.png
```
**导出输出布局**(匹配FModel — arkive `tools/`管道依赖于它):UE软件包(`.uasset`/`.umap`)→ `.json`(序列化导出数组);`UTexture`导出 → `.png`;AION2混淆`.dat`数据文件 → 解码`.json`;其他 → 原始复制。运行打印:
```
已导出:N个软件包,N个纹理,N个原始文件,N个解码数据文件 -> <outputDir>
```
### serve — JSON行 stdin/stdout
从一个进程中提供**所有**配置文件;每个配置文件在首次使用时延迟挂载。每个请求/响应一行:
```
> {"id":1,"cmd":"list","profile":"aion2","args":{"path":"AION2/Content/UI"}}
< {"id":1,"ok":true,"result":[ ... ]}
```
命令:`profiles`、`list`、`search`、`preview`、`preview-texture`、`export`、`shutdown`。错误返回为`{"id":..,"ok":false,"error":"..."}`。
### mcp — MCP stdio 服务器
官方C# MCP SDK。工具:`profiles`、`list_dir`、`search_paths`、`preview_asset`、`preview_texture`、`export_assets`。除`profiles`外,每个工具都需要`profile`参数,因此一个服务器涵盖所有游戏。注册它:
```bash
dotnet publish src/Uex -c Release -o publish
claude mcp add uex --scope user \
-e UEX_PROFILES=E:/arkive-games/uex/profiles.json \
-- E:/arkive-games/uex/publish/uex.exe mcp
```
## 多个游戏
配置文件*是*游戏。因为`profile`参数贯穿每个操作,并且按配置文件缓存挂载,所以单个`serve`或`mcp`进程通过延迟挂载和按配置文件`outputDir`透明地支持所有已配置的游戏。添加新游戏只需添加配置文件条目 — 无需代码更改。
## 游戏特定说明
- **AION2** 将大部分游戏数据存储在 `AION2/Content/Data/...` 下的混淆的松散 `.dat` 文件中。uex 将这些文件解码为 JSON,具有 FModel 兼容性(字节相同):表文件(密钥清单 AES + LZ4)、地图/世界地图(密钥流)、地图事件(解密 JSON 文本)和地图数据层次结构读取器。所有这些都隔离在 `Core/Aion2Dat.cs` 中。
- **首次挂载下载本机解压缩 DLL**(Oodle、zlib)到 `.uex-cache/` 目录中与可执行文件旁边 — 这需要一次网络访问。后续运行是离线的。
## 开发
```bash
dotnet build
dotnet test # 49 个测试,不需要游戏 pak
dotnet run --project src/Uex -- doctor --profile aion2 # 真实 pak 烟雾测试
dotnet publish src/Uex -c Release -o publish
```
单元测试覆盖纯逻辑(`Core/OutputPaths`、`Core/VfsQuery`、配置加载),不需要 pak。真实 pak 验证是针对已安装游戏的 `doctor`。
## License
Copyright (c) 2026 Yihao Liu (tc-imba)
在 [Apache License 2.0](LICENSE) 下许可 — 与 [CUE4Parse](https://github.com/FabianFG/CUE4Parse) 相同,后者是此工具的基础。