26 / 08 / 03

在现代 PC 上玩 KEYBOARDMANIA Yamaha Edition

KEYBOARDMANIA Yamaha Edition 是 KONAMI 于 2003 年在 Windows 平台推出的音乐游戏,也是该系列面向 YAMAHA 电子琴用户的家用版本,仅在北美地区发售。

本作采用 MIDI 输入,会检查连接的 YAMAHA 设备。因此,其他 MIDI 键盘无法直接用于游戏。与此同时,游戏所依赖的 Windows 98/XP 运行环境也与现代 Windows 系统存在明显的兼容性问题。

为解除设备限制并解决这些兼容性问题,我对游戏进行了逆向分析和修复,同时制作了一个 Launcher,用来配置和启动游戏。

准备内容

在现代 Windows 系统中运行游戏,需要准备 KMYE 本体、一台支持标准 MIDI 输入的电子琴或键盘、WinMM MIDI 软件合成器(推荐使用 VirtualMIDISynth)和 GM/GS 声库(推荐使用 GeneralUser GS)。

使用方法

下载并安装 VirtualMIDISynth ,然后下载 GeneralUser GS 声库。安装完成后,打开 VirtualMIDISynth 配置程序,在 Soundfont 标签页中导入 GeneralUser-GS.sf2。

KMYE_patched 解压到游戏目录。目录中应包含以下文件:

KMYE Launcher.exe KMYE_patched.exe msacm32.dll dxwrapper.dll dxwrapper.ini

双击 KMYE Launcher.exe,根据需要选择显示和 MIDI 选项,然后启动游戏。

为什么游戏无法在现代 Windows 系统中正常运行

KMYE 是一个 32 位 x86 程序,主要针对 Windows 98/XP 时代的多媒体环境开发,使用了 DirectDraw、DirectSound、Video for Windows、ACM 和 WinMM MIDI 等旧式接口。

DirectDraw 1.0

KMYE 请求使用 640×480 分辨率、16-bit 色深和 DirectDraw 1.0 独占显示模式。

Windows 10/11 的桌面由 DWM 和现代 WDDM 显卡驱动管理,通常使用 32-bit 色深。DirectDraw 1.0 Surface、16-bit 独占模式、GDI 绘制和 Video Surface Lock 等旧特性无法在现代驱动中正常工作,因此游戏启动后可能只有声音,没有画面。

音频缺失

KMYE 的音频输出分为两条链路。KEY 音、语音和部分音效通过 DirectSound 输出到声卡。BGM、音色和部分效果则以 MIDI 事件的形式,通过 WinMM MIDI Output 发送到 MIDI 合成器。

在 Windows 98/XP 中,用户可以从控制面板选择默认的 MIDI Synthesizer。这个选项从 Windows Vista 开始被移除,但游戏仍然会调用旧式的 midiOutOpen,导致 MIDI 音频无法正常播放。

此外,KMYE 会发送多通道 MIDI、Program Change、Control Change、SysEx 等状态消息。Microsoft GS Wavetable Synth 无法完整处理这些状态,可能导致伴奏轨道、鼓组、音色和效果缺失。

特定设备

KMYE 原版 EXE 会检查 YAMAHA 设备 ID,并进行兼容性验证。如果没有连接指定的 YAMAHA 电子琴,游戏将拒绝启动。

Traning Mode 崩溃

进入 Training Mode 时,游戏有一定概率崩溃,主要原因是一条空指针路径,部分训练状态下对象尚未建立,程序却继续读取该对象偏移 +0x0D 处的数据,最终触发访问冲突。

修复过程

DirectDraw

搭配 dxwarpper 运行,并通过 msacm32.dll 代理提前加载 dxwrapper。KMYE 会在初始化 DirectDraw 之前加载 ACM,因此可以利用这一入口,在图形设备创建前完成 Hook。

Launcher 还提供了显示模式设置,支持独占全屏、窗口化以及无边框窗口。

解除 YAMAHA 电子琴限制

KMYE 会同时检查系统中是否存在 MIDI 输入设备、设备是否为 YAMAHA,以及设备是否位于兼容列表中。任一检查失败,游戏都会拒绝启动。修复时去除了与 YAMAHA 专用错误参数入栈相关的指令:

0x1728A `68 E0 19 53 00` - `90 90 90 90 90` 0x17FCB `68 E0 19 53 00` - `90 90 90 90 90` 0x18045 `E8 96 B8 00 00` - `90 90 90 90 90` 0x1DAA8 `68 E0 19 53 00` - `90 90 90 90 90` 0x22BC9 `68 E0 19 53 00` - `90 90 90 90 90` 0x23A36 `68 E0 19 53 00` - `90 90 90 90 90`

音频问题

KEY 音和 BGM 使用完全不同的输出链路。

KEY 音来自 Sound.pac 中的 PCM/WAV 文件,通过 DirectSound 播放。BGM 则由游戏生成 MIDI 事件,再通过 WinMM MIDI 发送给外部 MIDI 合成器。直接启动游戏时,通常只能听到由 DirectSound 播放的 KEY 音。

修复分为两部分:

  1. 在 Windows 中安装 VirtualMIDISynth 并加载 GM/GS 声库,让系统重新获得可以接收 WinMM MIDI 输出的软件合成器。

  2. 修改 CMidiOut::Open 获取设备 ID 的位置,使 Launcher 可以直接指定 MIDI 输出设备,不再依赖 Windows 的默认 MIDI Mapper。

原始指令为:

8B 5C 24 08 mov ebx,[esp+08]

Launcher 在每次启动前会将其改为:

6A xx push <所指定的设备 ID> 5B pop ebx 90 nop

Launcher 使用 midiOutGetNumDevs 和 midiOutGetDevCapsW 枚举当前系统中的 WinMM MIDI 输出设备。启动游戏前,它会写入用户选择的设备 ID。游戏随后会把 Program Change、Control Change、Note On/Off 和 SysEx 等 MIDI 数据发送到指定的合成器。

Training Mode 崩溃

KMYE 中存在一条空指针路径。快速退出并重新进入 Training Mode 时,对象的初始化和销毁可能发生时序重叠,从而导致游戏崩溃。

Exception: 0xC0000005; RVA: KMYE_patched.exe+0x1115D; VA: 0x41115D; Address: 0x0000000D

修复从 0x411156 开始设置 Hook,并跳转到 0x5105FE 执行空指针检查:

pointer != NULL → 返回 0x411161,执行原来的 Training Mode 逻辑 pointer == NULL → 跳到 0x4111B6,绕过非法的 +0x0D 访问

这样可以避免对象尚未建立时发生访问冲突。

AdditionalBuffer

Launcher 提供了直接修改 VirtualMIDISynth AdditionalBuffer 的入口。将其设为 0ms 可以降低延迟,但在性能较差的设备上可能出现声音卡顿。

其他

Launcher 会在游戏目录中生成配置文件 KMYE_Launcher.ini。删除该文件即可恢复默认设置。

[Launcher] Width=960 Height=720 AudioBufferMs=0 ScalingMode=Aspect DisplayMode=Window MidiName=VirtualMIDISynth #1