news 2026/10/27 1:20:41

LostOdysseyRecomp跳表处理指南:XenonAnalyse、手工配置与自动脚本如何协同处理switch table

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LostOdysseyRecomp跳表处理指南:XenonAnalyse、手工配置与自动脚本如何协同处理switch table

【免费下载链接】LostOdysseyRecomp

Experimental native PC port of Lost Odyssey using static recompilation. No game assets included.

项目地址:https://gitcode.com/gh_mirrors/lo/LostOdysseyRecomp
点击查看免费下载

本文带你完整看懂LostOdysseyRecomp跳表(jump table / switch table)的处理流水线。LostOdysseyRecomp 是一个基于静态重编译的《失落的奥德赛》(Lost Odyssey)实验性 PC 移植项目。由于 Xenon(PowerPC)指令集中存在大量通过bctr完成的计算跳转,重编译器必须清楚"每张跳表跳到哪些地址"——自动发现、手工补全与自动化脚本三者缺一不可。

什么是跳表?为什么它对重编译器至关重要

跳表是编译器为 switch 分派生成的地址数组:CPU 以寄存器值作索引,从表中取出目标地址后再用bctr跳转过去。在这款游戏 14.3 MB 的.text代码里,共有841 张这样的表。

如果重编译器识别不出跳表,会出现两类事故:

  • bctr被误当成尾调用,函数边界被截短;
  • 被截断函数内部的b指令变成"跳出函数"的未解析目标(项目实测首轮就有 91 个 switch 站点、96 个未解析目标)。

换句话说,跳表配置的质量直接决定重编译输出是否完整,也就决定了游戏能不能稳定运行。

自动发现:XenonAnalyse 生成 switch_tables.toml

XenonAnalyse 是项目 XenonRecomp 工具链自带的静态分析器(由 build_tools.bat 一并编译),它借助 XEX 镜像中的.pdata函数表划定函数边界,自动扫描跳表并把结果写入 switch_tables.toml(约 2 万行、841 张绝对地址表)。每张表记录 4 个字段:

字段含义
base读表的跳转指令所在代码地址(索引在此处算出)
r充当表索引的寄存器
default越界时的默认分支
labels表中每一项的目标地址

该文件通过主配置 LostOdysseyRecomp.toml 中的switch_table_file_path = "switch_tables.toml"字段被重编译器引用。

手工补全:switch_tables_manual.toml 里的漏网之鱼

自动检测不可能覆盖所有写法,XenonAnalyse 漏掉的表就手工写进 switch_tables_manual.toml。目前手工补了 2 张表:

① 战斗环 UI 跳表—— 第一张手工表(对应下面这张战斗场景):

[[switch]] base = 0x82B14808 r = 10 default = 0x82B148BC labels = [0x82B14830, 0x82B14878, 0x82B14890, 0x82B148A4]

文件注释写明了后果:不补这张表,bctr会被当成尾调用,从而跳过栈恢复逻辑。

② u16 短偏移跳表—— 基址 0x82FB21AC、16 项的相对偏移表。XenonAnalyse 只识别绝对地址表,这种 16 位偏移写法它发现不了,只能人工列出全部 16 个标签。

⚠️ 一个容易踩的坑:switch_tables.toml每次重跑 XenonAnalyse 都会重新生成,所以重新生成后必须把手工文件的内容再次 append到它末尾——文件头注释里也写明了这条规则。

自动脚本:gen_function_bounds.py 把跳表变成函数边界

gen_function_bounds.py(约 170 行)解决"带跳表的函数被截短"这一连锁问题,核心有 5 条规则:

  1. 累积未解析目标:重编译输出中的// ERROR <addr>目标各自成为一个新函数,跨轮累积在config/branch_targets.txt;
  2. 补全 .pdata 之外的表:每张labels未被所属.pdata函数覆盖的跳表,都从表前最近符号生成一个函数,一直延伸到最远标签之后的下一个符号;
  3. 手工条目:function_bounds_manual.txt 中目前一条(827C39F8 18)——否则这个小桩会流进后面的 pdata 函数,"抢走"它的跳表;
  4. 指针专属函数:只被虚表/函数指针表引用、不在.pdata也不被bl指向的函数,边界分析完全看不见,会被并进前一个函数尾部,运行时虚函数调用直接跳到地址 0 崩溃。脚本扫描整个镜像的 4 字节对齐指针并用基线映射表(baseline_func_mapping.txt)过滤误报,最终命中 43 个;
  5. 代码中物化的地址:lis+addi/ori在 64 字节窗口内拼出的地址被识别为运行时写入对象的函数指针。

生成结果写入主配置的functions = [...]列表(116 项,含 47 个指针专属函数)。

协同流程:三步配合,迭代到收敛

三个环节串成一条"自动 + 手工 + 脚本"的流水线,约 8 轮迭代后收敛到 0 错误:

步骤动作验收标准
1跑 XenonAnalyse,重新生成switch_tables.toml得到 841 张表
2漏检表补进switch_tables_manual.toml,再 append 到自动文件末尾grep -l '// ERROR' ppc/*.cpp返回 0
3gen_function_bounds.py生成functions边界列表未解析目标数逐轮下降
4python -B tools/ppc_codegen.py generate重新生成客制代码0 未解析目标、0 跳表错误

整个过程(含invalid_instructions对异常处理描述符的跳过——不跳过它们,pass2 边界分析会把描述符当代码,在带跳表的函数上死循环)都有完整记录,可查阅专题笔记 recomp.zh-CN.md 与构建说明 BUILDING.md。

相关文件路径速查

  • 自动跳表:switch_tables.toml
  • 手工跳表:switch_tables_manual.toml
  • 主配置(functions、midasm_hook、invalid_instructions):LostOdysseyRecomp.toml
  • 手工函数边界条目:function_bounds_manual.txt
  • 边界生成脚本:gen_function_bounds.py
  • 工具链构建脚本:build_tools.bat
  • 代码生成入口:ppc_codegen.py
  • 专题笔记:recomp.zh-CN.md、BUILDING.md

📌 一句话总结:XenonAnalyse 负责"量大面广"的自动检测,手工 TOML 兜住自动化的盲区,gen_function_bounds.py再把跳表信息转化为精确的函数边界——三者协同,才能让 14 MB 的 Xenon 代码被完整、可运行地重编译到 PC 上。

【免费下载链接】LostOdysseyRecomp

Experimental native PC port of Lost Odyssey using static recompilation. No game assets included.

项目地址:https://gitcode.com/gh_mirrors/lo/LostOdysseyRecomp
点击查看免费下载

相关推荐

上一篇:PasteMD:一键解决Markdown到Office格式转换的终极方案
下一篇:AscendNPU IR 语言生态接入指南:Triton、TileLang、DLCompiler 与 FlagTree 实战详解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 1:17:44

Java代码优化心得

性能优化不是玄学&#xff0c;也不是只有资深架构师才需要关心的事。日常写业务代码时&#xff0c;一个数据结构选错、一次循环里的重复计算、一段用异常控制流程的逻辑&#xff0c;都可能在高并发或大数据量下被放大成明显的性能瓶颈。本文聚焦 Java 性能优化中最容易落地、收…

作者头像 李华
网站建设 2026/10/27 1:16:53

[UVM笔记]UVM 保存mem

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:16:50

课件---程序设计基础-第5章- 选择控制结构--if else

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:15:23

【GATT】规范精讲[6]: 写得进去还要写得靠谱——五种写过程、签名写、可靠写与写描述符

读过程解决的是把值拿回来,方向是服务器流向客户端;这一篇把方向倒过来:客户端把值写进服务器。表面看就是换个 PDU 的事,真写起来会发现写比读麻烦得多。读失败了大不了重试,数据还是对端那份;写一旦在半路上出岔子,配置可能改了一半、固件可能写花、设备可能变砖。所以…

作者头像 李华
网站建设 2026/10/27 1:14:38

Verilog基础:操作数的位选(bit-select)和域选(part select)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 位选 位选(bit-select)用于选择一个向量(vector)的某位&#xff0c;可以是线网大类(net)&#xff0c;也可以是变量大类(variable)中的reg、integer和time&#xf…

作者头像 李华
网站建设 2026/10/27 1:13:48

MySQL Innodb 引擎中预防 Update 操作上升为表锁

一、MySQL 如何预防 Update 上升为表锁 在 MySQL 中&#xff0c;进行任何数据的 修改 操作都会进行一定的锁操作&#xff0c;而锁的不同直接导致性能的差异。例如 MyISAM 引擎&#xff0c;更新时采用表锁&#xff0c;并发性较差。而 Innodb 引擎支持事务&#xff0c;更新时采用…

作者头像 李华