Files
win-troubleshooting-notes-p…/ori-wotw-troubleshooting/文章/博客模板-奥日2加载闪退修复.md
T

20 KiB
Raw Blame History

title, date, slug, description, tags, draft
title date slug description tags draft
奥日 2 加载闪退、偶尔进入却无声:一次 Wwise 线程故障排查与 8 字节兼容补丁 2026-09-16 ori-wotw-wwise-thread-affinity-fix 从崩溃转储和 Windows API 返回值定位 Wwise 音频初始化失败,并通过取消音频线程显式核心绑定完成本机验证。附带哈希校验与还原功能的补丁源码。
游戏排障
Windows
Unity
Wwise
逆向分析
true

我遇到的《奥日 2》问题很折磨人:小精灵还在加载界面跑步,游戏就闪退;偶尔成功进去,却完全没有声音。换了两个版本、限制 CPU 核心、禁用部分设备,都没有解决。同系统版本的 VMware 虚拟机反而可以运行。我折腾了数小时,最后甚至退了款,游戏文件倒还留在硬盘上。

这次借助 AI 编程助手做了进一步排查,最终找到了一个可以实际验证的触发链:音频线程绑定 CPU 核心失败,Wwise 的初始化失败路径又留下了仍在运行的线程。

最终补丁改动 8 个字节,让相关音频线程使用 Windows 默认调度。我的机器已经通过普通启动和 Windows 音频输出验证。下面记录证据、原理,以及补丁适用的严格范围。

这是一个具体 DLL 构建、具体故障路径的非官方兼容补丁,不是所有奥日 2 闪退的通用解法。请先看文件哈希和适用条件。

想先解决问题:下载、检查、安装、还原

补丁包:Ori Wwise Affinity Fix v1.0.0
完整研究报告:详细技术记录
源码仓库或镜像:{{SOURCE_URL_OR_REMOVE_THIS_LINE}}

分享包只包含脚本、说明与脱敏后的证据摘要。它在本地修改你自己的游戏文件,不附带完整游戏 DLL,也不修改游戏平台授权逻辑。

适用条件

  • 游戏常在加载阶段退出,偶尔进入但无声。
  • 日志中出现 Cannot create streaming I/O device、Failed to initialize the sound engine,崩溃栈指向 AkSoundEngine.dll 工作线程。
  • 原始 AkSoundEngine.dll 的 SHA-256 必须完全等于:
8F90A4A3856540CB97D03CA07B4BB10B7A08F7B4AB3C524BE2BEC1AFDD29FECA

DLL 位于游戏目录的 oriwotw_Data\Plugins 下。相同游戏名称、相同 Wwise 版本字符串,甚至同样的闪退画面,都不能替代哈希匹配。

使用方式

需要 Windows x64 和 PowerShell 7。先退出游戏,解压补丁包,再双击 Install.cmd,按提示粘贴游戏根目录,也就是包含 oriwotw.exe 的目录。

喜欢命令行的读者,可以在解压后的补丁目录打开 PowerShell 7。下面路径只是示例,需要改成你自己的:

# 只检查,不修改
& '.\Ori-AudioFix.ps1' -Action Check -GameDirectory 'D:\Games\Ori and the Will of the Wisps'

# 安装补丁并保留经过校验的原文件备份
& '.\Ori-AudioFix.ps1' -Action Install -GameDirectory 'D:\Games\Ori and the Will of the Wisps'

# 还原;请先退出游戏
& '.\Ori-AudioFix.ps1' -Action Restore -GameDirectory 'D:\Games\Ori and the Will of the Wisps'

也可以双击 Restore.cmd 还原。备份文件名为:

AkSoundEngine.dll.before-ori-affinity-fix

补丁后 DLL 的哈希应为:

62B2079A988D23EB251D4E83366AC04068A6F47E0D38EFE92A76DF022826F9F0

版本不匹配时,脚本会停止。请不要修改脚本中的哈希限制强行安装。游戏更新或平台文件校验可能覆盖补丁,更新后的 DLL 需要重新确认适用性。

我的测试环境

本机有一些值得如实记录的特征,尤其是固件和 Windows 对处理器的命名并不相同。它们是复现背景,不是已经证明的故障原因。

项目 环境
系统 Windows 10 专业工作站版,22H2,x64,构建 19045.6456
CPU:Windows 报告 Genuine Intel(R) 0000
CPU:固件产品名 i9-13900HK(ES)
CPU 标识 Family 6 / Model 186 / Stepping 2
可用逻辑处理器 20;测试进程亲和性 0xFFFFF
内存 约 32 GB;崩溃报告为 32600 MiB
主板固件字段 Intel / Z790M GAMING WIFI D5
BIOS AMI,Leo-Patch 1.5,日期 2024-07-26
本次实际渲染设备 AMD Radeon RX 9070 XT,驱动 32.0.31041.1004
图形接口 Direct3D 11,功能级别 11.1
主测版本 A Unity 2018.4.11f1,Build Id 5257
另一版本 B Unity 2018.4.24f1,Build Id 8978
两份游戏共用的音频组件版本 Wwise 2019.1.7 Build 7135;原始 DLL 哈希相同

注册表还存在 NVIDIA Tesla P4、Zako Display Adapter、Microsoft Remote Display Adapter 等显示组件。音频组件包括 Realtek High Definition Audio(6.0.9239.1)、AMD High Definition Audio(10.0.1.41)、Steam Streaming Speakers(17.56.13.764)、VB-Audio Virtual Cable(3.3.1.7)和 AMD Streaming Audio Device(1.0.0.14)。注册记录不等于它们当时都处于活动状态。

VMware 可运行的情况是我此前的观察,本次没有同时采集虚拟机的 vCPU、驱动和文件哈希,所以这里不把它包装成严格的对照实验。

第一步:把大量日志噪声与重复故障分开

Unity 日志里有不少“脚本引用缺失”等警告。真正有价值的线索是,这批历史记录中的 29 次崩溃,有 28 次走到了同一类 Wwise 工作线程路径。

对应的关键报错是:

Wwise: Cannot create streaming I/O device.
WwiseUnity: Failed to initialize the sound engine. Abort.

这里的 streaming I/O device 指音频数据读取机制,不是设备管理器中的某块物理声卡。这个区别很关键:如果直接把它理解成声卡坏了,很容易继续围绕音频驱动反复尝试。

还要注意,28/29 只是留存崩溃样本的分类,不能当成启动失败率。成功启动不会产生同样的崩溃报告,而且这些记录跨越了此前的不同排障尝试。

第二步:别完全相信文本栈给出的函数名

原始堆栈把出错帧显示成 AK::StreamMgr::SetFileLocationResolver。但没有完整调试符号时,工具可能只找到了附近的导出名称。

计算相对模块地址并反汇编后,真正值得注意的位置是:

AkSoundEngine.dll + 0x5B0C8
AkSoundEngine.dll + 0x5B16E

这两个位置分别紧跟在虚函数间接调用之后。崩溃线程试图跳转的目标又不在正常的模块代码区。

更细的一点:Unity 文本报告写的是“Write to location”,但对应转储中的 ExceptionInformation[0] 是 8。按微软文档,这意味着用户态数据执行保护(DEP)异常,即尝试执行了不允许执行的内存。

所以我没有把“文本日志说写入冲突”当作最终答案,而是继续检查对象和线程的生命周期。

第三步:记录真正失败的 Windows 调用

诊断脚本在自己的游戏子进程内,对音频线程初始化附近设置临时断点,记录参数、返回值和目标线程错误码。原始运行捕获到:

线程优先级:0
线程核心掩码:0xFFFFFFFC
线程栈大小:128 KiB

SetThreadPriority       返回 1:成功
SetThreadAffinityMask   返回 0:失败
Windows 错误码          87:ERROR_INVALID_PARAMETER
随后发生                0xC0000005 访问冲突

SetThreadAffinityMask 用来指定线程可以在哪些逻辑 CPU 上运行。掩码里的每一位代表一个逻辑处理器,例如 0xF 对应低四位。

起初很容易想到:游戏请求 0xFFFFFFFC,本机进程报告 0xFFFFF,是不是请求了不允许的高位?这个解释必须经过实验。

结果是:把掩码缩小到 0xFFFFC,游戏线程仍然失败;独立的小程序测试自己的线程时,相同掩码却能成功。因此,“只要截断多余的位就能修好”没有得到本机实验支持。

这里保留一个尚未解决的问题:宿主机为什么拒绝这个游戏线程的绑定?现有证据不足以把它归因于 ES CPU、混合架构、某个声卡或某个显示驱动。

网上说的“限制 CPU 核心”,和这次修复有什么区别?

我之前确实试过限制游戏可用核心,没有解决;旧启动文件里还留着 /affinity F。这里的 F 对应低 4 个逻辑处理器,不一定是 4 个物理核心。

需要区分三个操作:

  1. **限制整个游戏进程的核心范围:**改变进程能使用哪些处理器,但游戏内部仍可能对音频线程重新请求绑定。
  2. **缩小音频线程请求的掩码:**本轮实测过,改为 0xFFFFC 后仍返回错误 87。
  3. **取消音频线程的显式绑定:**本补丁把配置值设为 0,让 DLL 跳过绑定调用;这才是在本机通过验证的办法。

因此,准确说法是“限制核心没有解决我的这个问题”,而不是“限制核心对所有人都没用”。它可能帮助其他并发、时序或处理器拓扑问题;我们也没有穷举所有核心组合。关键仍然是检查相同的错误调用是否消失,而不是只看修改后的某一次是否碰巧启动。

第四步:找到导致随机崩溃的错误清理关系

更严重的问题出现在设置失败以后。

反汇编显示,Wwise 先创建一个可以立即运行的线程,再设置优先级和核心绑定。如果设置失败,它会关闭线程句柄并把字段清空。而后续清理逻辑只有在线程句柄仍然存在时才等待线程。

**关闭线程句柄,不等于结束线程。**微软的 CloseHandle 文档明确写了这一点。

下面是控制关系的简化表达,不是厂商源代码:

thread = CreateThread(...);  // 线程已经可以运行

if (!ConfigureThread(thread)) {
    CloseHandle(thread);
    thread = nullptr;
    return Failure;
}

// 后续清理
if (thread != nullptr) {
    WaitForSingleObject(thread, INFINITE);
}
// 此后才应销毁线程仍可能使用的资源。

一旦句柄清空但线程还活着,后续清理就可能跳过必要的等待。结合错误的间接调用目标,这强烈支持“仍在运行的线程使用了失效对象”的解释。

flowchart TD
    A[音频读取线程开始运行] --> B[设置 CPU 亲和性失败:错误 87]
    B --> C[关闭并清空线程句柄]
    C --> D[初始化失败,进入资源清理]
    D --> E[后续等待可能被跳过]
    E --> F[仍存活线程使用失效状态]
    F --> G[错误间接跳转,访问冲突]

本轮直接确认了调用失败、句柄清理分支以及后续异常;没有完整跟踪所有分配和释放事件,所以我把“释放后使用”写成有强证据支持的解释,而不是宣称已经掌握厂商源码层面的全部细节。

这个时序也能解释为什么现象不完全固定:音频初始化失败会造成无声,而线程什么时候撞上失效状态,还受调度和内存复用时机影响。

8 字节补丁:让音频线程交给 Windows 调度

这次选用的兼容方案,是利用 DLL 已有的“掩码为 0 时跳过显式绑定”分支。

被修改的导出函数是:

CSharp_AkThreadProperties_dwAffinityMask_set

原来相当于:

if (properties != nullptr)
    properties->dwAffinityMask = suppliedMask;

补丁后相当于:

if (properties != nullptr)
    properties->dwAffinityMask = 0;

注意这里的 0 不是把线程绑定到“零个核心”。该音频 DLL 在调用 Windows API 之前就会检查它;值为 0 时直接不调用绑定函数,让线程继承正常的系统调度。

对应构建的精确信息:

函数 RVA:  0x121F0
文件偏移:  0x115F0

原始:48 85 C9 74 03 89 51 04 C3 CC CC CC CC CC CC CC
补丁:48 85 C9 74 07 C7 41 04 00 00 00 00 C3 CC CC CC

替换区域为 16 字节,真正变化了其中 8 个字节。空指针检查保留,跳转距离相应调整,使用原函数尾部的填充空间。候选文件经过再次反汇编,确认指令和边界符合预期。

这是一种绕过触发条件的兼容修复。若能修改引擎源码,更完整的修复还应确保初始化失败时正确停止并等待线程,之后再释放其依赖资源。

验证结果

测试 结果
原始 DLL 动态跟踪 绑定失败,错误 87,随后崩溃
仅缩小核心掩码 仍然失败
进程内取消显式绑定 通过音频初始化和后续加载
安装补丁后普通启动 不依赖调试器,也通过加载
Windows 音频会话 活动、未静音、音量 100%,检测到非零音频信号
最后一次退出 我主动关闭了游戏
安装及还原 副本往返测试通过,还原后哈希与原件相同

音频探针在 3 秒采样中的最大电平约为 0.01062。它只读取游戏的音频会话状态,没有录音,也没有访问麦克风。这个结果证明游戏确实向 Windows 音频管线输出了声音信号。

本轮主要验证版本 A。版本 B 的音频 DLL 与 A 完全相同,已经安装并校验,但还没有单独进行本轮运行测试。完整长时间游玩、所有关卡、性能和音频延迟测试也还没有完成。

分享这个补丁时,我希望保留的限制

补丁安装器检查完整 SHA-256、原始指令和备份,已经修补的文件不会重复修改,未知版本会拒绝处理。补丁文件不携带游戏 DLL,读者使用自己的文件生成结果。

它影响通过这个 setter 配置的音频线程,不改变系统全局调度,不保证修复所有 0xC0000005。取消显式绑定也可能改变性能特征,需要更多环境验证。

如果想反馈适用情况,建议附上:Windows 构建号、CPU 标识及逻辑处理器数、Unity/Wwise 版本、DLL 原始哈希、关键报错和测试时长。请先去掉日志中的个人路径;完整崩溃转储不适合直接放在公开评论区。

这次研究最有价值的收获,是把模糊的“加载闪退”逐步缩小成可以观测和对照的行为:哪个线程、哪个调用、什么参数、什么返回值,以及修改之后是否在普通启动下仍然成立。

这算逆向工程吗?

算,而且是用于故障诊断和兼容性修复的逆向工程。

反汇编把已编译的机器码翻译成汇编,让我们能看到“创建线程后做了什么、失败分支跳到哪里”。动态调试则记录这些指令在本机真正收到的参数和返回值。把两者与崩溃记录对上,才有依据设计补丁。

这里没有恢复完整的游戏源码,文中的 C++ 风格代码也是解释逻辑的伪代码。但关键地址、调用参数、错误码和修改字节都保存下来了,其他人可以复核这条推理链。

网上那些“奥日 2 闪退解决办法”,到底有没有用?

我又核对了官方公告和玩家讨论。结论是:**不少办法有适用场景,但“加载闪退”不是一个足以确定根因的诊断。**下面是截至 2026-09-16 的资料核查,不代表我在本机逐项执行过。

办法 我的判断与依据
限制 CPU 核心 **本例此前没解决,不能推成人人无效。**查到的性能教程针对 CPU 近满载时的卡顿,使用动态限制规则;没有验证本例 Wwise 错误 87。它也不一定就是我最早看到的那篇教程
换声音输出/API、关闭空间音频 有官方用途依据。Patch 2 公告说明这些选项用于硬件兼容和无声排障;不能据此保证能修复我的线程绑定失败
改成 48 kHz 可能处理格式兼容,具体证据弱。相关帖子只有简短建议,没有硬件、日志与回复验证;48 kHz 不是已经证明的万能值
关闭动态模糊 有特定场景的成功反馈。Iris Xe 讨论中,两位玩家报告不再冻结。但他们是游玩一段时间后冻结,我是加载时音频初始化失败
窗口化、调整 HDR;用 DXVK 有图形路径的相关反馈。RTX 5070 帖同时改了多项显示设置,无法分清单项贡献;RTX 50 帖作者称 DXVK 修好了花屏。本机窗口化仍复现原故障,历史 DXVK 尝试还出现过另一类 DXGI 崩溃
回退游戏版本或检查 Windows 预览版 有特定历史问题的官方依据。Patch 3.1 公告提供 Patch 2 回退;开发者白屏帖初步关联当时 Insider Dev 预览版。我的正式版 Win10 不能直接照搬
校验文件、修运行库、换驱动 **有对应错误时有用。**分别处理损坏文件、缺失依赖和驱动兼容;恢复原版文件无法修正它自身的线程清理逻辑
安装 Unity 引擎、套用“一键优化” 作为万能办法缺乏依据。一篇启动指南确实建议安装 Unity,但游戏自带运行用的 Unity Player,不能由此推出玩家必须安装编辑器,也没有对应对照证据

我所说的“玄学”,是把缺少证据的相关性讲成必然有效的因果。像改采样率、关闭叠加层、调整核心限制,本身不神秘;问题是有没有对应的故障条件,有没有记录到底是哪一步起作用。

还有一个容易误读的例子:无声讨论帖收到许多建议,但楼主明确回复没用。几个月后游戏恢复声音,楼主也不知道原因。不能把这段故事压缩成“改声音设置/更新系统已验证有效”。

对我这次排障,最有分量的区别仍是:缩小音频线程请求掩码后,同一个绑定调用继续失败;让它不再发起显式绑定后,音频初始化及普通启动通过。它比仅凭一次“能进游戏了”更能支持这个补丁的作用机制,但仍不代替长时间游玩验证。

分享包附有 COMMUNITY-REVIEW.zh-CN.md,保留更详细的适用条件、来源和证据限制。

参考资料

官方资料核对日期:2026-09-16。本文的实验结论来自本机记录,不把论坛上的类似症状或未经验证的猜测当作根因。