
《戦巫〈センナギ〉》存档修改与脚本逆向全复盘
创作说明:本文基于作者与 AI 的调试对话整理生成,完整保留问题排查思路与技术细节。
1. 背景
| 项目 | 说明 |
|---|---|
| 游戏 | 戦巫〈センナギ〉―穢れた契りと神ころも― |
| 公司/引擎 | Escu:de(エスクード)/ YU-RIS |
| 版本 | 已打 UIF 注入式汉化补丁 |
| 好结局条件 | 神気率 100% → 触发“觉醒之仪” → 姫華线好结局 |
| 坏结局 | 神気未满 → 男主、女主、夏洛特同归于尽 |
| 用户现状 | 已通关一周目(坏结局),战斗系统繁琐不想二周目 |
核心挑战:把神気从 51% 改到 100%,让用户在现有存档上直接打出好结局。
2. 技术路径全景图
存档直改 ──→ 预览有效,游戏内无效 ──→ 校验拦截读不了档
│
▼
内存扫描 ──→ u8/u16/u32/f32 全试 ──→ 只找到预览副本
│
▼
解包脚本 ──→ EscudeTools 解 ESC-ARC2 + YU-RIS VM ──→ 定位觉醒判定逻辑
│
▼
改脚本重打包 ──→ 绕开神気≥100 判定 ──→ ✅ 成功
3. 阶段一:存档文件直改 ❌
3.1 存档结构
| 文件 | 大小 | 说明 |
|---|---|---|
save_00X.dat |
~1.9MB | 手动存档,YU-RIS 二进制序列化,含预览区和加密数据区 |
game_data.sys |
457KB | 系统档,含 CG/标记数据,不含神気 |
路径:C:\Users\<user>\Documents\ESCUDE\Sennagi\save\
3.2 第一次发现:预览字段
通过对比 20 个存档的递增轨迹(0→1→3→4→6→11→15→23→42→51→59),定位到偏移 0x2218A 处是神気的 4 字节整数预览字段。但这个字段只影响存档列表的显示,读档后游戏内神気不变——它是独立预览副本。
3.3 为什么直改失败
- 游戏中实际的属性数据非明文存储——搜索 449/292/319/4060 等属性值,存档中找不到对应字节
- 直接修改 0x2218A → 游戏校验拦截,存档读不出来(YU-RIS 存档有完整性校验)
- 神気不是独立变量:游戏根据战斗记录动态计算,而非存一个固定整数
根因:YU-RIS 的存档体(Save Body)对实际游戏状态进行了加密/编码。预览区只是给存档列表看的,和游戏运行时数据是两套。
4. 阶段二:内存扫描 ❌
4.1 尝试的扫描方式
| 编码 | 方法 | 结果 |
|---|---|---|
| u8 | 单字节值搜索 51/59/67 | 命中太多假阳性(图像纹理缓冲区) |
| u16 | 双字节值搜索 | 同上 |
| u32 | 四字节搜索 + 差分对比 | 找到候选但写入后只影响预览 |
| f32 | 浮点数搜索 | 不存在 |
| 连续序列 | [69, 4780, 505, 403, 410] 紧凑存储 |
不存在 |
| 三重交叉验证 | 同地址“曾为51、曾为59、现为67” | 无命中 |
4.2 为什么都不行
- 真正的神気变量地址不固定(每次战斗对象重建,内存地址漂移)
- 写入 100 到所有候选地址后,游戏内神気显示不变——这些候选全是 UI 缓存/预览副本
- 神気可能是从练度等底层数据计算得出(当前/上限百分比),不是独立存储
关键教训:YU-RIS 引擎的 VM 全局变量(GVAR)在内存中是通过 VM 解释器间接访问的,扫描直接内存值找不到真正的运行时变量。
5. 阶段三:解锁关键信息 —— 解包脚本 🔬
5.1 工具链
使用开源工具 EscudeTools(GitHub),功能:
- 解 ESC-ARC2 压缩包(游戏脚本的封包格式)
- 解 YU-RIS VM 字节码脚本
- 导出为 SQLite 数据库供分析
编译适配:源码目标 .NET 8,本机只有 .NET 6 和 10。改 TargetFramework 为 net10.0 后重编译成功。
5.2 解包结果
script.bin (5.9MB, ESC-ARC2 加密压缩)
└─ 247 个子脚本文件 (.bin, YU-RIS 字节码)
├─ a_prol_*.bin ← 序章
├─ b_05st*.bin ← 第五章
├─ b_06st*.bin ← 第六章
├─ b_07hime_01_* ← 好结局(姫華觉醒成功)
├─ b_07hime_02_* ← 同归于尽坏结局(觉醒失败)
├─ b_07dark_* ← 悪堕ち线
├─ z_flow_main.bin ← ⭐ 系统路由脚本(主流程控制)
├─ h_04chk_gp.bin ← 神気检查
└─ h_04chk_gpmax.bin ← 神気满检查
5.3 从脚本中确认的关键信息
通过搜索 YU-RIS VM 指令,确认:
神気 = 全局变量 #81(指令 INST_PUSH_GVAR 0x51)
觉醒判定在 z_flow_main.bin 中
主流程按 gvar41 分阶段:
- gvar41 == 2 → 神気进度检查阶段
- gvar41 == 3 → 觉醒判定阶段
关键判定指令序列:
INST_PUSH_GVAR 0x51 ; 读取 GVAR[81] = 神気
INST_PUSH_INT 100 ; 压入阈值 100
INST_CMP_GE ; 比较:神気 >= 100?
INST_JMPZ LABEL_BAD ; 不满足 → 跳转坏结局
6. 阶段四:成功 —— 改脚本重打包 ✅
6.1 修改目标
改的是 z_flow_main.bin 里的两处判定:
| 位置 | 偏移 | 原始逻辑 | 修改 |
|---|---|---|---|
| @3001(觉醒闸门) | gvar41==3 触发 | 神気 ≥ 100 才觉醒 |
无条件跳进觉醒分支 |
| @2322(流程推进门) | gvar41==2 触发 | 神気 < 100 才能推进 |
改成永远成立 |
6.2 改了什么
# @3001 觉醒判定:
原始:PUSH_GVAR(81) PUSH_INT(100) CMP_GE → JMPZ(跳坏结局)
修改:PUSH_GVAR(81) PUSH_INT(0) CMP_GE → 永远成立
+ 条件跳转改为无条件跳转 → 保险绕过
# @2322 流程推进:
原始:PUSH_GVAR(81) PUSH_INT(100) CMP_LT → 神気<100
修改:PUSH_GVAR(81) PUSH_INT(0) CMP_LT → 永远不成立,不卡流程
6.3 打包与部署
# 1. 修改子脚本字节(hex 编辑)
# 2. 用 EscudeTools 重打包为 script.bin
EscudeTools -r output_dir
# 3. 备份原脚本 + 替换
copy script.bin script.bin.orig
copy new_script.bin "D:\games\戦巫<...>\script.bin"
6.4 第一次失败 & 排查
第一次只改了 @3001(觉醒判定),但用户测试后仍然是坏结局。排查发现:主流程按 gvar41 分阶段,gvar41==2 时走的是 @2322 那个神気检查(不是觉醒判定),用户流程根本没进到觉醒阶段就分流了。
因此补改了第二处 @2322,两处一起改才生效。
7. 全部尝试一览
| 尝试 | 方法 | 结果 | 失败原因 |
|---|---|---|---|
| 直接改存档 0x2218A | 二进制写入 100 | 预览变,游戏内不变 | 只是预览字段 |
| 改存档后读档 | 覆盖 100 后读档 | 读不了档 | 校验拦截 |
| 内存写 100 到候选地址 | Python 进程注入 | 游戏内不变 | 只改到预览副本 |
| 连续属性序列搜索 | 内存搜 [69,4780,505,...] |
不存在 | 属性不连续存储 |
| f32 浮点搜索 | 搜 float 表示 | 不存在 | 不是浮点存储 |
改 game_data.sys |
对比两次存档的系统档 | 无变化 | 神気不在这里 |
| Gemini 给的工具 | .py 和 .ct 两个文件 |
用不了 | .py 会乱改内存,.ct 缺少注入地址 |
| 最终方案 | 改脚本字节 → 重打包 | ✅ 成功 | — |
8. 方法论总结
8.1 存档修改通用流程
1. 收集多份存档(不同进度)→ 做差分对比
2. 区分预览区 vs 实际数据区
3. 如果数据加密/编码 → 跳到脚本层面
4. 解包游戏脚本 → 搜关键变量 → 改判定逻辑
5. 重打包 → 替换 → 测试
8.2 YU-RIS 引擎特点
- 存档含预览区(明文值)和加密数据区,两者独立
- 游戏状态由 VM 全局变量(GVAR)管理,不直接暴露在内存中
- 脚本是 YU-RIS 字节码,需要专用工具解/打包
- 部分属性值不是静态存储,而是根据底层数据运行时计算
8.3 为什么不建议直改存档
直接改存档字节有三大障碍:
- 加密/编码:数据区不是明文
- 校验:改了之后通不过完整性检查
- 动态计算:某些值不是存储的,是运行时算出来的
→ 改脚本逻辑往往比改存档更可靠。 降低判定门槛(神気≥100 → 神気≥0)比改数值容易得多。
8.4 AI 协作的经验
- ✅ 用 AI 做脚本分析(在 247 个文件中搜索变量引用、追踪调用链)非常高效
- ✅ 用 AI 做工具适配(改编译目标从 .NET 8 到 .NET 10)
- ⚠️ AI 给的“成品工具”需要验证(Gemini 的
.py会乱改所有 30~99 的值,会把游戏搞崩) - ⚠️ 第一次改完没生效不要放弃——排查流程流转(gvar41 分阶段)是关键
9. 文件归档状态
| 文件 | 位置 | 说明 |
|---|---|---|
| 原版脚本备份 | D:\games\戦巫<...>\script.bin.orig |
想恢复时改名覆盖即可 |
| 修改后的脚本 | D:\games\戦巫<...>\script.bin |
当前生效,任何存档都走觉醒→好结局 |
| 用户存档 | Documents\ESCUDE\Sennagi\save\save_020~024 |
未动,完好 |
| 临时文件 | 全删 | EscudeTools 源码、编译产物、分析脚本、解包数据均已清理 |
10. 相关链接
- 随笔/当 Galgame 变成「旮旯给木」:我们还能守住那份感动吗? ← Galgame 文化思考
- Python-Windows下Errno22排查实录 ← 另一篇技术排错复盘
评论区
