Batch 2R: 40250 pack inventory (epk_scan) and delivery decision
Read-only scanner for Client/pack following PackInitialize -> CEterPackManager
-> CEterPack::__BuildIndex/Get2. Keys are read from the reference EterPack.cpp
at run time; none are stored. Pure-Python TEA + LZO1X.
- 136 registered packs, 54,891 entries: NONE/COMPRESS/SECURITY only, no
PANAMA/HybridCrypt, all entries decode
- first-registered pack wins; 2,282 shadowed entries (2,194 differ)
- metin2_patch_{sd,mineral,mundi} use a second packer layout; 4 SECURITY
files fail 40250's CRC check and never load in the original
- no case collisions; paths are already lowercase
- PORT-PLAN: read EPK on device via ported EterPack, base install minus
extra locales, rebuild dict in Index order on any pack change
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+76
-18
@@ -81,7 +81,7 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
| 1 | 删除运行时不加载的 106 个 `*_system.gd` 及其测试 | 完成(a989b8f1) |
|
||||
| — | 定下目录结构,评估内嵌 Python;Android 独立程序验证 | 完成(9d0e50de、b02c49bb) |
|
||||
| **2A** | 基础:Win32 类型层、参考公共头的最小闭包、`port_logic` CMake 目标、platform 接口骨架、头文件可编译门禁、port-map 重新基线 | **进行中**(剩 Python 库闭包头,等 2P) |
|
||||
| **2R** | 资源包能力盘点:EPK 类型/密钥/覆盖顺序/路径大小写/移动端交付 | **下一步**,可与 2A 并行(只读分析 + 独立工具) |
|
||||
| 2R | 资源包能力盘点:EPK 类型/密钥/覆盖顺序/路径大小写/移动端交付 | 完成:只有 NONE/COMPRESS/SECURITY,不需要服务器密钥;设备上直接读 EPK |
|
||||
| 2P | CPython 2.7.18 编进 libmtgodot,五个平台分别配置和验证 | 2A 之后 |
|
||||
| 2D | 数据源切到 40250(msm 路径、proto、资源根、严格资源测试) | proto 部分在 2A 之后;资源根在 2R 之后 |
|
||||
| **2V0** | UI 壳:真实 `PythonLauncher/wndMgr/grp/app` + platform UI,其他玩法模块用桩,显示并操作 Logo/Popup | 需要 2A、2P 和 root/uiscript 子集 |
|
||||
@@ -202,23 +202,80 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
已知的无关问题:完整 Android 扩展构建在 `extension/src/net/text_codec.cpp` 失败(bionic 从 API 28 才声明 `iconv`,
|
||||
而构建用 API 24;c9389431 引入);仓库里的 `build-android/` 缓存还指向旧路径 `mtgodot-poc`。
|
||||
|
||||
### 批次 2R:资源包能力盘点
|
||||
### 批次 2R:资源包能力盘点(完成,2026-09-22)
|
||||
|
||||
40250 的读取链不只是 `EterPack.cpp`,还有 `EterPackManager.cpp`(多包覆盖顺序、路径归一化)、
|
||||
`EterPackPolicy_CSHybridCrypt.cpp`(HybridCrypt,密钥可能来自登录/握手阶段的服务器下发)、`CMappedFile`、`CLZO`,
|
||||
以及 TEA/Panama/Camellia/Twofish/XTEA。压缩类型有六种:`NONE`、`COMPRESS`、`SECURITY`、`PANAMA`、
|
||||
`HYBRIDCRYPT`、`HYBRIDCRYPT_WITHSDB`。`Client/pack` 有 217 个文件、约 1.3 GB。
|
||||
工具:`tools/epk_scan/epk_scan.py`(只读)。`summary` 统计并写 `audit/packs/2R-scan.json`;`verify` 解码全部可本地解的条目。
|
||||
索引密钥和 SECURITY 密钥运行时从参考树 `EterPack/EterPack.cpp` 读取,仓库里没有密钥。读取链照 40250:`PackInitialize`
|
||||
(`pack/Index`)→ `CEterPackManager::RegisterPack`(每个包再注册 `<name>_texcache`,最后 `RegisterRootPack("pack/root")`)
|
||||
→ `CEterPack::__BuildIndex`(`.eix` 全部是 `MCOZ`:TEA + LZO 包着 `EPKD` v2,每条 192 字节)→ `Get2`。
|
||||
|
||||
1. 写一个只读的扫描工具(Python,放在 `tools/`),解析所有 `.eix`,统计每个包、每种 `compressed_type` 的文件数和字节数。
|
||||
2. 按统计结果决定:
|
||||
- 只有 `NONE/COMPRESS/SECURITY` 的包可以离线解出;
|
||||
- `PANAMA`/`HYBRIDCRYPT*` 如存在,确认密钥来源:本地的 `Index`/配置,还是服务器 `GC_HYBRIDCRYPT_KEYS`/`SDB` 下发。
|
||||
一次真服登录抓取仅可用于确认格式和密钥来源,不能作为构建或发布依赖;正式方案必须是可重复的运行时密钥链,或经过
|
||||
授权、可重复并有来源清单的重新打包流程,密钥本身不进仓库。
|
||||
3. 验证 `Index` 文件列出的包顺序和同名文件的覆盖优先级,与 `CEterPackManager` 一致。
|
||||
4. 路径归一化:40250 在 Windows 上大小写不敏感,并把 `d:/ymir work/` 等前缀映射到包内路径。确定统一的小写化规则,扫描大小写冲突。
|
||||
5. 移动端交付:决定最终形式(例如解包后重新打成我们自己的 `mtpack`,或直接在设备上读 EPK),给出 Android/iOS 的包体积、首包与按需下载的划分,以及更新方式。`MT_ASSETS` 只是开发期覆盖。
|
||||
6. 输出写进本文第 5 节,再决定 2D 的资源根切换方式。
|
||||
1. **类型统计**(Index 注册的 136 个包,54,891 条,52,609 个不同路径):
|
||||
|
||||
| 类型 | 文件数 | 存储 | 解码后 |
|
||||
| --- | --- | --- | --- |
|
||||
| `NONE` | 20,727 | 737.2 MiB | 737.2 MiB |
|
||||
| `COMPRESS`(LZO) | 29,812 | 592.9 MiB | 1296.8 MiB |
|
||||
| `SECURITY`(TEA + LZO) | 4,352 | 2.0 MiB | 6.6 MiB |
|
||||
| `PANAMA` / `HYBRIDCRYPT*` | **0** | — | — |
|
||||
|
||||
`verify --per-type 1000000` 全部 54,891 条解码成功。每个包的类型分布见 JSON 的 `per_pack`。
|
||||
2. **密钥来源**:这个客户端只用编译进客户端的两组 TEA 密钥(`s_adwEterPackKey` 解索引,`s_adwEterPackSecurityKey` 解
|
||||
SECURITY 数据)。没有 PANAMA 和 HybridCrypt 条目,所以**不需要服务器下发 IV/密钥**,也不需要真服抓包。
|
||||
`CAccountConnector` 的 `RegisterPack(..., abIV)` 和 `GC_HYBRIDCRYPT_KEYS/SDB` 照抄移植即可,但这批资源用不到。
|
||||
移植后的 `EterPack.cpp` 需要这两组常量;按"密钥不进仓库",由构建时从参考树生成一个 gitignore 的头文件,不直接写进源码。
|
||||
3. **注册顺序与覆盖**:
|
||||
- `Index` 第 0 行是 `PACK`,后面是 (目录, 名字) 对,共 124 个名字。
|
||||
- 实际存在的包有 136 个:Index 里的包,加上两个 `metin2_patch_dragon_rock*_texcache`,再加 `root`。
|
||||
- 缺失的包:106 个 `_texcache`,以及 `locale_{ae,bg,ca,mx,pa,uk,us}`。40250 遇到缺失的包只记日志、继续执行。
|
||||
- 目录里有、但没注册的文件,40250 永远不会读到:`Outdoor`、`Psyco`(0 字节)、`outdoorempirebattle1`、两个 `.rdch`。
|
||||
`.rdch` 在源码里没有任何引用。
|
||||
- 查找由 `CEterFileDict` 完成,它是以 `filename_crc` 为键的 `unordered_multimap`。`GetItem` 在 `equal_range` 里返回第一个
|
||||
同名项。40250 用 VS2022(`ClientVS22`)构建,MSVC STL 的 multi 插入会把新元素放在同键已有元素的后面,所以**先注册的
|
||||
包优先**。`UpdateItem`(后注册的覆盖先注册的)只在带 IV 的注册里用到,这批资源没有。
|
||||
- 同名条目共 2,282 个,其中 2,194 个内容不同。主要是 `patch1`/`patch2`/`season3_eu`/`metin2_patch_*` 覆盖后面的
|
||||
`zone`、`pc*`、`season2`、`outdoor*`、`icon`。**覆盖顺序会影响画面,必须 1:1。**
|
||||
- 包里有两种打包器留下的格式:
|
||||
- Ymir 格式:`data_size` 不小于整个 CLZObject,常补到 256 字节的倍数;`data_crc` 按存储字节算。
|
||||
- 另一种格式出现在 `metin2_patch_sd`、`metin2_patch_mineral`、`metin2_patch_mundi` 三个包里:`data_size` 比 CLZObject
|
||||
短 16 字节,`data_crc` 按解码后的内容算。
|
||||
- 在第二种格式下,40250 的 COMPRESS 条目仍能读出来:`CLZO::Decompress` 按 LZO 头里的长度读,不看 `data_size`,读到
|
||||
映射视图里 `data_size` 之后的字节。但 SECURITY 条目过不了 `Get2` 的 CRC 检查。
|
||||
- 结果是 4 个文件在原版里**加载失败**:`npc/mineral2/wait.msa`、`effect/background/metinstone_loop_redblack.mse`、
|
||||
`monster2/spider_spawn/{wait.msa,line1.mse}`。第 5 个 `effect/etc/buff/buff_symbol1.mse` 由先注册的 `metin2_patch_eu4`
|
||||
提供,不受影响。
|
||||
- 移植的读取器要照这个行为实现:按头部长度读,保留 CRC 拒绝。
|
||||
4. **路径归一化**:
|
||||
- `ConvertFileName` 只做两件事:ASCII `tolower`,以及 `\` 转 `/`。CP949 字节原样保留。
|
||||
- 包内名字全部已经是小写、只用 `/`;52,609 个路径没有大小写冲突,`filename_crc` 与 `crc32(name)` 全部一致。
|
||||
- `d:/ymir work/...` 是包内的真实路径,一共 26,955 个。40250 的相对路径映射已经注释掉,所以调用方必须使用 40250 原样的
|
||||
路径名,不能再用 m2dev `assets/` 的相对路径。
|
||||
- 包文件名与 `Index` 大小写不一致,例如 `Etc` 对应磁盘上的 `ETC.eix`。NTFS 下能打开;在移动端和 Linux 上,按包名查找
|
||||
磁盘文件时要做大小写不敏感匹配。
|
||||
5. **移动端交付**(已注册包的存储大小共 1337 MiB):
|
||||
|
||||
| 分组 | 大小 |
|
||||
| --- | --- |
|
||||
| `metin2_patch_*`/`patch1`/`patch2`/`season3_eu` | 692.7 MiB |
|
||||
| 核心 `d:/ymir work`(pc/monster/zone/effect/npc/item…)+ icon/property/textureset | 405.7 MiB |
|
||||
| sound/bgm | 87.6 MiB |
|
||||
| season1/2 | 71.5 MiB |
|
||||
| 其他 14 种语言的 locale | 49.9 MiB |
|
||||
| 地图包 | 25.9 MiB |
|
||||
| `locale_en` | 3.5 MiB |
|
||||
| root + uiscript | 0.6 MiB |
|
||||
|
||||
决定:
|
||||
- **设备上直接读 EPK**,用移植的 `EterPack`/`EterPackManager` 经 `asset_io` 读取。不重新打包成 `mtpack`。这样覆盖顺序、
|
||||
CRC 拒绝等行为天然 1:1,更新时直接替换包文件。
|
||||
- 首包包含 Index 里除其他语言外的全部包,约 1.29 GB。其他语言按需下载。
|
||||
- patch 包在覆盖链的最前面,按需下载会改变已有文件的内容,所以不做细粒度按需下载。
|
||||
- 如果以后要按需下载,任何新到的包都必须**按 Index 顺序重建整个字典**,不能追加到末尾。否则先注册者优先的结果就变了。
|
||||
- 更新方式是整包替换 `.eix/.epk`,由我们自己的清单记录版本号和 sha256。
|
||||
6. **2D 的资源根切换**:
|
||||
- 新增 pack 后端:`asset_io` 在 `MT_ASSETS` 之外,接上移植的 `CEterPackManager`,按 `pack/Index` 注册。
|
||||
- 渲染和 GDScript 的资源请求改用 40250 路径,经过 `ConvertFileName` 查找。
|
||||
- m2dev `assets/` 的散文件保留作开发期覆盖,直到对应读取方切到新路径。
|
||||
- 严格门禁(第 5 节步骤 3)的来源清单改为记录各个 `.eix/.epk` 的 sha256。
|
||||
|
||||
### 批次 2P:内嵌 Python 集成
|
||||
|
||||
@@ -276,7 +333,7 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
| `mob_proto` | `proto.cpp` | **读不了**:每条 255 字节 vs 335 字节,加密同上 | 同上 |
|
||||
| `item_list.txt` | `item_list.gd` | 177 行不同(m2dev 有 dummy 条目等);40250 放在 `locale/en/`,m2dev 在 `locale/common/` | 按 40250 路径读取 |
|
||||
| `locale_interface.txt`、`itemdesc.txt`、`skilldesc.txt`、`skilltable.txt` | UI、技能表 | 20 行 / 2 行 / 2 行 / 位置不同 | 按 40250 路径读取 |
|
||||
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | **未核对**:40250 是经典 `.eix/.epk` 包 | 由 2R 决定 |
|
||||
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | 40250 是经典 `.eix/.epk`,136 个包、52,609 个路径,全部可以本地解码(2R) | 移植 `EterPack`/`EterPackManager` 接到 `asset_io`,请求改用 40250 路径(见 2R 第 6 条) |
|
||||
|
||||
切换步骤(批次 2D):
|
||||
|
||||
@@ -288,7 +345,8 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
- 为 40250 的 root、locale、proto 和关键包生成来源清单(路径 + sha256),提交到 `audit/`,测试开始时核对。
|
||||
4. 新增并在 CI/发布前强制运行 `script/run_40250_asset_gate.sh`;它设置/校验 `MT_ASSETS_STRICT=1`,运行 native proto/pack/formats
|
||||
测试和 Godot root/locale/render 测试。脚本在没有 40250 资源时必须失败,不能把跳过报告成 PASS。
|
||||
5. 资源根切换方式按 2R 的结论执行,跑严格门禁,更新本节状态。
|
||||
5. 资源根切换按 2R 第 6 条执行:移植 `EterPack`(`CLZObject`/TEA 已经在第 2 步)和 `EterPackManager`,接上
|
||||
`asset_io` 的 pack 后端,读取方逐个改用 40250 路径,跑严格门禁,更新本节状态。
|
||||
|
||||
## 6. 进度与 port-map 基线
|
||||
|
||||
|
||||
Reference in New Issue
Block a user