メガドライブミニが発売されて2週間。
いろいろなハックをしてきましたが、やっておきたかったこの改造が成功したので記録しておきます。
(※この記事の内容は技術的検証であり、完全に再現可能な手順を記したものではありません。この手順で行ったことをもとに、平均的なユーザが利用可能なツールがリリースされる可能性はゼロではありません。平均的なユーザが、現在できることではそのリリースを待つことです。)
※検索でたどり着いた方へ※
このページには技術的情報がまとめてあります。
市販のゲームコントローラをメガドライブミニで使用するには
こちらのコンバータを使用するのが最もかんたんで確実です。
過去の記事にも書きましたが、メガドライブミニではいわゆるファームウェア(Linux)の設定で、特定の公式サポートゲームパッドしか接続・認識できないことがわかっています。
また、偶然USBのVIDやPIDが一致したパッドでも、上下左右のキーが正しく入力できなかったり、ボタンのマップがめちゃくちゃで、ろくにゲームができない、という状態のようです。
そんな中、
このような製品もサードパーティから発売されるようで、一般的なユーザはこの変換アダプタを購入して使用するのがいいのではないか、と思われます。
しかし、これ一本 5,480 円するようで…
処理内容を考えると、全く妥当ではあるんです。しかし、じゃぁこれを使ってアケコンで対戦する、となったらx2本で一万円かかってしまうわけで。
もちろん、それも一つのやり方ですが、ここはメガドラミニのほうを書き換えて
一般的なアケコンを使えるようにしてしまえ、というハックが成功しました。
使用したアーケードスティック
ボタントランスレートの目処が立ったので買ったんだけど...なんでこれボタン青いんだ
— ひろみつ(honeylab) 技術書典7け36D (@bakueikozo) October 9, 2019
軽量ボタンに変えてるとかなのかな
だからジャンクコーナーにあったのか..
ボタンは全部反応した pic.twitter.com/qZnVDZokq3
今回使用したアーケードスティックは、iBUFFALOの「ARCADE STICK 13Ⅱ」
(BSGPAC02BKBC)というものです。
近所のハードオフのジャンクコーナーにありました。
ジャンクコーナーにあったので、動作が不安だったのですが、どうやらボタンやスティックが前オーナーによってマルっと交換されているだけで、動作には問題はありませんでした。
ボタンの交換がジャンクコーナー行きの理由だったようです。
どうやってつくったか
メガドラミニの内蔵謹製エミュレータがどのようにしてコントローラ入力を読み取っているのかをまず観測してみます。
Linuxのシステムコールの呼び出しを観測できる「strace」ツールをメガドライブミニに入れて動かしてみます。
# /rootfs_data/strace -e trace=open -ff /usr/game/m2engage
すると、
[pid 29051] open("/dev/input/event4", O_RDONLY|O_LARGEFILE) = 8
という部分が見つかりました。
どうやら、/dev/input/event4を読んでいるようです。
今のカーネルだと、刺さってるHUBのポートみて本体1,2はevent4,5、本体1に刺さったHUBのポート順に6,7,8,9,本体2のHUBでは10,11,12,13に割り当てられる
— ひろみつ(honeylab) 技術書典7け36D (@bakueikozo) October 8, 2019
話を簡単にするために、一旦裏のOTG経由でつないだやつをフィルタ出来るようにしてみるかな pic.twitter.com/RhQx1Nd5PY
このデバイスは、Linuxのソースコード evdev.c をHUBのポート番号によって番号が固定されるようにオリジナルのソースから改変することで実現されていました。
純正のメガドライブパッドを接続したとき、event4がどのようなイベントを受け取っているのか、「evtest」ツールで確認します。
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0xca3 product 0x24 version 0x111
Input device name: "6B controller"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 288 (BTN_TRIGGER)
Event code 289 (BTN_THUMB)
Event code 290 (BTN_THUMB2)
Event code 291 (BTN_TOP)
Event code 292 (BTN_TOP2)
Event code 293 (BTN_PINKIE)
Event code 294 (BTN_BASE)
Event code 295 (BTN_BASE2)
Event code 296 (BTN_BASE3)
Event code 297 (BTN_BASE4)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 127
Min 0
Max 255
Flat 15
Event code 1 (ABS_Y)
Value 127
Min 0
Max 255
Flat 15
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
出力は上のようになりました。
ボタンは、288-297がレポートでき、方向キーは、X軸、Y軸それぞれ0-127-255の絶対座標で受け取れるようです。
では、この買ってきたアーケードスティックを接続した場合はどうなるでしょうか。
…… なんと、当然認識しません。カーネルにIDが記録されていないからです。
この辺の制限を外したカーネルを使って試す必要があります。
制限を外して再コンパイルしたカーネルを使うと以下のように認識させることができます。
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x1dd8 product 0xe version 0x110
Input device name: "BSGPAC02 Series"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 304 (BTN_A)
Event code 305 (BTN_B)
Event code 306 (BTN_C)
Event code 307 (BTN_X)
Event code 308 (BTN_Y)
Event code 309 (BTN_Z)
Event code 310 (BTN_TL)
Event code 311 (BTN_TR)
Event code 312 (BTN_TL2)
Event code 313 (BTN_TR2)
Event code 314 (BTN_SELECT)
Event code 315 (BTN_START)
Event code 316 (BTN_MODE)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 128
Min 0
Max 255
Flat 15
Event code 1 (ABS_Y)
Value 128
Min 0
Max 255
Flat 15
Event code 2 (ABS_Z)
Value 128
Min 0
Max 255
Flat 15
Event code 5 (ABS_RZ)
Value 128
Min 0
Max 255
Flat 15
Event code 16 (ABS_HAT0X)
Value 0
Min -1
Max 1
Event code 17 (ABS_HAT0Y)
Value 0
Min -1
Max 1
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
試してみると、ボタンは304~316、で、X-Y-ZとHATが使えるようです。
詳しく調べてみると、このコントローラ、「mode」ボタンで x-yモード、zモード、HATモードが切り替えられるようです。今回は、x-yモードを使います。
アーケードスティックの「1」~「10」を順に押していき、割り当てを確認します。
すると、「BTN_B,BTN_C,BTN_TR,BTN_TL,BTN_A,BTN_X,BTN_Z,BTN_Y,BTN_SELECT,BTN_START」のように割り当てられていることがわかりました。
これをメガドライブ純正パッドと同じようになるように変換してやればいい、ということです。
では、それをどうやって実現するか。
まず、標準のカーネルでは/dev/input/event4~event13に割り振られる部分のソースコードを変更し、+10して event14~event23に割り当てられるようにします(退避させる)
今のカーネルだと、刺さってるHUBのポートみて本体1,2はevent4,5、本体1に刺さったHUBのポート順に6,7,8,9,本体2のHUBでは10,11,12,13に割り当てられる
— ひろみつ(honeylab) 技術書典7け36D (@bakueikozo) October 8, 2019
話を簡単にするために、一旦裏のOTG経由でつないだやつをフィルタ出来るようにしてみるかな pic.twitter.com/RhQx1Nd5PY
そこで、LinuxのUsermode Input Deviceという機能を使い、イベントコードを変換するプログラムを書きます。
具体的には、以下のようなソースコードです。
このへん
を参考にしています。
このソースコードを、ホスト上でメガドライブミニ用にコンパイルし、メガドライブ上にコピーします。
iBuffaloのアーケードスティックをメガドライブに接続すると
[50270.421860] generic-usb 0003:1DD8:000E.0007: usb_submit_urb(ctrl) failed: -1
[50270.429714] generic-usb 0003:1DD8:000E.0007: timeout initializing reports
[50270.437645] input: BSGPAC02 Series as /devices/platform/sunxi-ehci.1/usb1/1-1/1-1.2/1-1.2:1.0/input/input14
[50270.449359] generic-usb 0003:1DD8:000E.0007: input,hidraw0: USB HID v1.10 Gamepad [BSGPAC02 Series] on usb-sunxi-ehci-1.2/input0
という表示が現れ、
# ./evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: axp22-supplyer
/dev/input/event1: sunxi-keyboard
/dev/input/event2: gpio-keys
/dev/input/event3: sunxi-ths
/dev/input/event14: BSGPAC02 Series
event14(本体のコントローラポート1に挿したため)が割り当てられました。
先ほどのプログラム(uinput)にこれを引数として渡します。
# ./uinput /dev/input/event14 &
# argv[0]=./uinput
[43017.030912] input: evfrom23 as /devices/virtual/input/input36
argv[1]=/dev/input/event14
すると、/devices/virtual/input/input36という仮想inputデバイスが作成され、プログラムで変換した値が送信されるようになります。
この時作成された仮想inputデバイスは、たまたまうまいことevent4に再マップされました。(udevルールを書くことで確実になるはずです)
# ./evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: axp22-supplyer
/dev/input/event1: sunxi-keyboard
/dev/input/event2: gpio-keys
/dev/input/event3: sunxi-ths
/dev/input/event4: evfrom23
この状態で、内蔵謹製エミュレータを起動すると、見事iBuffaloのアケコンを認識し、操作ができるようになりました!!!!
ここまで、手作業で実行して確認しているので、この一連の流れをスタートアップスクリプトとしてrootfsに書いてやれば、自動的にアーケードスティックを認識できるようになるはずです。
また、今回変換テーブルを作らず直接変換してしまっています。
そうではなく、接続されたコントローラのVIDなどを見て、適切なマップを追加することで別のコントローラも使用できるようになります。
専用の設定ファイルでも作ってやると後々追加出来て便利なんですかね。
加えて、現在のところ、本体にはんだ付けしたシリアルコンソールを利用しないで、
カーネルを書き換える仕組みがまだ整備されていません。
おそらく海外のproject LUNARやhakchiチームがそのあたりを実装したら、
そのコードを利用することで、本体を(物理的に)無改造でこのような動作をさせられるようになるはずです。しかし、保証は外れそうなので、その辺気になる方は、最初に出てきた変換アダプタを使うのがいいんでしょうね…
さて、この方法でメガドライブミニをプレイしている証拠映像がこちらです。
デモ撮影中とはいえゲームへたくそすぎんだろ…
まぁ、波動拳出てるんで、ちゃんと動いてるんだと思いますよ。
見た目ではわかりませんが、基板乗っ取りやArduinoなどの変換アダプタは使用していません。
メガドライブミニに直接コントローラのケーブルを挿しているだけです。
遅延など、測定はしていないのですが、本当に必要になったらどうにか測定してみたいと思います。
外付けUSBデバイスで変換するよりは理論上早いはずなんですがね…