26 / 08 / 03

在现代 PC 上玩 KEYBOARDMANIA Yamaha Edition

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

与一般游戏不同,本作采用 MIDI 输入设计,会检查特定 YAMAHA 设备,因此其他 MIDI 键盘无法直接进入游戏,并且依赖的 Windows 98/XP 环境也已经与现代 Windows 环境产生了明显兼容问题。为解除设备限制并修复现代系统兼容性问题,我尝试对游戏进行了逆向和修复,并制作了一个 Launcher 用于启动。

准备内容

要在现代 Windows 环境游玩,需要准备:

  • KMYE repacked (base64) aHR0cHM6Ly9tZXJsaW4tMTI1NjM0ODIxOS5jb3MuYXAtZ3Vhbmd6aG91Lm15cWNsb3VkLmNvbS9LTVlFX3BhdGNoZWQuemlw

  • 一台支持标准 MIDI 输入的电子琴或键盘

  • WinMM MIDI 软件合成器(推荐 VirtualMIDISynth)

  • GM/GS 声库(推荐 GernerlUserGS)

使用方法

下载软件 MIDI 合成器 VirtualMIDISynth 以及声库 GeneralUser GS 。合成器安装完毕后,打开配置,在 Soundfont 标签页导入下载好的 GeneralUser-GS.sf2 即可。

解压 KMYE_patched 至游戏目录,双击 KMYE Launcher.exe 打开 Launcher,按需选择后运行即可。

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

为何在现在 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,导致音频无法正常播放。

另外 KMYE 会发送多通道 MIDI、Program Change、Control Change、SysEx 等状态消息,微软默认的 Microsoft GS Wavetable Synth 对这些状态的处理并不完整,导致伴奏轨道、鼓组、音色和效果缺失。

(三)特定设备

KMYE 原版 EXE 会进行 YAHAMA 设备 ID 以及兼容性验证,如果没有使用特定的 YAMAHA 电子琴,会无法进入游戏。

(四)Traning Mode 崩溃

在进入 Training Mode 时一定概率发生崩溃,原因主要来自一个空指针路径,因为程序在部分训练状态未建立对象时,仍继续读取该对象偏移 +0x0D,最终触发访问冲突。

修复过程记录

(一)DirectDraw

搭配 dxwarpper ,通过 msacm32.dll proxy 尽早加载 dxwrapper。KMYE 在初始化 DirectDraw 之前会加载 ACM,因此这个入口可以在图形设备创建前完成 Hook。

另外 Launcher 基于此提供了显示模式的修改,支持独占全屏、窗口化以及无边框窗口。

(二)解除 YAMAHA 电子琴限制

KMYE 会同时检查系统是否存在 MIDI 输入,以及 MIDI 设备是否为 YAMAHA、是否在兼容的设备列表中,失败后会直接拒绝启动。因此只要 patch 掉 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 音。所以修改的思路为:

  • 在 Windows 中安装 VirtualMIDISynth,并加载 GM/GS 声库,使系统重新获得能够接收 WinMM MIDI 输出的软件合成器;

  • 修改 CMidiOut::Open 获取 device 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 通过 midiOutGetNumDevsmidiOutGetDevCapsW 枚举当前系统中的 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

因此 KMYE_patched 对原版 KMYE 进行了修改,从 0x411156 开始 hook,跳转至 0x5105FE 执行 null guard,解决了崩溃问题。

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