把服务端 XML 的 git unified diff 应用到客户端 .img 文件,保留所有未触及的 PNG / Sound / Canvas / UOL / Vector 等二进制资源。
适用场景:服务端用瘦 XML(只含业务节点)维护文本/数值变更,客户端的 .img 里有完整图标/音效/UI 资源;要把服务端的改动同步回客户端,又不能丢资源。
直接的"反向重生成 .img"流程会把客户端那些只存在于 .img、不存在于服务端 XML 的资源(PNG 图标、音效、UOL 引用、Vector 几何…)全部丢掉——之前出过 0403.img: 1.3 MB → 70 KB、Skill/000.img: 3.1 MB → 4.9 KB 这种事故。
本工具走另一条路:直接打开 .img、按 diff 改节点、原样写回,不重建文件。Diff 没碰到的节点一字节不动。
xml-img-patcher patch <input.img> <diff> <output.img> [选项]
xml-img-patcher dump-xml <input.img> <output.xml> [选项]
xml-img-patcher batch <img目录> <diff目录> <输出目录> [选项]
xml-img-patcher batch-dump-xml <img目录> <xml输出目录> [选项]
xml-img-patcher verify <patched.img> <diff> [full-xml或目录] [选项]
xml-img-patcher export --from=<hash或datetime> [选项]
xml-img-patcher dump-changes <diff> [full-xml或目录] [选项]
xml-img-patcher sync --client=<客户端根> --out=<输出根> [选项]
| 子命令 | 作用 |
|---|---|
patch |
对一个 .img 应用一个 .diff,输出新 .img。保留 PNG/Sound/UOL 等所有 diff 没碰过的二进制资源 |
dump-xml |
把 .img 转成服务端格式的 .xml,方便肉眼看或对比 |
batch |
批量版的 patch。按文件名自动配对:diff 目录下 a/b/Foo.img.xml.diff → 找 img 目录里的 a/b/Foo.img → 写到输出目录 a/b/Foo.img。diff 目录可多层嵌套,工具会递归扫所有 *.diff。没找到对应 img 的 diff 会跳过并在最后 BATCH SUMMARY 汇总 |
batch-dump-xml |
批量版的 dump-xml。递归把目录下所有 .img 都转成 .xml |
verify |
校验:直接加载 patch 后的 .img,把 diff 里每条 + 变更(Add/Modify)查节点比对值;DELETE 查节点是否已消失。绕过 dump-xml 序列化,测的就是 img 的实际内容 |
export |
从 git 仓库导出指定起点之后的 wz xml 与 diff。--from 同时支持 commit hash 和 datetime |
dump-changes |
调试用:打印 DiffParser 解析 diff 后得到的所有 Change(op / path / value / 源行号),不写文件 |
sync |
从服务端仓库/目录直接同步到客户端 img(节点级三方对比,砍掉文本 diff)。三种来源:--repo+--from(git 增量)、--server=<xml目录>(全量匹配)、--repo+--ref(git 全量)。替代 wz-sync.py 的 Python 编排 |
| 选项 | 说明 |
|---|---|
-v, --verbose |
失败时打印完整堆栈 |
--dry-run |
解析 diff、加载 img、模拟 patch,不写文件 |
--strict |
任何一条 change 失败立即中止;默认是尽力做完,最后汇总 |
--iv <GMS|EMS|BMS|CLASSIC> |
WZ 加密 IV,默认 GMS(大小写不敏感) |
--full-xml <file> |
完整服务端 XML(diff +++ 那一侧的最终文件)。当 hunk 上下文不带外层 imgdir 时,用它从 hunk 头的行号反查路径栈,避免歧义/找不到节点。强烈推荐配 |
--full-xml-dir <dir> |
完整服务端 XML 根目录,会按 diff 路径自动配对。批量处理时用这个,比 --full-xml 省事 |
| 选项 | 说明 |
|---|---|
--full-xml-dir <dir> |
完整服务端 XML 根目录(按 diff 路径自动配对) |
--dry-run / --strict / --iv / -v |
同 patch |
| 选项 | 说明 |
|---|---|
--iv <GMS|EMS|BMS|CLASSIC> |
同上 |
--linux |
用 LF 行尾(默认 CRLF) |
--indent <N> |
缩进空格数(默认 4,与 Java 版一致) |
默认会跳过 PNG / Sound 等二进制资源(只输出节点骨架),便于纯文本对比。
| 选项 | 说明 |
|---|---|
<full-xml 或目录> |
第 3 个位置参数。完整服务端 XML 文件,或与之同布局的目录(工具会按 diff 文件名配对查找)。用来恢复 hunk 路径栈 |
--iv <GMS|EMS|BMS|CLASSIC> |
同上 |
-v |
打印每条 ok / miss |
| 选项 | 说明 |
|---|---|
--from <hash或datetime> |
起点(必填)。commit hash 或 datetime 两种形态 |
--repo <dir> |
git 仓库根目录(默认当前目录) |
--out-xml <dir> |
xml 输出根(默认 ~/Desktop/upgrade_yyyyMMdd) |
--out-diff <dir> |
diff 输出根(默认 ~/Desktop/diff_yyyyMMdd) |
--prefix <pref> |
扫描目录前缀(可多个,默认 gms-server/wz、gms-server/wz-zh-CN) |
--no-diff |
只复制 xml,不生成 diff |
--context <N> |
git diff 上下文行数 -U(默认 30) |
| 选项 | 说明 |
|---|---|
--repo <dir> |
git 仓库根目录。给 --from → 增量模式;给 --ref → git 全量 |
--from <hash或datetime> |
起点。增量模式(带 DELETE 意图 + 报告改了什么) |
--ref <ref> |
git ref/tag/branch。全量模式(不需要 from 基线) |
--server <dir> |
服务端 XML 目录。全量匹配(完全不需要 git) |
--client <dir> |
客户端根目录(必填) |
--out <dir> |
输出根(默认写这里;--in-place 时可省略) |
--in-place |
直接写客户端(默认安全模式:先出 out 再部署) |
--mode review|trust |
值差异标记语义(默认 review)。review 把 third-default 列进复核清单;trust 静默对齐 |
--review-out <file> |
把人工复核清单写到文件(每行一条) |
--strict |
两方全量下,client 独有业务节点也 DELETE(二进制永远保留) |
--prefix <pref> |
扫描目录前缀(可多个,默认 gms-server/wz、gms-server/wz-zh-CN) |
--iv / --dry-run / -v |
同其他子命令 |
sync 的层→客户端映射:wz-zh-CN→Data/,wz→EN/(EN 不存在 fallback Data/),同 img 多层映射时 zh 优先。
| 码 | 含义 |
|---|---|
| 0 | 全部成功 |
| 1 | 部分 change 失败但已写出(非严格模式) |
| 2 | 参数错误或文件不存在 |
| 3 | diff 解析失败 |
| 4 | img 解析失败 |
| 5 | img 写入失败 |
可被 AI / shell 脚本解析,关键字 MODIFY / ADD / DELETE / [ok] / [err] 永远是英文:
[parse] 12 changes from diff
[ok] MODIFY Mob.img/9999999/name = "已杀怪物数"
[ok] ADD 0403.img/04031786 (subtree, 2 nodes)
[err] MODIFY Foo/Bar — node not found
3 applied, 1 failed. Output: D:\out.img (1,335,712 bytes)
batch 末尾额外有 BATCH SUMMARY,列出 ok/fail/skip 计数和失败/跳过的文件清单。
xml-img-patcher patch \
--full-xml=C:/upgrade_20260622/wz-zh-CN/Quest.wz/QuestInfo.img.xml \
C:/client/Data/Quest/QuestInfo.img \
C:/diff_20260622/wz-zh-CN/Quest.wz/QuestInfo.img.xml.diff \
C:/out/Quest/QuestInfo.imgxml-img-patcher patch --dry-run --full-xml="..." input.img diff output.img # 不写文件
# 确认 0 failed 后去掉 --dry-run 实跑xml-img-patcher batch \
--full-xml-dir=C:/upgrade_20260622/wz-zh-CN \
C:/client/Data \
C:/diff_20260622/wz-zh-CN \
C:/out/Data末尾会打印 BATCH SUMMARY,汇总 ok / fail / skip 文件数和每个失败/跳过的原因。
xml-img-patcher verify \
C:/out/Quest/QuestInfo.img \
C:/diff_20260622/wz-zh-CN/Quest.wz/QuestInfo.img.xml.diff \
C:/upgrade_20260622/wz-zh-CN输出 verify: N expected, N match, 0 miss 即通过。
xml-img-patcher dump-xml "E:/Client/EN/String/Mob.img" "C:/out/Mob.xml" --linux# 按 commit hash
xml-img-patcher export --from=27529d68 --repo="E:/LocalGit/GitHub/BeiDou-Server"
# 按时间点(找该时刻前最近一次 commit 作为起点)
xml-img-patcher export --from="2026-06-22 14:00" --repo="E:/LocalGit/GitHub/BeiDou-Server"# Step 1: 从服务端 git 仓库导出补丁数据
xml-img-patcher export --from=27529d68 \
--repo="E:/LocalGit/GitHub/BeiDou-Server" \
--out-xml=C:/upgrade --out-diff=C:/diff
# Step 2a: wz 层(英文层)应用到客户端 EN 目录
xml-img-patcher batch --full-xml-dir=C:/upgrade/wz \
E:/Client/EN C:/diff/wz C:/out/EN
# Step 2b: wz-zh-CN 层应用到客户端 Data 目录
xml-img-patcher batch --full-xml-dir=C:/upgrade/wz-zh-CN \
E:/Client/Data C:/diff/wz-zh-CN C:/out/Data
# Step 3: 校验
xml-img-patcher verify C:/out/Data/Quest/Say.img \
C:/diff/wz-zh-CN/Quest.wz/Say.img.xml.diff \
C:/upgrade/wz-zh-CN关键映射规则(patch/batch/sync 共用):
- 服务端
wz/层 → 客户端EN/(英文文本)目录(若不存在则回退到Data/) - 服务端
wz-zh-CN/层 → 客户端Data/(中文汉化)目录 - diff 路径
String.wz/Mob.img.xml.diff自动剥.wz段 → img 路径String/Mob.img
sync 走节点级三方对比,不生成文本 diff,替代 wz-sync.py 那套 Python 编排:
# git 增量(--from 起点 → HEAD 的变更,带 DELETE 意图 + 复核清单)
xml-img-patcher sync --repo=E:/LocalGit/GitHub/BeiDou-Server \
--from=27529d68 \
--client=E:/LocalGit/GitHub/BeiDou-Client \
--out=C:/out \
--review-out=C:/out/review.txt
# 服务端 XML 目录全量匹配(完全不需要 git)
xml-img-patcher sync --server=C:/upgrade/wz-zh-CN \
--client=E:/LocalGit/GitHub/BeiDou-Client \
--out=C:/out
# git 某 ref 全量匹配(不需要 from 基线)
xml-img-patcher sync --repo=E:/LocalGit/GitHub/BeiDou-Server --ref=HEAD \
--client=E:/LocalGit/GitHub/BeiDou-Client --out=C:/out末尾打印 SYNC SUMMARY(ok/nochange/fail/review)+ 人工复核清单(third-default / type-conflict / missing-unmodified)。
GitHub Releases 提供编译好的单文件 exe,目标机无需安装 .NET 运行时:
- 最新版下载页:https://github.com/SleepNap/MapleLib-cli/releases/latest
- 直接拉 exe:https://github.com/SleepNap/MapleLib-cli/releases/latest/download/xml-img-patcher.exe(约 80 MB,self-contained)
需要改代码或调试见下方「构建」。
要求 .NET 10.0+:
dotnet build MapleLib.XmlImgPatcher/MapleLib.XmlImgPatcher.csproj -c Release发布为 self-contained 单文件 exe(csproj 已默认 PublishSingleFile + win-x64):
dotnet publish MapleLib.XmlImgPatcher/MapleLib.XmlImgPatcher.csproj -c Release
# 产物在 bin/Release/net10.0-windows/win-x64/publish/xml-img-patcher.exe项目根 publish.bat 是封装好的一键构建,产出复制到 dist/xml-img-patcher.exe。
- v2(2026-08-08):修复 Say.img 8072/28344 等兄弟重排 imgdir 的假 DELETE 导致丢 8 个中文节点的问题(新增
CancelReorderDeletespass,详见TODO.md0.5 节)。全量 154 diff 零回归。
| 实现 | 仓库 | 产物 |
|---|---|---|
| C# | https://github.com/SleepNap/MapleLib-cli | dist/xml-img-patcher.exe(.NET AOT/publish 单文件) |
| Java | https://github.com/SleepNap/orange-wz-cli | dist/xml-img-patcher.exe(GraalVM native,standalone) |
两边功能、子命令、选项、退出码、输出格式完全一致,脚本可互换。两边 dump-xml --linux 非 canvas 部分逐字节一致;canvas 节点 C# 输出 width/height 真实尺寸,Java 输出 format/scale(Java 侧待修,见姊妹仓库 issue)。