跳转至

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 regmov reg,ecxmov 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 创建 .trnslt section
  • 多版本: 序言优先扫描 + 调试字符串验证实现自动适配
  • 指令安全: trampoline 按指令边界复制,不切断多字节指令
  • 栈兼容: __fastcall + dummy edx 参数模拟 __thiscall,参数布局一致