pyxelを中華ゲーム機のLinuxで動作させるためのノウハウ 2/2

# 前回のあらすじ
pyxelを動作させるために必要なpythonをビルドするためのツールチェーンについて説明し、そのツールチェーンを使ってビルドされたpythonをLinuxに配置する場所を選定する方法を記しました。

今回は「pyxelを実際に動作させるためにはどのようにすれば良いのか？」というテーマで記していきます。

[前回の記事はこちらから](https://qiita.com/moto_game_de_it/private/da1cc733afb9e20922f8)

# フロントエンドについて
中華ゲーム機のディストリには必ず「フロントエンド」と呼ばれるアプリが実行されています。

ここでいうフロントエンドとは「ゲームを実行させるためにコントローラーを使ったユーザインターフェイスを提供するアプリ」のことを指します。

 Nintendo Switchでは下記の画像の通り「ゲームの一覧表示・ゲームやSwitch本体の設定変更・ゲームの実行をしてくれるフロントエンドアプリ」が起動していますが、それと同じだと考えてください。
(フロントエンドの詳細な解説については、アドカレとは別に記事を書きたいと思います)

![local-feature-pic-tab.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3930874/c131237a-a7b8-bdd6-83ca-8533872c8f74.jpeg)


 
中華ゲーム機の代表的なフロントエンドの3つを紹介します。

- Emulationstation
![IMG_0359.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3930874/28bca39a-deca-220e-9220-15ab2a26179a.jpeg)

- Gmenu2x
![IMG_0360.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3930874/7bd8148a-071a-3edf-9b62-729abc9cd631.jpeg)

- 名前がない開発ベンダーオリジナルのフロントエンド
![IMG_0361.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3930874/8b3c368a-afd1-3e45-64f9-06f1f5bf4634.jpeg)


それぞれユーザインターフェイスのデザインが違いますが、共通して「ゲームを実行させる」という目的を持った機能が実装されています。
よって「pyxelを実行させるコマンド」をフロントエンドに割り込ませることができれば、pyxelのゲームを実行させることが可能になります。

## フロントエンドの問題点
フロントエンドの設定ファイルがext4領域に保存されている場合は、設定ファイルを編集することでpyxelの実行が可能になります。

またEmulationstationやGmenu2xなどは「設定ファイルをユーザが書き換えることが可能な設計」になっている場合が多いです。

しかしディストリの設計思想によってはフロントエンドの設定ファイルをsquashfsに保存しているため、ユーザーが変更できない場合があります。
別のパターンとして「設定ファイルは編集できるけどOS起動時に元の設定に戻ってしまう」ということもあります。
これは「設計思想としてフロントエンドの設定変更は許さない」という意思表示ですね。

またベンダー独自のフロントエンドでは、そもそも設定ファイルが無い場合があり、フロントエンドのバイナリファイルに設定内容がハードコーディングされているパターンもあります。

厄介なことにそういったオリジナルのフロントエンドは「クローズドソース」となっていることが多いため、ソースコードをいじってpyxelを実行させることもできません。

このような理由でフロントエンドの設定を変更できない場合、一つだけpyxelを実行させる手段があります。

Linuxに移植されたネイティブゲームを実行させる機能の「ports」を利用することです。
この「ports」という機能はほとんどのフロントエンドで実装されています。

# portsについて
前提として、レトロゲームを実行させるためには決まった手順が確立されており、それを実行するためのロジックがフロントエンドにプログラムされています。

しかしLinuxネイティブなportsゲームには決まった実行手順がありません。
ですのでLinuxネイティブゲームのportsを実行するにはシェルスクリプトが利用されます。

フロントエンドに実装されている「ports」という機能は、言い換えれば「シェルスクリプトを実行する機能」ですので、pyxelを実行させるためのシェルスクリプトを作成して「portsゲーム」として実行すればいいことになります。

私がpyxelを中華ゲーム機のディストリで実行させるために取った方法は下記のとおりです。

| Distribution    | method                  | 
|:----------------|-------------------------:|
| [plumOS-RN](https://github.com/game-de-it/plumOS-RN)   | Emulationstation     |
| [plumOS-GKD](https://github.com/game-de-it/plumOS-GKD)    | Emulationstation     |
| [plumOS(XU MINI M)](https://github.com/game-de-it/XU_MINI_M)     | ports  |
| [MuOS(RG35xxシリーズ)](https://github.com/game-de-it/rg35xx/releases/tag/python3.9.19_pyxel2.2.1)       | ports              |
| [Batocera Linux(RG35xxシリーズ)](https://github.com/game-de-it/rg35xx/releases/tag/python3.9.19_pyxel2.2.1)          | ports              |
| [KNULLI(RG35xxシリーズ)](https://github.com/game-de-it/rg35xx/releases/tag/python3.9.19_pyxel2.2.1)         | ports            |
| [Anbernic StockOS(RG35xxシリーズ)](https://github.com/game-de-it/rg35xx/releases/tag/python3.9.19_pyxel2.2.1)         | ports            |


あれ？　と思った方もいるかもしれません。
ほとんどがports方式です。

plumOS-RN、plumOS-GKDは私が直接手掛けているLinuxディストリなので、squashfsやEmulationstationに直接干渉できます。

plumOS(XU MINI M)については開発ベンダーの作ったStockOSをベースに作られているため、Emulationstationのソースコードは公開されていませんのでports方式を採用しています。
(厳密に言えばEmulationstationの設定ファイルを変更して、pyxel実行機能を設定することも可能でしたが、私の作業時間がなかったせいで実装できませんでした・・・恥)

また他のディストリは他人が作ったものなので、仮にフロントエンドの設定を変更できたとしても、OSアップデートや製作者の気分次第でフロントエンドの設定が上書きされる可能性があります。
そのためOSアップデートや製作者の意向に左右されず、確実にpyxelを実行できるports方式を採用しています。

なおports方式の問題点は **「pyxelのゲームごとに実行手順が書かれたシェルスクリプトを作成する必要がある」** ということです。

やりたいゲームが少ない場合はいいですが、これからpyxelゲーム作品がたくさん誕生していくことを考えると、フロントエンドからpyxelを実行できる環境が必要になってくると考えています。

ちなみにフロントエンド方式では任意の.pyファイルや.pyxappファイルをユーザが選択して実行が可能ですので、いくらpyxelゲームが増えても問題ありません。

・・・と、この記事を書いていた時点ではそんな感じでしたが、12/10に「pyxelでフロントエンド(ランチャー)を作っちゃえばいいじゃないか！」と思いつき、それを実現させた「pyxel_launch」を作りました。

https://x.com/0_game_it/status/1866484373027254362

しかし、このランチャーはLinux環境に依存してしまうため、あまり実用的ではなさそうです・・・:sob:

https://x.com/0_game_it/status/1866851893206773782

pyxelエンジン内部にランチャー機能が内包されれば、ports方式の問題を解決できるかもしれませんね。
(北尾さん、何卒ランチャーの実装をお願いいたします:pray:)

## portsでpyxelを実行させるためのシェルスクリプトの一例
これはAnbernicのRG35XXシリーズのオリジナルディストリ(StockOS)で[Dungeon Antiqua](https://dungeon-antiqua.com/)を実行させるシェルスクリプトです。


```
#!/bin/sh

export SDL_GAMECONTROLLERCONFIG="19000000010000000100000000010000,Deeplay-keys,platform:Linux,a:b1,b:b0,x:b2,y:b3,back:b5,start:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b8,righttrigger:b9"
export SDL_JOYSTICK_DEVICE="/dev/input/js0"

PYTHON_PATH=/mnt/sdcard/Roms/pyxel/Python-3.9.19
PYTHON_BIN="${PYTHON_PATH}/bin/python3.9"
PYXEL_BIN="${PYTHON_PATH}/bin/pyxel"
PYXAPP1_FILE="/mnt/sdcard/Roms/pyxel/dungeon-antiqua.pyxapp"
PYXAPP2_FILE=""

#if [ ! -L /usr/lib/libffi.so.6 ]; then
#	ln -s /usr/lib/libffi.so /usr/lib/libffi.so.6
#fi

if [ -f "${PYXAPP1_FILE}" ]; then
	${PYTHON_BIN} ${PYTHON_PATH}/bin/vol.py &
	${PYTHON_BIN} ${PYXEL_BIN} play ${PYXAPP1_FILE}
	killall -9 python3.9
	amixer -c 0 cset name='lineout volume' 100%
	exit 0
elif [ -f "${PYXAPP2_FILE}" ]; then
	${PYTHON_BIN} ${PYTHON_PATH}/bin/vol.py &
	${PYTHON_BIN} ${PYXEL_BIN} play ${PYXAPP2_FILE}
	killall -9 python3.9
	amixer -c 0 cset name='lineout volume' 100%
	exit 0
fi

exit 0

```

このスクリプトでは下記の要素があります。

- SDLコントローラーの設定(ディストリによって値が異なる場合があります)
- Python本体のパス定義
- pyxel実行ファイルのパス定義
- pyxappファイルのパス定義
- ライブラリのチェックと必要なシンボリックリンクの作成処理(ディストリによっては必要ありません)
- 音量調整機能を持つpythonプログラムの実行処理(ディストリによっては必要ない機能です)
- pyxelの実行処理

例えばRG35XXシリーズのディストリ以外では音量調整プログラムは不必要だったり、ライブラリの確認も不要な場合もあります。

よってスクリプト内の要素はディストリによって違うため、pyxelを実行させるための適切な処理を見つけ出すまでに結構時間がかかります:dizzy_face:

時間がかかる理由としては・・・
1. そもそもディストリの仕様書・説明書は一切無いこと
2. 実際にそのディストリにログイン(SSHまたはUART)してLinuxのブートシーケンスやOSの仕様を把握する必要があること
3. どのようなプログラムが起動しているのかを把握する必要があること
4. 最終的にどのようにしてpyxelを実行できるかを考えなければならないこと

という課題があるからですね。
最終的なスクリプトは数十行ですみますが、それに至るまでの根拠を探し出すのがとても大変です。:disappointed_relieved:


# フロントエンド(ports方式ではないパターン)でpyxelを実行させるためのシェルスクリプトの一例
plumOS-GKDのEmulationstationで使われるシェルスクリプト。

●pyxel_plumOS.sh

```
#!/bin/sh

. /etc/profile
. /etc/os-release

ondemand

export SDL_GAMECONTROLLERCONFIG="19009b4d4b4800000111000000010000,retrogame_joypad,platform:Linux,x:b3,a:b0,b:b1,y:b2,back:b8,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,"

PYXEL_DIR="/storage/.config/.pyxel"
PYXEL_BIN="/storage/pyxel_Python/bin/pyxel"
ROM="${1}"
EXTENSION=`echo "${ROM}" | awk -F. '{print $NF}'`
ROMNAME=`basename "${ROM}" | awk -F. '{print $1}'`

if [ "${EXTENSION}" = "py" ]; then
  "${PYXEL_BIN}" run "${ROM}"
elif [ "${EXTENSION}" = "pyxapp" ]; then
  "${PYXEL_BIN}" play "${ROM}"
elif [ "${EXTENSION}" = "edit" ]; then
  mkdir -p "${PYXEL_DIR}"/save
  "${PYXEL_BIN}" edit "${PYXEL_DIR}"/save/"${ROMNAME}".pyxres
  sync
else
  exit 0
fi
```
このスクリプトでは下記の要素があります。

- SDLコントローラーの設定(ディストリによって値が異なる場合があります)
- pyxel実行ファイルのパス定義
- pyxappファイルの拡張子を判断する処理
- pyxelの実行処理

このスクリプトを動作させるためにはEmulationstationのフローを理解する必要があります。
●Emulationstationのフロー
![スクリーンショット 2024-12-10 9.54.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3930874/573c9ddb-8f18-e7f2-05fe-26653526fea8.png)


EmulationstationのUI上でユーザが選択した.pyや.pyxappのフルパスなどの要素は段階的にリレーされて、最終的にpyxel_plumOS.shの第一引数として渡されます。

ただし、このフローは「plumOS-GKDのEmulationstationのフロー」なので、他のディストリのEmulationstationではフローが違う場合があります。

（ちなみに上記のスクリプトでは裏機能として拡張子をeditにした適当なファイルを指定すると、[pyxelのエディターモード](https://kitao.github.io/pyxel/wasm/examples/image_editor.html)が起動するようにしており、USBキーボードとマウスで操作することが可能です。）

# 最近対応させたTRIMUIのオリジナルフロントエンドでpyxelを動作させた時の動画
https://x.com/0_game_it/status/1862387118175387807

フロントエンドの機能でpyxelを実行できるようにすると、コントローラー操作でpyxelゲームのファイルを指定してpyxelを実行することが可能です。
これならゲームごとにシェルスクリプトを用意する必要はありませんね:relaxed:

# 中華ゲーム機でpyxelのゲームを実行しているデモ動画集
https://x.com/0_game_it/status/1862398069524111853

https://x.com/0_game_it/status/1845124571550318628

https://x.com/0_game_it/status/1806333009257251049

https://x.com/0_game_it/status/1790412403274080580

https://x.com/0_game_it/status/1774451942380675480

# pyxelを中華ゲーム機に普及させるためには
pyxelが動く環境を採用させる決定権はそのディストリの製作者にあります。
pyxelを普及する活動を行うことで、pyxelがディストリ製作者に広く知れ渡るようになり、将来的にpyxelが中華ゲーム機のディストリで当たり前のように動く未来を想像しています。

# 第2回の所感
第一回、第二回とご覧いただきましてありがとうございました。

「pyxel」というゲームエンジンを一つのミドルウエアとして考えると、今回ご紹介した内容はインフラエンジニアとしての作業と共通している部分があるかと思います。

インフラエンジニアは「ITシステムの土台を作るエンジニア」です。
「中華ゲーム機のディストリでpyxelゲームを動かす土台を作る」というのは、まさにインフラエンジニアの作業です。

WEB系インフラエンジニアなら「Apache、Nginx、Tomcat、PHP-FPM、Postgresql、mysqlなどのミドルウエアを使って、WEBプログラムを動作させる土台を作る事」と置き換えることができると思います。

なお、中華ゲーム機のディストリには仕様書や説明書はありません。
同じくフロントエンドも仕様書や説明書はありません。

オープンソースならソースコードを読んで挙動を把握しなければならないし、クローズドソースなら実際にプログラムの挙動を目で見て挙動を確認していく必要があります。

目で見て挙動がわからない場合は、実行プロセスに対してstraceコマンドを使う場面も出てくるでしょう。
([RAFEというプロジェクト](https://github.com/game-de-it/rg35xx/releases/tag/RetroArchFrontEnd_v5)は真っ暗闇の状態から手探りで挙動を把握して、pyxelを実行させるための処理をインターセプトできた成果物の一つです)

仕様・説明書がないということは、ディストリに直接ログインして情報を収集する必要があり、その過程で下記のような「インフラエンジニアに求められる知識・技術」を養うことができます。

- ブートプロセス含むLinux全般の基本的な仕組み
- Linuxの基本操作
- コントローラー、音声出力、画面出力などを行う際にHWがどのようにしてLinuxに認識されて利用できるかの知識
- ミドルウエアの仕様を把握する能力
- プログラムがどのように実行されるのかを把握する能力
- プログラムのソースコードや実行プロセスから挙動を読み解く能力
- ファイルの扱いに関する知識

インフラエンジニアを目指す人や、すでにインフラエンジニアとして活躍している人にこそ、中華ゲーム機に触れて自身の能力に磨きをかけていただきたいと考えています。

最後に
「pyxel」という素晴らしいゲームエンジンが多くの中華ゲーム機で動作するように、微力ながらもこれからも尽力したいと考えています:dizzy:

以上　お読みいただきありがとうございました:bow: