chusan_utf8.dll — 大四节奏系列中文化 Hook
1. 原理
游戏文本渲染链路
硬编码 SJIS 字符串 ("ロード中"等)
│ 游戏内部 SJIS→UTF-8 转换
▼
teaFontRenderer::setTextByString(this, "ロード中"的UTF-8, arg)
│ 唯一入口, 所有窄字符串渲染必经
▼
UTF-8 解码器 → Unicode codepoint → 字体 glyph → GPU 渲染
Hook 策略
在 setTextByString 入口处拦截,拿到原文 UTF-8 字节流,查翻译表替换为中文 UTF-8,下游 UTF-8 解码器直接渲染中文。
setTextByString(this, "E3 83 AD E3 83 BC E3 83 89 E4 B8 AD", arg)
│ hook 拦截
▼
lookup("ロード中") → "加载中"
│
▼
setTextByString(this, "E5 8A A0 E8 BD BD E4 B8 AD", arg)
│
▼
渲染 "加载中"
不需要修改 EXE,不需要 EXEPatch.py,不需要 .trnslt section。原始 EXE 直接配合 DLL 使用。
2. 多版本自动适配
问题
setTextByString 是未导出的内部函数,其地址在不同版本间变化:
| 版本 | ImageBase | setTextByString RVA |
|---|---|---|
| XV (旧 vtable) | 0x400000 | 0x3C5E70 |
| XV (新 vtable) | 0x400000 | 0xB077E0 |
| Air 1.10 | 0xFC0000 | 0x2353C0 |
方案:序言优先 + 调试字符串验证
不硬编码地址,通过 3 步自动定位:
Step 1 — 扫描 .text 段找序言模式
Pattern A: 55 56 8B F1 57 (chusanApp 旧)
Pattern B: 55 8B EC 56 57 (chusanApp 新)
Pattern C: 55 56 57 (chuniApp Air)
→ 候选函数列表
Step 2 — 验证函数体
扫描函数体内是否有 push imm32
→ 检查 push 的目标地址是否包含调试字符串:
"[teaFontRenderer] setTextByString() : string 's' is too long."
→ 匹配则确认身份
Step 3 — 验证调用约定
检查函数是否有 retn 8 (thiscall, 2参数)
→ 确认函数签名正确
不依赖任何硬编码地址,跨版本自动工作。
指令边界感知 Hook
不同版本的序言指令长度不同,Hook 自动计算需要复制到 trampoline 的字节数:
XVERSE: 55 56 8B F1 57 → 5字节 (全是单字节指令) → trampoline 5字节
chusan新: 55 8B EC 56 57 → 5字节 → trampoline 5字节
Air: 55 56 57 8B 7C 24 0C → push是单字节, mov edi是4字节 → 需要7字节
↑ 如果只复制5字节, 会切断mov edi指令, trampoline执行垃圾指令崩溃
hook_copy_len() 函数自动识别 push reg、mov reg,ecx、mov reg,[esp+disp8]、mov ebp,esp 等序言常见指令,计算完整指令边界进行复制。
3. 翻译表格式 (chusan_trans.txt)
UTF-8 编码,每行 日文原文|中文译文:
閉じる|关闭
ロード中|加载中
精确匹配
不含 % 的条目,完整字符串匹配:
はい|是
配信サーバーの重複をチェックしています|检查重复的分发服务器
模式匹配 (v5)
含 %u(数字) 或 %s(字符串) 的条目,一条规则覆盖所有运行时拼接的组合:
%u時|%u点 # 19時→19点, 30時→30点
%sグループ基準機の重複をチェックしています|%s群组正在检查基准机重复 # A~D四组
CREDIT(S) : %u|可用点数:%u # 0~N 全部
匹配逻辑:先精确匹配,失败后按翻译表顺序逐条尝试模式匹配,首个命中的返回。
查找流程
"19時" 到达 hook
→ step 1: 精确匹配 "19時" → 未命中
→ step 2: 模式匹配 "%u時" → 捕获19 → 输出"19点" ✓
4. 编译和使用
编译
buildv3.bat
需要 Visual Studio 2022 + x86 工具链。生成 chusan_utf8.dll。
部署
bin/
chusanXXX.exe (原始, 未打补丁)
chusanhook.dll
chusan_utf8.dll ← 放这里
chusan_trans.txt ← 放这里
启动
inject_x86.exe -d -k chusanhook.dll -k chusan_utf8.dll chusanApp.exe
控制台输出示例:
chusan_utf8: loaded
chusan_utf8: OK - setTextByString at base+0x3C5E70, 49 translations
失败时会显示:
chusan_utf8: ERROR - cannot find setTextByString
禁用
删除 chusan_utf8.dll 或去掉 -k chusan_utf8.dll 参数。
5. 已验证版本
| 游戏 | 版本 | 状态 |
|---|---|---|
| XVX | 2.45 | ✅ 两个 vtable 版本均通过 |
| XV | 2.40 | ✅ 可正常使用 |
| VERSE | 2.30 | ✅ 可正常使用 |
| Air | 1.10 | ✅ 不同 ImageBase 和序言模式, 已适配 |
6. 技术要点
- Hook 目标:
teaFontRenderer::setTextByString— 所有窄字符串渲染的唯一入口 - 编码: 游戏在上游已将 SJIS 转 UTF-8,到达 hook 时已是 UTF-8,中文 UTF-8 可直接透传(其实utf-16le的也可以正常hook)
- 不修改 EXE: 不需要 EXEPatch.py 创建
.trnsltsection - 多版本: 序言优先扫描 + 调试字符串验证实现自动适配
- 指令安全: trampoline 按指令边界复制,不切断多字节指令
- 栈兼容:
__fastcall+ dummyedx参数模拟__thiscall,参数布局一致