Show HN: 自动检测并修补Sierra游戏中的死局状态

Hacker News Top 工具

摘要

该工具使用静态分析自动检测并修补经典Sierra冒险游戏中的无法获胜状态,确保游戏保持可玩性,避免死锁。

Hi HN,我年纪大了变懒了,难以重玩我最喜欢的80年代和90年代的Sierra游戏,因为我总是遇到需要3幕前物品的情况,手头没有存档,现在还得去做饭。<p>所以我正在构建Lucasartsifier:一个静态分析工具,它反编译Sierra资源文件,自动找到这些状态,自动生成代码以防止玩家陷入这些状态,然后生成可以放置在原始游戏资源旁边的松散补丁文件。没有涉及游戏特定代码;所有逻辑都是通用的,当然Sierra在每个游戏中引入了新的习语和机制,所以每个新支持的游戏都需要大量的引擎工作。<p>例如在Leisure Suit Larry 2中,补丁游戏会阻止你登上游轮,直到你同时拥有防晒霜和Grotesque Gulp。没有它们,你在3个游戏小时后会在筏子上死亡。<p>到目前为止,这适用于Leisure Suit Larry 2 (SCI0)、King's Quest 4 (SCI0)、King's Quest 6 (SCI1.1)和Laura Bow 2 (SCI1.1)。我目前正在处理King's Quest 5 (SCI1.0)。<p>这是与Claude合作完成的工作——我负责设计和游戏测试,它负责其余部分 :D<p>任何反馈、游戏测试和建议都会很棒!
查看原文
查看缓存全文

缓存时间: 2026/08/19 07:05

katiahayati/lucasartsifier

来源:https://github.com/katiahayati/lucasartsifier

Sierra 软死锁分析器

针对30年前冒险游戏的静态分析工具

本工具可反编译 Sierra SCI 游戏,通过抽象解释将反编译后的脚本转化为包含受保护的房间转换、物品移动和剧情标记写入的图形结构,自动识别软死锁——即游戏仍可接受输入但胜利条件已悄然无法达成的状态,并生成、验证、编译用于防止此类死锁的保护机制,最终完成安装。

该工具无需预先声明任何游戏信息:起始房间、胜利房间、死亡信号及调试标记均从游戏代码本身自动发现。与 LucasArts 游戏不同,Sierra 游戏可能让你陷入困境。例如在《洛杉矶的防晒霜》中,一旦登上邮轮,你可能在几天后因无法返回而死在木筏上。本工具能自动识别此类陷阱,并在玩家仍可操作的最后时刻阻断导致死锁的关键路径。

已分析并测试四款游戏——《休闲衬衫拉里2》(SCI0,1988)、《国王秘史4》(SCI0,1988)、《国王秘史6》(SCI1.1,1992)和《劳拉·鲍2》(SCI1.1,1992)——使用相同引擎,无需针对特定游戏编写分析代码。

演示

《国王秘史4》补丁版——鲸鱼、夜钟及七处物品滞留点均已添加保护机制:
演示视频:国王秘史4 补丁版

三十秒速览

摘自《休闲衬衫拉里2》实际运行过程(python3 -m pipeline ):

[2] 分析 锚点:起始房间 rm11,胜利房间 [86](自动发现)  
死亡信号:global101 == 1001,调试全局变量:[14, 100](推导得出)  
101 个房间,27 个强连通分量,40 个门控寄存器  
软死锁:15 件物品 + 1 个析取组  
- 防晒霜 ...  
[3] 推导  
rm38 -> rm131:(且(玩家持有:11)(玩家持有:12)(玩家持有:14)(玩家持有:15))  
rm57 -> rm58:(且(玩家持有:21)(玩家持有:24)(玩家持有:25)(玩家持有:26))  
rm79 -> rm80:(或(玩家持有:30)(玩家持有:31))  
rm131 -> rm138:(非(玩家持有:13))  
rm63:删除 `(玩家放置:21 -1)`(毛发再生剂)  
验证保护模型...  
修复 15 + 1 组软死锁;新增软死锁:无  
[4] 补丁  
编译 117/118 个脚本  
script.000 主程序 10790 字节  
script.057 rm57 2938 字节  
...  
完成。build/patch 目录下生成 10 个补丁文件  

分析器已识别登船行为为单向通行,推导出必须携带的物品,重新验证保护模型以确保保护机制不会引入新的软死锁,并将修改过的脚本重新编译为 Sierra 自有的松散补丁格式。

注意 rm131 -> rm138:(非(玩家持有:13))——保护机制也支持否定字面量:在 rm138 房间持有菠菜蘸酱会导致死亡,因此解决方案是在玩家仍可丢弃该物品时拒绝通行。本项目认为「要求丢弃已无法丢弃的物品」造成的死墙比游戏漏洞更严重。

若保护机制验证失败或脚本编译出错,整个流程将中止生成任何文件。补丁版游戏可正常运行——该补丁机制正是 Sierra 当年修复漏洞的方式,且原版文件不会被修改(删除补丁文件即可恢复)。

游戏中可设置保护模式:

  • 完全保护:阻止所有危险操作
  • 轻度保护:首次阻止后允许操作(附带警告)
  • 关闭:禁用所有保护

何为软死锁?(或:玩家须知)

部分死亡结局被故意保留——即那些从当前状态仍可避免的死亡。分析通过可达性(而非死亡条件)区分「无法获胜状态」与「可避免死亡」。

在《休闲衬衫拉里2》中,未穿戴完整伪装进入 KGB 海滩会导致死亡。部分伪装部件仅存在于邮轮上,因此分析器禁止在未获取全部伪装时离船。但其余部件可在岛上获取——正位于死亡发生处——故该死亡结局被保留:这是 Sierra 游戏暗示任务目标的方式。正如 Al Lowe 所说:「常存档,早存档!」

当前状态

已完成四款游戏,覆盖引擎两个主要时期(SCI0 1988 → SCI1.1 1992),无需预先声明游戏信息——起始房间、胜利房间、死亡信号及调试标记均从游戏代码推导。

游戏引擎状态备注
休闲衬衫拉里2(1988)SCI0已完成并测试菠菜蘸酱:携带即死,故使用否定字面量保护,在仍可丢弃时触发
国王秘史4(1988)SCI0已完成并测试实时夜钟机制;鲸鱼——通过仅在可存活状态下激活随机事件进行保护
国王秘史6(1992)SCI1.1已完成并测试双结局路径极大增加分析复杂度;对婚礼开始(计时器)设置保护,直到关键物品在手
劳拉·鲍2(1992)SCI1.1已完成并测试幕间结构:剧情时钟为寄存器,幕间过渡为单向通行,利用幕间转换拦截器实现保护
国王秘史5(1990)SCI1-中间版本进行中(kq5 分支)村庄市场:匹配付款与商人以完成全部支付——检测转化为匹配问题

简要工作原理

  1. 反编译:将游戏二进制文件转换为带类型的控制流抽象语法树(JSON IR)
  2. 抽象解释:解析抽象语法树,组合路径条件生成游戏图:受保护的移动边、物品获取/丢失、寄存器写入。同时读取房间图像(PIC/VIEW)与障碍物多边形(部分门控为几何判定,脚本中无对应代码)
  3. 简化图结构:提取强连通分量——玩家可自由移动的区域。仅单向边可能导致滞留,使问题复杂度可控
  4. 识别滞留点:物品在通行前可获取、通行后不可用、后续仍需要
  5. 推导保护机制:从胜利区域反推目标可达条件,在玩家最后可操作点放置保护。物品消耗型死胡同单独处理,通过「只是开玩笑!」提示阻止浪费关键物品,且不扣分
  6. 重编译并输出:补丁版游戏可正常运行(如通过 ScummVM 或 DOSBox)

更详细说明见 docs/HOW-IT-WORKS.md;文件架构见 docs/ARCHITECTURE.md;国王秘史6当前状态见 docs/KQ6-STATUS.md;劳拉·鲍2推导日志见 docs/LB2-ORACLE.md。

工具链

步骤 1 和 6 依赖两个现有优秀项目(以无头模式运行):

  • 反编译:sci-tools(https://github.com/sluicebox/sci-tools)(sluicebox,MIT 协议)。我们维护的分支(https://github.com/katiahayati/sci-tools)在 .sc 源码输出之外增加了第二个输出器:带类型的控制流 JSON 抽象语法树,供分析使用。反编译逻辑本身未修改。
  • 编译:SCICompanion(https://github.com/icefallgames/SCICompanion)的脚本编译器(Philip Fortier,GPL-2.0+),已移植为 Linux 无头构建运行——tools/scicompile/ 包含小型 CLI 及兼容层,替代 MFC/Windows 界面,调用原始解析器、类浏览器、资源映射和代码生成器(GenerateScriptResource)。供应商树在构建时克隆且不作修改;因 MSVC 专有构建需修补少量文件,所有改动记录于 tools/scicompile/BUILD_NOTES.md。

安装

分析部分为纯 Python 3,无任何第三方依赖——src/ 仅导入标准库。需安装的是其驱动的两个外部工具链:反编译器(C#)和 SCI 编译器(C++),均在此从源码构建。

环境要求

sudo apt install python3 git cmake g++ make dotnet-sdk-8.0  # Debian/Ubuntu  
组件用途验证版本
Python 3.12分析与测试(src/)3.12.3
.NET SDK 8构建 sci-tools(反编译游戏)8.0.129
cmake ≥ 3.16、C++14 编译器、make构建 scicompile(重编译补丁脚本)cmake 3.28.3, g++ 13.3
git克隆两个供应商树2.43

已在 ubuntu:24.04 容器中全新验证:上述包、两次构建、完整流程运行及游戏无关测试——详见 docs/HOW-IT-WORKS.md 中的日志记录。

一次性构建

git clone https://github.com/katiahayati/lucasartsifier && cd lucasartsifier  

# 1. 反编译器:克隆我们的 sci-tools 分支至 vendor/,构建并将游戏反编译至 build/ir  
#    同时生成 .sc 源码树和带类型的 JSON 抽象语法树  
tools/sci-tools-fork/build.sh /path/to/game  

# 2. 编译器:SCICompanion 的无头移植版。源码克隆后不作修改;  
#    移植代码位于 tools/scicompile/{compat,patched}  
git clone --depth 1 https://github.com/icefallgames/SCICompanion vendor/SCICompanion  
cmake -S tools/scicompile -B tools/scicompile/build  
cmake --build tools/scicompile/build -j  

仅需步骤 1 即可分析游戏(--report);步骤 2 用于将推导的保护机制转为补丁文件。
vendor/ 目录已加入 gitignore——此处不重新分发第三方源码或游戏数据。

运行工具

需自行准备游戏副本(不包含在本仓库中)。以下命令在 src/ 目录下执行:

cd src  
python3 -m pipeline /path/to/game        # 反编译 -> 分析 -> 推导 -> 生成补丁  
python3 -m pipeline /path/to/game --report  # 仅分析,不生成文件  
python3 -m pipeline /path/to/game --skip-decompile  # 复用 build/ir 下的抽象语法树  

输出文件位于 build/patch/ 目录:

cp build/patch/script.* /copy/of/game/  # 安装补丁  
rm /copy/of/game/script.0*             # 恢复原版  

松散 script.NNN 文件会覆盖映射资源,因此无需修改 RESOURCE.MAP 和游戏卷文件,删除补丁文件即可完全回退。务必使用游戏副本操作,勿直接修改原始文件。

运行测试

python3 tools/run_tests.py  # 完整测试套件(首次全模型冷启动约 21 分钟)  

docs/TESTING.md 包含更多说明:为何部分检查故意显示红色、三组回归测试的不同用途、如何在提交前度量改动影响,以及如何在 ScummVM 中无键盘操作下测试补丁版游戏。

目录结构

src/                     分析代码(Python 3,仅标准库)  
src/testdata/            固化测试数据:两套基准 + 监控对  
tools/run_tests.py       测试运行器(见 docs/TESTING.md)  
tools/drive_scummvm.py   无键盘操作测试补丁版游戏  
tools/kq6_panel_probe.py ... 驱动脚本:冷启动 -> 国王秘史6保护控制  
tools/sci-tools-fork/    构建脚本(用于我们的 JSON-IR sci-tools 分支)[C#]  
tools/scicompile/        SCICompanion 编译器的无头 Linux 移植版 [C++, GPL-2.0+]  
docs/                    工作原理、架构、测试、各游戏状态、许可证  
docs/reviews/            已标记版本的无上下文评审(原文)  
docs/archive/            过时的规划文档(保留测量数据)[见其 README]  
vendor/                  构建时克隆,不纳入版本控制(见安装说明)  

各游戏配置(src/config.py)仅包含文件系统路径和显示名称——不包含游戏本身信息。起始房间、胜利房间、死亡信号及调试标记均有覆盖字段,但所有游戏均留空——整个流程从游戏代码自动推导这四项信息(见 src/anchors.py)。新游戏无需配置条目——config.by_name() 会自动识别所有反编译后置于 build/sweep// 下的游戏。

未来计划

  • 当前未建模必要操作。现有保护机制针对的是「执行某操作所需条件在通行后不再满足且无法获取」的转换。这覆盖了房间边、剧情推进和玩家无法控制的事件(如鲸鱼吞食、夜幕降临、幕间过渡)。但尚未建模「不执行某操作导致后续死亡」的情况,例如《国王秘史5》中需要向猫扔鞋以拯救后续会帮助你的老鼠。
  • 《荣耀传说》系列的状态爆炸问题。QFG 游戏内容过于复杂,分析器难以完成处理。推测可通过抽象化玩家属性、战斗和消耗品(口粮等)解决,但相关工作尚未开展。
  • SCI1.0 / SCI1-中间版本。已建模 SCI0 和 SCI1.1。《国王秘史5》(中间混合版本)在 kq5 分支开发中,已完成大部分。
  • 完整端到端测试。四款游戏均已在补丁后广泛测试,但尚未进行完整端到端游玩,可能发现新漏洞。
  • **更多游戏!**引擎中无游戏特定代码,但 Sierra 在每款游戏中发布了大量游戏特定代码,因此每个新游戏均需扩展分析逻辑。期望最终可收敛至零改动。
  • AGI 游戏。AGI 游戏应纳入支持,但尚未开始相关工作。

许可证

MIT 协议,但 tools/scicompile/ 目录采用 GPL-2.0 或更高版本——因其包含修改过的 SCICompanion 源码并链接其编译器,属于衍生作品。详见 LICENSE、NOTICE 和 docs/LICENSING.md。

基于 sci-tools(https://github.com/sluicebox/sci-tools)(sluicebox,MIT)和 SCICompanion(Philip Fortier,GPL-2.0+)构建。本仓库不包含任何游戏数据。

相似文章

Lean 内核可靠性漏洞 #14576 的事后剖析

Lobsters Hottest

对 Lean 内核中一个可靠性漏洞的事后剖析,该漏洞被利用来产生考拉兹猜想的“反证”,并说明了修复方案以及为什么独立的检查器最初也未能发现它。